diff --git a/build.gradle.kts b/build.gradle.kts index 7a6853fea..2f47500f3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,7 +23,15 @@ import groovy.json.JsonSlurper import net.fabricmc.loom.api.LoomGradleExtensionAPI import net.fabricmc.loom.task.RemapJarTask import org.gradle.configurationcache.extensions.capitalized +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.tree.AnnotationNode +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.FieldNode +import org.objectweb.asm.tree.MethodNode import java.io.ByteArrayOutputStream +import java.util.* +import java.util.function.Predicate import java.util.jar.JarEntry import java.util.jar.JarFile import java.util.jar.JarOutputStream @@ -159,7 +167,7 @@ subprojects { dependsOn(shadowJar) archiveClassifier = null doLast { - squishJar(outputs.files.singleFile) + transformJar(outputs.files.singleFile) } } @@ -237,7 +245,7 @@ subprojects { } } -fun squishJar(jar: File) { +fun transformJar(jar: File) { val contents = linkedMapOf() JarFile(jar).use { it.entries().asIterator().forEach { entry -> @@ -257,6 +265,8 @@ fun squishJar(jar: File) { if (name.endsWith(".json") || name.endsWith(".mcmeta")) { data = (JsonOutput.toJson(JsonSlurper().parse(data)).toByteArray()) + } else if (name.endsWith(".class")) { + data = transformClass(data) } out.putNextEntry(JarEntry(name)) @@ -268,6 +278,35 @@ fun squishJar(jar: File) { } } +fun transformClass(bytes: ByteArray): ByteArray { + val node = ClassNode() + ClassReader(bytes).accept(node, 0) + + // Remove Methods & Field Annotated with @DevEnvMixin + node.methods.removeIf { methodNode: MethodNode -> removeIfDevMixin(node.name, methodNode.visibleAnnotations) } + // Disabled as I don't feel ok with people being able to remove these + //node.fields.removeIf { fieldNode: FieldNode -> removeIfDevMixin(fieldNode.visibleAnnotations) } + + return ClassWriter(0).also { node.accept(it) }.toByteArray() +} + +fun removeIfDevMixin(nodeName: String, visibleAnnotations: List?): Boolean { + // Don't remove methods if it's not a GHA build/Release build + if (buildNumber == null || !nodeName.lowercase(Locale.ROOT).matches(Regex(".*\\/mixin\\/.*Mixin"))) + return false + + if (visibleAnnotations != null) { + for (annotationNode in visibleAnnotations) { + if (annotationNode.desc == "Lcom/railwayteam/railways/annotation/mixin/DevEnvMixin;") { + println("Removed Method/Field Annotated With @DevEnvMixin from: $nodeName") + return true + } + } + } + + return false +} + tasks.create("railwaysPublish") { when (val platform = System.getenv("PLATFORM")) { "both" -> { diff --git a/common/src/main/java/com/railwayteam/railways/annotation/mixin/DevMixin.java b/common/src/main/java/com/railwayteam/railways/annotation/mixin/DevEnvMixin.java similarity index 81% rename from common/src/main/java/com/railwayteam/railways/annotation/mixin/DevMixin.java rename to common/src/main/java/com/railwayteam/railways/annotation/mixin/DevEnvMixin.java index 37a66ffd5..e496b657d 100644 --- a/common/src/main/java/com/railwayteam/railways/annotation/mixin/DevMixin.java +++ b/common/src/main/java/com/railwayteam/railways/annotation/mixin/DevEnvMixin.java @@ -23,6 +23,9 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.TYPE) +/** + * You bear all the consequences of using this. Don't shoot yourself in the foot I guess? Try wearing proper mixin equipment? + */ +@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) -public @interface DevMixin { } +public @interface DevEnvMixin {} diff --git a/common/src/main/java/com/railwayteam/railways/mixin/MixinStationBlock.java b/common/src/main/java/com/railwayteam/railways/mixin/MixinStationBlock.java index 6226830d5..d3d0de687 100644 --- a/common/src/main/java/com/railwayteam/railways/mixin/MixinStationBlock.java +++ b/common/src/main/java/com/railwayteam/railways/mixin/MixinStationBlock.java @@ -110,8 +110,6 @@ private void autoWhistle(BlockState pState, Level pLevel, BlockPos pPos, Player return; } - //System.out.println("DB1"); - if (train.runtime.getSchedule() != null && !train.runtime.isAutoSchedule) { ItemStack scheduleStack = train.runtime.returnSchedule(); if (!scheduleStack.isEmpty()) { diff --git a/common/src/main/java/com/railwayteam/railways/mixin/client/MixinPlayerInfo.java b/common/src/main/java/com/railwayteam/railways/mixin/client/MixinPlayerInfo.java index 2098d8f9a..bc1c84dfa 100644 --- a/common/src/main/java/com/railwayteam/railways/mixin/client/MixinPlayerInfo.java +++ b/common/src/main/java/com/railwayteam/railways/mixin/client/MixinPlayerInfo.java @@ -21,8 +21,8 @@ import com.mojang.authlib.GameProfile; import com.mojang.authlib.minecraft.MinecraftProfileTexture; import com.railwayteam.railways.Railways; +import com.railwayteam.railways.annotation.mixin.DevEnvMixin; import com.railwayteam.railways.util.DevCapeUtils; -import com.railwayteam.railways.util.Utils; import net.minecraft.client.multiplayer.PlayerInfo; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.Nullable; @@ -47,15 +47,14 @@ public class MixinPlayerInfo { @Unique private boolean railways$texturesLoaded; @Unique private static final ResourceLocation DEV_CAPE = Railways.asResource("textures/misc/dev_cape.png"); - // Replaces skin inside the dev env with the conductor skin, won't crash if it fails to apply - @Inject(method = "getSkinLocation", at = @At("HEAD"), require = 0) + // Replaces skin inside the dev env with the conductor skin + @DevEnvMixin + @Inject(method = "getSkinLocation", at = @At("HEAD")) private void registerSkinTextures(CallbackInfoReturnable cir) { - if (Utils.isDevEnv()) { - this.textureLocations.put( - MinecraftProfileTexture.Type.SKIN, - Railways.asResource("textures/misc/devenv_skin.png") - ); - } + this.textureLocations.put( + MinecraftProfileTexture.Type.SKIN, + Railways.asResource("textures/misc/devenv_skin.png") + ); } @Inject(method = "getCapeLocation", at = @At("HEAD")) @@ -74,16 +73,14 @@ private void skipCapeIfNeeded(CallbackInfoReturnable cir) { } // Replaces skin model inside the dev env with the default "steve" skin model + @DevEnvMixin @Inject(method = "registerTextures", at = @At( value = "INVOKE", target = "Lnet/minecraft/client/resources/SkinManager;registerSkins(Lcom/mojang/authlib/GameProfile;Lnet/minecraft/client/resources/SkinManager$SkinTextureCallback;Z)V" - ), - require = 0 // Dev Env Mixin, Shouldn't crash if it fails + ) ) private void railways$setModelToLarge(CallbackInfo ci) { - if (Utils.isDevEnv()) { - skinModel = "default"; - } + skinModel = "default"; } } diff --git a/common/src/main/java/com/railwayteam/railways/util/ConditionalMixinManager.java b/common/src/main/java/com/railwayteam/railways/util/ConditionalMixinManager.java index 80f3e3cb6..dc9f9b123 100644 --- a/common/src/main/java/com/railwayteam/railways/util/ConditionalMixinManager.java +++ b/common/src/main/java/com/railwayteam/railways/util/ConditionalMixinManager.java @@ -25,7 +25,7 @@ package com.railwayteam.railways.util; import com.railwayteam.railways.annotation.mixin.ConditionalMixin; -import com.railwayteam.railways.annotation.mixin.DevMixin; +import com.railwayteam.railways.annotation.mixin.DevEnvMixin; import com.railwayteam.railways.compat.Mods; import com.railwayteam.railways.mixin.CRMixinPlugin; import org.objectweb.asm.Type; @@ -51,7 +51,7 @@ public static boolean shouldApply(String className) { shouldApply = anyModsLoaded == applyIfPresent; CRMixinPlugin.LOGGER.debug("{} is{}being applied because the mod(s) {} are{}loaded", className, shouldApply ? " " : " not ", mods, anyModsLoaded ? " " : " not "); } - if (node.desc.equals(Type.getDescriptor(DevMixin.class))) { + if (node.desc.equals(Type.getDescriptor(DevEnvMixin.class))) { shouldApply &= Utils.isDevEnv(); } }