diff --git a/LaunchServer/build.gradle b/LaunchServer/build.gradle index 303c086f..d03cc45d 100644 --- a/LaunchServer/build.gradle +++ b/LaunchServer/build.gradle @@ -80,6 +80,7 @@ pack project(':LauncherAPI') bundle group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty'] bundle group: 'io.netty', name: 'netty-transport-classes-epoll', version: rootProject['verNetty'] bundle group: 'io.netty', name: 'netty-transport-native-epoll', version: rootProject['verNetty'], classifier: 'linux-x86_64' + bundle group: 'io.netty', name: 'netty-transport-native-epoll', version: rootProject['verNetty'], classifier: 'linux-aarch_64' bundle group: 'org.slf4j', name: 'slf4j-api', version: rootProject['verSlf4j'] bundle group: 'com.mysql', name: 'mysql-connector-j', version: rootProject['verMySQLConn'] bundle group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: rootProject['verMariaDBConn'] diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java index 3745386f..a117dd48 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java @@ -70,6 +70,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab /** * The path to the folder with updates/webroot */ + @Deprecated public final Path updatesDir; // Constant paths @@ -80,7 +81,6 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab /** * The path to the folder with profiles */ - public final Path profilesDir; public final Path tmpDir; public final Path modulesDir; public final Path launcherModulesDir; @@ -119,8 +119,6 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab private final Logger logger = LogManager.getLogger(); public final int shardId; public LaunchServerConfig config; - // Updates and profiles - private volatile Set profilesList; public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, KeyAgreementManager keyAgreementManager, CommandHandler commandHandler, CertificateManager certificateManager, int shardId) throws IOException { this.dir = directories.dir; @@ -129,7 +127,6 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La this.config = config; this.launchServerConfigManager = launchServerConfigManager; this.modulesManager = modulesManager; - this.profilesDir = directories.profilesDir; this.updatesDir = directories.updatesDir; this.keyAgreementManager = keyAgreementManager; this.commandHandler = commandHandler; @@ -326,12 +323,14 @@ public void close() throws Exception { logger.info("LaunchServer stopped"); } + @Deprecated public Set getProfiles() { - return profilesList; + return config.profileProvider.getProfiles(); } + @Deprecated public void setProfiles(Set profilesList) { - this.profilesList = Collections.unmodifiableSet(profilesList); + throw new UnsupportedOperationException(); } public void rebindNettyServerSocket() { @@ -358,14 +357,10 @@ public void run() { CommonHelper.newThread("Profiles and updates sync", true, () -> { try { // Sync profiles dir - if (!IOHelper.isDir(profilesDir)) - Files.createDirectory(profilesDir); syncProfilesDir(); // Sync updates dir - if (!IOHelper.isDir(updatesDir)) - Files.createDirectory(updatesDir); - updatesManager.readUpdatesDir(); + config.updatesProvider.syncInitially(); modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this)); @@ -402,12 +397,7 @@ public void syncLauncherBinaries() throws IOException { public void syncProfilesDir() throws IOException { logger.info("Syncing profiles dir"); - List newProfies = new LinkedList<>(); - IOHelper.walk(profilesDir, new ProfilesFileVisitor(newProfies), false); - - // Sort and set new profiles - newProfies.sort(Comparator.comparing(a -> a)); - profilesList = Set.copyOf(newProfies); + config.profileProvider.sync(); if (config.netty.sendProfileUpdatesEvent) { sendUpdateProfilesEvent(); } @@ -422,7 +412,7 @@ private void sendUpdateProfilesEvent() { if (client == null || !client.isAuth) { return; } - ProfilesRequestEvent event = new ProfilesRequestEvent(ProfilesResponse.getListVisibleProfiles(this, client)); + ProfilesRequestEvent event = new ProfilesRequestEvent(config.profileProvider.getProfiles(client)); event.requestUUID = RequestEvent.eventUUID; handler.service.sendObject(ch, event); }); @@ -468,38 +458,11 @@ public interface LaunchServerConfigManager { void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException; } - private static final class ProfilesFileVisitor extends SimpleFileVisitor { - private final Collection result; - private final Logger logger = LogManager.getLogger(); - - private ProfilesFileVisitor(Collection result) { - this.result = result; - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - logger.info("Syncing '{}' profile", IOHelper.getFileName(file)); - - // Read profile - ClientProfile profile; - try (BufferedReader reader = IOHelper.newReader(file)) { - profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class); - } - profile.verify(); - profile.setProfileFilePath(file); - - // Add SIGNED profile to result list - result.add(profile); - return super.visitFile(file, attrs); - } - } - public static class LaunchServerDirectories { - public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles", + public static final String UPDATES_NAME = "updates", TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries", LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", LAUNCHERPACK_NAME = "launcher-pack", KEY_NAME = ".keys", MODULES = "modules", LAUNCHER_MODULES = "launcher-modules", LIBRARIES = "libraries"; public Path updatesDir; - public Path profilesDir; public Path librariesDir; public Path launcherLibrariesDir; public Path launcherLibrariesCompileDir; @@ -513,7 +476,6 @@ public static class LaunchServerDirectories { public void collect() { if (updatesDir == null) updatesDir = getPath(UPDATES_NAME); - if (profilesDir == null) profilesDir = getPath(PROFILES_NAME); if (trustStore == null) trustStore = getPath(TRUSTSTORE_NAME); if (launcherLibrariesDir == null) launcherLibrariesDir = getPath(LAUNCHERLIBRARIES_NAME); if (launcherLibrariesCompileDir == null) diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java index 16542fb6..3db4c2be 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java @@ -13,8 +13,10 @@ import pro.gravit.launchserver.auth.core.AuthCoreProvider; import pro.gravit.launchserver.auth.mix.MixProvider; import pro.gravit.launchserver.auth.password.PasswordVerifier; +import pro.gravit.launchserver.auth.profiles.ProfileProvider; import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.texture.TextureProvider; +import pro.gravit.launchserver.auth.updates.UpdatesProvider; import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; @@ -178,6 +180,8 @@ public static void registerAll() { OptionalAction.registerProviders(); OptionalTrigger.registerProviders(); MixProvider.registerProviders(); + ProfileProvider.registerProviders(); + UpdatesProvider.registerProviders(); } private static void printExperimentalBranch() { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/LocalProfileProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/LocalProfileProvider.java new file mode 100644 index 00000000..c938a1f8 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/LocalProfileProvider.java @@ -0,0 +1,108 @@ +package pro.gravit.launchserver.auth.profiles; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pro.gravit.launcher.base.Launcher; +import pro.gravit.launcher.base.profiles.ClientProfile; +import pro.gravit.utils.helper.IOHelper; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.*; + +public class LocalProfileProvider extends ProfileProvider { + public String profilesDir = "profiles"; + private transient volatile Map profilesMap; + private transient volatile Set profilesList; // Cache + @Override + public void sync() throws IOException { + Path profilesDirPath = Path.of(profilesDir); + if (!IOHelper.isDir(profilesDirPath)) + Files.createDirectory(profilesDirPath); + Map newProfiles = new HashMap<>(); + IOHelper.walk(profilesDirPath, new ProfilesFileVisitor(newProfiles), false); + Set newProfilesList = new HashSet<>(newProfiles.values()); + profilesMap = newProfiles; + profilesList = newProfilesList; + } + + @Override + public Set getProfiles() { + return profilesList; + } + + @Override + public void addProfile(ClientProfile profile) throws IOException { + Path profilesDirPath = Path.of(profilesDir); + ClientProfile oldProfile; + Path target = null; + for(var e : profilesMap.entrySet()) { + if(e.getValue().getUUID().equals(profile.getUUID())) { + target = e.getKey(); + } + } + if(target == null) { + target = profilesDirPath.resolve(profile.getTitle()+".json"); + oldProfile = profilesMap.get(target); + if(oldProfile != null && !oldProfile.getUUID().equals(profile.getUUID())) { + throw new FileAlreadyExistsException(target.toString()); + } + } + try (BufferedWriter writer = IOHelper.newWriter(target)) { + Launcher.gsonManager.configGson.toJson(profile, writer); + } + addProfile(target, profile); + } + + @Override + public void deleteProfile(ClientProfile profile) throws IOException { + for(var e : profilesMap.entrySet()) { + if(e.getValue().getUUID().equals(profile.getUUID())) { + Files.deleteIfExists(e.getKey()); + profilesMap.remove(e.getKey()); + profilesList.remove(e.getValue()); + break; + } + } + } + + private void addProfile(Path path, ClientProfile profile) { + for(var e : profilesMap.entrySet()) { + if(e.getValue().getUUID().equals(profile.getUUID())) { + profilesMap.remove(e.getKey()); + profilesList.remove(e.getValue()); + break; + } + } + profilesMap.put(path, profile); + profilesList.add(profile); + } + + private static final class ProfilesFileVisitor extends SimpleFileVisitor { + private final Map result; + private final Logger logger = LogManager.getLogger(); + + private ProfilesFileVisitor(Map result) { + this.result = result; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + logger.info("Syncing '{}' profile", IOHelper.getFileName(file)); + + // Read profile + ClientProfile profile; + try (BufferedReader reader = IOHelper.newReader(file)) { + profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class); + } + profile.verify(); + + // Add SIGNED profile to result list + result.put(file.toAbsolutePath(), profile); + return super.visitFile(file, attrs); + } + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/ProfileProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/ProfileProvider.java new file mode 100644 index 00000000..b5580021 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/ProfileProvider.java @@ -0,0 +1,76 @@ +package pro.gravit.launchserver.auth.profiles; + +import pro.gravit.launcher.base.profiles.ClientProfile; +import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler; +import pro.gravit.launchserver.socket.Client; +import pro.gravit.utils.ProviderMap; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +public abstract class ProfileProvider { + public static final ProviderMap providers = new ProviderMap<>("ProfileProvider"); + private static boolean registredProviders = false; + protected transient LaunchServer server; + + public static void registerProviders() { + if (!registredProviders) { + providers.register("local", LocalProfileProvider.class); + registredProviders = true; + } + } + + public void init(LaunchServer server) { + this.server = server; + } + + public abstract void sync() throws IOException; + + public abstract Set getProfiles(); + + public abstract void addProfile(ClientProfile profile) throws IOException; + + public abstract void deleteProfile(ClientProfile profile) throws IOException; + + public void close() { + + } + + public ClientProfile getProfile(UUID uuid) { + for(var e : getProfiles()) { + if(e.getUUID().equals(uuid)) { + return e; + } + } + return null; + } + + public ClientProfile getProfile(String title) { + for(var e : getProfiles()) { + if(e.getTitle().equals(title)) { + return e; + } + } + return null; + } + + public List getProfiles(Client client) { + List profileList; + Set serverProfiles = getProfiles(); + if (server.config.protectHandler instanceof ProfilesProtectHandler protectHandler) { + profileList = new ArrayList<>(4); + for (ClientProfile profile : serverProfiles) { + if (protectHandler.canGetProfile(profile, client)) { + profileList.add(profile); + } + } + } else { + profileList = List.copyOf(serverProfiles); + } + return profileList; + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/LocalUpdatesProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/LocalUpdatesProvider.java new file mode 100644 index 00000000..581b35ac --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/LocalUpdatesProvider.java @@ -0,0 +1,186 @@ +package pro.gravit.launchserver.auth.updates; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pro.gravit.launcher.core.hasher.HashedDir; +import pro.gravit.launcher.core.serialize.HInput; +import pro.gravit.launcher.core.serialize.HOutput; +import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.modules.events.LaunchServerUpdatesSyncEvent; +import pro.gravit.utils.helper.IOHelper; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.stream.Stream; + +public class LocalUpdatesProvider extends UpdatesProvider { + private final transient Logger logger = LogManager.getLogger(); + public String cacheFile = ".updates-cache"; + public String updatesDir = "updates"; + public boolean cacheUpdates = true; + private volatile transient Map updatesDirMap; + + private void writeCache(Path file) throws IOException { + try (HOutput output = new HOutput(IOHelper.newOutput(file))) { + output.writeLength(updatesDirMap.size(), 0); + for (Map.Entry entry : updatesDirMap.entrySet()) { + output.writeString(entry.getKey(), 0); + entry.getValue().write(output); + } + } + logger.debug("Saved {} updates to cache", updatesDirMap.size()); + } + + private void readCache(Path file) throws IOException { + Map updatesDirMap = new HashMap<>(16); + try (HInput input = new HInput(IOHelper.newInput(file))) { + int size = input.readLength(0); + for (int i = 0; i < size; ++i) { + String name = input.readString(0); + HashedDir dir = new HashedDir(input); + updatesDirMap.put(name, dir); + } + } + logger.debug("Found {} updates from cache", updatesDirMap.size()); + this.updatesDirMap = Collections.unmodifiableMap(updatesDirMap); + } + + public void readUpdatesFromCache() throws IOException { + readCache(Path.of(cacheFile)); + } + + public void readUpdatesDir() throws IOException { + var cacheFilePath = Path.of(cacheFile); + if (cacheUpdates) { + if (Files.exists(cacheFilePath)) { + try { + readCache(cacheFilePath); + return; + } catch (Throwable e) { + logger.error("Read updates cache failed", e); + } + } + } + sync(null); + } + + @Override + public void init(LaunchServer server) { + super.init(server); + try { + if (!IOHelper.isDir(Path.of(updatesDir))) + Files.createDirectory(Path.of(updatesDir)); + } catch (IOException e) { + logger.error("Updates not synced", e); + } + } + + @Override + public void syncInitially() throws IOException { + readUpdatesDir(); + } + + public void sync(Collection dirs) throws IOException { + logger.info("Syncing updates dir"); + Map newUpdatesDirMap = new HashMap<>(16); + try (DirectoryStream dirStream = Files.newDirectoryStream(Path.of(updatesDir))) { + for (final Path updateDir : dirStream) { + if (Files.isHidden(updateDir)) + continue; // Skip hidden + + // Resolve name and verify is dir + String name = IOHelper.getFileName(updateDir); + if (!IOHelper.isDir(updateDir)) { + if (!IOHelper.isFile(updateDir) && Stream.of(".jar", ".exe", ".hash").noneMatch(e -> updateDir.toString().endsWith(e))) + logger.warn("Not update dir: '{}'", name); + continue; + } + + // Add from previous map (it's guaranteed to be non-null) + if (dirs != null && !dirs.contains(name)) { + HashedDir hdir = updatesDirMap.get(name); + if (hdir != null) { + newUpdatesDirMap.put(name, hdir); + continue; + } + } + + // Sync and sign update dir + logger.info("Syncing '{}' update dir", name); + HashedDir updateHDir = new HashedDir(updateDir, null, true, true); + newUpdatesDirMap.put(name, updateHDir); + } + } + updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap); + if (cacheUpdates) { + try { + writeCache(Path.of(cacheFile)); + } catch (Throwable e) { + logger.error("Write updates cache failed", e); + } + } + server.modulesManager.invokeEvent(new LaunchServerUpdatesSyncEvent(server)); + } + + @Override + public HashedDir getUpdatesDir(String updateName) { + return updatesDirMap.get(updateName); + } + + private Path resolveUpdateName(String updateName) { + if(updateName == null) { + return Path.of(updatesDir); + } + return Path.of(updatesDir).resolve(updateName); + } + + @Override + public void upload(String updateName, Map files, boolean deleteAfterUpload) throws IOException { + var path = resolveUpdateName(updateName); + for(var e : files.entrySet()) { + var target = path.resolve(e.getKey()); + var source = e.getValue(); + IOHelper.createParentDirs(target); + if(deleteAfterUpload) { + Files.move(source, target, StandardCopyOption.REPLACE_EXISTING); + } else { + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + } + } + } + + @Override + public Map download(String updateName, List files) { + var path = resolveUpdateName(updateName); + Map map = new HashMap<>(); + for(var e : files) { + map.put(e, path.resolve(e)); + } + return map; + } + + @Override + public void delete(String updateName, List files) throws IOException { + var path = resolveUpdateName(updateName); + for(var e : files) { + var target = path.resolve(e); + Files.delete(target); + } + } + + @Override + public void delete(String updateName) throws IOException { + var path = resolveUpdateName(updateName); + IOHelper.deleteDir(path, true); + } + + @Override + public void create(String updateName) throws IOException { + var path = resolveUpdateName(updateName); + Files.createDirectories(path); + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/UpdatesProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/UpdatesProvider.java new file mode 100644 index 00000000..f7677ea8 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/UpdatesProvider.java @@ -0,0 +1,52 @@ +package pro.gravit.launchserver.auth.updates; + +import pro.gravit.launcher.core.hasher.HashedDir; +import pro.gravit.launchserver.LaunchServer; +import pro.gravit.utils.ProviderMap; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public abstract class UpdatesProvider { + public static final ProviderMap providers = new ProviderMap<>("UpdatesProvider"); + private static boolean registredProviders = false; + protected transient LaunchServer server; + + public static void registerProviders() { + if (!registredProviders) { + providers.register("local", LocalUpdatesProvider.class); + registredProviders = true; + } + } + + public void init(LaunchServer server) { + this.server = server; + } + + public void sync() throws IOException { + sync(null); + } + + public abstract void syncInitially() throws IOException; + + public abstract void sync(Collection updateNames) throws IOException; + + public abstract HashedDir getUpdatesDir(String updateName); + + public abstract void upload(String updateName, Map files, boolean deleteAfterUpload) throws IOException; + + public abstract Map download(String updateName, List files); + + public abstract void delete(String updateName, List files) throws IOException; + + public abstract void delete(String updateName) throws IOException; + + public abstract void create(String updateName) throws IOException; + + public void close() { + + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/BinaryPipeline.java b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/BinaryPipeline.java index 86fbf9c3..c735d0e0 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/BinaryPipeline.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/BinaryPipeline.java @@ -14,11 +14,11 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -public class BinaryPipeline { +public abstract class BinaryPipeline { public final List tasks = new ArrayList<>(); public final Path buildDir; public final String nameFormat; - private transient final Logger logger = LogManager.getLogger(); + protected transient final Logger logger = LogManager.getLogger(); public BinaryPipeline(Path buildDir, String nameFormat) { this.buildDir = buildDir; @@ -80,27 +80,6 @@ public Optional getTaskBefore(Predicate pr return Optional.empty(); } - public void build(Path target, boolean deleteTempFiles) throws IOException { - logger.info("Building launcher binary file"); - Path thisPath = null; - long time_start = System.currentTimeMillis(); - long time_this = time_start; - for (LauncherBuildTask task : tasks) { - logger.info("Task {}", task.getName()); - Path oldPath = thisPath; - thisPath = task.process(oldPath); - long time_task_end = System.currentTimeMillis(); - long time_task = time_task_end - time_this; - time_this = time_task_end; - logger.info("Task {} processed from {} millis", task.getName(), time_task); - } - long time_end = System.currentTimeMillis(); - if (deleteTempFiles) IOHelper.move(thisPath, target); - else IOHelper.copy(thisPath, target); - IOHelper.deleteDir(buildDir, false); - logger.info("Build successful from {} millis", time_end - time_start); - } - public String nextName(String taskName) { return nameFormat.formatted(taskName); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/LauncherBinary.java b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/LauncherBinary.java index a4ba76c0..a336c439 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/LauncherBinary.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/LauncherBinary.java @@ -1,11 +1,14 @@ package pro.gravit.launchserver.binary; import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.binary.tasks.LauncherBuildTask; import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.SecurityHelper; import java.io.IOException; import java.nio.file.Path; +import java.util.List; +import java.util.Map; public abstract class LauncherBinary extends BinaryPipeline { public final LaunchServer server; @@ -19,11 +22,27 @@ protected LauncherBinary(LaunchServer server, Path binaryFile, String nameFormat } public static Path resolve(LaunchServer server, String ext) { - return server.config.copyBinaries ? server.updatesDir.resolve(server.config.binaryName + ext) : server.dir.resolve(server.config.binaryName + ext); + return Path.of(server.config.binaryName + ext); } public void build() throws IOException { - build(syncBinaryFile, server.config.launcher.deleteTempFiles); + logger.info("Building launcher binary file"); + Path thisPath = null; + long time_start = System.currentTimeMillis(); + long time_this = time_start; + for (LauncherBuildTask task : tasks) { + logger.info("Task {}", task.getName()); + Path oldPath = thisPath; + thisPath = task.process(oldPath); + long time_task_end = System.currentTimeMillis(); + long time_task = time_task_end - time_this; + time_this = time_task_end; + logger.info("Task {} processed from {} millis", task.getName(), time_task); + } + long time_end = System.currentTimeMillis(); + server.config.updatesProvider.upload(null, Map.of(syncBinaryFile.toString(), thisPath), true); + IOHelper.deleteDir(buildDir, false); + logger.info("Build successful from {} millis", time_end - time_start); } public final boolean exists() { @@ -38,9 +57,13 @@ public void init() { } public final boolean sync() throws IOException { - boolean exists = exists(); - digest = exists ? SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(syncBinaryFile)) : null; - - return exists; + try { + var target = syncBinaryFile.toString(); + var path = server.config.updatesProvider.download(null, List.of(target)).get(target); + digest = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(path)); + return true; + } catch (Throwable e) { + return false; + } } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/PrepareBuildTask.java b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/PrepareBuildTask.java index e509edf3..753f48c5 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/PrepareBuildTask.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/PrepareBuildTask.java @@ -7,10 +7,7 @@ import pro.gravit.utils.helper.UnpackHelper; import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; +import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; import java.util.stream.Collectors; @@ -40,7 +37,7 @@ public Path process(Path inputFile) throws IOException { if(Files.isDirectory(server.launcherLibrariesCompile)) { IOHelper.walk(server.launcherLibrariesCompile, new ListFileVisitor(server.launcherBinary.addonLibs), false); } - try(Stream stream = Files.walk(server.launcherPack).filter((e) -> { + try(Stream stream = Files.walk(server.launcherPack, FileVisitOption.FOLLOW_LINKS).filter((e) -> { try { return !Files.isDirectory(e) && !Files.isHidden(e); } catch (IOException ex) { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadClientCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadClientCommand.java index 9da32e9c..3b331e25 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadClientCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadClientCommand.java @@ -97,10 +97,7 @@ public void invoke(String... args) throws IOException, CommandException { isMirrorClientDownload = true; } } - try (BufferedWriter writer = IOHelper.newWriter(IOHelper.resolveIncremental(server.profilesDir, - dirName, "json"))) { - Launcher.gsonManager.configGson.toJson(clientProfile, writer); - } + server.config.profileProvider.addProfile(clientProfile); // Finished server.syncProfilesDir(); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/CloneProfileCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/CloneProfileCommand.java index e943aa05..1b33cd43 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/CloneProfileCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/CloneProfileCommand.java @@ -7,6 +7,7 @@ import pro.gravit.launcher.base.profiles.ClientProfileBuilder; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.command.Command; +import pro.gravit.utils.helper.CommonHelper; import pro.gravit.utils.helper.IOHelper; import java.io.IOException; @@ -26,7 +27,7 @@ public CloneProfileCommand(LaunchServer server) { @Override public String getArgsDescription() { - return "[profile file name] [new profile title]"; + return "[profile title/uuid] [new profile title]"; } @Override @@ -37,13 +38,12 @@ public String getUsageDescription() { @Override public void invoke(String... args) throws Exception { verifyArgs(args, 2); - var profilePath = server.profilesDir.resolve(args[0].concat(".json")); - if(!Files.exists(profilePath)) { - logger.error("File {} not found", profilePath); - } ClientProfile profile; - try(Reader reader = IOHelper.newReader(profilePath)) { - profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class); + try { + UUID uuid = UUID.fromString(args[0]); + profile = server.config.profileProvider.getProfile(uuid); + } catch (IllegalArgumentException ex) { + profile = server.config.profileProvider.getProfile(args[0]); } var builder = new ClientProfileBuilder(profile); builder.setTitle(args[1]); @@ -65,10 +65,7 @@ public void invoke(String... args) throws Exception { } builder.setDir(args[1]); profile = builder.createClientProfile(); - var targetPath = server.profilesDir.resolve(args[1].concat(".json")); - try(Writer writer = IOHelper.newWriter(targetPath)) { - Launcher.gsonManager.gson.toJson(profile, writer); - } + server.config.profileProvider.addProfile(profile); logger.info("Profile {} cloned from {}", args[1], args[0]); server.syncProfilesDir(); server.syncUpdatesDir(List.of(args[1])); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/DeleteProfileCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/DeleteProfileCommand.java index 642a115c..f6a36659 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/DeleteProfileCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/DeleteProfileCommand.java @@ -8,6 +8,7 @@ import pro.gravit.utils.helper.IOHelper; import java.nio.file.Files; +import java.util.UUID; public class DeleteProfileCommand extends Command { private final transient Logger logger = LogManager.getLogger(ListProfilesCommand.class); @@ -28,29 +29,24 @@ public String getUsageDescription() { @Override public void invoke(String... args) throws Exception { verifyArgs(args, 1); - ClientProfile profile = null; - for(var p : server.getProfiles()) { - if(p.getUUID().toString().equals(args[0]) || p.getTitle().equals(args[0])) { - profile = p; - break; - } + ClientProfile profile; + try { + UUID uuid = UUID.fromString(args[0]); + profile = server.config.profileProvider.getProfile(uuid); + } catch (IllegalArgumentException ex) { + profile = server.config.profileProvider.getProfile(args[0]); } if(profile == null) { logger.error("Profile {} not found", args[0]); return; } - var clientDir = server.updatesDir.resolve(profile.getDir()).toAbsolutePath(); - logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", clientDir); + logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", profile.getDir()); if(!showApplyDialog("Continue?")) { return; } - logger.info("Delete {}", clientDir); - IOHelper.deleteDir(clientDir, true); - var profileFile = profile.getProfileFilePath(); - if(profileFile == null) { - profileFile = server.profilesDir.resolve(profile.getTitle().concat(".json")); - } - logger.info("Delete {}", profileFile); - Files.deleteIfExists(profileFile); + logger.info("Delete {} ({})", profile.getTitle(), profile.getUUID()); + server.config.profileProvider.deleteProfile(profile); + logger.info("Delete {}", profile.getDir()); + server.config.updatesProvider.delete(profile.getDir()); } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/MakeProfileCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/MakeProfileCommand.java index ad57670c..7a010c64 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/MakeProfileCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/MakeProfileCommand.java @@ -37,9 +37,7 @@ public void invoke(String... args) throws Exception { logger.info("Detected option {}", option); } ClientProfile profile = MakeProfileHelper.makeProfile(version, args[0], options); - try (Writer writer = IOHelper.newWriter(server.profilesDir.resolve(args[0].concat(".json")))) { - Launcher.gsonManager.configGson.toJson(profile, writer); - } + server.config.profileProvider.addProfile(profile); logger.info("Profile {} created", args[0]); server.syncProfilesDir(); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/SaveProfilesCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/SaveProfilesCommand.java index 7aba43a4..9d796c1e 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/SaveProfilesCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/SaveProfilesCommand.java @@ -22,20 +22,6 @@ public SaveProfilesCommand(LaunchServer server) { super(server); } - public static void saveProfile(ClientProfile profile, Path path) throws IOException { - if (profile.getServers().isEmpty()) { - ClientProfile.ServerProfile serverProfile = new ClientProfile.ServerProfile(); - serverProfile.isDefault = true; - serverProfile.name = profile.getTitle(); - serverProfile.serverAddress = profile.getServerAddress(); - serverProfile.serverPort = profile.getServerPort(); - profile.getServers().add(serverProfile); - } - try (Writer w = IOHelper.newWriter(path)) { - Launcher.gsonManager.configGson.toJson(profile, w); - } - } - @Override public String getArgsDescription() { return "[profile names...]"; @@ -51,17 +37,14 @@ public void invoke(String... args) throws Exception { verifyArgs(args, 1); if (args.length > 0) { for (String profileName : args) { - Path profilePath = server.profilesDir.resolve(profileName.concat(".json")); - if (!Files.exists(profilePath)) { - logger.error("Profile {} not found", profilePath.toString()); - return; - } ClientProfile profile; - try (Reader reader = IOHelper.newReader(profilePath)) { - profile = Launcher.gsonManager.configGson.fromJson(reader, ClientProfile.class); + try { + UUID uuid = UUID.fromString(profileName); + profile = server.config.profileProvider.getProfile(uuid); + } catch (IllegalArgumentException ex) { + profile = server.config.profileProvider.getProfile(profileName); } - saveProfile(profile, profilePath); - logger.info("Profile {} save successful", profilePath.toString()); + server.config.profileProvider.addProfile(profile); } server.syncProfilesDir(); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java b/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java index ecba3a95..f2121ba7 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java @@ -9,9 +9,13 @@ import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.core.RejectAuthCoreProvider; +import pro.gravit.launchserver.auth.profiles.LocalProfileProvider; +import pro.gravit.launchserver.auth.profiles.ProfileProvider; import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.protect.StdProtectHandler; import pro.gravit.launchserver.auth.texture.RequestTextureProvider; +import pro.gravit.launchserver.auth.updates.LocalUpdatesProvider; +import pro.gravit.launchserver.auth.updates.UpdatesProvider; import pro.gravit.launchserver.components.AuthLimiterComponent; import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.components.ProGuardComponent; @@ -30,12 +34,13 @@ public final class LaunchServerConfig { public String[] mirrors; public String binaryName; public boolean copyBinaries = true; - public boolean cacheUpdates = true; public LauncherConfig.LauncherEnvironment env; public Map auth; // Handlers & Providers public ProtectHandler protectHandler; public Map components; + public ProfileProvider profileProvider = new LocalProfileProvider(); + public UpdatesProvider updatesProvider = new LocalUpdatesProvider(); public NettyConfig netty; public LauncherConf launcher; public JarSignerConf sign; @@ -85,6 +90,7 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) { newConfig.components.put("authLimiter", authLimiterComponent); ProGuardComponent proGuardComponent = new ProGuardComponent(); newConfig.components.put("proguard", proGuardComponent); + newConfig.profileProvider = new LocalProfileProvider(); return newConfig; } @@ -166,6 +172,14 @@ public void init(LaunchServer.ReloadType type) { server.registerObject("protectHandler", protectHandler); protectHandler.init(server); } + if(profileProvider != null) { + server.registerObject("profileProvider", profileProvider); + profileProvider.init(server); + } + if(updatesProvider != null) { + server.registerObject("updatesProvider", updatesProvider); + updatesProvider.init(server); + } if (components != null) { components.forEach((k, v) -> server.registerObject("component.".concat(k), v)); } @@ -206,6 +220,14 @@ public void close(LaunchServer.ReloadType type) { server.unregisterObject("protectHandler", protectHandler); protectHandler.close(); } + if(profileProvider != null) { + server.unregisterObject("profileProvider", profileProvider); + profileProvider.close(); + } + if(updatesProvider != null) { + server.unregisterObject("updatesProvider", updatesProvider); + updatesProvider.close(); + } } public static class JarSignerConf { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java b/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java index 5b139d12..5b375301 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java @@ -197,9 +197,6 @@ public static String getMainClassByVersion(ClientProfile.Version version, MakePr if(version.compareTo(ClientProfileVersions.MINECRAFT_1_7_10) == 0) { return "com.gtnewhorizons.retrofuturabootstrap.Main"; } - if(version.compareTo(ClientProfileVersions.MINECRAFT_1_12_2) == 0) { - return "top.outlands.foundation.boot.Foundation"; // Cleanroom - } if (findOption(options, MakeProfileOptionLaunchWrapper.class).isPresent()) { return "net.minecraft.launchwrapper.Launch"; } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java index 7f26c486..4970e4f0 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java @@ -14,8 +14,10 @@ import pro.gravit.launchserver.auth.core.AuthCoreProvider; import pro.gravit.launchserver.auth.mix.MixProvider; import pro.gravit.launchserver.auth.password.PasswordVerifier; +import pro.gravit.launchserver.auth.profiles.ProfileProvider; import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.texture.TextureProvider; +import pro.gravit.launchserver.auth.updates.UpdatesProvider; import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; import pro.gravit.launchserver.socket.WebSocketService; @@ -46,6 +48,8 @@ public void registerAdapters(GsonBuilder builder) { builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers)); builder.registerTypeAdapter(OptionalTrigger.class, new UniversalJsonAdapter<>(OptionalTrigger.providers)); builder.registerTypeAdapter(MixProvider.class, new UniversalJsonAdapter<>(MixProvider.providers)); + builder.registerTypeAdapter(ProfileProvider.class, new UniversalJsonAdapter<>(ProfileProvider.providers)); + builder.registerTypeAdapter(UpdatesProvider.class, new UniversalJsonAdapter<>(UpdatesProvider.providers)); modulesManager.invokeEvent(new PreGsonPhase(builder)); //ClientWebSocketService.appendTypeAdapters(builder); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java index c2642d2e..717c65a7 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java @@ -18,112 +18,38 @@ public class UpdatesManager { private final LaunchServer server; - private final Logger logger = LogManager.getLogger(); - private final Path cacheFile; - private volatile Map updatesDirMap; public UpdatesManager(LaunchServer server) { this.server = server; - this.cacheFile = server.dir.resolve(".updates-cache"); - } - - private void writeCache(Path file) throws IOException { - try (HOutput output = new HOutput(IOHelper.newOutput(file))) { - output.writeLength(updatesDirMap.size(), 0); - for (Map.Entry entry : updatesDirMap.entrySet()) { - output.writeString(entry.getKey(), 0); - entry.getValue().write(output); - } - } - logger.debug("Saved {} updates to cache", updatesDirMap.size()); - } - - private void readCache(Path file) throws IOException { - Map updatesDirMap = new HashMap<>(16); - try (HInput input = new HInput(IOHelper.newInput(file))) { - int size = input.readLength(0); - for (int i = 0; i < size; ++i) { - String name = input.readString(0); - HashedDir dir = new HashedDir(input); - updatesDirMap.put(name, dir); - } - } - logger.debug("Found {} updates from cache", updatesDirMap.size()); - this.updatesDirMap = Collections.unmodifiableMap(updatesDirMap); } + @Deprecated public void readUpdatesFromCache() throws IOException { - readCache(cacheFile); + } + @Deprecated public void readUpdatesDir() throws IOException { - if (server.config.cacheUpdates) { - if (Files.exists(cacheFile)) { - try { - readCache(cacheFile); - return; - } catch (Throwable e) { - logger.error("Read updates cache failed", e); - } - } - } - syncUpdatesDir(null); + } + @Deprecated public void syncUpdatesDir(Collection dirs) throws IOException { - logger.info("Syncing updates dir"); - Map newUpdatesDirMap = new HashMap<>(16); - try (DirectoryStream dirStream = Files.newDirectoryStream(server.updatesDir)) { - for (final Path updateDir : dirStream) { - if (Files.isHidden(updateDir)) - continue; // Skip hidden - - // Resolve name and verify is dir - String name = IOHelper.getFileName(updateDir); - if (!IOHelper.isDir(updateDir)) { - if (!IOHelper.isFile(updateDir) && Stream.of(".jar", ".exe", ".hash").noneMatch(e -> updateDir.toString().endsWith(e))) - logger.warn("Not update dir: '{}'", name); - continue; - } - - // Add from previous map (it's guaranteed to be non-null) - if (dirs != null && !dirs.contains(name)) { - HashedDir hdir = updatesDirMap.get(name); - if (hdir != null) { - newUpdatesDirMap.put(name, hdir); - continue; - } - } - - // Sync and sign update dir - logger.info("Syncing '{}' update dir", name); - HashedDir updateHDir = new HashedDir(updateDir, null, true, true); - newUpdatesDirMap.put(name, updateHDir); - } - } - updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap); - if (server.config.cacheUpdates) { - try { - writeCache(cacheFile); - } catch (Throwable e) { - logger.error("Write updates cache failed", e); - } - } - server.modulesManager.invokeEvent(new LaunchServerUpdatesSyncEvent(server)); + server.config.updatesProvider.sync(dirs); } + @Deprecated public HashSet getUpdatesList() { - HashSet set = new HashSet<>(); - for (Map.Entry entry : updatesDirMap.entrySet()) - set.add(entry.getKey()); - return set; + return new HashSet<>(); } + @Deprecated public HashedDir getUpdate(String name) { - return updatesDirMap.get(name); + return server.config.updatesProvider.getUpdatesDir(name); } + @Deprecated public void addUpdate(String name, HashedDir dir) { - updatesDirMap.put(name, dir); + throw new UnsupportedOperationException(); } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ProfilesResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ProfilesResponse.java index 39058b76..6e5be16a 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ProfilesResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ProfilesResponse.java @@ -13,6 +13,7 @@ import java.util.Set; public class ProfilesResponse extends SimpleResponse { + @Deprecated public static List getListVisibleProfiles(LaunchServer server, Client client) { List profileList; Set serverProfiles = server.getProfiles(); @@ -40,6 +41,6 @@ public void execute(ChannelHandlerContext ctx, Client client) { sendError("Access denied"); return; } - sendResult(new ProfilesRequestEvent(getListVisibleProfiles(server, client))); + sendResult(new ProfilesRequestEvent(server.config.profileProvider.getProfiles(client))); } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/LauncherResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/LauncherResponse.java index 05e6c958..1e2285f1 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/LauncherResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/LauncherResponse.java @@ -52,7 +52,7 @@ public void execute(ChannelHandlerContext ctx, Client client) { client.checkSign = true; sendResult(new LauncherRequestEvent(false, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); } else { - sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); + sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, null, 0)); } } else if (launcher_type == 2) //EXE { @@ -62,7 +62,7 @@ public void execute(ChannelHandlerContext ctx, Client client) { client.checkSign = true; sendResult(new LauncherRequestEvent(false, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); } else { - sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); + sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, null, 0)); } } else sendError("Request launcher type error"); } diff --git a/Launcher/build.gradle b/Launcher/build.gradle index a2253345..42ed13a7 100644 --- a/Launcher/build.gradle +++ b/Launcher/build.gradle @@ -1,7 +1,6 @@ apply plugin: 'com.github.johnrengelman.shadow' String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper" -String mainAgentName = "pro.gravit.launcher.runtime.LauncherAgent" repositories { maven { @@ -20,7 +19,6 @@ jar { archiveClassifier.set('clean') manifest.attributes("Main-Class": mainClassName, - "Premain-Class": mainAgentName, "Multi-Release": "true", "Automatic-Module-Name": "GravitLauncher") } diff --git a/Launcher/src/main/java/pro/gravit/launcher/runtime/LauncherEngine.java b/Launcher/src/main/java/pro/gravit/launcher/runtime/LauncherEngine.java index 8de18308..df1e873b 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/runtime/LauncherEngine.java +++ b/Launcher/src/main/java/pro/gravit/launcher/runtime/LauncherEngine.java @@ -124,7 +124,6 @@ public static void main(String... args) throws Throwable { LogHelper.printLicense("Launcher"); LauncherEngine.checkClass(LauncherEngineWrapper.class); LauncherEngine.checkClass(LauncherEngine.class); - LauncherEngine.checkClass(LauncherAgent.class); LauncherEngine.checkClass(ClientLauncherEntryPoint.class); LauncherEngine.modulesManager = new RuntimeModuleManager(); LauncherEngine.modulesManager.loadModule(new RuntimeLauncherCoreModule()); diff --git a/Launcher/src/main/java/pro/gravit/launcher/runtime/client/ClientLauncherProcess.java b/Launcher/src/main/java/pro/gravit/launcher/runtime/client/ClientLauncherProcess.java index aaf4c5be..dc1b5bcd 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/runtime/client/ClientLauncherProcess.java +++ b/Launcher/src/main/java/pro/gravit/launcher/runtime/client/ClientLauncherProcess.java @@ -142,10 +142,41 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException { } //ADD CLASSPATH processArgs.add(JVMHelper.jvmProperty("java.library.path", this.params.nativesDir)); - if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.AGENT) { - processArgs.add("-javaagent:".concat(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString())); - } else if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) { - systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(new HashSet<>(), workDir, params.actions, params.profile) + if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) { + Set ignorePath = new HashSet<>(); + var moduleConf = params.profile.getModuleConf(); + if(moduleConf != null) { + if(moduleConf.modulePath != null && !moduleConf.modulePath.isEmpty()) { + processArgs.add("-p"); + for(var e : moduleConf.modulePath) { + ignorePath.add(Path.of(e)); + } + processArgs.add(String.join(File.pathSeparator, moduleConf.modulePath)); + } + if(moduleConf.modules != null && !moduleConf.modules.isEmpty()) { + processArgs.add("--add-modules"); + processArgs.add(String.join(",", moduleConf.modules)); + } + if(moduleConf.exports != null && !moduleConf.exports.isEmpty()) { + for(var e : moduleConf.exports.entrySet()) { + processArgs.add("--add-exports"); + processArgs.add(String.format("%s=%s", e.getKey(), e.getValue())); + } + } + if(moduleConf.opens != null && !moduleConf.opens.isEmpty()) { + for(var e : moduleConf.opens.entrySet()) { + processArgs.add("--add-opens"); + processArgs.add(String.format("%s=%s", e.getKey(), e.getValue())); + } + } + if(moduleConf.reads != null && !moduleConf.reads.isEmpty()) { + for(var e : moduleConf.reads.entrySet()) { + processArgs.add("--add-reads"); + processArgs.add(String.format("%s=%s", e.getKey(), e.getValue())); + } + } + } + systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(ignorePath, workDir, params.actions, params.profile) .map(Path::toString) .toList()); } diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/base/profiles/ClientProfile.java b/LauncherAPI/src/main/java/pro/gravit/launcher/base/profiles/ClientProfile.java index 5d55896d..74390d36 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/base/profiles/ClientProfile.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/base/profiles/ClientProfile.java @@ -18,7 +18,6 @@ public final class ClientProfile implements Comparable { private static final FileNameMatcher ASSET_MATCHER = new FileNameMatcher( new String[0], new String[]{"indexes", "objects"}, new String[0]); - private transient Path profileFilePath; @LauncherNetworkAPI private String title; @LauncherNetworkAPI @@ -392,16 +391,8 @@ public List getFlags() { return flags; } - public Path getProfileFilePath() { - return profileFilePath; - } - - public void setProfileFilePath(Path profileFilePath) { - this.profileFilePath = profileFilePath; - } - public enum ClassLoaderConfig { - AGENT, LAUNCHER, MODULE, SYSTEM_ARGS + LAUNCHER, MODULE, SYSTEM_ARGS } public enum CompatibilityFlags { diff --git a/LauncherClient/build.gradle b/LauncherClient/build.gradle index 9a59ce29..78ad4310 100644 --- a/LauncherClient/build.gradle +++ b/LauncherClient/build.gradle @@ -1,7 +1,6 @@ apply plugin: 'org.openjfx.javafxplugin' -String mainClassName = "pro.gravit.launcher.ClientLauncherWrapper" -String mainAgentName = "pro.gravit.launcher.LauncherAgent" +String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper" repositories { maven { @@ -14,7 +13,6 @@ jar { archiveClassifier.set('clean') manifest.attributes("Main-Class": mainClassName, - "Premain-Class": mainAgentName, "Multi-Release": "true") } diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java index d084f9d5..f767206f 100644 --- a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java +++ b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java @@ -86,9 +86,7 @@ private static void realMain(String[] args) throws Throwable { modulesManager.invokeEvent(new PreConfigPhase()); LogHelper.debug("Reading ClientLauncher params"); ClientParams params = readParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort)); - if (params.profile.getClassLoaderConfig() != ClientProfile.ClassLoaderConfig.AGENT) { - ClientLauncherMethods.verifyNoAgent(); - } + ClientLauncherMethods.verifyNoAgent(); if(params.timestamp > System.currentTimeMillis() || params.timestamp + 30*1000 < System.currentTimeMillis() ) { LogHelper.error("Timestamp failed. Exit"); ClientLauncherMethods.exitLauncher(-662); @@ -160,7 +158,7 @@ private static void realMain(String[] args) throws Throwable { System.load(Paths.get(params.nativesDir).resolve(ClientService.findLibrary(e)).toAbsolutePath().toString()); } } - if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) { + if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER || classLoaderConfig == ClientProfile.ClassLoaderConfig.MODULE) { if(JVMHelper.JVM_VERSION <= 11) { launch = new LegacyLaunch(); } else { @@ -170,20 +168,12 @@ private static void realMain(String[] args) throws Throwable { System.setProperty("java.class.path", classpath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator))); modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, classLoaderControl, profile)); ClientService.baseURLs = classLoaderControl.getURLs(); - } else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.AGENT) { - launch = new BasicLaunch(LauncherAgent.inst); - classpathURLs.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toUri().toURL()); - classLoaderControl = launch.init(classpath, params.nativesDir, options); - for (URL url : classpathURLs) { - LauncherAgent.addJVMClassPath(Paths.get(url.toURI())); - } - ClientService.instrumentation = LauncherAgent.inst; - modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, null, profile)); - ClientService.baseURLs = classpathURLs.toArray(new URL[0]); } else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) { launch = new BasicLaunch(); classLoaderControl = launch.init(classpath, params.nativesDir, options); ClientService.baseURLs = classpathURLs.toArray(new URL[0]); + } else { + throw new UnsupportedOperationException(String.format("Unknown classLoaderConfig %s", classLoaderConfig)); } if(profile.hasFlag(ClientProfile.CompatibilityFlags.CLASS_CONTROL_API)) { ClientService.classLoaderControl = classLoaderControl; diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/client/LauncherAgent.java b/LauncherClient/src/main/java/pro/gravit/launcher/client/LauncherAgent.java deleted file mode 100644 index 0683af97..00000000 --- a/LauncherClient/src/main/java/pro/gravit/launcher/client/LauncherAgent.java +++ /dev/null @@ -1,59 +0,0 @@ -package pro.gravit.launcher.client; - -import pro.gravit.launcher.client.utils.NativeJVMHalt; -import pro.gravit.utils.helper.LogHelper; - -import java.io.File; -import java.io.IOException; -import java.lang.instrument.Instrumentation; -import java.nio.file.Path; -import java.util.jar.JarFile; - - -public final class LauncherAgent { - public static Instrumentation inst; - private static boolean isAgentStarted = false; - - public static void addJVMClassPath(String path) throws IOException { - LogHelper.debug("Launcher Agent addJVMClassPath"); - inst.appendToSystemClassLoaderSearch(new JarFile(new File(path))); - } - - public static void addJVMClassPath(Path path) throws IOException { - LogHelper.debug("Launcher Agent addJVMClassPath"); - inst.appendToSystemClassLoaderSearch(new JarFile(path.toFile())); - } - - public static void premain(String agentArgument, Instrumentation instrumentation) { - System.out.println("Launcher Agent"); - checkAgentStacktrace(); - inst = instrumentation; - NativeJVMHalt.initFunc(); - isAgentStarted = true; - } - - public static void checkAgentStacktrace() { - RuntimeException ex = new SecurityException("Error check agent stacktrace"); - boolean isFoundNative = false; - boolean foundPreMain = false; - for (StackTraceElement e : ex.getStackTrace()) { - if (e.isNativeMethod()) { - if (!isFoundNative) isFoundNative = true; - else throw ex; - } - if (e.getMethodName().equals("premain")) { - if (!foundPreMain) foundPreMain = true; - else throw ex; - } - } - if (!isFoundNative || !foundPreMain) throw ex; - } - - public static boolean isStarted() { - return isAgentStarted; - } - - public boolean isAgentStarted() { - return isAgentStarted; - } -} diff --git a/LauncherCore/src/main/java/pro/gravit/utils/Version.java b/LauncherCore/src/main/java/pro/gravit/utils/Version.java index 8201c010..aac03549 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/Version.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/Version.java @@ -6,7 +6,7 @@ public final class Version implements Comparable { public static final int MAJOR = 5; public static final int MINOR = 6; - public static final int PATCH = 5; + public static final int PATCH = 6; public static final int BUILD = 1; public static final Version.Type RELEASE = Type.STABLE; public final int major; diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java index 1a155fe4..a7511ad9 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java @@ -54,6 +54,10 @@ private class LegacyClassLoader extends URLClassLoader { private final Map> classMap = new ConcurrentHashMap<>(); private String nativePath; + static { + ClassLoader.registerAsParallelCapable(); + } + private final List packages = new ArrayList<>(); public LegacyClassLoader(URL[] urls) { super(urls); diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java index 5a414ea8..d83e3c44 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java @@ -164,6 +164,11 @@ private class ModuleClassLoader extends URLClassLoader { private String nativePath; private final List packages = new ArrayList<>(); + + static { + ClassLoader.registerAsParallelCapable(); + } + public ModuleClassLoader(URL[] urls, ClassLoader parent) { super("LAUNCHER", urls, parent); packages.add("pro.gravit.launcher."); diff --git a/build.gradle b/build.gradle index 473b0c47..4f30a8a7 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ id 'org.openjfx.javafxplugin' version '0.1.0' apply false } group = 'pro.gravit.launcher' -version = '5.6.5' +version = '5.6.6' apply from: 'props.gradle' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136..2c352119 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e7646dea..dedd5d1e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 25da30db..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## diff --git a/modules b/modules index 9f1ccd0b..65f882bd 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit 9f1ccd0bbc767d04a8a2d2909cc050980c8a458f +Subproject commit 65f882bd90623aa1581ad24cfa4f52563ea95bc0 diff --git a/props.gradle b/props.gradle index af811c06..8c8498c3 100644 --- a/props.gradle +++ b/props.gradle @@ -1,7 +1,7 @@ project.ext { verAsm = '9.7' verNetty = '4.1.111.Final' - verOshiCore = '6.6.1' + verOshiCore = '6.6.2' verJunit = '5.10.2' verJansi = '2.4.1' verJline = '3.26.1'