diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java index d37556692..78ea579f3 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitBlockManager.java @@ -25,8 +25,10 @@ import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.sound.SoundSet; -import net.momirealms.craftengine.core.util.*; -import net.momirealms.craftengine.core.world.chunk.PalettedContainer; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ObjectHolder; +import net.momirealms.craftengine.core.util.Tristate; +import net.momirealms.craftengine.core.util.VersionHelper; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.event.HandlerList; @@ -322,9 +324,6 @@ public final class BukkitBlockManager extends AbstractBlockManager { // 注册服务端侧的真实方块 private void registerServerSideCustomBlocks(int count) { // 这个会影响全局调色盘 - if (MiscUtils.ceilLog2(this.vanillaBlockStateCount + count) == MiscUtils.ceilLog2(this.vanillaBlockStateCount)) { - PalettedContainer.NEED_DOWNGRADE = false; - } try { unfreezeRegistry(); for (int i = 0; i < count; i++) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java index 7c0456145..e2ccd93e6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/BukkitBlockEntityTypes.java @@ -8,4 +8,6 @@ public class BukkitBlockEntityTypes extends BlockEntityTypes { public static final BlockEntityType SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE, SimpleStorageBlockEntity::new); public static final BlockEntityType SIMPLE_PARTICLE = register(BlockEntityTypeKeys.SIMPLE_PARTICLE, SimpleParticleBlockEntity::new); public static final BlockEntityType WALL_TORCH_PARTICLE = register(BlockEntityTypeKeys.WALL_TORCH_PARTICLE, WallTorchParticleBlockEntity::new); + + private BukkitBlockEntityTypes() {} } \ No newline at end of file diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/BukkitBlockEntityElementConfigs.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/BukkitBlockEntityElementConfigs.java index b8c12c41b..8553a7c56 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/BukkitBlockEntityElementConfigs.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/entity/renderer/element/BukkitBlockEntityElementConfigs.java @@ -9,6 +9,8 @@ public class BukkitBlockEntityElementConfigs extends BlockEntityElementConfigs { register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY); } + private BukkitBlockEntityElementConfigs() {} + public static void init() { } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index e33baec87..bb2014a0e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -299,7 +299,12 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes } this.blockStateRemapper = newMappings; this.modBlockStateRemapper = newMappingsMOD; - registerS2CGamePacketListener(new LevelChunkWithLightListener(newMappings, newMappingsMOD, newMappings.length, RegistryUtils.currentBiomeRegistrySize()), this.packetIds.clientboundLevelChunkWithLightPacket(), "ClientboundLevelChunkWithLightPacket"); + registerS2CGamePacketListener(new LevelChunkWithLightListener( + newMappings, + newMappingsMOD, + newMappings.length, + RegistryUtils.currentBiomeRegistrySize() + ), this.packetIds.clientboundLevelChunkWithLightPacket(), "ClientboundLevelChunkWithLightPacket"); registerS2CGamePacketListener(new SectionBlockUpdateListener(newMappings, newMappingsMOD), this.packetIds.clientboundSectionBlocksUpdatePacket(), "ClientboundSectionBlocksUpdatePacket"); registerS2CGamePacketListener(new BlockUpdateListener(newMappings, newMappingsMOD), this.packetIds.clientboundBlockUpdatePacket(), "ClientboundBlockUpdatePacket"); registerS2CGamePacketListener( @@ -1922,12 +1927,14 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes private final int[] modBlockStateMapper; private final IntIdentityList biomeList; private final IntIdentityList blockList; + private final boolean needsDowngrade; public LevelChunkWithLightListener(int[] blockStateMapper, int[] modBlockStateMapper, int blockRegistrySize, int biomeRegistrySize) { this.blockStateMapper = blockStateMapper; this.modBlockStateMapper = modBlockStateMapper; this.biomeList = new IntIdentityList(biomeRegistrySize); this.blockList = new IntIdentityList(blockRegistrySize); + this.needsDowngrade = MiscUtils.ceilLog2(BlockStateUtils.vanillaBlockStateCount()) != MiscUtils.ceilLog2(blockRegistrySize); } @Override @@ -1936,8 +1943,10 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes FriendlyByteBuf buf = event.getBuffer(); int chunkX = buf.readInt(); int chunkZ = buf.readInt(); + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); boolean named = !VersionHelper.isOrAbove1_20_2(); - // ClientboundLevelChunkPacketData + + // 读取区块数据 int heightmapsCount = 0; Map heightmapsMap = null; net.momirealms.sparrow.nbt.Tag heightmaps = null; @@ -1956,112 +1965,105 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes int chunkDataBufferSize = buf.readVarInt(); byte[] chunkDataBytes = new byte[chunkDataBufferSize]; buf.readBytes(chunkDataBytes); - int blockEntitiesDataCount = buf.readVarInt(); - List blockEntitiesData = new ArrayList<>(); - for (int i = 0; i < blockEntitiesDataCount; i++) { - byte packedXZ = buf.readByte(); - short y = buf.readShort(); - int type = buf.readVarInt(); - Tag tag = buf.readNbt(named); - BlockEntityData blockEntityData = new BlockEntityData(packedXZ, y, type, tag); - blockEntitiesData.add(blockEntityData); - } - // ClientboundLightUpdatePacketData - BitSet skyYMask = buf.readBitSet(); - BitSet blockYMask = buf.readBitSet(); - BitSet emptySkyYMask = buf.readBitSet(); - BitSet emptyBlockYMask = buf.readBitSet(); - List skyUpdates = buf.readByteArrayList(2048); - List blockUpdates = buf.readByteArrayList(2048); + // 客户端侧section数量很重要,不能读取此时玩家所在的真实世界,包具有滞后性 int count = player.clientSideSectionCount(); MCSection[] sections = new MCSection[count]; - FriendlyByteBuf chunkDataByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(chunkDataBytes)); - // 开始处理 - if (user.clientModEnabled()) { - for (int i = 0; i < count; i++) { - MCSection mcSection = new MCSection(user.clientBlockList(), this.blockList, this.biomeList); - mcSection.readPacket(chunkDataByteBuf); - PalettedContainer container = mcSection.blockStateContainer(); - remapBiomes(user, mcSection.biomeContainer()); - Palette palette = container.data().palette(); - if (palette.canRemap()) { - palette.remap(s -> this.modBlockStateMapper[s]); - } else { - for (int j = 0; j < 4096; j++) { - int state = container.get(j); - int newState = this.modBlockStateMapper[state]; - if (newState != state) { - container.set(j, newState); - } - } - } - sections[i] = mcSection; - } - } else { - for (int i = 0; i < count; i++) { - MCSection mcSection = new MCSection(user.clientBlockList(), this.blockList, this.biomeList); - mcSection.readPacket(chunkDataByteBuf); - PalettedContainer container = mcSection.blockStateContainer(); - remapBiomes(user, mcSection.biomeContainer()); - Palette palette = container.data().palette(); - if (palette.canRemap()) { - palette.remap(s -> this.blockStateMapper[s]); - } else { - for (int j = 0; j < 4096; j++) { - int state = container.get(j); - int newState = this.blockStateMapper[state]; - if (newState != state) { - container.set(j, newState); - } - } - } - sections[i] = mcSection; - } - } - FriendlyByteBuf newChunkDataBuf = new FriendlyByteBuf(Unpooled.buffer(chunkDataBufferSize)); + boolean hasChangedAnyBlock = false; + boolean hasGlobalPalette = false; + for (int i = 0; i < count; i++) { - sections[i].writePacket(newChunkDataBuf); - } - chunkDataBytes = newChunkDataBuf.array(); - - // 开始修改 - event.setChanged(true); - buf.clear(); - buf.writeVarInt(event.packetID()); - buf.writeInt(chunkX); - buf.writeInt(chunkZ); - if (VersionHelper.isOrAbove1_21_5()) { - buf.writeVarInt(heightmapsCount); - for (Map.Entry entry : heightmapsMap.entrySet()) { - buf.writeVarInt(entry.getKey()); - buf.writeLongArray(entry.getValue()); + MCSection mcSection = new MCSection(user.clientBlockList(), this.blockList, this.biomeList); + mcSection.readPacket(chunkDataByteBuf); + PalettedContainer container = mcSection.blockStateContainer(); + remapBiomes(user, mcSection.biomeContainer()); + Palette palette = container.data().palette(); + if (palette.canRemap()) { + if (palette.remapAndCheck(s -> this.blockStateMapper[s])) { + hasChangedAnyBlock = true; + } + } else { + hasGlobalPalette = true; + for (int j = 0; j < 4096; j++) { + int state = container.get(j); + int newState = this.blockStateMapper[state]; + if (newState != state) { + container.set(j, newState); + hasChangedAnyBlock = true; + } + } } - } else { - buf.writeNbt(heightmaps, named); + sections[i] = mcSection; + } + + // 只有被修改了,才读后续内容,并改写 + if (hasChangedAnyBlock || (this.needsDowngrade && hasGlobalPalette)) { + // 读取其他非必要信息 + int blockEntitiesDataCount = buf.readVarInt(); + List blockEntitiesData = new ArrayList<>(); + for (int i = 0; i < blockEntitiesDataCount; i++) { + byte packedXZ = buf.readByte(); + short y = buf.readShort(); + int type = buf.readVarInt(); + Tag tag = buf.readNbt(named); + BlockEntityData blockEntityData = new BlockEntityData(packedXZ, y, type, tag); + blockEntitiesData.add(blockEntityData); + } + // 光照信息 + BitSet skyYMask = buf.readBitSet(); + BitSet blockYMask = buf.readBitSet(); + BitSet emptySkyYMask = buf.readBitSet(); + BitSet emptyBlockYMask = buf.readBitSet(); + List skyUpdates = buf.readByteArrayList(2048); + List blockUpdates = buf.readByteArrayList(2048); + + // 预分配容量 + FriendlyByteBuf newChunkDataBuf = new FriendlyByteBuf(Unpooled.buffer(chunkDataBufferSize + 16)); + for (int i = 0; i < count; i++) { + sections[i].writePacket(newChunkDataBuf); + } + chunkDataBytes = newChunkDataBuf.array(); + + // 开始修改 + event.setChanged(true); + buf.clear(); + buf.writeVarInt(event.packetID()); + buf.writeInt(chunkX); + buf.writeInt(chunkZ); + if (VersionHelper.isOrAbove1_21_5()) { + buf.writeVarInt(heightmapsCount); + for (Map.Entry entry : heightmapsMap.entrySet()) { + buf.writeVarInt(entry.getKey()); + buf.writeLongArray(entry.getValue()); + } + } else { + buf.writeNbt(heightmaps, named); + } + buf.writeVarInt(chunkDataBytes.length); + buf.writeBytes(chunkDataBytes); + buf.writeVarInt(blockEntitiesDataCount); + for (BlockEntityData blockEntityData : blockEntitiesData) { + buf.writeByte(blockEntityData.packedXZ()); + buf.writeShort(blockEntityData.y()); + buf.writeVarInt(blockEntityData.type()); + buf.writeNbt(blockEntityData.tag(), named); + } + buf.writeBitSet(skyYMask); + buf.writeBitSet(blockYMask); + buf.writeBitSet(emptySkyYMask); + buf.writeBitSet(emptyBlockYMask); + buf.writeByteArrayList(skyUpdates); + buf.writeByteArrayList(blockUpdates); + } else { + System.out.println("没变化啊"); } - buf.writeVarInt(chunkDataBytes.length); - buf.writeBytes(chunkDataBytes); - buf.writeVarInt(blockEntitiesDataCount); - for (BlockEntityData blockEntityData : blockEntitiesData) { - buf.writeByte(blockEntityData.packedXZ()); - buf.writeShort(blockEntityData.y()); - buf.writeVarInt(blockEntityData.type()); - buf.writeNbt(blockEntityData.tag(), named); - } - buf.writeBitSet(skyYMask); - buf.writeBitSet(blockYMask); - buf.writeBitSet(emptySkyYMask); - buf.writeBitSet(emptyBlockYMask); - buf.writeByteArrayList(skyUpdates); - buf.writeByteArrayList(blockUpdates); - ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); // 记录加载的区块 player.addTrackedChunk(chunkPos.longKey, new ChunkStatus()); + // 生成方块实体 CEWorld ceWorld = BukkitWorldManager.instance().getWorld(player.world().uuid()); CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(chunkPos.longKey); if (ceChunk != null) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java index d3d0c0941..5719f4449 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/BlockEntityTypes.java @@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.registry.WritableRegistry; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.ResourceKey; -public class BlockEntityTypes { +public abstract class BlockEntityTypes { public static BlockEntityType register(Key id, BlockEntity.Factory factory) { BlockEntityType type = new BlockEntityType<>(id, factory); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java index 77841caa8..910c56f56 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/entity/render/element/BlockEntityElementConfigs.java @@ -10,7 +10,7 @@ import net.momirealms.craftengine.core.util.ResourceKey; import java.util.Map; import java.util.Optional; -public class BlockEntityElementConfigs { +public abstract class BlockEntityElementConfigs { public static final Key ITEM_DISPLAY = Key.of("craftengine:item_display"); public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/Int2ObjectBiMap.java b/core/src/main/java/net/momirealms/craftengine/core/util/Int2ObjectBiMap.java index 5eeb3c669..a7c87d8d2 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/Int2ObjectBiMap.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/Int2ObjectBiMap.java @@ -35,15 +35,36 @@ public class Int2ObjectBiMap implements IndexedIterable { public void remapValues(Function function) { for (int i = 0; i < values.length; i++) { - if (values[i] == null) break; - values[i] = function.apply(values[i]); + K prev = values[i]; + if (prev == null) break; + values[i] = function.apply(prev); } for (int i = 0; i < idToValues.length; i++) { - if (idToValues[i] == null) break; - idToValues[i] = function.apply(idToValues[i]); + K prev = idToValues[i]; + if (prev == null) break; + idToValues[i] = function.apply(prev); } } + public boolean remapValuesAndCheck(Function function) { + boolean changed = false; + for (int i = 0; i < values.length; i++) { + K prev = values[i]; + if (prev == null) break; + K apply = function.apply(prev); + values[i] = apply; + if (apply != prev) { + changed = true; + } + } + for (int i = 0; i < idToValues.length; i++) { + K prev = idToValues[i]; + if (prev == null) break; + idToValues[i] = function.apply(prev); + } + return changed; + } + public static Int2ObjectBiMap create(int expectedSize) { return new Int2ObjectBiMap<>((int) ((float) expectedSize / LOAD_FACTOR)); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ArrayPalette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ArrayPalette.java index 94b254c65..6758fe1ee 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ArrayPalette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/ArrayPalette.java @@ -108,11 +108,27 @@ public class ArrayPalette implements Palette { @Override public void remap(Function function) { for (int i = 0; i < this.array.length; i++) { - if (this.array[i] == null) return; - this.array[i] = function.apply(this.array[i]); + T prev = this.array[i]; + if (prev == null) return; + this.array[i] = function.apply(prev); } } + @Override + public boolean remapAndCheck(Function function) { + boolean changed = false; + for (int i = 0; i < this.array.length; i++) { + T prev = this.array[i]; + if (prev == null) return changed; + T newV = function.apply(prev); + this.array[i] = newV; + if (newV != prev) { + changed = true; + } + } + return changed; + } + @Override public boolean canRemap() { return true; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/BiMapPalette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/BiMapPalette.java index a9d27c98b..02d0b190d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/BiMapPalette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/BiMapPalette.java @@ -106,6 +106,11 @@ public class BiMapPalette implements Palette { this.map.remapValues(function); } + @Override + public boolean remapAndCheck(Function function) { + return this.map.remapValuesAndCheck(function); + } + @Override public boolean canRemap() { return true; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/IdListPalette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/IdListPalette.java index 2775ccc51..51ee07710 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/IdListPalette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/IdListPalette.java @@ -63,6 +63,11 @@ public class IdListPalette implements Palette { return false; } + @Override + public boolean remapAndCheck(Function function) { + return false; + } + @Override public void readPacket(FriendlyByteBuf buf) { } diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/Palette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/Palette.java index c15620008..f190afaee 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/Palette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/Palette.java @@ -25,6 +25,8 @@ public interface Palette { void remap(Function function); + boolean remapAndCheck(Function function); + boolean canRemap(); interface Factory { diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java index 9ce501846..302661f9c 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/PalettedContainer.java @@ -23,7 +23,6 @@ import java.util.function.Predicate; import java.util.stream.LongStream; public class PalettedContainer implements PaletteResizeListener, ReadableContainer { - public static boolean NEED_DOWNGRADE = true; private static final BiConsumer RAW_DATA_WRITER = VersionHelper.isOrAbove1_21_5() ? (FriendlyByteBuf::writeFixedSizeLongArray) : (FriendlyByteBuf::writeLongArray); private static final BiConsumer RAW_DATA_READER = VersionHelper.isOrAbove1_21_5() ? @@ -75,10 +74,7 @@ public class PalettedContainer implements PaletteResizeListener, ReadableC return false; } - public PalettedContainer downgradeTo(IndexedIterable idList) { - if (!NEED_DOWNGRADE) { - return this; - } + public PalettedContainer getClientCompatiblePalettedContainer(IndexedIterable idList) { Palette palette = this.data.palette; if (!(palette instanceof IdListPalette idListPalette)) { return this; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/SingularPalette.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/SingularPalette.java index 35736ff09..5a872dacf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/SingularPalette.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/SingularPalette.java @@ -73,6 +73,13 @@ public class SingularPalette implements Palette { this.entry = function.apply(this.entry); } + @Override + public boolean remapAndCheck(Function function) { + T previous = this.entry; + this.entry = function.apply(previous); + return previous == this.entry; + } + @Override public boolean canRemap() { return true; diff --git a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java index 81948b867..0837a559d 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java +++ b/core/src/main/java/net/momirealms/craftengine/core/world/chunk/packet/MCSection.java @@ -26,7 +26,7 @@ public class MCSection { public void writePacket(FriendlyByteBuf buf) { buf.writeShort(this.nonEmptyBlockCount); - this.serverBlockStateContainer.downgradeTo(this.clientBlockStateList).writePacket(buf); + this.serverBlockStateContainer.getClientCompatiblePalettedContainer(this.clientBlockStateList).writePacket(buf); this.biomeContainer.writePacket(buf); }