9
0
mirror of https://github.com/LeavesMC/Leaves.git synced 2025-12-19 14:59:32 +00:00
Files
LeavesMC/patches/server/0123-Servux-Protocol.patch
2023-09-24 17:30:45 +08:00

224 lines
10 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Wed, 13 Sep 2023 19:31:20 +0800
Subject: [PATCH] Servux Protocol
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index fba016014ffc628887dc873cfa54f8c76a72c7ef..3201913bd2fba0038ab4853931a641e361a883b1 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1610,6 +1610,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
top.leavesmc.leaves.protocol.AppleSkinProtocol.tick(); // Leaves - appleskin
top.leavesmc.leaves.util.BreakBedrockList.endTick(); // Leaves - break bedrock list
top.leavesmc.leaves.protocol.bladeren.MsptSyncProtocol.tick(this); // Leaves - mspt sync
+ top.leavesmc.leaves.protocol.ServuxProtocol.tick(); // Leaves - servux
for (int i = 0; i < this.tickables.size(); ++i) {
((Runnable) this.tickables.get(i)).run();
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 9b4bdbd98ed4d2a39d5908b0ba29f85ec6d45799..52d400b81ed45fc40983b7a238b5f8f679380471 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3605,6 +3605,11 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
try {
String channels = packet.data.toString(com.google.common.base.Charsets.UTF_8);
for (String channel : channels.split("\0")) {
+ // Leaves start - leaves extra protocol
+ if (top.leavesmc.leaves.LeavesConfig.servuxProtocol && channel.equals(top.leavesmc.leaves.protocol.ServuxProtocol.CHANNEL.toString())) {
+ top.leavesmc.leaves.protocol.ServuxProtocol.onPlayerSubscribed(player);
+ }
+ // Leaves end - leaves extra protocol
this.getCraftPlayer().addChannel(channel);
}
} catch (Exception ex) {
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index cf4ea675119d3c1a12435a6f00309ea2023e0a71..34cb5a6bd8ea93b4cdab6c9ce289c8cb68edf5f8 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -769,6 +769,7 @@ public abstract class PlayerList {
top.leavesmc.leaves.protocol.BBORProtocol.onPlayerLoggedOut(entityplayer); // Leaves - bbor
top.leavesmc.leaves.protocol.AppleSkinProtocol.onPlayerLoggedOut(entityplayer); // Leaves - appleskin
top.leavesmc.leaves.protocol.bladeren.MsptSyncProtocol.onPlayerLoggedOut(entityplayer); // Leaves - mspt sync
+ top.leavesmc.leaves.protocol.ServuxProtocol.onPlayerLoggedOut(entityplayer); // Leaves - servux
// Paper end
ServerLevel worldserver = entityplayer.serverLevel();
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
index e34e6fa60cedb925e2cb6f3a031d4157a9633eb5..f08c9be4a3fbab9f95167226b489d5d7bf0bdfac 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -1030,6 +1030,7 @@ public class LevelChunk extends ChunkAccess {
// Leaves start - bbor
if (loaded) {
top.leavesmc.leaves.protocol.BBORProtocol.onChunkLoaded(this);
+ top.leavesmc.leaves.protocol.ServuxProtocol.onChunkLoaded(this); // and servux
}
// Leaves end - bbor
}
diff --git a/src/main/java/top/leavesmc/leaves/protocol/ServuxProtocol.java b/src/main/java/top/leavesmc/leaves/protocol/ServuxProtocol.java
new file mode 100644
index 0000000000000000000000000000000000000000..3102c81528595d82c5ef9b8ceee15d2d595fc434
--- /dev/null
+++ b/src/main/java/top/leavesmc/leaves/protocol/ServuxProtocol.java
@@ -0,0 +1,159 @@
+package top.leavesmc.leaves.protocol;
+
+import io.netty.buffer.Unpooled;
+import net.minecraft.core.Registry;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.levelgen.structure.BoundingBox;
+import net.minecraft.world.level.levelgen.structure.Structure;
+import net.minecraft.world.level.levelgen.structure.StructureStart;
+import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import top.leavesmc.leaves.LeavesConfig;
+import top.leavesmc.leaves.util.ProtocolUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ServuxProtocol {
+
+ public static final String PROTOCOL_ID = "servux";
+
+ public static final int PROTOCOL_VERSION = 1;
+ public static final int PACKET_METADATA = 1;
+ public static final int PACKET_STRUCTURE_DATA = 2;
+
+ public static final ResourceLocation CHANNEL = id("structures");
+
+ private static final Map<Integer, ServerPlayer> players = new ConcurrentHashMap<>();
+ private static final Map<Integer, Set<BoundingBox>> playerBoundingBoxesCache = new HashMap<>();
+ private static final Map<ResourceLocation, Map<BoundingBox, CompoundTag>> dimensionCache = new ConcurrentHashMap<>();
+
+ @Contract("_ -> new")
+ public static @NotNull ResourceLocation id(String path) {
+ return new ResourceLocation(PROTOCOL_ID, path);
+ }
+
+ public static void onPlayerSubscribed(@NotNull ServerPlayer player) {
+ if (LeavesConfig.servuxProtocol) {
+ players.put(player.getId(), player);
+
+ CompoundTag tag = new CompoundTag();
+ tag.putInt("version", PROTOCOL_VERSION);
+ tag.putString("id", CHANNEL.toString());
+ tag.putInt("timeout", Integer.MAX_VALUE - 200);
+ sendNBTPacket(player, PACKET_METADATA, tag);
+ }
+ }
+
+ public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
+ if (LeavesConfig.servuxProtocol) {
+ players.remove(player.getId());
+ playerBoundingBoxesCache.remove(player.getId());
+ }
+ }
+
+ public static void onChunkLoaded(@NotNull LevelChunk chunk) {
+ if (LeavesConfig.servuxProtocol) {
+ List<StructureStart> structures = new ArrayList<>();
+ final Registry<Structure> structureFeatureRegistry = chunk.getLevel().registryAccess().registryOrThrow(Registries.STRUCTURE);
+ for (var es : chunk.getAllStarts().entrySet()) {
+ final var optional = structureFeatureRegistry.getResourceKey(es.getKey());
+ optional.ifPresent(key -> structures.add(es.getValue()));
+ }
+
+ if (!structures.isEmpty()) {
+ onStructuresLoaded((ServerLevel) chunk.getLevel(), structures);
+ }
+ }
+ }
+
+ public static void onStructuresLoaded(@NotNull ServerLevel level, @NotNull List<StructureStart> structures) {
+ Map<BoundingBox, CompoundTag> cache = getOrCreateCache(level.dimension().location());
+ for (StructureStart structureStart : structures) {
+ if (structureStart == null) {
+ return;
+ }
+
+ StructurePieceSerializationContext ctx = StructurePieceSerializationContext.fromLevel(level);
+
+ BoundingBox boundingBox = structureStart.getBoundingBox();
+ if (cache.containsKey(boundingBox)) {
+ return;
+ }
+
+ cache.put(boundingBox, structureStart.createTag(ctx, structureStart.getChunkPos()));
+ }
+ }
+
+ private static void sendBoundingToPlayer(int id, ServerPlayer player) {
+ Map<BoundingBox, CompoundTag> boundingBoxMap = dimensionCache.get(player.level().dimension().location());
+ if (boundingBoxMap == null) {
+ return;
+ }
+
+ Set<BoundingBox> playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(id, k -> new HashSet<>());
+
+ ListTag listTag = new ListTag();
+ for (BoundingBox key : boundingBoxMap.keySet()) {
+ if (listTag.size() >= 50) {
+ break;
+ }
+ if (playerBoundingBoxes.contains(key)) {
+ continue;
+ }
+
+ CompoundTag boundingBoxes = boundingBoxMap.get(key);
+ if (boundingBoxes != null) {
+ listTag.add(boundingBoxes);
+ }
+ playerBoundingBoxes.add(key);
+ }
+
+ if (!listTag.isEmpty()) {
+ CompoundTag tag = new CompoundTag();
+ tag.put("Structures", listTag);
+ sendNBTPacket(player, PACKET_STRUCTURE_DATA, tag);
+ }
+ }
+
+ public static void tick() {
+ if (LeavesConfig.servuxProtocol) {
+ for (var playerEntry : players.entrySet()) {
+ sendBoundingToPlayer(playerEntry.getKey(), playerEntry.getValue());
+ }
+ }
+ }
+
+ private static Map<BoundingBox, CompoundTag> getOrCreateCache(ResourceLocation dimensionId) {
+ return dimensionCache.computeIfAbsent(dimensionId, dt -> new ConcurrentHashMap<>());
+ }
+
+ public static void sendNBTPacket(ServerPlayer player, int packetType, CompoundTag data) {
+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
+ buf.writeVarInt(packetType);
+ buf.writeNbt(data);
+
+ int len = buf.writerIndex();
+ buf.readerIndex(0);
+
+ FriendlyByteBuf buf1 = new FriendlyByteBuf(Unpooled.buffer(len));
+ buf1.writeVarInt(len);
+ buf1.writeBytes(buf, len);
+ ProtocolUtils.sendPayloadPacket(player, CHANNEL, buf1);
+
+ buf.release();
+ }
+}