diff --git a/leaves-server/minecraft-patches/features/0118-Vanilla-player-display-name.patch b/leaves-server/minecraft-patches/features/0118-Vanilla-player-display-name.patch new file mode 100644 index 00000000..b028ef14 --- /dev/null +++ b/leaves-server/minecraft-patches/features/0118-Vanilla-player-display-name.patch @@ -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(); + } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/LeavesConfig.java b/leaves-server/src/main/java/org/leavesmc/leaves/LeavesConfig.java index 8ef1cf73..9e364f1d 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/LeavesConfig.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/LeavesConfig.java @@ -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; } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/ChatImageIndex.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/ChatImageIndex.java new file mode 100644 index 00000000..26eca28c --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/ChatImageIndex.java @@ -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; + } +} \ No newline at end of file diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/ChatImageProtocol.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/ChatImageProtocol.java new file mode 100644 index 00000000..ecbc7e1b --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/ChatImageProtocol.java @@ -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 blocks = SERVER_BLOCK_CACHE.createBlock(title, res); + if (title.total != blocks.size()) { + return; + } + List 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 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 entry : list.entrySet()) { + ProtocolUtils.sendPayloadPacket(player, new DownloadFileChannelPayload(entry.getValue())); + } + } +} \ No newline at end of file diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/DownloadFileChannelPayload.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/DownloadFileChannelPayload.java new file mode 100644 index 00000000..1562aadf --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/DownloadFileChannelPayload.java @@ -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 { + + private static final ResourceLocation ID = ChatImageProtocol.id("download_file_channel"); + + private static final StreamCodec 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)); + } +} diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/FileChannelPayload.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/FileChannelPayload.java new file mode 100644 index 00000000..2a8e3cc9 --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/FileChannelPayload.java @@ -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 { + + private static final ResourceLocation ID = ChatImageProtocol.id("get_file_channel"); + + private static final StreamCodec 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)); + } +} \ No newline at end of file diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/FileInfoChannelPayload.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/FileInfoChannelPayload.java new file mode 100644 index 00000000..389235fd --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/FileInfoChannelPayload.java @@ -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 { + + private static final ResourceLocation ID = ChatImageProtocol.id("file_info"); + + private static final StreamCodec 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)); + } +} \ No newline at end of file diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/ServerBlockCache.java b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/ServerBlockCache.java new file mode 100644 index 00000000..6498eb79 --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/protocol/chatimage/ServerBlockCache.java @@ -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> userCache = CacheBuilder.newBuilder().expireAfterAccess(60, TimeUnit.SECONDS).build(); + public Cache> blockCache = CacheBuilder.newBuilder().expireAfterAccess(60, TimeUnit.SECONDS).build(); + public Cache fileCount = CacheBuilder.newBuilder().expireAfterAccess(60, TimeUnit.SECONDS).build(); + + public Map createBlock(ChatImageIndex title, String imgBytes) { + try { + Map 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 getBlock(String url) { + Map 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 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 getUsers(String url) { + List names; + if ((names = this.userCache.getIfPresent(url)) != null) { + this.userCache.put(url, Lists.newArrayList()); + return names; + } else { + return null; + } + } +} \ No newline at end of file