From 0b070d9e4fb906cc95391312e635943da69a3d56 Mon Sep 17 00:00:00 2001 From: violetc <58360096+s-yh-china@users.noreply.github.com> Date: Tue, 29 Nov 2022 09:53:15 +0800 Subject: [PATCH] BBOR Protocol --- docs/MODIFICATION.md | 3 +- docs/MODIFICATION_cn.md | 3 +- patches/server/0046-PCA-sync-protocol.patch | 7 +- patches/server/0047-BBOR-Protocol.patch | 301 ++++++++++++++++++++ 4 files changed, 307 insertions(+), 7 deletions(-) create mode 100644 patches/server/0047-BBOR-Protocol.patch diff --git a/docs/MODIFICATION.md b/docs/MODIFICATION.md index 959a3e33..b0f7d9a6 100644 --- a/docs/MODIFICATION.md +++ b/docs/MODIFICATION.md @@ -63,4 +63,5 @@ Leaves Modification > All of it will have configuration -- PCA sync protocol \ No newline at end of file +- PCA sync protocol +- BBOR protocol \ No newline at end of file diff --git a/docs/MODIFICATION_cn.md b/docs/MODIFICATION_cn.md index 24cf3e79..695eb4a6 100644 --- a/docs/MODIFICATION_cn.md +++ b/docs/MODIFICATION_cn.md @@ -63,4 +63,5 @@ Leaves Modification > 所有的协议内容都会存在配置项 -- PCA同步协议 \ No newline at end of file +- PCA同步协议 +- BBOR结构显示协议 \ No newline at end of file diff --git a/patches/server/0046-PCA-sync-protocol.patch b/patches/server/0046-PCA-sync-protocol.patch index 0d0b9d7a..4cc62a91 100644 --- a/patches/server/0046-PCA-sync-protocol.patch +++ b/patches/server/0046-PCA-sync-protocol.patch @@ -395,10 +395,10 @@ index 40d1d67172a90123a23020e71e06ced9d859d752..51a576f75c065a896f4f2a223c3e8724 public final String worldName; diff --git a/src/main/java/top/leavesmc/leaves/protocol/PcaSyncProtocol.java b/src/main/java/top/leavesmc/leaves/protocol/PcaSyncProtocol.java new file mode 100644 -index 0000000000000000000000000000000000000000..708ea80b79a776f719505d7bb6a39d02d0e0728c +index 0000000000000000000000000000000000000000..d8f84c38aaae14a093bfe8e901a3be802617fb12 --- /dev/null +++ b/src/main/java/top/leavesmc/leaves/protocol/PcaSyncProtocol.java -@@ -0,0 +1,360 @@ +@@ -0,0 +1,357 @@ +package top.leavesmc.leaves.protocol; + +import io.netty.buffer.Unpooled; @@ -413,8 +413,6 @@ index 0000000000000000000000000000000000000000..708ea80b79a776f719505d7bb6a39d02 +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; -+import net.minecraft.world.level.block.BarrelBlock; -+import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; @@ -423,7 +421,6 @@ index 0000000000000000000000000000000000000000..708ea80b79a776f719505d7bb6a39d02 +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.logging.log4j.util.TriConsumer; -+import org.bukkit.World; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; diff --git a/patches/server/0047-BBOR-Protocol.patch b/patches/server/0047-BBOR-Protocol.patch new file mode 100644 index 00000000..3d7215a0 --- /dev/null +++ b/patches/server/0047-BBOR-Protocol.patch @@ -0,0 +1,301 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: violetc <58360096+s-yh-china@users.noreply.github.com> +Date: Tue, 29 Nov 2022 09:51:16 +0800 +Subject: [PATCH] BBOR Protocol + + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index c95d1fb9854ed303200d9800d5c230a8b8c0a177..fd71694e3c21926851d91b2f1a5a9be577add443 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1581,6 +1581,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop players = new ConcurrentHashMap<>(); ++ private static final Map> playerBoundingBoxesCache = new HashMap<>(); ++ private static final Map>> dimensionCache = new ConcurrentHashMap<>(); ++ ++ private static final ServerLevel OVERWORLD = MinecraftServer.getServer().overworld(); ++ ++ @Contract("_ -> new") ++ public static @NotNull ResourceLocation id(String path) { ++ return new ResourceLocation(PROTOCOL_ID, path); ++ } ++ ++ public static void onPlayerLoggedIn(@NotNull ServerPlayer player) { ++ if (LeavesConfig.bborProtocol) { ++ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); ++ buf.writeLong(OVERWORLD.getSeed()); ++ buf.writeInt(OVERWORLD.levelData.getXSpawn()); ++ buf.writeInt(OVERWORLD.levelData.getZSpawn()); ++ ProtocolUtils.sendPayloadPacket(player, INITIALIZE_CLIENT, buf); ++ } ++ } ++ ++ public static void onPlayerSubscribed(@NotNull ServerPlayer player) { ++ if (LeavesConfig.bborProtocol) { ++ players.put(player.getId(), player); ++ } ++ } ++ ++ public static void onPlayerLoggedOut(@NotNull ServerPlayer player) { ++ if (LeavesConfig.bborProtocol) { ++ players.remove(player.getId()); ++ playerBoundingBoxesCache.remove(player.getId()); ++ } ++ } ++ ++ public static void onChunkLoaded(@NotNull LevelChunk chunk) { ++ if (LeavesConfig.bborProtocol) { ++ Map structures = new HashMap<>(); ++ final Registry structureFeatureRegistry = chunk.getLevel().registryAccess().registryOrThrow(Registry.STRUCTURE_REGISTRY); ++ for (var es : chunk.getAllStarts().entrySet()) { ++ final var optional = structureFeatureRegistry.getResourceKey(es.getKey()); ++ optional.ifPresent(key -> structures.put(key.location().toString(), es.getValue())); ++ } ++ if (structures.size() > 0) { ++ onStructuresLoaded(chunk.getLevel().dimension().location(), structures); ++ } ++ } ++ } ++ ++ public static void onStructuresLoaded(@NotNull ResourceLocation dimensionID, @NotNull Map structures) { ++ Map> cache = getOrCreateCache(dimensionID); ++ for (var entry : structures.entrySet()) { ++ StructureStart structureStart = entry.getValue(); ++ if (structureStart == null) { ++ return; ++ } ++ ++ String type = "structure:x" + entry.getKey(); ++ BoundingBox bb = structureStart.getBoundingBox(); ++ BBoundingBox boundingBox = buildStructure(bb, type); ++ if (cache.containsKey(boundingBox)) { ++ return; ++ } ++ ++ Set structureBoundingBoxes = new HashSet<>(); ++ for (StructurePiece structureComponent : structureStart.getPieces()) { ++ structureBoundingBoxes.add(buildStructure(structureComponent.getBoundingBox(), type)); ++ } ++ cache.put(boundingBox, structureBoundingBoxes); ++ } ++ } ++ ++ private static @NotNull BBoundingBox buildStructure(@NotNull BoundingBox bb, String type) { ++ BlockPos min = new BlockPos(bb.minX(), bb.minY(), bb.minZ()); ++ BlockPos max = new BlockPos(bb.maxX(), bb.maxY(), bb.maxZ()); ++ return new BBoundingBox(type, min, max); ++ } ++ ++ private static void sendBoundingToPlayer(int id, ServerPlayer player) { ++ for (var entry : dimensionCache.entrySet()) { ++ if (entry.getValue() == null) { ++ return; ++ } ++ ++ Set playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(id, k -> new HashSet<>()); ++ Map> boundingBoxMap = entry.getValue(); ++ for (BBoundingBox key : boundingBoxMap.keySet()) { ++ if (playerBoundingBoxes.contains(key)) { ++ continue; ++ } ++ ++ Set boundingBoxes = boundingBoxMap.get(key); ++ ++ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); ++ buf.writeResourceLocation(entry.getKey()); ++ key.serialize(buf); ++ if (boundingBoxes != null && boundingBoxes.size() > 1) { ++ for (BBoundingBox box : boundingBoxes) { ++ box.serialize(buf); ++ } ++ } ++ ProtocolUtils.sendPayloadPacket(player, ADD_BOUNDING_BOX, buf); ++ playerBoundingBoxes.add(key); ++ } ++ } ++ } ++ ++ public static void tick() { ++ if (LeavesConfig.bborProtocol) { ++ for (var playerEntry : players.entrySet()) { ++ sendBoundingToPlayer(playerEntry.getKey(), playerEntry.getValue()); ++ } ++ } ++ } ++ ++ public static void initAllPlayer() { ++ for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) { ++ onPlayerLoggedIn(player); ++ } ++ } ++ ++ public static void loggedOutAllPlayer() { ++ players.clear(); ++ playerBoundingBoxesCache.clear(); ++ for (var cache : dimensionCache.values()) { ++ cache.clear(); ++ } ++ dimensionCache.clear(); ++ } ++ ++ private static Map> getOrCreateCache(ResourceLocation dimensionId) { ++ return dimensionCache.computeIfAbsent(dimensionId, dt -> new ConcurrentHashMap<>()); ++ } ++ ++ private record BBoundingBox(String type, BlockPos min, BlockPos max) { ++ ++ public void serialize(@NotNull FriendlyByteBuf buf) { ++ buf.writeChar('S'); ++ buf.writeInt(type.hashCode()); ++ buf.writeVarInt(min.getX()).writeVarInt(min.getY()).writeVarInt(min.getZ()); ++ buf.writeVarInt(max.getX()).writeVarInt(max.getY()).writeVarInt(max.getZ()); ++ } ++ ++ @Override ++ public int hashCode() { ++ return combineHashCodes(min.hashCode(), max.hashCode()); ++ } ++ ++ private static int combineHashCodes(int @NotNull ... hashCodes) { ++ final int prime = 31; ++ int result = 0; ++ for (int hashCode : hashCodes) { ++ result = prime * result + hashCode; ++ } ++ return result; ++ } ++ } ++}