diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java index dc858e67..417fe442 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java @@ -27,6 +27,7 @@ import pro.gravit.utils.command.CommandHandler; import pro.gravit.utils.command.SubCommand; import pro.gravit.utils.helper.CommonHelper; +import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.SecurityHelper; @@ -305,6 +306,12 @@ public void checkCertificateExpired() { } } + public Path createTempDirectory(String name) throws IOException { + var path = tmpDir.resolve(String.format("launchserver-%s-%s", name, SecurityHelper.randomStringToken())); + Files.createDirectories(path); + return path; + } + private LauncherBinary binary() { LaunchServerLauncherExeInit event = new LaunchServerLauncherExeInit(this, null); modulesManager.invokeEvent(event); 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 index 581b35ac..fcea2fcd 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/LocalUpdatesProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/LocalUpdatesProvider.java @@ -9,7 +9,7 @@ import pro.gravit.launchserver.modules.events.LaunchServerUpdatesSyncEvent; import pro.gravit.utils.helper.IOHelper; -import java.io.IOException; +import java.io.*; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; @@ -153,6 +153,14 @@ public void upload(String updateName, Map files, boolean deleteAft } } + @Override + public OutputStream upload(String updateName, String file) throws IOException { + var path = resolveUpdateName(updateName); + var target = path.resolve(file); + IOHelper.createParentDirs(target); + return new FileOutputStream(target.toFile()); + } + @Override public Map download(String updateName, List files) { var path = resolveUpdateName(updateName); @@ -163,6 +171,39 @@ public Map download(String updateName, List files) { return map; } + @Override + public void download(String updateName, Map files) throws IOException { + var path = resolveUpdateName(updateName); + for(var e : files.entrySet()) { + var source = path.resolve(e.getKey()); + var target = e.getValue(); + IOHelper.copy(source, target); + } + } + + @Override + public InputStream download(String updateName, String path) throws IOException { + return new FileInputStream(resolveUpdateName(updateName).resolve(path).toFile()); + } + + @Override + public void move(Map files) throws IOException { + for(var e : files.entrySet()) { + var source = resolveUpdateName(e.getKey().updateName()).resolve(e.getKey().path()); + var target = resolveUpdateName(e.getValue().updateName()).resolve(e.getValue().path()); + IOHelper.move(source, target); + } + } + + @Override + public void copy(Map files) throws IOException { + for(var e : files.entrySet()) { + var source = resolveUpdateName(e.getKey().updateName()).resolve(e.getKey().path()); + var target = resolveUpdateName(e.getValue().updateName()).resolve(e.getValue().path()); + IOHelper.copy(source, target); + } + } + @Override public void delete(String updateName, List files) throws IOException { var path = resolveUpdateName(updateName); 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 index f7677ea8..52fdd9c3 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/UpdatesProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/UpdatesProvider.java @@ -1,12 +1,22 @@ package pro.gravit.launchserver.auth.updates; import pro.gravit.launcher.core.hasher.HashedDir; +import pro.gravit.launcher.core.hasher.HashedEntry; import pro.gravit.launchserver.LaunchServer; import pro.gravit.utils.ProviderMap; +import pro.gravit.utils.helper.IOHelper; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,7 +48,55 @@ public void sync() throws IOException { public abstract void upload(String updateName, Map files, boolean deleteAfterUpload) throws IOException; - public abstract Map download(String updateName, List files); + public abstract OutputStream upload(String updateName, String file) throws IOException; + + public void upload(String updateName, Path dir, boolean deleteAfterUpload) throws IOException { + if(!Files.isDirectory(dir)) { + throw new UnsupportedEncodingException(String.format("%s is not a directory", dir)); + } + Map map = new HashMap<>(); + IOHelper.walk(dir, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + map.put(dir.relativize(file).toString(), file); + return FileVisitResult.CONTINUE; + } + }, true); + upload(updateName, map, deleteAfterUpload); + } + + public abstract Map download(String updateName, List files) throws IOException; + + public abstract void download(String updateName, Map files) throws IOException; + + public abstract InputStream download(String updateName, String path) throws IOException; + + public abstract void move(Map files) throws IOException; + + public void move(String updateName, String newUpdateName) throws IOException { + create(newUpdateName); + var updatesDir = getUpdatesDir(updateName); + Map map = new HashMap<>(); + updatesDir.walk("/", (path, name, entry) -> { + map.put(UpdateNameAndFile.of(updateName, path), UpdateNameAndFile.of(newUpdateName, path)); + return HashedDir.WalkAction.CONTINUE; + }); + move(map); + delete(updateName); + } + + public abstract void copy(Map files) throws IOException; + + public void copy(String updateName, String newUpdateName) throws IOException { + create(newUpdateName); + var updatesDir = getUpdatesDir(updateName); + Map map = new HashMap<>(); + updatesDir.walk("/", (path, name, entry) -> { + map.put(UpdateNameAndFile.of(updateName, path), UpdateNameAndFile.of(newUpdateName, path)); + return HashedDir.WalkAction.CONTINUE; + }); + copy(map); + } public abstract void delete(String updateName, List files) throws IOException; @@ -49,4 +107,10 @@ public void sync() throws IOException { public void close() { } + + public record UpdateNameAndFile(String updateName, String path) { + public static UpdateNameAndFile of(String updateName, String path) { + return new UpdateNameAndFile(updateName, path); + } + } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadAssetCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadAssetCommand.java index ba005879..977f0fd7 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadAssetCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadAssetCommand.java @@ -44,7 +44,8 @@ public void invoke(String... args) throws Exception { String versionName = args[0]; String dirName = IOHelper.verifyFileName(args.length > 1 ? args[1] : "assets"); String type = args.length > 2 ? args[2] : "mojang"; - Path assetDir = server.updatesDir.resolve(dirName); + Path assetDir = server.createTempDirectory("assets"); + var updatesDir = server.config.updatesProvider.getUpdatesDir(dirName); // Create asset dir if (Files.notExists(assetDir)) { @@ -91,14 +92,8 @@ public void invoke(String... args) throws Exception { hash = hash.substring(0, 2) + "/" + hash; var size = value.get("size").getAsLong(); var path = "objects/" + hash; - var target = assetDir.resolve(path); - if (Files.exists(target)) { - long fileSize = Files.size(target); - if (fileSize != size) { - logger.warn("File {} corrupted. Size {}, expected {}", target, size, fileSize); - } else { - continue; - } + if (updatesDir.findRecursive(path).isFound()) { + continue; } toDownload.add(new Downloader.SizedFile(hash, path, size)); } @@ -112,6 +107,8 @@ public void invoke(String... args) throws Exception { server.mirrorManager.downloadZip(assetDir, "assets/%s.zip", versionName); } + server.config.updatesProvider.upload(dirName, assetDir, true); + // Finished server.syncUpdatesDir(Collections.singleton(dirName)); logger.info("Asset successfully downloaded: '{}'", dirName); 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 041825a1..c13a5a5c 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 @@ -42,7 +42,7 @@ public void invoke(String... args) throws IOException, CommandException { //Version version = Version.byName(args[0]); String versionName = args[0]; String dirName = IOHelper.verifyFileName(args[1] != null ? args[1] : args[0]); - Path clientDir = server.updatesDir.resolve(dirName); + Path clientDir = server.createTempDirectory("client"); boolean isMirrorClientDownload = false; if (args.length > 2) { @@ -97,6 +97,7 @@ public void invoke(String... args) throws IOException, CommandException { } } server.config.profileProvider.addProfile(clientProfile); + server.config.updatesProvider.upload(dirName, clientDir, true); // Finished server.syncProfilesDir(); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/IndexAssetCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/IndexAssetCommand.java index 8c7fe611..fe248417 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/IndexAssetCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/IndexAssetCommand.java @@ -19,6 +19,8 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; public final class IndexAssetCommand extends Command { public static final String INDEXES_DIR = "indexes"; @@ -54,24 +56,21 @@ public void invoke(String... args) throws Exception { String inputAssetDirName = IOHelper.verifyFileName(args[0]); String indexFileName = IOHelper.verifyFileName(args[1]); String outputAssetDirName = IOHelper.verifyFileName(args[2]); - Path inputAssetDir = server.updatesDir.resolve(inputAssetDirName); - Path outputAssetDir = server.updatesDir.resolve(outputAssetDirName); - if (outputAssetDir.equals(inputAssetDir)) - throw new CommandException("Unindexed and indexed asset dirs can't be same"); - - // Create new asset dir - logger.info("Creating indexed asset dir: '{}'", outputAssetDirName); - Files.createDirectory(outputAssetDir); + Path inputAssetDir = Path.of(inputAssetDirName); + Map uploadMap = new HashMap<>(); // Index objects JsonObject objects = new JsonObject(); logger.info("Indexing objects"); - IOHelper.walk(inputAssetDir, new IndexAssetVisitor(objects, inputAssetDir, outputAssetDir), false); + IOHelper.walk(inputAssetDir, new IndexAssetVisitor(objects, inputAssetDir, uploadMap), false); + server.config.updatesProvider.upload(outputAssetDirName, uploadMap, false); // Write index file logger.info("Writing asset index file: '{}'", indexFileName); - try (BufferedWriter writer = IOHelper.newWriter(resolveIndexFile(outputAssetDir, indexFileName))) { + var indexFile = resolveIndexFile(Path.of(""), indexFileName); + + try (BufferedWriter writer = IOHelper.newWriter(server.config.updatesProvider.upload(outputAssetDirName, indexFile.toString()))) { JsonObject result = new JsonObject(); result.add("objects", objects); writer.write(Launcher.gsonManager.gson.toJson(result)); @@ -95,12 +94,12 @@ public IndexObject(long size, String hash) { private final class IndexAssetVisitor extends SimpleFileVisitor { private final JsonObject objects; private final Path inputAssetDir; - private final Path outputAssetDir; + private final Map uploadMap; - private IndexAssetVisitor(JsonObject objects, Path inputAssetDir, Path outputAssetDir) { + private IndexAssetVisitor(JsonObject objects, Path inputAssetDir, Map uploadMap) { this.objects = objects; this.inputAssetDir = inputAssetDir; - this.outputAssetDir = outputAssetDir; + this.uploadMap = uploadMap; } @Override @@ -112,7 +111,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO String digest = SecurityHelper.toHex(SecurityHelper.digest(DigestAlgorithm.SHA1, file)); IndexObject obj = new IndexObject(attrs.size(), digest); objects.add(name, Launcher.gsonManager.gson.toJsonTree(obj)); - IOHelper.copy(file, resolveObjectFile(outputAssetDir, digest)); + uploadMap.put(resolveObjectFile(Path.of(""), digest).toString(), file); // Continue visiting return super.visitFile(file, attrs); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/UnindexAssetCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/UnindexAssetCommand.java index 1293172f..3d596998 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/UnindexAssetCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/UnindexAssetCommand.java @@ -14,6 +14,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; +import java.util.List; import java.util.Map; public final class UnindexAssetCommand extends Command { @@ -39,10 +40,8 @@ public void invoke(String... args) throws Exception { String inputAssetDirName = IOHelper.verifyFileName(args[0]); String indexFileName = IOHelper.verifyFileName(args[1]); String outputAssetDirName = IOHelper.verifyFileName(args[2]); - Path inputAssetDir = server.updatesDir.resolve(inputAssetDirName); - Path outputAssetDir = server.updatesDir.resolve(outputAssetDirName); - if (outputAssetDir.equals(inputAssetDir)) - throw new CommandException("Indexed and unindexed asset dirs can't be same"); + var updatesDir = server.config.updatesProvider.getUpdatesDir(inputAssetDirName); + Path outputAssetDir = Path.of(outputAssetDirName); // Create new asset dir logger.info("Creating unindexed asset dir: '{}'", outputAssetDirName); @@ -51,7 +50,8 @@ public void invoke(String... args) throws Exception { // Read JSON file JsonObject objects; logger.info("Reading asset index file: '{}'", indexFileName); - try (BufferedReader reader = IOHelper.newReader(IndexAssetCommand.resolveIndexFile(inputAssetDir, indexFileName))) { + Path indexFilePath = IndexAssetCommand.resolveIndexFile(Path.of(""), indexFileName); + try (BufferedReader reader = IOHelper.newReader(server.config.updatesProvider.download(inputAssetDirName, indexFilePath.toString()))) { objects = JsonParser.parseReader(reader).getAsJsonObject().get("objects").getAsJsonObject(); } @@ -63,12 +63,12 @@ public void invoke(String... args) throws Exception { // Copy hashed file to target String hash = member.getValue().getAsJsonObject().get("hash").getAsString(); - Path source = IndexAssetCommand.resolveObjectFile(inputAssetDir, hash); - IOHelper.copy(source, outputAssetDir.resolve(name)); + Path source = IndexAssetCommand.resolveObjectFile(Path.of(""), hash); + server.config.updatesProvider.download(inputAssetDirName, Map.of(source.toString(), outputAssetDir.resolve(name))); } // Finished server.syncUpdatesDir(Collections.singleton(outputAssetDirName)); - logger.info("Asset successfully unindexed: '{}'", inputAssetDirName); + logger.info("Asset successfully unindexed: '{}'", outputAssetDir.toAbsolutePath().toString()); } } 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 4ca43720..ba017cfd 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 @@ -48,17 +48,7 @@ public void invoke(String... args) throws Exception { profile.getServers().getFirst().name = args[1]; } logger.info("Copy {} to {}", profile.getDir(), args[1]); - var src = server.updatesDir.resolve(profile.getDir()); - var dest = server.updatesDir.resolve(args[1]); - try (Stream stream = Files.walk(src)) { - stream.forEach(source -> { - try { - IOHelper.copy(source, dest.resolve(src.relativize(source))); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } + server.config.updatesProvider.copy(profile.getDir(), args[1]); builder.setDir(args[1]); profile = builder.createClientProfile(); server.config.profileProvider.addProfile(profile); 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 21e8448d..f8b580ff 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 @@ -7,6 +7,8 @@ import pro.gravit.launchserver.command.Command; import pro.gravit.launchserver.helper.MakeProfileHelper; +import java.nio.file.Path; + public class MakeProfileCommand extends Command { private transient final Logger logger = LogManager.getLogger(); @@ -28,7 +30,7 @@ public String getUsageDescription() { public void invoke(String... args) throws Exception { verifyArgs(args, 3); ClientProfile.Version version = parseClientVersion(args[1]); - MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(server.updatesDir.resolve(args[2]), version); + MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(Path.of(args[2]), version); for (MakeProfileHelper.MakeProfileOption option : options) { logger.info("Detected option {}", option); } diff --git a/LauncherCore/src/main/java/pro/gravit/launcher/core/hasher/HashedDir.java b/LauncherCore/src/main/java/pro/gravit/launcher/core/hasher/HashedDir.java index 93bbd315..5a6f45f4 100644 --- a/LauncherCore/src/main/java/pro/gravit/launcher/core/hasher/HashedDir.java +++ b/LauncherCore/src/main/java/pro/gravit/launcher/core/hasher/HashedDir.java @@ -295,6 +295,10 @@ public FindRecursiveResult(HashedDir parent, HashedEntry entry, String name) { this.entry = entry; this.name = name; } + + public boolean isFound() { + return entry != null; + } } public static final class Diff { diff --git a/modules b/modules index 5d45e024..287d0b83 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit 5d45e024375779aef4f6dafff6ea5efba8c250f0 +Subproject commit 287d0b83b3c71fc7146e2b1cba7bc1760629ade1