From ef5f9c4b8d3d81ef1a17cc7b8e1a65c40a10059a Mon Sep 17 00:00:00 2001 From: zeng-github01 <95841646+zeng-github01@users.noreply.github.com> Date: Sat, 2 May 2026 00:53:06 +0800 Subject: [PATCH 1/2] Fix getAssetsIndex task undo format --- .../net/minecraftforge/gradle/FileUtils.java | 31 ++++ .../gradle/common/BasePlugin.java | 146 +++++++++++++++--- .../gradle/common/Constants.java | 15 +- .../gradle/common/version/Version.java | 3 + .../common/version/json/JsonFactory.java | 8 + .../gradle/json/MCVersionManifest.java | 32 ++++ .../gradle/json/version/Downloads.java | 14 ++ .../gradle/json/version/VersionToString.java | 7 + .../tasks/abstractutil/EtagDownloadTask.java | 135 ++++++++++++++++ .../gradle/tasks/dev/ObfuscateTask.java | 3 +- .../gradle/user/UserBasePlugin.java | 2 +- 11 files changed, 372 insertions(+), 24 deletions(-) create mode 100644 src/main/java/net/minecraftforge/gradle/FileUtils.java create mode 100644 src/main/java/net/minecraftforge/gradle/json/MCVersionManifest.java create mode 100644 src/main/java/net/minecraftforge/gradle/json/version/Downloads.java create mode 100644 src/main/java/net/minecraftforge/gradle/json/version/VersionToString.java create mode 100644 src/main/java/net/minecraftforge/gradle/tasks/abstractutil/EtagDownloadTask.java diff --git a/src/main/java/net/minecraftforge/gradle/FileUtils.java b/src/main/java/net/minecraftforge/gradle/FileUtils.java new file mode 100644 index 000000000..cbae3c53b --- /dev/null +++ b/src/main/java/net/minecraftforge/gradle/FileUtils.java @@ -0,0 +1,31 @@ +package net.minecraftforge.gradle; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Objects; + +public class FileUtils { + public static String readString(File file) throws IOException { + return readString(file, StandardCharsets.UTF_8); + } + + public static String readString(File file, Charset charset) throws IOException { + return new String(Files.readAllBytes(file.toPath()), charset); + } + + public static void updateDate(File file) throws IOException { + if (!file.createNewFile() && !file.setLastModified(System.currentTimeMillis())) { + throw new IOException("Unable to update modification time of " + file); + } + } + + public static String getFileExtension(String fullName) { + Objects.requireNonNull(fullName); + String fileName = new File(fullName).getName(); + int dotIndex = fileName.lastIndexOf('.'); + return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1); + } +} diff --git a/src/main/java/net/minecraftforge/gradle/common/BasePlugin.java b/src/main/java/net/minecraftforge/gradle/common/BasePlugin.java index e68879d17..ee28a6239 100644 --- a/src/main/java/net/minecraftforge/gradle/common/BasePlugin.java +++ b/src/main/java/net/minecraftforge/gradle/common/BasePlugin.java @@ -4,7 +4,11 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.HashMap; +import java.util.List; +import java.util.function.Function; import net.minecraftforge.gradle.FileLogListenner; import net.minecraftforge.gradle.common.version.AssetIndex; @@ -14,10 +18,13 @@ import net.minecraftforge.gradle.delayed.DelayedFile; import net.minecraftforge.gradle.delayed.DelayedFileTree; import net.minecraftforge.gradle.delayed.DelayedString; +import net.minecraftforge.gradle.json.MCVersionManifest; +import net.minecraftforge.gradle.json.version.VersionToString; import net.minecraftforge.gradle.tasks.DownloadAssetsTask; import net.minecraftforge.gradle.tasks.ObtainFernFlowerTask; import net.minecraftforge.gradle.tasks.abstractutil.DownloadTask; +import net.minecraftforge.gradle.tasks.abstractutil.EtagDownloadTask; import org.gradle.api.Action; import org.gradle.api.DefaultTask; import org.gradle.api.Plugin; @@ -129,16 +136,108 @@ private void makeObtainTasks() // download tasks DownloadTask task; + EtagDownloadTask etagDlTask; + etagDlTask = makeTask("getVersionJsonIndex", EtagDownloadTask.class); + { + etagDlTask.setUri(delayedString(Constants.MC_JSON_INDEX_URL)); + etagDlTask.setFile(delayedFile(Constants.VERSION_JSON_INDEX)); + etagDlTask.setDieWithError(false); + } + + etagDlTask = makeTask("getVersionJson", EtagDownloadTask.class); + { + class GetVersionJsonUrl extends DelayedString { + public GetVersionJsonUrl() { + super(BasePlugin.this.project, ""); + } + + @Override + public String call() { + try { + MCVersionManifest manifest = JsonFactory.loadMCVersionManifest(delayedFile(Constants.VERSION_JSON_INDEX).call()); + MCVersionManifest.Version version = manifest.findVersion(delayedString("{MC_VERSION}").call()); + return version.url; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + etagDlTask.dependsOn("getVersionJsonIndex"); + etagDlTask.getInputs().file(delayedFile(Constants.VERSION_JSON_INDEX)); + etagDlTask.setUri(new GetVersionJsonUrl()); + etagDlTask.setFile(delayedFile(Constants.VERSION_JSON)); + etagDlTask.setDieWithError(false); + //TODO: this is not necessary? + etagDlTask.doLast(new Action() { // normalizes to linux endings + @Override + public void execute(Task task) { + try { + File json = delayedFile(Constants.VERSION_JSON).call(); + if (!json.exists()) + return; + + List lines = Files.readAllLines(json.toPath()); + StringBuilder buf = new StringBuilder(); + for (String line : lines) { + buf.append(line).append('\n'); + } + Files.write(json.toPath(), buf.toString().getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } + + class GetDataFromJson extends DelayedString { + private final VersionToString function; + + public GetDataFromJson(VersionToString function) { + super(BasePlugin.this.project, ""); + this.function = function; + } + + @Override + public String call() { + try { + Version manifest = JsonFactory.loadVersion(delayedFile(Constants.VERSION_JSON).call()); + return function.apply(manifest); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + task = makeTask("downloadClient", DownloadTask.class); { + task.getInputs().file(delayedFile(Constants.VERSION_JSON)); + task.dependsOn("getVersionJson"); + task.setOutput(delayedFile(Constants.JAR_CLIENT_FRESH)); - task.setUrl(delayedString(Constants.MC_JAR_URL)); + task.setUrl(new GetDataFromJson(new VersionToString() { + @Override + public String apply(Version json) { + return json.downloads.client.url; + } + })); + + } task = makeTask("downloadServer", DownloadTask.class); { + task.getInputs().file(delayedFile(Constants.VERSION_JSON)); + task.dependsOn("getVersionJson"); + task.setOutput(delayedFile(Constants.JAR_SERVER_FRESH)); - task.setUrl(delayedString(Constants.MC_SERVER_URL)); + task.setUrl(new GetDataFromJson(new VersionToString() { + @Override + public String apply(Version json) { + return json.downloads.server.url; + } + })); + + } ObtainFernFlowerTask mcpTask = makeTask("downloadMcpTools", ObtainFernFlowerTask.class); @@ -146,28 +245,39 @@ private void makeObtainTasks() mcpTask.setMcpUrl(delayedString(Constants.MCP_URL)); mcpTask.setFfJar(delayedFile(Constants.FERNFLOWER)); } - - DownloadTask getAssetsIndex = makeTask("getAssetsIndex", DownloadTask.class); + + etagDlTask = makeTask("getAssetsIndex", EtagDownloadTask.class); { - getAssetsIndex.setUrl(delayedString(Constants.ASSETS_INDEX_URL)); - getAssetsIndex.setOutput(delayedFile(Constants.ASSETS + "/indexes/{ASSET_INDEX}.json")); - getAssetsIndex.setDoesCache(false); + etagDlTask.getInputs().file(delayedFile(Constants.VERSION_JSON)); + etagDlTask.dependsOn("getVersionJson"); - getAssetsIndex.doLast(new Action() { - public void execute(Task task) - { - try - { + etagDlTask.setUrl(new GetDataFromJson(new VersionToString() { + @Override + public String apply(Version json) { + return json.assetIndex.url; + } + })); + + + etagDlTask.setFile(delayedFile(Constants.ASSETS + "/indexes/{ASSET_INDEX}.json")); + etagDlTask.setDieWithError(false); + + etagDlTask.doLast(new Action() { + @Override + public void execute(Task task1) { + try { parseAssetIndex(); - } - catch (Exception e) - { - Throwables.propagate(e); + } catch (JsonSyntaxException e) { + throw new RuntimeException(e); + } catch (JsonIOException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); } } }); - - getAssetsIndex.getOutputs().upToDateWhen(new Closure(this, null) { + + etagDlTask.getOutputs().upToDateWhen(new Closure(this, null) { public Boolean call(Object... obj) { return false; diff --git a/src/main/java/net/minecraftforge/gradle/common/Constants.java b/src/main/java/net/minecraftforge/gradle/common/Constants.java index 552a77611..b34204d0a 100644 --- a/src/main/java/net/minecraftforge/gradle/common/Constants.java +++ b/src/main/java/net/minecraftforge/gradle/common/Constants.java @@ -49,6 +49,9 @@ public String toString() public static final OperatingSystem OPERATING_SYSTEM = getOs(); public static final SystemArch SYSTEM_ARCH = getArch(); + public static final String HASH_FUNC = "MD5"; + public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"; + // extension nam public static final String EXT_NAME_MC = "minecraft"; public static final String EXT_NAME_JENKINS = "jenkins"; @@ -60,12 +63,13 @@ public String toString() public static final Closure CALL_FALSE = new Closure(null){ public Boolean call(Object o){ return false; }}; // urls - public static final String MC_JAR_URL = "http://s3.amazonaws.com/Minecraft.Download/versions/{MC_VERSION}/{MC_VERSION}.jar"; - public static final String MC_SERVER_URL = "http://s3.amazonaws.com/Minecraft.Download/versions/{MC_VERSION}/minecraft_server.{MC_VERSION}.jar"; - public static final String MCP_URL = "http://files.minecraftforge.net/fernflower_temporary.zip"; - public static final String ASSETS_URL = "http://resources.download.minecraft.net"; + public static final String MC_JAR_URL = "https://s3.amazonaws.com/Minecraft.Download/versions/{MC_VERSION}/{MC_VERSION}.jar"; + public static final String MC_SERVER_URL = "https://s3.amazonaws.com/Minecraft.Download/versions/{MC_VERSION}/minecraft_server.{MC_VERSION}.jar"; + public static final String MCP_URL = "https://files.minecraftforge.net/fernflower_temporary.zip"; + public static final String ASSETS_URL = "https://resources.download.minecraft.net"; public static final String LIBRARY_URL = "https://libraries.minecraft.net/"; public static final String ASSETS_INDEX_URL = "https://s3.amazonaws.com/Minecraft.Download/indexes/{ASSET_INDEX}.json"; + public static final String MC_JSON_INDEX_URL = "https://piston-meta.mojang.com/mc/game/version_manifest.json"; public static final String LOG = ".gradle/gradle.log"; public static final String ASSETS_INDEX = "legacy"; @@ -77,6 +81,9 @@ public String toString() public static final String FERNFLOWER = "{CACHE_DIR}/minecraft/fernflower.jar"; public static final String EXCEPTOR = "{CACHE_DIR}/minecraft/exceptor.jar"; public static final String ASSETS = "{CACHE_DIR}/minecraft/assets"; + public static final String JSONS_DIR = "{CACHE_DIR}/minecraft/versionJsons"; + public static final String VERSION_JSON_INDEX = JSONS_DIR + "/index.json"; + public static final String VERSION_JSON = JSONS_DIR + "/{MC_VERSION}.json"; public static final String DEOBF_JAR = "{BUILD_DIR}/deobfuscated.jar"; public static final String DEOBF_BIN_JAR = "{BUILD_DIR}/deobfuscated-bin.jar"; diff --git a/src/main/java/net/minecraftforge/gradle/common/version/Version.java b/src/main/java/net/minecraftforge/gradle/common/version/Version.java index 41121bff7..1bb06f141 100644 --- a/src/main/java/net/minecraftforge/gradle/common/version/Version.java +++ b/src/main/java/net/minecraftforge/gradle/common/version/Version.java @@ -5,6 +5,7 @@ import java.util.List; import net.minecraftforge.gradle.common.Constants; +import net.minecraftforge.gradle.json.version.Downloads; public class Version { @@ -19,6 +20,8 @@ public class Version public String incompatibilityReason; private String assets; public List rules; + public Downloads downloads; + public Downloads.DownloadFileInfo assetIndex; private List _libraries; diff --git a/src/main/java/net/minecraftforge/gradle/common/version/json/JsonFactory.java b/src/main/java/net/minecraftforge/gradle/common/version/json/JsonFactory.java index a0dcff630..b9485a77c 100644 --- a/src/main/java/net/minecraftforge/gradle/common/version/json/JsonFactory.java +++ b/src/main/java/net/minecraftforge/gradle/common/version/json/JsonFactory.java @@ -13,6 +13,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; +import net.minecraftforge.gradle.json.MCVersionManifest; public class JsonFactory { @@ -43,4 +44,11 @@ public static AssetIndex loadAssetsIndex(File json) throws JsonSyntaxException, reader.close(); return a; } + + public static MCVersionManifest loadMCVersionManifest(File json) throws JsonSyntaxException, JsonIOException, IOException { + FileReader reader = new FileReader(json); + MCVersionManifest a = GSON.fromJson(reader, MCVersionManifest.class); + reader.close(); + return a; + } } diff --git a/src/main/java/net/minecraftforge/gradle/json/MCVersionManifest.java b/src/main/java/net/minecraftforge/gradle/json/MCVersionManifest.java new file mode 100644 index 000000000..7abde652a --- /dev/null +++ b/src/main/java/net/minecraftforge/gradle/json/MCVersionManifest.java @@ -0,0 +1,32 @@ +package net.minecraftforge.gradle.json; + +import java.util.Date; +import java.util.List; + +// format of https://piston-meta.mojang.com/mc/game/version_manifest.json +public class MCVersionManifest { + public LatestInfo latest; + public List versions; + + public Version findVersion(String versionId) { + for (Version v : versions) { + if (versionId.equals(v.id)) { + return v; + } + } + throw new IllegalArgumentException(versionId + " not found"); + } + + public static class LatestInfo { + public String release; + public String snapshot; + } + + public static class Version { + public String id; + public String type; + public String url; + public Date time; + public Date releaseTime; + } +} diff --git a/src/main/java/net/minecraftforge/gradle/json/version/Downloads.java b/src/main/java/net/minecraftforge/gradle/json/version/Downloads.java new file mode 100644 index 000000000..691d2155d --- /dev/null +++ b/src/main/java/net/minecraftforge/gradle/json/version/Downloads.java @@ -0,0 +1,14 @@ +package net.minecraftforge.gradle.json.version; + + +public class Downloads { + public DownloadFileInfo client; + public DownloadFileInfo server; + public DownloadFileInfo windows_server; + + public static class DownloadFileInfo { + public String sha1; + public long size; + public String url; + } +} diff --git a/src/main/java/net/minecraftforge/gradle/json/version/VersionToString.java b/src/main/java/net/minecraftforge/gradle/json/version/VersionToString.java new file mode 100644 index 000000000..afe55b40a --- /dev/null +++ b/src/main/java/net/minecraftforge/gradle/json/version/VersionToString.java @@ -0,0 +1,7 @@ +package net.minecraftforge.gradle.json.version; + +import net.minecraftforge.gradle.common.version.Version; + +public interface VersionToString { + String apply(Version version); +} diff --git a/src/main/java/net/minecraftforge/gradle/tasks/abstractutil/EtagDownloadTask.java b/src/main/java/net/minecraftforge/gradle/tasks/abstractutil/EtagDownloadTask.java new file mode 100644 index 000000000..62f7f23e4 --- /dev/null +++ b/src/main/java/net/minecraftforge/gradle/tasks/abstractutil/EtagDownloadTask.java @@ -0,0 +1,135 @@ +package net.minecraftforge.gradle.tasks.abstractutil; + +import com.google.common.base.Strings; +import com.google.common.io.ByteStreams; +import groovy.lang.Closure; +import net.minecraftforge.gradle.FileUtils; +import net.minecraftforge.gradle.common.Constants; +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +public class EtagDownloadTask extends DefaultTask { + Object uri; + Object file; + boolean dieWithError; + + @TaskAction + public void doTask() throws IOException, URISyntaxException { + URI uri = getUri(); + File outFile = getFile(); + File etagFile = getProject().file(getFile().getPath() + ".etag"); + + // ensure folder exists + outFile.getParentFile().mkdirs(); + + String etag; + if (etagFile.exists()) { + etag = FileUtils.readString(etagFile); + } else { + etag = ""; + } + + try { + HttpURLConnection con = (HttpURLConnection) uri.toURL().openConnection(); + con.setInstanceFollowRedirects(true); + con.setRequestProperty("User-Agent", Constants.USER_AGENT); + con.setRequestProperty("If-None-Match", etag); + + con.connect(); + + switch (con.getResponseCode()) { + case 404: // file not found.... duh... + error(uri + " 404'ed!"); + break; + case 304: // content is the same. + this.setDidWork(false); + break; + case 200: // worked + + // write file + InputStream stream = con.getInputStream(); + Files.write(outFile.toPath(), ByteStreams.toByteArray(stream)); + stream.close(); + + // write etag + etag = con.getHeaderField("ETag"); + if (!Strings.isNullOrEmpty(etag)) { + Files.write(etagFile.toPath(), etag.getBytes(StandardCharsets.UTF_8)); + } + + break; + default: // another code?? uh.. + error("Unexpected reponse " + con.getResponseCode() + " from " + uri); + break; + } + + con.disconnect(); + } catch (Throwable e) { + // just in case people dont have internet at the moment. + error(e.getLocalizedMessage()); + } + } + + private void error(String error) { + if (dieWithError) { + throw new RuntimeException(error); + } else { + getLogger().error(error); + } + } + + @Deprecated + public URL getUrl() throws MalformedURLException { + try { + return getUri().toURL(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + @Deprecated + public void setUrl(Object url) { + this.setUri(url); + } + + @Input + public URI getUri() throws URISyntaxException { + while (uri instanceof Closure) { + uri = ((Closure) uri).call(); + } + + return new URI(uri.toString()); + } + + public void setUri(Object url) { + this.uri = url; + } + + @OutputFile + public File getFile() { + return getProject().file(file); + } + + public void setFile(Object file) { + this.file = file; + } + + @Input + public boolean isDieWithError() { + return dieWithError; + } + + public void setDieWithError(boolean dieWithError) { + this.dieWithError = dieWithError; + } +} + diff --git a/src/main/java/net/minecraftforge/gradle/tasks/dev/ObfuscateTask.java b/src/main/java/net/minecraftforge/gradle/tasks/dev/ObfuscateTask.java index a2397938f..bf80444f2 100644 --- a/src/main/java/net/minecraftforge/gradle/tasks/dev/ObfuscateTask.java +++ b/src/main/java/net/minecraftforge/gradle/tasks/dev/ObfuscateTask.java @@ -89,7 +89,8 @@ private void executeTask(AbstractTask task) { for (Object dep : task.getTaskDependencies().getDependencies(task)) { - executeTask((AbstractTask) dep); + if (dep instanceof AbstractTask) + executeTask((AbstractTask) dep); } if (!task.getState().getExecuted()) diff --git a/src/main/java/net/minecraftforge/gradle/user/UserBasePlugin.java b/src/main/java/net/minecraftforge/gradle/user/UserBasePlugin.java index 29c118af6..326bde0d9 100644 --- a/src/main/java/net/minecraftforge/gradle/user/UserBasePlugin.java +++ b/src/main/java/net/minecraftforge/gradle/user/UserBasePlugin.java @@ -253,7 +253,7 @@ public void execute(ArtifactSpec arg0) task5.setAssetsDir(delayedFile(Constants.ASSETS)); task5.setOutputDir(delayedFile("{ASSET_DIR}")); task5.setAssetIndex(getAssetIndexClosure()); - task5.dependsOn("getAssets"); + task5.dependsOn("getAssets", "getAssetsIndex"); } } From b5cc10e57f70843e92eb62903541d78f34c90843 Mon Sep 17 00:00:00 2001 From: zeng-github01 <95841646+zeng-github01@users.noreply.github.com> Date: Mon, 18 May 2026 20:38:16 +0800 Subject: [PATCH 2/2] don't remove getAssetsIndexn task cache --- .../java/net/minecraftforge/gradle/common/BasePlugin.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/net/minecraftforge/gradle/common/BasePlugin.java b/src/main/java/net/minecraftforge/gradle/common/BasePlugin.java index ee28a6239..82e35e7d7 100644 --- a/src/main/java/net/minecraftforge/gradle/common/BasePlugin.java +++ b/src/main/java/net/minecraftforge/gradle/common/BasePlugin.java @@ -276,13 +276,6 @@ public void execute(Task task1) { } } }); - - etagDlTask.getOutputs().upToDateWhen(new Closure(this, null) { - public Boolean call(Object... obj) - { - return false; - } - }); } DownloadAssetsTask assets = makeTask("getAssets", DownloadAssetsTask.class);