diff --git a/gradle.properties b/gradle.properties index 670382d..eb2c2c9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,3 @@ #The version of the WilderWorkspace gradle plugin -workspaceVersion = 6.9.0.1 - -org.gradle.jvmargs=-Xmx4096m +workspaceVersion = 7.0.0.0 +org.gradle.jvmargs=-Xmx4096m \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c556120..01b6e6d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,14 +3,14 @@ asm_version = "9.9" commons_lang_version = "3.20.0" commons_text_version = "1.15.0" commons_io_version = "2.22.0" -fabric_loader_version = "0.18.4+WilderForge.0.4.0+WilderForge" -fabric_loom_version = "1.14.10" +fabric_loader_version = "0.19.1+WilderForge.0.5.0" +fabric_loom_version = "1.16.2" guava_version = "33.4.8-jre" gson_version = "2.14.0" mixin_version = "0.16.3+mixin.0.8.7" provider_version = "1.14.0.0" -log4j_version = "2.25.4" -thrixlvault_version = "0.12.1.1" +log4j_version = "2.26.0" +thrixlvault_version = "0.12.2.0" vineflower_version = "1.12.0" libgdx_version = "1.11.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1a70468..5dd3c01 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/com/wildermods/workspace/GameJars.java b/src/main/java/com/wildermods/workspace/GameJars.java deleted file mode 100644 index 84761a5..0000000 --- a/src/main/java/com/wildermods/workspace/GameJars.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.wildermods.workspace; - -import java.nio.file.Path; - -import com.wildermods.workspace.util.FileHelper; - -public enum GameJars { - devvotes(FileHelper.libDir, "devvotes-client.jar"), - gameEngine(FileHelper.libDir, "gameEngine-1.0.jar"), - server_1_0(FileHelper.libDir, "server-1.0.jar"), - libgdx_1_11(FileHelper.libDir, "gdx-1.11.0.jar"), - scratchpad("scratchpad.jar"), - wildermyth("wildermyth.jar"), - - ; - - private final Path dir; - private final String name; - - private GameJars(Path dir, String name) { - this.dir = dir; - this.name = name; - } - - private GameJars(String name) { - this(FileHelper.mainDir, name); - } - - public Path getDir() { - return dir; - } - - public Path getPath() { - return dir.resolve(name); - } - - public String getJarName() { - return name; - } - - public static GameJars fromString(String name) { - for(GameJars jar : values()) { - if(jar.name.equals(name)) { - return jar; - } - System.out.println(name + "!= " + jar.name); - } - return null; - } - - public static GameJars fromPath(Path path) { - if(path == null) {return null;}; - return fromString(path.getFileName().toString()); - } - - public static GameJars fromPathString(String pathString) { - if(pathString == null) {return null;}; - return fromPath(Path.of(pathString)); - } - - public String toString() { - return name(); - } -} diff --git a/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java b/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java index 6deff4f..adaaf31 100644 --- a/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java +++ b/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java @@ -1,6 +1,7 @@ package com.wildermods.workspace; import org.gradle.api.Project; +import org.gradle.api.Task; import org.gradle.api.artifacts.ComponentMetadataContext; import org.gradle.api.artifacts.ComponentMetadataDetails; import org.gradle.api.artifacts.ComponentMetadataRule; @@ -39,6 +40,7 @@ import com.google.gson.JsonParser; import com.wildermods.thrixlvault.utils.version.Version; import com.wildermods.workspace.dependency.CapabilityHandler; +import com.wildermods.workspace.dependency.CapabilityHandler.SourceStrategy; import com.wildermods.workspace.dependency.ProjectDependencyType; import com.wildermods.workspace.dependency.WWProjectDependency; import com.wildermods.workspace.tasks.ClearLocalRuntimeTask; @@ -53,6 +55,7 @@ import net.fabricmc.loom.build.nesting.NestableJarGenerationTask; import java.io.File; +import java.io.IOError; import java.io.IOException; import java.io.InputStreamReader; import java.io.Serializable; @@ -77,6 +80,7 @@ import javax.net.ssl.HttpsURLConnection; import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; import org.gradle.api.Plugin; /** @@ -101,6 +105,7 @@ public class WilderWorkspacePluginImpl implements Plugin { /** The version of the WilderWorkspace plugin. */ public static final String VERSION = "@workspaceVersion@"; public static final String GAME_LIBS_REPO_NAME = "gameLibs"; + public static final String DECOMP_MODULES = "wildermyth.decompilationModules"; static { JavaPlugin.class.arrayType(); @@ -363,19 +368,33 @@ private void setupTasks(WWProjectContext context) { Project project = context.getProject(); WilderWorkspaceExtension extension = context.getWWExtension(); + project.getTasks().register("prepare", task -> { + task.dependsOn("copyLocalDependenciesToWorkspace"); + }); + + TaskProvider checkGame = project.getTasks().register("checkGame", task -> { + task.doLast(t -> { + Path binLib = project.getRootDir().toPath().resolve("bin/lib"); + if (!Files.isDirectory(binLib)) { + throw new GradleException( + "Game not found. Please run the 'prepare' task first: ./gradlew prepare" + ); + } + }); + }); + project.getTasks().register("copyLocalDependenciesToWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> { task.setPlatform(extension.getPlatform()); task.setPatchline(extension.getPatchline()); task.setDestDir(extension.getGameDestDir()); - task.setPlatform(extension.getPlatform()); task.setSteamUser(extension.getSteamUser()); task.finalizedBy(project.getTasks().getByName("copyProjectDependencies")); task.getOutputs().cacheIf(t -> false); task.getOutputs().upToDateWhen(t -> false); + task.mustRunAfter(project.getTasks().getByName("checkGame")); }); project.getTasks().register("decompileJars", DecompileJarsTask.class, task -> { - task.setCompiledDir(extension.getGameDestDir()); task.setDecompDir(extension.getDecompDir()); }); @@ -387,11 +406,10 @@ private void setupTasks(WWProjectContext context) { project.getTasks().register("setupDecompWorkspace", DefaultTask.class, task -> { task.getOutputs().upToDateWhen(taskOutput -> false); task.getOutputs().cacheIf(taskOutput -> false); - + task.dependsOn(checkGame); task.dependsOn(project.getTasks().getByName("copyLocalDependenciesToWorkspace")); task.dependsOn(project.provider(() -> { - DecompileJarsTask decompileTask = (DecompileJarsTask)project.getTasks().named("decompileJars").get(); - + DecompileJarsTask decompileTask = (DecompileJarsTask) project.getTasks().named("decompileJars").get(); return decompileTask; })); @@ -520,6 +538,14 @@ private void setupTasks(WWProjectContext context) { }); }); + project.afterEvaluate(p -> { + Task nestGenTask = p.getTasks().findByName("generateNestableJars"); + if (nestGenTask instanceof NestableJarGenerationTask) { + NestableJarGenerationTask task = (NestableJarGenerationTask) nestGenTask; + task.getUncompressNestedJars().set(true); + } + }); + project.getTasks().named("assemble").configure(assemble -> assemble.dependsOn(nestJarsTask)); project.getTasks().named("publish").configure(publish -> { publish.dependsOn(project.getTasks().named("assemble")); @@ -530,6 +556,8 @@ private void setupTasks(WWProjectContext context) { project.getTasks().named("eclipseClasspath").configure(eclipse -> eclipse.dependsOn(genNestJars)); } + + project.getTasks().named("jar", Jar.class, jar -> { jar.getOutputs().upToDateWhen(t -> false); jar.doFirst(t -> { @@ -617,6 +645,43 @@ private void setupCapabilities(WWProjectContext context) { // 7. Remove the temporary flatDir project.getRepositories().remove(project.getRepositories().getByName(GAME_LIBS_REPO_NAME)); + + // 8. After project is evaluated, set project property + project.afterEvaluate((p) -> { + Set resolvedJarPaths = project.getConfigurations() + .getByName(ProjectDependencyType.compileClasspath.name()) + .getResolvedConfiguration() + .getResolvedArtifacts() + .stream() + .map(artifact -> { + try { + return artifact.getFile().toPath().toRealPath().normalize(); + } catch (IOException e) { + return artifact.getFile().toPath().normalize(); + } + }) + .collect(Collectors.toSet()); + + Map filteredModules = new HashMap<>(); + for (Map.Entry entry : flatDirModuleInfo.entrySet()) { + Path jarPath; + try { + jarPath = project.getRootDir().toPath().resolve(entry.getValue().relativeJarPath()).toRealPath().normalize(); + } + catch(IOException e) { + throw new IOError(e); + } + + if (resolvedJarPaths.contains(jarPath)) { + filteredModules.put(entry.getKey(), entry.getValue()); + } else { + project.getLogger().info("Excluding from decompilation (not in classpath): " + jarPath); + } + } + project.getExtensions().getExtraProperties().set(DECOMP_MODULES, filteredModules); + project.getLogger().info("Filtered decompilation modules: " + filteredModules.size() + " (from " + flatDirModuleInfo.size() + " total)"); + }); + project.getExtensions().getExtraProperties().set(DECOMP_MODULES, flatDirModuleInfo); } catch (Exception e) { throw new Error("Failed to set up local Ivy repository", e); @@ -685,9 +750,14 @@ private void setupEclipsePlugin(WWProjectContext context) { project.afterEvaluate(proj -> { if (project.getPlugins().hasPlugin("eclipse")) { + Map modules = (Map) proj.getExtensions().getExtraProperties().get(DECOMP_MODULES); EclipseModel eclipseModel = proj.getExtensions().getByType(EclipseModel.class); EclipseClasspath classpath = eclipseModel.getClasspath(); + if (modules == null || modules.isEmpty()) { + project.getLogger().warn("No decompilation modules found; source attachments will be missing. Null: " + (modules == null)); + } + classpath.file(xmlFileContent -> { xmlFileContent.getWhenMerged().add((classPathMerged) -> { Classpath c = (Classpath) classPathMerged; @@ -719,24 +789,65 @@ private void setupEclipsePlugin(WWProjectContext context) { List knotClasspath = new ArrayList<>(); Iterator it = (Iterator)(Object)c.getEntries().iterator(); - String path = null; + Path path = null; while(it.hasNext()) { ClasspathEntry cpe = it.next(); if(cpe instanceof AbstractClasspathEntry entry) { - path = Path.of(entry.getPath()).toAbsolutePath().normalize().toString(); + String pathStr = entry.getPath(); + path = Path.of(entry.getPath()).toAbsolutePath().normalize(); boolean isProvider = false; - + project.getLogger().warn(entry.getClass().getCanonicalName()); if(entry instanceof Library lib) { - - - GameJars gameJar = GameJars.fromPathString(lib.getPath()); - if(gameJar != null) { - project.getLogger().info("Found a game jar to add sources to: " + lib.getPath()); - FileReference source = c.fileReference(Path.of(extension.getDecompDir()).resolve("decomp").resolve(gameJar.getJarName()).normalize().toAbsolutePath().toFile()); - lib.setSourcePath(source); - project.getLogger().info("Setting sources of " + gameJar + " to " + source.getPath()); + if(modules != null) { + ModuleInfo module = null; + for (ModuleInfo info : modules.values()) { + try { + Path jarPath = project.getRootDir().toPath() + .resolve(info.relativeJarPath()) + .toRealPath() + .normalize(); + Path libPath = path.toRealPath().normalize(); + if (jarPath.equals(libPath)) { + module = info; + break; + } + } catch (IOException e) { + project.getLogger().debug("failed to resolve source jar for JAR", e); + } + } + if(module != null) { + SourceStrategy strategy = module.sourceStrategy(); + if ("decompile".equals(strategy.type)) { + Path decompRoot = Path.of(extension.getDecompDir()); + // Get the original JAR file name (e.g., "wildermyth.jar") + Path originalJarName = module.relativeJarPath().getFileName(); + Path sourcePath = decompRoot.resolve("decomp").resolve(originalJarName); + if (Files.exists(sourcePath)) { + FileReference sourceRef = c.fileReference(sourcePath.toFile()); + lib.setSourcePath(sourceRef); + project.getLogger().info("Attached decompiled sources for " + pathStr + " -> " + sourcePath); + } + else { + project.getLogger().warn("Decompiled sources not found for " + pathStr + " expected at " + sourcePath); + } + } + else if("file".equals(strategy.type)) { + Path sourcePath = project.getRootDir().toPath().resolve(strategy.path).normalize(); + if (Files.exists(sourcePath)) { + FileReference sourceRef = c.fileReference(sourcePath.toFile()); + lib.setSourcePath(sourceRef); + project.getLogger().info("Attached source file for " + pathStr + " -> " + sourcePath); + } + else { + project.getLogger().warn("Source file not found: " + sourcePath); + } + } + else if ("skip".equals(strategy.type)) { + project.getLogger().info("Skipping source attachment for " + pathStr); + } + } } if(pPaths.contains(lib.getPath())) { @@ -749,7 +860,7 @@ private void setupEclipsePlugin(WWProjectContext context) { continue; } } - knotClasspath.add(path); + knotClasspath.add(pathStr); } if(path != null) { @@ -850,15 +961,23 @@ private Map scanFlatDirModules(Project project, CapabilityHa handler.findModuleForFile(jar).ifPresent(module -> { project.getLogger().info("Creating modules for " + jar); module.fileAliases.stream() - .filter(alias -> alias.matches(jar)) - .findFirst() - .flatMap(alias -> alias.extractVersion(jar)) - .ifPresent(version -> { - Path relative = project.getRootDir().toPath().relativize(jar); - ModuleInfo m = new ModuleInfo(module.getGroup(), module.getName(), version, relative, project.getRootDir().toPath()); - result.put(moduleName, m); - project.getLogger().info("Created module for " + jar + " (" + m + ")"); - }); + .filter(alias -> alias.matches(jar)) + .findFirst() + .flatMap(alias -> alias.extractVersion(jar, project)) // now returns Optional + .ifPresent(res -> { + Path relative = project.getRootDir().toPath().relativize(jar); + ModuleInfo m = new ModuleInfo( + module.getGroup(), + module.getName(), + res.version, + relative, + project.getRootDir().toPath(), + res.sourceStrategy // pass the source strategy + ); + result.put(moduleName, m); + project.getLogger().info("Created module for " + jar + " (" + m + ")"); + project.getLogger().info("Expecting source strategy: " + res.sourceStrategy.type); + }); }); }); } @@ -968,5 +1087,9 @@ public void execute(ComponentMetadataContext context) { } } - public static record ModuleInfo(String group, String artifact, Version version, Path relativeJarPath, Path projectRoot) implements Serializable {} + private boolean isWorkspaceInitialized(Project project) { + return Files.exists(project.getLayout().getBuildDirectory().getAsFile().get().toPath().resolve("bin").resolve("lib")); + } + + public static record ModuleInfo(String group, String artifact, Version version, Path relativeJarPath, Path projectRoot, SourceStrategy sourceStrategy) implements Serializable {} } diff --git a/src/main/java/com/wildermods/workspace/decomp/DecompilerBuilder.java b/src/main/java/com/wildermods/workspace/decomp/DecompilerBuilder.java index cd2f381..f3e292b 100644 --- a/src/main/java/com/wildermods/workspace/decomp/DecompilerBuilder.java +++ b/src/main/java/com/wildermods/workspace/decomp/DecompilerBuilder.java @@ -1,13 +1,14 @@ package com.wildermods.workspace.decomp; import java.nio.file.Path; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Set; -import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import com.wildermods.workspace.WilderWorkspacePluginImpl.ModuleInfo; +import com.wildermods.workspace.dependency.CapabilityHandler.SourceStrategy; import net.fabricmc.loom.api.decompilers.DecompilationMetadata; import net.fabricmc.loom.util.IOStringConsumer; @@ -17,68 +18,101 @@ public class DecompilerBuilder { private int numberOfThreads = 4; private Path javadocs; private Path decompDest; - private Collection jarsToDecomp = new HashSet(); - private Collection libraries = new HashSet(); + private final Set libraries = new HashSet<>(); + private final Set sources = new HashSet<>(); // internal private IOStringConsumer logger; - private Map options = new HashMap<>(); - + private final Map options = new HashMap<>(); + + // For module‑based configuration + private Map modules; + private Path projectRoot; + public DecompilerBuilder() {} - + + // Existing setters (threadCount, javadocs, setDecompDest, addLibraries, setLogger, setOption) public DecompilerBuilder setThreadCount(int threadCount) { - if(threadCount > 0 && threadCount <= Runtime.getRuntime().availableProcessors()) { + if (threadCount > 0 && threadCount <= Runtime.getRuntime().availableProcessors()) { numberOfThreads = threadCount; } return this; } - + public DecompilerBuilder setJavadocs(Path javadocs) { this.javadocs = javadocs; return this; } - + public DecompilerBuilder setDecompDest(Path decompDest) { this.decompDest = decompDest; return this; } - - public DecompilerBuilder addJarsToDecomp(Path... jars) { - this.jarsToDecomp.addAll(Arrays.asList(jars)); - return this; - } - - public DecompilerBuilder addLibraries(Path... libraries) { - this.libraries.addAll(Arrays.asList(libraries)); + + public DecompilerBuilder addLibraries(Path... libs) { + for (Path lib : libs) { + libraries.add(lib); + } return this; } - - public DecompilerBuilder setLogger(Logger logger) { + + public DecompilerBuilder setLogger(IOStringConsumer logger) { this.logger = logger; return this; } - + public DecompilerBuilder setOption(String key, String value) { options.put(key, value); return this; } - + + // New method: supply modules and project root + public DecompilerBuilder setModules(Map modules, Path projectRoot) { + this.modules = modules; + this.projectRoot = projectRoot; + return this; + } + + // Internal method to add a source JAR (for decompilation) + private void addSource(Path jar) { + sources.add(jar); + } + + public Collection getSources() { + return sources; + } + public DecompilationMetadata getMetaData() { return new DecompilationMetadata(numberOfThreads, javadocs, libraries, logger, options); } - - public Collection getJarsToDecomp() { - return jarsToDecomp; - } - + public Path getDecompDest() { return decompDest.resolve("decomp"); } - + public Path getLinemapDest() { return decompDest.resolve("decomp").resolve("linemaps"); } - + public WilderWorkspaceDecompiler build() { - decompDest.getClass(); //throw if null + if (decompDest == null) { + throw new IllegalStateException("Decomp destination not set"); + } + // Process modules if supplied + if (modules != null && projectRoot != null) { + for (ModuleInfo info : modules.values()) { + Path jarPath = projectRoot.resolve(info.relativeJarPath()).normalize(); + if (!jarPath.toFile().exists()) { + continue; + } + SourceStrategy strategy = info.sourceStrategy(); + if ("decompile".equals(strategy.type)) { + addSource(jarPath); + } else { + // treat as library for decompilation context + libraries.add(jarPath); + } + } + } + // Now create the decompiler instance return new WilderWorkspaceDecompiler(this); } -} +} \ No newline at end of file diff --git a/src/main/java/com/wildermods/workspace/decomp/WilderWorkspaceDecompiler.java b/src/main/java/com/wildermods/workspace/decomp/WilderWorkspaceDecompiler.java index e4bce55..0194f45 100644 --- a/src/main/java/com/wildermods/workspace/decomp/WilderWorkspaceDecompiler.java +++ b/src/main/java/com/wildermods/workspace/decomp/WilderWorkspaceDecompiler.java @@ -3,6 +3,7 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.Map; + import org.jetbrains.java.decompiler.main.Fernflower; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; @@ -13,51 +14,45 @@ public class WilderWorkspaceDecompiler { private final Fernflower ff; - private final DecompilerBuilder builder; - private final DecompilationMetadata metaData; - + WilderWorkspaceDecompiler(DecompilerBuilder builder) { - this.builder = builder; - this.metaData = builder.getMetaData(); - - final Map options = new HashMap<>( - Map.of( - IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1", - IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "1", - IFernflowerPreferences.DUMP_CODE_LINES, "1", - IFernflowerPreferences.REMOVE_SYNTHETIC, "1", - IFernflowerPreferences.LOG_LEVEL, "trace", - IFernflowerPreferences.THREADS, String.valueOf(metaData.numberOfThreads()), - IFernflowerPreferences.INDENT_STRING, "\t" - //IFabricJavadocProvider.PROPERTY_NAME, new WilderWorkspaceJavadocProvider(metaData.javaDocs().toFile()) - ) - ); - + DecompilationMetadata metaData = builder.getMetaData(); + Path decompDest = builder.getDecompDest(); + Path linemapDest = builder.getLinemapDest(); + + final Map options = new HashMap<>(Map.of( + IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1", + IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, "1", + IFernflowerPreferences.DUMP_CODE_LINES, "1", + IFernflowerPreferences.REMOVE_SYNTHETIC, "1", + IFernflowerPreferences.LOG_LEVEL, "trace", + IFernflowerPreferences.THREADS, String.valueOf(metaData.numberOfThreads()), + IFernflowerPreferences.INDENT_STRING, "\t" + )); options.putAll(metaData.options()); - + IResultSaver saver = new WWThreadSafeResultSaver( - () -> builder.getDecompDest(), - () -> builder.getLinemapDest() - ); + () -> decompDest, + () -> linemapDest + ); ff = new Fernflower(saver, options, (IFernflowerLogger) metaData.logger()); - - for(Path library : metaData.libraries()) { + + // Add libraries + for (Path library : metaData.libraries()) { ff.addLibrary(library.toFile()); } - - for(Path compiledJar : builder.getJarsToDecomp()) { - ff.addSource(compiledJar.toFile()); + + // Add sources to decompile + for (Path source : builder.getSources()) { + ff.addSource(source.toFile()); } - } public void decompile() { try { ff.decompileContext(); - } - finally { + } finally { ff.clearContext(); } } - -} +} \ No newline at end of file diff --git a/src/main/java/com/wildermods/workspace/decomp/WilderWorkspaceJavadocProvider.java b/src/main/java/com/wildermods/workspace/decomp/WilderWorkspaceJavadocProvider.java index 4a441f6..04cd9d4 100644 --- a/src/main/java/com/wildermods/workspace/decomp/WilderWorkspaceJavadocProvider.java +++ b/src/main/java/com/wildermods/workspace/decomp/WilderWorkspaceJavadocProvider.java @@ -12,13 +12,13 @@ public class WilderWorkspaceJavadocProvider extends TinyJavadocProvider { private final TinyJavadocProvider parent; - public WilderWorkspaceJavadocProvider(File tinyFile) { - super(tinyFile); + public WilderWorkspaceJavadocProvider(File tinyFile, String runtimeNamespace) { + super(tinyFile, runtimeNamespace); if(tinyFile == null) { parent = null; } else { - parent = new TinyJavadocProvider(tinyFile); + parent = new TinyJavadocProvider(tinyFile, runtimeNamespace); } } diff --git a/src/main/java/com/wildermods/workspace/decomp/WildermythDecompilerSetup.java b/src/main/java/com/wildermods/workspace/decomp/WildermythDecompilerSetup.java index 44cb670..1ba158c 100644 --- a/src/main/java/com/wildermods/workspace/decomp/WildermythDecompilerSetup.java +++ b/src/main/java/com/wildermods/workspace/decomp/WildermythDecompilerSetup.java @@ -1,18 +1,14 @@ package com.wildermods.workspace.decomp; -import java.io.FileNotFoundException; import java.io.IOException; -import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.HashMap; +import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.gradle.api.Project; -import com.wildermods.workspace.GameJars; -import com.wildermods.workspace.util.FileHelper; +import com.wildermods.workspace.WilderWorkspacePluginImpl.ModuleInfo; import net.fabricmc.loom.decompilers.ClassLineNumbers; import net.fabricmc.loom.decompilers.LineNumberRemapper; @@ -20,133 +16,63 @@ public class WildermythDecompilerSetup { private final DecompilerBuilder builder; - - public WildermythDecompilerSetup(DecompilerBuilder builder) { + private final Project project; + private final Map modules; + + public WildermythDecompilerSetup(DecompilerBuilder builder, Project project, Map modules) { this.builder = builder; + this.project = project; + this.modules = modules; } - - public void decompile(Path compiledDir, Path decompDir) throws IOException { - DecompilerBuilder b = builder; - b.setDecompDest(decompDir); - HashMap compiledJars = new HashMap(); - HashMap decompiledJarDests = new HashMap(); - - /* - * Add jars to decompile - */ - Files.walkFileTree(compiledDir, new SimpleFileVisitor() { - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - //the only files we want to decompile are located in the '.', './lib', and "./unmapped" directories - if(attrs.isSymbolicLink() || ( - !dir.getFileName().toString().equals("lib") && - !dir.equals(compiledDir) && - !dir.getFileName().toString().equals("unmapped"))) { - return FileVisitResult.SKIP_SUBTREE; - } - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if(FileHelper.shouldBeRemapped(file)) { - System.out.println("Adding " + file.toAbsolutePath().normalize().toString() + " as input for the decompiler."); - compiledJars.put(file.getFileName().toString(), file); - decompiledJarDests.put(file.getFileName().toString(), compiledDir.resolve(GameJars.fromPath(file).getPath())); - b.addJarsToDecomp(file.normalize().toAbsolutePath()); - return FileVisitResult.CONTINUE; - } - return FileVisitResult.CONTINUE; - } - - }); - - /* - * Add remaining jars as libraries - */ - Files.walkFileTree(compiledDir, new SimpleFileVisitor() { - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - //the only files we want to add as libraries are located in the '.' and './lib' - if(attrs.isSymbolicLink() || ( - !dir.getFileName().toString().equals("lib") && - !dir.equals(compiledDir) && - !dir.getFileName().toString().equals("unmapped"))) { - return FileVisitResult.SKIP_SUBTREE; - } - return FileVisitResult.CONTINUE; - } + public void decompile(Path decompDir) throws IOException { + // Pass modules and project root to the builder + builder.setModules(modules, project.getRootDir().toPath()); + builder.setDecompDest(decompDir); - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if(FileHelper.shouldBeRemapped(file)) { - System.out.println("Skipping " + file.toAbsolutePath().normalize().toString() + " as library for the decompiler."); - return FileVisitResult.CONTINUE; - } - System.out.println("Adding " + file.toAbsolutePath().normalize().toString() + " as library for the decompiler."); - b.addLibraries(file.normalize().toAbsolutePath()); - return FileVisitResult.CONTINUE; - } - - }); - - WilderWorkspaceDecompiler decompiler = b.build(); + // Optionally add any extra libraries (if needed) – but they are already handled via modules + // Build and decompile + WilderWorkspaceDecompiler decompiler = builder.build(); decompiler.decompile(); - + + // Remap line numbers (unchanged from your previous version) Path linemapDir = decompDir.resolve("decomp").resolve("linemaps"); - if(Files.exists(linemapDir)) { - Files.walkFileTree(linemapDir, new SimpleFileVisitor() { - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if(!dir.equals(linemapDir)) { //there are no subdirectories we want to visit - return FileVisitResult.SKIP_SUBTREE; - } - return FileVisitResult.CONTINUE; + if (Files.exists(linemapDir)) { + Map sourceJars = new java.util.HashMap<>(); + for (ModuleInfo info : modules.values()) { + if ("decompile".equals(info.sourceStrategy().type)) { + Path jar = project.getRootDir().toPath().resolve(info.relativeJarPath()).normalize(); + sourceJars.put(jar.getFileName().toString(), jar); } - - @Override - public FileVisitResult visitFile(Path linemap, BasicFileAttributes attrs) throws IOException { - System.out.println("Found linemap " + linemap.getFileName()); + } + Files.list(linemapDir) + .filter(p -> p.toString().endsWith(".linemap")) + .forEach(linemap -> { String jarName = StringUtils.removeEnd(linemap.getFileName().toString(), ".linemap"); - Path unmappedJar = compiledJars.get(jarName); - if(Files.exists(unmappedJar)) { - System.out.println("Found jar to remap: " + unmappedJar.getFileName()); + Path sourceJar = sourceJars.get(jarName); + if (sourceJar != null && Files.exists(sourceJar)) { + project.getLogger().info("Remapping " + sourceJar); try { - remap(linemap, unmappedJar, decompiledJarDests.get(jarName)); + remap(linemap, sourceJar, sourceJar); // overwrite original (as before) } catch (Throwable e) { - throw new IOException(e); + throw new RuntimeException("Failed to remap " + jarName, e); } + } else { + project.getLogger().warn("No source jar found for linemap: " + jarName); } - else { - throw new FileNotFoundException(unmappedJar.normalize().toAbsolutePath().toString()); - } - return FileVisitResult.CONTINUE; - } - }); - } - else { - System.out.println("Did not find linemap dir at " + linemapDir); + }); + } else { + project.getLogger().info("No linemap directory found at " + linemapDir); } } - - /** - * Remaps the line numbers of the specified JAR file using the provided line map. - * - * @param linemap the path to the line map file - * @param jarToRemap the path to the JAR file to remap - * @param remappedJarDest the destination path for the remapped JAR file - * @throws Throwable if an error occurs during remapping - */ + private void remap(Path linemap, Path jarToRemap, Path remappedJarDest) throws Throwable { - System.out.println("Remapping " + jarToRemap + " to " + remappedJarDest); + project.getLogger().info("Remapping " + jarToRemap + " to " + remappedJarDest); ClassLineNumbers lineNumbers = ClassLineNumbers.readMappings(Files.newBufferedReader(linemap)); LineNumberRemapper remapper = new LineNumberRemapper(lineNumbers); - if(Files.notExists(remappedJarDest)) { + if (Files.notExists(remappedJarDest)) { Files.createDirectories(remappedJarDest.getParent()); } remapper.process(jarToRemap, remappedJarDest); } - -} +} \ No newline at end of file diff --git a/src/main/java/com/wildermods/workspace/dependency/CapabilityHandler.java b/src/main/java/com/wildermods/workspace/dependency/CapabilityHandler.java index 5021413..e0373e2 100644 --- a/src/main/java/com/wildermods/workspace/dependency/CapabilityHandler.java +++ b/src/main/java/com/wildermods/workspace/dependency/CapabilityHandler.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Serializable; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; @@ -19,11 +20,13 @@ import org.gradle.api.Project; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.wildermods.thrixlvault.exception.VersionParsingException; import com.wildermods.thrixlvault.utils.version.Version; +import com.wildermods.thrixlvault.utils.version.VersionPredicate; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; @@ -94,33 +97,125 @@ public static class CanonicalModule { public String toString() { return key;} } + /** + * Source strategy for a version rule. + */ + public static class SourceStrategy implements Serializable { + private static final long serialVersionUID = 7503141011986913752L; + public final String type; // "decompile", "file", "skip" + public final List exclude; // version exclusions (simple string matching) + public final String path; // for "file" type, path to source jar (relative to project root) + + public SourceStrategy(String type, List exclude, String path) { + this.type = type; + this.exclude = exclude == null ? List.of() : List.copyOf(exclude); + this.path = path; + } + + @Override + public String toString() { + return "SourceStrategy{" + type + ", exclude=" + exclude + ", path=" + path + "}"; + } + } + + /** + * Container for a version result with its associated source strategy. + */ + public static class VersionResult { + public final Version version; + public final SourceStrategy sourceStrategy; + + public VersionResult(Version version, SourceStrategy sourceStrategy) { + this.version = version; + this.sourceStrategy = sourceStrategy; + } + } + + /** + * A version rule: knows how to extract the version string and also holds candidate source strategies. + */ + public static class VersionRule { + private final VersionExtractor extractor; + private final List sourceStrategies; // in order of appearance in JSON + + public VersionRule(VersionExtractor extractor, List sourceStrategies) { + this.extractor = extractor; + this.sourceStrategies = sourceStrategies; + } + + public Optional extract(Path file, Project project) { + Optional versionStr = extractor.extract(file); + if (versionStr.isEmpty() || versionStr.get().isBlank()) { + return Optional.empty(); + } + Version version; + try { + version = Version.parse(versionStr.get()); + } catch (VersionParsingException e) { + project.getLogger().warn("Failed to parse version '{}' for {}", versionStr.get(), file); + throw new AssertionError(e); //fail fast so we don't deploy broken stuff + } + // Select the first source strategy that does NOT exclude this version + SourceStrategy selected = null; + for (SourceStrategy s : sourceStrategies) { + boolean excluded = false; + for (String excl : s.exclude) { + VersionPredicate exclude; + try { + exclude = VersionPredicate.parse(excl); + } catch (VersionParsingException e) { + throw new AssertionError(e); + } + if (exclude.test(version)) { + excluded = true; + break; + } + } + if (!excluded) { + selected = s; + break; + } + } + if (selected == null) { + project.getLogger().warn("No source strategy applicable for version {} of {}", version, file); + selected = new SourceStrategy("skip", List.of(), null); + } + return Optional.of(new VersionResult(version, selected)); + } + } + + @FunctionalInterface + public interface VersionExtractor { + Optional extract(Path file); + } + public static class FileAlias { public final List directories; public final List namePatterns; - public final List versionExtractors; + public final List versionRules; - private FileAlias(List dirs, List patterns, List extractors) { + private FileAlias(List dirs, List patterns, List rules) { this.directories = dirs; this.namePatterns = patterns; - this.versionExtractors = extractors; + this.versionRules = rules; } public static FileAlias fromJson(CanonicalModule module, JsonObject json, Project project) { Path baseDir = project.getRootDir().toPath(); - + List dirs = json.getAsJsonArray("location").asList().stream() - .map(e -> baseDir.resolve(e.getAsString()).normalize()) - .collect(Collectors.toList()); - + .map(e -> baseDir.resolve(e.getAsString()).normalize()) + .collect(Collectors.toList()); + List patterns = json.getAsJsonArray("name").asList().stream() - .map(e -> Pattern.compile(e.getAsString())) - .collect(Collectors.toList()); - - List extractors = json.getAsJsonArray("version").asList().stream() - .map(e -> createVersionExtractor(module, e.getAsJsonObject(), project)) - .collect(Collectors.toList()); - - return new FileAlias(dirs, patterns, extractors); + .map(e -> Pattern.compile(e.getAsString())) + .collect(Collectors.toList()); + + List rules = json.getAsJsonArray("version").asList().stream() + .map(e -> createVersionRule(module, e.getAsJsonObject(), project)) + .collect(Collectors.toList()); + + return new FileAlias(dirs, patterns, rules); } public boolean matches(Path file) { @@ -136,19 +231,11 @@ public boolean matches(Path file) { return inDirectory; } - public Optional extractVersion(Path file) { - for (var extractor : versionExtractors) { - Optional version; - if(extractor.extract(file).isPresent()) { - try { - version = Optional.of(Version.parse(extractor.extract(file).get())); - } - catch (VersionParsingException e) { - version = Optional.empty(); - } - if (version.isPresent()) { - return version; - } + public Optional extractVersion(Path file, Project project) { + for (VersionRule rule : versionRules) { + Optional result = rule.extract(file, project); + if (result.isPresent()) { + return result; } } return Optional.empty(); @@ -164,93 +251,112 @@ public static List fromJson(JsonObject json) { } // ---------- Version extraction ---------- - @FunctionalInterface - public interface VersionExtractor { - Optional extract(Path file); - } + private static VersionRule createVersionRule(CanonicalModule module, JsonObject ruleObj, Project project) { + String type = ruleObj.get("type").getAsString(); + String value = ruleObj.get("value").getAsString(); + + project.getLogger().info(module + ": Creating version rule: " + type + " - " + value); - private static VersionExtractor createVersionExtractor(CanonicalModule module, JsonObject rule, Project project) { - String type = rule.get("type").getAsString(); - String value = rule.get("value").getAsString(); + // Parse source array + List sourceStrategies = parseSourceArray(ruleObj, project); - project.getLogger().info(module + ": Version extractor for " + module); - + // Build the appropriate extractor + VersionExtractor extractor; switch (type) { case "derived": - project.getLogger().info("Creating " + module + ": derived - " + value); Pattern pattern = Pattern.compile(value); - return file -> { + extractor = file -> { project.getLogger().info("Executing " + module + ": derived - " + value); return pattern.matcher(file.getFileName().toString()).results() - .findFirst().map(m -> m.group(1)); + .findFirst().map(m -> m.group(1)); }; + break; case "literal": - project.getLogger().info("Creating " + module + ": literal - " + value); - return file -> { + extractor = file -> { project.getLogger().info("Executing " + module + ": literal - " + value); return Optional.of(value); }; + break; case "projectVar": - project.getLogger().info("Creating " + module + ": projectVar - " + value); - project.getExtensions().getExtraProperties().getProperties().forEach((key, val) -> { - project.getLogger().info("Creating [debug] " + module + ": EXTRA-PROPERTIES - KEY:" + key + " - VALUE: " + val); - }); - return file -> { - project.getLogger().info("Executing " + module + ": projectVar - " + value); - return Optional.ofNullable(project.getExtensions().getExtraProperties().get("gameVersion")) - .map(Object::toString); + String varName = value; + extractor = file -> { + project.getLogger().info("Executing " + module + ": projectVar - " + varName); + return Optional.ofNullable(project.findProperty(varName)) + .map(Object::toString); }; + break; case "method": project.getLogger().info("Creating " + module + ": method - " + value); - return file -> { + extractor = file -> { project.getLogger().info("Executing " + module + ": method - " + value); try { - String clazzName = rule.get("class").getAsString(); - + String clazzName = ruleObj.get("class").getAsString(); Class clazz = Class.forName(clazzName); Method m = clazz.getMethod(value); m.setAccessible(true); - m.invoke(null); - return Optional.of((String)m.invoke(null)); - } - catch(Exception | LinkageError e) { + return Optional.of((String) m.invoke(null)); + } catch (Exception | LinkageError e) { e.printStackTrace(); + return Optional.empty(); } - return Optional.empty(); }; + break; case "assert": project.getLogger().info("Creating " + module + ": assert - " + value); - return file -> { + extractor = file -> { project.getLogger().info("Executing " + module + ": assert - " + value); throw new AssertionError(value); }; + break; default: - throw new IllegalArgumentException("Unknown version rule: " + type); + throw new IllegalArgumentException("Unknown version rule type: " + type); } + return new VersionRule(extractor, sourceStrategies); } - + + private static List parseSourceArray(JsonObject ruleObj, Project project) { + if (!ruleObj.has("source")) { + // default: decompile with no exclusions + return List.of(new SourceStrategy("decompile", List.of(), null)); + } + JsonArray sourceArr = ruleObj.getAsJsonArray("source"); + List strategies = new ArrayList<>(); + for (JsonElement elem : sourceArr) { + JsonObject src = elem.getAsJsonObject(); + String sType = src.get("type").getAsString(); + List exclude = new ArrayList<>(); + if (src.has("exclude")) { + JsonArray exclArr = src.getAsJsonArray("exclude"); + for (JsonElement excl : exclArr) { + exclude.add(excl.getAsString()); + } + } + String path = null; + if (src.has("path")) { + path = src.get("path").getAsString(); + } + strategies.add(new SourceStrategy(sType, exclude, path)); + } + if (strategies.isEmpty()) { + // fallback + strategies.add(new SourceStrategy("decompile", List.of(), null)); + } + return strategies; + } + private String getTweenVersion(Path jarPath) throws Exception { AtomicReference version = new AtomicReference<>(); - try (JarFile jar = new JarFile(jarPath.toFile())) { JarEntry entry = jar.getJarEntry("aurelienribon/tweenengine/Tween.class"); - if (entry == null) { throw new IOException("Tween.class not found in jar: " + jarPath); } - try (InputStream in = jar.getInputStream(entry)) { ClassReader reader = new ClassReader(in); - reader.accept(new ClassVisitor(Opcodes.ASM9) { @Override - public MethodVisitor visitMethod( - int access, - String name, - String descriptor, - String signature, - String[] exceptions - ) { + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { if (name.equals("getVersion") && descriptor.equals("()Ljava/lang/String;")) { return new MethodVisitor(Opcodes.ASM9) { @Override @@ -266,7 +372,6 @@ public void visitLdcInsn(Object value) { }, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); } } - return version.get(); } } \ No newline at end of file diff --git a/src/main/java/com/wildermods/workspace/tasks/CopyLocalDependenciesToWorkspaceTask.java b/src/main/java/com/wildermods/workspace/tasks/CopyLocalDependenciesToWorkspaceTask.java index 9144c2c..6b8bf50 100644 --- a/src/main/java/com/wildermods/workspace/tasks/CopyLocalDependenciesToWorkspaceTask.java +++ b/src/main/java/com/wildermods/workspace/tasks/CopyLocalDependenciesToWorkspaceTask.java @@ -43,7 +43,6 @@ import com.wildermods.workspace.WilderWorkspaceExtension; import com.wildermods.workspace.WilderWorkspacePluginImpl; import com.wildermods.workspace.dependency.VaultedDependencySpec; -import com.wildermods.workspace.util.FileHelper; import com.wildermods.workspace.util.OS; import com.wildermods.workspace.util.Platform; @@ -229,13 +228,8 @@ private void copyFromLocalInstallation(Platform platform) throws Exception { Files.walkFileTree(installDir, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Path target; - if(FileHelper.shouldBeRemapped(file)) { - target = destDir.resolve("unmapped").resolve(installDir.relativize(file)); - } - else { - target = destDir.resolve(installDir.relativize(file)); - } + Path target = destDir.resolve(installDir.relativize(file)); + if(!overwrite && Files.exists(target)) { LOGGER.info("Not copying " + target + " - File at target location already exists."); return FileVisitResult.CONTINUE; diff --git a/src/main/java/com/wildermods/workspace/tasks/DecompileJarsTask.java b/src/main/java/com/wildermods/workspace/tasks/DecompileJarsTask.java index a7b05dc..1673422 100644 --- a/src/main/java/com/wildermods/workspace/tasks/DecompileJarsTask.java +++ b/src/main/java/com/wildermods/workspace/tasks/DecompileJarsTask.java @@ -2,50 +2,39 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Map; + import org.gradle.api.DefaultTask; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.TaskAction; import org.gradle.work.DisableCachingByDefault; +import com.wildermods.workspace.WilderWorkspacePluginImpl; +import com.wildermods.workspace.WilderWorkspacePluginImpl.ModuleInfo; import com.wildermods.workspace.decomp.DecompilerBuilder; import com.wildermods.workspace.decomp.GradleDecompilerBuilder; import com.wildermods.workspace.decomp.WildermythDecompilerSetup; @DisableCachingByDefault(because = "This task is a one time workspace setup task and should not be cached") public class DecompileJarsTask extends DefaultTask { - - @Input - private String compiledDir; @Input private String decompDir; @TaskAction public void decompile() throws IOException { - DecompilerBuilder b = new GradleDecompilerBuilder(this); - WildermythDecompilerSetup setup = new WildermythDecompilerSetup(b); - Path compiledDir = Path.of(this.compiledDir); - Path decompDir = Path.of(this.decompDir); - setup.decompile(compiledDir, decompDir); - - } - - /** - * Gets the directory containing the compiled JAR files. - * - * @return the compiled directory path as a string - */ - public String getCompiledDir() { - return compiledDir; - } + // Retrieve the module map from the project's extra properties + Object modulesObj = getProject().getExtensions().getExtraProperties().get(WilderWorkspacePluginImpl.DECOMP_MODULES); + if (!(modulesObj instanceof Map)) { + throw new IllegalStateException("No module map found for decompilation. Ensure setupCapabilities ran first."); + } + @SuppressWarnings("unchecked") + Map modules = (Map) modulesObj; - /** - * Sets the directory containing the compiled JAR files. - * - * @param compiledDir the compiled directory path as a string - */ - public void setCompiledDir(String compiledDir) { - this.compiledDir = compiledDir; + DecompilerBuilder b = new GradleDecompilerBuilder(this); + WildermythDecompilerSetup setup = new WildermythDecompilerSetup(b, getProject(), modules); + Path decompPath = Path.of(this.decompDir); + setup.decompile(decompPath); } /** diff --git a/src/main/java/com/wildermods/workspace/util/FileHelper.java b/src/main/java/com/wildermods/workspace/util/FileHelper.java index 373cc87..ae4bebf 100644 --- a/src/main/java/com/wildermods/workspace/util/FileHelper.java +++ b/src/main/java/com/wildermods/workspace/util/FileHelper.java @@ -1,8 +1,6 @@ package com.wildermods.workspace.util; import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; @@ -38,19 +36,4 @@ public static Path relativePath(Path parent, Path child) { return parent.relativize(child); } - public static boolean shouldBeRemapped(Path file) { - if(Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS)) { - switch(file.getFileName().toString()) { - case "devvotes-client.jar": - case "gameEngine-1.0.jar": - case "server-1.0.jar": - case "gdx-1.11.0.jar": - case "scratchpad.jar": - case "wildermyth.jar": - return true; - } - } - return false; - } - } diff --git a/src/main/resources/capabilities.json b/src/main/resources/capabilities.json index 75b2650..c4494de 100644 --- a/src/main/resources/capabilities.json +++ b/src/main/resources/capabilities.json @@ -356,11 +356,21 @@ "version": [ { "type": "derived", - "value": "^gdx-freetype-platform-?(.*?)\\.jar" + "value": "^gdx-freetype-platform-?(.*?)\\.jar", + "source": [ + { + "type":"skip" + } + ] }, { "type": "literal", - "value": "0.0.0+unknown" + "value": "0.0.0+unknown", + "source": [ + { + "type":"skip" + } + ] } ] } @@ -398,11 +408,21 @@ "version": [ { "type": "derived", - "value": "^gdx-platform-?(.*?)\\.jar" + "value": "^gdx-platform-?(.*?)\\.jar", + "source": [ + { + "type":"skip" + } + ] }, { "type": "literal", - "value": "0.0.0+unknown" + "value": "0.0.0+unknown", + "source": [ + { + "type":"skip" + } + ] } ] } @@ -765,16 +785,42 @@ "version": [ { "type": "derived", - "value": "^tween-engine-api-?(.*?)\\.jar" + "value": "^tween-engine-api-?(.*?)\\.jar", + "source": [ + { + "type": "decompile" + } + ] }, { "type": "method", "value": "getTweenVersion", - "class": "com.wildermods.workspace.dependency.CapabilityHandler" + "class": "com.wildermods.workspace.dependency.CapabilityHandler", + "source": [ + { + "type": "decompile", + "exclude": [ + "6.3.3" + ] + }, + { + "type": "file", + "path": "./tween-engine-api-sources.jar" + } + ] }, { "type": "literal", - "value": "6.3.3" + "value": "6.3.3", + "source": [ + { + "type": "file", + "path": "./tween-engine-api-sources.jar" + }, + { + "type": "skip" + } + ] } ] },