9
0
mirror of https://github.com/LeavesMC/Leaves.git synced 2025-12-19 14:59:32 +00:00

Support chatimage protocol (#447)

This commit is contained in:
Lumine1909
2025-04-05 11:49:13 -04:00
committed by GitHub
parent 93af7681a3
commit 941be1ce7d
8 changed files with 274 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lumine1909 <133463833+Lumine1909@users.noreply.github.com>
Date: Sun, 30 Mar 2025 21:53:45 +0800
Subject: [PATCH] Vanilla player display name
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index eba1717566a8ea534bbf149e0593cc7656df2db9..311d4abc9cbd99e6b03135dadbd8ee93e7bc4a48 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -449,7 +449,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
// CraftBukkit start
this.displayName = this.getScoreboardName();
- this.adventure$displayName = net.kyori.adventure.text.Component.text(this.getScoreboardName()); // Paper
+ this.adventure$displayName = org.leavesmc.leaves.LeavesConfig.fix.vanillaDisplayName ? io.papermc.paper.adventure.PaperAdventure.asAdventure(this.getDisplayName()) : net.kyori.adventure.text.Component.text(this.getScoreboardName()); // Paper // Leaves - Vanilla display name
this.bukkitPickUpLoot = true;
this.maxHealthCache = this.getMaxHealth();
}

View File

@@ -844,6 +844,9 @@ public final class LeavesConfig {
@GlobalConfig("rei-server-protocol")
public boolean reiServerProtocol = false;
@GlobalConfig("chat-image-protocol")
public boolean chatImageProtocol = false;
@GlobalConfig("recipe-send-all")
public boolean recipeSendAll = false;
}
@@ -1032,6 +1035,9 @@ public final class LeavesConfig {
@GlobalConfig("vanilla-hopper")
public boolean vanillaHopper = false;
@GlobalConfig("vanilla-display-name")
public boolean vanillaDisplayName = false;
@RemovedConfig(name = "spigot-EndPlatform-destroy", category = "fix")
private final boolean spigotEndPlatformDestroy = false;
}

View File

@@ -0,0 +1,16 @@
package org.leavesmc.leaves.protocol.chatimage;
public class ChatImageIndex {
public int index;
public int total;
public String url;
public String bytes;
public ChatImageIndex(int index, int total, String url, String bytes) {
this.index = index;
this.total = total;
this.url = url;
this.bytes = bytes;
}
}

View File

@@ -0,0 +1,72 @@
package org.leavesmc.leaves.protocol.chatimage;
import com.google.gson.Gson;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Contract;
import org.leavesmc.leaves.LeavesConfig;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static org.leavesmc.leaves.protocol.chatimage.ServerBlockCache.SERVER_BLOCK_CACHE;
@LeavesProtocol(namespace = "chatimage")
public class ChatImageProtocol {
public static final String PROTOCOL_ID = "chatimage";
public static final Gson gson = new Gson();
@Contract("_ -> new")
public static ResourceLocation id(String path) {
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
}
@ProtocolHandler.PayloadReceiver(payload = FileChannelPayload.class, payloadId = "get_file_channel")
public static void serverFileChannelReceived(ServerPlayer player, FileChannelPayload payload) {
if (!LeavesConfig.protocol.chatImageProtocol) {
return;
}
MinecraftServer server = MinecraftServer.getServer();
String res = payload.message();
ChatImageIndex title = gson.fromJson(res, ChatImageIndex.class);
Map<Integer, String> blocks = SERVER_BLOCK_CACHE.createBlock(title, res);
if (title.total != blocks.size()) {
return;
}
List<UUID> names = SERVER_BLOCK_CACHE.getUsers(title.url);
if (names == null || player == null) {
return;
}
for (UUID uuid : names) {
ServerPlayer serverPlayer = server.getPlayerList().getPlayer(uuid);
if (serverPlayer != null) {
ProtocolUtils.sendPayloadPacket(serverPlayer, new FileInfoChannelPayload("true->" + title.url));
}
}
}
@ProtocolHandler.PayloadReceiver(payload = FileInfoChannelPayload.class, payloadId = "file_info")
public static void serverGetFileChannelReceived(ServerPlayer player, FileInfoChannelPayload packet) {
if (!LeavesConfig.protocol.chatImageProtocol) {
return;
}
String url = packet.message();
Map<Integer, String> list = SERVER_BLOCK_CACHE.getBlock(url);
if (list == null) {
ProtocolUtils.sendPayloadPacket(player, new FileInfoChannelPayload("null->" + url));
SERVER_BLOCK_CACHE.tryAddUser(url, player.getUUID());
return;
}
for (Map.Entry<Integer, String> entry : list.entrySet()) {
ProtocolUtils.sendPayloadPacket(player, new DownloadFileChannelPayload(entry.getValue()));
}
}
}

