diff options
Diffstat (limited to 'src')
9 files changed, 251 insertions, 104 deletions
diff --git a/src/main/java/moe/nea/firmament/init/MixinPlugin.java b/src/main/java/moe/nea/firmament/init/MixinPlugin.java index 61e8f14..513efef 100644 --- a/src/main/java/moe/nea/firmament/init/MixinPlugin.java +++ b/src/main/java/moe/nea/firmament/init/MixinPlugin.java @@ -15,11 +15,13 @@ import java.util.stream.Collectors; public class MixinPlugin implements IMixinConfigPlugin { AutoDiscoveryPlugin autoDiscoveryPlugin = new AutoDiscoveryPlugin(); - public static String mixinPackage; + public static List<MixinPlugin> instances = new ArrayList<>(); + public String mixinPackage; @Override public void onLoad(String mixinPackage) { MixinExtrasBootstrap.init(); - MixinPlugin.mixinPackage = mixinPackage; + instances.add(this); + this.mixinPackage = mixinPackage; autoDiscoveryPlugin.setMixinPackage(mixinPackage); } @@ -52,7 +54,7 @@ public class MixinPlugin implements IMixinConfigPlugin { } - public static List<String> appliedMixins = new ArrayList<>(); + public List<String> appliedMixins = new ArrayList<>(); @Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { diff --git a/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java b/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java index 2c40b08..8b65946 100644 --- a/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java +++ b/src/main/java/moe/nea/firmament/init/SectionBuilderRiser.java @@ -5,6 +5,7 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.block.BlockState; import net.minecraft.client.render.block.BlockRenderManager; import net.minecraft.client.render.chunk.SectionBuilder; +import net.minecraft.client.render.model.BlockStateModel; import net.minecraft.util.math.BlockPos; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -18,100 +19,100 @@ 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"; + @IntermediaryName(SectionBuilder.class) + String SectionBuilder; + @IntermediaryName(BlockPos.class) + String BlockPos; + @IntermediaryName(BlockRenderManager.class) + String BlockRenderManager; + @IntermediaryName(BlockState.class) + String BlockState; + @IntermediaryName(BlockStateModel.class) + String BlockStateModel; + 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( - // TODO: fix this riser -// getTypeForClassName(Intermediary.<BakedModel>className()), - getTypeForClassName(Intermediary.<BlockState>className()) - ) - ); + 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.<BlockStateModel>className()), + getTypeForClassName(Intermediary.<BlockState>className()) + ) + ); - @Override - public void addTinkerers() { - if (FabricLoader.getInstance().isModLoaded("fabric-renderer-indigo")) - ClassTinkerers.addTransformation(SectionBuilder, this::handle, true); - } + @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$hookBuildRenderBlock") - || method.name.endsWith("$fabric-renderer-indigo$hookChunkBuildTessellate")) && - method.name.startsWith("redirect$")) { - handleIndigo(method); - return; - } - } - System.err.println("Could not inject indigo rendering hook. Is a custom renderer installed (e.g. sodium)?"); - } + private void handle(ClassNode classNode) { + System.out.println("AVAST! "+ getModel); + for (MethodNode method : classNode.methods) { + if ((method.name.endsWith("$fabric-renderer-indigo$hookBuildRenderBlock") + || method.name.endsWith("$fabric-renderer-indigo$hookChunkBuildTessellate")) && + method.name.startsWith("redirect$")) { + handleIndigo(method); + return; + } + } + System.err.println("Could not inject indigo rendering hook. Is a custom renderer installed (e.g. sodium)?"); + } - 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) - )); + 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); - } - } + 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(BlockStateModel), + getTypeForClassName(BlockStateModel), + getTypeForClassName(BlockPos), + getTypeForClassName(BlockState)), + false + )); + method.instructions.insert(methodInsn, insnList); + } + } } diff --git a/src/main/kotlin/Firmament.kt b/src/main/kotlin/Firmament.kt index 79f9743..7bc7d44 100644 --- a/src/main/kotlin/Firmament.kt +++ b/src/main/kotlin/Firmament.kt @@ -26,6 +26,7 @@ import net.fabricmc.loader.api.Version import net.fabricmc.loader.api.metadata.ModMetadata import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger +import org.spongepowered.asm.launch.MixinBootstrap import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job diff --git a/src/main/kotlin/commands/rome.kt b/src/main/kotlin/commands/rome.kt index c3eb03d..e6d6dfe 100644 --- a/src/main/kotlin/commands/rome.kt +++ b/src/main/kotlin/commands/rome.kt @@ -262,7 +262,8 @@ fun firmamentCommand() = literal("firmament") { source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.gametype", locrawInfo.gametype)) source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.mode", locrawInfo.mode)) source.sendFeedback(Text.stringifiedTranslatable("firmament.sbinfo.map", locrawInfo.map)) - source.sendFeedback(tr("firmament.sbinfo.custommining", "Custom Mining: ${formatBool(locrawInfo.skyblockLocation?.hasCustomMining ?: false)}")) + source.sendFeedback(tr("firmament.sbinfo.custommining", + "Custom Mining: ${formatBool(locrawInfo.skyblockLocation?.hasCustomMining ?: false)}")) } } } @@ -313,13 +314,15 @@ fun firmamentCommand() = literal("firmament") { } thenLiteral("mixins") { thenExecute { - source.sendFeedback(Text.translatable("firmament.mixins.start")) - MixinPlugin.appliedMixins - .map { it.removePrefix(MixinPlugin.mixinPackage) } - .forEach { - source.sendFeedback(Text.literal(" - ").withColor(0xD020F0) - .append(Text.literal(it).withColor(0xF6BA20))) - } + MixinPlugin.instances.forEach { plugin -> + source.sendFeedback(tr("firmament.mixins.start.package", "Mixins (base ${plugin.mixinPackage}):")) + plugin.appliedMixins + .map { it.removePrefix(plugin.mixinPackage) } + .forEach { + source.sendFeedback(Text.literal(" - ").withColor(0xD020F0) + .append(Text.literal(it).withColor(0xF6BA20))) + } + } } } thenLiteral("repo") { diff --git a/src/main/kotlin/features/debug/DeveloperFeatures.kt b/src/main/kotlin/features/debug/DeveloperFeatures.kt index 8f0c25c..af1e92e 100644 --- a/src/main/kotlin/features/debug/DeveloperFeatures.kt +++ b/src/main/kotlin/features/debug/DeveloperFeatures.kt @@ -3,6 +3,10 @@ package moe.nea.firmament.features.debug import java.io.File import java.nio.file.Path import java.util.concurrent.CompletableFuture +import org.objectweb.asm.ClassReader +import org.objectweb.asm.Type +import org.objectweb.asm.tree.ClassNode +import org.spongepowered.asm.mixin.Mixin import kotlinx.serialization.json.encodeToStream import kotlin.io.path.absolute import kotlin.io.path.exists @@ -10,11 +14,14 @@ import net.minecraft.client.MinecraftClient import net.minecraft.text.Text import moe.nea.firmament.Firmament import moe.nea.firmament.annotations.Subscribe +import moe.nea.firmament.events.DebugInstantiateEvent import moe.nea.firmament.events.TickEvent import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.gui.config.ManagedConfig +import moe.nea.firmament.init.MixinPlugin import moe.nea.firmament.util.MC import moe.nea.firmament.util.TimeMark +import moe.nea.firmament.util.asm.AsmAnnotationUtil import moe.nea.firmament.util.iterate object DeveloperFeatures : FirmamentFeature { @@ -42,6 +49,42 @@ object DeveloperFeatures : FirmamentFeature { } @Subscribe + fun loadAllMixinClasses(event: DebugInstantiateEvent) { + val allMixinClasses = mutableSetOf<String>() + MixinPlugin.instances.forEach { plugin -> + val prefix = plugin.mixinPackage + "." + val classes = plugin.mixins.map { prefix + it } + allMixinClasses.addAll(classes) + for (cls in classes) { + val targets = javaClass.classLoader.getResourceAsStream("${cls.replace(".", "/")}.class").use { + val node = ClassNode() + ClassReader(it).accept(node, 0) + val mixins = mutableListOf<Mixin>() + (node.visibleAnnotations.orEmpty() + node.invisibleAnnotations.orEmpty()).forEach { + val annotationType = Type.getType(it.desc) + val mixinType = Type.getType(Mixin::class.java) + if (mixinType == annotationType) { + mixins.add(AsmAnnotationUtil.createProxy(Mixin::class.java, it)) + } + } + mixins.flatMap { it.targets.toList() } + mixins.flatMap { it.value.map { it.java.name } } + } + for (target in targets) + try { + Firmament.logger.debug("Loading ${target} to force instantiate ${cls}") + Class.forName(target, true, javaClass.classLoader) + } catch (ex: Throwable) { + Firmament.logger.error("Could not load class ${target} that has been mixind by $cls", ex) + } + } + } + Firmament.logger.info("Forceloaded all Firmament mixins:") + val applied = MixinPlugin.instances.flatMap { it.appliedMixins }.toSet() + applied.forEach { Firmament.logger.info(" - ${it}") } + require(allMixinClasses == applied) + } + + @Subscribe fun dumpMissingTranslations(tickEvent: TickEvent) { val toDump = missingTranslations ?: return missingTranslations = null diff --git a/src/main/kotlin/util/asm/AsmAnnotationUtil.kt b/src/main/kotlin/util/asm/AsmAnnotationUtil.kt new file mode 100644 index 0000000..fb0e92c --- /dev/null +++ b/src/main/kotlin/util/asm/AsmAnnotationUtil.kt @@ -0,0 +1,89 @@ +package moe.nea.firmament.util.asm + +import com.google.common.base.Defaults +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method +import java.lang.reflect.Proxy +import org.objectweb.asm.Type +import org.objectweb.asm.tree.AnnotationNode + +object AsmAnnotationUtil { + class AnnotationProxy( + val originalType: Class<out Annotation>, + val annotationNode: AnnotationNode, + ) : InvocationHandler { + val offsets = annotationNode.values.withIndex() + .chunked(2) + .map { it.first() } + .associate { (idx, value) -> value as String to idx + 1 } + + fun nestArrayType(depth: Int, comp: Class<*>): Class<*> = + if (depth == 0) comp + else java.lang.reflect.Array.newInstance(nestArrayType(depth - 1, comp), 0).javaClass + + fun unmap( + value: Any?, + comp: Class<*>, + depth: Int, + ): Any? { + value ?: return null + if (depth > 0) + return ((value as List<Any>) + .map { unmap(it, comp, depth - 1) } as java.util.List<Any>) + .toArray(java.lang.reflect.Array.newInstance(nestArrayType(depth - 1, comp), 0) as Array<*>) + if (comp.isEnum) { + comp as Class<out Enum<*>> + when (value) { + is String -> return java.lang.Enum.valueOf(comp, value) + is List<*> -> return java.lang.Enum.valueOf(comp, value[1] as String) + else -> error("Unknown enum variant $value for $comp") + } + } + when (value) { + is Type -> return Class.forName(value.className) + is AnnotationNode -> return createProxy(comp as Class<out Annotation>, value) + is String, is Boolean, is Byte, is Double, is Int, is Float, is Long, is Short, is Char -> return value + } + error("Unknown enum variant $value for $comp") + } + + fun defaultFor(fullType: Class<*>): Any? { + if (fullType.isArray) return java.lang.reflect.Array.newInstance(fullType.componentType, 0) + if (fullType.isPrimitive) { + return Defaults.defaultValue(fullType) + } + if (fullType == String::class.java) + return "" + return null + } + + override fun invoke( + proxy: Any, + method: Method, + args: Array<out Any?>? + ): Any? { + val name = method.name + val ret = method.returnType + val retU = generateSequence(ret) { if (it.isArray) it.componentType else null } + .toList() + val arrayDepth = retU.size - 1 + val componentType = retU.last() + + val off = offsets[name] + if (off == null) { + return defaultFor(ret) + } + return unmap(annotationNode.values[off], componentType, arrayDepth) + } + } + + fun <T : Annotation> createProxy( + annotationClass: Class<T>, + annotationNode: AnnotationNode + ): T { + require(Type.getType(annotationClass) == Type.getType(annotationNode.desc)) + return Proxy.newProxyInstance(javaClass.classLoader, + arrayOf(annotationClass), + AnnotationProxy(annotationClass, annotationNode)) as T + } +} diff --git a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt index 79d1e46..462b1e1 100644 --- a/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt +++ b/src/texturePacks/java/moe/nea/firmament/features/texturepack/CustomBlockTextures.kt @@ -316,6 +316,15 @@ object CustomBlockTextures { ) } + /** + * Used by [moe.nea.firmament.init.SectionBuilderRiser] + */ + + @JvmStatic + fun patchIndigo(original: BlockStateModel, pos: BlockPos?, state: BlockState): BlockStateModel { + return getReplacementModel(state, pos) ?: original + } + @JvmStatic fun collectExtraModels(modelsCollector: ReferencedModelsCollector) { preparationFuture.join().collectAllReplacements() diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/PatchLegacyTexturePathsIntoArmorLayers.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/PatchLegacyTexturePathsIntoArmorLayers.java index f829da0..0fb6bf8 100644 --- a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/PatchLegacyTexturePathsIntoArmorLayers.java +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/PatchLegacyTexturePathsIntoArmorLayers.java @@ -26,7 +26,6 @@ public class PatchLegacyTexturePathsIntoArmorLayers { // legacy format: "assets/{identifier.namespace}/textures/models/armor/{identifier.path}_layer_{isLegs ? 2 : 1}{suffix}.png" // suffix is sadly not available to us here. this means leather armor will look a bit shite var legacyIdentifier = this.textureId.withPath((textureName) -> { - String var10000 = layerType.asString(); return "textures/models/armor/" + textureName + "_layer_" + (layerType == EquipmentModel.LayerType.HUMANOID_LEGGINGS ? 2 : 1) + ".png"; diff --git a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java index 97abd1f..f2a7409 100644 --- a/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java +++ b/src/texturePacks/java/moe/nea/firmament/mixins/custommodels/ReplaceItemModelPatch.java @@ -26,7 +26,7 @@ public class ReplaceItemModelPatch implements IntrospectableItemModelManager { private Function<Identifier, ItemModel> modelGetter; @WrapOperation( - method = "update(Lnet/minecraft/client/render/item/ItemRenderState;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ModelTransformationMode;Lnet/minecraft/world/World;Lnet/minecraft/entity/LivingEntity;I)V", + method = "update", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;get(Lnet/minecraft/component/ComponentType;)Ljava/lang/Object;")) private Object replaceItemModelByIdentifier(ItemStack instance, ComponentType componentType, Operation<Object> original) { var override = CustomItemModelEvent.getModelIdentifier(instance, this); |