diff options
author | Linnea Gräf <nea@nea.moe> | 2024-08-10 01:59:34 +0200 |
---|---|---|
committer | Linnea Gräf <nea@nea.moe> | 2024-08-12 21:02:44 +0200 |
commit | 3c7e6b6177de6ef3cff8a46bb1726466a299cdde (patch) | |
tree | 2ebc75e705b5422a68d5d7f04d88e3d8934cf02d /src | |
parent | 1606188d9ad65c66e9d873497ea3271dbdadaf77 (diff) | |
download | firmament-3c7e6b6177de6ef3cff8a46bb1726466a299cdde.tar.gz firmament-3c7e6b6177de6ef3cff8a46bb1726466a299cdde.tar.bz2 firmament-3c7e6b6177de6ef3cff8a46bb1726466a299cdde.zip |
Add indigo support to custom block textures
Diffstat (limited to 'src')
9 files changed, 227 insertions, 9 deletions
diff --git a/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java b/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java index 18dfa17..0a5bedd 100644 --- a/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java +++ b/src/main/java/moe/nea/firmament/init/ClientPlayerRiser.java @@ -1,4 +1,3 @@ - package moe.nea.firmament.init; import me.shedaniel.mm.api.ClassTinkerers; @@ -14,11 +13,15 @@ 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"); + @IntermediaryName(net.minecraft.entity.player.PlayerEntity.class) + String PlayerEntity; + @IntermediaryName(net.minecraft.world.World.class) + String World; String GameProfile = "com.mojang.authlib.GameProfile"; - String BlockPos = remapper.mapClassName("intermediary", "net.minecraft.class_2338"); - String AbstractClientPlayerEntity = remapper.mapClassName("intermediary", "net.minecraft.class_742"); + @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)); diff --git a/src/main/java/moe/nea/firmament/init/EarlyRiser.java b/src/main/java/moe/nea/firmament/init/EarlyRiser.java index 77c044d..5eab563 100644 --- a/src/main/java/moe/nea/firmament/init/EarlyRiser.java +++ b/src/main/java/moe/nea/firmament/init/EarlyRiser.java @@ -6,5 +6,6 @@ public class EarlyRiser implements Runnable { public void run() { new ClientPlayerRiser().addTinkerers(); new HandledScreenRiser().addTinkerers(); + new SectionBuilderRiser().addTinkerers(); } } diff --git a/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java b/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java index 0215523..baa0501 100644 --- a/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java +++ b/src/main/java/moe/nea/firmament/init/HandledScreenRiser.java @@ -2,6 +2,8 @@ package moe.nea.firmament.init; import me.shedaniel.mm.api.ClassTinkerers; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; @@ -17,8 +19,10 @@ import org.objectweb.asm.tree.VarInsnNode; import java.lang.reflect.Modifier; public class HandledScreenRiser extends RiserUtils { - String Screen = remapper.mapClassName("intermediary", "net.minecraft.class_437"); - String HandledScreen = remapper.mapClassName("intermediary", "net.minecraft.class_465"); + @IntermediaryName(net.minecraft.client.gui.screen.Screen.class) + String Screen; + @IntermediaryName(net.minecraft.client.gui.screen.ingame.HandledScreen.class) + String HandledScreen; Type mouseScrolledDesc = Type.getMethodType(Type.BOOLEAN_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE); String mouseScrolled = remapper.mapMethodName("intermediary", "net.minecraft.class_364", "method_25401", mouseScrolledDesc.getDescriptor()); diff --git a/src/main/java/moe/nea/firmament/init/Intermediary.java b/src/main/java/moe/nea/firmament/init/Intermediary.java new file mode 100644 index 0000000..61494d7 --- /dev/null +++ b/src/main/java/moe/nea/firmament/init/Intermediary.java @@ -0,0 +1,63 @@ +package moe.nea.firmament.init; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.MappingResolver; +import org.objectweb.asm.Type; + +import java.util.List; + +public class Intermediary { + private static final MappingResolver RESOLVER = FabricLoader.getInstance().getMappingResolver(); + + static String methodName(Object object) { + throw new AssertionError("Cannot be called at runtime"); + } + + static <T> String className() { + throw new AssertionError("Cannot be called at runtime"); + } + + static String id(String source) { + return source; + } + +// public record Class( +// Type intermediaryClass +// ) { +// public Class(String intermediaryClass) { +// this(Type.getObjectType(intermediaryClass.replace('.', '/'))); +// } +// +// public String getMappedName() { +// return RESOLVER.mapClassName("intermediary", intermediaryClass.getInternalName() +// .replace('/', '.')); +// } +// } +// +// public record Method( +// Type intermediaryClassName, +// String intermediaryMethodName, +// Type intermediaryReturnType, +// List<Type> intermediaryArgumentTypes +// ) { +// public Method( +// String intermediaryClassName, +// String intermediaryMethodName, +// String intermediaryReturnType, +// String... intermediaryArgumentTypes +// ) { +// this(intermediaryClassName, intermediaryMethodName, intermediaryReturnType, List.of(intermediaryArgumentTypes)); +// } +// +// public String getMappedMethodName() { +// return RESOLVER.mapMethodName("intermediary", +// intermediaryClassName.getInternalName().replace('/', '.')); +// } +// +// public Type getIntermediaryDescriptor() { +// return Type.getMethodType(intermediaryReturnType, intermediaryArgumentTypes.toArray(Type[]::new)); +// } +// +// +// } +} diff --git a/src/main/java/moe/nea/firmament/init/IntermediaryName.java b/src/main/java/moe/nea/firmament/init/IntermediaryName.java new file mode 100644 index 0000000..a22ad0f --- /dev/null +++ b/src/main/java/moe/nea/firmament/init/IntermediaryName.java @@ -0,0 +1,21 @@ +package moe.nea.firmament.init; + +import net.fabricmc.loader.api.MappingResolver; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Injects the intermediary name of the given field into this field by replacing its initializer with a call to + * {@link MappingResolver#mapClassName(String, String)} + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.FIELD) +public @interface IntermediaryName { + // String method() default ""; +// +// String field() default ""; + Class<?> value(); +} diff --git a/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java b/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java new file mode 100644 index 0000000..2be11a6 --- /dev/null +++ b/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java @@ -0,0 +1,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); + } + } +} diff --git a/src/main/kotlin/moe/nea/firmament/Firmament.kt b/src/main/kotlin/moe/nea/firmament/Firmament.kt index 4b742e8..400dcf2 100644 --- a/src/main/kotlin/moe/nea/firmament/Firmament.kt +++ b/src/main/kotlin/moe/nea/firmament/Firmament.kt @@ -33,6 +33,7 @@ import kotlinx.coroutines.plus import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import kotlin.coroutines.EmptyCoroutineContext +import net.minecraft.client.render.chunk.SectionBuilder import net.minecraft.command.CommandRegistryAccess import net.minecraft.util.Identifier import moe.nea.firmament.commands.registerFirmamentCommand @@ -112,6 +113,8 @@ object Firmament { ClientTickEvents.END_CLIENT_TICK.register(ClientTickEvents.EndTick { instance -> TickEvent.publish(TickEvent(tick++)) }) + // TODO: remove me + Class.forName(SectionBuilder::class.java.name) IDataHolder.registerEvents() RepoManager.initialize() SBData.init() diff --git a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt index 2289be2..c869ba4 100644 --- a/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt +++ b/src/main/kotlin/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt @@ -157,7 +157,10 @@ object CustomBlockTextures { currentIslandReplacements = replacements if (lastReplacements != replacements) { MC.nextTick { - MC.worldRenderer.reload() + MC.worldRenderer.chunks?.chunks?.forEach { + // false schedules rebuilds outside a 27 block radius to happen async + it.scheduleRebuild(false) + } } } } @@ -259,6 +262,10 @@ object CustomBlockTextures { return BakedReplacements(map.mapValues { LocationReplacements(it.value) }) } + @JvmStatic + fun patchIndigo(orig: BakedModel, pos: BlockPos, state: BlockState): BakedModel { + return getReplacementModel(state, pos) ?: orig + } @Subscribe fun onStart(event: FinalizeResourceManagerEvent) { diff --git a/src/main/resources/firmament.accesswidener b/src/main/resources/firmament.accesswidener index b69b99d..c69725f 100644 --- a/src/main/resources/firmament.accesswidener +++ b/src/main/resources/firmament.accesswidener @@ -20,4 +20,4 @@ mutable field net/minecraft/screen/slot/Slot x I mutable field net/minecraft/screen/slot/Slot y I accessible field net/minecraft/entity/player/PlayerEntity PLAYER_MODEL_PARTS Lnet/minecraft/entity/data/TrackedData; - +accessible field net/minecraft/client/render/WorldRenderer chunks Lnet/minecraft/client/render/BuiltChunkStorage; |