First working version.
This commit is contained in:
parent
8cfdf32bc0
commit
359d1aa1b8
|
@ -118,12 +118,10 @@ public class MainViewController {
|
|||
throwable.printStackTrace();
|
||||
return new ArrayList<>();
|
||||
})
|
||||
.thenAccept(newServers -> {
|
||||
Platform.runLater(() -> {
|
||||
.thenAccept(newServers -> Platform.runLater(() -> {
|
||||
this.servers.clear();
|
||||
this.servers.addAll(newServers);
|
||||
});
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
@ -150,7 +148,20 @@ public class MainViewController {
|
|||
|
||||
@FXML
|
||||
public void play() {
|
||||
|
||||
Profile profile = this.selectedProfile.get();
|
||||
Server server = this.selectedServer.get();
|
||||
VersionFetcher.INSTANCE.ensureVersionIsDownloaded(profile.getClientVersion())
|
||||
.thenAccept(path -> {
|
||||
try {
|
||||
Process p = new ProcessBuilder()
|
||||
.command("java", "-jar", path.toAbsolutePath().toString())
|
||||
.directory(Launcher.BASE_DIR.toFile())
|
||||
.inheritIO()
|
||||
.start();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void selectProfile(ProfileView view) {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package nl.andrewl.aos2_launcher;
|
||||
|
||||
public class SystemVersionValidator {
|
||||
private static final String os = System.getProperty("os.name").trim().toLowerCase();
|
||||
private static final String arch = System.getProperty("os.arch").trim().toLowerCase();
|
||||
|
||||
private static final boolean OS_WINDOWS = os.contains("win");
|
||||
private static final boolean OS_MAC = os.contains("mac");
|
||||
private static final boolean OS_LINUX = os.contains("nix") || os.contains("nux") || os.contains("aix");
|
||||
|
||||
private static final boolean ARCH_X86 = arch.equals("x86");
|
||||
private static final boolean ARCH_X86_64 = arch.equals("x86_64");
|
||||
private static final boolean ARCH_AMD64 = arch.equals("amd64");
|
||||
private static final boolean ARCH_AARCH64 = arch.equals("aarch64");
|
||||
private static final boolean ARCH_ARM = arch.equals("arm");
|
||||
private static final boolean ARCH_ARM32 = arch.equals("arm32");
|
||||
|
||||
public static String getPreferredVersionSuffix() {
|
||||
if (OS_LINUX) {
|
||||
if (ARCH_AARCH64) return "linux-aarch64";
|
||||
if (ARCH_AMD64) return "linux-amd64";
|
||||
if (ARCH_ARM) return "linux-arm";
|
||||
if (ARCH_ARM32) return "linux-arm32";
|
||||
} else if (OS_MAC) {
|
||||
if (ARCH_AARCH64) return "macos-aarch64";
|
||||
if (ARCH_X86_64) return "macos-x86_64";
|
||||
} else if (OS_WINDOWS) {
|
||||
if (ARCH_AARCH64) return "windows-aarch64";
|
||||
if (ARCH_AMD64) return "windows-amd64";
|
||||
if (ARCH_X86) return "windows-x86";
|
||||
}
|
||||
System.err.println("Couldn't determine the preferred OS/ARCH version. Defaulting to windows-amd64.");
|
||||
return "windows-amd64";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package nl.andrewl.aos2_launcher;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import nl.andrewl.aos2_launcher.model.ClientVersionRelease;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class VersionFetcher {
|
||||
private static final String BASE_GITHUB_URL = "https://api.github.com/repos/andrewlalis/ace-of-shades-2";
|
||||
|
||||
public static final VersionFetcher INSTANCE = new VersionFetcher();
|
||||
|
||||
private final List<ClientVersionRelease> availableReleases;
|
||||
|
||||
private final HttpClient httpClient = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build();
|
||||
private boolean loaded = false;
|
||||
private CompletableFuture<List<ClientVersionRelease>> activeReleaseFetchFuture;
|
||||
|
||||
public VersionFetcher() {
|
||||
this.availableReleases = new ArrayList<>();
|
||||
System.out.println(System.getProperty("os.name"));
|
||||
System.out.println(System.getProperty("os.arch"));
|
||||
}
|
||||
|
||||
public CompletableFuture<ClientVersionRelease> getRelease(String versionTag) {
|
||||
return getAvailableReleases().thenApply(releases -> releases.stream()
|
||||
.filter(r -> r.tag().equals(versionTag))
|
||||
.findFirst().orElse(null));
|
||||
}
|
||||
|
||||
public CompletableFuture<List<ClientVersionRelease>> getAvailableReleases() {
|
||||
if (loaded) {
|
||||
return CompletableFuture.completedFuture(Collections.unmodifiableList(availableReleases));
|
||||
}
|
||||
|
||||
System.out.println("Fetching the list of available releases...");
|
||||
return fetchReleasesFromGitHub();
|
||||
}
|
||||
|
||||
private CompletableFuture<List<ClientVersionRelease>> fetchReleasesFromGitHub() {
|
||||
if (activeReleaseFetchFuture != null) return activeReleaseFetchFuture;
|
||||
HttpRequest req = HttpRequest.newBuilder(URI.create(BASE_GITHUB_URL + "/releases"))
|
||||
.timeout(Duration.ofSeconds(3))
|
||||
.GET()
|
||||
.build();
|
||||
activeReleaseFetchFuture = httpClient.sendAsync(req, HttpResponse.BodyHandlers.ofInputStream())
|
||||
.thenApplyAsync(resp -> {
|
||||
if (resp.statusCode() == 200) {
|
||||
JsonArray releasesArray = new Gson().fromJson(new InputStreamReader(resp.body()), JsonArray.class);
|
||||
availableReleases.clear();
|
||||
for (var element : releasesArray) {
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject obj = element.getAsJsonObject();
|
||||
String tag = obj.get("tag_name").getAsString();
|
||||
String apiUrl = obj.get("url").getAsString();
|
||||
String assetsUrl = obj.get("assets_url").getAsString();
|
||||
OffsetDateTime publishedAt = OffsetDateTime.parse(obj.get("published_at").getAsString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
|
||||
LocalDateTime localPublishedAt = publishedAt.atZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime();
|
||||
availableReleases.add(new ClientVersionRelease(tag, apiUrl, assetsUrl, localPublishedAt));
|
||||
}
|
||||
}
|
||||
availableReleases.sort(Comparator.comparing(ClientVersionRelease::publishedAt).reversed());
|
||||
loaded = true;
|
||||
return availableReleases;
|
||||
} else {
|
||||
throw new RuntimeException("Error while requesting releases.");
|
||||
}
|
||||
});
|
||||
return activeReleaseFetchFuture;
|
||||
}
|
||||
|
||||
public List<String> getDownloadedVersions() {
|
||||
try (var s = Files.list(Launcher.VERSIONS_DIR)) {
|
||||
return s.filter(this::isVersionFile)
|
||||
.map(this::extractVersion)
|
||||
.toList();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<Path> ensureVersionIsDownloaded(String versionTag) {
|
||||
try (var s = Files.list(Launcher.VERSIONS_DIR)) {
|
||||
Optional<Path> optionalFile = s.filter(f -> isVersionFile(f) && versionTag.equals(extractVersion(f)))
|
||||
.findFirst();
|
||||
if (optionalFile.isPresent()) return CompletableFuture.completedFuture(optionalFile.get());
|
||||
} catch (IOException e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
return getRelease(versionTag)
|
||||
.thenComposeAsync(this::downloadVersion);
|
||||
}
|
||||
|
||||
private CompletableFuture<Path> downloadVersion(ClientVersionRelease release) {
|
||||
System.out.println("Downloading version " + release.tag());
|
||||
HttpRequest req = HttpRequest.newBuilder(URI.create(release.assetsUrl()))
|
||||
.GET().timeout(Duration.ofSeconds(3)).build();
|
||||
CompletableFuture<JsonObject> downloadUrlFuture = httpClient.sendAsync(req, HttpResponse.BodyHandlers.ofInputStream())
|
||||
.thenApplyAsync(resp -> {
|
||||
if (resp.statusCode() == 200) {
|
||||
JsonArray assetsArray = new Gson().fromJson(new InputStreamReader(resp.body()), JsonArray.class);
|
||||
String preferredVersionSuffix = SystemVersionValidator.getPreferredVersionSuffix();
|
||||
String regex = "aos2-client-\\d+\\.\\d+\\.\\d+-" + preferredVersionSuffix + "\\.jar";
|
||||
for (var asset : assetsArray) {
|
||||
JsonObject assetObj = asset.getAsJsonObject();
|
||||
String name = assetObj.get("name").getAsString();
|
||||
if (name.matches(regex)) {
|
||||
System.out.println("Found matching asset: " + name);
|
||||
return assetObj;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Couldn't find a matching release asset.");
|
||||
} else {
|
||||
throw new RuntimeException("Error while requesting release assets.");
|
||||
}
|
||||
});
|
||||
return downloadUrlFuture.thenComposeAsync(asset -> {
|
||||
String url = asset.get("browser_download_url").getAsString();
|
||||
String fileName = asset.get("name").getAsString();
|
||||
HttpRequest downloadRequest = HttpRequest.newBuilder(URI.create(url))
|
||||
.GET().timeout(Duration.ofMinutes(5)).build();
|
||||
Path file = Launcher.VERSIONS_DIR.resolve(fileName);
|
||||
System.out.printf("Downloading %s to %s.%n", fileName, file.toAbsolutePath());
|
||||
return httpClient.sendAsync(downloadRequest, HttpResponse.BodyHandlers.ofFile(file))
|
||||
.thenApplyAsync(resp -> {
|
||||
System.out.println(resp);
|
||||
return resp.body();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isVersionDownloaded(String versionTag) {
|
||||
return getDownloadedVersions().contains(versionTag);
|
||||
}
|
||||
|
||||
private boolean isVersionFile(Path p) {
|
||||
return Files.isRegularFile(p) && p.getFileName().toString()
|
||||
.matches("aos2-client-\\d+\\.\\d+\\.\\d+-.+\\.jar");
|
||||
}
|
||||
|
||||
private String extractVersion(Path file) {
|
||||
Pattern pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+");
|
||||
Matcher matcher = pattern.matcher(file.getFileName().toString());
|
||||
if (matcher.find()) {
|
||||
return "v" + matcher.group();
|
||||
}
|
||||
throw new IllegalArgumentException("File doesn't contain a valid version pattern.");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package nl.andrewl.aos2_launcher.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
public record ClientVersionRelease (
|
||||
String tag,
|
||||
String apiUrl,
|
||||
String assetsUrl,
|
||||
LocalDateTime publishedAt
|
||||
) {}
|
|
@ -11,6 +11,8 @@ import javafx.scene.Parent;
|
|||
import javafx.scene.control.*;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Window;
|
||||
import nl.andrewl.aos2_launcher.VersionFetcher;
|
||||
import nl.andrewl.aos2_launcher.model.ClientVersionRelease;
|
||||
import nl.andrewl.aos2_launcher.model.Profile;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -41,8 +43,15 @@ public class EditProfileDialog extends Dialog<Profile> {
|
|||
.or(clientVersionChoiceBox.valueProperty().isNull());
|
||||
nameField.setText(profile.getName());
|
||||
descriptionTextArea.setText(profile.getDescription());
|
||||
clientVersionChoiceBox.setItems(FXCollections.observableArrayList("v1.2.0", "v1.3.0", "v1.4.0"));
|
||||
clientVersionChoiceBox.setValue(profile.getClientVersion());
|
||||
VersionFetcher.INSTANCE.getAvailableReleases().thenAccept(releases -> {
|
||||
Platform.runLater(() -> {
|
||||
clientVersionChoiceBox.setItems(FXCollections.observableArrayList(releases.stream().map(ClientVersionRelease::tag).toList()));
|
||||
String lastRelease = releases.size() == 0 ? null : releases.get(0).tag();
|
||||
if (lastRelease != null) {
|
||||
clientVersionChoiceBox.setValue(lastRelease);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
DialogPane pane = new DialogPane();
|
||||
pane.setContent(parent);
|
||||
|
|
Loading…
Reference in New Issue