From ae17fdb8c1af4b6aed7c9acee6a2abec4a86d642 Mon Sep 17 00:00:00 2001 From: ZekerZhayard Date: Sat, 25 Apr 2020 18:22:08 +0800 Subject: [PATCH] Support OptiForge running under development environment. Add the function about removing OptiFine class locator if MixinBootstrap didn't install. --- README.md | 18 ++- build.gradle | 91 +++++------ gradle.properties | 5 +- .../optiforge/asm/MixinConfigPlugin.java | 7 +- .../FakeOptiFineTransformationService.java | 43 ++++++ .../fml/OptiForgeTransformationService.java | 144 +++++++++--------- .../asm/fml/OptiForgeTransformer.java | 45 +++--- .../optiforge/asm/fml/VersionChecker.java | 38 +++-- .../client/renderer/MixinOverlayRenderer.java | 4 +- .../client/world/MixinClientWorld.java | 2 - .../net/minecraft/entity/MixinMobEntity.java | 19 ++- .../renderer/OverlayRendererTransformer.java | 8 +- .../EntityRendererManagerTransformer.java | 3 +- .../entity/ItemFrameRendererTransformer.java | 3 +- src/main/resources/META-INF/MANIFEST.MF | 1 + src/main/resources/mixins.optiforge.json | 1 - src/main/resources/requiredmods.properties | 3 + 17 files changed, 265 insertions(+), 170 deletions(-) create mode 100644 src/main/java/io/github/zekerzhayard/optiforge/asm/fml/FakeOptiFineTransformationService.java create mode 100644 src/main/resources/META-INF/MANIFEST.MF create mode 100644 src/main/resources/requiredmods.properties diff --git a/README.md b/README.md index e82ee9f..fa20fda 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # OptiForge ([CurseForge](https://www.curseforge.com/minecraft/mc-mods/optiforge)) -*Compatibility: (2020-04-20)* - *OptiForge-0.1.11* +*Compatibility: (2020-04-25)* + *OptiForge-0.1.12*  *Forge-1.15.2-31.1.39 or newer version*  *OptiFine-1.15.2-G1-pre7 or newer version* @@ -15,5 +15,19 @@ *Notice: If you find a bug, please report it to OptiForge first unless you are sure it is an OptiFine bug.* +### For Development Environment + +1. There is a project called [OptiFineDevTweaker](https://github.com/OpenCubicChunks/OptiFineDevTweaker) can help us use OptiFine under development environment. You should put OptiFine itself and OptiFineDevTweaker mod to `/run/mods` folder. +2. If your project doesn't depend on Mixin, you should put MixinBootstrap mod to mods folder. +3. You should also put OptiForge jar with `-deobf` suffix to mods folder. + +*Notice:* +*1. If you redefined your Gradle user home, you should add a VM argument:* +*`-Dofdev.mcjar=\caches\forge_gradle\minecraft_repo\versions\1.15.2\client.jar`* +*2. OptiFineDevTweaker will dump all deobfuscated OptiFine classes to `./run/.optifineDev.classes` folder.* +*3. There are some useful Mixin properties for debugging: [Mixin Java System Properties](https://github.com/SpongePowered/Mixin/wiki/Mixin-Java-System-Properties)* +*I recommend you add `-Dmixin.debug.export=true`, it can dump all mixed classes to `./run/.mixin.out/class` folder.* +*4. The recommended mcp mappings version is `snapshot_20200407-1.15.1`* + ## Screenshots ![OF0.png](https://i.loli.net/2020/03/31/IBfv1ShQt7wVY2u.png) diff --git a/build.gradle b/build.gradle index cf221aa..1ee5f15 100644 --- a/build.gradle +++ b/build.gradle @@ -46,41 +46,7 @@ minecraft { property "forge.logging.console.level", "debug" mods { - examplemod { - source sourceSets.main - } - } - } - - server { - workingDirectory project.file("run") - - // Recommended logging data for a userdev environment - property "forge.logging.markers", "SCAN,REGISTRIES,REGISTRYDUMP" - - // Recommended logging level for the console - property "forge.logging.console.level", "debug" - - mods { - examplemod { - source sourceSets.main - } - } - } - - data { - workingDirectory project.file("run") - - // Recommended logging data for a userdev environment - property "forge.logging.markers", "SCAN,REGISTRIES,REGISTRYDUMP" - - // Recommended logging level for the console - property "forge.logging.console.level", "debug" - - args "--mod", "examplemod", "--all", "--output", file("src/generated/resources/") - - mods { - examplemod { + optiforge { source sourceSets.main } } @@ -124,18 +90,53 @@ dependencies { } // Example for how to get properties into the manifest for reading by the runtime.. +def attrs = [ + "Specification-Title": "OptiForge", + "Specification-Vendor": "ZekerZhayard", + "Specification-Version": "1", // We are version 1 of ourselves + "Implementation-Title": project.name, + "Implementation-Version": "${version}", + "Implementation-Vendor" :"ZekerZhayard", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + "MixinConnector": "io.github.zekerzhayard.optiforge.asm.MixinConnector" +] + jar { manifest { - attributes([ - "Specification-Title": "OptiForge", - "Specification-Vendor": "ZekerZhayard", - "Specification-Version": "1", // We are version 1 of ourselves - "Implementation-Title": project.name, - "Implementation-Version": "${version}", - "Implementation-Vendor" :"ZekerZhayard", - "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - "MixinConnector": "io.github.zekerzhayard.optiforge.asm.MixinConnector" - ]) + attributes(attrs) + } +} + +task sourcesJar(type: Jar) { + manifest { + attributes(attrs) + } + from sourceSets.main.allSource + archiveName = "${archivesBaseName}-${version}-sources.${extension}" +} + +task deobfJar(type: Jar) { + manifest { + attributes(attrs) + } + from sourceSets.main.output + archiveName = "${archivesBaseName}-${version}-deobf.${extension}" +} + +artifacts { + archives sourcesJar + archives deobfJar +} + +processResources { + def pros = ["minecraft_version": "${minecraft_version}", "optifine_version": "${optifine_version}", "forge_version": "${forge_version}"] + inputs.properties pros + from sourceSets.main.resources.srcDirs, { + include "requiredmods.properties" + expand pros + } + from sourceSets.main.resources.srcDirs, { + exclude "requiredmods.properties" } } diff --git a/gradle.properties b/gradle.properties index 6228f7d..721a6a1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,10 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false -mod_version=0.1.11 +mod_version=0.1.12 +# G1_pre7 +optifine_version=7.1.7 +minecraft_version=1.15.2 forge_version=31.1.39 forge_gradle_version=3.0.169 mcp_mappings=20200407-1.15.1 \ No newline at end of file diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/MixinConfigPlugin.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/MixinConfigPlugin.java index 6bd3cf5..b93998c 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/MixinConfigPlugin.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/MixinConfigPlugin.java @@ -6,6 +6,7 @@ import java.util.ServiceLoader; import java.util.Set; +import cpw.mods.modlauncher.Launcher; import io.github.zekerzhayard.optiforge.asm.transformers.ITransformer; import io.github.zekerzhayard.optiforge.asm.utils.ASMUtils; import net.minecraftforge.fml.loading.FMLLoader; @@ -16,7 +17,6 @@ import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; -import org.spongepowered.asm.util.Bytecode; public class MixinConfigPlugin implements IMixinConfigPlugin { private final static Logger LOGGER = LogManager.getLogger("OptiForge"); @@ -53,13 +53,14 @@ public void onLoad(String mixinPackage) { this.checked = true; } catch (Exception e) { LOGGER.error("An unexpected issue occurred when loading transformers and all mixin classes will not apply: ", e); - checked = false; + this.checked = false; } } @Override public String getRefMapperConfig() { - return null; + // If we are under development environment, we needn't reference mappings. + return Launcher.INSTANCE.environment().findNameMapping("srg").isPresent() ? null : "mixins.optiforge.refmap.json"; } @Override diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/FakeOptiFineTransformationService.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/FakeOptiFineTransformationService.java new file mode 100644 index 0000000..733982b --- /dev/null +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/FakeOptiFineTransformationService.java @@ -0,0 +1,43 @@ +package io.github.zekerzhayard.optiforge.asm.fml; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import javax.annotation.Nonnull; + +import cpw.mods.modlauncher.api.IEnvironment; +import cpw.mods.modlauncher.api.ITransformationService; +import cpw.mods.modlauncher.api.ITransformer; + +/** + * If someone used wrong forge version or didn't install MixinBootstrap, use this class to replace OptiFineTransformationService. + */ +public class FakeOptiFineTransformationService implements ITransformationService { + @Nonnull + @Override + public String name() { + return "OptiFine"; + } + + @Override + public void initialize(@Nonnull IEnvironment environment) { + + } + + @Override + public void beginScanning(@Nonnull IEnvironment environment) { + + } + + @Override + public void onLoad(@Nonnull IEnvironment env, @Nonnull Set otherServices) { + + } + + @Nonnull + @Override + @SuppressWarnings("rawtypes") + public List transformers() { + return Collections.emptyList(); + } +} diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/OptiForgeTransformationService.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/OptiForgeTransformationService.java index a541fe8..eaf0af0 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/OptiForgeTransformationService.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/OptiForgeTransformationService.java @@ -1,11 +1,13 @@ package io.github.zekerzhayard.optiforge.asm.fml; +import java.lang.reflect.Constructor; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; import javax.annotation.Nonnull; @@ -15,6 +17,7 @@ import cpw.mods.modlauncher.TransformList; import cpw.mods.modlauncher.TransformStore; import cpw.mods.modlauncher.TransformTargetLabel; +import cpw.mods.modlauncher.TransformationServiceDecorator; import cpw.mods.modlauncher.api.IEnvironment; import cpw.mods.modlauncher.api.ITransformationService; import cpw.mods.modlauncher.api.ITransformer; @@ -25,20 +28,31 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.objectweb.asm.Type; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.MethodNode; +// Method invoker orderer: +// +// +// name (more than once) +// onLoad +// arguments (parallel) +// argumentValues (parallel) +// initialize +// runScan +// (beginScanning) +// transformers +// additionalClassesLocator +// additionalResourcesLocator public class OptiForgeTransformationService implements ITransformationService { private final static Logger LOGGER = LogManager.getLogger(); - private static String name = "optiforge"; + private final static String NAME; private static boolean checked; static { + NAME = "optiforge"; // Avoid compiler to replace strings automatically. try { - // This can make sure this class is always ordered after FMLServiceProvider. - FieldUtils.writeDeclaredField(name, "hash", "fml".hashCode() + 1, true); + // This can make sure this class is always ordered after all other services. + FieldUtils.writeDeclaredField(NAME, "hash", -65536, true); } catch (IllegalAccessException e) { LOGGER.error("", e); } @@ -47,41 +61,50 @@ public class OptiForgeTransformationService implements ITransformationService { @Nonnull @Override public String name() { - return name; + return NAME; } - // This method is called after FMLServiceProvider#argumentValues, but before FMLServiceProvider#runScan, - // so we can check Forge version and add custom mod locator. + // This method is called before FMLServiceProvider#runScan, so we can check Forge version and add custom mod locator. @Override - public void argumentValues(@Nonnull OptionResult option) { - String currentFMLVersion = null; + @SuppressWarnings("unchecked") + public void initialize(@Nonnull IEnvironment environment) { + StringBuilder currentFMLVersion = new StringBuilder(); + Object transformationServicesHandler = new Object(); try { - Object transformationServicesHandler = FieldUtils.readDeclaredField(Launcher.INSTANCE, "transformationServicesHandler", true); - @SuppressWarnings("unchecked") + transformationServicesHandler = FieldUtils.readDeclaredField(Launcher.INSTANCE, "transformationServicesHandler", true); ServiceLoader transformationServices = (ServiceLoader) FieldUtils.readDeclaredField(transformationServicesHandler, "transformationServices", true); - Optional transformationService = ServiceLoaderStreamUtils.map(transformationServices, ts -> ts).filter(ts -> ts.getClass().equals(FMLServiceProvider.class)).findFirst(); - if (transformationService.isPresent()) { - currentFMLVersion = (String) FieldUtils.readDeclaredField(transformationService.get(), "targetForgeVersion", true); - } + ServiceLoaderStreamUtils.map(transformationServices, ts -> ts).filter(ts -> ts.getClass().equals(FMLServiceProvider.class)).findFirst().ifPresent(ts -> { + try { + // The method "initialize" is called after "argumentValues", so we can read the field "targetForgeVersion" directly. + currentFMLVersion.append(FieldUtils.readDeclaredField(ts, "targetForgeVersion", true)); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); } catch (Exception e) { - LOGGER.error("An unexpected issue occurred when detect Forge version: ", e); + LOGGER.error("An unexpected issue occurred when detecting Forge version, ignore this if you are under development environment: ", e); } - if (checked = (VersionChecker.checkOptiFineVersion(VersionChecker.DEFAULT_FUNCTION) && VersionChecker.checkMixinVersion(VersionChecker.DEFAULT_FUNCTION) && VersionChecker.checkForgeVersion(VersionChecker.DEFAULT_FUNCTION, currentFMLVersion))) { - try { + try { + Path path = Paths.get(OptiForgeTransformationService.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + // We should check if it is under development environments or loaded required versions successfully. + if (checked = Files.isDirectory(path) || currentFMLVersion.length() == 0 || (VersionChecker.IS_LOADED && VersionChecker.checkOptiFineVersion(VersionChecker.DEFAULT_FUNCTION) && VersionChecker.checkMixinVersion(VersionChecker.DEFAULT_FUNCTION) && VersionChecker.checkForgeVersion(VersionChecker.DEFAULT_FUNCTION, currentFMLVersion.toString()))) { // FML can't detect IModLocator when ITransformationService exists in the same jar, so we must add it manually. - ModDirTransformerDiscoverer.getExtraLocators().add(Paths.get(OptiForgeTransformationService.class.getProtectionDomain().getCodeSource().getLocation().toURI())); - } catch (Exception e) { - LOGGER.error("An unexpected issue occurred when adding custom mod locators: ", e); + ModDirTransformerDiscoverer.getExtraLocators().add(path); + } else { + // If required mods don't exist, we should remove OptiFineTransformationService. + Map serviceLookup = (Map) FieldUtils.readDeclaredField(transformationServicesHandler, "serviceLookup", true); + if (serviceLookup.containsKey("OptiFine")) { + Constructor constructor = TransformationServiceDecorator.class.getDeclaredConstructor(ITransformationService.class); + constructor.setAccessible(true); + serviceLookup.put("OptiFine", constructor.newInstance(new FakeOptiFineTransformationService())); + } } + } catch (Exception e) { + LOGGER.error("An unexpected issue occurred when checking versions: ", e); } } - @Override - public void initialize(@Nonnull IEnvironment environment) { - - } - @Override public void beginScanning(@Nonnull IEnvironment environment) { @@ -100,54 +123,29 @@ public void onLoad(@Nonnull IEnvironment env, @Nonnull Set otherServices }) public List transformers() { List list = new ArrayList<>(); - // see https://github.com/cpw/modlauncher/issues/37 - // OptiFine transformer overwrite all JavaScript field and method transformers. - // We are now called after FMLServiceProvider#transformers so that we can wrap JavaScript core mod system. - // But we still have no idea to wrap all transformers which added by other transformation service systems. - try { - Object transformationServicesHandler = FieldUtils.readDeclaredField(Launcher.INSTANCE, "transformationServicesHandler", true); - TransformStore transformStore = (TransformStore) FieldUtils.readDeclaredField(transformationServicesHandler, "transformStore", true); - EnumMap> transformers = (EnumMap>) FieldUtils.readDeclaredField(transformStore, "transformers", true); - - // If OptiFine exists but Mixin doesn't, remove OptiFine transformer to avoid crash. - // Else we must wrap all JavaScript transformers. - if (!checked) { - TransformList classList = (TransformList) transformers.get(TransformTargetLabel.LabelType.CLASS); - Map>> classTransformers = (Map>>) FieldUtils.readDeclaredField(classList, "transformers", true); - for (List> value : classTransformers.values()) { - value.removeIf(t -> { - try { - // assert t.getClass().getName().equals("cpw.mods.modlauncher.TransformerHolder"); - return FieldUtils.readDeclaredField(t, "wrapped", true).getClass().getName().equals("optifine.OptiFineTransformer"); - } catch (Exception e) { - LOGGER.error("", e); - return false; - } - }); - } - } else { - // Wrap all method transformers. - TransformList methodList = (TransformList) transformers.get(TransformTargetLabel.LabelType.METHOD); - Map>> methodTransformers = (Map>>) FieldUtils.readDeclaredField(methodList, "transformers", true); - for (Map.Entry>> entry : methodTransformers.entrySet()) { - for (ITransformer transformer : entry.getValue()) { - list.add(new OptiForgeTransformer(transformer, ITransformer.Target.targetMethod(((Type) MethodUtils.invokeMethod(entry.getKey(), true, "getClassName")).getInternalName(), entry.getKey().getElementName(), entry.getKey().getElementDescriptor().getInternalName()))); + if (checked) { + // See https://github.com/cpw/modlauncher/issues/37 + // OptiFine transformer overwrite all JavaScript field and method transformers. + // We are now called after all ITransformationService#transformers so that we can wrap all transformation systems. + try { + Object transformationServicesHandler = FieldUtils.readDeclaredField(Launcher.INSTANCE, "transformationServicesHandler", true); + TransformStore transformStore = (TransformStore) FieldUtils.readDeclaredField(transformationServicesHandler, "transformStore", true); + EnumMap> transformers = (EnumMap>) FieldUtils.readDeclaredField(transformStore, "transformers", true); + for (Map.Entry> transformListEntry : transformers.entrySet()) { + if (transformListEntry.getKey().equals(TransformTargetLabel.LabelType.CLASS)) { + continue; } - } - methodTransformers.clear(); - - // Wrap all field transformers. - TransformList fieldList = (TransformList) transformers.get(TransformTargetLabel.LabelType.FIELD); - Map>> fieldTransformers = (Map>>) FieldUtils.readDeclaredField(fieldList, "transformers", true); - for (Map.Entry>> entry : fieldTransformers.entrySet()) { - for (ITransformer transformer : entry.getValue()) { - list.add(new OptiForgeTransformer(transformer, ITransformer.Target.targetField(((Type) MethodUtils.invokeMethod(entry.getKey(), true, "getClassName")).getInternalName(), entry.getKey().getElementName()))); + Map>> transformersMap = (Map>>) FieldUtils.readDeclaredField(transformListEntry.getValue(), "transformers", true); + for (Map.Entry>> entry : transformersMap.entrySet()) { + for (ITransformer transformer : entry.getValue()) { + list.add(new OptiForgeTransformer(transformer, transformListEntry.getKey().equals(TransformTargetLabel.LabelType.FIELD) ? ITransformer.Target.targetField(((Type) MethodUtils.invokeMethod(entry.getKey(), true, "getClassName")).getInternalName(), entry.getKey().getElementName()) : ITransformer.Target.targetMethod(((Type) MethodUtils.invokeMethod(entry.getKey(), true, "getClassName")).getInternalName(), entry.getKey().getElementName(), entry.getKey().getElementDescriptor().getInternalName()))); + } } + transformersMap.clear(); } - fieldTransformers.clear(); + } catch (Exception e) { + LOGGER.error("", e); } - } catch (Exception e) { - LOGGER.error("", e); } return list; } diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/OptiForgeTransformer.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/OptiForgeTransformer.java index ff09584..ef0cf03 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/OptiForgeTransformer.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/OptiForgeTransformer.java @@ -26,33 +26,32 @@ public OptiForgeTransformer(ITransformer wrappedTransformer, Target target) { @Override @SuppressWarnings("unchecked") public ClassNode transform(@Nonnull ClassNode input, @Nonnull ITransformerVotingContext context) { - if (input.name.equals(this.target.getClassName())) { - List elements = new ArrayList<>(); - switch (this.target.getTargetType()) { - case CLASS: { - input = (ClassNode) this.wrappedTransformer.transform((T) input, context); - break; - } - case METHOD: { - for (MethodNode mn : input.methods) { - if (mn.name.equals(this.target.getElementName()) && mn.desc.equals(this.target.getElementDescriptor())) { - mn = (MethodNode) this.wrappedTransformer.transform((T) mn, context); - } - elements.add((T) mn); + List elements = new ArrayList<>(); + switch (this.target.getTargetType()) { + // For insurance purposes, we still need invoke class transformers. + case CLASS: { + input = (ClassNode) this.wrappedTransformer.transform((T) input, context); + break; + } + case METHOD: { + for (MethodNode mn : input.methods) { + if (mn.name.equals(this.target.getElementName()) && mn.desc.equals(this.target.getElementDescriptor())) { + mn = (MethodNode) this.wrappedTransformer.transform((T) mn, context); } - input.methods = (List) elements; - break; + elements.add((T) mn); } - case FIELD: { - for (FieldNode fn : input.fields) { - if (fn.name.equals(this.target.getElementName())) { - fn = (FieldNode) this.wrappedTransformer.transform((T) fn, context); - } - elements.add((T) fn); + input.methods = (List) elements; + break; + } + case FIELD: { + for (FieldNode fn : input.fields) { + if (fn.name.equals(this.target.getElementName())) { + fn = (FieldNode) this.wrappedTransformer.transform((T) fn, context); } - input.fields = (List) elements; - break; + elements.add((T) fn); } + input.fields = (List) elements; + break; } } return input; diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/VersionChecker.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/VersionChecker.java index 644ba3d..eb76322 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/VersionChecker.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/fml/VersionChecker.java @@ -1,5 +1,7 @@ package io.github.zekerzhayard.optiforge.asm.fml; +import java.io.IOException; +import java.util.Properties; import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -11,10 +13,10 @@ import org.apache.maven.artifact.versioning.VersionRange; public class VersionChecker { - public final static String REQUIRED_MINECRAFT_VERSION = "1.15.2"; - public final static String REQUIRED_OPTIFINE_VERSION = "[7.1.7,)"; // G1_pre7 - public final static String REQUIRED_OPTIFINE_COMPLETED_VERSION = "OptiFine_1.15.2_HD_U_G1_pre7 or newer"; - public final static String REQUIRED_FORGE_VERSION = "[31.1.39,)"; + public final static boolean IS_LOADED; + public final static String REQUIRED_MINECRAFT_VERSION; + public final static String REQUIRED_OPTIFINE_VERSION; + public final static String REQUIRED_FORGE_VERSION; public final static Function DEFAULT_FUNCTION = str -> { JOptionPane.showMessageDialog(null, str, "OptiForge Version Checker", JOptionPane.WARNING_MESSAGE); return false; @@ -22,6 +24,22 @@ public class VersionChecker { private final static Logger LOGGER = LogManager.getLogger(); + static { + boolean isLoaded; + Properties properties = new Properties(); + try { + properties.load(VersionChecker.class.getResourceAsStream("/requiredmods.properties")); + isLoaded = true; + } catch (IOException e) { + LOGGER.error("An unexpected issue occurred when loading required mods versions, version checker will not work: ", e); + isLoaded = false; + } + IS_LOADED = isLoaded; + REQUIRED_MINECRAFT_VERSION = properties.getProperty("required.minecraft.version"); + REQUIRED_OPTIFINE_VERSION = properties.getProperty("required.optifine.version"); + REQUIRED_FORGE_VERSION = properties.getProperty("required.forge.version"); + } + public static boolean checkOptiFineVersion(Function function) { try { String ofVer = (String) Class.forName("optifine.Installer").getMethod("getOptiFineVersion").invoke(null); @@ -38,12 +56,14 @@ public static boolean checkOptiFineVersion(Function function) { ofMinorVersion++; } String ofVersion = ofMajorVersion + "." + ofMinorVersion + "." + ofPreVersion; - VersionRange version = VersionRange.createFromVersionSpec(REQUIRED_OPTIFINE_VERSION); - if (!mcVersion.equals(REQUIRED_MINECRAFT_VERSION) || !version.containsVersion(new DefaultArtifactVersion(ofVersion))) { + VersionRange versionRange = VersionRange.createFromVersionSpec("[" + REQUIRED_OPTIFINE_VERSION + ",)"); + if (!mcVersion.equals(REQUIRED_MINECRAFT_VERSION) || !versionRange.containsVersion(new DefaultArtifactVersion(ofVersion))) { + DefaultArtifactVersion requiredOFVersion = new DefaultArtifactVersion(REQUIRED_OPTIFINE_VERSION); + String requiredOptiFineCompletedVersion = "OptiFine_" + REQUIRED_MINECRAFT_VERSION + "_HD_U_" + (char) (requiredOFVersion.getMajorVersion() + 'A' - 1) + (requiredOFVersion.getMinorVersion() + (requiredOFVersion.getIncrementalVersion() == 0 ? -1 : 0)) + (requiredOFVersion.getIncrementalVersion() == 0 ? "" : "_pre" + requiredOFVersion.getIncrementalVersion()); return function.apply( "You are using an unsupported OptiFine version, you can download the newer version from https://optifine.net/downloads.\n" + "The game will continue, and run without OptiFine and OptiForge.\n" + - "(You installed: " + ofVer + ", required: " + REQUIRED_OPTIFINE_COMPLETED_VERSION + ")" + "(You installed: " + ofVer + ", required: " + requiredOptiFineCompletedVersion + " or newer)" ); } } else { @@ -94,12 +114,12 @@ public static boolean checkMixinVersion(Function function) { */ public static boolean checkForgeVersion(Function function, String currentFMLVersion) { try { - VersionRange version = VersionRange.createFromVersionSpec(REQUIRED_FORGE_VERSION); + VersionRange version = VersionRange.createFromVersionSpec("[" + REQUIRED_FORGE_VERSION + ",)"); if (!version.containsVersion(new DefaultArtifactVersion(currentFMLVersion))) { return function.apply( "You are using an unsupported Forge version, you can download the newer version from https://files.minecraftforge.net/maven/net/minecraftforge/forge/index_" + REQUIRED_MINECRAFT_VERSION + ".html.\n" + "The game will continue, and run without OptiFine and OptiForge.\n" + - "(You installed: " + currentFMLVersion + ", required: " + REQUIRED_FORGE_VERSION + ")" + "(You installed: " + currentFMLVersion + ", required: " + REQUIRED_FORGE_VERSION + " or newer)" ); } } catch (Exception e) { diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/client/renderer/MixinOverlayRenderer.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/client/renderer/MixinOverlayRenderer.java index 4824911..99be596 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/client/renderer/MixinOverlayRenderer.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/client/renderer/MixinOverlayRenderer.java @@ -14,7 +14,6 @@ import net.minecraftforge.client.event.RenderBlockOverlayEvent; import net.minecraftforge.event.ForgeEventFactory; import org.apache.commons.lang3.tuple.Pair; -import org.spongepowered.asm.mixin.Intrinsic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -80,8 +79,7 @@ private static void renderTexture(Minecraft minecraftIn, TextureAtlasSprite spri return player.isBurning() && !ForgeEventFactory.renderFireOverlay(playerEntity, matrixStackIn); } - @Intrinsic - private static BlockState getViewBlockingState(PlayerEntity playerIn) { + private static BlockState optiforge_getViewBlockingState(PlayerEntity playerIn) { return MixinOverlayRenderer.getOverlayBlock(playerIn).getLeft(); } diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/client/world/MixinClientWorld.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/client/world/MixinClientWorld.java index b6f1b53..0ce0171 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/client/world/MixinClientWorld.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/client/world/MixinClientWorld.java @@ -1,11 +1,9 @@ package io.github.zekerzhayard.optiforge.asm.mixins.net.minecraft.client.world; -import java.util.Collection; import java.util.function.BiFunction; import net.minecraft.client.world.ClientWorld; import net.minecraft.profiler.IProfiler; -import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraft.world.chunk.AbstractChunkProvider; import net.minecraft.world.dimension.Dimension; diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/entity/MixinMobEntity.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/entity/MixinMobEntity.java index f4a0340..2b1eacd 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/entity/MixinMobEntity.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/mixins/net/minecraft/entity/MixinMobEntity.java @@ -2,6 +2,7 @@ import io.github.zekerzhayard.optiforge.asm.utils.RedirectSurrogate; import net.minecraft.entity.MobEntity; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; @@ -53,6 +54,20 @@ public abstract class MixinMobEntity { return ((ItemStack) params[0]).isShield(null); } + @Redirect( + method = "Lnet/minecraft/entity/MobEntity;attackEntityAsMob(Lnet/minecraft/entity/Entity;)Z", + at = @At( + value = "INVOKE", + target = "Lnet/optifine/reflect/ReflectorForge;isShield(Lnet/minecraft/item/ItemStack;Lnet/minecraft/entity/player/PlayerEntity;)Z", + remap = false + ), + require = 1, + allow = 1 + ) + private boolean redirect$attackEntityAsMob$0(ItemStack itemStack, PlayerEntity playerEntity) { + return itemStack.isShield(playerEntity); + } + @Redirect( method = "Lnet/minecraft/entity/MobEntity;attackEntityAsMob(Lnet/minecraft/entity/Entity;)Z", at = @At( @@ -62,12 +77,12 @@ public abstract class MixinMobEntity { require = 1, allow = 1 ) - private Item redirect$attackEntityAsMob$0(ItemStack itemStack) { + private Item redirect$attackEntityAsMob$1(ItemStack itemStack) { return null; } @RedirectSurrogate(loacalVariableOrdinals = 0) - private Item redirect$attackEntityAsMob$0(ItemStack itemStack2, ItemStack itemStack) { + private Item redirect$attackEntityAsMob$1(ItemStack itemStack2, ItemStack itemStack) { return itemStack.getItem(); } } diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/OverlayRendererTransformer.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/OverlayRendererTransformer.java index 9f4ce2f..c3c70c3 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/OverlayRendererTransformer.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/OverlayRendererTransformer.java @@ -3,7 +3,7 @@ import java.util.Objects; import io.github.zekerzhayard.optiforge.asm.transformers.ITransformer; -import io.github.zekerzhayard.optiforge.asm.utils.ASMUtils; +import net.minecraftforge.coremod.api.ASMAPI; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; @@ -21,15 +21,15 @@ public boolean isTargetClass(String className) { @Override public ClassNode postTransform(ClassNode cn, String mixinClassName) { cn.methods.remove(Bytecode.findMethod(cn, "getOverlayBlock", "(Lnet/minecraft/entity/player/PlayerEntity;)Lorg/apache/commons/lang3/tuple/Pair;")); - MethodNode mn1 = Objects.requireNonNull(Bytecode.findMethod(cn, "func_230018_a_", "(Lnet/minecraft/entity/player/PlayerEntity;)Lnet/minecraft/block/BlockState;")); + MethodNode mn1 = Objects.requireNonNull(Bytecode.findMethod(cn, ASMAPI.mapMethod("func_230018_a_"), "(Lnet/minecraft/entity/player/PlayerEntity;)Lnet/minecraft/block/BlockState;")); mn1.name = "getOverlayBlock"; mn1.desc = "(Lnet/minecraft/entity/player/PlayerEntity;)Lorg/apache/commons/lang3/tuple/Pair;"; AbstractInsnNode ain = Objects.requireNonNull(Bytecode.findInsn(mn1, Opcodes.ARETURN)); mn1.instructions.insertBefore(ain, new VarInsnNode(Opcodes.ALOAD, 1)); mn1.instructions.insertBefore(ain, new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "net/minecraft/util/math/BlockPos$Mutable", "func_185334_h", "()Lnet/minecraft/util/math/BlockPos;")); mn1.instructions.insertBefore(ain, new MethodInsnNode(Opcodes.INVOKESTATIC, "org/apache/commons/lang3/tuple/Pair", "of", "(Ljava/lang/Object;Ljava/lang/Object;)Lorg/apache/commons/lang3/tuple/Pair;")); - MethodNode mn2 = Objects.requireNonNull(Bytecode.findMethod(cn, "getViewBlockingState", "(Lnet/minecraft/entity/player/PlayerEntity;)Lnet/minecraft/block/BlockState;")); - mn2.name = "func_230018_a_"; + MethodNode mn2 = Objects.requireNonNull(Bytecode.findMethod(cn, "optiforge_getViewBlockingState", "(Lnet/minecraft/entity/player/PlayerEntity;)Lnet/minecraft/block/BlockState;")); + mn2.name = ASMAPI.mapMethod("func_230018_a_"); return cn; } } diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/entity/EntityRendererManagerTransformer.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/entity/EntityRendererManagerTransformer.java index d2dbcc6..cf041d4 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/entity/EntityRendererManagerTransformer.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/entity/EntityRendererManagerTransformer.java @@ -4,6 +4,7 @@ import io.github.zekerzhayard.optiforge.asm.transformers.ITransformer; import io.github.zekerzhayard.optiforge.asm.utils.ASMUtils; +import net.minecraftforge.coremod.api.ASMAPI; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; @@ -20,7 +21,7 @@ public boolean isTargetClass(String className) { @Override public ClassNode preTransform(ClassNode cn) { MethodNode mn = Objects.requireNonNull(Bytecode.findMethod(cn, "", "(Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/renderer/ItemRenderer;Lnet/minecraft/resources/IReloadableResourceManager;Lnet/minecraft/client/gui/FontRenderer;Lnet/minecraft/client/GameSettings;)V")); - FieldInsnNode fin = Objects.requireNonNull(ASMUtils.findFirstFieldInsnNode(mn, Opcodes.GETSTATIC, "net/minecraft/util/registry/Registry", "field_212629_r", "Lnet/minecraft/util/registry/DefaultedRegistry;")); + FieldInsnNode fin = Objects.requireNonNull(ASMUtils.findFirstFieldInsnNode(mn, Opcodes.GETSTATIC, "net/minecraft/util/registry/Registry", ASMAPI.mapField("field_212629_r"), "Lnet/minecraft/util/registry/DefaultedRegistry;")); mn.instructions.insertBefore(fin, new InsnNode(Opcodes.RETURN)); return cn; } diff --git a/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/entity/ItemFrameRendererTransformer.java b/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/entity/ItemFrameRendererTransformer.java index b4d074f..04ae9b0 100644 --- a/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/entity/ItemFrameRendererTransformer.java +++ b/src/main/java/io/github/zekerzhayard/optiforge/asm/transformers/net/minecraft/client/renderer/entity/ItemFrameRendererTransformer.java @@ -3,6 +3,7 @@ import java.util.Objects; import io.github.zekerzhayard.optiforge.asm.transformers.ITransformer; +import net.minecraftforge.coremod.api.ASMAPI; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; @@ -21,7 +22,7 @@ public boolean isTargetClass(String className) { @Override public ClassNode preTransform(ClassNode cn) { - MethodNode mn = Objects.requireNonNull(Bytecode.findMethod(cn, "func_225623_a_", "(Lnet/minecraft/entity/item/ItemFrameEntity;FFLcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;I)V")); + MethodNode mn = Objects.requireNonNull(Bytecode.findMethod(cn, ASMAPI.mapMethod("func_225623_a_"), "(Lnet/minecraft/entity/item/ItemFrameEntity;FFLcom/mojang/blaze3d/matrix/MatrixStack;Lnet/minecraft/client/renderer/IRenderTypeBuffer;I)V")); LocalVariableNode flag = null, mapdata = null; for (LocalVariableNode lvn : mn.localVariables) { if (lvn.name.equals("flag") && lvn.desc.equals("Z")) { diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..ccb0ca8 --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1 @@ +MixinConnector: io.github.zekerzhayard.optiforge.asm.MixinConnector diff --git a/src/main/resources/mixins.optiforge.json b/src/main/resources/mixins.optiforge.json index 4b7b76f..860f09e 100644 --- a/src/main/resources/mixins.optiforge.json +++ b/src/main/resources/mixins.optiforge.json @@ -42,7 +42,6 @@ "net.minecraft.network.datasync.MixinEntityDataManager", "net.minecraft.world.server.MixinChunkManager" ], - "refmap": "mixins.optiforge.refmap.json", "plugin": "io.github.zekerzhayard.optiforge.asm.MixinConfigPlugin", "injectors": { "maxShiftBy": 2 diff --git a/src/main/resources/requiredmods.properties b/src/main/resources/requiredmods.properties new file mode 100644 index 0000000..e0faa72 --- /dev/null +++ b/src/main/resources/requiredmods.properties @@ -0,0 +1,3 @@ +required.minecraft.version=${minecraft_version} +required.optifine.version=${optifine_version} +required.forge.version=${forge_version} \ No newline at end of file