diff --git a/src/main/java/turniplabs/halplibe/helper/NetworkHelper.java b/src/main/java/turniplabs/halplibe/helper/NetworkHelper.java new file mode 100644 index 0000000..ef17d2c --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/NetworkHelper.java @@ -0,0 +1,99 @@ +package turniplabs.halplibe.helper; + +import net.minecraft.core.net.packet.Packet; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.TreeSet; +import java.util.function.Consumer; + +public class NetworkHelper { + private static final TreeSet ENTRIES = new TreeSet<>(); + + private static boolean locked = false; + + public static void register(Class clazz, boolean toServer, boolean toClient) { + if (locked) + // if a packet is registered after the game starts, the game will most likely crash due to int[]'s in network managers not being long enough + throw new RuntimeException("Packet " + clazz + " was registered too late, packets should be registered before the game starts running."); + ENTRIES.add(new NetworkEntry(clazz, toServer, toClient)); + } + + public static void iterateEntries(Consumer consumer) { + ENTRIES.forEach(consumer); + } + + public static int getLastPacketId() { + return lastPacket; + } + + // this class is likely more or less redundant, I'm just using it to try to minimize issues from mod load ordering + public static final class NetworkEntry implements Comparable { + public final Class clazz; + + public final boolean toServer, toClient; + + public NetworkEntry(Class clazz, boolean toServer, boolean toClient) { + this.clazz = clazz; + this.toServer = toServer; + this.toClient = toClient; + } + + @Override + public int compareTo(@NotNull NetworkEntry o) { + String mname = clazz.getName(); + String oname = o.clazz.getName(); + + int v = Integer.compare(mname.length(), oname.length()); + + if (v == 0) { + for (int i = 0; i < mname.length(); i++) { + v = Character.compare(mname.charAt(i), oname.charAt(i)); + if (v != 0) break; + } + } + + return v; + } + } + + private static int latestId = 0; + private static int lastPacket = 0; + + private static final Map> map; + private static final Method addMapping; + + static { + try { + Field f = Packet.class.getDeclaredField("packetIdToClassMap"); + f.setAccessible(true); + + map = (Map>) f.get(null); + for (Integer integer : map.keySet()) + lastPacket = Math.max(integer, lastPacket); + + f.setAccessible(false); + + addMapping = Packet.class.getDeclaredMethod("addIdClassMapping", int.class, boolean.class, boolean.class, Class.class); + } catch (Throwable err) { + throw new RuntimeException(err); + } + } + + @SuppressWarnings("unused") + private static void doRegister(Class packet, boolean server, boolean client) { + try { + while (map.containsKey(latestId)) latestId++; + if (lastPacket < latestId) lastPacket = latestId; + + addMapping.setAccessible(true); + addMapping.invoke(null, latestId, client, server, packet); + addMapping.setAccessible(false); + + locked = true; + } catch (Throwable ignored) { + } + } +} diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/network/MinecraftMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/network/MinecraftMixin.java new file mode 100644 index 0000000..ea2d720 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/network/MinecraftMixin.java @@ -0,0 +1,27 @@ +package turniplabs.halplibe.mixin.mixins.network; + +import net.minecraft.client.Minecraft; +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 turniplabs.halplibe.helper.NetworkHelper; + +import java.lang.reflect.Method; + +@Mixin(value = Minecraft.class, remap = false) +public class MinecraftMixin { + @Inject(at = @At("HEAD"), method = "run") + public void postStartClient(CallbackInfo ci) { + NetworkHelper.iterateEntries((e) -> { + try { + Method m = NetworkHelper.class.getDeclaredMethod("doRegister", Class.class, boolean.class, boolean.class); + m.setAccessible(true); + m.invoke(null, e.clazz, e.toServer, e.toClient); + m.setAccessible(false); + } catch (Throwable err) { + err.printStackTrace(); + } + }); + } +} diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/network/MinecraftServerMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/network/MinecraftServerMixin.java new file mode 100644 index 0000000..4d3f2c3 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/network/MinecraftServerMixin.java @@ -0,0 +1,27 @@ +package turniplabs.halplibe.mixin.mixins.network; + +import net.minecraft.server.MinecraftServer; +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 turniplabs.halplibe.helper.NetworkHelper; + +import java.lang.reflect.Method; + +@Mixin(value = MinecraftServer.class, remap = false) +public class MinecraftServerMixin { + @Inject(at = @At("HEAD"), method = "run") + public void postStartClient(CallbackInfo ci) { + NetworkHelper.iterateEntries((e) -> { + try { + Method m = NetworkHelper.class.getDeclaredMethod("doRegister", Class.class, boolean.class, boolean.class); + m.setAccessible(true); + m.invoke(null, e.clazz, e.toServer, e.toClient); + m.setAccessible(false); + } catch (Throwable err) { + err.printStackTrace(); + } + }); + } +} diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/network/NetworkManagerMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/network/NetworkManagerMixin.java new file mode 100644 index 0000000..3a34062 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/network/NetworkManagerMixin.java @@ -0,0 +1,25 @@ +package turniplabs.halplibe.mixin.mixins.network; + +import net.minecraft.core.net.NetworkManager; +import net.minecraft.core.net.handler.NetHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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.NetworkHelper; + +import java.net.Socket; + +@Mixin(value = NetworkManager.class, remap = false) +public class NetworkManagerMixin { + @Shadow public static int[] field_28144_e; + + @Shadow public static int[] field_28145_d; + + @Inject(at = @At("TAIL"), method = "") + public void postInit(Socket socket, String s, NetHandler nethandler, CallbackInfo ci) { + field_28144_e = new int[NetworkHelper.getLastPacketId()]; + field_28145_d = new int[NetworkHelper.getLastPacketId()]; + } +} diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/network/PacketMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/network/PacketMixin.java new file mode 100644 index 0000000..a07b6d9 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/network/PacketMixin.java @@ -0,0 +1,26 @@ +package turniplabs.halplibe.mixin.mixins.network; + +import net.minecraft.core.net.packet.Packet; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import turniplabs.halplibe.helper.NetworkHelper; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +@Mixin(value = Packet.class, remap = false) +public class PacketMixin { + @Redirect(at = @At(value = "INVOKE", target = "Ljava/io/DataOutputStream;write(I)V"), method = "writePacket") + private static void preWrite(DataOutputStream instance, int b) throws IOException { + if (NetworkHelper.getLastPacketId() > 255) instance.writeInt(b); + else instance.write(b); + } + + @Redirect(at = @At(value = "INVOKE", target = "Ljava/io/DataInputStream;read()I"), method = "readPacket") + private static int preWrite(DataInputStream instance) throws IOException { + if (NetworkHelper.getLastPacketId() > 255) return instance.readInt(); + else return instance.read(); + } +} diff --git a/src/main/resources/halplibe.mixins.json b/src/main/resources/halplibe.mixins.json index f813a8d..8107430 100644 --- a/src/main/resources/halplibe.mixins.json +++ b/src/main/resources/halplibe.mixins.json @@ -22,9 +22,13 @@ "mixins.GuiIngameMenuMixin", "mixins.I18nMixin", "mixins.RenderEngineMixin", - "mixins.RenderGlobalMixin" + "mixins.RenderGlobalMixin", + "mixins.network.MinecraftServerMixin", + "mixins.network.NetworkManagerMixin", + "mixins.network.PacketMixin" ], "client": [ + "mixins.network.MinecraftMixin" ], "injectors": { "defaultRequire": 1