SciCraft/tools/patches/TdwcmPatcher.java
MiTHRAL fde71b7f86
All checks were successful
Build & Release Pack / build (push) Successful in 1m51s
fix: TDWCM join failure — optional channels + install on both sides
tdwcm 0.2.1 registered ~31 payloads via playBidirectional without
.optional() AND registers real blocks/items/entities, so a one-sided
install fails login twice (required-channel mismatch, then registry
sync). Ship ASM-patched jar (tools/patches/build_patched_tdwcm.sh,
TdwcmPatcher.java) that inserts registrar.optional(), and add it to
server_pack so negotiation and registry sync pass on both sides.

Verified: dedicated server boots clean (Done ~27s), tdwcm:tardis_interior
dimension comes up under Sable physics + IPSable portal storage.
Audit: no other client-only mod in the pack uses NeoForge's payload API.
2026-08-24 16:47:23 -04:00

68 lines
3 KiB
Java

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* Makes all of TDWCM's payload registrations optional.
*
* Upstream bug: net.dumonde.tdwcm.networking.NetworkRegistry.register() does
* registrar = event.registrar("1.0");
* MESSAGES.forEach((t, m) -> registrar.playBidirectional(t, ...));
* — every channel (entity_rotation_sync + ~30 more) is REQUIRED, so clients
* carrying the mod cannot join any server without it ("channel missing").
*
* Fix: insert PayloadRegistrar.optional() right after registrar creation:
* registrar = event.registrar("1.0").optional();
* Optional payloads are skipped during login negotiation when absent on the
* other side, and client→server messages are dropped server-side instead of
* failing handshake. Exactly what a client-side-only mod wants.
*/
public class TdwcmPatcher {
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.err.println("usage: TdwcmPatcher <NetworkRegistry.class-in> <class-out>");
System.exit(2);
}
byte[] bytes = Files.readAllBytes(Paths.get(args[0]));
ClassNode cn = new ClassNode();
new ClassReader(bytes).accept(cn, 0);
boolean patched = false;
for (MethodNode mn : cn.methods) {
if (!"register".equals(mn.name)) continue;
for (AbstractInsnNode n : mn.instructions) {
if (!(n instanceof MethodInsnNode mi)) continue;
if (!"registrar".equals(mi.name)) continue;
if (!(mi.getNext() instanceof VarInsnNode store) || store.getOpcode() != Opcodes.ASTORE) continue;
InsnList ins = new InsnList();
ins.add(new MethodInsnNode(
Opcodes.INVOKEVIRTUAL,
"net/neoforged/neoforge/network/registration/PayloadRegistrar",
"optional",
"()Lnet/neoforged/neoforge/network/registration/PayloadRegistrar;",
false));
mn.instructions.insert(mi, ins);
patched = true;
break;
}
}
if (!patched) throw new IllegalStateException("registrar(...) call site not found");
// COMPUTE_FRAMES: offsets shifted by the insertion; classpath must contain
// the original jar + neoforge universal jar for hierarchy resolution.
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cn.accept(cw);
Files.write(Paths.get(args[1]), cw.toByteArray());
System.out.println("patched " + cn.name + ".register: registrar(...).optional() inserted");
}
}