More attempts at fixing the wrapper (also get rid of slf4j)

This commit is contained in:
Johannes Frohnmeyer 2022-09-06 21:20:59 +02:00
parent f1d1979977
commit c7aafad507
Signed by: Johannes
GPG Key ID: E76429612C2929F4
18 changed files with 196 additions and 182 deletions

View File

@ -28,8 +28,7 @@ println("Using Inceptum Build Script $version")
val lwjglVersion by extra("3.3.1")
val imguiVersion by extra("1.86.4")
val logbackVersion by extra("1.3.0-alpha15")
val jfCommonsVersion by extra("2022.9.4+14-8-34")
val jfCommonsVersion by extra("2022.9.6+19-13-24")
val jgitVersion by extra("6.2.0.202206071550-r")
val flavorProp: String by extra(if (project.hasProperty("flavor")) "${project.property("flavor")}" else "custom")
if (flavorProp != "custom" && flavorProp != "maven" && flavorProp != "fat" && flavorProp != "windows" && flavorProp != "linux" && flavorProp != "macos")

View File

@ -5,8 +5,6 @@ plugins {
dependencies {
api("io.gitlab.jfronny:commons:${rootProject.extra["jfCommonsVersion"]}")
api("io.gitlab.jfronny:commons-gson:${rootProject.extra["jfCommonsVersion"]}")
implementation("io.gitlab.jfronny:commons-slf4j:${rootProject.extra["jfCommonsVersion"]}")
implementation("ch.qos.logback:logback-classic:${rootProject.extra["logbackVersion"]}")
}
val javaVersion by extra(project.java.targetCompatibility)

View File

@ -1,14 +1,21 @@
package io.gitlab.jfronny.inceptum.common;
import io.gitlab.jfronny.commons.HttpUtils;
import io.gitlab.jfronny.commons.log.Logger;
import io.gitlab.jfronny.commons.log.StdoutLogger;
import io.gitlab.jfronny.commons.serialize.gson.api.GsonHolder;
import java.io.IOException;
public class InceptumEnvironmentInitializer {
public static void initialize() throws IOException {
Logger.registerFactory(InceptumEnvironmentInitializer::defaultFactory);
HttpUtils.setUserAgent("jfmods/inceptum/" + BuildMetadata.VERSION);
GsonHolder.register();
ConfigHolder.load();
}
public static Logger defaultFactory(String name) {
return new StdoutLogger(name, true, true, true);
}
}

View File

@ -1,28 +0,0 @@
package io.gitlab.jfronny.inceptum.common;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import java.time.Instant;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class MapAppender extends AppenderBase<ILoggingEvent> {
public static final ConcurrentMap<String, ILoggingEvent> EVENT_MAP = new ConcurrentHashMap<>();
public static final Set<String> LOG = new LinkedHashSet<>();
@Override
protected void append(ILoggingEvent event) {
EVENT_MAP.put(Instant.now().toString(), event);
LOG.add(event.getLevel().toString() +
" | " +
Instant.now().toString() +
" | [" +
event.getThreadName() +
"] " +
event.getFormattedMessage());
}
}

View File

@ -7,8 +7,7 @@ import io.gitlab.jfronny.inceptum.common.api.GitlabApi;
import io.gitlab.jfronny.inceptum.common.api.MavenApi;
import io.gitlab.jfronny.inceptum.common.model.gitlab.*;
import io.gitlab.jfronny.inceptum.common.model.inceptum.*;
import io.gitlab.jfronny.inceptum.common.model.maven.MavenDependency;
import io.gitlab.jfronny.inceptum.common.model.maven.Pom;
import io.gitlab.jfronny.inceptum.common.model.maven.*;
import org.jetbrains.annotations.Nullable;
import org.xml.sax.SAXException;
@ -48,15 +47,16 @@ public class Updater {
WrapperConfig config = new WrapperConfig();
config.natives = new HashMap<>();
config.libraries = new LinkedList<>();
config.repositories = new LinkedList<>(source.repositories);
source.natives.forEach((k, v) -> config.natives.put(k, new LinkedList<>(v)));
config.libraries = new LinkedHashSet<>();
config.repositories = new LinkedHashSet<>(source.repositories);
source.natives.forEach((k, v) -> config.natives.put(k, new LinkedHashSet<>(v)));
downloadLibrary(source.repositories, "io.gitlab.jfronny.inceptum:launcher-dist:" + source.version, config.libraries);
DependencyNode node = downloadLibrary(source.repositories, "io.gitlab.jfronny.inceptum:launcher-dist:" + source.version, config.libraries);
Utils.LOGGER.info("Downloaded Dependencies:\n" + node);
List<String> currentLibraries = new LinkedList<>(config.libraries);
if (source.natives.containsKey(Utils.getCurrentFlavor())) {
List<String> natives = new LinkedList<>();
Set<String> natives = new LinkedHashSet<>();
for (String lib : source.natives.get(Utils.getCurrentFlavor())){
downloadLibrary(source.repositories, lib, natives);
}
@ -83,10 +83,10 @@ public class Updater {
}
public static List<Path> getLaunchClasspath(WrapperConfig wrapperConfig) throws IOException, URISyntaxException {
List<String> natives = wrapperConfig.natives.get(Utils.getCurrentFlavor());
if (natives == null) natives = new LinkedList<>();
List<String> libs = wrapperConfig.libraries;
if (libs == null) libs = new LinkedList<>();
Set<String> natives = wrapperConfig.natives.get(Utils.getCurrentFlavor());
if (natives == null) natives = new LinkedHashSet<>();
Set<String> libs = wrapperConfig.libraries;
if (libs == null) libs = new LinkedHashSet<>();
boolean configChanged = false;
@ -114,7 +114,7 @@ public class Updater {
return libraries.map(Updater::artifactToPath);
}
private static void downloadLibrary(List<String> repositories, final String artifact, List<String> libraries) throws IOException, URISyntaxException {
private static DependencyNode downloadLibrary(Set<String> repositories, final String artifact, Set<String> libraries) throws IOException, URISyntaxException {
List<Exception> exceptions = new LinkedList<>();
for (String repository : Stream.concat(Stream.of(PROJECT_MAVEN), repositories.stream()).toList()) {
Pom pom;
@ -124,15 +124,16 @@ public class Updater {
exceptions.add(new Exception("Could not download artifact from " + repository, e));
continue;
}
Set<DependencyNode> dependencies = new LinkedHashSet<>();
if (pom.dependencies != null) {
for (MavenDependency dependency : pom.dependencies) {
String mvnName = dependency.groupId + ":" + dependency.artifactId + ":" + dependency.version;
downloadLibrary(repositories, mvnName, libraries);
dependencies.add(downloadLibrary(repositories, mvnName, libraries));
}
}
MavenApi.downloadLibrary(repository, artifact);
libraries.add(artifact);
return;
return new DependencyNode(pom, dependencies);
}
IOException exception = new IOException("Could not find any repository containing the artifact " + artifact + " (searched: " + String.join(", ", repositories) + ")");
for (Exception e : exceptions) {

View File

@ -18,6 +18,7 @@ import java.util.*;
public class MavenApi {
private static final DocumentBuilder FACTORY;
private static final Set<String> RUNTIME_SCOPES = Set.of("compile", "runtime");
static {
try {
@ -127,7 +128,7 @@ public class MavenApi {
case "scope" -> {
hasScope = true;
result.scope = node.getTextContent();
if (!result.scope.equals("runtime")) return null;
if (!RUNTIME_SCOPES.contains(result.scope)) return null;
}
case "optional" -> {
if (node.getTextContent().equals("true")) return null;
@ -142,14 +143,16 @@ public class MavenApi {
// The required modules are explicit dependencies of launcher-imgui anyway
return null;
}
throw new IOException("Pom lacks version");
throw new IOException("Dependency " + result.groupId + ":" + result.artifactId + " lacks version");
}
if (!hasScope) throw new IOException("Pom lacks scope");
return result;
}
private static boolean isWhitespace(Node node) {
return node.getNodeType() == Node.TEXT_NODE && node.getTextContent().isBlank();
if (node.getNodeType() == Node.TEXT_NODE && node.getTextContent().isBlank()) return true;
if (node.getNodeType() == Node.COMMENT_NODE) return true;
return false;
}
private static Iterable<Node> iterable(NodeList list) {

View File

@ -2,8 +2,7 @@ package io.gitlab.jfronny.inceptum.common.model.inceptum;
import io.gitlab.jfronny.commons.ComparableVersion;
import java.util.List;
import java.util.Map;
import java.util.*;
public class UpdateMetadata {
public Integer wrapperVersion;
@ -11,6 +10,6 @@ public class UpdateMetadata {
public Boolean isPublic;
public Boolean isRelease;
public Integer jvm;
public List<String> repositories;
public Map<String, List<String>> natives;
public Set<String> repositories;
public Map<String, Set<String>> natives;
}

View File

@ -1,10 +1,9 @@
package io.gitlab.jfronny.inceptum.common.model.inceptum;
import java.util.List;
import java.util.Map;
import java.util.*;
public class WrapperConfig {
public List<String> libraries;
public List<String> repositories;
public Map<String, List<String>> natives;
public Set<String> libraries;
public Set<String> repositories;
public Map<String, Set<String>> natives;
}

View File

@ -0,0 +1,46 @@
package io.gitlab.jfronny.inceptum.common.model.maven;
import java.util.*;
public class DependencyNode {
private final String name;
private final Set<DependencyNode> dependencies;
public DependencyNode(Pom pom, Set<DependencyNode> dependencies) {
Objects.requireNonNull(pom);
this.name = pom.groupId + ":" + pom.artifactId + ":" + pom.version;
this.dependencies = Objects.requireNonNull(dependencies);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DependencyNode that = (DependencyNode) o;
return name.equals(that.name) && dependencies.equals(that.dependencies);
}
@Override
public int hashCode() {
return Objects.hash(name, dependencies);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
generateTree(sb, "", "");
return sb.toString();
}
private void generateTree(StringBuilder sb, String prefix, String childrenPrefix) {
sb.append(prefix).append(name).append('\n');
for (Iterator<DependencyNode> it = dependencies.iterator(); it.hasNext();) {
DependencyNode next = it.next();
if (it.hasNext()) {
next.generateTree(sb, childrenPrefix + "├── ", childrenPrefix + "");
} else {
next.generateTree(sb, childrenPrefix + "└── ", childrenPrefix + " ");
}
}
}
}

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss.SSS} %boldCyan(%thread) %boldGreen(%logger{0}) %highlight(%level) %msg%n</pattern>
</layout>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>inceptum.log</file>
<append>false</append>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss.SSS} %thread %logger{0} %level %msg%n</pattern>
</layout>
</appender>
<appender name="MEMORY" class="io.gitlab.jfronny.inceptum.common.MapAppender">
</appender>
<root level="info">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
<appender-ref ref="MEMORY"/>
</root>
</configuration>

View File

@ -6,6 +6,7 @@ import imgui.flag.ImGuiConfigFlags;
import imgui.gl3.ImGuiImplGl3;
import imgui.glfw.ImGuiImplGlfw;
import io.gitlab.jfronny.commons.io.JFiles;
import io.gitlab.jfronny.commons.log.*;
import io.gitlab.jfronny.inceptum.common.model.inceptum.UpdateMetadata;
import io.gitlab.jfronny.inceptum.imgui.window.MainWindow;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
@ -29,6 +30,7 @@ import java.nio.file.Files;
import java.util.*;
public class GuiMain {
public static final MemoryLogger MEMLOG = new MemoryLogger("Inceptum");
public static final Set<Window> WINDOWS = new LinkedHashSet<>();
private static final ImGuiImplGlfw imGuiGlfw = new ImGuiImplGlfw();
private static final ImGuiImplGl3 imGuiGl3 = new ImGuiImplGl3();
@ -40,9 +42,9 @@ public class GuiMain {
public static void main(String[] args) throws IOException {
LauncherEnv.checkClassLoaderState();
LauncherEnv.initialize(new GuiEnvBackend());
Utils.LOGGER.info("Launching Inceptum v" + BuildMetadata.VERSION);
Utils.LOGGER.info("Loading from " + MetaHolder.BASE_PATH);
LauncherEnv.initialize(new GuiEnvBackend());
try {
main();
} finally {
@ -51,6 +53,7 @@ public class GuiMain {
}
public static void main() {
Logger.registerFactory(name -> new CompoundLogger(name, InceptumEnvironmentInitializer.defaultFactory(name), MEMLOG));
UpdateMetadata update = BuildMetadata.IS_PUBLIC ? Updater.getUpdate() : null;
AccountManager.loadAccounts();
Utils.LOGGER.info("Initializing UI");

View File

@ -5,9 +5,9 @@ import imgui.ImGui;
import java.util.Set;
public class LogWindow extends Window {
private final Set<String> source;
private final Iterable<String> source;
public LogWindow(Set<String> source) {
public LogWindow(Iterable<String> source) {
super("Log");
this.source = source;
}

View File

@ -78,7 +78,7 @@ public class MainWindow extends Window {
}
if (ImGui.beginMenu("Help")) {
if (ImGui.menuItem("About")) GuiMain.open(new AboutWindow());
if (ImGui.menuItem("Log")) GuiMain.open(new LogWindow(MapAppender.LOG));
if (ImGui.menuItem("Log")) GuiMain.open(new LogWindow(GuiMain.MEMLOG));
ImGui.checkbox("Debug Tools", debugTools);
ImGui.endMenu();
}

View File

@ -3,9 +3,11 @@ plugins {
}
application {
mainClass.set("io.gitlab.jfronny.inceptum.WrapperStrap")
mainClass.set("io.gitlab.jfronny.inceptum.wrapper.Wrapper")
}
dependencies {
implementation(project(":common"))
}
}
tasks.runShadow.get().workingDir = rootProject.projectDir

View File

@ -1,40 +0,0 @@
package io.gitlab.jfronny.inceptum;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.Set;
public class WrapperStrap {
public static final ClassLoader SYSTEM_LOADER = ClassLoader.getSystemClassLoader();
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, URISyntaxException {
System.out.println("Starting Inceptum Wrapper ClassLoader");
bootstrap(Set.of(new File(WrapperStrap.class.getProtectionDomain().getCodeSource().getLocation().toURI()).toPath()),
"io.gitlab.jfronny.inceptum.Wrapper",
args);
}
public static void bootstrap(Iterable<Path> jars, String mainClass, String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, URISyntaxException {
try (WrapperClassLoader loader = new WrapperClassLoader(jars, SYSTEM_LOADER)) {
Thread.currentThread().setContextClassLoader(loader);
wrapperInit(loader);
loader.loadClass(mainClass)
.getDeclaredMethod("main", String[].class)
.invoke(null, new Object[]{args});
}
}
private static void wrapperInit(ClassLoader loader) {
try {
loader.loadClass("io.gitlab.jfronny.inceptum.common.Utils")
.getMethod("wrapperInit", ClassLoader.class)
.invoke(null, SYSTEM_LOADER);
} catch (Throwable e) {
System.out.println("Could not perform wrapper init, utils class probably moved");
e.printStackTrace();
}
}
}

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.inceptum;
package io.gitlab.jfronny.inceptum.wrapper;
import io.gitlab.jfronny.commons.io.JFiles;
import org.jetbrains.annotations.Nullable;
@ -7,21 +7,24 @@ import java.io.Closeable;
import java.io.IOException;
import java.net.*;
import java.nio.file.*;
import java.security.*;
import java.security.cert.Certificate;
import java.util.*;
import java.util.stream.Stream;
public class WrapperClassLoader extends ClassLoader implements Closeable {
/**
* A ClassLoader that supports loading classes from Paths, which may be directories or jars
* Does not support modules
*/
public class NioClassLoader extends ClassLoader implements Closeable {
private final Set<Path> sources = new LinkedHashSet<>();
private final Set<Closeable> closeables = new HashSet<>();
private final ClassLoader parent;
static {
registerAsParallelCapable();
}
public WrapperClassLoader(Iterable<Path> jars, ClassLoader parent) throws IOException, URISyntaxException {
super(null);
this.parent = parent;
public NioClassLoader(Iterable<Path> jars, ClassLoader parent) throws IOException, URISyntaxException {
super(parent);
for (Path jar : jars) {
if (Files.isDirectory(jar)) {
sources.add(jar);
@ -35,44 +38,31 @@ public class WrapperClassLoader extends ClassLoader implements Closeable {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Path path = findClassSource(name);
if (path == null) return parent.loadClass(name);
Path path = findSource(name.replace('.', '/') + ".class");
if (path == null) throw new ClassNotFoundException(name);
byte[] clazz;
try {
clazz = Files.readAllBytes(path);
} catch (IOException e) {
throw new ClassNotFoundException("Could not find class", e);
throw new ClassNotFoundException(name, e);
}
return defineClass(name, clazz, 0, clazz.length);
CodeSource cs = new CodeSource(toUrl(path), (Certificate[]) null);
ProtectionDomain pd = new ProtectionDomain(cs, null);
return defineClass(name, clazz, 0, clazz.length, pd);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (findClassSource(name) == null || name.equals("io.gitlab.jfronny.inceptum.WrapperStrap"))
return parent.loadClass(name);
synchronized (this.getClassLoadingLock(name)) {
Class<?> c = this.findLoadedClass(name);
if (c == null) c = this.findClass(name);
if (resolve) this.resolveClass(c);
return c;
}
}
@Override
public URL getResource(String name) {
Path p = findSource(name);
if (p == null) return parent.getResource(name);
return toUrl(p);
protected URL findResource(String name) {
return toUrl(findSource(name));
}
@Override
protected Enumeration<URL> findResources(String name) {
Stream<URL> resources = findPaths(name).map(this::toUrl);
resources = Stream.concat(resources, parent.resources(name));
return Collections.enumeration(resources.toList());
return Collections.enumeration(findPaths(name).map(this::toUrl).toList());
}
private URL toUrl(Path path) {
if (path == null) return null;
try {
return path.toUri().toURL();
} catch (MalformedURLException e) {
@ -80,10 +70,6 @@ public class WrapperClassLoader extends ClassLoader implements Closeable {
}
}
private @Nullable Path findClassSource(String className) {
return findSource(className.replace('.', '/') + ".class");
}
private @Nullable Path findSource(String path) {
return findPaths(path).findFirst().orElse(null);
}
@ -93,9 +79,21 @@ public class WrapperClassLoader extends ClassLoader implements Closeable {
}
@Override
public void close() throws IOException {
public void close() {
List<IOException> exceptions = new LinkedList<>();
for (Closeable closeable : closeables) {
closeable.close();
try {
closeable.close();
} catch (IOException e) {
exceptions.add(e);
}
}
if (!exceptions.isEmpty()) {
RuntimeException ex = new RuntimeException("Could not properly close WrapperClassLoader");
for (IOException suppressed : exceptions) {
ex.addSuppressed(suppressed);
}
throw ex;
}
}
}

View File

@ -0,0 +1,59 @@
package io.gitlab.jfronny.inceptum.wrapper;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
public final class RuntimeEnv implements Runnable {
public static void start(Iterable<Path> jars, String mainClass, String[] args) throws IOException, URISyntaxException {
NioClassLoader loader = new NioClassLoader(jars, ClassLoader.getPlatformClassLoader());
Thread th = new Thread(new RuntimeEnv(loader, mainClass, args), getThreadName(mainClass));
th.setContextClassLoader(loader);
th.start();
}
private static String getThreadName(String className) {
String[] split = className.split("\\.");
return "[WS/" + split[split.length - 1] + "]";
}
private final NioClassLoader loader;
private final String mainClass;
private final String[] args;
private boolean ran = false;
private RuntimeEnv(NioClassLoader loader, String mainClass, String[] args) {
this.loader = loader;
this.mainClass = mainClass;
this.args = args;
}
@Override
public synchronized void run() {
if (ran) throw new IllegalStateException("Attempted to run environment which already finished executing");
ran = true;
try {
wrapperInit();
try {
loader.loadClass(mainClass)
.getDeclaredMethod("main", String[].class)
.invoke(null, new Object[]{args});
} catch (Exception e) {
throw new RuntimeException(e);
}
} finally {
loader.close();
}
}
private void wrapperInit() {
try {
loader.loadClass("io.gitlab.jfronny.inceptum.common.Utils")
.getMethod("wrapperInit", ClassLoader.class)
.invoke(null, ClassLoader.getSystemClassLoader());
} catch (Throwable e) {
System.out.println("Could not perform wrapper init, utils class probably moved");
e.printStackTrace();
}
}
}

View File

@ -1,4 +1,4 @@
package io.gitlab.jfronny.inceptum;
package io.gitlab.jfronny.inceptum.wrapper;
import io.gitlab.jfronny.commons.ComparableVersion;
import io.gitlab.jfronny.commons.io.JFiles;
@ -6,7 +6,6 @@ import io.gitlab.jfronny.inceptum.common.*;
import io.gitlab.jfronny.inceptum.common.model.inceptum.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
@ -14,30 +13,25 @@ import java.util.List;
import java.util.stream.Collectors;
public class Wrapper {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, URISyntaxException {
public static void main(String[] args) throws IOException, URISyntaxException {
System.out.println("Inceptum Wrapper v" + BuildMetadata.VERSION);
System.out.println("Loading from " + MetaHolder.BASE_PATH);
InceptumEnvironmentInitializer.initialize();
List<Path> classpath = getClasspath();
if (!BuildMetadata.IS_RELEASE) {
System.out.println("Using classpath: " + classpath.stream().map(Path::toString).collect(Collectors.joining("" + File.pathSeparator)));
}
System.out.println("Starting Inceptum ClassLoader");
WrapperStrap.bootstrap(classpath,
"io.gitlab.jfronny.inceptum.Inceptum",
args);
}
private static List<Path> getClasspath() throws IOException, URISyntaxException {
if (!Files.exists(MetaHolder.WRAPPER_CONFIG_PATH)) {
UpdateMetadata update = Updater.check(UpdateChannel.Stable, new ComparableVersion("0"), R::nop);
if (update == null) {
throw new FileNotFoundException("Could not identify a valid inceptum version. Are you connected to the internet?");
}
Updater.update(update, false);
if (!Files.exists(MetaHolder.WRAPPER_CONFIG_PATH))
if (!Files.exists(MetaHolder.WRAPPER_CONFIG_PATH)) {
throw new FileNotFoundException("Something went wrong while downloading the latest version.");
}
}
return Updater.getLaunchClasspath(JFiles.readObject(MetaHolder.WRAPPER_CONFIG_PATH, WrapperConfig.class));
List<Path> classpath = Updater.getLaunchClasspath(JFiles.readObject(MetaHolder.WRAPPER_CONFIG_PATH, WrapperConfig.class));
if (!BuildMetadata.IS_RELEASE) {
System.out.println("Using classpath: " + classpath.stream().map(Path::toString).collect(Collectors.joining("" + File.pathSeparator)));
}
System.out.println("Starting Inceptum ClassLoader");
RuntimeEnv.start(classpath, "io.gitlab.jfronny.inceptum.Inceptum", args);
}
}