From 64ca733ba945596e5f8598e5b5c2fe70b50dba40 Mon Sep 17 00:00:00 2001 From: NebelNidas Date: Wed, 17 May 2023 11:59:34 +0200 Subject: [PATCH] Add preliminary plugin support --- build.gradle | 65 ++++++++++--------- minecraft-plugin/build.gradle | 31 +++++++++ .../stitch/plugins/MinecraftPlugin.java | 41 ++++++++++++ .../net.fabricmc.stitch.plugin.StitchPlugin | 1 + settings.gradle | 2 + src/main/java/net/fabricmc/stitch/Main.java | 2 + .../fabricmc/stitch/commands/GenState.java | 50 +++++++++----- .../fabricmc/stitch/plugin/PluginLoader.java | 33 ++++++++++ .../stitch/plugin/PluginRegistry.java | 32 +++++++++ .../fabricmc/stitch/plugin/StitchPlugin.java | 59 +++++++++++++++++ 10 files changed, 270 insertions(+), 46 deletions(-) create mode 100644 minecraft-plugin/build.gradle create mode 100644 minecraft-plugin/src/main/java/net/fabricmc/stitch/plugins/MinecraftPlugin.java create mode 100644 minecraft-plugin/src/main/resources/META-INF/services/net.fabricmc.stitch.plugin.StitchPlugin create mode 100644 src/main/java/net/fabricmc/stitch/plugin/PluginLoader.java create mode 100644 src/main/java/net/fabricmc/stitch/plugin/PluginRegistry.java create mode 100644 src/main/java/net/fabricmc/stitch/plugin/StitchPlugin.java diff --git a/build.gradle b/build.gradle index 67dd535..b459891 100644 --- a/build.gradle +++ b/build.gradle @@ -5,24 +5,48 @@ plugins { id 'checkstyle' } -checkstyle { - configFile = file('checkstyle.xml') -} +allprojects { + apply plugin: 'java-library' + apply plugin: 'maven-publish' + apply plugin: 'org.cadixdev.licenser' + apply plugin: 'checkstyle' -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 -def ENV = System.getenv() -version += (ENV.GITHUB_ACTIONS ? '' : '+local') + repositories { + mavenCentral() + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + } -repositories { - mavenCentral() - maven { - name = 'Fabric' - url = 'https://maven.fabricmc.net/' + checkstyle { + configFile = rootProject.file('checkstyle.xml') + } + + license { + header rootProject.file('HEADER') + include '**/*.java' + } + + java { + withSourcesJar() + } + + tasks.withType(JavaCompile).configureEach { + it.options.encoding = 'UTF-8' + + if (JavaVersion.current().isJava9Compatible()) { + it.options.release = 8 + } } } +def ENV = System.getenv() +version += (ENV.GITHUB_ACTIONS ? '' : '+local') + configurations { ship implementation.extendsFrom ship @@ -42,11 +66,6 @@ dependencies { testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}" } -license { - header project.file('HEADER') - include '**/*.java' -} - jar { manifest { attributes 'Implementation-Title': 'Stitch', @@ -70,18 +89,6 @@ task allJar(type: Jar) { with jar } -java { - withSourcesJar() -} - -tasks.withType(JavaCompile).configureEach { - it.options.encoding = 'UTF-8' - - if (JavaVersion.current().isJava9Compatible()) { - it.options.release = 8 - } -} - publishing { publications { mavenJava(MavenPublication) { diff --git a/minecraft-plugin/build.gradle b/minecraft-plugin/build.gradle new file mode 100644 index 0000000..21c0343 --- /dev/null +++ b/minecraft-plugin/build.gradle @@ -0,0 +1,31 @@ +archivesBaseName = 'stitch-minecraft-plugin' +version = rootProject.version +group = rootProject.group + +dependencies { + implementation project(':') +} + +def ENV = System.getenv() + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact(jar) + } + } + + repositories { + if (ENV.MAVEN_URL) { + repositories.maven { + name 'fabric' + url ENV.MAVEN_URL + credentials { + username ENV.MAVEN_USERNAME + password ENV.MAVEN_PASSWORD + } + } + } + } +} diff --git a/minecraft-plugin/src/main/java/net/fabricmc/stitch/plugins/MinecraftPlugin.java b/minecraft-plugin/src/main/java/net/fabricmc/stitch/plugins/MinecraftPlugin.java new file mode 100644 index 0000000..5f482ea --- /dev/null +++ b/minecraft-plugin/src/main/java/net/fabricmc/stitch/plugins/MinecraftPlugin.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.stitch.plugins; + +import net.fabricmc.stitch.plugin.StitchPlugin; +import net.fabricmc.stitch.representation.ClassStorage; +import net.fabricmc.stitch.representation.JarClassEntry; +import net.fabricmc.stitch.representation.JarFieldEntry; +import net.fabricmc.stitch.representation.JarMethodEntry; + +public class MinecraftPlugin implements StitchPlugin { + @Override + public int needsIntermediaryName(ClassStorage storage, JarClassEntry cls, JarFieldEntry fld) { + String name = fld.getName(); + + return name.length() <= 2 || (name.length() == 3 && name.charAt(2) == '_') ? 2 : -2; + } + + @Override + public int needsIntermediaryName(ClassStorage storage, JarClassEntry cls, JarMethodEntry mth) { + String name = mth.getName(); + + return (name.length() <= 2 || (name.length() == 3 && name.charAt(2) == '_')) + && name.charAt(0) != '<' + && mth.isSource(storage, cls) ? 2 : -2; + } +} diff --git a/minecraft-plugin/src/main/resources/META-INF/services/net.fabricmc.stitch.plugin.StitchPlugin b/minecraft-plugin/src/main/resources/META-INF/services/net.fabricmc.stitch.plugin.StitchPlugin new file mode 100644 index 0000000..4a2032f --- /dev/null +++ b/minecraft-plugin/src/main/resources/META-INF/services/net.fabricmc.stitch.plugin.StitchPlugin @@ -0,0 +1 @@ +net.fabricmc.stitch.plugin.StitchPlugin diff --git a/settings.gradle b/settings.gradle index b6f62b4..4546737 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,3 +9,5 @@ pluginManagement { } rootProject.name = 'stitch' + +include ':minecraft-plugin' diff --git a/src/main/java/net/fabricmc/stitch/Main.java b/src/main/java/net/fabricmc/stitch/Main.java index c31ce03..dcc545a 100644 --- a/src/main/java/net/fabricmc/stitch/Main.java +++ b/src/main/java/net/fabricmc/stitch/Main.java @@ -28,6 +28,7 @@ import net.fabricmc.stitch.commands.CommandRewriteIntermediary; import net.fabricmc.stitch.commands.CommandUpdateIntermediary; import net.fabricmc.stitch.commands.CommandValidateRecords; +import net.fabricmc.stitch.plugin.PluginLoader; public class Main { private static final Map COMMAND_MAP = new TreeMap<>(); @@ -72,6 +73,7 @@ public static void main(String[] args) { System.arraycopy(args, 1, argsCommand, 0, argsCommand.length); } + PluginLoader.loadPlugins(); COMMAND_MAP.get(args[0].toLowerCase(Locale.ROOT)).run(argsCommand); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/net/fabricmc/stitch/commands/GenState.java b/src/main/java/net/fabricmc/stitch/commands/GenState.java index a8ac5f7..742d380 100644 --- a/src/main/java/net/fabricmc/stitch/commands/GenState.java +++ b/src/main/java/net/fabricmc/stitch/commands/GenState.java @@ -34,8 +34,10 @@ import java.util.Scanner; import java.util.Set; import java.util.TreeSet; +import java.util.function.Function; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.Opcodes; @@ -51,6 +53,8 @@ import net.fabricmc.mappingio.tree.MappingTree.ClassMapping; import net.fabricmc.mappingio.tree.MappingTree.FieldMapping; import net.fabricmc.mappingio.tree.MappingTree.MethodMapping; +import net.fabricmc.stitch.plugin.PluginRegistry; +import net.fabricmc.stitch.plugin.StitchPlugin; import net.fabricmc.stitch.representation.AbstractJarEntry; import net.fabricmc.stitch.representation.ClassStorage; import net.fabricmc.stitch.representation.JarClassEntry; @@ -77,11 +81,11 @@ class GenState { private boolean writeAll = false; private Scanner scanner = new Scanner(System.in); - private String targetPackage = "net/minecraft/"; + private String targetPackage = ""; private final List obfuscatedPatterns = new ArrayList(); GenState() throws IOException { - this.obfuscatedPatterns.add(Pattern.compile("^[^/]*$")); // Default obfuscation. Minecraft classes without a package are obfuscated. + this.obfuscatedPatterns.add(Pattern.compile("^.*$")); // Default obfuscation. Minecraft classes without a package are obfuscated. mappingTree.visitNamespaces(official, Arrays.asList(intermediary, intermediary)); } @@ -180,30 +184,42 @@ public void generate(File file, JarRootEntry jarEntry, JarRootEntry jarOld) thro writer.close(); } - public static boolean isMappedClass(ClassStorage storage, JarClassEntry c) { - return !c.isAnonymous(); - } + private boolean needsIntermediaryName(Function priorityGetter) { + List results = PluginRegistry.getPlugins().stream() + .map(plugin -> priorityGetter.apply(plugin)) + .sorted((a, b) -> Math.abs(b) - Math.abs(a)) + .collect(Collectors.toList()); + + int first = results.get(0); + + if (results.size() > 1) { + int second = results.get(1); + + if (first != 0 + && first != second + && Math.abs(first) == Math.abs(second)) { + throw new IllegalStateException("Got conflicting scores of the same priority!"); + } + } - public static boolean isMappedField(ClassStorage storage, JarClassEntry c, JarFieldEntry f) { - return isUnmappedFieldName(f.getName()); + return first > 0; } - public static boolean isUnmappedFieldName(String name) { - return name.length() <= 2 || (name.length() == 3 && name.charAt(2) == '_'); + private boolean needsIntermediaryName(ClassStorage storage, JarClassEntry cls) { + return needsIntermediaryName((plugin) -> plugin.needsIntermediaryName(storage, cls)); } - public static boolean isMappedMethod(ClassStorage storage, JarClassEntry c, JarMethodEntry m) { - return isUnmappedMethodName(m.getName()) && m.isSource(storage, c); + private boolean needsIntermediaryName(ClassStorage storage, JarClassEntry cls, JarFieldEntry fld) { + return needsIntermediaryName((plugin) -> plugin.needsIntermediaryName(storage, cls, fld)); } - public static boolean isUnmappedMethodName(String name) { - return (name.length() <= 2 || (name.length() == 3 && name.charAt(2) == '_')) - && name.charAt(0) != '<'; + private boolean needsIntermediaryName(ClassStorage storage, JarClassEntry cls, JarMethodEntry mth) { + return needsIntermediaryName((plugin) -> plugin.needsIntermediaryName(storage, cls, mth)); } @Nullable private String getFieldName(ClassStorage storage, JarClassEntry c, JarFieldEntry f) { - if (!isMappedField(storage, c, f)) { + if (!needsIntermediaryName(storage, c, f)) { return null; } @@ -354,7 +370,7 @@ private void findNames(ClassStorage storageOld, ClassStorage storageNew, JarClas @Nullable private String getMethodName(ClassStorage storageOld, ClassStorage storageNew, JarClassEntry c, JarMethodEntry m) { - if (!isMappedMethod(storageNew, c, m)) { + if (!needsIntermediaryName(storageNew, c, m)) { return null; } @@ -435,7 +451,7 @@ private void addClass(JarClassEntry c, ClassStorage storageOld, ClassStorage sto // an intermediary name, so we just leave it as is and // don't add a prefix. prefix = ""; - } else if (!isMappedClass(storage, c)) { + } else if (!needsIntermediaryName(storage, c)) { cName = c.getName(); } else { cName = null; diff --git a/src/main/java/net/fabricmc/stitch/plugin/PluginLoader.java b/src/main/java/net/fabricmc/stitch/plugin/PluginLoader.java new file mode 100644 index 0000000..4ae08df --- /dev/null +++ b/src/main/java/net/fabricmc/stitch/plugin/PluginLoader.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.stitch.plugin; + +import java.util.ServiceLoader; + +public class PluginLoader { + public static void loadPlugins() { + Iterable plugins = ServiceLoader.load(StitchPlugin.class); + + for (StitchPlugin plugin : plugins) { + PluginRegistry.registerPlugin(plugin); + } + + // Register default plugin + PluginRegistry.registerPlugin(new StitchPlugin() { + }); + } +} diff --git a/src/main/java/net/fabricmc/stitch/plugin/PluginRegistry.java b/src/main/java/net/fabricmc/stitch/plugin/PluginRegistry.java new file mode 100644 index 0000000..c3475b4 --- /dev/null +++ b/src/main/java/net/fabricmc/stitch/plugin/PluginRegistry.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.stitch.plugin; + +import java.util.ArrayList; +import java.util.List; + +public class PluginRegistry { + private static List plugins = new ArrayList<>(5); + + public static void registerPlugin(StitchPlugin plugin) { + plugins.add(plugin); + } + + public static List getPlugins() { + return plugins; + } +} diff --git a/src/main/java/net/fabricmc/stitch/plugin/StitchPlugin.java b/src/main/java/net/fabricmc/stitch/plugin/StitchPlugin.java new file mode 100644 index 0000000..96091f2 --- /dev/null +++ b/src/main/java/net/fabricmc/stitch/plugin/StitchPlugin.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.stitch.plugin; + +import net.fabricmc.stitch.representation.ClassStorage; +import net.fabricmc.stitch.representation.JarClassEntry; +import net.fabricmc.stitch.representation.JarFieldEntry; +import net.fabricmc.stitch.representation.JarMethodEntry; + +public interface StitchPlugin { + /** + * Whether or not the passed class needs an intermediary name. + * A positive number means true, a negative number means false. + * The returned integer's value represents the result's priority; + * a higher priority will overwrite other plugins' results. + * Return at least +/-2 to overwrite the default plugin. + */ + default int needsIntermediaryName(ClassStorage storage, JarClassEntry cls) { + return cls.isAnonymous() ? -1 : 1; + } + + /** + * Whether or not the passed field needs an intermediary name. + * A positive number means true, a negative number means false. + * The returned integer's value represents the result's priority; + * a higher priority will overwrite other plugins' results. + * Return at least +/-2 to overwrite the default plugin. + */ + default int needsIntermediaryName(ClassStorage storage, JarClassEntry cls, JarFieldEntry fld) { + return 1; + } + + /** + * Whether or not the passed method needs an intermediary name. + * A positive number means true, a negative number means false. + * The returned integer's value represents the result's priority; + * a higher priority will overwrite other plugins' results. + * Return at least +/-2 to overwrite the default plugin. + */ + default int needsIntermediaryName(ClassStorage storage, JarClassEntry cls, JarMethodEntry mth) { + String name = mth.getName(); + + return name.charAt(0) != '<' && mth.isSource(storage, cls) ? 1 : -1; + } +}