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 3dc75d267340fdda50668ec0cc7270867dd96117..5a36cebb698ff296a057abb2745c60b41c243ad7 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1594,6 +1594,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); + + final Registry structureRegistry = player.server.registryAccess().registryOrThrow(Registries.STRUCTURE); + final Set structureIds = structureRegistry.entrySet().stream().map(e -> e.getKey().location().toString()).collect(Collectors.toSet()); + FriendlyByteBuf buf1 = new FriendlyByteBuf(Unpooled.buffer()); + buf1.writeVarInt(structureIds.size()); + structureIds.forEach(buf1::writeUtf); + ProtocolUtils.sendPayloadPacket(player, STRUCTURE_LIST_SYNC, buf1); + } + } + + public static void onPlayerSubscribed(@NotNull ServerPlayer player) { + if (LeavesConfig.bborProtocol) { + players.put(player.getId(), player); + sendBoundingToPlayer(player.getId(), player); + } + } + + public static void onDataPackReload() { + if (LeavesConfig.bborProtocol) { + for (var playerEntry : players.entrySet()) { + final Registry structureRegistry = MinecraftServer.getServer().registryAccess().registryOrThrow(Registries.STRUCTURE); + final Set structureIds = structureRegistry.entrySet().stream().map(e -> e.getKey().location().toString()).collect(Collectors.toSet()); + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + buf.writeVarInt(structureIds.size()); + structureIds.forEach(buf::writeUtf); + ProtocolUtils.sendPayloadPacket(playerEntry.getValue(), STRUCTURE_LIST_SYNC, buf); + } + } + } + + 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(Registries.STRUCTURE); + 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:" + 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; + } + } +}