diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java index 595d1cdb..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 @@ -359,9 +360,7 @@ public void run() { syncProfilesDir(); // Sync updates dir - if (!IOHelper.isDir(updatesDir)) - Files.createDirectory(updatesDir); - updatesManager.readUpdatesDir(); + config.updatesProvider.syncInitially(); modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this)); 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..dc429863 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/LocalUpdatesProvider.java @@ -0,0 +1,168 @@ +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.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); + } + + @Override + public void upload(String updateName, Map files, boolean deleteAfterUpload) throws IOException { + var path = Path.of(updatesDir).resolve(updateName); + for(var e : files.entrySet()) { + var target = path.resolve(e.getKey()); + var source = Path.of(e.getKey()); + IOHelper.createParentDirs(target); + if(deleteAfterUpload) { + Files.move(source, target); + } else { + Files.copy(source, target); + } + } + } + + @Override + public void delete(String updateName, List files) throws IOException { + var path = Path.of(updatesDir).resolve(updateName); + for(var e : files) { + var target = path.resolve(e); + Files.delete(target); + } + } + + @Override + public void delete(String updateName) throws IOException { + var path = Path.of(updatesDir).resolve(updateName); + IOHelper.deleteDir(path, true); + } + + @Override + public void create(String updateName) throws IOException { + var path = Path.of(updatesDir).resolve(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..a6912d1e --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/UpdatesProvider.java @@ -0,0 +1,45 @@ +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) { + 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 void delete(String updateName, List files) throws IOException; + + public abstract void delete(String updateName) throws IOException; + + public abstract void create(String updateName) throws IOException; +} 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 562460c7..0b4e6937 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java @@ -14,6 +14,8 @@ 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; @@ -32,13 +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; 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 74a6ba98..4970e4f0 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java @@ -17,6 +17,7 @@ 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; @@ -48,6 +49,7 @@ public void registerAdapters(GsonBuilder builder) { 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(); } }