[VERY VERY VERY BAD IDEA]

This commit is contained in:
Brian Neumann-Fopiano
2026-09-20 04:24:42 -04:00
parent f0e7fea13b
commit 2fa0853e42
892 changed files with 13482 additions and 53554 deletions
-1
View File
@@ -32,7 +32,6 @@ dependencies {
implementation(gradleApi())
implementation('org.ow2.asm:asm:9.10.1')
implementation('org.apache.commons:commons-compress:1.28.0')
implementation('com.github.VolmitSoftware:NMSTools:c88961416f')
implementation('io.papermc.paperweight:paperweight-userdev:2.0.0-beta.21')
testImplementation('junit:junit:4.13.2')
}
-5
View File
@@ -1,5 +0,0 @@
public class Config {
public int jvm = 25;
public NMSBinding.Type type = NMSBinding.Type.DIRECT;
public String version;
}
-285
View File
@@ -1,285 +0,0 @@
import com.volmit.nmstools.NMSToolsExtension;
import com.volmit.nmstools.NMSToolsPlugin;
import io.papermc.paperweight.userdev.PaperweightUser;
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension;
import io.papermc.paperweight.userdev.PaperweightUserExtension;
import io.papermc.paperweight.userdev.attribute.Obfuscation;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.Named;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.attributes.Bundling;
import org.gradle.api.attributes.Category;
import org.gradle.api.attributes.LibraryElements;
import org.gradle.api.attributes.Usage;
import org.gradle.api.file.FileTree;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskAction;
import org.gradle.jvm.toolchain.JavaLanguageVersion;
import org.gradle.jvm.toolchain.JavaToolchainService;
import org.gradle.work.DisableCachingByDefault;
import javax.inject.Inject;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static io.papermc.paperweight.util.constants.ConstantsKt.REOBF_CONFIG;
public class NMSBinding implements Plugin<Project> {
private static final String NEW_LINE = System.lineSeparator();
private static final byte[] NEW_LINE_BYTES = NEW_LINE.getBytes(StandardCharsets.UTF_8);
@Override
public void apply(Project target) {
ExtraPropertiesExtension extra = target.getExtensions().getExtraProperties();
Object configValue = extra.has("nms") ? extra.get("nms") : null;
if (!(configValue instanceof Config)) {
throw new GradleException("No NMS binding configuration found");
}
Config config = (Config) configValue;
int jvm = config.jvm;
Type type = config.type;
if (type == Type.USER_DEV) {
target.getPlugins().apply(PaperweightUser.class);
PaperweightUserDependenciesExtension dependenciesExtension =
target.getDependencies().getExtensions().findByType(PaperweightUserDependenciesExtension.class);
if (dependenciesExtension != null) {
dependenciesExtension.paperDevBundle(config.version);
}
JavaPluginExtension java = target.getExtensions().findByType(JavaPluginExtension.class);
if (java == null) {
throw new GradleException("Java plugin not found");
}
java.getToolchain().getLanguageVersion().set(JavaLanguageVersion.of(jvm));
JavaToolchainService javaToolchains = target.getExtensions().getByType(JavaToolchainService.class);
target.getExtensions().configure(PaperweightUserExtension.class,
extension -> extension.getJavaLauncher().set(javaToolchains.launcherFor(java.getToolchain())));
} else {
extra.set("nmsTools.useBuildTools", type == Type.BUILD_TOOLS);
target.getPlugins().apply(NMSToolsPlugin.class);
target.getExtensions().configure(NMSToolsExtension.class, extension -> {
extension.getJvm().set(jvm);
extension.getVersion().set(config.version);
});
}
String outgoingArtifactTask = type == Type.USER_DEV ? "jar" : "remap";
ObjectFactory objects = target.getObjects();
Configuration reobfConfiguration = target.getConfigurations().findByName(REOBF_CONFIG);
if (reobfConfiguration == null) {
reobfConfiguration = target.getConfigurations().create(REOBF_CONFIG);
}
target.getConfigurations().named(REOBF_CONFIG).configure(configuration -> {
configuration.setCanBeConsumed(true);
configuration.setCanBeResolved(false);
configuration.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, named(objects, Usage.class, Usage.JAVA_RUNTIME));
configuration.getAttributes().attribute(Category.CATEGORY_ATTRIBUTE, named(objects, Category.class, Category.LIBRARY));
configuration.getAttributes().attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, named(objects, LibraryElements.class, LibraryElements.JAR));
configuration.getAttributes().attribute(Bundling.BUNDLING_ATTRIBUTE, named(objects, Bundling.class, Bundling.EXTERNAL));
configuration.getAttributes().attribute(Obfuscation.Companion.getOBFUSCATION_ATTRIBUTE(), named(objects, Obfuscation.class, Obfuscation.OBFUSCATED));
configuration.getOutgoing().artifact(target.getTasks().named(outgoingArtifactTask));
});
int[] version = parseVersion(config.version);
int major = version[0];
int minor = version[1];
if (major <= 20 && minor <= 4) {
return;
}
target.getTasks().register("convert", ConversionTask.class, type);
target.getTasks().named("compileJava").configure(task -> task.dependsOn("convert"));
target.getRootProject().getTasks()
.matching(task -> task.getName().equals("prepareKotlinBuildScriptModel"))
.configureEach(task -> task.dependsOn(target.getPath() + ":convert"));
}
public static void nmsBinding(Project project, Action<Config> action) {
Config config = new Config();
action.execute(config);
project.getExtensions().getExtraProperties().set("nms", config);
project.getPlugins().apply(NMSBinding.class);
}
private static int[] parseVersion(String version) {
String trimmed = version;
int suffix = trimmed.indexOf('-');
if (suffix >= 0) {
trimmed = trimmed.substring(0, suffix);
}
String[] parts = trimmed.split("\\.");
if ("1".equals(parts[0])) {
return new int[]{Integer.parseInt(parts[1]), Integer.parseInt(parts[2])};
}
return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1])};
}
private static <T extends Named> T named(ObjectFactory objects, Class<T> type, String name) {
return objects.named(type, name);
}
@DisableCachingByDefault
public abstract static class ConversionTask extends DefaultTask {
private final Pattern pattern;
private final String replacement;
@Inject
public ConversionTask(Type type) {
setGroup("nms");
getInputs().property("type", type);
JavaPluginExtension java = getProject().getExtensions().findByType(JavaPluginExtension.class);
if (java == null) {
throw new GradleException("Java plugin not found");
}
Provider<FileTree> source = java.getSourceSets().named(SourceSet.MAIN_SOURCE_SET_NAME).map(SourceSet::getAllJava);
getInputs().files(source);
getOutputs().files(source);
if (type == Type.USER_DEV) {
this.pattern = Pattern.compile("org\\.bukkit\\.craftbukkit\\." + getProject().getName());
this.replacement = "org.bukkit.craftbukkit";
} else {
this.pattern = Pattern.compile("org\\.bukkit\\.craftbukkit\\.(?!" + getProject().getName() + ")");
this.replacement = "org.bukkit.craftbukkit." + getProject().getName() + ".";
}
}
@TaskAction
public void process() {
ExecutorService executor = Executors.newFixedThreadPool(16);
try {
Set<File> files = getInputs().getFiles().getFiles();
List<Future<?>> futures = new ArrayList<>(files.size());
for (File file : files) {
if (!file.getName().endsWith(".java")) {
continue;
}
futures.add(executor.submit(() -> processFile(file)));
}
for (Future<?> future : futures) {
future.get();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e.getCause());
} finally {
executor.shutdown();
}
}
private void processFile(File file) {
List<String> output = new ArrayList<>();
boolean changed = false;
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("package") || line.isBlank()) {
output.add(line);
continue;
}
if (!line.startsWith("import")) {
if (!changed) {
return;
}
output.add(line);
continue;
}
Matcher matcher = pattern.matcher(line);
if (!matcher.find()) {
output.add(line);
continue;
}
output.add(matcher.replaceAll(replacement));
changed = true;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
if (!changed) {
return;
}
try {
if (hasTrailingNewLine(file)) {
output.add("");
}
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
for (int i = 0; i < output.size(); i++) {
writer.append(output.get(i));
if (i + 1 < output.size()) {
writer.append(NEW_LINE);
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private boolean hasTrailingNewLine(File file) throws IOException {
if (NEW_LINE_BYTES.length == 0) {
return false;
}
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
if (raf.length() < NEW_LINE_BYTES.length) {
return false;
}
byte[] bytes = new byte[NEW_LINE_BYTES.length];
raf.seek(raf.length() - bytes.length);
raf.readFully(bytes);
return Arrays.equals(bytes, NEW_LINE_BYTES);
}
}
}
public enum Type {
USER_DEV,
BUILD_TOOLS,
DIRECT
}
}
@@ -0,0 +1,123 @@
import org.gradle.api.GradleException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HexFormat;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public final class NativeRuntimeArtifacts {
public static final String MANIFEST = "META-INF/iris/native-runtime.properties";
private static final String GROUP = "com.github.VolmitSoftware.VolmLib";
private static final String NATIVE = "art/arcane/volmlib/nativelib/";
private NativeRuntimeArtifacts() {
}
public static void generate(Map<String, File> artifacts, String version, File output, File rules) throws IOException {
List<String> manifest = new ArrayList<>();
manifest.add("modules=" + String.join(",", artifacts.keySet()));
manifest.add("repository=https://jitpack.io/");
Set<String> retained = new TreeSet<>();
for (Map.Entry<String, File> artifact : artifacts.entrySet()) {
String module = artifact.getKey();
manifest.add(module + ".coordinate=" + GROUP + ":" + module + ":" + version);
manifest.add(module + ".sha256=" + digest(artifact.getValue()));
try (JarFile jar = new JarFile(artifact.getValue())) {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.getName().endsWith(".class")) {
continue;
}
try (InputStream input = jar.getInputStream(entry)) {
for (String reference : ClassReferences.read(input.readAllBytes())) {
if (reference.startsWith("art/arcane/volmlib/") && !implementation(reference)) {
retained.add(reference);
}
}
}
}
}
}
File manifestFile = new File(output, MANIFEST);
Files.createDirectories(manifestFile.toPath().getParent());
Files.write(manifestFile.toPath(), manifest, StandardCharsets.UTF_8);
Path runtimeRoots = output.toPath().resolve("META-INF/volmit/runtime-roots.list");
Files.createDirectories(runtimeRoots.getParent());
Files.write(runtimeRoots, retained, StandardCharsets.UTF_8);
List<String> keep = new ArrayList<>();
keep.add("-keep class art.arcane.volmlib.nativelib.** { *; }");
for (String reference : retained) {
keep.add("-keep class " + reference.replace('/', '.') + " { *; }");
}
Files.createDirectories(rules.toPath().getParent());
Files.write(rules.toPath(), keep, StandardCharsets.UTF_8);
}
public static void verify(File artifact, List<String> adapters, String version) throws IOException {
try (JarFile jar = new JarFile(artifact)) {
JarEntry entry = jar.getJarEntry(MANIFEST);
if (entry == null) {
throw new GradleException("Missing native runtime dependency manifest");
}
Properties manifest = new Properties();
try (InputStream input = jar.getInputStream(entry)) {
manifest.load(input);
}
Set<String> expected = new TreeSet<>();
expected.add("native-common");
for (String adapter : adapters) {
expected.add("native-" + adapter);
}
Set<String> actual = new TreeSet<>(List.of(manifest.getProperty("modules", "").split(",")));
if (!expected.equals(actual)) {
throw new GradleException("Native dependency modules differ: " + actual + "; expected " + expected);
}
for (String module : expected) {
String coordinate = GROUP + ":" + module + ":" + version;
if (!coordinate.equals(manifest.getProperty(module + ".coordinate"))
|| !manifest.getProperty(module + ".sha256", "").matches("[0-9a-f]{64}")) {
throw new GradleException("Invalid native runtime dependency: " + module);
}
}
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
String name = entries.nextElement().getName();
if (name.endsWith(".class") && implementation(name)) {
throw new GradleException("Native runtime implementation is bundled: " + name);
}
}
}
}
private static boolean implementation(String name) {
if (!name.startsWith(NATIVE)) {
return false;
}
String relative = name.substring(NATIVE.length());
return relative.startsWith("common/") || relative.startsWith("minecraft")
|| relative.matches("v[0-9_]+R[0-9]+/.*");
}
private static String digest(File file) throws IOException {
try {
return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(file.toPath())));
} catch (NoSuchAlgorithmException exception) {
throw new IllegalStateException(exception);
}
}
}
@@ -11,8 +11,6 @@ public final class GenerationRevisionScope {
"core/src/main/java",
"spi/src/main/java",
"adapters/bukkit/plugin/src/main/java",
"adapters/bukkit/nms/v26_2_R1/src/main/java",
"adapters/bukkit/nms/v26_3_R1/src/main/java",
"adapters/minecraft-common/src/main/java",
"adapters/modded-common/src/main/java",
"adapters/client-common/src/main/java",
@@ -31,8 +29,6 @@ public final class GenerationRevisionScope {
"core/src/main/java/art/arcane/iris/world/safeguard/Diagnostic.java",
"core/src/main/java/art/arcane/iris/world/safeguard/ValueWithDiagnostics.java",
"core/src/main/java/art/arcane/iris/diagnostics/splash/IrisSplashRenderer.java",
"adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsWorldLifecycle.java",
"adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsWorldLifecycle.java",
"adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandDeveloper.java",
"adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandPregen.java",
"adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandIris.java",
@@ -20,7 +20,7 @@ import static org.junit.Assert.assertTrue;
public class GenerationRevisionScopeTest {
private static final String ENGINE_ROOT = "core/src/main/java/art/arcane/iris/generation/runtime/";
private static final String NMS_ROOT = "adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/";
private static final String BUKKIT_POLICY_ROOT = "core/src/main/java/art/arcane/iris/platform/bukkit/nms/";
private static final String COMMAND_ROOT = "adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/";
@Rule
@@ -70,7 +70,7 @@ public class GenerationRevisionScopeTest {
assertNewSourceChangesRevision(fixture, root + "/NewSource.java");
}
assertNewSourceChangesRevision(fixture, ENGINE_ROOT + "NewDiagnostics.java");
assertNewSourceChangesRevision(fixture, NMS_ROOT + "NmsWorldLifecycleHelper.java");
assertNewSourceChangesRevision(fixture, BUKKIT_POLICY_ROOT + "NewGenerationPolicy.java");
assertNewSourceChangesRevision(fixture, COMMAND_ROOT + "NewCommand.java");
assertNewSourceChangesRevision(fixture, "core/src/main/java/art/arcane/iris/world/safeguard/NewTask.java");
}
@@ -87,10 +87,31 @@ public class GenerationRevisionScopeTest {
assertTrue(relative, manifest.sources().containsKey(relative));
}
assertFalse(manifest.sources().containsKey(ENGINE_ROOT + "EngineDiagnostics.java"));
assertFalse(manifest.sources().containsKey(NMS_ROOT + "NmsWorldLifecycle.java"));
assertFalse(manifest.sources().containsKey(COMMAND_ROOT + "CommandIris.java"));
}
@Test
public void publishedNativeSourcesChangeTheGenerationFingerprint() throws Exception {
Fixture fixture = fixture();
Path nativeSources = fixture.root().resolve("native-sources.jar");
writeNativeSources(nativeSources, "class NativeGenerator { int height = 1; }");
Map<String, Path> dependencies = Map.of("volmlib", fixture.dependencies().get("volmlib"),
"com.github.VolmitSoftware.VolmLib:native-minecraft26_2-sources", nativeSources);
String original = GenerationBuildRevision.fingerprint(
GenerationBuildRevision.capture(fixture.options(), dependencies));
writeNativeSources(nativeSources, "class NativeGenerator { int height = 2; }");
assertNotEquals(original, GenerationBuildRevision.fingerprint(
GenerationBuildRevision.capture(fixture.options(), dependencies)));
}
private static void writeNativeSources(Path archive, String content) throws IOException {
try (ZipOutputStream output = new ZipOutputStream(Files.newOutputStream(archive))) {
output.putNextEntry(new ZipEntry("modded/art/arcane/volmlib/nativelib/NativeGenerator.java"));
output.write(content.getBytes(StandardCharsets.UTF_8));
output.closeEntry();
}
}
private static void assertNewSourceChangesRevision(Fixture fixture, String relative) throws IOException {
Path source = fixture.root().resolve(relative);
write(source, "class NewSource {}");
@@ -144,9 +165,9 @@ public class GenerationRevisionScopeTest {
"core/src/main/java/art/arcane/iris/pack/loading/IrisData.java",
"core/src/main/java/art/arcane/iris/configuration/IrisSettings.java",
"core/agent/src/main/java/GenerationTransformer.java",
NMS_ROOT + "NmsGenerationHooks.java",
NMS_ROOT + "NmsGenerationRegistry.java",
NMS_ROOT + "NMSBinding.java",
BUKKIT_POLICY_ROOT + "BukkitGeneratorContext.java",
BUKKIT_POLICY_ROOT + "BukkitBiomePolicy.java",
BUKKIT_POLICY_ROOT + "BukkitBinding.java",
"spi/src/main/java/art/arcane/iris/spi/PlatformGenerationRegistry.java",
"adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java"
);
@@ -25,8 +25,8 @@ public class ModdedArtifactVerifierTest {
private static final String METADATA = "fabric.mod.json";
private static final String CODEC_CLASS = "art/arcane/volmlib/util/mantle/io/Lz4IOWorkerCodecSupport.class";
private static final String HARDWARE_CLASS = "art/arcane/iris/platform/bootstrap/getHardware.class";
private static final String MIXIN_CONFIG = "irisworldgen.entity.mixins.json";
private static final String CLIENT_MIXIN_CONFIG = "irisworldgen.client.mixins.json";
private static final String MIXIN_CONFIG = "volmlib.entity.mixins.json";
private static final String CLIENT_MIXIN_CONFIG = "volmlib.client.mixins.json";
private static final String NEOFORGE_METADATA = "META-INF/neoforge.mods.toml";
private static final List<String> REQUIRED_ENTRIES = List.of(METADATA, CODEC_CLASS, HARDWARE_CLASS);
private static final byte[] INTERNAL_CODEC = (
@@ -0,0 +1,45 @@
import org.gradle.api.GradleException;
import org.junit.Test;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
public class NativeRuntimeArtifactsTest {
@Test
public void recordsExactDependenciesAndRetainsSharedProviderReferences() throws Exception {
Path directory = Files.createTempDirectory("native-artifacts");
File provider = directory.resolve("provider.jar").toFile();
ClassWriter writer = new ClassWriter(0);
writer.visit(Opcodes.V17, Opcodes.ACC_PUBLIC, "art/arcane/volmlib/nativelib/v26_2_R1/terrain/Provider", null,
"art/arcane/volmlib/util/collection/KList", null);
writer.visitEnd();
try (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(provider.toPath()))) {
jar.putNextEntry(new JarEntry("art/arcane/volmlib/nativelib/v26_2_R1/terrain/Provider.class"));
jar.write(writer.toByteArray());
jar.closeEntry();
}
File rules = directory.resolve("keep.pro").toFile();
NativeRuntimeArtifacts.generate(Map.of("native-common", provider, "native-v26_2_R1", provider),
"revision", directory.toFile(), rules);
assertTrue(Files.readString(rules.toPath()).contains("-keep class art.arcane.volmlib.util.collection.KList { *; }"));
File plugin = directory.resolve("plugin.jar").toFile();
try (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(plugin.toPath()))) {
jar.putNextEntry(new JarEntry(NativeRuntimeArtifacts.MANIFEST));
Files.copy(directory.resolve(NativeRuntimeArtifacts.MANIFEST), jar);
jar.closeEntry();
}
NativeRuntimeArtifacts.verify(plugin, List.of("v26_2_R1"), "revision");
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, List.of("v26_3_R1"), "revision"));
assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, List.of("v26_2_R1"), "other"));
}
}