mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-06-05 13:57:00 +03:00
[FEATURE][EXPERIMENTAL] Full support UpdatesProvider
This commit is contained in:
parent
1e4e1c5837
commit
e664c579a5
11 changed files with 152 additions and 47 deletions
|
@ -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);
|
||||
|
|
|
@ -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<String, Path> 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<String, Path> download(String updateName, List<String> files) {
|
||||
var path = resolveUpdateName(updateName);
|
||||
|
@ -163,6 +171,39 @@ public Map<String, Path> download(String updateName, List<String> files) {
|
|||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void download(String updateName, Map<String, Path> 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<UpdateNameAndFile, UpdateNameAndFile> 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<UpdateNameAndFile, UpdateNameAndFile> 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<String> files) throws IOException {
|
||||
var path = resolveUpdateName(updateName);
|
||||
|
|
|
@ -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<String, Path> files, boolean deleteAfterUpload) throws IOException;
|
||||
|
||||
public abstract Map<String, Path> download(String updateName, List<String> 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<String, Path> 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<String, Path> download(String updateName, List<String> files) throws IOException;
|
||||
|
||||
public abstract void download(String updateName, Map<String, Path> files) throws IOException;
|
||||
|
||||
public abstract InputStream download(String updateName, String path) throws IOException;
|
||||
|
||||
public abstract void move(Map<UpdateNameAndFile, UpdateNameAndFile> files) throws IOException;
|
||||
|
||||
public void move(String updateName, String newUpdateName) throws IOException {
|
||||
create(newUpdateName);
|
||||
var updatesDir = getUpdatesDir(updateName);
|
||||
Map<UpdateNameAndFile, UpdateNameAndFile> 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<UpdateNameAndFile, UpdateNameAndFile> files) throws IOException;
|
||||
|
||||
public void copy(String updateName, String newUpdateName) throws IOException {
|
||||
create(newUpdateName);
|
||||
var updatesDir = getUpdatesDir(updateName);
|
||||
Map<UpdateNameAndFile, UpdateNameAndFile> 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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<String, Path> 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<Path> {
|
||||
private final JsonObject objects;
|
||||
private final Path inputAssetDir;
|
||||
private final Path outputAssetDir;
|
||||
private final Map<String, Path> uploadMap;
|
||||
|
||||
private IndexAssetVisitor(JsonObject objects, Path inputAssetDir, Path outputAssetDir) {
|
||||
private IndexAssetVisitor(JsonObject objects, Path inputAssetDir, Map<String, Path> 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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Path> 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
|||
Subproject commit 5d45e024375779aef4f6dafff6ea5efba8c250f0
|
||||
Subproject commit 287d0b83b3c71fc7146e2b1cba7bc1760629ade1
|
Loading…
Reference in a new issue