Skip to content

Commit 2025819

Browse files
committed
fabric: use Fabric installer to retrieve server launcher
Also - validate fabric launcher jar and retry
1 parent f6aec2f commit 2025819

File tree

1 file changed

+55
-1
lines changed

1 file changed

+55
-1
lines changed

src/main/java/me/itzg/helpers/fabric/FabricMetaClient.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
package me.itzg.helpers.fabric;
22

3+
import java.io.BufferedReader;
34
import java.io.IOException;
5+
import java.nio.file.Files;
46
import java.nio.file.Path;
57
import java.time.Duration;
68
import java.util.List;
9+
import java.util.Properties;
710
import java.util.function.Predicate;
811
import lombok.Setter;
12+
import lombok.extern.slf4j.Slf4j;
913
import me.itzg.helpers.errors.GenericException;
14+
import me.itzg.helpers.errors.InvalidContentException;
15+
import me.itzg.helpers.files.IoStreams;
1016
import me.itzg.helpers.http.FileDownloadStatusHandler;
1117
import me.itzg.helpers.http.SharedFetch;
1218
import me.itzg.helpers.http.UriBuilder;
1319
import org.jetbrains.annotations.NotNull;
1420
import org.jetbrains.annotations.Nullable;
1521
import reactor.core.publisher.Mono;
22+
import reactor.core.scheduler.Schedulers;
1623
import reactor.util.retry.Retry;
1724

25+
@Slf4j
1826
public class FabricMetaClient {
1927

2028
private final SharedFetch sharedFetch;
@@ -32,7 +40,7 @@ public class FabricMetaClient {
3240
private Duration retryMinBackoff = Duration.ofMillis(500);
3341

3442
@Setter
35-
private int downloadRetryMaxAttempts = 5;
43+
private int downloadRetryMaxAttempts = 10;
3644
@Setter
3745
private Duration downloadRetryMinBackoff = Duration.ofMillis(500);
3846

@@ -146,9 +154,55 @@ public Mono<Path> downloadLauncher(
146154
.handleStatus(statusHandler)
147155
.assemble()
148156
.retryWhen(Retry.backoff(downloadRetryMaxAttempts, downloadRetryMinBackoff).filter(IOException.class::isInstance))
157+
.flatMap(this::validateLauncherJar)
158+
.retryWhen(Retry.backoff(downloadRetryMaxAttempts, downloadRetryMinBackoff).filter(InvalidContentException.class::isInstance))
149159
.checkpoint("downloadLauncher");
150160
}
151161

162+
private Mono<Path> validateLauncherJar(Path path) {
163+
return Mono.fromCallable(() -> {
164+
log.debug("Validating Fabric launcher file {}", path);
165+
166+
if (!path.toFile().isFile()) {
167+
throw new InvalidContentException("Downloaded launcher jar is not a file");
168+
}
169+
try {
170+
final Properties installProperties = IoStreams.readFileFromZip(path, "install.properties", in -> {
171+
Properties p = new Properties();
172+
p.load(in);
173+
return p;
174+
}
175+
);
176+
if (installProperties == null) {
177+
debugDownloadedContent(path);
178+
throw new InvalidContentException("Downloaded launcher jar does not contain an install.properties");
179+
}
180+
if (!installProperties.containsKey("game-version")) {
181+
debugDownloadedContent(path);
182+
throw new InvalidContentException("Downloaded launcher jar does not contain a valid install.properties");
183+
}
184+
} catch (IOException e) {
185+
debugDownloadedContent(path);
186+
throw new InvalidContentException("Downloaded launcher jar could not be read as a jar/zip", e);
187+
}
188+
189+
return path;
190+
})
191+
.subscribeOn(Schedulers.boundedElastic());
192+
}
193+
194+
private static void debugDownloadedContent(Path path) {
195+
if (log.isDebugEnabled()) {
196+
try (BufferedReader reader = Files.newBufferedReader(path)) {
197+
final char[] buf = new char[100];
198+
final int amount = reader.read(buf);
199+
log.debug("Downloaded launcher jar content starts with: {}", new String(buf, 0, amount));
200+
} catch (IOException e) {
201+
throw new GenericException("Failed to read downloaded launcher jar for debugging", e);
202+
}
203+
}
204+
}
205+
152206
@NotNull
153207
private static Mono<String> findFirst(List<VersionEntry> versionEntries, Predicate<VersionEntry> condition
154208
) {

0 commit comments

Comments
 (0)