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:
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user