From ed6ba740bb49a3d0ea020e9d51088629d45e861a Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Mon, 6 Apr 2020 20:34:43 +0200 Subject: [PATCH] Workaround OF breaking spectator hand mixin --- .../core/ReplayModMixinConfigPlugin.java | 75 +++++++++++++++++++ ...java => Mixin_ShowSpectatedHand_NoOF.java} | 2 +- .../mixin/Mixin_ShowSpectatedHand_OF.java | 42 +++++++++++ .../resources/mixins.replay.replaymod.json | 4 +- 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/replaymod/core/ReplayModMixinConfigPlugin.java rename src/main/java/com/replaymod/replay/mixin/{MixinGameRenderer.java => Mixin_ShowSpectatedHand_NoOF.java} (96%) create mode 100644 src/main/java/com/replaymod/replay/mixin/Mixin_ShowSpectatedHand_OF.java diff --git a/src/main/java/com/replaymod/core/ReplayModMixinConfigPlugin.java b/src/main/java/com/replaymod/core/ReplayModMixinConfigPlugin.java new file mode 100644 index 00000000..4bdd6a7a --- /dev/null +++ b/src/main/java/com/replaymod/core/ReplayModMixinConfigPlugin.java @@ -0,0 +1,75 @@ +package com.replaymod.core; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +//#if MC>=11400 +import java.io.InputStream; +//#else +//$$ import net.minecraft.launchwrapper.Launch; +//#endif + +public class ReplayModMixinConfigPlugin implements IMixinConfigPlugin { + static boolean hasClass(String name) throws IOException { + //#if MC>=11400 + InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(name.replace('.', '/') + ".class"); + if (stream != null) stream.close(); + return stream != null; + //#else + //$$ return Launch.classLoader.getClassBytes(name) != null; + //#endif + } + + private final Logger logger = LogManager.getLogger("replaymod/mixin"); + private final boolean hasOF = hasClass("optifine.OptiFineForgeTweaker") || hasClass("me.modmuss50.optifabric.mod.Optifabric"); + + { + logger.error("hasOF: " + hasOF); + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + if (mixinClassName.endsWith("_OF")) return hasOF; + if (mixinClassName.endsWith("_NoOF")) return !hasOF; + return true; + } + + @Override + public void onLoad(String mixinPackage) { + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + public ReplayModMixinConfigPlugin() throws IOException { + } +} diff --git a/src/main/java/com/replaymod/replay/mixin/MixinGameRenderer.java b/src/main/java/com/replaymod/replay/mixin/Mixin_ShowSpectatedHand_NoOF.java similarity index 96% rename from src/main/java/com/replaymod/replay/mixin/MixinGameRenderer.java rename to src/main/java/com/replaymod/replay/mixin/Mixin_ShowSpectatedHand_NoOF.java index 5823e7ce..d4b208dc 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinGameRenderer.java +++ b/src/main/java/com/replaymod/replay/mixin/Mixin_ShowSpectatedHand_NoOF.java @@ -13,7 +13,7 @@ import org.spongepowered.asm.mixin.injection.Redirect; import static com.replaymod.core.versions.MCVer.getMinecraft; @Mixin(GameRenderer.class) -public abstract class MixinGameRenderer { +public abstract class Mixin_ShowSpectatedHand_NoOF { @Redirect( method = "renderHand", at = @At( diff --git a/src/main/java/com/replaymod/replay/mixin/Mixin_ShowSpectatedHand_OF.java b/src/main/java/com/replaymod/replay/mixin/Mixin_ShowSpectatedHand_OF.java new file mode 100644 index 00000000..684c6a51 --- /dev/null +++ b/src/main/java/com/replaymod/replay/mixin/Mixin_ShowSpectatedHand_OF.java @@ -0,0 +1,42 @@ +//#if MC>=11400 +package com.replaymod.replay.mixin; + +import com.replaymod.replay.camera.CameraEntity; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.world.GameMode; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import static com.replaymod.core.versions.MCVer.getMinecraft; + +@Pseudo +@Mixin(targets = "net.optifine.shaders.Shaders", remap = false) +public abstract class Mixin_ShowSpectatedHand_OF { + // OptiFine redirects renderHand to a new method with the same name but with more arguments + // since that method is only added by Optifabric, we can't inject our redirect in there (Mixin won't see it). + // So instead we fake the getCurrentGameMode for an entire section of the new renderHand method by injecting + // into `Shaders.setRenderingFirstPersonHand` which OF calls before and after our original target. + // We could also just inject into HEAD and RETURN of the original renderHand method but then we'd miss any calls + // which OF manually does to its method (and given it has extra parameters, I'd assume that it'll do some). + + @Inject(method = "setRenderingFirstPersonHand", at = @At("HEAD"), remap = false) + private static void fakePlayerGameMode(boolean renderingHand, CallbackInfo ci) { + ClientPlayerEntity camera = getMinecraft().player; + if (camera instanceof CameraEntity) { + ClientPlayerInteractionManager interactionManager = getMinecraft().interactionManager; + assert interactionManager != null; + if (renderingHand) { + // alternative doesn't really matter, the caller only checks for equality to SPECTATOR + interactionManager.setGameMode(camera.isSpectator() ? GameMode.SPECTATOR : GameMode.SURVIVAL); + } else { + // reset back to spectator (we're always in spectator during a replay) + interactionManager.setGameMode(GameMode.SPECTATOR); + } + } + } +} +//#endif diff --git a/src/main/resources/mixins.replay.replaymod.json b/src/main/resources/mixins.replay.replaymod.json index 0a717fd1..67b9da55 100644 --- a/src/main/resources/mixins.replay.replaymod.json +++ b/src/main/resources/mixins.replay.replaymod.json @@ -1,6 +1,7 @@ { "required": true, "package": "com.replaymod.replay.mixin", + "plugin": "com.replaymod.core.ReplayModMixinConfigPlugin", "mixins": [], "server": [], "client": [ @@ -10,7 +11,8 @@ //#endif "EntityLivingBaseAccessor", //#if MC>=11400 - "MixinGameRenderer", + "Mixin_ShowSpectatedHand_NoOF", + "Mixin_ShowSpectatedHand_OF", //#else //$$ "EntityOtherPlayerMPAccessor", //#endif