Add indigo support to custom block textures
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -6,5 +6,6 @@ public class EarlyRiser implements Runnable {
|
||||
public void run() {
|
||||
new ClientPlayerRiser().addTinkerers();
|
||||
new HandledScreenRiser().addTinkerers();
|
||||
new SectionBuilderRiser().addTinkerers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
63
src/main/java/moe/nea/firmament/init/Intermediary.java
Normal file
63
src/main/java/moe/nea/firmament/init/Intermediary.java
Normal file
@@ -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));
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
||||
}
|
||||
21
src/main/java/moe/nea/firmament/init/IntermediaryName.java
Normal file
21
src/main/java/moe/nea/firmament/init/IntermediaryName.java
Normal file
@@ -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();
|
||||
}
|
||||
116
src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java
Normal file
116
src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user