package io.gitlab.jfronny.inceptum.common; import gsoncompile.extensions.io.gitlab.jfronny.inceptum.common.model.inceptum.UpdateMetadata.GC_UpdateMetadata; import gsoncompile.extensions.io.gitlab.jfronny.inceptum.common.model.inceptum.WrapperConfig.GC_WrapperConfig; import io.gitlab.jfronny.commons.OSUtils; import io.gitlab.jfronny.inceptum.common.api.MavenApi; import io.gitlab.jfronny.inceptum.common.model.inceptum.*; import io.gitlab.jfronny.inceptum.common.model.maven.*; import org.jetbrains.annotations.Nullable; import org.xml.sax.SAXException; import javax.xml.stream.XMLStreamException; import java.io.*; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.function.Consumer; import java.util.stream.Stream; public class Updater { public static final String PROJECT_MAVEN = "https://maven.frohnmeyer-wds.de/artifacts/"; private static final String ARTIFACTS_URL = "https://pages.frohnmeyer-wds.de/JfMods/Inceptum/artifacts/"; private static final String STABLE_URL = "https://pages.frohnmeyer-wds.de/JfMods/Inceptum/stable/"; public static UpdateMetadata getUpdate(boolean versionCompare, boolean checkEnv) throws UpdateCheckException { return Updater.check(InceptumConfig.channel, versionCompare, checkEnv, channel -> { Utils.LOGGER.error("No stable version was found, switching to experimental channel"); InceptumConfig.channel = channel; InceptumConfig.saveConfig(); }); } public static void update(UpdateMetadata source, boolean relaunch) throws IOException, URISyntaxException { Utils.LOGGER.info("Downloading version " + source.version); WrapperConfig config = new WrapperConfig( new LinkedHashSet<>(), new LinkedHashSet<>(source.repositories), new HashMap<>() ); source.natives.forEach((k, v) -> config.natives[k] = new LinkedHashSet<>(v)); DependencyNode node = downloadLibrary(source.repositories, "io.gitlab.jfronny.inceptum:launcher-dist:" + source.version, config.libraries); Utils.LOGGER.info("Downloaded Dependencies:\n" + node); List currentLibraries = new LinkedList<>(config.libraries); if (source.natives.containsKey(Utils.currentFlavor)) { Set natives = new LinkedHashSet<>(); for (String lib : source.natives[Utils.currentFlavor]) { downloadLibrary(source.repositories, lib, natives); } currentLibraries.addAll(natives); config.natives[Utils.currentFlavor] = natives; } GC_WrapperConfig.write(config, MetaHolder.WRAPPER_CONFIG_PATH); if (relaunch) { Runtime.runtime.addShutdownHook(new Thread(() -> { try { new ProcessBuilder(OSUtils.jvmBinary, "-cp", buildClasspath(currentLibraries.stream()) .map(Path::toString) .join(File.pathSeparatorChar) ).inheritIO().start(); } catch (IOException e) { Utils.LOGGER.error("Could not relaunch", e); } })); } } public static List getLaunchClasspath(WrapperConfig wrapperConfig) throws IOException, URISyntaxException { Set natives = wrapperConfig.natives[Utils.currentFlavor]; if (natives == null) natives = new LinkedHashSet<>(); Set libs = wrapperConfig.libraries; if (libs == null) libs = new LinkedHashSet<>(); boolean configChanged = false; for (String lib : libs) { Path p = ArtifactMeta.parse(lib).localPath; if (!Files.exists(p)) { configChanged = true; downloadLibrary(wrapperConfig.repositories, lib, libs); } } for (String lib : natives) { Path p = ArtifactMeta.parse(lib).localPath; if (!Files.exists(p)) { configChanged = true; downloadLibrary(wrapperConfig.repositories, lib, natives); } } if (configChanged) GC_WrapperConfig.write(wrapperConfig, MetaHolder.WRAPPER_CONFIG_PATH); return buildClasspath(libs.stream().concat(natives.stream())).toList(); } private static Stream buildClasspath(Stream libraries) { return libraries.map(ArtifactMeta::parse).map(ArtifactMeta::getLocalPath); } private static DependencyNode downloadLibrary(Set repositories, final String artifact, Set libraries) throws IOException, URISyntaxException { List suppressed = new LinkedList<>(); for (String repository : Stream.of(PROJECT_MAVEN).concat(repositories.stream())) { ArtifactMeta meta; try { meta = MavenApi.getMetadata(repository, artifact); } catch (FileNotFoundException ignored) { meta = ArtifactMeta.parse(artifact); } catch (IOException | URISyntaxException | SAXException e) { throw new IOException("Could not download artifact from " + repository, e); } Pom pom; try { pom = MavenApi.getPom(repository, meta); } catch (FileNotFoundException notFound) { suppressed.add(notFound); continue; } catch (IOException | URISyntaxException | XMLStreamException | SAXException e) { throw new IOException("Could not download artifact " + meta.mavenNotation + " from " + repository, e); } Set dependencies = new LinkedHashSet<>(); if (pom.dependencies != null) { for (MavenDependency dependency : pom.dependencies) { String mvnName = dependency.groupId + ":" + dependency.artifactId + ":" + dependency.version; dependencies.add(downloadLibrary(repositories, mvnName, libraries)); } } MavenApi.downloadLibrary(repository, meta); libraries.add(artifact); return new DependencyNode(artifact, dependencies); } IOException e = new IOException("Could not find any repository containing the artifact " + artifact + " (searched: " + String.join(", ", repositories) + ")"); for (FileNotFoundException ex : suppressed) e.addSuppressed(ex); throw e; } public static @Nullable UpdateMetadata check(UpdateChannel channel, boolean versionCompare, boolean checkEnv, Consumer channelInvalid) throws UpdateCheckException { try { UpdateMetadata experimental = Net.downloadObject(ARTIFACTS_URL + "version.json", json -> GC_UpdateMetadata.read(json)); UpdateMetadata stable = null; try { stable = Net.downloadObject(STABLE_URL + "version.json", json -> GC_UpdateMetadata.read(json)); } catch (Throwable ignored) {} if (stable == null && channel == UpdateChannel.Stable) { channel = UpdateChannel.CI; channelInvalid.accept(channel); } UpdateMetadata info = switch (channel) { case CI -> experimental; case Stable -> stable; }; if (checkEnv) { if (info.jvm > Runtime.version().feature()) throw new UpdateCheckException("A newer JVM is required to use the latest inceptum version. Please update!", "Outdated Java"); if (info.wrapperVersion != BuildMetadata.WRAPPER_VERSION) throw new UpdateCheckException("A different version of the Inceptum Wrapper is required for this update!", "Mismatched Wrapper"); } if (versionCompare) { Utils.LOGGER.info("Latest version is " + info.version + ", current is " + BuildMetadata.VERSION); if (BuildMetadata.BUILD_TIME >= info.buildTime) { Utils.LOGGER.info("Up-to-date"); return null; } } return info; } catch (IOException e) { Utils.LOGGER.error("Could not check for updates", e); } return null; } public static String getShadowJarUrl(UpdateChannel channel) { return switch (channel) { case CI -> ARTIFACTS_URL; case Stable -> STABLE_URL; } + "/Inceptum-" + Utils.currentFlavor + ".jar"; } public static class UpdateCheckException extends Exception { public final String message; public final String title; public UpdateCheckException(String message, String title) { super(message); this.message = message; this.title = title; } } }