Skip to content

Commit d1b1bd4

Browse files
committed
Add Minecraft Data task to gather vanilla files
1 parent 610bb2b commit d1b1bd4

3 files changed

Lines changed: 206 additions & 1 deletion

File tree

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* Copyright (c) Forge Development LLC and contributors
3+
* SPDX-License-Identifier: LGPL-2.1-only
4+
*/
5+
package net.minecraftforge.mcmaven.cli;
6+
7+
import java.io.File;
8+
import java.io.IOException;
9+
import java.nio.charset.StandardCharsets;
10+
import java.nio.file.Files;
11+
import java.util.ArrayList;
12+
import java.util.LinkedHashMap;
13+
import java.util.List;
14+
import java.util.stream.Collectors;
15+
16+
import joptsimple.OptionParser;
17+
import net.minecraftforge.mcmaven.impl.Mavenizer;
18+
import net.minecraftforge.mcmaven.impl.cache.Cache;
19+
import net.minecraftforge.mcmaven.impl.repo.mcpconfig.MCPConfigRepo;
20+
import net.minecraftforge.mcmaven.impl.repo.mcpconfig.MinecraftTasks;
21+
import net.minecraftforge.mcmaven.impl.repo.mcpconfig.MinecraftTasks.ArtifactFile;
22+
import net.minecraftforge.mcmaven.impl.repo.mcpconfig.MinecraftTasks.MCFile;
23+
import net.minecraftforge.util.data.json.JsonData;
24+
import net.minecraftforge.util.hash.HashStore;
25+
26+
import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER;
27+
28+
import org.apache.commons.io.FileUtils;
29+
30+
/**
31+
* Downlaods and prepares Minecraft related files.
32+
* Currently the only goal of this is to gain access to vanilla files.
33+
* No actual processing, except for extraction of the bundled server jar is done.
34+
*
35+
* The paths contained in this json file are system specific and thus can not be shared
36+
* across workspaces. Unless you specify the --output-dir option which will copy all files
37+
* to the target directory and the json will list relative paths.
38+
*
39+
* Output:
40+
* {
41+
* "id": <Minecraft Version>
42+
* "version": <path> -- Version json file
43+
* "client": <path>, -- The raw client jar
44+
* "server": <path>, -- The raw server jar
45+
*
46+
* "server.extracted": <path> -- equal to "server" if the server jar is not a bundle
47+
*
48+
* "client.libraries": [<path>] -- List of all client libraries, reguardless of OS
49+
* "server.libraries": [<path>] -- Empty if the server jar is not a bundle.
50+
*
51+
* "client.mappings": <path>
52+
* "server.mappings": <path> -- Official mappings file, null if none specified in version json
53+
*
54+
* // TODO: [Mavenizer] Download client assets
55+
* //"client.assets": <path> -- Path to the root 'assets' directory, only if asked for using --assets
56+
* }
57+
*/
58+
class MinecraftDataTask {
59+
static OptionParser run(String[] args, boolean getParser) throws Exception {
60+
var parser = new OptionParser();
61+
parser.allowsUnrecognizedOptions();
62+
63+
//@formatter:off
64+
// help message
65+
var helpO = parser.accepts("help",
66+
"Displays this help message and exits")
67+
.forHelp();
68+
69+
var cacheO = parser.accepts("cache",
70+
"Directory to store data needed for this program")
71+
.withRequiredArg().ofType(File.class).defaultsTo(new File("cache"));
72+
73+
var outputO = parser.accepts("output",
74+
"File to output a JSON containing paths to extra files")
75+
.withRequiredArg().ofType(File.class).defaultsTo(new File("output.json"));
76+
77+
var outputDirO = parser.accepts("output-dir",
78+
"Folder to output all files to, if specified, paths in output json will be relative to this")
79+
.withRequiredArg().ofType(File.class).defaultsTo(new File("output"));
80+
81+
var versionO = parser.accepts("version",
82+
"Minecraft version")
83+
.withRequiredArg().required();
84+
//@formatter:on
85+
86+
if (getParser)
87+
return parser;
88+
89+
var options = parser.parse(args);
90+
if (options.has(helpO)) {
91+
parser.printHelpOn(LOGGER.getInfo());
92+
LOGGER.release();
93+
return parser;
94+
}
95+
96+
97+
var outputDir = options.has(outputDirO) ? options.valueOf(outputDirO) : null;
98+
var output = options.valueOf(outputO);
99+
100+
var cacheRoot = options.valueOf(cacheO);
101+
var version = options.valueOf(versionO);
102+
103+
LOGGER.info(" Output: " + output.getAbsolutePath());
104+
LOGGER.info(" Output-Dir: " + outputDir == null ? "null" : outputDir.getAbsolutePath());
105+
LOGGER.info(" Version: " + version);
106+
LOGGER.info(" Cache: " + cacheRoot.getAbsolutePath());
107+
LOGGER.info();
108+
109+
var task = new MinecraftDataTask(output, outputDir, version, cacheRoot);
110+
task.run();
111+
112+
return parser;
113+
}
114+
115+
private final File output;
116+
private final File outputDir;
117+
private final String version;
118+
private final MCPConfigRepo repo;
119+
private final MinecraftTasks tasks;
120+
121+
private MinecraftDataTask(File output, File outputDir, String version, File cacheRoot) {
122+
this.output = output;
123+
this.outputDir = outputDir;
124+
this.version = version;
125+
repo = new MCPConfigRepo(new Cache(cacheRoot, new File(cacheRoot, "jdks")), false);
126+
tasks = repo.getMCTasks(version);
127+
}
128+
129+
private void run() {
130+
var data = new LinkedHashMap<String, String>();
131+
var versionJson = tasks.versionJson.execute();
132+
var json = JsonData.minecraftVersion(versionJson);
133+
var server = tasks.versionFile(MCFile.SERVER_JAR).execute();
134+
var serverExtracted = tasks.extractServer().execute();
135+
var root = outputDir != null ? new File(outputDir, version) : tasks.versionCache();
136+
137+
data.put("id", version);
138+
data.put("version", local(versionJson, version + "/version.json"));
139+
data.put("client", local(tasks.versionFile(MCFile.CLIENT_JAR).execute(), version + "/client.jar"));
140+
data.put("client.libraries", libraries(root, "client", tasks.getClientLibraries()));
141+
data.put("server", local(server, version + "/server.jar"));
142+
if (server.getAbsoluteFile().equals(serverExtracted.getAbsoluteFile()))
143+
data.put("server.extracted", data.get("server"));
144+
else {
145+
data.put("server.extracted", local(serverExtracted, version + "/server-extracted.jar"));
146+
data.put("server.libraries", libraries(root, "server", tasks.getServerLibraries()));
147+
}
148+
if (json.getDownload("client_mappings") != null)
149+
data.put("client.mappings", local(tasks.versionFile(MCFile.CLIENT_MAPPINGS).execute(), version +"/client_mappings.txt"));
150+
if (json.getDownload("server_mappings") != null)
151+
data.put("server.mappings", local(tasks.versionFile(MCFile.SERVER_MAPPINGS).execute(), version +"/server_mappings.txt"));
152+
153+
try {
154+
JsonData.toJson(data, output);
155+
} catch (IOException e) {
156+
throw new RuntimeException("Failed to generate file: %s".formatted(output.getAbsolutePath()), e);
157+
}
158+
}
159+
160+
private String libraries(File root, String side, List<ArtifactFile> libraries) {
161+
var libs = new ArrayList<String>();
162+
for (var af : libraries)
163+
libs.add(local(af.file(), "libraries/" + af.artifact().getPath()));
164+
165+
var target = new File(root, side + "-libraries.txt");
166+
167+
try {
168+
FileUtils.createParentDirectories(target);
169+
Files.writeString(target.toPath(), libs.stream().collect(Collectors.joining("\n")), StandardCharsets.UTF_8);
170+
} catch (IOException e) {
171+
throw new RuntimeException("Failed to generate file: %s".formatted(target.getAbsolutePath()), e);
172+
}
173+
174+
return local(target, version + '/' + side + "-libraries.txt");
175+
}
176+
177+
private String local(File source, String relative) {
178+
if (outputDir == null)
179+
return source.getAbsolutePath();
180+
181+
var target = new File(outputDir, relative);
182+
183+
// Incase the output dir is the shared cache
184+
if (target.getAbsoluteFile().equals(source.getAbsoluteFile()))
185+
return relative;
186+
187+
var cache = HashStore.fromFile(target)
188+
.add("source", source);
189+
if (Mavenizer.checkCache(target, cache))
190+
return relative;
191+
try {
192+
FileUtils.createParentDirectories(target);
193+
org.apache.commons.io.FileUtils.copyFile(source, target);
194+
cache.save();
195+
} catch (Throwable t) {
196+
throw new RuntimeException("Failed to generate file: %s".formatted(target.getAbsolutePath()), t);
197+
}
198+
return relative;
199+
}
200+
}

src/main/java/net/minecraftforge/mcmaven/cli/Tasks.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
enum Tasks {
1212
MAVEN(MavenTask::run, "Generates a maven repository for Minecraft Artifacts"),
1313
MCP(MCPTask::run, "Generates a 'clean' sources jar from a MCPConfig pipeline"),
14-
MCP_DATA(MCPDataTask::run, "Extracts a data file from a MCPConfig archive")
14+
MCP_DATA(MCPDataTask::run, "Extracts a data file from a MCPConfig archive"),
15+
MINECRAFT_DATA(MinecraftDataTask::run, "Gathers vanilla files")
1516
;
1617

1718
interface Callback {

src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MinecraftTasks.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ private MCFile(String key, String ext) {
8181
this.versionJson = Task.named("downloadVersionJson[" + version + ']', Task.deps(this.launcherManifest), this::downloadVersionJson);
8282
}
8383

84+
public File versionCache() {
85+
return this.cacheRoot;
86+
}
87+
8488
public String getVersion() {
8589
return this.version;
8690
}

0 commit comments

Comments
 (0)