9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-28 19:39:11 +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
)
);
}