aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java
blob: 2be11a6610561e7fd62486f978bd5b810be14ec5 (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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package moe.nea.firmament.init;

import me.shedaniel.mm.api.ClassTinkerers;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.block.BlockModels;
import net.minecraft.client.render.block.BlockRenderManager;
import net.minecraft.client.render.chunk.SectionBuilder;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.math.BlockPos;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;

public class SectionBuilderRiser extends RiserUtils {

    @IntermediaryName(SectionBuilder.class)
    String SectionBuilder;
    @IntermediaryName(BlockPos.class)
    String BlockPos;
    @IntermediaryName(BlockRenderManager.class)
    String BlockRenderManager;
    @IntermediaryName(BlockState.class)
    String BlockState;
    @IntermediaryName(BakedModel.class)
    String BakedModel;
    String CustomBlockTextures = "moe.nea.firmament.features.texturepack.CustomBlockTextures";

    Type getModelDesc = Type.getMethodType(
        getTypeForClassName(BlockRenderManager),
        getTypeForClassName(BlockState)
    );
    String getModel = remapper.mapMethodName(
        "intermediary",
        Intermediary.<BlockRenderManager>className(),
        Intermediary.methodName(net.minecraft.client.render.block.BlockRenderManager::getModel),
        Type.getMethodDescriptor(
            getTypeForClassName(Intermediary.<BakedModel>className()),
            getTypeForClassName(Intermediary.<BlockState>className())
        )
    );

    @Override
    public void addTinkerers() {
        if (FabricLoader.getInstance().isModLoaded("fabric-renderer-indigo"))
            ClassTinkerers.addTransformation(SectionBuilder, this::handle, true);
    }

    private void handle(ClassNode classNode) {
        for (MethodNode method : classNode.methods) {
            if (method.name.endsWith("$fabric-renderer-indigo$hookChunkBuildTessellate") &&
                method.name.startsWith("redirect$")) {
                handleIndigo(method);
                return;
            }
        }
        new RuntimeException("Could not inject tesselation hook despite fabric renderer indigo being loaded").printStackTrace();
    }

    private void handleIndigo(MethodNode method) {
        LocalVariableNode blockPosVar = null, blockStateVar = null;
        for (LocalVariableNode localVariable : method.localVariables) {
            if (Type.getType(localVariable.desc).equals(getTypeForClassName(BlockPos))) {
                blockPosVar = localVariable;
            }
            if (Type.getType(localVariable.desc).equals(getTypeForClassName(BlockState))) {
                blockStateVar = localVariable;
            }
        }
        if (blockPosVar == null || blockStateVar == null) {
            System.err.println("Firmament could inject into indigo: missing either block pos or blockstate");
            return;
        }
        for (AbstractInsnNode instruction : method.instructions) {
            if (instruction.getOpcode() != Opcodes.INVOKEVIRTUAL) continue;
            var methodInsn = (MethodInsnNode) instruction;
            if (!(methodInsn.name.equals(getModel) && Type.getObjectType(methodInsn.owner).equals(getTypeForClassName(BlockRenderManager))))
                continue;
            method.instructions.insertBefore(
                methodInsn,
                new MethodInsnNode(
                    Opcodes.INVOKESTATIC,
                    getTypeForClassName(CustomBlockTextures).getInternalName(),
                    "enterFallbackCall",
                    Type.getMethodDescriptor(Type.VOID_TYPE)
                ));

            var insnList = new InsnList();
            insnList.add(new MethodInsnNode(
                Opcodes.INVOKESTATIC,
                getTypeForClassName(CustomBlockTextures).getInternalName(),
                "exitFallbackCall",
                Type.getMethodDescriptor(Type.VOID_TYPE)
            ));
            insnList.add(new VarInsnNode(Opcodes.ALOAD, blockPosVar.index));
            insnList.add(new VarInsnNode(Opcodes.ALOAD, blockStateVar.index));
            insnList.add(new MethodInsnNode(
                Opcodes.INVOKESTATIC,
                getTypeForClassName(CustomBlockTextures).getInternalName(),
                "patchIndigo",
                Type.getMethodDescriptor(getTypeForClassName(BakedModel),
                                         getTypeForClassName(BakedModel),
                                         getTypeForClassName(BlockPos),
                                         getTypeForClassName(BlockState)),
                false
            ));
            method.instructions.insert(methodInsn, insnList);
        }
    }
}