Skip to content

Commit 868521f

Browse files
authored
Use PaperMC's Fill v3 downloads API (#587)
1 parent f6108ca commit 868521f

27 files changed

+695
-2963
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ jobs:
1818
with:
1919
arguments: test
2020
include-test-report: true
21+
retest-showing-standard-streams: true

src/main/java/me/itzg/helpers/http/FetchBuilderBase.java

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package me.itzg.helpers.http;
22

3-
import static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT;
4-
import static io.netty.handler.codec.http.HttpHeaderNames.AUTHORIZATION;
5-
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
3+
import static io.netty.handler.codec.http.HttpHeaderNames.*;
64

5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import io.netty.handler.codec.http.HttpHeaderNames;
7+
import io.netty.handler.codec.http.HttpHeaders;
8+
import io.netty.handler.codec.http.HttpStatusClass;
9+
import io.netty.handler.codec.http.HttpUtil;
710
import java.net.URI;
811
import java.net.URISyntaxException;
912
import java.nio.charset.StandardCharsets;
@@ -22,19 +25,11 @@
2225
import java.util.regex.Matcher;
2326
import java.util.regex.Pattern;
2427
import java.util.stream.Collectors;
25-
26-
import org.slf4j.Logger;
27-
28-
import com.fasterxml.jackson.databind.ObjectMapper;
29-
30-
import io.netty.handler.codec.http.HttpHeaderNames;
31-
import io.netty.handler.codec.http.HttpHeaders;
32-
import io.netty.handler.codec.http.HttpStatusClass;
33-
import io.netty.handler.codec.http.HttpUtil;
3428
import lombok.extern.slf4j.Slf4j;
3529
import me.itzg.helpers.errors.GenericException;
3630
import me.itzg.helpers.http.SharedFetch.Options;
3731
import me.itzg.helpers.json.ObjectMappers;
32+
import org.slf4j.Logger;
3833
import reactor.core.publisher.Mono;
3934
import reactor.netty.ByteBufMono;
4035
import reactor.netty.Connection;
@@ -147,6 +142,16 @@ protected URI uri() {
147142
return state.uri;
148143
}
149144

145+
protected URI uriForFile() {
146+
if (state.sharedFetch == null) {
147+
return state.uri;
148+
}
149+
150+
final URI filesViaUrl = state.sharedFetch.getFilesViaUrl();
151+
return filesViaUrl != null ? filesViaUrl.resolve(state.uri.getPath())
152+
: state.uri;
153+
}
154+
150155
public Set<String> getAcceptContentTypes() {
151156
return state.acceptContentTypes;
152157
}

