9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2026-01-03 22:26:16 +00:00

fix(network): 修复mod用户可能收到原版remap方块的问题并且优化性能

This commit is contained in:
jhqwqmc
2025-03-26 22:13:11 +08:00
parent c92bf94422
commit 212a327142
5 changed files with 124 additions and 89 deletions

View File

@@ -10,7 +10,6 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.network.impl.*;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.util.RegistryUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
@@ -19,10 +18,7 @@ import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.ListMonitor;
import net.momirealms.craftengine.core.util.TriConsumer;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.ChunkPos;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -30,17 +26,15 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRegisterChannelEvent;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
public class BukkitNetworkManager implements NetworkManager, Listener {
public class BukkitNetworkManager implements NetworkManager, Listener, PluginMessageListener {
private static BukkitNetworkManager instance;
private static final Map<Class<?>, TriConsumer<NetWorkUser, NMSPacketEvent, Object>> nmsPacketFunctions = new HashMap<>();
private static final Map<Integer, BiConsumer<NetWorkUser, ByteBufPacketEvent>> byteBufPacketFunctions = new HashMap<>();
@@ -135,6 +129,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener {
registerNMSPacketConsumer(PacketConsumers.RENAME_ITEM, Reflections.clazz$ServerboundRenameItemPacket);
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, Reflections.clazz$ServerboundEditBookPacket);
registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD, Reflections.clazz$ServerboundCustomPayloadPacket);
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
registerByteBufPacketConsumer(PacketConsumers.LEVEL_PARTICLE, this.packetIds.clientboundLevelParticlesPacket());
@@ -165,61 +160,21 @@ public class BukkitNetworkManager implements NetworkManager, Listener {
this.onlineUsers.remove(player.getUniqueId());
}
// for mod
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerRegisterChannel(PlayerRegisterChannelEvent event) {
if (!event.getChannel().equals(MOD_CHANNEL)) return;
Player player = event.getPlayer();
NetWorkUser user = getUser(player);
if (user == null) return;
user.setClientModState(true);
int blockRegistrySize = RegistryUtils.currentBlockRegistrySize();
byte[] payload = ("cp:" + blockRegistrySize).getBytes(StandardCharsets.UTF_8);
player.sendPluginMessage(plugin.bootstrap(), MOD_CHANNEL, payload);
plugin.scheduler().executeAsync(() -> sendClientModPlayerChunk(player, user));
}
private void sendClientModPlayerChunk(Player player, NetWorkUser user) {
try {
Chunk centerChunk = player.getLocation().getChunk();
World world = centerChunk.getWorld();
int centerX = centerChunk.getX();
int centerZ = centerChunk.getZ();
List<Object> packets = new ArrayList<>();
for (int xOffset = -1; xOffset <= 1; xOffset++) {
for (int zOffset = -1; zOffset <= 1; zOffset++) {
int targetX = centerX + xOffset;
int targetZ = centerZ + zOffset;
if (!world.isChunkLoaded(targetX, targetZ)) continue;
Chunk chunk = world.getChunkAt(targetX, targetZ);
Object worldServer = Reflections.field$CraftChunk$worldServer.get(chunk);
Object chunkSource = Reflections.field$ServerLevel$chunkSource.get(worldServer);
Object levelChunk = Reflections.method$ServerChunkCache$getChunkAtIfLoadedMainThread.invoke(chunkSource, targetX, targetZ);
if (levelChunk == null) continue;
long chunkKey = ChunkPos.asLong(targetX, targetZ);
Object chunkHolder = Reflections.method$ServerChunkCache$getVisibleChunkIfPresent.invoke(chunkSource, chunkKey);
if (chunkHolder == null) continue;
Object lightEngine = Reflections.field$ChunkHolder$lightEngine.get(chunkHolder);
Object packet = Reflections.constructor$ClientboundLevelChunkWithLightPacket.newInstance(levelChunk, lightEngine, null, null);
packets.add(packet);
}
}
sendPackets(user, packets);
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to send chunk correction packet", e);
}
}
@Override
public Collection<BukkitServerPlayer> onlineUsers() {
return onlineUsers.values();
}
@Override
// 保留仅注册入频道用
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] message) {}
@Override
public void init() {
if (init) return;
try {
plugin.bootstrap().getServer().getMessenger().registerOutgoingPluginChannel(plugin.bootstrap(), "craftengine:payload");
plugin.bootstrap().getServer().getMessenger().registerIncomingPluginChannel(plugin.bootstrap(), MOD_CHANNEL, this);
plugin.bootstrap().getServer().getMessenger().registerOutgoingPluginChannel(plugin.bootstrap(), MOD_CHANNEL);
Object server = Reflections.method$MinecraftServer$getServer.invoke(null);
Object serverConnection = Reflections.field$MinecraftServer$connection.get(server);
@SuppressWarnings("unchecked")

View File

@@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.plugin.network;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.ints.IntList;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslationArgument;
import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent;
import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent;
@@ -19,6 +21,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.chunk.Palette;
@@ -33,6 +36,7 @@ import org.bukkit.util.RayTraceResult;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -896,4 +900,43 @@ public class PacketConsumers {
callback.accept(new String(newCodepoints, 0, newCodepoints.length));
}
}
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> CUSTOM_PAYLOAD = (user, event, packet) -> {
try {
if (!VersionHelper.isVersionNewerThan1_20_5()) return;
Object payload = Reflections.field$ServerboundCustomPayloadPacket$payload.get(packet);
if (payload.getClass().equals(Reflections.clazz$DiscardedPayload)) {
Object type = Reflections.method$CustomPacketPayload$type.invoke(payload);
Object id = Reflections.method$CustomPacketPayload$Type$id.invoke(type);
String channel = id.toString();
if (!channel.equals(NetworkManager.MOD_CHANNEL)) return;
ByteBuf buf = (ByteBuf) Reflections.method$DiscardedPayload$data.invoke(payload);
byte[] data = new byte[buf.readableBytes()];
buf.readBytes(data);
String decodeData = new String(data, StandardCharsets.UTF_8);
if (!decodeData.endsWith("init")) return;
int firstColon = decodeData.indexOf(':');
if (firstColon == -1) return;
int secondColon = decodeData.indexOf(':', firstColon + 1);
if (secondColon == -1) return;
int clientBlockRegistrySize = Integer.parseInt(decodeData.substring(firstColon + 1, secondColon));
int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize();
if (clientBlockRegistrySize != serverBlockRegistrySize) {
Object kickPacket = Reflections.constructor$ClientboundDisconnectPacket.newInstance(
ComponentUtils.adventureToMinecraft(
Component.translatable(
"disconnect.craftengine.block_registry_mismatch",
TranslationArgument.numeric(clientBlockRegistrySize),
TranslationArgument.numeric(serverBlockRegistrySize)
)
)
);
user.nettyChannel().writeAndFlush(kickPacket);
user.nettyChannel().disconnect();
}
user.setClientModState(true);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to handle ServerboundCustomPayloadPacket", e);
}
};
}

View File

@@ -5540,4 +5540,62 @@ public class Reflections {
? ReflectionUtils.getMethod(clazz$BlockBehaviour, String.class)
: ReflectionUtils.getMethod(clazz$Block, String.class)
);
public static final Class<?> clazz$ServerboundCustomPayloadPacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundCustomPayloadPacket"),
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInCustomPayload")
)
);
// 1.20.2+
public static final Class<?> clazz$CustomPacketPayload =
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.common.custom.CustomPacketPayload")
);
// 1.20.5+
public static final Class<?> clazz$CustomPacketPayload$Type =
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.common.custom.CustomPacketPayload$Type")
);
// 1.20.2+
public static final Field field$ServerboundCustomPayloadPacket$payload = Optional.ofNullable(clazz$CustomPacketPayload)
.map(it -> ReflectionUtils.getDeclaredField(clazz$ServerboundCustomPayloadPacket, it, 0))
.orElse(null);
// 1.20.2+
public static final Class<?> clazz$DiscardedPayload =
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.common.custom.DiscardedPayload")
);
// 1.20.5+
public static final Method method$CustomPacketPayload$type = Optional.ofNullable(clazz$CustomPacketPayload$Type)
.map(it -> ReflectionUtils.getMethod(clazz$CustomPacketPayload, it))
.orElse(null);
// 1.20.5+
public static final Method method$CustomPacketPayload$Type$id = Optional.ofNullable(clazz$CustomPacketPayload$Type)
.map(it -> ReflectionUtils.getMethod(it, clazz$ResourceLocation))
.orElse(null);
// 1.20.5+
public static final Method method$DiscardedPayload$data = Optional.ofNullable(clazz$DiscardedPayload)
.map(it -> ReflectionUtils.getMethod(it, ByteBuf.class))
.orElse(null);
public static final Class<?> clazz$ClientboundDisconnectPacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ClientboundDisconnectPacket"),
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutKickDisconnect")
)
);
public static final Constructor<?> constructor$ClientboundDisconnectPacket = requireNonNull(
ReflectionUtils.getConstructor(
clazz$ClientboundDisconnectPacket, clazz$Component
)
);
}

