package moe.nea.firmament.init; import me.shedaniel.mm.api.ClassTinkerers; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.VarInsnNode; import java.lang.reflect.Modifier; import java.util.Objects; public class ClientPlayerRiser extends RiserUtils { @IntermediaryName(net.minecraft.entity.player.PlayerEntity.class) String PlayerEntity; @IntermediaryName(net.minecraft.world.World.class) String World; String GameProfile = "com.mojang.authlib.GameProfile"; @IntermediaryName(net.minecraft.util.math.BlockPos.class) String BlockPos; @IntermediaryName(net.minecraft.client.network.AbstractClientPlayerEntity.class) String AbstractClientPlayerEntity; String GuiPlayer = "moe.nea.firmament.gui.entity.GuiPlayer"; // World world, BlockPos pos, float yaw, GameProfile gameProfile Type constructorDescriptor = Type.getMethodType(Type.VOID_TYPE, getTypeForClassName(World), getTypeForClassName(BlockPos), Type.FLOAT_TYPE, getTypeForClassName(GameProfile)); private void mapClassNode(ClassNode classNode, Type superClass) { for (MethodNode method : classNode.methods) { if (Objects.equals(method.name, "") && Type.getMethodType(method.desc).equals(constructorDescriptor)) { modifyConstructor(method, superClass); return; } } var node = new MethodNode(Opcodes.ASM9, "", constructorDescriptor.getDescriptor(), null, null); classNode.methods.add(node); modifyConstructor(node, superClass); } private void modifyConstructor(MethodNode method, Type superClass) { method.access = (method.access | Modifier.PUBLIC) & ~Modifier.PRIVATE & ~Modifier.PROTECTED; if (method.instructions.size() != 0) return; // Some other mod has already made a constructor here // World world, BlockPos pos, float yaw, GameProfile gameProfile // ALOAD this method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // ALOAD World method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 1)); // ALOAD BlockPos method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 2)); // ALOAD yaw method.instructions.add(new VarInsnNode(Opcodes.FLOAD, 3)); // ALOAD gameProfile method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 4)); // Call super method.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, superClass.getInternalName(), "", constructorDescriptor.getDescriptor(), false)); // Return method.instructions.add(new InsnNode(Opcodes.RETURN)); } @Override public void addTinkerers() { ClassTinkerers.addTransformation(AbstractClientPlayerEntity, it -> mapClassNode(it, getTypeForClassName(PlayerEntity)), true); ClassTinkerers.addTransformation(GuiPlayer, it -> mapClassNode(it, getTypeForClassName(AbstractClientPlayerEntity)), true); } }