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 18e730594..7d0e94b2b 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 @@ -6,6 +6,9 @@ import net.momirealms.craftengine.bukkit.block.behavior.UnsafeCompositeBlockBeha import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.injector.BlockGenerator; +import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager; +import net.momirealms.craftengine.bukkit.plugin.network.payload.PayloadHelper; +import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.VisualBlockStatePacket; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; @@ -63,6 +66,8 @@ public final class BukkitBlockManager extends AbstractBlockManager { private Set missingHitSounds = Set.of(); private Set missingStepSounds = Set.of(); private Set missingInteractSoundBlocks = Set.of(); + // 缓存的VisualBlockStatePacket + private VisualBlockStatePacket cachedVisualBlockStatePacket; public BukkitBlockManager(BukkitCraftEngine plugin) { super(plugin, RegistryUtils.currentBlockRegistrySize(), Config.serverSideBlocks()); @@ -122,6 +127,11 @@ public final class BukkitBlockManager extends AbstractBlockManager { public void delayedLoad() { this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 重置方块映射表 super.delayedLoad(); + this.cachedVisualBlockStatePacket = VisualBlockStatePacket.create(); + for (BukkitServerPlayer player : BukkitNetworkManager.instance().onlineUsers()) { + if (!player.clientModEnabled()) continue; + PayloadHelper.sendData(player, this.cachedVisualBlockStatePacket); + } } @Override @@ -358,6 +368,10 @@ public final class BukkitBlockManager extends AbstractBlockManager { return this.cachedUpdateTagsPacket; } + public VisualBlockStatePacket cachedVisualBlockStatePacket() { + return this.cachedVisualBlockStatePacket; + } + private void markVanillaNoteBlocks() { try { Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK)); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java index 0fee4fb84..2ee971f74 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/PayloadHelper.java @@ -4,6 +4,7 @@ import io.netty.buffer.Unpooled; import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.CancelBlockUpdatePacket; import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.ClientBlockStateSizePacket; import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.ClientCustomBlockPacket; +import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.VisualBlockStatePacket; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.network.ModPacket; import net.momirealms.craftengine.core.plugin.network.NetWorkUser; @@ -20,6 +21,7 @@ public class PayloadHelper { registerDataType(ClientCustomBlockPacket.TYPE, ClientCustomBlockPacket.CODEC); registerDataType(CancelBlockUpdatePacket.TYPE, CancelBlockUpdatePacket.CODEC); registerDataType(ClientBlockStateSizePacket.TYPE, ClientBlockStateSizePacket.CODEC); + registerDataType(VisualBlockStatePacket.TYPE, VisualBlockStatePacket.CODEC); } public static void registerDataType(ResourceKey> key, NetworkCodec codec) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java index 257399aae..eee81789d 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/ClientCustomBlockPacket.java @@ -4,10 +4,13 @@ package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TranslationArgument; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.network.payload.PayloadHelper; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.paper.PaperReflections; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.RegistryUtils; import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.network.ModPacket; @@ -17,7 +20,7 @@ import net.momirealms.craftengine.core.registry.BuiltInRegistries; import net.momirealms.craftengine.core.util.*; import org.bukkit.entity.Player; -public record ClientCustomBlockPacket(int size) implements ModPacket { +public record ClientCustomBlockPacket(int vanillaSize, int currentSize) implements ModPacket { public static final ResourceKey> TYPE = ResourceKey.create( BuiltInRegistries.MOD_PACKET.key().location(), Key.of("craftengine", "client_custom_block") ); @@ -27,11 +30,12 @@ public record ClientCustomBlockPacket(int size) implements ModPacket { ); private ClientCustomBlockPacket(FriendlyByteBuf buf) { - this(buf.readInt()); + this(buf.readInt(), buf.readInt()); } private void encode(FriendlyByteBuf buf) { - buf.writeInt(this.size); + buf.writeInt(this.vanillaSize); + buf.writeInt(this.currentSize); } @Override @@ -42,17 +46,27 @@ public record ClientCustomBlockPacket(int size) implements ModPacket { @Override public void handle(NetWorkUser user) { if (user.clientModEnabled()) return; // 防止滥用 - int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); - if (this.size != serverBlockRegistrySize) { + int vanillaBlockRegistrySize = BlockStateUtils.vanillaBlockStateCount(); + if (this.vanillaSize != vanillaBlockRegistrySize) { user.kick(Component.translatable( - "disconnect.craftengine.block_registry_mismatch", - TranslationArgument.numeric(this.size), + "disconnect.craftengine.vanilla_block_registry_mismatch", + TranslationArgument.numeric(this.vanillaSize), + TranslationArgument.numeric(vanillaBlockRegistrySize) + )); + return; + } + int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize(); + if (this.currentSize != serverBlockRegistrySize) { + user.kick(Component.translatable( + "disconnect.craftengine.current_block_registry_mismatch", + TranslationArgument.numeric(this.currentSize), TranslationArgument.numeric(serverBlockRegistrySize) )); return; } user.setClientModState(true); - user.setClientBlockList(new IntIdentityList(this.size)); + user.setClientBlockList(new IntIdentityList(this.currentSize)); + PayloadHelper.sendData(user, BukkitBlockManager.instance().cachedVisualBlockStatePacket()); if (!VersionHelper.isOrAbove1_20_2()) { // 因为旧版本没有配置阶段需要重新发送区块 try { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/VisualBlockStatePacket.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/VisualBlockStatePacket.java new file mode 100644 index 000000000..75984e6c5 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/payload/protocol/VisualBlockStatePacket.java @@ -0,0 +1,47 @@ +package net.momirealms.craftengine.bukkit.plugin.network.payload.protocol; + +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.RegistryUtils; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.plugin.config.Config; +import net.momirealms.craftengine.core.plugin.network.ModPacket; +import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec; +import net.momirealms.craftengine.core.registry.BuiltInRegistries; +import net.momirealms.craftengine.core.util.FriendlyByteBuf; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.ResourceKey; + +public record VisualBlockStatePacket(int[] data) implements ModPacket { + public static final ResourceKey> TYPE = ResourceKey.create( + BuiltInRegistries.MOD_PACKET.key().location(), Key.of("craftengine", "visual_block_state") + ); + public static final NetworkCodec CODEC = ModPacket.codec( + VisualBlockStatePacket::encode, + VisualBlockStatePacket::new + ); + + private VisualBlockStatePacket(FriendlyByteBuf buf) { + this(buf.readVarIntArray()); + } + + private void encode(FriendlyByteBuf buf) { + buf.writeVarIntArray(this.data); + } + + @Override + public ResourceKey> type() { + return TYPE; + } + + public static VisualBlockStatePacket create() { + int[] mappings = new int[RegistryUtils.currentBlockRegistrySize()]; + for (int i = 0; i < Config.serverSideBlocks(); i++) { + ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(i + BlockStateUtils.vanillaBlockStateCount()); + if (state.isEmpty()) continue; + mappings[state.customBlockState().registryId()] = state.vanillaBlockState().registryId(); + } + return new VisualBlockStatePacket(mappings); + } + +}