src/main/java/me/itzg/helpers/http/SharedFetch.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public class SharedFetch implements AutoCloseable {
3333

3434
private final HttpClient reactiveClient;
3535

36+
private final URI filesViaUrl;
37+
3638
public SharedFetch(String forCommand, Options options) {
3739
final String userAgent = String.format("%s/%s/%s (cmd=%s)",
3840
"itzg",
@@ -74,6 +76,8 @@ public SharedFetch(String forCommand, Options options) {
7476
);
7577

7678
headers.put("x-fetch-session", fetchSessionId);
79+
80+
this.filesViaUrl = options.getFilesViaUrl();
7781
}
7882

7983
public FetchBuilderBase<?> fetch(URI uri) {
@@ -118,14 +122,20 @@ public static class Options {
118122

119123
private final Map<String,String> extraHeaders;
120124

125+
/**
126+
* Can be set for unit testing file downloads where the original URL's path is resolved
127+
* against this given URL.
128+
*/
129+
private final URI filesViaUrl;
130+
121131
public Options withHeader(String key, String value) {
122132
final Map<String, String> newHeaders = extraHeaders != null ?
123133
new HashMap<>(extraHeaders) : new HashMap<>();
124134
newHeaders.put(key, value);
125135

126136
return new Options(
127137
responseTimeout, tlsHandshakeTimeout, maxIdleTimeout, pendingAcquireTimeout,
128-
newHeaders
138+
newHeaders, filesViaUrl
129139
);
130140
}
131141
}

src/main/java/me/itzg/helpers/http/SpecificFileFetchBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public Path execute() throws IOException {
5454
}
5555

5656
public Mono<Path> assemble() {
57-
final URI uri = uri();
57+
final URI uri = uriForFile();
5858

5959
if (skipExisting && Files.exists(file)) {
6060
log.debug("Skipping file={} that already exists due to request", file);

src/main/java/me/itzg/helpers/paper/InstallPaperCommand.java

Lines changed: 39 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@
44
import java.io.IOException;
55
import java.net.URI;
66
import java.nio.file.Path;
7-
import java.util.Arrays;
87
import java.util.Collections;
98
import java.util.Objects;
109
import java.util.concurrent.Callable;
1110
import java.util.regex.Matcher;
1211
import java.util.regex.Pattern;
13-
import java.util.stream.Collectors;
1412
import lombok.Builder;
1513
import lombok.extern.slf4j.Slf4j;
1614
import me.itzg.helpers.errors.GenericException;
@@ -19,15 +17,14 @@
1917
import me.itzg.helpers.files.ManifestException;
2018
import me.itzg.helpers.files.Manifests;
2119
import me.itzg.helpers.files.ResultsFileWriter;
22-
import me.itzg.helpers.http.FailedRequestException;
2320
import me.itzg.helpers.http.Fetch;
21+
import me.itzg.helpers.http.FileDownloadStatusHandler;
2422
import me.itzg.helpers.http.SharedFetch;
2523
import me.itzg.helpers.http.SharedFetchArgs;
2624
import me.itzg.helpers.json.ObjectMappers;
27-
import me.itzg.helpers.paper.model.ReleaseChannel;
25+
import me.itzg.helpers.paper.PaperDownloadsClient.VersionBuildFile;
2826
import me.itzg.helpers.paper.model.VersionMeta;
2927
import me.itzg.helpers.sync.MultiCopyManifest;
30-
import org.jetbrains.annotations.NotNull;
3128
import picocli.CommandLine;
3229
import picocli.CommandLine.ArgGroup;
3330
import picocli.CommandLine.Command;
@@ -83,15 +80,16 @@ public void setVersion(String version) {
8380
@Option(names = "--build")
8481
Integer build;
8582

86-
@Option(names = "--channel", defaultValue = "default")
87-
ReleaseChannel channel;
83+
@Option(names = "--channel", description = "This is ignored for now",
84+
defaultValue = "default")
85+
RequestedChannel channel;
8886
}
8987
}
9088

9189
@Option(names = {"--output-directory", "-o"}, defaultValue = ".")
9290
Path outputDirectory;
9391

94-
@Option(names = "--base-url", defaultValue = "https://api.papermc.io")
92+
@Option(names = "--base-url", defaultValue = "https://fill.papermc.io")
9593
String baseUrl;
9694

9795
@Option(names = "--results-file", description = ResultsFileWriter.OPTION_DESCRIPTION, paramLabel = "FILE")
@@ -123,13 +121,12 @@ public Integer call() throws Exception {
123121
else {
124122
if (requestCheckUpdates) {
125123
return checkForUpdates(client, oldManifest,
126-
inputs.coordinates.project, inputs.coordinates.version, inputs.coordinates.build,
127-
inputs.coordinates.channel);
124+
inputs.coordinates.project, inputs.coordinates.version, inputs.coordinates.build
125+
);
128126
}
129127

130128
result = downloadUsingCoordinates(client, inputs.coordinates.project,
131-
inputs.coordinates.version, inputs.coordinates.build,
132-
inputs.coordinates.channel
129+
inputs.coordinates.version, inputs.coordinates.build
133130
)
134131
.block();
135132
}
@@ -155,8 +152,7 @@ public Integer call() throws Exception {
155152
}
156153

157154
private Integer checkForUpdates(PaperDownloadsClient client, PaperManifest oldManifest,
158-
String project, String version, Integer build,
159-
ReleaseChannel channel
155+
String project, String version, Integer build
160156
) {
161157
if (oldManifest != null && oldManifest.getCustomDownloadUrl() != null) {
162158
log.info("Using custom download URL before");
@@ -173,7 +169,7 @@ private Integer checkForUpdates(PaperDownloadsClient client, PaperManifest oldMa
173169
}
174170
}
175171
else {
176-
return client.getLatestBuild(project, version, channel)
172+
return client.getLatestBuild(project, version)
177173
.map(resolvedBuild -> {
178174
if (oldManifest == null) {
179175
return logVersion(project, version, resolvedBuild);
@@ -185,14 +181,11 @@ private Integer checkForUpdates(PaperDownloadsClient client, PaperManifest oldMa
185181
return ExitCode.SOFTWARE;
186182
}
187183
})
188-
.switchIfEmpty(Mono.error(() -> new InvalidParameterException(
189-
String.format("No build found for version %s with channel %s", version, channel)
190-
)))
191184
.block();
192185
}
193186
}
194187
else {
195-
return client.getLatestVersionBuild(project, channel)
188+
return client.getLatestVersionBuild(project)
196189
.map(versionBuild -> {
197190
if (oldManifest == null) {
198191
return logVersion(project, versionBuild.getVersion(), versionBuild.getBuild());
@@ -204,9 +197,6 @@ private Integer checkForUpdates(PaperDownloadsClient client, PaperManifest oldMa
204197
return ExitCode.SOFTWARE;
205198
}
206199
})
207-
.switchIfEmpty(
208-
Mono.error(() -> new InvalidParameterException("No build found with channel " + channel))
209-
)
210200
.block();
211201
}
212202
return ExitCode.SOFTWARE;
@@ -234,72 +224,48 @@ private static boolean mismatchingVersions(PaperManifest oldManifest, String pro
234224
}
235225

236226
private Mono<Result> downloadUsingCoordinates(PaperDownloadsClient client, String project,
237-
String version, Integer build, ReleaseChannel channel
227+
String version, Integer build
238228
) {
229+
return
230+
assembleDownload(client, project, version, build)
231+
.map(result ->
232+
Result.builder()
233+
.newManifest(
234+
PaperManifest.builder()
235+
.project(project)
236+
.minecraftVersion(result.getVersion())
237+
.build(result.getBuild())
238+
.files(Collections.singleton(Manifests.relativize(outputDirectory, result.getFile())))
239+
.build()
240+
)
241+
.serverJar(result.getFile())
242+
.version(result.getVersion())
243+
.build()
244+
);
245+
}
246+
247+
private Mono<VersionBuildFile> assembleDownload(PaperDownloadsClient client, String project, String version,
248+
Integer build
249+
) {
250+
final FileDownloadStatusHandler downloadStatusHandler = Fetch.loggingDownloadStatusHandler(log);
251+
239252
if (isSpecificVersion(version)) {
240253
if (build != null) {
241-
return download(client, project, version, build)
242-
.onErrorMap(
243-
FailedRequestException::isNotFound,
244-
throwable -> new InvalidParameterException(
245-
String.format("Requested version %s, build %d is not available", version, build))
246-
);
254+
return client.download(project, outputDirectory, downloadStatusHandler, version, build);
247255
}
248256
else {
249-
return client.getLatestBuild(project, version, channel)
250-
.onErrorMap(
251-
FailedRequestException::isNotFound,
252-
throwable -> new InvalidParameterException(
253-
String.format("Requested version %s is not available", version))
254-
)
255-
.switchIfEmpty(Mono.error(() -> new InvalidParameterException(
256-
String.format("No build found for version %s with channel '%s'. Perhaps try with a different channel: %s",
257-
version, channel, channelsExcept(channel))
258-
)))
259-
.flatMap(resolvedBuild -> download(client, project, version, resolvedBuild));
257+
return client.downloadLatestBuild(project, outputDirectory, downloadStatusHandler, version);
260258
}
261259
}
262260
else {
263-
return client.getLatestVersionBuild(project, channel)
264-
.switchIfEmpty(
265-
Mono.error(() -> new InvalidParameterException(
266-
String.format("No build found with channel '%s'. Perhaps try a different channel: %s",
267-
channel, channelsExcept(channel)
268-
)))
269-
)
270-
.flatMap(resolved -> download(client, project, resolved.getVersion(), resolved.getBuild()));
261+
return client.downloadLatest(project, outputDirectory, downloadStatusHandler);
271262
}
272263
}
273264

274-
private String channelsExcept(ReleaseChannel channel) {
275-
return Arrays.stream(ReleaseChannel.values())
276-
.filter(c -> !Objects.equals(c, channel))
277-
.map(ReleaseChannel::toString)
278-
.collect(Collectors.joining(", "));
279-
}
280-
281265
private static boolean isSpecificVersion(String version) {
282266
return version != null && !version.equalsIgnoreCase("latest");
283267
}
284268

285-
private @NotNull Mono<Result> download(PaperDownloadsClient client, String project, String v, Integer b) {
286-
return client.download(project, v, b, outputDirectory, Fetch.loggingDownloadStatusHandler(log))
287-
.map(serverJar ->
288-
Result.builder()
289-
.newManifest(
290-
PaperManifest.builder()
291-
.project(project)
292-
.minecraftVersion(v)
293-
.build(b)
294-
.files(Collections.singleton(Manifests.relativize(outputDirectory, serverJar)))
295-
.build()
296-
)
297-
.serverJar(serverJar)
298-
.version(v)
299-
.build()
300-
);
301-
}
302-
303269
private Result downloadCustom(URI downloadUrl) {
304270
try (SharedFetch sharedFetch = Fetch.sharedFetch("install-paper", sharedFetchArgs.options())) {
305271
return sharedFetch

0 commit comments

Comments
 (0)