Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

network utility #8

Merged
merged 2 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions src/main/java/turniplabs/halplibe/helper/NetworkHelper.java
Original file line number Diff line number Diff line change
@@ -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<NetworkEntry> ENTRIES = new TreeSet<>();

private static boolean locked = false;

public static void register(Class<Packet> 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<NetworkEntry> 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<NetworkEntry> {
public final Class<Packet> clazz;

public final boolean toServer, toClient;

public NetworkEntry(Class<Packet> 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<Integer, Class<? extends Packet>> map;
private static final Method addMapping;

static {
try {
Field f = Packet.class.getDeclaredField("packetIdToClassMap");
f.setAccessible(true);

map = (Map<Integer, Class<? extends Packet>>) 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<? extends Packet> 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) {
}
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
});
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
});
}
}
Original file line number Diff line number Diff line change
@@ -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 = "<init>")
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()];
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
6 changes: 5 additions & 1 deletion src/main/resources/halplibe.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down