View File

@@ -0,0 +1,32 @@
package org.leavesmc.leaves.protocol.chatimage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
public record DownloadFileChannelPayload(String message) implements LeavesCustomPayload<DownloadFileChannelPayload> {
private static final ResourceLocation ID = ChatImageProtocol.id("download_file_channel");
private static final StreamCodec<RegistryFriendlyByteBuf, DownloadFileChannelPayload> CODEC =
StreamCodec.composite(ByteBufCodecs.STRING_UTF8, DownloadFileChannelPayload::message, DownloadFileChannelPayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
public ResourceLocation id() {
return ID;
}
@New
public static DownloadFileChannelPayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
}

View File

@@ -0,0 +1,32 @@
package org.leavesmc.leaves.protocol.chatimage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
public record FileChannelPayload(String message) implements LeavesCustomPayload<FileChannelPayload> {
private static final ResourceLocation ID = ChatImageProtocol.id("get_file_channel");
private static final StreamCodec<RegistryFriendlyByteBuf, FileChannelPayload> CODEC =
StreamCodec.composite(ByteBufCodecs.STRING_UTF8, FileChannelPayload::message, FileChannelPayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
public ResourceLocation id() {
return ID;
}
@New
public static FileChannelPayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
}

View File

@@ -0,0 +1,32 @@
package org.leavesmc.leaves.protocol.chatimage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
public record FileInfoChannelPayload(String message) implements LeavesCustomPayload<FileInfoChannelPayload> {
private static final ResourceLocation ID = ChatImageProtocol.id("file_info");
private static final StreamCodec<RegistryFriendlyByteBuf, FileInfoChannelPayload> CODEC =
StreamCodec.composite(ByteBufCodecs.STRING_UTF8, FileInfoChannelPayload::message, FileInfoChannelPayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
public ResourceLocation id() {
return ID;
}
@New
public static FileInfoChannelPayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
}

View File

@@ -0,0 +1,65 @@
package org.leavesmc.leaves.protocol.chatimage;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import org.leavesmc.leaves.LeavesLogger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class ServerBlockCache {
public static final ServerBlockCache SERVER_BLOCK_CACHE = new ServerBlockCache();
public Cache<String, List<UUID>> userCache = CacheBuilder.newBuilder().expireAfterAccess(60, TimeUnit.SECONDS).build();
public Cache<String, Map<Integer, String>> blockCache = CacheBuilder.newBuilder().expireAfterAccess(60, TimeUnit.SECONDS).build();
public Cache<String, Integer> fileCount = CacheBuilder.newBuilder().expireAfterAccess(60, TimeUnit.SECONDS).build();
public Map<Integer, String> createBlock(ChatImageIndex title, String imgBytes) {
try {
Map<Integer, String> blocks = this.blockCache.get(title.url, HashMap::new);
blocks.put(title.index, imgBytes);
this.blockCache.put(title.url, blocks);
this.fileCount.put(title.url, title.total);
return blocks;
} catch (Exception e) {
LeavesLogger.LOGGER.warning("Failed to create block for title " + title.url + ": " + e);
return null;
}
}
public Map<Integer, String> getBlock(String url) {
Map<Integer, String> list;
Integer total;
if ((list = this.blockCache.getIfPresent(url)) != null && (total = this.fileCount.getIfPresent(url)) != null) {
if (total == list.size()) {
return list;
}
}
return null;
}
public void tryAddUser(String url, UUID uuid) {
try {
List<UUID> names = this.userCache.get(url, Lists::newArrayList);
names.add(uuid);
this.userCache.put(url, names);
} catch (Exception e) {
LeavesLogger.LOGGER.warning("Failed to add user " + uuid + ": " + e);
}
}
public List<UUID> getUsers(String url) {
List<UUID> names;
if ((names = this.userCache.getIfPresent(url)) != null) {
this.userCache.put(url, Lists.newArrayList());
return names;
} else {
return null;
}
}
}