mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-11-15 03:31:15 +03:00
[FEATURE][EXPERIMENTAL] Копирование модов из других клиентов
This commit is contained in:
parent
a11764054c
commit
c6389c697b
7 changed files with 206 additions and 8 deletions
|
@ -3,6 +3,7 @@
|
||||||
import ru.gravit.launcher.client.ClientModuleManager;
|
import ru.gravit.launcher.client.ClientModuleManager;
|
||||||
import ru.gravit.launcher.client.DirBridge;
|
import ru.gravit.launcher.client.DirBridge;
|
||||||
import ru.gravit.launcher.client.FunctionalBridge;
|
import ru.gravit.launcher.client.FunctionalBridge;
|
||||||
|
import ru.gravit.launcher.client.LauncherUpdateController;
|
||||||
import ru.gravit.launcher.guard.LauncherGuardManager;
|
import ru.gravit.launcher.guard.LauncherGuardManager;
|
||||||
import ru.gravit.launcher.gui.JSRuntimeProvider;
|
import ru.gravit.launcher.gui.JSRuntimeProvider;
|
||||||
import ru.gravit.launcher.gui.RuntimeProvider;
|
import ru.gravit.launcher.gui.RuntimeProvider;
|
||||||
|
@ -11,6 +12,7 @@
|
||||||
import ru.gravit.launcher.request.Request;
|
import ru.gravit.launcher.request.Request;
|
||||||
import ru.gravit.launcher.request.RequestException;
|
import ru.gravit.launcher.request.RequestException;
|
||||||
import ru.gravit.launcher.request.auth.RestoreSessionRequest;
|
import ru.gravit.launcher.request.auth.RestoreSessionRequest;
|
||||||
|
import ru.gravit.launcher.request.update.UpdateRequest;
|
||||||
import ru.gravit.launcher.request.websockets.StandartClientWebSocketService;
|
import ru.gravit.launcher.request.websockets.StandartClientWebSocketService;
|
||||||
import ru.gravit.utils.helper.CommonHelper;
|
import ru.gravit.utils.helper.CommonHelper;
|
||||||
import ru.gravit.utils.helper.EnvHelper;
|
import ru.gravit.utils.helper.EnvHelper;
|
||||||
|
@ -95,6 +97,7 @@ public void start(String... args) throws Throwable {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
LauncherGuardManager.initGuard(false);
|
LauncherGuardManager.initGuard(false);
|
||||||
|
UpdateRequest.setController(new LauncherUpdateController());
|
||||||
Objects.requireNonNull(args, "args");
|
Objects.requireNonNull(args, "args");
|
||||||
if (started.getAndSet(true))
|
if (started.getAndSet(true))
|
||||||
throw new IllegalStateException("Launcher has been already started");
|
throw new IllegalStateException("Launcher has been already started");
|
||||||
|
|
|
@ -37,6 +37,8 @@ public class NewLauncherSettings {
|
||||||
public List<ClientProfile> lastProfiles = new LinkedList<>();
|
public List<ClientProfile> lastProfiles = new LinkedList<>();
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public Map<String, UserSettings> userSettings = new HashMap<>();
|
public Map<String, UserSettings> userSettings = new HashMap<>();
|
||||||
|
@LauncherAPI
|
||||||
|
public boolean featureStore;
|
||||||
|
|
||||||
public static class HashedStoreEntry {
|
public static class HashedStoreEntry {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
package ru.gravit.launcher.client;
|
||||||
|
|
||||||
|
import ru.gravit.launcher.NewLauncherSettings;
|
||||||
|
import ru.gravit.launcher.downloader.ListDownloader;
|
||||||
|
import ru.gravit.launcher.events.request.UpdateRequestEvent;
|
||||||
|
import ru.gravit.launcher.hasher.HashedDir;
|
||||||
|
import ru.gravit.launcher.hasher.HashedEntry;
|
||||||
|
import ru.gravit.launcher.hasher.HashedFile;
|
||||||
|
import ru.gravit.launcher.managers.SettingsManager;
|
||||||
|
import ru.gravit.launcher.request.update.UpdateRequest;
|
||||||
|
import ru.gravit.utils.helper.IOHelper;
|
||||||
|
import ru.gravit.utils.helper.LogHelper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
public class LauncherUpdateController implements UpdateRequest.UpdateController {
|
||||||
|
@Override
|
||||||
|
public void preUpdate(UpdateRequest request, UpdateRequestEvent e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preDiff(UpdateRequest request, UpdateRequestEvent e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postDiff(UpdateRequest request, UpdateRequestEvent e, HashedDir.Diff diff) throws IOException {
|
||||||
|
if(e.zip) return;
|
||||||
|
if(SettingsManager.settings.featureStore)
|
||||||
|
{
|
||||||
|
LogHelper.info("Enabled HStore feature. Find");
|
||||||
|
AtomicReference<NewLauncherSettings.HashedStoreEntry> lastEn = null;
|
||||||
|
ArrayList<String> removed = new ArrayList<>();
|
||||||
|
diff.mismatch.walk(File.separator, (path, name, entry) -> {
|
||||||
|
if(entry.getType() == HashedEntry.Type.DIR) return false;
|
||||||
|
HashedFile file = (HashedFile) entry;
|
||||||
|
//Первый экспериментальный способ - честно обходим все возможные Store
|
||||||
|
Path ret = null;
|
||||||
|
if(lastEn.get() == null)
|
||||||
|
{
|
||||||
|
for(NewLauncherSettings.HashedStoreEntry en : SettingsManager.settings.lastHDirs)
|
||||||
|
{
|
||||||
|
ret = tryFind(en, file);
|
||||||
|
if(ret != null) {
|
||||||
|
lastEn.set(en);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ret = tryFind(lastEn.get(), file);
|
||||||
|
}
|
||||||
|
if(ret == null)
|
||||||
|
{
|
||||||
|
for(NewLauncherSettings.HashedStoreEntry en : SettingsManager.settings.lastHDirs)
|
||||||
|
{
|
||||||
|
ret = tryFind(en, file);
|
||||||
|
if(ret != null) {
|
||||||
|
lastEn.set(en);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ret != null)
|
||||||
|
{
|
||||||
|
//Еще раз проверим корректность хеша
|
||||||
|
//Возможно эта проверка избыточна
|
||||||
|
if(file.isSame(ret, true))
|
||||||
|
{
|
||||||
|
Path source = request.dir.resolve(path).resolve(name);
|
||||||
|
LogHelper.debug("Copy file %s to %s", source.toAbsolutePath().toString(), ret.toAbsolutePath().toString());
|
||||||
|
//Let's go!
|
||||||
|
Files.copy(ret, source);
|
||||||
|
removed.add(path.concat(File.separator).concat(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
for(String rem : removed)
|
||||||
|
{
|
||||||
|
diff.mismatch.removeR(rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public Path tryFind(NewLauncherSettings.HashedStoreEntry en, HashedFile file) throws IOException
|
||||||
|
{
|
||||||
|
AtomicReference<Path> ret = null;
|
||||||
|
en.hdir.walk(File.separator, (path, name, entry) -> {
|
||||||
|
if(entry.getType() == HashedEntry.Type.DIR) return false;
|
||||||
|
HashedFile tfile = (HashedFile) entry;
|
||||||
|
if(tfile.isSame(file))
|
||||||
|
{
|
||||||
|
LogHelper.debug("[DIR:%s] Found file %s in %s", en.name, name, path);
|
||||||
|
Path tdir = Paths.get(en.fullPath).resolve(path).resolve(name);
|
||||||
|
try {
|
||||||
|
if(tfile.isSame(tdir, true))
|
||||||
|
{
|
||||||
|
LogHelper.debug("[DIR:%s] Confirmed file %s in %s", en.name, name, path);
|
||||||
|
ret.set(tdir);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (IOException e)
|
||||||
|
{
|
||||||
|
LogHelper.error("Check file error %s", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return ret.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preDownload(UpdateRequest request, UpdateRequestEvent e, List<ListDownloader.DownloadTask> adds) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postDownload(UpdateRequest request, UpdateRequestEvent e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postUpdate(UpdateRequest request, UpdateRequestEvent e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package ru.gravit.launcher.console;
|
||||||
|
|
||||||
|
import ru.gravit.launcher.managers.SettingsManager;
|
||||||
|
import ru.gravit.utils.command.Command;
|
||||||
|
import ru.gravit.utils.helper.LogHelper;
|
||||||
|
|
||||||
|
public class FeatureCommand extends Command {
|
||||||
|
@Override
|
||||||
|
public String getArgsDescription() {
|
||||||
|
return "[feature] [true/false]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsageDescription() {
|
||||||
|
return "Enable or disable feature";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(String... args) throws Exception {
|
||||||
|
verifyArgs(args, 2);
|
||||||
|
boolean enabled = Boolean.valueOf(args[1]);
|
||||||
|
switch (args[0])
|
||||||
|
{
|
||||||
|
case "store":
|
||||||
|
{
|
||||||
|
SettingsManager.settings.featureStore = enabled;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
LogHelper.info("Features: [store]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package ru.gravit.launcher.managers;
|
package ru.gravit.launcher.managers;
|
||||||
|
|
||||||
|
import ru.gravit.launcher.console.FeatureCommand;
|
||||||
import ru.gravit.launcher.console.UnlockCommand;
|
import ru.gravit.launcher.console.UnlockCommand;
|
||||||
import ru.gravit.launcher.console.admin.ExecCommand;
|
import ru.gravit.launcher.console.admin.ExecCommand;
|
||||||
import ru.gravit.launcher.console.admin.LogListenerCommand;
|
import ru.gravit.launcher.console.admin.LogListenerCommand;
|
||||||
|
@ -54,6 +55,7 @@ public static boolean checkUnlockKey(String key) {
|
||||||
|
|
||||||
public static void unlock() {
|
public static void unlock() {
|
||||||
handler.registerCommand("debug", new DebugCommand());
|
handler.registerCommand("debug", new DebugCommand());
|
||||||
|
handler.registerCommand("feature", new FeatureCommand());
|
||||||
BaseCommandCategory admin = new BaseCommandCategory();
|
BaseCommandCategory admin = new BaseCommandCategory();
|
||||||
admin.registerCommand("exec", new ExecCommand());
|
admin.registerCommand("exec", new ExecCommand());
|
||||||
admin.registerCommand("logListen", new LogListenerCommand());
|
admin.registerCommand("logListen", new LogListenerCommand());
|
||||||
|
|
|
@ -27,6 +27,20 @@
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public final class UpdateRequest extends Request<UpdateRequestEvent> implements RequestInterface {
|
public final class UpdateRequest extends Request<UpdateRequestEvent> implements RequestInterface {
|
||||||
|
public interface UpdateController
|
||||||
|
{
|
||||||
|
void preUpdate(UpdateRequest request, UpdateRequestEvent e) throws IOException;
|
||||||
|
void preDiff(UpdateRequest request, UpdateRequestEvent e) throws IOException;
|
||||||
|
void postDiff(UpdateRequest request, UpdateRequestEvent e,HashedDir.Diff diff) throws IOException;
|
||||||
|
void preDownload(UpdateRequest request, UpdateRequestEvent e, List<ListDownloader.DownloadTask> adds) throws IOException;
|
||||||
|
void postDownload(UpdateRequest request, UpdateRequestEvent e) throws IOException;
|
||||||
|
void postUpdate(UpdateRequest request, UpdateRequestEvent e) throws IOException;
|
||||||
|
}
|
||||||
|
private static UpdateController controller;
|
||||||
|
|
||||||
|
public static void setController(UpdateController controller) {
|
||||||
|
UpdateRequest.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getType() {
|
public String getType() {
|
||||||
|
@ -170,10 +184,14 @@ public double getTotalSizeMiB() {
|
||||||
public UpdateRequestEvent requestDo(StandartClientWebSocketService service) throws Exception {
|
public UpdateRequestEvent requestDo(StandartClientWebSocketService service) throws Exception {
|
||||||
LogHelper.debug("Start update request");
|
LogHelper.debug("Start update request");
|
||||||
UpdateRequestEvent e = (UpdateRequestEvent) service.sendRequest(this);
|
UpdateRequestEvent e = (UpdateRequestEvent) service.sendRequest(this);
|
||||||
|
if(controller != null) controller.preUpdate(this, e);
|
||||||
LogHelper.debug("Start update");
|
LogHelper.debug("Start update");
|
||||||
Launcher.profile.pushOptionalFile(e.hdir, !Launcher.profile.isUpdateFastCheck());
|
Launcher.profile.pushOptionalFile(e.hdir, !Launcher.profile.isUpdateFastCheck());
|
||||||
|
if(controller != null) controller.preDiff(this, e);
|
||||||
HashedDir.Diff diff = e.hdir.diff(localDir, matcher);
|
HashedDir.Diff diff = e.hdir.diff(localDir, matcher);
|
||||||
|
if(controller != null) controller.postDiff(this, e, diff);
|
||||||
final List<ListDownloader.DownloadTask> adds = new ArrayList<>();
|
final List<ListDownloader.DownloadTask> adds = new ArrayList<>();
|
||||||
|
if(controller != null) controller.preDownload(this, e, adds);
|
||||||
diff.mismatch.walk(IOHelper.CROSS_SEPARATOR, (path, name, entry) -> {
|
diff.mismatch.walk(IOHelper.CROSS_SEPARATOR, (path, name, entry) -> {
|
||||||
if (entry.getType().equals(HashedEntry.Type.FILE)) {
|
if (entry.getType().equals(HashedEntry.Type.FILE)) {
|
||||||
HashedFile file = (HashedFile) entry;
|
HashedFile file = (HashedFile) entry;
|
||||||
|
@ -186,6 +204,7 @@ public UpdateRequestEvent requestDo(StandartClientWebSocketService service) thro
|
||||||
LogHelper.error(ex);
|
LogHelper.error(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
totalSize = diff.mismatch.size();
|
totalSize = diff.mismatch.size();
|
||||||
startTime = Instant.now();
|
startTime = Instant.now();
|
||||||
|
@ -199,7 +218,9 @@ public UpdateRequestEvent requestDo(StandartClientWebSocketService service) thro
|
||||||
{
|
{
|
||||||
listDownloader.download(e.url, adds, dir, this::updateState, (add) -> totalDownloaded += add);
|
listDownloader.download(e.url, adds, dir, this::updateState, (add) -> totalDownloaded += add);
|
||||||
}
|
}
|
||||||
|
if(controller != null) controller.postDownload(this, e);
|
||||||
deleteExtraDir(dir, diff.extra, diff.extra.flag);
|
deleteExtraDir(dir, diff.extra, diff.extra.flag);
|
||||||
|
if(controller != null) controller.postUpdate(this, e);
|
||||||
LogHelper.debug("Update success");
|
LogHelper.debug("Update success");
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
@ -207,7 +228,7 @@ public UpdateRequestEvent requestDo(StandartClientWebSocketService service) thro
|
||||||
// Instance
|
// Instance
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
private final String dirName;
|
private final String dirName;
|
||||||
private transient final Path dir;
|
public transient final Path dir;
|
||||||
private transient final FileNameMatcher matcher;
|
private transient final FileNameMatcher matcher;
|
||||||
|
|
||||||
private transient final boolean digest;
|
private transient final boolean digest;
|
||||||
|
|
|
@ -338,31 +338,32 @@ public void write(HOutput output) throws IOException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void walk(CharSequence separator, WalkCallback callback) {
|
public void walk(CharSequence separator, WalkCallback callback) throws IOException {
|
||||||
String append = "";
|
String append = "";
|
||||||
walk(append, separator, callback, true);
|
walk(append, separator, callback, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface WalkCallback {
|
public interface WalkCallback {
|
||||||
void walked(String path, String name, HashedEntry entry);
|
boolean walked(String path, String name, HashedEntry entry) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void walk(String append, CharSequence separator, WalkCallback callback, boolean noSeparator) {
|
private boolean walk(String append, CharSequence separator, WalkCallback callback, boolean noSeparator) throws IOException {
|
||||||
for (Map.Entry<String, HashedEntry> entry : map.entrySet()) {
|
for (Map.Entry<String, HashedEntry> entry : map.entrySet()) {
|
||||||
HashedEntry e = entry.getValue();
|
HashedEntry e = entry.getValue();
|
||||||
if (e.getType() == Type.FILE) {
|
if (e.getType() == Type.FILE) {
|
||||||
if (noSeparator)
|
if (noSeparator)
|
||||||
callback.walked(append + entry.getKey(), entry.getKey(), e);
|
if(callback.walked(append + entry.getKey(), entry.getKey(), e)) return true;
|
||||||
else
|
else
|
||||||
callback.walked(append + separator + entry.getKey(), entry.getKey(), e);
|
if(callback.walked(append + separator + entry.getKey(), entry.getKey(), e)) return true;
|
||||||
} else {
|
} else {
|
||||||
String newAppend;
|
String newAppend;
|
||||||
if (noSeparator) newAppend = append + entry.getKey();
|
if (noSeparator) newAppend = append + entry.getKey();
|
||||||
else newAppend = append + separator + entry.getKey();
|
else newAppend = append + separator + entry.getKey();
|
||||||
callback.walked(newAppend, entry.getKey(), e);
|
if(callback.walked(newAppend, entry.getKey(), e)) return true;
|
||||||
((HashedDir) e).walk(newAppend, separator, callback, false);
|
if(((HashedDir) e).walk(newAppend, separator, callback, false)) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue