From c719f2448eb8488fc2399569fc102ea5df45b382 Mon Sep 17 00:00:00 2001 From: Gravita Date: Tue, 1 Jun 2021 05:48:33 +0700 Subject: [PATCH] [FEATURE] Updates cache --- .../pro/gravit/launchserver/LaunchServer.java | 41 +----- .../config/LaunchServerConfig.java | 1 + .../manangers/UpdatesManager.java | 125 ++++++++++++++++++ .../response/update/UpdateListResponse.java | 6 +- .../response/update/UpdateResponse.java | 2 +- .../launcher/ClientLauncherWrapper.java | 2 +- 6 files changed, 136 insertions(+), 41 deletions(-) create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java index ef5c0fb1..170269e3 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java @@ -46,7 +46,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Stream; /** * The main LaunchServer class. Contains links to all necessary objects @@ -115,6 +114,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab public final PingServerManager pingServerManager; public final FeaturesManager featuresManager; public final KeyAgreementManager keyAgreementManager; + public final UpdatesManager updatesManager; // HWID ban + anti-brutforce public final CertificateManager certificateManager; // Server @@ -127,6 +127,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab public final LauncherModuleLoader launcherModuleLoader; private final Logger logger = LogManager.getLogger(); public LaunchServerConfig config; + @Deprecated public volatile Map updatesDirMap; // Updates and profiles private volatile Set profilesList; @@ -173,6 +174,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La pingServerManager = new PingServerManager(this); featuresManager = new FeaturesManager(this); authManager = new AuthManager(this); + updatesManager = new UpdatesManager(this); RestoreResponse.registerProviders(this); //Generate or set new Certificate API certificateManager.orgName = config.projectName; @@ -316,10 +318,12 @@ public void setProfiles(Set profilesList) { this.profilesList = Collections.unmodifiableSet(profilesList); } + @Deprecated public HashedDir getUpdateDir(String name) { return updatesDirMap.get(name); } + @Deprecated public Set> getUpdateDirs() { return updatesDirMap.entrySet(); } @@ -349,8 +353,7 @@ public void run() { try { if (!IOHelper.isDir(updatesDir)) Files.createDirectory(updatesDir); - syncUpdatesDir(null); - modulesManager.invokeEvent(new LaunchServerUpdatesSyncEvent(this)); + updatesManager.readUpdatesDir(); // Sync profiles dir if (!IOHelper.isDir(profilesDir)) @@ -402,37 +405,7 @@ public void syncProfilesDir() throws IOException { } public void syncUpdatesDir(Collection dirs) throws IOException { - logger.info("Syncing updates dir"); - Map newUpdatesDirMap = new HashMap<>(16); - try (DirectoryStream dirStream = Files.newDirectoryStream(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); + updatesManager.syncUpdatesDir(dirs); } public void restart() { 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 fdfe852b..95ca98af 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java @@ -35,6 +35,7 @@ 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; @Deprecated diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java new file mode 100644 index 00000000..baa751ff --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java @@ -0,0 +1,125 @@ +package pro.gravit.launchserver.manangers; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pro.gravit.launcher.hasher.HashedDir; +import pro.gravit.launcher.serialize.HInput; +import pro.gravit.launcher.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.util.*; +import java.util.stream.Stream; + +public class UpdatesManager { + private final LaunchServer server; + private final Logger logger = LogManager.getLogger(); + private volatile Map updatesDirMap; + private Path cacheFile; + + 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); + } + + 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); + } + + 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)); + } + + public HashSet getUpdatesList() { + HashSet set = new HashSet<>(); + for (Map.Entry entry : updatesDirMap.entrySet()) + set.add(entry.getKey()); + return set; + } + + public HashedDir getUpdate(String name) { + return updatesDirMap.get(name); + } + + public void addUpdate(String name, HashedDir dir) { + updatesDirMap.put(name, dir); + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/UpdateListResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/UpdateListResponse.java index 5c84f9b7..bda0328b 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/UpdateListResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/UpdateListResponse.java @@ -2,12 +2,10 @@ import io.netty.channel.ChannelHandlerContext; import pro.gravit.launcher.events.request.UpdateListRequestEvent; -import pro.gravit.launcher.hasher.HashedDir; import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.response.SimpleResponse; import java.util.HashSet; -import java.util.Map; public class UpdateListResponse extends SimpleResponse { @@ -22,9 +20,7 @@ public void execute(ChannelHandlerContext ctx, Client client) { sendError("Access denied"); return; } - HashSet set = new HashSet<>(); - for (Map.Entry entry : server.updatesDirMap.entrySet()) - set.add(entry.getKey()); + HashSet set = server.updatesManager.getUpdatesList(); sendResult(new UpdateListRequestEvent(set)); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/UpdateResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/UpdateResponse.java index c823acd4..bd7bbfe8 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/UpdateResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/UpdateResponse.java @@ -27,7 +27,7 @@ public void execute(ChannelHandlerContext ctx, Client client) { sendError("Invalid request"); return; } - HashedDir dir = server.updatesDirMap.get(dirName); + HashedDir dir = server.updatesManager.getUpdate(dirName); if (dir == null) { sendError(String.format("Directory %s not found", dirName)); return; diff --git a/Launcher/src/main/java/pro/gravit/launcher/ClientLauncherWrapper.java b/Launcher/src/main/java/pro/gravit/launcher/ClientLauncherWrapper.java index 2d6cb28f..61e87a96 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/ClientLauncherWrapper.java +++ b/Launcher/src/main/java/pro/gravit/launcher/ClientLauncherWrapper.java @@ -67,7 +67,7 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep context.javaVersion = version; continue; } - if (version.enabledJavaFX) { + if (version.enabledJavaFX == context.javaVersion.enabledJavaFX) { if (context.javaVersion.version < version.version) { context.javaVersion = version; } else if (context.javaVersion.version == version.version && context.javaVersion.build < version.build) {