[FEATURE] Updates cache

This commit is contained in:
Gravita 2021-06-01 05:48:33 +07:00
parent 8445ed2dca
commit c719f2448e
6 changed files with 136 additions and 41 deletions

View file

@ -46,7 +46,6 @@
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Stream;
/** /**
* The main LaunchServer class. Contains links to all necessary objects * The main LaunchServer class. Contains links to all necessary objects
@ -115,6 +114,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
public final PingServerManager pingServerManager; public final PingServerManager pingServerManager;
public final FeaturesManager featuresManager; public final FeaturesManager featuresManager;
public final KeyAgreementManager keyAgreementManager; public final KeyAgreementManager keyAgreementManager;
public final UpdatesManager updatesManager;
// HWID ban + anti-brutforce // HWID ban + anti-brutforce
public final CertificateManager certificateManager; public final CertificateManager certificateManager;
// Server // Server
@ -127,6 +127,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
public final LauncherModuleLoader launcherModuleLoader; public final LauncherModuleLoader launcherModuleLoader;
private final Logger logger = LogManager.getLogger(); private final Logger logger = LogManager.getLogger();
public LaunchServerConfig config; public LaunchServerConfig config;
@Deprecated
public volatile Map<String, HashedDir> updatesDirMap; public volatile Map<String, HashedDir> updatesDirMap;
// Updates and profiles // Updates and profiles
private volatile Set<ClientProfile> profilesList; private volatile Set<ClientProfile> profilesList;
@ -173,6 +174,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
pingServerManager = new PingServerManager(this); pingServerManager = new PingServerManager(this);
featuresManager = new FeaturesManager(this); featuresManager = new FeaturesManager(this);
authManager = new AuthManager(this); authManager = new AuthManager(this);
updatesManager = new UpdatesManager(this);
RestoreResponse.registerProviders(this); RestoreResponse.registerProviders(this);
//Generate or set new Certificate API //Generate or set new Certificate API
certificateManager.orgName = config.projectName; certificateManager.orgName = config.projectName;
@ -316,10 +318,12 @@ public void setProfiles(Set<ClientProfile> profilesList) {
this.profilesList = Collections.unmodifiableSet(profilesList); this.profilesList = Collections.unmodifiableSet(profilesList);
} }
@Deprecated
public HashedDir getUpdateDir(String name) { public HashedDir getUpdateDir(String name) {
return updatesDirMap.get(name); return updatesDirMap.get(name);
} }
@Deprecated
public Set<Entry<String, HashedDir>> getUpdateDirs() { public Set<Entry<String, HashedDir>> getUpdateDirs() {
return updatesDirMap.entrySet(); return updatesDirMap.entrySet();
} }
@ -349,8 +353,7 @@ public void run() {
try { try {
if (!IOHelper.isDir(updatesDir)) if (!IOHelper.isDir(updatesDir))
Files.createDirectory(updatesDir); Files.createDirectory(updatesDir);
syncUpdatesDir(null); updatesManager.readUpdatesDir();
modulesManager.invokeEvent(new LaunchServerUpdatesSyncEvent(this));
// Sync profiles dir // Sync profiles dir
if (!IOHelper.isDir(profilesDir)) if (!IOHelper.isDir(profilesDir))
@ -402,37 +405,7 @@ public void syncProfilesDir() throws IOException {
} }
public void syncUpdatesDir(Collection<String> dirs) throws IOException { public void syncUpdatesDir(Collection<String> dirs) throws IOException {
logger.info("Syncing updates dir"); updatesManager.syncUpdatesDir(dirs);
Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(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);
} }
public void restart() { public void restart() {

View file

@ -35,6 +35,7 @@ public final class LaunchServerConfig {
public String[] mirrors; public String[] mirrors;
public String binaryName; public String binaryName;
public boolean copyBinaries = true; public boolean copyBinaries = true;
public boolean cacheUpdates = true;
public LauncherConfig.LauncherEnvironment env; public LauncherConfig.LauncherEnvironment env;
public Map<String, AuthProviderPair> auth; public Map<String, AuthProviderPair> auth;
@Deprecated @Deprecated

View file

@ -0,0 +1,125 @@
package pro.gravit.launchserver.manangers;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.launcher.serialize.HInput;
import pro.gravit.launcher.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 UpdatesManager {
private final LaunchServer server;
private final Logger logger = LogManager.getLogger();
private volatile Map<String, HashedDir> updatesDirMap;
private Path cacheFile;
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<String, HashedDir> 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<String, HashedDir> 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 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);
}
public void syncUpdatesDir(Collection<String> dirs) throws IOException {
logger.info("Syncing updates dir");
Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16);
try (DirectoryStream<Path> 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));
}
public HashSet<String> getUpdatesList() {
HashSet<String> set = new HashSet<>();
for (Map.Entry<String, HashedDir> entry : updatesDirMap.entrySet())
set.add(entry.getKey());
return set;
}
public HashedDir getUpdate(String name) {
return updatesDirMap.get(name);
}
public void addUpdate(String name, HashedDir dir) {
updatesDirMap.put(name, dir);
}
}

View file

@ -2,12 +2,10 @@
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.UpdateListRequestEvent; import pro.gravit.launcher.events.request.UpdateListRequestEvent;
import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
public class UpdateListResponse extends SimpleResponse { public class UpdateListResponse extends SimpleResponse {
@ -22,9 +20,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
sendError("Access denied"); sendError("Access denied");
return; return;
} }
HashSet<String> set = new HashSet<>(); HashSet<String> set = server.updatesManager.getUpdatesList();
for (Map.Entry<String, HashedDir> entry : server.updatesDirMap.entrySet())
set.add(entry.getKey());
sendResult(new UpdateListRequestEvent(set)); sendResult(new UpdateListRequestEvent(set));
} }

View file

@ -27,7 +27,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
sendError("Invalid request"); sendError("Invalid request");
return; return;
} }
HashedDir dir = server.updatesDirMap.get(dirName); HashedDir dir = server.updatesManager.getUpdate(dirName);
if (dir == null) { if (dir == null) {
sendError(String.format("Directory %s not found", dirName)); sendError(String.format("Directory %s not found", dirName));
return; return;

View file

@ -67,7 +67,7 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep
context.javaVersion = version; context.javaVersion = version;
continue; continue;
} }
if (version.enabledJavaFX) { if (version.enabledJavaFX == context.javaVersion.enabledJavaFX) {
if (context.javaVersion.version < version.version) { if (context.javaVersion.version < version.version) {
context.javaVersion = version; context.javaVersion = version;
} else if (context.javaVersion.version == version.version && context.javaVersion.build < version.build) { } else if (context.javaVersion.version == version.version && context.javaVersion.build < version.build) {