/* * A Gradle plugin for the creation of Minecraft mods and MinecraftForge plugins. * Copyright (C) 2013 Minecraft Forge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ package net.minecraftforge.gradle.common; import static net.minecraftforge.gradle.common.Constants.*; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.HttpURLConnection; import java.net.URL; import java.util.Arrays; import java.util.List; import java.util.Map; import net.minecraftforge.gradle.util.json.version.ManifestVersion; import org.gradle.api.Action; import org.gradle.api.DefaultTask; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration.State; import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.api.artifacts.repositories.FlatDirectoryArtifactRepository; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.logging.Logger; import org.gradle.api.plugins.ExtraPropertiesExtension; import org.gradle.api.tasks.Delete; import org.gradle.testfixtures.ProjectBuilder; import com.google.common.base.Charsets; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.base.Throwables; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.io.ByteStreams; import com.google.common.io.Files; import com.google.gson.reflect.TypeToken; import groovy.lang.Closure; import net.minecraftforge.gradle.tasks.CrowdinDownload; import net.minecraftforge.gradle.tasks.Download; import net.minecraftforge.gradle.tasks.DownloadAssetsTask; import net.minecraftforge.gradle.tasks.EtagDownloadTask; import net.minecraftforge.gradle.tasks.ExtractConfigTask; import net.minecraftforge.gradle.tasks.GenSrgs; import net.minecraftforge.gradle.tasks.JenkinsChangelog; import net.minecraftforge.gradle.tasks.MergeJars; import net.minecraftforge.gradle.tasks.ObtainFernFlowerTask; import net.minecraftforge.gradle.tasks.SignJar; import net.minecraftforge.gradle.tasks.SplitJarTask; import net.minecraftforge.gradle.util.FileLogListenner; import net.minecraftforge.gradle.util.GradleConfigurationException; import net.minecraftforge.gradle.util.delayed.DelayedFile; import net.minecraftforge.gradle.util.delayed.DelayedFileTree; import net.minecraftforge.gradle.util.delayed.DelayedString; import net.minecraftforge.gradle.util.delayed.ReplacementProvider; import net.minecraftforge.gradle.util.delayed.TokenReplacer; import net.minecraftforge.gradle.util.json.JsonFactory; import net.minecraftforge.gradle.util.json.fgversion.FGBuildStatus; import net.minecraftforge.gradle.util.json.fgversion.FGVersion; import net.minecraftforge.gradle.util.json.fgversion.FGVersionWrapper; import net.minecraftforge.gradle.util.json.version.Version; public abstract class BasePlugin<K extends BaseExtension> implements Plugin<Project> { public Project project; public BasePlugin<?> otherPlugin; public ReplacementProvider replacer = new ReplacementProvider(); private Map<String, ManifestVersion> mcManifest; private Version mcVersionJson; @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public final void apply(Project arg) { project = arg; // check for gradle version { List<String> split = Splitter.on('.').splitToList(project.getGradle().getGradleVersion()); int major = Integer.parseInt(split.get(0)); int minor = Integer.parseInt(split.get(1).split("-")[0]); if (major <= 1 || (major == 2 && minor < 3)) throw new RuntimeException("ForgeGradle 2.0 requires Gradle 2.3 or above."); } if (project.getBuildDir().getAbsolutePath().contains("!")) { project.getLogger().error("Build path has !, This will screw over a lot of java things as ! is used to denote archive paths, REMOVE IT if you want to continue"); throw new RuntimeException("Build path contains !"); } // set the obvious replacements replacer.putReplacement(REPLACE_CACHE_DIR, cacheFile("").getAbsolutePath()); replacer.putReplacement(REPLACE_BUILD_DIR, project.getBuildDir().getAbsolutePath()); // logging { File projectCacheDir = project.getGradle().getStartParameter().getProjectCacheDir(); if (projectCacheDir == null) projectCacheDir = new File(project.getProjectDir(), ".gradle"); replacer.putReplacement(REPLACE_PROJECT_CACHE_DIR, projectCacheDir.getAbsolutePath()); FileLogListenner listener = new FileLogListenner(new File(projectCacheDir, "gradle.log")); project.getLogging().addStandardOutputListener(listener); project.getLogging().addStandardErrorListener(listener); project.getGradle().addBuildListener(listener); } // extension objects { Type t = getClass().getGenericSuperclass(); while (t instanceof Class) { t = ((Class) t).getGenericSuperclass(); } project.getExtensions().create(EXT_NAME_MC, (Class<K>) ((ParameterizedType) t).getActualTypeArguments()[0], this); } // add buildscript usable tasks { ExtraPropertiesExtension ext = project.getExtensions().getExtraProperties(); ext.set("SignJar", SignJar.class); ext.set("Download", Download.class); ext.set("EtagDownload", EtagDownloadTask.class); ext.set("CrowdinDownload", CrowdinDownload.class); ext.set("JenkinsChangelog", JenkinsChangelog.class); } // repos project.allprojects(new Action<Project>() { public void execute(Project proj) { addMavenRepo(proj, "forge", URL_FORGE_MAVEN); proj.getRepositories().mavenCentral(); addMavenRepo(proj, "minecraft", URL_LIBRARY); } }); // do Mcp Snapshots Stuff getRemoteJsons(); project.getConfigurations().maybeCreate(CONFIG_MCP_DATA); project.getConfigurations().maybeCreate(CONFIG_MAPPINGS); // set other useful configs project.getConfigurations().maybeCreate(CONFIG_MC_DEPS); project.getConfigurations().maybeCreate(CONFIG_MC_DEPS_CLIENT); project.getConfigurations().maybeCreate(CONFIG_NATIVES); // should be assumed until specified otherwise project.getConfigurations().getByName(CONFIG_MC_DEPS).extendsFrom(project.getConfigurations().getByName(CONFIG_MC_DEPS_CLIENT)); // after eval project.afterEvaluate(new Action<Project>() { @Override public void execute(Project project) { // dont continue if its already failed! if (project.getState().getFailure() != null) return; afterEvaluate(); } }); // some default tasks makeCommonTasks(); // at last, apply the child plugins applyPlugin(); } public abstract void applyPlugin(); private static boolean displayBanner = true; private void getRemoteJsons() { // MCP json File jsonCache = cacheFile("McpMappings.json"); File etagFile = new File(jsonCache.getAbsolutePath() + ".etag"); getExtension().mcpJson = JsonFactory.GSON.fromJson(getWithEtag(URL_MCP_JSON, jsonCache, etagFile), new TypeToken<Map<String, Map<String, int[]>>>() {}.getType()); // MC manifest json jsonCache = cacheFile("McManifest.json"); etagFile = new File(jsonCache.getAbsolutePath() + ".etag"); mcManifest = JsonFactory.GSON.fromJson(getWithEtag(URL_MC_MANIFEST, jsonCache, etagFile), new TypeToken<Map<String, ManifestVersion>>() {}.getType()); } protected void afterEvaluate() { // validate MC version if (Strings.isNullOrEmpty(getExtension().getVersion())) { throw new GradleConfigurationException("You must set the Minecraft version!"); } // http://files.minecraftforge.net/maven/de/oceanlabs/mcp/mcp/1.7.10/mcp-1.7.10-srg.zip project.getDependencies().add(CONFIG_MAPPINGS, ImmutableMap.of( "group", "de.oceanlabs.mcp", "name", delayedString("mcp_" + REPLACE_MCP_CHANNEL).call(), "version", delayedString(REPLACE_MCP_VERSION + "-" + REPLACE_MC_VERSION).call(), "ext", "zip" )); project.getDependencies().add(CONFIG_MCP_DATA, ImmutableMap.of( "group", "de.oceanlabs.mcp", "name", "mcp", "version", delayedString(REPLACE_MC_VERSION).call(), "classifier", "srg", "ext", "zip" )); // Check FG Version, unless its disabled List<String> lines = Lists.newArrayListWithExpectedSize(5); Object disableUpdateCheck = project.getProperties().get("net.minecraftforge.gradle.disableUpdateChecker"); if (!"true".equals(disableUpdateCheck) && !"yes".equals(disableUpdateCheck) && !new Boolean(true).equals(disableUpdateCheck)) { doFGVersionCheck(lines); } if (!displayBanner) return; Logger logger = this.project.getLogger(); logger.lifecycle("#################################################"); logger.lifecycle(" ForgeGradle {} ", this.getVersionString()); logger.lifecycle(" https://github.com/MinecraftForge/ForgeGradle "); logger.lifecycle("#################################################"); logger.lifecycle(" Powered by MCP {} ", this.getExtension().getMcpVersion()); logger.lifecycle(" http://modcoderpack.com "); logger.lifecycle(" by: Searge, ProfMobius, Fesh0r, "); logger.lifecycle(" R4wk, ZeuX, IngisKahn, bspkrs "); logger.lifecycle("#################################################"); for (String str : lines) logger.lifecycle(str); displayBanner = false; } private String getVersionString() { String version = this.getClass().getPackage().getImplementationVersion(); if (Strings.isNullOrEmpty(version)) { version = this.getExtension().forgeGradleVersion + "-unknown"; } return version; } protected void doFGVersionCheck(List<String> outLines) { String version = getExtension().forgeGradleVersion; if (version.endsWith("-SNAPSHOT")) { // no version checking necessary if the are on the snapshot already return; } final String checkUrl = "https://www.abrarsyed.com/ForgeGradleVersion.json"; final File jsonCache = cacheFile("ForgeGradleVersion.json"); final File etagFile = new File(jsonCache.getAbsolutePath() + ".etag"); FGVersionWrapper wrapper = JsonFactory.GSON.fromJson(getWithEtag(checkUrl, jsonCache, etagFile), FGVersionWrapper.class); FGVersion webVersion = wrapper.versionObjects.get(version); String latestVersion = wrapper.versions.get(wrapper.versions.size()-1); if (webVersion == null || webVersion.status == FGBuildStatus.FINE) { return; } // broken implies outdated if (webVersion.status == FGBuildStatus.BROKEN) { outLines.add("ForgeGradle "+webVersion.version+" HAS " + (webVersion.bugs.length > 1 ? "SERIOUS BUGS" : "a SERIOUS BUG") + "!"); outLines.add("UPDATE TO "+latestVersion+" IMMEDIATELY!"); outLines.add(" Bugs:"); for (String str : webVersion.bugs) { outLines.add(" -- "+str); } outLines.add("****************************"); return; } else if (webVersion.status == FGBuildStatus.OUTDATED) { outLines.add("ForgeGradle "+latestVersion + " is out! You should update!"); outLines.add(" Features:"); for (int i = webVersion.index; i < wrapper.versions.size(); i++) { for (String feature : wrapper.versionObjects.get(wrapper.versions.get(i)).changes) { outLines.add(" -- " + feature); } } outLines.add("****************************"); } onVersionCheck(webVersion, wrapper); } /** * Function to do stuff with the version check json information. Is called afterEvaluate * * @param version The ForgeGradle version * @param wrapper Version wrapper */ protected void onVersionCheck(FGVersion version, FGVersionWrapper wrapper) { // not required.. but you probably wanan implement this } @SuppressWarnings("serial") private void makeCommonTasks() { EtagDownloadTask getVersionJson = makeTask(TASK_DL_VERSION_JSON, EtagDownloadTask.class); { getVersionJson.setUrl(new Closure<String>(null, null) { @Override public String call() { return mcManifest.get(getExtension().getVersion()).url; } }); getVersionJson.setFile(delayedFile(JSON_VERSION)); getVersionJson.setDieWithError(false); getVersionJson.doLast(new Closure<Boolean>(project) // normalizes to linux endings { @Override public Boolean call() { try { // normalize the line endings... File json = delayedFile(JSON_VERSION).call(); if (!json.exists()) return true; List<String> lines = Files.readLines(json, Charsets.UTF_8); StringBuilder buf = new StringBuilder(); for (String line : lines) { buf = buf.append(line).append('\n'); } Files.write(buf.toString().getBytes(Charsets.UTF_8), json); // grab the AssetIndex if it isnt already there if (!replacer.hasReplacement(REPLACE_ASSET_INDEX)) { parseAndStoreVersion(json, json.getParentFile()); } } catch (Throwable t) { Throwables.propagate(t); } return true; } }); } ExtractConfigTask extractNatives = makeTask(TASK_EXTRACT_NATIVES, ExtractConfigTask.class); { extractNatives.setDestinationDir(delayedFile(DIR_NATIVES)); extractNatives.setConfig(CONFIG_NATIVES); extractNatives.exclude("META-INF/**", "META-INF/**"); extractNatives.setDoesCache(true); extractNatives.dependsOn(getVersionJson); } EtagDownloadTask getAssetsIndex = makeTask(TASK_DL_ASSET_INDEX, EtagDownloadTask.class); { getAssetsIndex.setUrl(new Closure<String>(null, null) { @Override public String call() { return mcVersionJson.assetIndex.url; } }); getAssetsIndex.setFile(delayedFile(JSON_ASSET_INDEX)); getAssetsIndex.setDieWithError(false); getAssetsIndex.dependsOn(getVersionJson); } DownloadAssetsTask getAssets = makeTask(TASK_DL_ASSETS, DownloadAssetsTask.class); { getAssets.setAssetsDir(delayedFile(DIR_ASSETS)); getAssets.setAssetsIndex(delayedFile(JSON_ASSET_INDEX)); getAssets.dependsOn(getAssetsIndex); } Download dlClient = makeTask(TASK_DL_CLIENT, Download.class); { dlClient.setOutput(delayedFile(JAR_CLIENT_FRESH)); dlClient.setUrl(new Closure<String>(null, null) { @Override public String call() { return mcVersionJson.getClientUrl(); } }); dlClient.dependsOn(getVersionJson); } Download dlServer = makeTask(TASK_DL_SERVER, Download.class); { dlServer.setOutput(delayedFile(JAR_SERVER_FRESH)); dlServer.setUrl(new Closure<String>(null, null) { @Override public String call() { return mcVersionJson.getServerUrl(); } }); dlServer.dependsOn(getVersionJson); } SplitJarTask splitServer = makeTask(TASK_SPLIT_SERVER, SplitJarTask.class); { splitServer.setInJar(delayedFile(JAR_SERVER_FRESH)); splitServer.setOutFirst(delayedFile(JAR_SERVER_PURE)); splitServer.setOutSecond(delayedFile(JAR_SERVER_DEPS)); splitServer.exclude("org/bouncycastle", "org/bouncycastle/*", "org/bouncycastle/**"); splitServer.exclude("org/apache", "org/apache/*", "org/apache/**"); splitServer.exclude("com/google", "com/google/*", "com/google/**"); splitServer.exclude("com/mojang/authlib", "com/mojang/authlib/*", "com/mojang/authlib/**"); splitServer.exclude("com/mojang/util", "com/mojang/util/*", "com/mojang/util/**"); splitServer.exclude("gnu/trove", "gnu/trove/*", "gnu/trove/**"); splitServer.exclude("io/netty", "io/netty/*", "io/netty/**"); splitServer.exclude("javax/annotation", "javax/annotation/*", "javax/annotation/**"); splitServer.exclude("argo", "argo/*", "argo/**"); splitServer.dependsOn(dlServer); } MergeJars merge = makeTask(TASK_MERGE_JARS, MergeJars.class); { merge.setClient(delayedFile(JAR_CLIENT_FRESH)); merge.setServer(delayedFile(JAR_SERVER_PURE)); merge.setOutJar(delayedFile(JAR_MERGED)); merge.dependsOn(dlClient, splitServer); merge.setGroup(null); merge.setDescription(null); } ExtractConfigTask extractMcpData = makeTask(TASK_EXTRACT_MCP, ExtractConfigTask.class); { extractMcpData.setDestinationDir(delayedFile(DIR_MCP_DATA)); extractMcpData.setConfig(CONFIG_MCP_DATA); extractMcpData.setDoesCache(true); } ExtractConfigTask extractMcpMappings = makeTask(TASK_EXTRACT_MAPPINGS, ExtractConfigTask.class); { extractMcpMappings.setDestinationDir(delayedFile(DIR_MCP_MAPPINGS)); extractMcpMappings.setConfig(CONFIG_MAPPINGS); extractMcpMappings.setDoesCache(true); } GenSrgs genSrgs = makeTask(TASK_GENERATE_SRGS, GenSrgs.class); { genSrgs.setInSrg(delayedFile(MCP_DATA_SRG)); genSrgs.setInExc(delayedFile(MCP_DATA_EXC)); genSrgs.setMethodsCsv(delayedFile(CSV_METHOD)); genSrgs.setFieldsCsv(delayedFile(CSV_FIELD)); genSrgs.setNotchToSrg(delayedFile(Constants.SRG_NOTCH_TO_SRG)); genSrgs.setNotchToMcp(delayedFile(Constants.SRG_NOTCH_TO_MCP)); genSrgs.setSrgToMcp(delayedFile(SRG_SRG_TO_MCP)); genSrgs.setMcpToSrg(delayedFile(SRG_MCP_TO_SRG)); genSrgs.setMcpToNotch(delayedFile(SRG_MCP_TO_NOTCH)); genSrgs.setSrgExc(delayedFile(EXC_SRG)); genSrgs.setMcpExc(delayedFile(EXC_MCP)); genSrgs.setDoesCache(true); genSrgs.dependsOn(extractMcpData, extractMcpMappings); } ObtainFernFlowerTask ffTask = makeTask(TASK_DL_FERNFLOWER, ObtainFernFlowerTask.class); { ffTask.setMcpUrl(delayedString(URL_FF)); ffTask.setFfJar(delayedFile(JAR_FERNFLOWER)); ffTask.setDoesCache(true); } Delete clearCache = makeTask(TASK_CLEAN_CACHE, Delete.class); { clearCache.delete(delayedFile(REPLACE_CACHE_DIR), delayedFile(DIR_LOCAL_CACHE)); clearCache.setGroup(GROUP_FG); clearCache.setDescription("Cleares the ForgeGradle cache. DONT RUN THIS unless you want a fresh start, or the dev tells you to."); } } /** * @return the extension object with name * @see Constants#EXT_NAME_MC */ @SuppressWarnings("unchecked") public final K getExtension() { return (K) project.getExtensions().getByName(EXT_NAME_MC); } public DefaultTask makeTask(String name) { return makeTask(name, DefaultTask.class); } public DefaultTask maybeMakeTask(String name) { return maybeMakeTask(name, DefaultTask.class); } public <T extends Task> T makeTask(String name, Class<T> type) { return makeTask(project, name, type); } public <T extends Task> T maybeMakeTask(String name, Class<T> type) { return maybeMakeTask(project, name, type); } public static <T extends Task> T maybeMakeTask(Project proj, String name, Class<T> type) { return (T) proj.getTasks().maybeCreate(name, type); } public static <T extends Task> T makeTask(Project proj, String name, Class<T> type) { return (T) proj.getTasks().create(name, type); } public static Project buildProject(File buildFile, Project parent) { ProjectBuilder builder = ProjectBuilder.builder(); if (buildFile != null) { builder = builder.withProjectDir(buildFile.getParentFile()).withName(buildFile.getParentFile().getName()); } else { builder = builder.withProjectDir(new File(".")); } if (parent != null) { builder = builder.withParent(parent); } Project project = builder.build(); if (buildFile != null) { project.apply(ImmutableMap.of("from", buildFile.getAbsolutePath())); } return project; } public void applyExternalPlugin(String plugin) { project.apply(ImmutableMap.of("plugin", plugin)); } public MavenArtifactRepository addMavenRepo(Project proj, final String name, final String url) { return proj.getRepositories().maven(new Action<MavenArtifactRepository>() { @Override public void execute(MavenArtifactRepository repo) { repo.setName(name); repo.setUrl(url); } }); } public FlatDirectoryArtifactRepository addFlatRepo(Project proj, final String name, final Object... dirs) { return proj.getRepositories().flatDir(new Action<FlatDirectoryArtifactRepository>() { @Override public void execute(FlatDirectoryArtifactRepository repo) { repo.setName(name); repo.dirs(dirs); } }); } protected String getWithEtag(String strUrl, File cache, File etagFile) { try { if (project.getGradle().getStartParameter().isOffline()) // dont even try the internet return Files.toString(cache, Charsets.UTF_8); // dude, its been less than 1 minute since the last time.. if (cache.exists() && cache.lastModified() + 60000 >= System.currentTimeMillis()) return Files.toString(cache, Charsets.UTF_8); String etag; if (etagFile.exists()) { etag = Files.toString(etagFile, Charsets.UTF_8); } else { etagFile.getParentFile().mkdirs(); etag = ""; } URL url = new URL(strUrl); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setInstanceFollowRedirects(true); con.setRequestProperty("User-Agent", USER_AGENT); con.setIfModifiedSince(cache.lastModified()); if (!Strings.isNullOrEmpty(etag)) { con.setRequestProperty("If-None-Match", etag); } con.connect(); String out = null; if (con.getResponseCode() == 304) { // the existing file is good Files.touch(cache); // touch it to update last-modified time, to wait another minute out = Files.toString(cache, Charsets.UTF_8); } else if (con.getResponseCode() == 200) { InputStream stream = con.getInputStream(); byte[] data = ByteStreams.toByteArray(stream); Files.write(data, cache); stream.close(); // write etag etag = con.getHeaderField("ETag"); if (Strings.isNullOrEmpty(etag)) { Files.touch(etagFile); } else { Files.write(etag, etagFile, Charsets.UTF_8); } out = new String(data); } else { project.getLogger().error("Etag download for " + strUrl + " failed with code " + con.getResponseCode()); } con.disconnect(); return out; } catch (Exception e) { e.printStackTrace(); } if (cache.exists()) { try { return Files.toString(cache, Charsets.UTF_8); } catch (IOException e) { Throwables.propagate(e); } } throw new RuntimeException("Unable to obtain url (" + strUrl + ") with etag!"); } /** * Parses the version json in the provided file, and saves it in memory. * Also populates the McDeps and natives configurations. * Also sets the ASSET_INDEX replacement string * Does nothing (returns null) if the file is not found, but hard-crashes if it could not be parsed. * @param file version file to parse * @param inheritanceDirs folders to look for the parent json, should include DIR_JSON * @return NULL if the file doesnt exist */ protected Version parseAndStoreVersion(File file, File... inheritanceDirs) { if (!file.exists()) return null; Version version = null; if (version == null) { try { version = JsonFactory.loadVersion(file, delayedString(REPLACE_MC_VERSION).call(), inheritanceDirs); } catch (Exception e) { project.getLogger().error("" + file + " could not be parsed"); Throwables.propagate(e); } } // apply the dep info. DependencyHandler handler = project.getDependencies(); // actual dependencies if (project.getConfigurations().getByName(CONFIG_MC_DEPS).getState() == State.UNRESOLVED) { for (net.minecraftforge.gradle.util.json.version.Library lib : version.getLibraries()) { if (lib.natives == null) { String configName = CONFIG_MC_DEPS; if (lib.name.contains("java3d") || lib.name.contains("paulscode") || lib.name.contains("lwjgl") || lib.name.contains("twitch") || lib.name.contains("jinput")) { configName = CONFIG_MC_DEPS_CLIENT; } handler.add(configName, lib.getArtifactName()); } } } else project.getLogger().debug("RESOLVED: " + CONFIG_MC_DEPS); // the natives if (project.getConfigurations().getByName(CONFIG_NATIVES).getState() == State.UNRESOLVED) { for (net.minecraftforge.gradle.util.json.version.Library lib : version.getLibraries()) { if (lib.natives != null) handler.add(CONFIG_NATIVES, lib.getArtifactName()); } } else project.getLogger().debug("RESOLVED: " + CONFIG_NATIVES); // set asset index replacer.putReplacement(REPLACE_ASSET_INDEX, version.assetIndex.id); this.mcVersionJson = version; return version; } // DELAYED STUFF ONLY ------------------------------------------------------------------------ private LoadingCache<String, TokenReplacer> replacerCache = CacheBuilder.newBuilder() .weakValues() .build( new CacheLoader<String, TokenReplacer>() { public TokenReplacer load(String key) { return new TokenReplacer(replacer, key); } }); private LoadingCache<String, DelayedString> stringCache = CacheBuilder.newBuilder() .weakValues() .build( new CacheLoader<String, DelayedString>() { public DelayedString load(String key) { return new DelayedString(replacerCache.getUnchecked(key)); } }); private LoadingCache<String, DelayedFile> fileCache = CacheBuilder.newBuilder() .weakValues() .build( new CacheLoader<String, DelayedFile>() { public DelayedFile load(String key) { return new DelayedFile(project, replacerCache.getUnchecked(key)); } }); public DelayedString delayedString(String path) { return stringCache.getUnchecked(path); } public DelayedFile delayedFile(String path) { return fileCache.getUnchecked(path); } public DelayedFileTree delayedTree(String path) { return new DelayedFileTree(project, replacerCache.getUnchecked(path)); } protected File cacheFile(String path) { return new File(project.getGradle().getGradleUserHomeDir(), "caches/minecraft/" + path); } }