View File

@@ -2,38 +2,33 @@ package net.momirealms.craftengine.fabric.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientConfigurationNetworking;
import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.block.Block;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.color.world.BiomeColors;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.ClientConfigurationNetworkHandler;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.network.DisconnectionInfo;
import net.minecraft.registry.Registries;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.world.biome.FoliageColors;
import net.momirealms.craftengine.fabric.client.blocks.CustomBlock;
import net.momirealms.craftengine.fabric.client.config.ModConfig;
import net.momirealms.craftengine.fabric.client.network.CraftEnginePayload;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
public class CraftEngineFabricModClient implements ClientModInitializer {
public static final String MOD_ID = "craftengine";
@Override
public void onInitializeClient() {
PayloadTypeRegistry.playS2C().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC);
initChannel(MinecraftClient.getInstance().getNetworkHandler());
PayloadTypeRegistry.configurationS2C().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC);
PayloadTypeRegistry.configurationC2S().register(CraftEnginePayload.ID, CraftEnginePayload.CODEC);
registerRenderLayer();
ClientPlayConnectionEvents.INIT.register((handler, client) -> initChannel(handler));
ClientConfigurationConnectionEvents.START.register(CraftEngineFabricModClient::initChannel);
}
public static void registerRenderLayer() {
@@ -62,30 +57,15 @@ public class CraftEngineFabricModClient implements ClientModInitializer {
);
}
private static void initChannel(ClientPlayNetworkHandler handler) {
private static void initChannel(ClientConfigurationNetworkHandler handler, MinecraftClient client) {
if (ModConfig.enableNetwork) {
registerChannel(handler);
} else {
ClientPlayNetworking.unregisterGlobalReceiver(CraftEnginePayload.ID.id());
ClientConfigurationNetworking.unregisterGlobalReceiver(CraftEnginePayload.ID);
}
}
private static void registerChannel(ClientPlayNetworkHandler handler) {
ClientPlayNetworking.registerGlobalReceiver(CraftEnginePayload.ID, (payload, context) -> {
byte[] data = payload.data();
String decoded = new String(data, StandardCharsets.UTF_8);
if (decoded.startsWith("cp:")) {
int blockRegistrySize = Integer.parseInt(decoded.substring(3));
if (Block.STATE_IDS.size() != blockRegistrySize) {
handler.getConnection().disconnect(
new DisconnectionInfo(
Text.translatable("disconnect.craftengine.block_registry_mismatch", Block.STATE_IDS.size(), blockRegistrySize),
Optional.of(FabricLoader.getInstance().getConfigDir().resolve("craft-engine-fabric-mod/mappings.yml")),
Optional.of(URI.create("https://github.com/Xiao-MoMi/craft-engine"))
)
);
}
}
});
private static void registerChannel(ClientConfigurationNetworkHandler handler) {
ClientConfigurationNetworking.send(new CraftEnginePayload((":" + Block.STATE_IDS.size() + ":init").getBytes(StandardCharsets.UTF_8)));
}
}

View File

@@ -1,6 +1,6 @@
package net.momirealms.craftengine.fabric.client.network;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.packet.CustomPayload;
import net.minecraft.util.Identifier;
@@ -8,10 +8,9 @@ import net.minecraft.util.Identifier;
public record CraftEnginePayload(byte[] data) implements CustomPayload {
public static final Identifier CRAFTENGINE_PAYLOAD = Identifier.of("craftengine", "payload");
public static final Id<CraftEnginePayload> ID = new Id<>(CraftEnginePayload.CRAFTENGINE_PAYLOAD);
public static final PacketCodec<RegistryByteBuf, CraftEnginePayload> CODEC = PacketCodec.tuple(
new ByteArrayCodec(), CraftEnginePayload::data,
CraftEnginePayload::new
);
public static final PacketCodec<PacketByteBuf, CraftEnginePayload> CODEC = PacketCodec.of(
(payload, byteBuf) -> byteBuf.writeByteArray(payload.data()),
buf -> new CraftEnginePayload(buf.readByteArray()));
@Override
public Id<? extends CustomPayload> getId() {