[FEATURE][EXPERIMENTAL] Копирование модов из других клиентов

This commit is contained in:
Gravit 2019-05-20 06:20:06 +07:00
parent a11764054c
commit c6389c697b
7 changed files with 206 additions and 8 deletions

View file

@ -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");

View file

@ -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

View file

@ -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) {
}
}

View file

@ -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]");
}
}
}
}

View file

@ -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());

View file

@ -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;

View file

@ -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;
} }
} }