diff --git a/build.gradle b/build.gradle index 7715ee3..4c91146 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,7 @@ repositories { changelog { from '1.0.0' + publishAll = false } license { @@ -40,7 +41,7 @@ dependencies { compileOnly(libs.log4j.api) compileOnly(libs.nulls) compileOnly(libs.forgespi) - + api(libs.bundles.asm) implementation(libs.nashorn) } @@ -60,6 +61,7 @@ tasks.named('jar', Jar) { publishing { publications.register('mavenJava', MavenPublication) { + changelog.publish(it) pom { from components.java artifactId = 'coremods' @@ -68,14 +70,14 @@ publishing { url = 'https://github.com/MinecraftForge/CoreMods' PomUtils.setGitHubDetails(pom, 'CoreMods') license PomUtils.Licenses.LGPLv2_1 - + developers { developer PomUtils.Developers.LexManos developer PomUtils.Developers.cpw } } } - + repositories { maven gradleutils.publishingForgeMaven } diff --git a/coremods-api/build.gradle b/coremods-api/build.gradle new file mode 100644 index 0000000..8b79304 --- /dev/null +++ b/coremods-api/build.gradle @@ -0,0 +1,81 @@ +import net.minecraftforge.gradleutils.PomUtils + +plugins { + id 'idea' + id 'eclipse' + id 'java-library' + id 'maven-publish' + alias libs.plugins.license + //alias libs.plugins.versions + alias libs.plugins.gradleutils +} + +group = 'net.minecraftforge' +version = rootProject.version +println "Version: $version" + +java { + toolchain.languageVersion = JavaLanguageVersion.of(17) + withSourcesJar() +} + +repositories { + mavenCentral() + maven gradleutils.forgeMaven + mavenLocal() +} + +license { + header = rootProject.file('LICENSE-header.txt') + newLine = false +} + +dependencies { + compileOnly(libs.modlauncher) + compileOnly(libs.securemodules) // Needed by Modlauncher + + compileOnly(libs.nulls) + compileOnly(rootProject) + + api(libs.bundles.asm) +} + +tasks.named('jar', Jar) { + manifest { + attributes([ + 'Specification-Title': 'coremods-api', + 'Specification-Vendor': 'Forge Development LLC', + 'Specification-Version': '1', // TODO: Use the tag + 'Implementation-Title': project.name, + 'Implementation-Version': project.version, + 'Implementation-Vendor' :'Forge Development LLC' + ], 'net/minecraftforge/coremod/api/') + } +} + +publishing { + publications.register('mavenJava', MavenPublication) { + pom { + from components.java + artifactId = 'coremods-api' + name = 'Core Mods API' + description = 'API that is useful for Minecraft Transformers' + url = 'https://github.com/MinecraftForge/CoreMods' + PomUtils.setGitHubDetails(pom, 'CoreMods') + license PomUtils.Licenses.LGPLv2_1 + + developers { + developer PomUtils.Developers.LexManos + developer PomUtils.Developers.cpw + } + } + } + + repositories { + maven gradleutils.publishingForgeMaven + } +} + +idea.module { + downloadJavadoc = downloadSources = true +} diff --git a/coremods-api/src/main/java/module-info.java b/coremods-api/src/main/java/module-info.java new file mode 100644 index 0000000..9141844 --- /dev/null +++ b/coremods-api/src/main/java/module-info.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) Forge Development LLC + * SPDX-License-Identifier: LGPL-2.1-only + */ +module net.minecraftforge.coremod.api { + // ASMAPI + exports net.minecraftforge.coremod.api; + + requires cpw.mods.modlauncher; + + requires static org.jetbrains.annotations; + requires org.objectweb.asm.util; + + // JavaScript specific helpers. + requires static net.minecraftforge.coremod; +} diff --git a/src/main/java/net/minecraftforge/coremod/api/ASMAPI.java b/coremods-api/src/main/java/net/minecraftforge/coremod/api/ASMAPI.java similarity index 96% rename from src/main/java/net/minecraftforge/coremod/api/ASMAPI.java rename to coremods-api/src/main/java/net/minecraftforge/coremod/api/ASMAPI.java index b046368..07d48bd 100644 --- a/src/main/java/net/minecraftforge/coremod/api/ASMAPI.java +++ b/coremods-api/src/main/java/net/minecraftforge/coremod/api/ASMAPI.java @@ -6,8 +6,9 @@ import cpw.mods.modlauncher.Launcher; import cpw.mods.modlauncher.api.INameMappingService; -import net.minecraftforge.coremod.CoreModEngine; +import cpw.mods.modlauncher.api.TypesafeMap; import net.minecraftforge.coremod.CoreModTracker; + import org.jetbrains.annotations.Nullable; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; @@ -15,7 +16,6 @@ import org.objectweb.asm.util.TraceClassVisitor; import org.objectweb.asm.util.TraceMethodVisitor; -import javax.script.ScriptException; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; @@ -31,7 +31,7 @@ * to prevent boilerplate code, excessive imports, unnecessary loops, and to provide a more user-friendly API for * coremod developers. */ -@SuppressWarnings({"unused", "exports"}) // annoying IDE warnings +@SuppressWarnings({"exports"}) // annoying IDE warnings public class ASMAPI { /* BUILDING INSTRUCTION LISTS */ @@ -473,7 +473,7 @@ public int get() { * {@link #findFirstInstructionBefore(MethodNode, int, InsnType, int, boolean)}. */ public static @Nullable AbstractInsnNode findFirstInstructionBefore(MethodNode method, int opCode, @Nullable InsnType type, int startIndex) { - return findFirstInstructionBefore(method, opCode, type, startIndex, !CoreModEngine.DO_NOT_FIX_INSNBEFORE); + return findFirstInstructionBefore(method, opCode, type, startIndex, !DO_NOT_FIX_INSNBEFORE); } /** @@ -1121,6 +1121,7 @@ public static void redirectFieldToMethod(final ClassNode classNode, final String * still work for sake of backwards-compatibility, you should not be using this method if you are on 1.20.4 or * later. */ + @Deprecated(forRemoval = true, since = "5.2") public static String mapMethod(String name) { return map(name, INameMappingService.Domain.METHOD); } @@ -1135,6 +1136,7 @@ public static String mapMethod(String name) { * still work for sake of backwards-compatibility, you should not be using this method if you are on 1.20.4 or * later. */ + @Deprecated(forRemoval = true, since = "5.2") public static String mapField(String name) { return map(name, INameMappingService.Domain.FIELD); } @@ -1171,8 +1173,11 @@ public static boolean getSystemPropertyFlag(final String propertyName) { * * @throws ScriptException If the script engine encounters an error, usually due to a syntax error in the script * @throws IOException If an I/O error occurs while reading the file, usually due to a corrupt or missing file + * + * @apiNote This method only functions for JavaScript coremods managed by the main CoreMod engine. + * If using ASMAPI in a normal transformer do not use this method. Unknown exceptions could be thrown. */ - public static boolean loadFile(String file) throws ScriptException, IOException { + public static boolean loadFile(String file) throws IOException { return CoreModTracker.loadFileByName(file); } @@ -1184,10 +1189,13 @@ public static boolean loadFile(String file) throws ScriptException, IOException * {@code initializeCoreMod()} or any of the transformer functions returned by it. * * @throws ScriptException If the parsed JSON data is malformed - * @throws IOException If an I/O error occurs while reading the file, usually due to a corrupt or missing file + * @throws IOException If an I/O error occurs while reading the file, usually due to a corrupt or missing file. + * + * @apiNote This method only functions for JavaScript coremods managed by the main CoreMod engine. + * If using ASMAPI in a normal transformer do not use this method. Unknown exceptions could be thrown. */ @Nullable - public static Object loadData(String file) throws ScriptException, IOException { + public static Object loadData(String file) throws IOException { return CoreModTracker.loadDataByName(file); } @@ -1202,6 +1210,9 @@ public static Object loadData(String file) throws ScriptException, IOException { * @param message The message * @param args Any formatting arguments * @see CoreModTracker#log(String, String, Object[]) + * + * @apiNote This method only functions for JavaScript coremods managed by the main CoreMod engine. + * If using ASMAPI in a normal transformer do not use this method. Unknown exceptions could be thrown. */ public static void log(String level, String message, Object... args) { CoreModTracker.log(level, message, args); @@ -1293,4 +1304,29 @@ private static String toString(Textifier text) { private static int clamp(int value, int min, int max) { return Math.max(min, Math.min(max, value)); } + + // INTERNAL + /** + * Whether to preserve the legacy behavior of + * {@link net.minecraftforge.coremod.api.ASMAPI#findFirstInstructionBefore(org.objectweb.asm.tree.MethodNode, int, + * int)} for backwards-compatibility. + *

+ * In Forge's case, this is set by FML in Minecraft 1.21.1 and earlier, but not in 1.21.3 and later. + * + * @see net.minecraftforge.coremod.api.ASMAPI#findFirstInstructionBefore(org.objectweb.asm.tree.MethodNode, int, + * int) + */ + private static final boolean DO_NOT_FIX_INSNBEFORE = shouldntFixInsnBefore(); + + private static final boolean shouldntFixInsnBefore() { + try { + if (Launcher.INSTANCE == null) + return false; + + var blackboardVar = Launcher.INSTANCE.blackboard().get(TypesafeMap.Key.getOrCreate(Launcher.INSTANCE.blackboard(), "coremods.use_old_findFirstInstructionBefore", Boolean.class)); + return blackboardVar.isPresent() && blackboardVar.get(); + } catch (Throwable t) { // If ModLauncher doesn't exist. + return false; + } + } } diff --git a/coremods-test/build.gradle b/coremods-test/build.gradle index 50e0d17..943d043 100644 --- a/coremods-test/build.gradle +++ b/coremods-test/build.gradle @@ -2,7 +2,6 @@ plugins { id 'eclipse' id 'java-library' alias libs.plugins.license - alias libs.plugins.modules //alias libs.plugins.versions alias libs.plugins.gradleutils } @@ -58,11 +57,6 @@ dependencies { testCompileOnly(libs.nulls) } -extraJavaModuleInfo { - failOnMissingModuleInfo = false - automaticModule('jopt-simple-5.0.4.jar', 'jopt.simple') -} - // If we are being told a specific vendor then we are probably being run in parallel if (project.hasProperty('javaVendor') && project.hasProperty('javaVersion')) { test.javaLauncher.set(javaToolchains.launcherFor { diff --git a/coremods-test/src/test/resources/log4j2.xml b/coremods-test/src/test/resources/log4j2.xml index 7eec6f7..b98efbd 100644 --- a/coremods-test/src/test/resources/log4j2.xml +++ b/coremods-test/src/test/resources/log4j2.xml @@ -1,9 +1,8 @@ - diff --git a/gradle.properties b/gradle.properties index 415d879..c27d3d1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,5 @@ org.gradle.caching=true org.gradle.parallel=true org.gradle.configuration-cache=true +org.gradle.configuration-cache.problems=warn org.gradle.configureondemand=true diff --git a/settings.gradle b/settings.gradle index 6ebc662..1760b45 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,13 +16,14 @@ dependencyResolutionManagement { plugin('gradleutils', 'net.minecraftforge.gradleutils').version('[2.3,2.4)') plugin('modules', 'org.gradlex.extra-java-module-info').version('1.9') plugin('versions', 'com.github.ben-manes.versions').version('0.51.0') - + version('asm', '9.7.1') library('asm', 'org.ow2.asm', 'asm' ).versionRef('asm') library('asm-tree', 'org.ow2.asm', 'asm-tree' ).versionRef('asm') library('asm-commons', 'org.ow2.asm', 'asm-commons').versionRef('asm') - bundle('asm', ['asm', 'asm-tree', 'asm-commons']) - + library('asm-util', 'org.ow2.asm', 'asm-util' ).versionRef('asm') + bundle('asm', ['asm', 'asm-tree', 'asm-commons', 'asm-util']) + version('junit', '5.11.4') library('junit-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') library('junit-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') @@ -35,16 +36,16 @@ dependencyResolutionManagement { library('gson', 'com.google.code.gson:gson:2.10.1') library('jopt-simple', 'net.sf.jopt-simple:jopt-simple:5.0.4') */ - - library('forgespi', 'net.minecraftforge:forgespi:7.1.0') + + library('forgespi', 'net.minecraftforge:forgespi:7.1.0') library('modlauncher', 'net.minecraftforge:modlauncher:10.1.1') // Needs securemodules library('nulls', 'org.jetbrains:annotations:26.0.1') library('nashorn', 'org.openjdk.nashorn:nashorn-core:15.4') // Needed by coremods, because the JRE no longer ships JS - + // Used by our test aggrigator scripts library('ivy', 'org.apache.ivy:ivy:2.5.3') library('groovy', 'org.codehaus.groovy:groovy-all:3.0.23') - + version('log4j', '2.17.0') // Needs to be kept in step with modlauncher because of its config.. TODO: Figure out why? library('log4j-api', 'org.apache.logging.log4j', 'log4j-api' ).versionRef('log4j') library('log4j-core', 'org.apache.logging.log4j', 'log4j-core').versionRef('log4j') @@ -52,6 +53,7 @@ dependencyResolutionManagement { } } -rootProject.name = 'CoreMods' +rootProject.name = 'coremods' +include 'coremods-api' include 'coremods-test' include 'coremods-test-jar' diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 31b1d9f..d856ba9 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -5,8 +5,6 @@ module net.minecraftforge.coremod { // CoreMods framework exports net.minecraftforge.coremod; - // ASMAPI - exports net.minecraftforge.coremod.api; requires cpw.mods.modlauncher; requires net.minecraftforge.forgespi; diff --git a/src/main/java/net/minecraftforge/coremod/CoreMod.java b/src/main/java/net/minecraftforge/coremod/CoreMod.java index 5a9086c..b4f141e 100644 --- a/src/main/java/net/minecraftforge/coremod/CoreMod.java +++ b/src/main/java/net/minecraftforge/coremod/CoreMod.java @@ -4,8 +4,9 @@ */ package net.minecraftforge.coremod; +import cpw.mods.modlauncher.Launcher; +import cpw.mods.modlauncher.api.INameMappingService; import cpw.mods.modlauncher.api.ITransformer; -import net.minecraftforge.coremod.api.ASMAPI; import net.minecraftforge.coremod.transformer.CoreModClassTransformer; import net.minecraftforge.coremod.transformer.CoreModFieldTransformer; import net.minecraftforge.coremod.transformer.CoreModMethodTransformer; @@ -105,17 +106,33 @@ private ITransformer buildCore(Map.Entry entry) { return new CoreModClassTransformer(this, coreName, targets, NashornFactory.getFunction(function)); case METHOD: targets = Collections.singleton(ITransformer.Target.targetMethod( - (String) targetData.get("class"), ASMAPI.mapMethod((String) targetData.get("methodName")), (String) targetData.get("methodDesc"))); + (String) targetData.get("class"), map((String) targetData.get("methodName"), INameMappingService.Domain.METHOD), (String) targetData.get("methodDesc"))); return new CoreModMethodTransformer(this, coreName, targets, NashornFactory.getFunction(function)); case FIELD: targets = Collections.singleton(ITransformer.Target.targetField( - (String) targetData.get("class"), ASMAPI.mapField((String) targetData.get("fieldName")))); + (String) targetData.get("class"), map((String)targetData.get("fieldName"), INameMappingService.Domain.FIELD))); return new CoreModFieldTransformer(this, coreName, targets, NashornFactory.getFunction(function)); default: throw new RuntimeException("Unimplemented target type " + targetData); } } + + + private static String map(String name, INameMappingService.Domain domain) { + final class LazyInit { + static final BiFunction MAPPER; + static { + BiFunction mapper = null; + if (Launcher.INSTANCE != null && Launcher.INSTANCE.environment() != null) { + mapper = Launcher.INSTANCE.environment().findNameMapping("srg").orElse(null); + } + MAPPER = mapper; + } + } + return LazyInit.MAPPER == null ? name : LazyInit.MAPPER.apply(domain, name); + } + /** * Returns whether the coremod has an error. Usually paired with {@link #getError()}. * @@ -154,13 +171,17 @@ public ICoreModFile getFile() { * @throws ScriptException If the script engine encounters an error, usually due to a syntax error in the script * @throws IOException If an I/O error occurs while reading the file, usually due to a corrupt or missing file */ - public boolean loadAdditionalFile(final String fileName) throws ScriptException, IOException { + public boolean loadAdditionalFile(final String fileName) throws IOException { // why does this method return a boolean if we're going to crash anyways on load failure? // it looks like the case of the coremod not being tracked is never reached if (this.loaded) return false; Reader additional = this.file.getAdditionalFile(fileName); - this.scriptEngine.eval(additional); + try { + this.scriptEngine.eval(additional); + } catch (ScriptException e) { + return sneak(e); + } return true; } @@ -174,7 +195,7 @@ public boolean loadAdditionalFile(final String fileName) throws ScriptException, * @throws IOException If an I/O error occurs while reading the file, usually due to a corrupt or missing file */ @Nullable - public Object loadAdditionalData(final String fileName) throws ScriptException, IOException { + public Object loadAdditionalData(final String fileName) throws IOException { // again with this shit, dude! why are we going to return null?? // isn't the coremod always going to be tracked if it calls ASMAPI.loadData from itself? if (this.loaded) return null; @@ -188,7 +209,16 @@ public Object loadAdditionalData(final String fileName) throws ScriptException, builder.append(buf, 0, numChars); String str = builder.toString(); - return this.scriptEngine.eval("tmp_json_loading_variable = " + str + ";"); + try { + return this.scriptEngine.eval("tmp_json_loading_variable = " + str + ";"); + } catch (ScriptException e) { + return sneak(e); + } + } + + @SuppressWarnings("unchecked") + private static R sneak(Throwable e) throws E { + throw (E)e; } /** diff --git a/src/main/java/net/minecraftforge/coremod/CoreModTracker.java b/src/main/java/net/minecraftforge/coremod/CoreModTracker.java index 9026d3b..af9dcfb 100644 --- a/src/main/java/net/minecraftforge/coremod/CoreModTracker.java +++ b/src/main/java/net/minecraftforge/coremod/CoreModTracker.java @@ -43,7 +43,7 @@ public static void clearCoreMod() { * @throws IOException If an I/O error occurs while reading the file, usually due to a corrupt or missing file * @see net.minecraftforge.coremod.api.ASMAPI#loadFile(String) */ - public static boolean loadFileByName(final String file) throws ScriptException, IOException { + public static boolean loadFileByName(final String file) throws IOException { final CoreMod tracked = LOCAL.get().tracked; if (tracked != null) { return tracked.loadAdditionalFile(file); @@ -62,7 +62,7 @@ public static boolean loadFileByName(final String file) throws ScriptException, * @see net.minecraftforge.coremod.api.ASMAPI#loadData(String) */ @Nullable - public static Object loadDataByName(final String file) throws ScriptException, IOException { + public static Object loadDataByName(final String file) throws IOException { final CoreMod tracked = LOCAL.get().tracked; if (tracked != null) { return tracked.loadAdditionalData(file);