aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java
blob: 1d80a9ec369355b4275b11630c34ab5c903bd0ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/*
 * SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

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 {
    String PlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_1657");
    String World = remapper.mapClassName("intermediary", "net.minecraft.class_1937");
    String GameProfile = "com.mojang.authlib.GameProfile";
    String BlockPos = remapper.mapClassName("intermediary", "net.minecraft.class_2338");
    String AbstractClientPlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_742");
    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, "<init>") && Type.getMethodType(method.desc).equals(constructorDescriptor)) {
                modifyConstructor(method, superClass);
                return;
            }
        }
        var node = new MethodNode(Opcodes.ASM9, "<init>", 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(), "<init>", constructorDescriptor.getDescriptor(), false));

        // Return
        method.instructions.add(new InsnNode(Opcodes.RETURN));
    }

    @Override
    public void addTinkerers() {
        ClassTinkerers.addTransformation(AbstractClientPlayerEntity, it -> mapClassNode(it, getTypeForClassName(PlayerEntity)));
        ClassTinkerers.addTransformation(GuiPlayer, it -> mapClassNode(it, getTypeForClassName(AbstractClientPlayerEntity)));
    }
}