From 3f74b5e4d7a10f41215845d4d77eead17b653b0c Mon Sep 17 00:00:00 2001 From: Gungun974 Date: Mon, 3 Feb 2025 10:00:00 +0400 Subject: [PATCH 1/7] Add helper to know if we are on server, client are singleplayer --- .../halplibe/helper/EnvironmentHelper.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/turniplabs/halplibe/helper/EnvironmentHelper.java diff --git a/src/main/java/turniplabs/halplibe/helper/EnvironmentHelper.java b/src/main/java/turniplabs/halplibe/helper/EnvironmentHelper.java new file mode 100644 index 0000000..324696c --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/EnvironmentHelper.java @@ -0,0 +1,23 @@ +package turniplabs.halplibe.helper; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.Global; + +public class EnvironmentHelper { + public static boolean isServerEnvironment() { + return Global.isServer; + } + + public static boolean isSinglePlayer() { + if (Global.isServer) { + return false; + } + + return !Minecraft.getMinecraft().isMultiplayerWorld(); + } + + public static boolean isClientWorld() { + return !isSinglePlayer() && !isServerEnvironment(); + } + +} From 85e33a1da59777e7550299a16074f8f77fdcfa0d Mon Sep 17 00:00:00 2001 From: Gungun974 Date: Mon, 3 Feb 2025 10:01:09 +0400 Subject: [PATCH 2/7] Add a universal NetworkMessage using a single packet and compatible both with Singleplayer and ClientServer --- .../java/turniplabs/halplibe/HalpLibe.java | 3 + .../helper/network/NetworkHandler.java | 157 +++++++++ .../helper/network/NetworkMessage.java | 42 +++ .../helper/network/UniversalPacket.java | 298 ++++++++++++++++++ .../PacketHandlerServerAccessor.java | 12 + src/main/resources/halplibe.mixins.json | 3 +- 6 files changed, 514 insertions(+), 1 deletion(-) create mode 100644 src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java create mode 100644 src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java create mode 100644 src/main/java/turniplabs/halplibe/helper/network/UniversalPacket.java create mode 100644 src/main/java/turniplabs/halplibe/mixin/accessors/PacketHandlerServerAccessor.java diff --git a/src/main/java/turniplabs/halplibe/HalpLibe.java b/src/main/java/turniplabs/halplibe/HalpLibe.java index b12ae7d..da627cf 100644 --- a/src/main/java/turniplabs/halplibe/HalpLibe.java +++ b/src/main/java/turniplabs/halplibe/HalpLibe.java @@ -13,6 +13,8 @@ import net.minecraft.core.item.Item; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import turniplabs.halplibe.helper.NetworkHelper; +import turniplabs.halplibe.helper.network.NetworkHandler; import turniplabs.halplibe.util.ModelEntrypoint; import turniplabs.halplibe.util.TomlConfigHandler; import turniplabs.halplibe.util.toml.Toml; @@ -38,6 +40,7 @@ public static String addModId(String modId, String name) { @Override public void onInitialize() { + NetworkHandler.setup(); LOGGER.info("HalpLibe initialized."); } diff --git a/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java new file mode 100644 index 0000000..3e1b0ac --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java @@ -0,0 +1,157 @@ +package turniplabs.halplibe.helper.network; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.core.entity.player.Player; +import net.minecraft.core.net.packet.Packet; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.entity.player.PlayerServer; +import turniplabs.halplibe.helper.EnvironmentHelper; +import turniplabs.halplibe.helper.NetworkHelper; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +public final class NetworkHandler +{ + private static final Map> packetReaders = new HashMap<>(); + private static final Map, Integer> packetIds = new HashMap<>(); + + private NetworkHandler() + { + } + + public static void setup() + { + NetworkHelper.register ( UniversalPacket.class, true, true ); + } + + public static void receiveUniversalPacket(NetworkMessage.NetworkContext context, UniversalPacket buffer ) + { + int type = buffer.readByte(); + packetReaders.get( type ) + .accept( context, buffer ); + } + + /** + * Register a NetworkMessage, and a thread-unsafe handler for it. + * + * @param The type of the NetworkMessage to send. + * @param id The identifier for this message type + * @param factory The factory for this type of message. + */ + @SuppressWarnings({"unused"}) + public static void registerNetworkMessage( int id, Supplier factory ) + { + registerNetworkMessage( id, getType( factory ), buf -> { + T instance = factory.get(); + instance.fromBytes( buf ); + return instance; + } ); + } + + /** + * Register a NetworkMessage, and a thread-unsafe handler for it. + * + * @param The type of the NetworkMessage to send. + * @param type The class of the type of message to send. + * @param id The identifier for this message type + * @param decoder The factory for this type of message. + */ + private static void registerNetworkMessage( int id, Class type, Function decoder ) + { + packetIds.put( type, id ); + packetReaders.put( id, ( context, buf ) -> { + T result = decoder.apply( buf ); + result.handle(context); + } ); + } + + @SuppressWarnings( "unchecked" ) + private static Class getType( Supplier supplier ) + { + return (Class) supplier.get() + .getClass(); + } + + private static UniversalPacket encode(NetworkMessage message ) + { + UniversalPacket buf = new UniversalPacket(); + buf.writeByte( packetIds.get( message.getClass() ) ); + message.toBytes( buf ); + return buf; + } + + @Environment(EnvType.CLIENT) + private static void sendToPlayerLocal(NetworkMessage message) + { + message.handle(new NetworkMessage.NetworkContext(Minecraft.getMinecraft().thePlayer)); + } + + @Environment(EnvType.SERVER) + private static void sendToPlayerServer(Player player, NetworkMessage message ) + { + ((PlayerServer)player).playerNetServerHandler.sendPacket(encode(message)); + } + + /** + * Send a NetworkMessage to a specific Player from the server + * If we are in SinglePlayer this will skip encoding and directly call the message handle + */ + @SuppressWarnings({"unused"}) + public static void sendToPlayer(Player player, NetworkMessage message ) + { + if (!EnvironmentHelper.isServerEnvironment()){ + sendToPlayerLocal(message); + return; + } + sendToPlayerServer(player, message); + } + + /** + * Send a NetworkMessage to all Players from the server + * If we are in SinglePlayer this will skip encoding and directly call the message handle + */ + @SuppressWarnings({"unused"}) + public static void sendToAllPlayers( NetworkMessage packet ) + { + if (!EnvironmentHelper.isServerEnvironment()){ + sendToPlayerLocal(packet); + return; + } + MinecraftServer.getInstance().playerList.sendPacketToAllPlayers(encode(packet)); + } + + /** + * Send a NetworkMessage to the Server from the player + * If we are in SinglePlayer this will skip encoding and directly call the message handle + */ + @SuppressWarnings({"unused"}) + @Environment( EnvType.CLIENT ) + public static void sendToServer( NetworkMessage packet ) + { + if (EnvironmentHelper.isSinglePlayer()){ + sendToPlayerLocal(packet); + return; + } + Minecraft.getMinecraft().getSendQueue().addToSendQueue(encode(packet)); + } + + /** + * Send a NetworkMessage to all Players around a block from the server + * If we are in SinglePlayer this will skip encoding and directly call the message handle + */ + @SuppressWarnings({"unused"}) + public static void sendToAllAround(NetworkMessage packet, double x, double y, double z, double radius, int dimension ) + { + if (!EnvironmentHelper.isServerEnvironment()){ + sendToPlayerLocal(packet); + return; + } + MinecraftServer.getInstance().playerList.sendPacketToPlayersAroundPoint(x, y, z, radius, dimension, encode(packet)); + } +} \ No newline at end of file diff --git a/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java b/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java new file mode 100644 index 0000000..3f1b1c6 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java @@ -0,0 +1,42 @@ +package turniplabs.halplibe.helper.network; + +import net.minecraft.core.entity.player.Player; + +import javax.annotation.Nonnull; + +public interface NetworkMessage { + /** + * Write this packet to a buffer. + * This may be called on any thread, so this should be a pure operation. + * + * @param packet The packet to write data to. + */ + void toBytes( @Nonnull UniversalPacket packet ); + + /** + * Read this packet from a buffer. + * This may be called on any thread, so this should be a pure operation. + * + * @param buf The packet to read data from. + */ + void fromBytes( @Nonnull UniversalPacket buf ); + + /** + * Handle this {@link NetworkMessage}. + * + * @param context An intermediary representation of Packet handler common on both Client and Server environment. + */ + void handle(NetworkContext context); + + class NetworkContext { + /** + * The player that send the NetworkPacket to the handle + */ + public Player player; + + public NetworkContext(Player player) { + this.player = player; + } + } + +} diff --git a/src/main/java/turniplabs/halplibe/helper/network/UniversalPacket.java b/src/main/java/turniplabs/halplibe/helper/network/UniversalPacket.java new file mode 100644 index 0000000..62c2656 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/network/UniversalPacket.java @@ -0,0 +1,298 @@ +package turniplabs.halplibe.helper.network; + +import com.mojang.nbt.NbtIo; +import com.mojang.nbt.tags.CompoundTag; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.core.net.handler.PacketHandler; +import net.minecraft.core.net.packet.Packet; +import org.jetbrains.annotations.NotNull; +import turniplabs.halplibe.helper.EnvironmentHelper; +import turniplabs.halplibe.mixin.accessors.PacketHandlerServerAccessor; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +/** + * UniversalPacket is a general purpose packet made to transport multiple message into the same PacketType + * This work similar as DataInput/DataOutput, netty ByteBuf or modern Minecraft PacketByteBuf + */ +public class UniversalPacket extends Packet { + private byte[] buffer; + private int writeIndex; + private int readIndex; + + public UniversalPacket() { + this.buffer = new byte[0]; + this.writeIndex = 0; + this.readIndex = 0; + } + + @Deprecated + public void read(DataInputStream dis) throws IOException { + final int length = dis.readInt(); + buffer = new byte[length]; + writeIndex = dis.read(buffer, 0, length); + } + + /** + * If you want to write the UniversalPacket content to a DataOutputStream, use rawWrite instead + * since this method add an extra 4 bytes to every packet + */ + @Deprecated + public void write(DataOutputStream dos) throws IOException { + dos.writeInt(this.buffer.length); + dos.write(this.buffer); + } + + @SuppressWarnings("unused") + public void rawWrite(DataOutputStream dos) throws IOException { + dos.write(this.buffer); + } + + public void handlePacket(PacketHandler packetHandler) { + if (EnvironmentHelper.isServerEnvironment()) { + handlePacketServer(packetHandler); + return; + } + handlePacketClient(); + } + + @Environment(EnvType.SERVER) + private void handlePacketServer(PacketHandler packetHandler) { + NetworkHandler.receiveUniversalPacket(new NetworkMessage.NetworkContext(( + (PacketHandlerServerAccessor)packetHandler).getPlayerEntity() + ), this); + } + + @Environment(EnvType.CLIENT) + private void handlePacketClient() { + NetworkHandler.receiveUniversalPacket(new NetworkMessage.NetworkContext( + Minecraft.getMinecraft().thePlayer + ), this); + } + + public int getEstimatedSize() { + return buffer.length; + } + + @SuppressWarnings("unused") + public void writeByte(byte value) { + ensureCapacity(1); + buffer[writeIndex++] = value; + } + + @SuppressWarnings("unused") + public void writeByte(int value) { + writeByte((byte) value); + } + + @SuppressWarnings("unused") + public byte readByte() { + ensureReadable(1); + return buffer[readIndex++]; + } + + @SuppressWarnings("unused") + public void writeBytes(int... values) { + ensureCapacity(values.length); + for (int value : values) { + buffer[writeIndex++] = (byte) value; + } + } + + @SuppressWarnings("unused") + public void writeBytes(byte... values) { + ensureCapacity(values.length); + for (int value : values) { + buffer[writeIndex++] = (byte) value; + } + } + + @SuppressWarnings("unused") + public void readBytes(byte[] destination, int length) { + if (length > destination.length) { + throw new IllegalArgumentException(""); + } + ensureReadable(length); + System.arraycopy(buffer, readIndex, destination, 0, length); + readIndex += length; + } + + @SuppressWarnings("unused") + public void writeInt(int value) { + ensureCapacity(4); + buffer[writeIndex++] = (byte) (value >> 24); + buffer[writeIndex++] = (byte) (value >> 16); + buffer[writeIndex++] = (byte) (value >> 8); + buffer[writeIndex++] = (byte) value; + } + + @SuppressWarnings("unused") + public int readInt() { + ensureReadable(4); + return ((buffer[readIndex++] & 0xFF) << 24) | + ((buffer[readIndex++] & 0xFF) << 16) | + ((buffer[readIndex++] & 0xFF) << 8) | + (buffer[readIndex++] & 0xFF); + } + + @SuppressWarnings("unused") + public void writeShort(short value) { + ensureCapacity(2); + buffer[writeIndex++] = (byte) (value >> 8); + buffer[writeIndex++] = (byte) value; + } + + @SuppressWarnings("unused") + public short readShort() { + ensureReadable(2); + return (short) (((buffer[readIndex++] & 0xFF) << 8) | + (buffer[readIndex++] & 0xFF)); + } + + @SuppressWarnings("unused") + public void writeString(String value) { + byte[] stringBytes = value.getBytes(StandardCharsets.UTF_8); + writeInt(stringBytes.length); + ensureCapacity(stringBytes.length); + System.arraycopy(stringBytes, 0, buffer, writeIndex, stringBytes.length); + writeIndex += stringBytes.length; + } + + @SuppressWarnings("unused") + public String readString() { + int length = readInt(); + ensureReadable(length); + String value = new String(buffer, readIndex, length, StandardCharsets.UTF_8); + readIndex += length; + return value; + } + + @SuppressWarnings("unused") + public void writeBoolean(boolean value) { + ensureCapacity(1); + buffer[writeIndex++] = (byte) (value ? 1 : 0); + } + + @SuppressWarnings("unused") + public boolean readBoolean() { + ensureReadable(1); + return buffer[readIndex++] != 0; + } + + @SuppressWarnings("unused") + public void writeDouble(double value) { + long bits = Double.doubleToLongBits(value); + writeLong(bits); + } + + @SuppressWarnings("unused") + public double readDouble() { + long bits = readLong(); + return Double.longBitsToDouble(bits); + } + + @SuppressWarnings("unused") + public void writeLong(long value) { + ensureCapacity(8); + for (int i = 7; i >= 0; i--) { + buffer[writeIndex++] = (byte) (value >> (i * 8)); + } + } + + @SuppressWarnings("unused") + public long readLong() { + ensureReadable(8); + long value = 0; + for (int i = 0; i < 8; i++) { + value = (value << 8) | (buffer[readIndex++] & 0xFF); + } + return value; + } + + @SuppressWarnings("unused") + public void writeEnumConstant(Enum instance) { + int ordinal = instance.ordinal(); + this.writeByte(ordinal); + } + + @SuppressWarnings("unused") + public > T readEnumConstant(Class enumClass) { + int ordinal = this.readByte(); + T[] enumConstants = enumClass.getEnumConstants(); + return enumConstants[ordinal]; + } + + @SuppressWarnings("unused") + public void writeCompoundTag(CompoundTag tag) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + NbtIo.writeCompressed(tag, baos); + } catch (IOException e) { + throw new RuntimeException(e); + } + byte[] buffer = baos.toByteArray(); + writeShort((short)buffer.length); + writeBytes(buffer); + } + + @SuppressWarnings("unused") + public CompoundTag readCompoundTag() { + int length = Short.toUnsignedInt(readShort()); + if (length == 0) { + return null; + } else { + byte[] data = new byte[length]; + readBytes(data, length); + try { + return NbtIo.readCompressed(new ByteArrayInputStream(data)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @SuppressWarnings("unused") + public InputStream readBytesAsStream(int length) { + ensureReadable(length); + return new InputStream() { + private int remaining = length; + + @Override + public int read() { + if (remaining <= 0) { + return -1; + } + remaining--; + return buffer[readIndex++] & 0xFF; + } + + @Override + public int read(byte @NotNull [] b, int off, int len) { + if (remaining <= 0) { + return -1; + } + int toRead = Math.min(len, remaining); + System.arraycopy(buffer, readIndex, b, off, toRead); + readIndex += toRead; + remaining -= toRead; + return toRead; + } + }; + } + + private void ensureCapacity(int length) { + if (writeIndex + length > buffer.length) { + buffer = Arrays.copyOf(buffer, buffer.length + length + 64); + } + } + + private void ensureReadable(int length) { + if (readIndex + length > writeIndex) { + throw new IndexOutOfBoundsException("Not enough data to read."); + } + } +} \ No newline at end of file diff --git a/src/main/java/turniplabs/halplibe/mixin/accessors/PacketHandlerServerAccessor.java b/src/main/java/turniplabs/halplibe/mixin/accessors/PacketHandlerServerAccessor.java new file mode 100644 index 0000000..26ca5b9 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/accessors/PacketHandlerServerAccessor.java @@ -0,0 +1,12 @@ +package turniplabs.halplibe.mixin.accessors; + +import net.minecraft.server.entity.player.PlayerServer; +import net.minecraft.server.net.handler.PacketHandlerServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(value = PacketHandlerServer.class, remap = false) +public interface PacketHandlerServerAccessor { + @Accessor + PlayerServer getPlayerEntity(); +} diff --git a/src/main/resources/halplibe.mixins.json b/src/main/resources/halplibe.mixins.json index 3a061b4..df40f93 100644 --- a/src/main/resources/halplibe.mixins.json +++ b/src/main/resources/halplibe.mixins.json @@ -14,7 +14,8 @@ "accessors.EntityFXAccessor", "accessors.LanguageAccessor", "accessors.WeightedRandomBagAccessor", - "accessors.WeightedRandomBagEntryAccessor" + "accessors.WeightedRandomBagEntryAccessor", + "accessors.PacketHandlerServerAccessor" ], "client": [ "PacketHandlerClientMixin", From 26ee0a75a69015d7377ad4b94fb1a643fbeabd86 Mon Sep 17 00:00:00 2001 From: Gungun974 Date: Mon, 3 Feb 2025 10:12:05 +0400 Subject: [PATCH 3/7] make NetworkHandler register message based on the modId --- .../halplibe/helper/network/NetworkHandler.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java index 3e1b0ac..f8d5eb9 100644 --- a/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java +++ b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java @@ -19,6 +19,7 @@ public final class NetworkHandler { private static final Map> packetReaders = new HashMap<>(); + private static final Map modIds = new HashMap<>(); private static final Map, Integer> packetIds = new HashMap<>(); private NetworkHandler() @@ -45,9 +46,16 @@ public static void receiveUniversalPacket(NetworkMessage.NetworkContext context, * @param factory The factory for this type of message. */ @SuppressWarnings({"unused"}) - public static void registerNetworkMessage( int id, Supplier factory ) + public static void registerNetworkMessage( String modId, short id, Supplier factory ) { - registerNetworkMessage( id, getType( factory ), buf -> { + if (!modIds.containsKey(modId)) { + modIds.put(modId, (short)modId.length()); + } + + final int high = (modIds.get(modId) & 0xFFFF) << 16; + final int low = id & 0xFFFF; + + registerNetworkMessage( high | low, getType( factory ), buf -> { T instance = factory.get(); instance.fromBytes( buf ); return instance; From 9f2027c5685da68107a98fd004d778ff29eb6436 Mon Sep 17 00:00:00 2001 From: Gungun974 Date: Mon, 3 Feb 2025 10:35:54 +0400 Subject: [PATCH 4/7] Better name --- .../helper/network/NetworkHandler.java | 33 +++++++++---------- .../helper/network/NetworkMessage.java | 10 +++--- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java index f8d5eb9..6294093 100644 --- a/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java +++ b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java @@ -8,7 +8,6 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.entity.player.PlayerServer; import turniplabs.halplibe.helper.EnvironmentHelper; -import turniplabs.halplibe.helper.NetworkHelper; import java.util.HashMap; import java.util.Map; @@ -28,12 +27,12 @@ private NetworkHandler() public static void setup() { - NetworkHelper.register ( UniversalPacket.class, true, true ); + Packet.addMapping (88, true, true, UniversalPacket.class ); } public static void receiveUniversalPacket(NetworkMessage.NetworkContext context, UniversalPacket buffer ) { - int type = buffer.readByte(); + int type = buffer.readInt(); packetReaders.get( type ) .accept( context, buffer ); } @@ -46,10 +45,10 @@ public static void receiveUniversalPacket(NetworkMessage.NetworkContext context, * @param factory The factory for this type of message. */ @SuppressWarnings({"unused"}) - public static void registerNetworkMessage( String modId, short id, Supplier factory ) + public static void registerNetworkMessage( String modId, int id, Supplier factory ) { if (!modIds.containsKey(modId)) { - modIds.put(modId, (short)modId.length()); + modIds.put(modId, (short)modId.length()); } final int high = (modIds.get(modId) & 0xFFFF) << 16; @@ -57,7 +56,7 @@ public static void registerNetworkMessage( String mod registerNetworkMessage( high | low, getType( factory ), buf -> { T instance = factory.get(); - instance.fromBytes( buf ); + instance.decodeFromUniversalPacket( buf ); return instance; } ); } @@ -89,8 +88,8 @@ private static Class getType( Supplier supplier ) private static UniversalPacket encode(NetworkMessage message ) { UniversalPacket buf = new UniversalPacket(); - buf.writeByte( packetIds.get( message.getClass() ) ); - message.toBytes( buf ); + buf.writeInt( packetIds.get( message.getClass() ) ); + message.encodeToUniversalPacket( buf ); return buf; } @@ -125,13 +124,13 @@ public static void sendToPlayer(Player player, NetworkMessage message ) * If we are in SinglePlayer this will skip encoding and directly call the message handle */ @SuppressWarnings({"unused"}) - public static void sendToAllPlayers( NetworkMessage packet ) + public static void sendToAllPlayers( NetworkMessage message ) { if (!EnvironmentHelper.isServerEnvironment()){ - sendToPlayerLocal(packet); + sendToPlayerLocal(message); return; } - MinecraftServer.getInstance().playerList.sendPacketToAllPlayers(encode(packet)); + MinecraftServer.getInstance().playerList.sendPacketToAllPlayers(encode(message)); } /** @@ -140,13 +139,13 @@ public static void sendToAllPlayers( NetworkMessage packet ) */ @SuppressWarnings({"unused"}) @Environment( EnvType.CLIENT ) - public static void sendToServer( NetworkMessage packet ) + public static void sendToServer( NetworkMessage message ) { if (EnvironmentHelper.isSinglePlayer()){ - sendToPlayerLocal(packet); + sendToPlayerLocal(message); return; } - Minecraft.getMinecraft().getSendQueue().addToSendQueue(encode(packet)); + Minecraft.getMinecraft().getSendQueue().addToSendQueue(encode(message)); } /** @@ -154,12 +153,12 @@ public static void sendToServer( NetworkMessage packet ) * If we are in SinglePlayer this will skip encoding and directly call the message handle */ @SuppressWarnings({"unused"}) - public static void sendToAllAround(NetworkMessage packet, double x, double y, double z, double radius, int dimension ) + public static void sendToAllAround(double x, double y, double z, double radius, int dimension, NetworkMessage message ) { if (!EnvironmentHelper.isServerEnvironment()){ - sendToPlayerLocal(packet); + sendToPlayerLocal(message); return; } - MinecraftServer.getInstance().playerList.sendPacketToPlayersAroundPoint(x, y, z, radius, dimension, encode(packet)); + MinecraftServer.getInstance().playerList.sendPacketToPlayersAroundPoint(x, y, z, radius, dimension, encode(message)); } } \ No newline at end of file diff --git a/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java b/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java index 3f1b1c6..5a5e192 100644 --- a/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java +++ b/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java @@ -6,20 +6,20 @@ public interface NetworkMessage { /** - * Write this packet to a buffer. + * Encode the UniversalPacket into your NetworkMessage. * This may be called on any thread, so this should be a pure operation. * * @param packet The packet to write data to. */ - void toBytes( @Nonnull UniversalPacket packet ); + void encodeToUniversalPacket(@Nonnull UniversalPacket packet ); /** - * Read this packet from a buffer. + * Decode the UniversalPacket into your NetworkMessage. * This may be called on any thread, so this should be a pure operation. * - * @param buf The packet to read data from. + * @param packet The packet to read data from. */ - void fromBytes( @Nonnull UniversalPacket buf ); + void decodeFromUniversalPacket(@Nonnull UniversalPacket packet ); /** * Handle this {@link NetworkMessage}. From 5cc0b0c42bbaca1c6a589f6b2927db39e8e3cc7a Mon Sep 17 00:00:00 2001 From: Gungun974 Date: Mon, 3 Feb 2025 22:40:35 +0400 Subject: [PATCH 5/7] Make the server decide for the ids and the client use the server list --- .../java/turniplabs/halplibe/HalpLibe.java | 1 - .../helper/network/NetworkHandler.java | 401 +++++++++++------- .../helper/network/NetworkMessage.java | 66 +-- .../halplibe/mixin/MinecraftMixin.java | 2 + .../halplibe/mixin/MinecraftServerMixin.java | 2 + .../mixin/PacketHandlerLoginMixin.java | 23 + src/main/resources/halplibe.mixins.json | 1 + 7 files changed, 315 insertions(+), 181 deletions(-) create mode 100644 src/main/java/turniplabs/halplibe/mixin/PacketHandlerLoginMixin.java diff --git a/src/main/java/turniplabs/halplibe/HalpLibe.java b/src/main/java/turniplabs/halplibe/HalpLibe.java index da627cf..0357ef6 100644 --- a/src/main/java/turniplabs/halplibe/HalpLibe.java +++ b/src/main/java/turniplabs/halplibe/HalpLibe.java @@ -40,7 +40,6 @@ public static String addModId(String modId, String name) { @Override public void onInitialize() { - NetworkHandler.setup(); LOGGER.info("HalpLibe initialized."); } diff --git a/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java index 6294093..b1f600a 100644 --- a/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java +++ b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java @@ -7,158 +7,265 @@ import net.minecraft.core.net.packet.Packet; import net.minecraft.server.MinecraftServer; import net.minecraft.server.entity.player.PlayerServer; +import org.jetbrains.annotations.NotNull; import turniplabs.halplibe.helper.EnvironmentHelper; -import java.util.HashMap; -import java.util.Map; +import java.lang.reflect.InvocationTargetException; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; public final class NetworkHandler { - private static final Map> packetReaders = new HashMap<>(); - private static final Map modIds = new HashMap<>(); - private static final Map, Integer> packetIds = new HashMap<>(); - - private NetworkHandler() - { - } - - public static void setup() - { - Packet.addMapping (88, true, true, UniversalPacket.class ); - } - - public static void receiveUniversalPacket(NetworkMessage.NetworkContext context, UniversalPacket buffer ) - { - int type = buffer.readInt(); - packetReaders.get( type ) - .accept( context, buffer ); - } - - /** - * Register a NetworkMessage, and a thread-unsafe handler for it. - * - * @param The type of the NetworkMessage to send. - * @param id The identifier for this message type - * @param factory The factory for this type of message. - */ - @SuppressWarnings({"unused"}) - public static void registerNetworkMessage( String modId, int id, Supplier factory ) - { - if (!modIds.containsKey(modId)) { - modIds.put(modId, (short)modId.length()); - } - - final int high = (modIds.get(modId) & 0xFFFF) << 16; - final int low = id & 0xFFFF; - - registerNetworkMessage( high | low, getType( factory ), buf -> { - T instance = factory.get(); - instance.decodeFromUniversalPacket( buf ); - return instance; - } ); - } - - /** - * Register a NetworkMessage, and a thread-unsafe handler for it. - * - * @param The type of the NetworkMessage to send. - * @param type The class of the type of message to send. - * @param id The identifier for this message type - * @param decoder The factory for this type of message. - */ - private static void registerNetworkMessage( int id, Class type, Function decoder ) - { - packetIds.put( type, id ); - packetReaders.put( id, ( context, buf ) -> { - T result = decoder.apply( buf ); - result.handle(context); - } ); - } - - @SuppressWarnings( "unchecked" ) - private static Class getType( Supplier supplier ) - { - return (Class) supplier.get() - .getClass(); - } - - private static UniversalPacket encode(NetworkMessage message ) - { - UniversalPacket buf = new UniversalPacket(); - buf.writeInt( packetIds.get( message.getClass() ) ); - message.encodeToUniversalPacket( buf ); - return buf; - } - - @Environment(EnvType.CLIENT) - private static void sendToPlayerLocal(NetworkMessage message) - { - message.handle(new NetworkMessage.NetworkContext(Minecraft.getMinecraft().thePlayer)); - } - - @Environment(EnvType.SERVER) - private static void sendToPlayerServer(Player player, NetworkMessage message ) - { - ((PlayerServer)player).playerNetServerHandler.sendPacket(encode(message)); - } - - /** - * Send a NetworkMessage to a specific Player from the server - * If we are in SinglePlayer this will skip encoding and directly call the message handle - */ - @SuppressWarnings({"unused"}) - public static void sendToPlayer(Player player, NetworkMessage message ) - { - if (!EnvironmentHelper.isServerEnvironment()){ - sendToPlayerLocal(message); - return; - } - sendToPlayerServer(player, message); - } - - /** - * Send a NetworkMessage to all Players from the server - * If we are in SinglePlayer this will skip encoding and directly call the message handle - */ - @SuppressWarnings({"unused"}) - public static void sendToAllPlayers( NetworkMessage message ) - { - if (!EnvironmentHelper.isServerEnvironment()){ - sendToPlayerLocal(message); - return; - } - MinecraftServer.getInstance().playerList.sendPacketToAllPlayers(encode(message)); - } - - /** - * Send a NetworkMessage to the Server from the player - * If we are in SinglePlayer this will skip encoding and directly call the message handle - */ - @SuppressWarnings({"unused"}) - @Environment( EnvType.CLIENT ) - public static void sendToServer( NetworkMessage message ) - { - if (EnvironmentHelper.isSinglePlayer()){ - sendToPlayerLocal(message); - return; - } - Minecraft.getMinecraft().getSendQueue().addToSendQueue(encode(message)); - } - - /** - * Send a NetworkMessage to all Players around a block from the server - * If we are in SinglePlayer this will skip encoding and directly call the message handle - */ - @SuppressWarnings({"unused"}) - public static void sendToAllAround(double x, double y, double z, double radius, int dimension, NetworkMessage message ) - { - if (!EnvironmentHelper.isServerEnvironment()){ - sendToPlayerLocal(message); - return; - } - MinecraftServer.getInstance().playerList.sendPacketToPlayersAroundPoint(x, y, z, radius, dimension, encode(message)); - } -} \ No newline at end of file + private static final List> messagesToRegisterForServer = new LinkedList<>(Collections.singletonList( + MessageIdsNetworkMessage::new + )); + + private static final Map> packetReaders = new HashMap<>(); + private static final Map, Short> packetIds = new HashMap<>(); + + private NetworkHandler() + { + } + + public static void setup() + { + Packet.addMapping (88, true, true, UniversalPacket.class ); + + register(); + } + + public static void register() + { + packetReaders.clear(); + packetIds.clear(); + + for (Supplier networkMessage : messagesToRegisterForServer) { + addNetworkMessage(networkMessage); + } + } + + public static void receiveUniversalPacket(NetworkMessage.NetworkContext context, UniversalPacket buffer ) + { + short type = buffer.readShort(); + + if (!packetReaders.containsKey(type)) { + return; + } + + packetReaders.get( type ) + .accept( context, buffer ); + } + + /** + * Register a NetworkMessage, and a thread-unsafe handler for it. + * + * @param factory The factory for this type of message. + */ + @SuppressWarnings({"unused"}) + public static void registerNetworkMessage( Supplier factory ) + { + messagesToRegisterForServer.add(factory); + } + + /** + * Register a NetworkMessage, and a thread-unsafe handler for it. + * + * @param The type of the NetworkMessage to send. + * @param factory The factory for this type of message. + */ + @SuppressWarnings({"unused"}) + public static void addNetworkMessage( Supplier factory ) + { + registerNetworkMessage((short) packetIds.size(), factory); + } + + /** + * Register a NetworkMessage, and a thread-unsafe handler for it. + * + * @param The type of the NetworkMessage to send. + * @param id The identifier for this message type + * @param factory The factory for this type of message. + */ + @SuppressWarnings({"unused"}) + private static void registerNetworkMessage( short id, Supplier factory ) + { + registerNetworkMessage( id, getType( factory ), buf -> { + T instance = factory.get(); + instance.decodeFromUniversalPacket( buf ); + return instance; + } ); + } + + /** + * Register a NetworkMessage, and a thread-unsafe handler for it. + * + * @param The type of the NetworkMessage to send. + * @param type The class of the type of message to send. + * @param id The identifier for this message type + * @param decoder The factory for this type of message. + */ + private static void registerNetworkMessage( short id, Class type, Function decoder ) + { + packetIds.put( type, id ); + packetReaders.put( id, ( context, buf ) -> { + T result = decoder.apply( buf ); + result.handle(context); + } ); + } + + @SuppressWarnings( "unchecked" ) + private static Class getType( Supplier supplier ) + { + return (Class) supplier.get() + .getClass(); + } + + private static UniversalPacket encode(NetworkMessage message ) + { + UniversalPacket buf = new UniversalPacket(); + buf.writeShort( packetIds.get( message.getClass() ) ); + message.encodeToUniversalPacket( buf ); + return buf; + } + + @Environment(EnvType.CLIENT) + private static void sendToPlayerLocal(NetworkMessage message) + { + message.handle(new NetworkMessage.NetworkContext(Minecraft.getMinecraft().thePlayer)); + } + + @Environment(EnvType.SERVER) + private static void sendToPlayerServer(Player player, NetworkMessage message) + { + ((PlayerServer)player).playerNetServerHandler.sendPacket(encode(message)); + } + + @Environment(EnvType.SERVER) + public static void sendToPlayerMessagesConfiguration(Player player) + { + ((PlayerServer)player).playerNetServerHandler.sendPacket(encode(new MessageIdsNetworkMessage(packetIds))); + } + + /** + * Send a NetworkMessage to a specific Player from the server + * If we are in SinglePlayer this will skip encoding and directly call the message handle + */ + @SuppressWarnings({"unused"}) + public static void sendToPlayer(Player player, NetworkMessage message ) + { + if (!EnvironmentHelper.isServerEnvironment()){ + sendToPlayerLocal(message); + return; + } + sendToPlayerServer(player, message); + } + + /** + * Send a NetworkMessage to all Players from the server + * If we are in SinglePlayer this will skip encoding and directly call the message handle + */ + @SuppressWarnings({"unused"}) + public static void sendToAllPlayers( NetworkMessage message ) + { + if (!EnvironmentHelper.isServerEnvironment()){ + sendToPlayerLocal(message); + return; + } + MinecraftServer.getInstance().playerList.sendPacketToAllPlayers(encode(message)); + } + + /** + * Send a NetworkMessage to the Server from the player + * If we are in SinglePlayer this will skip encoding and directly call the message handle + */ + @SuppressWarnings({"unused"}) + @Environment( EnvType.CLIENT ) + public static void sendToServer( NetworkMessage message ) + { + if (EnvironmentHelper.isSinglePlayer()){ + sendToPlayerLocal(message); + return; + } + Minecraft.getMinecraft().getSendQueue().addToSendQueue(encode(message)); + } + + /** + * Send a NetworkMessage to all Players around a block from the server + * If we are in SinglePlayer this will skip encoding and directly call the message handle + */ + @SuppressWarnings({"unused"}) + public static void sendToAllAround(double x, double y, double z, double radius, int dimension, NetworkMessage message ) + { + if (!EnvironmentHelper.isServerEnvironment()){ + sendToPlayerLocal(message); + return; + } + MinecraftServer.getInstance().playerList.sendPacketToPlayersAroundPoint(x, y, z, radius, dimension, encode(message)); + } + + private static class MessageIdsNetworkMessage implements NetworkMessage{ + Map, Short> packetIds; + + public MessageIdsNetworkMessage() {} + + public MessageIdsNetworkMessage(Map, Short> packetIds) { + this.packetIds = packetIds; + } + + @Override + public void encodeToUniversalPacket(@NotNull UniversalPacket packet) { + packet.writeShort((short) packetIds.size()); + + for (Map.Entry, Short> entry : packetIds.entrySet()) { + packet.writeShort(entry.getValue()); + packet.writeString(entry.getKey().getName()); + } + } + + @Override + public void decodeFromUniversalPacket(@NotNull UniversalPacket packet) { + this.packetIds = new HashMap<>(); + + final short size = packet.readShort(); + + try { + for (int i = 0; i < size; i++) { + final short id = packet.readShort(); + final Class messageClass = Class.forName(packet.readString()); + + this.packetIds.put(messageClass, id); + } + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + public void handle(NetworkContext context) { + try { + NetworkHandler.packetReaders.clear(); + NetworkHandler.packetIds.clear(); + + for (Map.Entry, Short> entry : packetIds.entrySet()) { + Class klass = entry.getKey(); + if (NetworkMessage.class.isAssignableFrom(klass)) { + Supplier supplier = () -> { + try { + return (NetworkMessage) klass.getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + }; + NetworkHandler.registerNetworkMessage(entry.getValue(), supplier); + } else { + throw new IllegalArgumentException("Class " + klass.getName() + " does not extend NetworkMessage"); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java b/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java index 5a5e192..b53524d 100644 --- a/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java +++ b/src/main/java/turniplabs/halplibe/helper/network/NetworkMessage.java @@ -5,38 +5,38 @@ import javax.annotation.Nonnull; public interface NetworkMessage { - /** - * Encode the UniversalPacket into your NetworkMessage. - * This may be called on any thread, so this should be a pure operation. - * - * @param packet The packet to write data to. - */ - void encodeToUniversalPacket(@Nonnull UniversalPacket packet ); - - /** - * Decode the UniversalPacket into your NetworkMessage. - * This may be called on any thread, so this should be a pure operation. - * - * @param packet The packet to read data from. - */ - void decodeFromUniversalPacket(@Nonnull UniversalPacket packet ); - - /** - * Handle this {@link NetworkMessage}. - * - * @param context An intermediary representation of Packet handler common on both Client and Server environment. - */ - void handle(NetworkContext context); - - class NetworkContext { - /** - * The player that send the NetworkPacket to the handle - */ - public Player player; - - public NetworkContext(Player player) { - this.player = player; - } - } + /** + * Encode the UniversalPacket into your NetworkMessage. + * This may be called on any thread, so this should be a pure operation. + * + * @param packet The packet to write data to. + */ + void encodeToUniversalPacket(@Nonnull UniversalPacket packet ); + + /** + * Decode the UniversalPacket into your NetworkMessage. + * This may be called on any thread, so this should be a pure operation. + * + * @param packet The packet to read data from. + */ + void decodeFromUniversalPacket(@Nonnull UniversalPacket packet ); + + /** + * Handle this {@link NetworkMessage}. + * + * @param context An intermediary representation of Packet handler common on both Client and Server environment. + */ + void handle(NetworkContext context); + + class NetworkContext { + /** + * The player that send the NetworkPacket to the handle + */ + public Player player; + + public NetworkContext(Player player) { + this.player = player; + } + } } diff --git a/src/main/java/turniplabs/halplibe/mixin/MinecraftMixin.java b/src/main/java/turniplabs/halplibe/mixin/MinecraftMixin.java index 04dd80f..8f2df9d 100644 --- a/src/main/java/turniplabs/halplibe/mixin/MinecraftMixin.java +++ b/src/main/java/turniplabs/halplibe/mixin/MinecraftMixin.java @@ -7,6 +7,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import turniplabs.halplibe.helper.network.NetworkHandler; import turniplabs.halplibe.util.ClientStartEntrypoint; import turniplabs.halplibe.util.GameStartEntrypoint; import turniplabs.halplibe.util.RecipeEntrypoint; @@ -32,6 +33,7 @@ public void beforeGameStartEntrypoint(CallbackInfo ci){ @Inject(method = "startGame", at = @At("TAIL")) public void afterGameStartEntrypoint(CallbackInfo ci){ + NetworkHandler.setup(); FabricLoader.getInstance().getEntrypoints("afterGameStart", GameStartEntrypoint.class).forEach(GameStartEntrypoint::afterGameStart); FabricLoader.getInstance().getEntrypoints("afterClientStart", ClientStartEntrypoint.class).forEach(ClientStartEntrypoint::afterClientStart); } diff --git a/src/main/java/turniplabs/halplibe/mixin/MinecraftServerMixin.java b/src/main/java/turniplabs/halplibe/mixin/MinecraftServerMixin.java index e8159f7..52ec383 100644 --- a/src/main/java/turniplabs/halplibe/mixin/MinecraftServerMixin.java +++ b/src/main/java/turniplabs/halplibe/mixin/MinecraftServerMixin.java @@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import turniplabs.halplibe.helper.network.NetworkHandler; import turniplabs.halplibe.util.GameStartEntrypoint; import turniplabs.halplibe.util.RecipeEntrypoint; @@ -24,6 +25,7 @@ public void recipeEntrypoint(CallbackInfoReturnable cir){ public void beforeGameStartEntrypoint(CallbackInfoReturnable cir){ instance = (MinecraftServer)(Object)this; Global.isServer = true; + NetworkHandler.setup(); FabricLoader.getInstance().getEntrypoints("beforeGameStart", GameStartEntrypoint.class).forEach(GameStartEntrypoint::beforeGameStart); } diff --git a/src/main/java/turniplabs/halplibe/mixin/PacketHandlerLoginMixin.java b/src/main/java/turniplabs/halplibe/mixin/PacketHandlerLoginMixin.java new file mode 100644 index 0000000..4b6a757 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/PacketHandlerLoginMixin.java @@ -0,0 +1,23 @@ +package turniplabs.halplibe.mixin; + +import net.minecraft.core.net.packet.PacketLogin; +import net.minecraft.server.entity.player.PlayerServer; +import net.minecraft.server.net.handler.PacketHandlerLogin; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; +import turniplabs.halplibe.helper.network.NetworkHandler; + +@Mixin(value = PacketHandlerLogin.class, remap = false) +public class PacketHandlerLoginMixin { + @Inject(method = "doLogin(Lnet/minecraft/core/net/packet/PacketLogin;)V", at = @At(value = "INVOKE", + target = "Lnet/minecraft/server/net/handler/PacketHandlerServer;sendPacket(Lnet/minecraft/core/net/packet/Packet;)V", + ordinal = 0, shift = At.Shift.AFTER), + locals = LocalCapture.CAPTURE_FAILHARD + ) + public void doLogin(PacketLogin packetlogin, CallbackInfo ci, PlayerServer player) { + NetworkHandler.sendToPlayerMessagesConfiguration(player); + } + } diff --git a/src/main/resources/halplibe.mixins.json b/src/main/resources/halplibe.mixins.json index df40f93..3d576d9 100644 --- a/src/main/resources/halplibe.mixins.json +++ b/src/main/resources/halplibe.mixins.json @@ -26,6 +26,7 @@ "models.TileEntityRendererDispatcherMixin" ], "server": [ + "PacketHandlerLoginMixin" ], "injectors": { "defaultRequire": 1 From 4d98a4eb85d3ed5192d7d885491bd2754ed93168 Mon Sep 17 00:00:00 2001 From: Gungun974 Date: Mon, 3 Feb 2025 23:47:43 +0400 Subject: [PATCH 6/7] Remove old import --- src/main/java/turniplabs/halplibe/HalpLibe.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/turniplabs/halplibe/HalpLibe.java b/src/main/java/turniplabs/halplibe/HalpLibe.java index 0357ef6..b12ae7d 100644 --- a/src/main/java/turniplabs/halplibe/HalpLibe.java +++ b/src/main/java/turniplabs/halplibe/HalpLibe.java @@ -13,8 +13,6 @@ import net.minecraft.core.item.Item; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import turniplabs.halplibe.helper.NetworkHelper; -import turniplabs.halplibe.helper.network.NetworkHandler; import turniplabs.halplibe.util.ModelEntrypoint; import turniplabs.halplibe.util.TomlConfigHandler; import turniplabs.halplibe.util.toml.Toml; From 2db7779fa45f15f93eb1e0c974fe8666f847d096 Mon Sep 17 00:00:00 2001 From: Gungun974 Date: Tue, 4 Feb 2025 23:23:06 +0400 Subject: [PATCH 7/7] The server should never change the register messages ids from a client packet --- .../turniplabs/halplibe/helper/network/NetworkHandler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java index b1f600a..bedb045 100644 --- a/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java +++ b/src/main/java/turniplabs/halplibe/helper/network/NetworkHandler.java @@ -244,6 +244,10 @@ public void decodeFromUniversalPacket(@NotNull UniversalPacket packet) { @Override public void handle(NetworkContext context) { + if (EnvironmentHelper.isServerEnvironment()) { + return; + } + try { NetworkHandler.packetReaders.clear(); NetworkHandler.packetIds.clear();