From a33142a5dbc99f365f77fd68bf4f7c197a625e5b Mon Sep 17 00:00:00 2001 From: Gamebuster19901 Date: Fri, 9 Aug 2024 21:21:38 -0400 Subject: [PATCH] Retrieve JarInJar dependencies --- build.gradle | 1 + gradle.properties | 2 +- .../workspace/PluginDependency.java | 1 + .../workspace/ProjectDependencyType.java | 2 + .../workspace/WWProjectDependency.java | 18 +- .../workspace/WilderWorkspacePlugin.java | 366 +------------ .../workspace/WilderWorkspacePluginImpl.java | 493 ++++++++++++++++++ 7 files changed, 513 insertions(+), 370 deletions(-) create mode 100644 src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java diff --git a/build.gradle b/build.gradle index 257122d..4568918 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ dependencies { implementation group: 'commons-io', name: 'commons-io', version: commonsIOVersion implementation group: 'net.fabricmc', name: 'fabric-loom', version: loomVersion implementation group: 'org.vineflower', name: 'vineflower', version: vineFlowerVersion + implementation group: 'com.google.code.gson', name: 'gson', version: gsonVersion } import org.apache.tools.ant.filters.ReplaceTokens diff --git a/gradle.properties b/gradle.properties index e3e9290..73bc51d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,6 @@ accessWidenerVersion = 2.1.0 tinyMappingsVersion = 0.3.0+ mixinVersion = 0.15.0+mixin.0.8.7 guavaVersion = 33.2.1-jre -gsonVersion = 2.11.0 providerVersion = 1.4.0.0 asmVersion = 9.7 fabricLoaderVersion = 0.16.0 @@ -15,5 +14,6 @@ fabricLoaderVersion = 0.16.0 commonsIOVersion = 2.16.1 commonsLangVersion = 3.14.0 commonsTextVersion = 1.12.0 +gsonVersion = 2.10.1 vineFlowerVersion = 1.10.1 loomVersion = 1.7.2 \ No newline at end of file diff --git a/src/main/java/com/wildermods/workspace/PluginDependency.java b/src/main/java/com/wildermods/workspace/PluginDependency.java index 6b67d06..d6620d1 100644 --- a/src/main/java/com/wildermods/workspace/PluginDependency.java +++ b/src/main/java/com/wildermods/workspace/PluginDependency.java @@ -14,6 +14,7 @@ enum PluginDependency implements Dependency { COMMONS_IO("commons-io", "commons-io", "@commonsIOVersion@"), COMMONS_LANG("org.apache.commons", "commons-lang3", "@commonsLangVersion@"), COMMONS_TEXT("org.apache.commons", "commons-text", "@commonsTextVersion@"), + GSON("com.google.code.gson", "gson", "@gsonVersion@"), LOOM("net.fabricmc", "fabric-loom", "@loomVersion@"), VINEFLOWER("org.vineflower", "vineflower", "@vineFlowerVersion@"); diff --git a/src/main/java/com/wildermods/workspace/ProjectDependencyType.java b/src/main/java/com/wildermods/workspace/ProjectDependencyType.java index 7b16e98..7ec8ac6 100644 --- a/src/main/java/com/wildermods/workspace/ProjectDependencyType.java +++ b/src/main/java/com/wildermods/workspace/ProjectDependencyType.java @@ -45,4 +45,6 @@ public enum ProjectDependencyType { * project's dependencies to the workspace */ excludedFabricDeps, + + retrieveJson } diff --git a/src/main/java/com/wildermods/workspace/WWProjectDependency.java b/src/main/java/com/wildermods/workspace/WWProjectDependency.java index 7eae777..5054f63 100644 --- a/src/main/java/com/wildermods/workspace/WWProjectDependency.java +++ b/src/main/java/com/wildermods/workspace/WWProjectDependency.java @@ -32,23 +32,11 @@ *

*/ public enum WWProjectDependency implements Dependency { - - commonsText(fabricDep, "org.apache.commons", "commons-text", "@commonsTextVersion@"), - accessWidener(fabricDep, "net.fabricmc", "access-widener", "@accessWidenerVersion@"), - tinyMappingsParser(fabricDep, "net.fabricmc", "tiny-mappings-parser", "@tinyMappingsVersion@"), - mixin(fabricDep, "net.fabricmc", "sponge-mixin", "@mixinVersion@"), - guava(fabricDep, "com.google.guava", "guava", "@guavaVersion@"), - gson(fabricDep, "com.google.code.gson", "gson", "@gsonVersion@"), - - asm(fabricDep, "org.ow2.asm", "asm", "@asmVersion@"), - asmAnalysis(fabricDep, "org.ow2.asm", "asm-analysis", "@asmVersion@"), - asmCommons(fabricDep, "org.ow2.asm", "asm-commons", "@asmVersion@"), - asmTree(fabricDep, "org.ow2.asm", "asm-tree", "@asmVersion@"), - asmUtil(fabricDep, "org.ow2.asm", "asm-util", "@asmVersion@"), - - + fabricLoader(fabricImpl, "net.fabricmc", "fabric-loader", "@fabricLoaderVersion@"), + fabricLoaderDepsJson(retrieveJson, "net.fabricmc", "fabric-loader", "@fabricLoaderVersion@"), gameProvider(fabricImpl, "com.wildermods", "provider", "@providerVersion@", "https://wildermods.com/WildermythGameProvider.git"), + ; private final ProjectDependencyType type; diff --git a/src/main/java/com/wildermods/workspace/WilderWorkspacePlugin.java b/src/main/java/com/wildermods/workspace/WilderWorkspacePlugin.java index ccb9c78..472f642 100644 --- a/src/main/java/com/wildermods/workspace/WilderWorkspacePlugin.java +++ b/src/main/java/com/wildermods/workspace/WilderWorkspacePlugin.java @@ -1,368 +1,26 @@ package com.wildermods.workspace; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.dsl.DependencyHandler; -import org.gradle.api.artifacts.repositories.MavenArtifactRepository; -import org.gradle.api.initialization.Settings; -import org.gradle.api.initialization.dsl.ScriptHandler; -import org.gradle.api.logging.LogLevel; -import org.gradle.api.tasks.Copy; -import org.gradle.internal.classloader.VisitableURLClassLoader; -import org.gradle.plugins.ide.eclipse.EclipsePlugin; -import org.gradle.plugins.ide.eclipse.model.Classpath; -import org.gradle.plugins.ide.eclipse.model.ClasspathEntry; -import org.gradle.plugins.ide.eclipse.model.EclipseClasspath; -import org.gradle.plugins.ide.eclipse.model.EclipseModel; -import org.gradle.plugins.ide.eclipse.model.FileReference; -import org.gradle.plugins.ide.eclipse.model.Library; -import org.gradle.plugins.ide.idea.IdeaPlugin; -import org.gradle.vcs.SourceControl; - -import com.wildermods.workspace.tasks.ClearLocalRuntimeTask; -import com.wildermods.workspace.tasks.CopyLocalDependenciesToWorkspaceTask; -import com.wildermods.workspace.tasks.DecompileJarsTask; -import com.wildermods.workspace.util.ExceptionUtil; - -import java.io.File; -import java.net.MalformedURLException; -import java.nio.file.Path; -import java.util.Set; - import org.gradle.api.Plugin; /** * A Gradle plugin for setting up and managing the WilderWorkspace development environment. - *

- * This plugin configures various tasks and settings required for working with the WilderWorkspace project, - * including setting up dependencies, configuring tasks, and integrating with IDEs like Eclipse and IntelliJ IDEA. - *

- *

- * It supports: - *

- *

+ * + * This is a dummy class. The actual implementation is in {@link WilderWorkspacePluginImpl} + * + * This workaround is needed because gradle cannot directly launch and decorate a plugin + * that directly classloads dependencies (I.E. Gson, apache commons, etc) + * + * Basically, if any external dependency required by the plugin is referenced by the + * class gradle directly launches, a NoClassDefFoundError occurs. + * */ public class WilderWorkspacePlugin implements Plugin { - - /** The version of the WilderWorkspace plugin. */ - public static final String VERSION = "@workspaceVersion@"; - - /** - * Applies the plugin to either a {@link Project} or {@link Settings}. - * - * @param object the object to apply the plugin to - * @throws Error if the object is neither a {@link Project} nor a {@link Settings} - */ - public void apply(Object object) { - if(object instanceof Project) { - apply((Project)object); - } - else if (object instanceof Settings) { - apply((Settings)object); - } - else { - throw new Error("WilderWorkspacePlugin can only be applied to projects and settings"); - } - } - - /** - * Applies the plugin to the project. - *

- * This method initializes the plugin, sets up dependencies, creates the WilderWorkspace extension, - * configures tasks, and handles post-evaluation tasks. - *

- * - * @param project the Gradle project to apply the plugin to - */ - public void apply(Project project) { - - project.getLogger().log(LogLevel.INFO, "Initializing WilderWorkspace PROJECT plugin version " + VERSION); - try { - addPluginDependencies(project); - - WilderWorkspaceExtension extension = project.getExtensions().create("wilderWorkspace", WilderWorkspaceExtension.class); - extension.loadUserConfig(); - - WWProjectContext context = new WWProjectContext(project, extension) {}; - - setupConfigurations(context); - - setupTasks(context); - - setupPostEvaluations(context); - } - catch(Throwable t) { - Throwable cause = ExceptionUtil.getInitialCause(t); - if(cause instanceof NoClassDefFoundError) { - throw new LinkageError("Required class not in classpath.", t); - } - throw new LinkageError("Failed to initialize WilderWorkspace " + VERSION, t); - } - - project.getLogger().log(LogLevel.INFO, "Initialized WilderWorkspace plugin version " + VERSION); - } - - /** - * Applies the plugin to the given settings. - *

- * This method sets up source control repositories for project dependencies as defined in {@link WWProjectDependency}. - *

- * - * @param settings the Gradle settings to apply the plugin to - */ - public void apply(Settings settings) { - SourceControl sourceControl = settings.getSourceControl(); - for(WWProjectDependency dependency : WWProjectDependency.values()) { - if(dependency.getRepo() != null) { - sourceControl.gitRepository(dependency.getRepo(), repo -> { - repo.producesModule(dependency.getModule()); - }); - } - } - } - - /** - * Adds plugin dependencies to the project's build script classpath. - *

- * This method configures repositories and adds dependencies required for the plugin. - *

- * - * @param project the Gradle project - */ - private static void addPluginDependencies(Project project) { - ScriptHandler buildscript = project.getBuildscript(); - { - - MavenArtifactRepository fabricRepository = buildscript.getRepositories().maven((c) -> { - c.setUrl("https://maven.fabricmc.net"); - }); - MavenArtifactRepository mavenCentral = buildscript.getRepositories().mavenCentral(); - MavenArtifactRepository mavenLocal = buildscript.getRepositories().mavenLocal(); - - DependencyHandler dependencies = buildscript.getDependencies(); - for(PluginDependency dependency : PluginDependency.values()) { - dependencies.add(ScriptHandler.CLASSPATH_CONFIGURATION, dependency.toString()); - } - } - - buildscript.getConfigurations().forEach((config -> { - Set dependencies = config.resolve(); - VisitableURLClassLoader classLoader = (VisitableURLClassLoader) WilderWorkspacePlugin.class.getClassLoader(); - for(File dependency : dependencies) { - try { - classLoader.addURL(dependency.toURI().toURL()); - } catch (MalformedURLException e) { - throw new LinkageError("Could not resolve dependency", e); - } - } - })); - } - - /** - * Sets up configurations required by the plugin. - *

- * This method creates configurations for Fabric dependencies and implementations. - *

- * - * @param context the project context - */ - private static void setupConfigurations(WWProjectContext context) { - Project project = context.getProject(); - Configuration fabricDep = project.getConfigurations().create(ProjectDependencyType.fabricDep.name()); - Configuration fabricImpl = project.getConfigurations().create(ProjectDependencyType.fabricImpl.name()); - - project.getConfigurations().create(ProjectDependencyType.resolvableImplementation.name(), configuration -> { - configuration.extendsFrom(project.getConfigurations().getByName("implementation")); - configuration.setCanBeResolved(true); - configuration.setCanBeConsumed(false); - }); - - project.getConfigurations().create(ProjectDependencyType.excludedFabricDeps.name(), configuration -> { - configuration.extendsFrom(project.getConfigurations().getByName(ProjectDependencyType.fabricDep.name())); - configuration.extendsFrom(project.getConfigurations().getByName(ProjectDependencyType.fabricImpl.name())); - configuration.setCanBeResolved(true); - configuration.setCanBeConsumed(false); - }); - - DependencyHandler dependencies = project.getDependencies(); - for(WWProjectDependency dependency : WWProjectDependency.values()) { - dependencies.add("implementation", dependency.toString()); - } + @Override + public void apply(Object target) { + new WilderWorkspacePluginImpl().apply((Object)target); } - - /** - * Sets up tasks for the plugin. - *

- * This method registers tasks for copying local dependencies, decompiling JARs, and configuring the workspace. - *

- * - * @param context the project context - */ - private static void setupTasks(WWProjectContext context) { - Project project = context.getProject(); - WilderWorkspaceExtension extension = context.getWWExtension(); - - project.getTasks().register("copyLocalDependenciesToWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> { - task.setPlatform(extension.getPlatform()); - task.setPatchline(extension.getPatchline()); - task.setDestDir(extension.getGameDestDir()); - task.finalizedBy(project.getTasks().getByName("copyFabricDependencies")); - task.finalizedBy(project.getTasks().getByName("copyFabricImplementors")); - task.finalizedBy(project.getTasks().getByName("copyProjectDependencies")); - }); - - project.getTasks().register("decompileJars", DecompileJarsTask.class, task -> { - task.setCompiledDir(extension.getGameDestDir()); - task.setDecompDir(extension.getDecompDir()); - }); - - project.getTasks().register("clearLocalRuntime", ClearLocalRuntimeTask.class, task -> { - task.setDecompDir(extension.getDecompDir()); - task.setDestDir(extension.getGameDestDir()); - }); - - project.getTasks().register("setupDecompWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> { - task.setOverwrite(false); - project.getPlugins().withType(EclipsePlugin.class, eclipsePlugin -> { - task.dependsOn(project.getTasks().named("eclipse")); - }); - project.getPlugins().withType(IdeaPlugin.class, ideaPlugin -> { - task.dependsOn(project.getTasks().named("idea")); - }); - task.finalizedBy(project.provider(() -> { - DecompileJarsTask decompileTask = (DecompileJarsTask)project.getTasks().named("decompileJars").get(); - return decompileTask; - })); - task.finalizedBy(project.getTasks().getByName("copyFabricDependencies")); - task.finalizedBy(project.getTasks().getByName("copyFabricImplementors")); - }); - - project.getTasks().register("updateDecompWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> { - task.setPlatform(extension.getPlatform()); - task.setPatchline(extension.getPatchline()); - task.setDestDir(extension.getGameDestDir()); - task.setOverwrite(true); - task.dependsOn(project.getTasks().named("clearLocalDependencies")); - task.finalizedBy(project.provider(() -> { - CopyLocalDependenciesToWorkspaceTask copyTask = (CopyLocalDependenciesToWorkspaceTask) project.getTasks().named("copyLocalDependenciesToWorkspace").get(); - copyTask.setOverwrite(true); - copyTask.finalizedBy(project.provider(() -> { - DecompileJarsTask decompileTask = (DecompileJarsTask)project.getTasks().named("decompileJars").get(); - return decompileTask; - })); - return copyTask; - })); - }); - - project.getTasks().register("copyFabricDependencies", Copy.class, task -> { - Configuration fabricDep = context.getProject().getConfigurations().getByName(ProjectDependencyType.fabricDep.name()); - task.from(fabricDep); - task.into(Path.of(extension.getGameDestDir()).resolve("fabric")); - }); - - project.getTasks().register("copyFabricImplementors", Copy.class, task -> { - Configuration fabricImpl = context.getProject().getConfigurations().getByName(ProjectDependencyType.fabricImpl.name()); - task.from(fabricImpl); - task.into(extension.getGameDestDir()); - }); - - project.getTasks().register("copyProjectDependencies", Copy.class, task -> { - Configuration resolvableImplementation = project.getConfigurations().getByName(ProjectDependencyType.resolvableImplementation.name()); - Configuration exclusionConfiguration = project.getConfigurations().getByName(ProjectDependencyType.excludedFabricDeps.name()); - - // Get the files from the exclusion configuration - Set exclusionFiles = exclusionConfiguration.getResolvedConfiguration().getFiles(); - - task.from(resolvableImplementation, copySpec -> { - // Exclude files that are in the exclusion configuration - copySpec.exclude(fileTreeElement -> exclusionFiles.contains(fileTreeElement.getFile())); - }); - - task.into(Path.of(extension.getGameDestDir()).resolve("fabric")); - }); - } - - /** - * Sets up post-evaluation tasks for the plugin. - *

- * This method adds workspace dependencies and configures IDE plugins. - *

- * - * @param context the project context - */ - private static void setupPostEvaluations(WWProjectContext context) { - addWorkspaceDependencies(context); - setupEclipsePlugin(context); - } - - /** - * Adds workspace dependencies to the project after evaluation. - *

- * This method ensures that project dependencies are added to the correct configurations. - *

- * - * @param context the project context - */ - private static void addWorkspaceDependencies(WWProjectContext context) { - Project project = context.getProject(); - project.afterEvaluate((proj -> { - DependencyHandler dependencyHandler = proj.getDependencies(); - - - - for(WWProjectDependency dependency : WWProjectDependency.values()) { - dependencyHandler.add(dependency.getType().toString(), dependency.toString()); - } - })); - } - - /** - * Configures the Eclipse plugin after project evaluation. - *

- * This method integrates with the Eclipse plugin to set source paths for game JARs. - *

- * - * @param context the project context - */ - @SuppressWarnings({ "unchecked"}) - private static void setupEclipsePlugin(WWProjectContext context) { - Project project = context.getProject(); - WilderWorkspaceExtension extension = context.getWWExtension(); - project.afterEvaluate(proj -> { - if (project.getPlugins().hasPlugin("eclipse")) { - EclipseModel eclipseModel = proj.getExtensions().getByType(EclipseModel.class); - EclipseClasspath classpath = eclipseModel.getClasspath(); - - classpath.file(xmlFileContent -> { - xmlFileContent.getWhenMerged().add((classPathMerged) -> { - Classpath c = (Classpath) classPathMerged; - for (ClasspathEntry entry : c.getEntries()) { - project.getLogger().warn(entry.getClass().getCanonicalName()); - if(entry instanceof Library) { - Library lib = (Library) entry; - 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()); - } - } - } - }); - }); - } else { - project.getLogger().warn("Eclipse plugin is not applied. The eclipse source attachment will not be configured."); - } - }); - } } diff --git a/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java b/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java new file mode 100644 index 0000000..7ddfddd --- /dev/null +++ b/src/main/java/com/wildermods/workspace/WilderWorkspacePluginImpl.java @@ -0,0 +1,493 @@ +package com.wildermods.workspace; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.dsl.DependencyHandler; +import org.gradle.api.artifacts.dsl.RepositoryHandler; +import org.gradle.api.artifacts.repositories.MavenArtifactRepository; +import org.gradle.api.initialization.Settings; +import org.gradle.api.initialization.dsl.ScriptHandler; +import org.gradle.api.logging.LogLevel; +import org.gradle.api.tasks.Copy; +import org.gradle.internal.classloader.VisitableURLClassLoader; +import org.gradle.plugins.ide.eclipse.EclipsePlugin; +import org.gradle.plugins.ide.eclipse.model.Classpath; +import org.gradle.plugins.ide.eclipse.model.ClasspathEntry; +import org.gradle.plugins.ide.eclipse.model.EclipseClasspath; +import org.gradle.plugins.ide.eclipse.model.EclipseModel; +import org.gradle.plugins.ide.eclipse.model.FileReference; +import org.gradle.plugins.ide.eclipse.model.Library; +import org.gradle.plugins.ide.idea.IdeaPlugin; +import org.gradle.vcs.SourceControl; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.wildermods.workspace.tasks.ClearLocalRuntimeTask; +import com.wildermods.workspace.tasks.CopyLocalDependenciesToWorkspaceTask; +import com.wildermods.workspace.tasks.DecompileJarsTask; +import com.wildermods.workspace.util.ExceptionUtil; + +import java.io.File; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import javax.net.ssl.HttpsURLConnection; + +import org.gradle.api.Plugin; + +/** + * A Gradle plugin for setting up and managing the WilderWorkspace development environment. + *

+ * This plugin configures various tasks and settings required for working with the WilderWorkspace project, + * including setting up dependencies, configuring tasks, and integrating with IDEs like Eclipse and IntelliJ IDEA. + *

+ *

+ * It supports: + *

    + *
  • Adding plugin dependencies
  • + *
  • Setting up configurations and tasks
  • + *
  • Configuring IDE plugins (Eclipse and IntelliJ IDEA)
  • + *
  • Handling post-evaluation of the project for dependency management
  • + *
+ *

+ */ + +public class WilderWorkspacePluginImpl implements Plugin { + + /** The version of the WilderWorkspace plugin. */ + public static final String VERSION = "@workspaceVersion@"; + + private Configuration implementation; + private Configuration fabricDep; + private Configuration fabricImpl; + private Configuration retrieveJson; + private Configuration resolvableImplementation; + private Configuration excludedFabricDeps; + + /** + * Applies the plugin to either a {@link Project} or {@link Settings}. + * + * @param object the object to apply the plugin to + * @throws Error if the object is neither a {@link Project} nor a {@link Settings} + */ + public void apply(Object object) { + if(object instanceof Project) { + apply((Project)object); + } + else if (object instanceof Settings) { + apply((Settings)object); + } + else { + throw new Error("WilderWorkspacePlugin can only be applied to projects and settings"); + } + } + + /** + * Applies the plugin to the project. + *

+ * This method initializes the plugin, sets up dependencies, creates the WilderWorkspace extension, + * configures tasks, and handles post-evaluation tasks. + *

+ * + * @param project the Gradle project to apply the plugin to + */ + public void apply(Project project) { + + project.getLogger().log(LogLevel.INFO, "Initializing WilderWorkspace PROJECT plugin version " + VERSION); + try { + + addPluginDependencies(project); + + WilderWorkspaceExtension extension = project.getExtensions().create("wilderWorkspace", WilderWorkspaceExtension.class); + extension.loadUserConfig(); + + WWProjectContext context = new WWProjectContext(project, extension) {}; + + setupRepositories(context); + + project.getLogger().log(LogLevel.INFO, "SETTING UP CONFIGURATIONS "); + setupConfigurations(context); + + setupTasks(context); + + setupPostEvaluations(context); + } + catch(Throwable t) { + Throwable cause = ExceptionUtil.getInitialCause(t); + if(cause instanceof NoClassDefFoundError) { + throw new LinkageError("Required class not in classpath.", t); + } + throw new LinkageError("Failed to apply WilderWorkspace " + VERSION, t); + } + } + + /** + * Applies the plugin to the given settings. + *

+ * This method sets up source control repositories for project dependencies as defined in {@link WWProjectDependency}. + *

+ * + * @param settings the Gradle settings to apply the plugin to + */ + public void apply(Settings settings) { + SourceControl sourceControl = settings.getSourceControl(); + for(WWProjectDependency dependency : WWProjectDependency.values()) { + if(dependency.getRepo() != null) { + sourceControl.gitRepository(dependency.getRepo(), repo -> { + repo.producesModule(dependency.getModule()); + }); + } + } + } + + /** + * Adds plugin dependencies to the project's build script classpath. + *

+ * This method configures repositories and adds dependencies required for the plugin. + *

+ * + * @param project the Gradle project + */ + private void addPluginDependencies(Project project) { + ScriptHandler buildscript = project.getBuildscript(); + { + + MavenArtifactRepository fabricRepository = buildscript.getRepositories().maven((c) -> { + c.setUrl("https://maven.fabricmc.net"); + }); + MavenArtifactRepository mavenCentral = buildscript.getRepositories().mavenCentral(); + MavenArtifactRepository mavenLocal = buildscript.getRepositories().mavenLocal(); + + DependencyHandler dependencies = buildscript.getDependencies(); + for(PluginDependency dependency : PluginDependency.values()) { + dependencies.add(ScriptHandler.CLASSPATH_CONFIGURATION, dependency.toString()); + } + } + + buildscript.getConfigurations().forEach((config -> { + Set dependencies = config.resolve(); + VisitableURLClassLoader classLoader = (VisitableURLClassLoader) WilderWorkspacePlugin.class.getClassLoader(); + for(File dependency : dependencies) { + try { + classLoader.addURL(dependency.toURI().toURL()); + } catch (MalformedURLException e) { + throw new LinkageError("Could not resolve dependency", e); + } + } + })); + } + + private void setupRepositories(WWProjectContext context) { + Project project = context.getProject(); + RepositoryHandler repositories = project.getRepositories(); + repositories.add(repositories.mavenCentral()); + repositories.add(repositories.maven((repo) -> { + repo.setUrl("https://maven.fabricmc.net/"); + })); + } + + /** + * Sets up configurations required by the plugin. + *

+ * This method creates configurations for Fabric dependencies and implementations. + *

+ * + * @param context the project context + */ + private void setupConfigurations(WWProjectContext context) { + Project project = context.getProject(); + implementation = project.getConfigurations().getByName(ProjectDependencyType.implementation.name()); + fabricDep = project.getConfigurations().create(ProjectDependencyType.fabricDep.name()); + fabricImpl = project.getConfigurations().create(ProjectDependencyType.fabricImpl.name()); + retrieveJson = project.getConfigurations().create(ProjectDependencyType.retrieveJson.name(), configuration -> { + configuration.setCanBeResolved(true); + configuration.setCanBeConsumed(false); + }); + + resolvableImplementation = project.getConfigurations().create(ProjectDependencyType.resolvableImplementation.name(), configuration -> { + configuration.extendsFrom(implementation); + configuration.setCanBeResolved(true); + configuration.setCanBeConsumed(false); + }); + + excludedFabricDeps = project.getConfigurations().create(ProjectDependencyType.excludedFabricDeps.name(), configuration -> { + configuration.extendsFrom(project.getConfigurations().getByName(ProjectDependencyType.fabricDep.name())); + configuration.extendsFrom(project.getConfigurations().getByName(ProjectDependencyType.fabricImpl.name())); + configuration.setCanBeResolved(true); + configuration.setCanBeConsumed(false); + }); + + DependencyHandler dependencies = project.getDependencies(); + for(WWProjectDependency dependency : WWProjectDependency.values()) { + project.getLogger().log(LogLevel.INFO, "ADDING " + dependency.getModule() + ":" + dependency.getVersion() + " TO " + dependency.getType() + " CONFIGURATION"); + dependencies.add(dependency.getType().name(), dependency.toString()); + project.getLogger().log(LogLevel.INFO, "ADDING " + dependency.getModule() + ":" + dependency.getVersion() + " TO " + implementation + " CONFIGURATION"); + dependencies.add(implementation.getName(), dependency.toString()); + } + + project.getLogger().log(LogLevel.INFO, "ADDING JSON DEPENDENCIES " + VERSION); + addJsonDependencies(context); + + } + + private void addJsonDependencies(WWProjectContext context) { + Project project = context.getProject(); + RepositoryHandler repositories = project.getRepositories(); + Set addedRepositories = new HashSet<>(); + + project.getLogger().log(LogLevel.INFO, "Searching for json dependencies..."); + + // Iterate over dependencies in retrieveJson configuration + for (Dependency jsonDependency : retrieveJson.getAllDependencies()) { + project.getLogger().info("Found json dependency: " + jsonDependency); + // Construct the JSON file URL + String jsonUrlPath = jsonDependency.getGroup().replace('.', '/') + "/" + + jsonDependency.getName() + "/" + jsonDependency.getVersion() + "/" + + jsonDependency.getName() + "-" + jsonDependency.getVersion() + ".json"; + + Optional repositoryOpt = repositories.withType(MavenArtifactRepository.class).stream() + .filter(repo -> canResolveUrl(context, repo.getUrl().toString() + jsonUrlPath)) + .findFirst(); + + if (!repositoryOpt.isPresent()) { + throw new RuntimeException("Unable to determine the repository URL for the JSON dependency: " + + jsonDependency.getGroup() + ":" + jsonDependency.getName() + ":" + jsonDependency.getVersion()); + } + + // Get the repository URL + MavenArtifactRepository repository = repositoryOpt.get(); + String jsonUrl = repository.getUrl().toString() + jsonUrlPath; + + try { + // Download and parse the JSON file + InputStreamReader reader = new InputStreamReader(new URL(jsonUrl).openStream()); + JsonObject dependencyJsonData = JsonParser.parseReader(reader).getAsJsonObject(); + + JsonObject libraries = dependencyJsonData.getAsJsonObject("libraries"); + libraries.entrySet().forEach((entry) -> { + if(entry.getValue().isJsonArray()) { + JsonArray dependencyGroup = entry.getValue().getAsJsonArray(); + addDependenciesToFabricDep(project, jsonDependency, dependencyGroup); + } + }); + + } catch (Exception e) { + throw new RuntimeException("Failed to process JSON dependency: " + jsonUrl, e); + } + } + + project.getLogger().log(LogLevel.INFO, "Found all json dependnecies"); + } + + private void addDependenciesToFabricDep(Project project, Dependency superDependency, JsonArray libraries) { + DependencyHandler dependencies = project.getDependencies(); + for (JsonElement element : libraries) { + JsonObject lib = element.getAsJsonObject(); + String dependencyNotation = lib.get("name").getAsString(); + + // Add dependency to fabricDep and implementation configuration + try { + dependencies.add(fabricDep.getName(), dependencyNotation); + dependencies.add(implementation.getName(), dependencyNotation); + project.getLogger().log(LogLevel.INFO, "[" + superDependency.getGroup() + ":" + superDependency.getName() + "]: Registered subdependency " + dependencyNotation); + + } + catch(Exception e) { + throw new RuntimeException("[" + superDependency.getGroup() + ":" + superDependency.getName() + "]: Could not register dependency " + dependencyNotation, e); + } + } + } + + /** + * Sets up tasks for the plugin. + *

+ * This method registers tasks for copying local dependencies, decompiling JARs, and configuring the workspace. + *

+ * + * @param context the project context + */ + private void setupTasks(WWProjectContext context) { + Project project = context.getProject(); + WilderWorkspaceExtension extension = context.getWWExtension(); + + project.getTasks().register("copyLocalDependenciesToWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> { + task.setPlatform(extension.getPlatform()); + task.setPatchline(extension.getPatchline()); + task.setDestDir(extension.getGameDestDir()); + task.finalizedBy(project.getTasks().getByName("copyFabricDependencies")); + task.finalizedBy(project.getTasks().getByName("copyFabricImplementors")); + task.finalizedBy(project.getTasks().getByName("copyProjectDependencies")); + }); + + project.getTasks().register("decompileJars", DecompileJarsTask.class, task -> { + task.setCompiledDir(extension.getGameDestDir()); + task.setDecompDir(extension.getDecompDir()); + }); + + project.getTasks().register("clearLocalRuntime", ClearLocalRuntimeTask.class, task -> { + task.setDecompDir(extension.getDecompDir()); + task.setDestDir(extension.getGameDestDir()); + }); + + project.getTasks().register("setupDecompWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> { + task.setOverwrite(false); + task.finalizedBy(project.provider(() -> { + DecompileJarsTask decompileTask = (DecompileJarsTask)project.getTasks().named("decompileJars").get(); + return decompileTask; + })); + task.finalizedBy(project.getTasks().getByName("copyLocalDependenciesToWorkspace")); + project.getPlugins().withType(EclipsePlugin.class, eclipsePlugin -> { + task.finalizedBy(project.getTasks().named("eclipse")); + }); + project.getPlugins().withType(IdeaPlugin.class, ideaPlugin -> { + task.finalizedBy(project.getTasks().named("idea")); + }); + }); + + //This task isn't implemented correctly yet, so we're not including it. + + /** + project.getTasks().register("updateDecompWorkspace", CopyLocalDependenciesToWorkspaceTask.class, task -> { + task.setPlatform(extension.getPlatform()); + task.setPatchline(extension.getPatchline()); + task.setDestDir(extension.getGameDestDir()); + task.setOverwrite(true); + task.dependsOn(project.getTasks().named("clearLocalDependencies")); + task.finalizedBy(project.provider(() -> { + CopyLocalDependenciesToWorkspaceTask copyTask = (CopyLocalDependenciesToWorkspaceTask) project.getTasks().named("copyLocalDependenciesToWorkspace").get(); + copyTask.setOverwrite(true); + copyTask.finalizedBy(project.provider(() -> { + DecompileJarsTask decompileTask = (DecompileJarsTask)project.getTasks().named("decompileJars").get(); + return decompileTask; + })); + return copyTask; + })); + }); + */ + + project.getTasks().register("copyFabricDependencies", Copy.class, task -> { + task.from(fabricDep); + task.from(retrieveJson); + task.into(Path.of(extension.getGameDestDir()).resolve("fabric")); + }); + + project.getTasks().register("copyFabricImplementors", Copy.class, task -> { + task.from(fabricImpl); + task.into(extension.getGameDestDir()); + }); + + project.getTasks().register("copyProjectDependencies", Copy.class, task -> { + task.doFirst(t -> { + // Get the files from the exclusion configuration + Set exclusionFiles = excludedFabricDeps.getResolvedConfiguration().getFiles(); + + task.from(resolvableImplementation, copySpec -> { + // Exclude files that are in the exclusion configuration + copySpec.exclude(fileTreeElement -> exclusionFiles.contains(fileTreeElement.getFile())); + }); + + task.into(Path.of(extension.getGameDestDir()).resolve("fabric")); + }); + }); + + } + + /** + * Sets up post-evaluation tasks for the plugin. + *

+ * This method adds workspace dependencies and configures IDE plugins. + *

+ * + * @param context the project context + */ + private void setupPostEvaluations(WWProjectContext context) { + addWorkspaceDependencies(context); + setupEclipsePlugin(context); + } + + /** + * Adds workspace dependencies to the project after evaluation. + *

+ * This method ensures that project dependencies are added to the correct configurations. + *

+ * + * @param context the project context + */ + private void addWorkspaceDependencies(WWProjectContext context) { + Project project = context.getProject(); + project.afterEvaluate((proj -> { + DependencyHandler dependencyHandler = proj.getDependencies(); + + + + for(WWProjectDependency dependency : WWProjectDependency.values()) { + dependencyHandler.add(dependency.getType().toString(), dependency.toString()); + } + })); + } + + /** + * Configures the Eclipse plugin after project evaluation. + *

+ * This method integrates with the Eclipse plugin to set source paths for game JARs. + *

+ * + * @param context the project context + */ + @SuppressWarnings({ "unchecked"}) + private void setupEclipsePlugin(WWProjectContext context) { + Project project = context.getProject(); + WilderWorkspaceExtension extension = context.getWWExtension(); + project.afterEvaluate(proj -> { + if (project.getPlugins().hasPlugin("eclipse")) { + EclipseModel eclipseModel = proj.getExtensions().getByType(EclipseModel.class); + EclipseClasspath classpath = eclipseModel.getClasspath(); + + classpath.file(xmlFileContent -> { + xmlFileContent.getWhenMerged().add((classPathMerged) -> { + Classpath c = (Classpath) classPathMerged; + for (ClasspathEntry entry : c.getEntries()) { + project.getLogger().warn(entry.getClass().getCanonicalName()); + if(entry instanceof Library) { + Library lib = (Library) entry; + 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()); + } + } + } + }); + }); + + } else { + project.getLogger().warn("Eclipse plugin is not applied. The eclipse source attachment will not be configured."); + } + }); + } + + private boolean canResolveUrl(WWProjectContext context, String url) { + try { + context.getProject().getLogger().log(LogLevel.INFO, "Checking " + url); + HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); + connection.setRequestMethod("HEAD"); + int responseCode = connection.getResponseCode(); + context.getProject().getLogger().log(LogLevel.INFO, "Got response " + responseCode); + return responseCode == HttpsURLConnection.HTTP_OK; + } catch (Exception e) { + context.getProject().getLogger().log(LogLevel.INFO, "Could not open " + url, e); + return false; + } + } + +}