Launcher/LaunchServer/src/main/java/ru/gravit/launchserver/LaunchServer.java

604 lines
24 KiB
Java
Raw Normal View History

2018-09-17 10:07:32 +03:00
package ru.gravit.launchserver;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
2018-09-17 10:07:32 +03:00
import java.net.SocketAddress;
import java.nio.file.DirectoryStream;
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.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.time.Duration;
import java.time.Instant;
2018-12-02 15:21:27 +03:00
import java.util.ArrayList;
2018-09-17 10:07:32 +03:00
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.CRC32;
import ru.gravit.launcher.Launcher;
2018-09-17 10:07:32 +03:00
import ru.gravit.launcher.hasher.HashedDir;
import ru.gravit.launcher.managers.GarbageManager;
import ru.gravit.launcher.profiles.ClientProfile;
import ru.gravit.launcher.serialize.config.ConfigObject;
import ru.gravit.launcher.serialize.config.TextConfigReader;
import ru.gravit.launcher.serialize.config.TextConfigWriter;
import ru.gravit.launcher.serialize.config.entry.BlockConfigEntry;
import ru.gravit.launcher.serialize.config.entry.BooleanConfigEntry;
import ru.gravit.launcher.serialize.config.entry.IntegerConfigEntry;
import ru.gravit.launcher.serialize.config.entry.ListConfigEntry;
import ru.gravit.launcher.serialize.config.entry.StringConfigEntry;
2018-09-17 10:07:32 +03:00
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
import ru.gravit.launchserver.auth.AuthLimiter;
import ru.gravit.launchserver.auth.handler.AuthHandler;
import ru.gravit.launchserver.auth.hwid.HWIDHandler;
import ru.gravit.launchserver.auth.provider.AuthProvider;
import ru.gravit.launchserver.binary.EXEL4JLauncherBinary;
import ru.gravit.launchserver.binary.EXELauncherBinary;
import ru.gravit.launchserver.binary.JARLauncherBinary;
import ru.gravit.launchserver.binary.LauncherBinary;
import ru.gravit.launchserver.command.handler.CommandHandler;
import ru.gravit.launchserver.command.handler.JLineCommandHandler;
import ru.gravit.launchserver.command.handler.StdCommandHandler;
import ru.gravit.launchserver.manangers.BuildHookManager;
import ru.gravit.launchserver.manangers.MirrorManager;
2018-09-17 10:07:32 +03:00
import ru.gravit.launchserver.manangers.ModulesManager;
import ru.gravit.launchserver.manangers.SessionManager;
import ru.gravit.launchserver.response.Response;
import ru.gravit.launchserver.socket.ServerSocketHandler;
import ru.gravit.launchserver.texture.TextureProvider;
import ru.gravit.utils.helper.CommonHelper;
import ru.gravit.utils.helper.IOHelper;
import ru.gravit.utils.helper.JVMHelper;
import ru.gravit.utils.helper.LogHelper;
import ru.gravit.utils.helper.SecurityHelper;
import ru.gravit.utils.helper.VerifyHelper;
2018-09-17 10:07:32 +03:00
public final class LaunchServer implements Runnable, AutoCloseable {
public static final class Config extends ConfigObject {
public final int port;
// Handlers & Providers
2018-10-13 11:01:10 +03:00
2018-09-23 15:07:07 +03:00
public final AuthHandler[] authHandler;
2018-10-13 11:01:10 +03:00
2018-09-23 14:42:10 +03:00
public final AuthProvider[] authProvider;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final TextureProvider textureProvider;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final HWIDHandler hwidHandler;
// Misc options
public final int threadCount;
public final int threadCoreCount;
2018-09-17 10:07:32 +03:00
public final ExeConf launch4j;
2018-10-13 11:01:10 +03:00
2018-11-26 10:43:18 +03:00
public final PostBuildTransformConf buildPostTransform;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final boolean compress;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final int authRateLimit;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final int authRateLimitMilis;
2018-10-13 11:01:10 +03:00
public final ListConfigEntry authLimitExclusions;
2018-09-17 10:07:32 +03:00
public final String authRejectString;
2018-10-13 11:01:10 +03:00
public final String projectName;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final String whitelistRejectString;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final boolean genMappings;
public final boolean isUsingWrapper;
public final boolean isDownloadJava;
2018-10-13 11:01:10 +03:00
public ListConfigEntry mirrors;
2018-09-17 10:07:32 +03:00
public final String binaryName;
private final StringConfigEntry address;
private final String bindAddress;
2018-11-08 15:30:16 +03:00
private Config(BlockConfigEntry block, Path coredir, LaunchServer server) {
2018-09-17 10:07:32 +03:00
super(block);
address = block.getEntry("address", StringConfigEntry.class);
port = VerifyHelper.verifyInt(block.getEntryValue("port", IntegerConfigEntry.class),
VerifyHelper.range(0, 65535), "Illegal LaunchServer port");
threadCoreCount = block.hasEntry("threadCoreCacheSize") ? VerifyHelper.verifyInt(block.getEntryValue("threadCoreCacheSize", IntegerConfigEntry.class),
VerifyHelper.range(0, 100), "Illegal LaunchServer inital thread pool cache size") : 0;
int internalThreadCount = block.hasEntry("threadCacheSize") ? VerifyHelper.verifyInt(block.getEntryValue("threadCacheSize", IntegerConfigEntry.class),
VerifyHelper.range(2, 100), "Illegal LaunchServer thread pool cache size") : (JVMHelper.OPERATING_SYSTEM_MXBEAN.getAvailableProcessors() >= 4 ? JVMHelper.OPERATING_SYSTEM_MXBEAN.getAvailableProcessors() / 2 : JVMHelper.OPERATING_SYSTEM_MXBEAN.getAvailableProcessors());
threadCount = threadCoreCount > internalThreadCount ? threadCoreCount : internalThreadCount;
2018-09-17 10:07:32 +03:00
authRateLimit = VerifyHelper.verifyInt(block.getEntryValue("authRateLimit", IntegerConfigEntry.class),
VerifyHelper.range(0, 1000000), "Illegal authRateLimit");
authRateLimitMilis = VerifyHelper.verifyInt(block.getEntryValue("authRateLimitMilis", IntegerConfigEntry.class),
VerifyHelper.range(10, 10000000), "Illegal authRateLimitMillis");
authLimitExclusions = block.hasEntry("authLimitExclusions") ? block.getEntry("authLimitExclusions", ListConfigEntry.class) : null;
2018-09-17 10:07:32 +03:00
bindAddress = block.hasEntry("bindAddress") ?
block.getEntryValue("bindAddress", StringConfigEntry.class) : getAddress();
authRejectString = block.hasEntry("authRejectString") ?
block.getEntryValue("authRejectString", StringConfigEntry.class) : "Вы превысили лимит авторизаций. Подождите некоторое время перед повторной попыткой";
whitelistRejectString = block.hasEntry("whitelistRejectString") ?
block.getEntryValue("whitelistRejectString", StringConfigEntry.class) : "Вас нет в белом списке";
// Set handlers & providers
2018-09-23 15:07:07 +03:00
authHandler = new AuthHandler[1];
authHandler[0] = AuthHandler.newHandler(block.getEntryValue("authHandler", StringConfigEntry.class),
2018-09-17 10:07:32 +03:00
block.getEntry("authHandlerConfig", BlockConfigEntry.class));
2018-09-23 14:42:10 +03:00
authProvider = new AuthProvider[1];
authProvider[0] = AuthProvider.newProvider(block.getEntryValue("authProvider", StringConfigEntry.class),
2018-11-08 15:30:16 +03:00
block.getEntry("authProviderConfig", BlockConfigEntry.class), server);
2018-09-17 10:07:32 +03:00
textureProvider = TextureProvider.newProvider(block.getEntryValue("textureProvider", StringConfigEntry.class),
block.getEntry("textureProviderConfig", BlockConfigEntry.class));
hwidHandler = HWIDHandler.newHandler(block.getEntryValue("hwidHandler", StringConfigEntry.class),
block.getEntry("hwidHandlerConfig", BlockConfigEntry.class));
// Set misc config
genMappings = block.getEntryValue("proguardPrintMappings", BooleanConfigEntry.class);
2018-11-08 15:30:16 +03:00
mirrors = block.getEntry("mirrors", ListConfigEntry.class);
2018-09-17 10:07:32 +03:00
launch4j = new ExeConf(block.getEntry("launch4J", BlockConfigEntry.class));
2018-11-26 10:43:18 +03:00
buildPostTransform = new PostBuildTransformConf(block.getEntry("buildExtendedOperation", BlockConfigEntry.class), coredir);
2018-09-17 10:07:32 +03:00
binaryName = block.getEntryValue("binaryName", StringConfigEntry.class);
projectName = block.hasEntry("projectName") ? block.getEntryValue("projectName", StringConfigEntry.class) : "Minecraft";
2018-09-17 10:07:32 +03:00
compress = block.getEntryValue("compress", BooleanConfigEntry.class);
isUsingWrapper = block.getEntryValue("isUsingWrapper", BooleanConfigEntry.class);
isDownloadJava = block.getEntryValue("isDownloadJava", BooleanConfigEntry.class);
2018-09-17 10:07:32 +03:00
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public String getAddress() {
return address.getValue();
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public String getBindAddress() {
return bindAddress;
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public SocketAddress getSocketAddress() {
return new InetSocketAddress(bindAddress, port);
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public void setAddress(String address) {
this.address.setValue(address);
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public void verify() {
VerifyHelper.verify(getAddress(), VerifyHelper.NOT_EMPTY, "LaunchServer address can't be empty");
}
}
2018-09-22 17:33:00 +03:00
2018-09-17 10:07:32 +03:00
public static class ExeConf extends ConfigObject {
public final boolean enabled;
2018-11-26 10:43:18 +03:00
public final String productName;
public final String productVer;
public final String fileDesc;
public final String fileVer;
public final String internalName;
public final String copyright;
public final String trademarks;
2018-09-17 10:07:32 +03:00
2018-11-26 10:43:18 +03:00
public final String txtFileVersion;
public final String txtProductVersion;
2018-09-17 10:07:32 +03:00
private ExeConf(BlockConfigEntry block) {
super(block);
enabled = block.getEntryValue("enabled", BooleanConfigEntry.class);
productName = block.hasEntry("productName") ? block.getEntryValue("productName", StringConfigEntry.class)
: "sashok724's Launcher v3 mod by Gravit";
productVer = block.hasEntry("productVer") ? block.getEntryValue("productVer", StringConfigEntry.class)
: "1.0.0.0";
fileDesc = block.hasEntry("fileDesc") ? block.getEntryValue("fileDesc", StringConfigEntry.class)
: "sashok724's Launcher v3 mod by Gravit";
fileVer = block.hasEntry("fileVer") ? block.getEntryValue("fileVer", StringConfigEntry.class) : "1.0.0.0";
internalName = block.hasEntry("internalName") ? block.getEntryValue("internalName", StringConfigEntry.class)
: "Launcher";
copyright = block.hasEntry("copyright") ? block.getEntryValue("copyright", StringConfigEntry.class)
: "© sashok724 LLC";
trademarks = block.hasEntry("trademarks") ? block.getEntryValue("trademarks", StringConfigEntry.class)
: "This product is licensed under MIT License";
txtFileVersion = block.hasEntry("txtFileVersion") ? block.getEntryValue("txtFileVersion", StringConfigEntry.class)
: String.format("%s, build %d", Launcher.getVersion().getVersionString(), Launcher.getVersion().build);
2018-09-17 10:07:32 +03:00
txtProductVersion = block.hasEntry("txtProductVersion") ? block.getEntryValue("txtProductVersion", StringConfigEntry.class)
: String.format("%s, build %d", Launcher.getVersion().getVersionString(), Launcher.getVersion().build);
2018-09-17 10:07:32 +03:00
}
}
2018-09-22 17:33:00 +03:00
2018-09-17 10:07:32 +03:00
private final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
private final Collection<SignedObjectHolder<ClientProfile>> result;
private ProfilesFileVisitor(Collection<SignedObjectHolder<ClientProfile>> result) {
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
LogHelper.info("Syncing '%s' profile", IOHelper.getFileName(file));
// Read profile
ClientProfile profile;
try (BufferedReader reader = IOHelper.newReader(file)) {
profile = new ClientProfile(TextConfigReader.read(reader, true));
}
profile.verify();
// Add SIGNED profile to result list
result.add(new SignedObjectHolder<>(profile, privateKey));
return super.visitFile(file, attrs);
}
}
2018-09-22 17:33:00 +03:00
2018-11-26 10:43:18 +03:00
public static class PostBuildTransformConf extends ConfigObject {
2018-09-17 10:07:32 +03:00
public final boolean enabled;
2018-12-02 15:21:27 +03:00
public List<String> script;
2018-11-26 10:43:18 +03:00
private PostBuildTransformConf(BlockConfigEntry block, Path coredir) {
2018-09-17 10:07:32 +03:00
super(block);
enabled = block.getEntryValue("enabled", BooleanConfigEntry.class);
2018-12-02 15:21:27 +03:00
script = new ArrayList<>(1);
if (block.hasEntry("script"))
block.getEntry("script", ListConfigEntry.class).stream(StringConfigEntry.class).forEach(script::add);
2018-09-17 10:07:32 +03:00
}
}
2018-09-22 17:33:00 +03:00
2018-09-17 10:07:32 +03:00
public static void main(String... args) throws Throwable {
JVMHelper.checkStackTrace(LaunchServer.class);
2018-09-17 10:07:32 +03:00
JVMHelper.verifySystemProperties(LaunchServer.class, true);
LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log"));
LogHelper.printVersion("LaunchServer");
LogHelper.printLicense("LauncherServer");
2018-09-17 10:07:32 +03:00
// Start LaunchServer
Instant start = Instant.now();
try {
2018-11-10 20:47:58 +03:00
try (LaunchServer lsrv = new LaunchServer(IOHelper.WORKING_DIR)) {
2018-09-17 10:07:32 +03:00
lsrv.run();
}
} catch (Throwable exc) {
LogHelper.error(exc);
return;
}
Instant end = Instant.now();
LogHelper.debug("LaunchServer started in %dms", Duration.between(start, end).toMillis());
}
2018-09-22 17:33:00 +03:00
2018-09-17 10:07:32 +03:00
// Constant paths
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final Path dir;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final Path configFile;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final Path publicKeyFile;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final Path privateKeyFile;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final Path updatesDir;
2018-10-01 10:48:24 +03:00
public static LaunchServer server;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final Path profilesDir;
// Server config
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final Config config;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final RSAPublicKey publicKey;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final RSAPrivateKey privateKey;
// Launcher binary
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final LauncherBinary launcherBinary;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final LauncherBinary launcherEXEBinary;
// HWID ban + anti-brutforce
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final AuthLimiter limiter;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final SessionManager sessionManager;
// Server
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final ModulesManager modulesManager;
public final MirrorManager mirrorManager;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final BuildHookManager buildHookManager;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final ProguardConf proguardConf;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final CommandHandler commandHandler;
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public final ServerSocketHandler serverSocketHandler;
2018-09-22 17:33:00 +03:00
private final AtomicBoolean started = new AtomicBoolean(false);
2018-09-17 10:07:32 +03:00
// Updates and profiles
private volatile List<SignedObjectHolder<ClientProfile>> profilesList;
public volatile Map<String, SignedObjectHolder<HashedDir>> updatesDirMap;
2018-09-17 10:07:32 +03:00
2018-11-10 20:47:58 +03:00
public LaunchServer(Path dir) throws IOException, InvalidKeySpecException {
2018-09-17 10:07:32 +03:00
// Setup config locations
this.dir = dir;
configFile = dir.resolve("LaunchServer.cfg");
publicKeyFile = dir.resolve("public.key");
privateKeyFile = dir.resolve("private.key");
updatesDir = dir.resolve("updates");
profilesDir = dir.resolve("profiles");
//Registration handlers and providers
AuthHandler.registerHandlers();
AuthProvider.registerProviders();
TextureProvider.registerProviders();
HWIDHandler.registerHandlers();
Response.registerResponses();
2018-10-01 10:48:24 +03:00
LaunchServer.server = this;
2018-09-17 10:07:32 +03:00
// Set command handler
CommandHandler localCommandHandler;
2018-11-10 20:47:58 +03:00
try {
Class.forName("jline.Terminal");
// JLine2 available
localCommandHandler = new JLineCommandHandler(this);
LogHelper.info("JLine2 terminal enabled");
} catch (ClassNotFoundException ignored) {
localCommandHandler = new StdCommandHandler(this, true);
LogHelper.warning("JLine2 isn't in classpath, using std");
}
2018-09-17 10:07:32 +03:00
commandHandler = localCommandHandler;
// Set key pair
if (IOHelper.isFile(publicKeyFile) && IOHelper.isFile(privateKeyFile)) {
LogHelper.info("Reading RSA keypair");
publicKey = SecurityHelper.toPublicRSAKey(IOHelper.read(publicKeyFile));
privateKey = SecurityHelper.toPrivateRSAKey(IOHelper.read(privateKeyFile));
if (!publicKey.getModulus().equals(privateKey.getModulus()))
2018-09-22 17:33:00 +03:00
throw new IOException("Private and public key modulus mismatch");
2018-09-17 10:07:32 +03:00
} else {
LogHelper.info("Generating RSA keypair");
KeyPair pair = SecurityHelper.genRSAKeyPair();
publicKey = (RSAPublicKey) pair.getPublic();
privateKey = (RSAPrivateKey) pair.getPrivate();
// Write key pair files
LogHelper.info("Writing RSA keypair files");
IOHelper.write(publicKeyFile, publicKey.getEncoded());
IOHelper.write(privateKeyFile, privateKey.getEncoded());
}
// Print keypair fingerprints
CRC32 crc = new CRC32();
crc.update(publicKey.getModulus().toByteArray()); // IDEA говорит, что это Java 9 API. WTF?
2018-09-17 10:07:32 +03:00
LogHelper.subInfo("Modulus CRC32: 0x%08x", crc.getValue());
2018-09-22 17:33:00 +03:00
2018-09-17 10:07:32 +03:00
// pre init modules
modulesManager = new ModulesManager(this);
2018-09-19 15:03:52 +03:00
modulesManager.autoload(dir.resolve("modules"));
2018-09-17 10:07:32 +03:00
modulesManager.preInitModules();
2018-09-22 17:33:00 +03:00
2018-09-17 10:07:32 +03:00
// Read LaunchServer config
generateConfigIfNotExists();
LogHelper.info("Reading LaunchServer config file");
try (BufferedReader reader = IOHelper.newReader(configFile)) {
2018-11-08 15:30:16 +03:00
config = new Config(TextConfigReader.read(reader, true), dir, this);
2018-09-17 10:07:32 +03:00
}
config.verify();
// build hooks, anti-brutforce and other
buildHookManager = new BuildHookManager();
limiter = new AuthLimiter(this);
proguardConf = new ProguardConf(this);
sessionManager = new SessionManager();
mirrorManager = new MirrorManager();
2018-09-17 10:07:32 +03:00
GarbageManager.registerNeedGC(sessionManager);
GarbageManager.registerNeedGC(limiter);
config.mirrors.stream(StringConfigEntry.class).forEach(s -> {
try {
mirrorManager.addMirror(s);
} catch (MalformedURLException e) {
e.printStackTrace();
}
});
2018-09-22 17:33:00 +03:00
2018-09-17 10:07:32 +03:00
// init modules
modulesManager.initModules();
// Set launcher EXE binary
launcherBinary = new JARLauncherBinary(this);
launcherEXEBinary = binary();
syncLauncherBinaries();
// Sync updates dir
if (!IOHelper.isDir(updatesDir))
2018-09-22 17:33:00 +03:00
Files.createDirectory(updatesDir);
2018-09-17 10:07:32 +03:00
syncUpdatesDir(null);
// Sync profiles dir
if (!IOHelper.isDir(profilesDir))
2018-09-22 17:33:00 +03:00
Files.createDirectory(profilesDir);
2018-09-17 10:07:32 +03:00
syncProfilesDir();
// Set server socket thread
serverSocketHandler = new ServerSocketHandler(this, sessionManager);
2018-09-22 17:33:00 +03:00
2018-09-17 10:07:32 +03:00
// post init modules
modulesManager.postInitModules();
}
private LauncherBinary binary() {
2018-09-22 17:33:00 +03:00
if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this);
return new EXELauncherBinary(this);
2018-09-17 10:07:32 +03:00
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public void buildLauncherBinaries() throws IOException {
launcherBinary.build();
launcherEXEBinary.build();
}
@Override
public void close() {
serverSocketHandler.close();
// Close handlers & providers
try {
2018-11-08 15:30:16 +03:00
for (AuthHandler h : config.authHandler) h.close();
2018-09-17 10:07:32 +03:00
} catch (IOException e) {
LogHelper.error(e);
}
try {
2018-11-08 15:30:16 +03:00
for (AuthProvider p : config.authProvider) p.close();
2018-09-17 10:07:32 +03:00
} catch (IOException e) {
LogHelper.error(e);
}
try {
config.textureProvider.close();
} catch (IOException e) {
LogHelper.error(e);
}
2018-09-22 17:33:00 +03:00
config.hwidHandler.close();
2018-09-17 10:07:32 +03:00
modulesManager.close();
// Print last message before death :(
LogHelper.info("LaunchServer stopped");
}
private void generateConfigIfNotExists() throws IOException {
if (IOHelper.isFile(configFile))
2018-09-22 17:33:00 +03:00
return;
2018-09-17 10:07:32 +03:00
// Create new config
LogHelper.info("Creating LaunchServer config");
Config newConfig;
try (BufferedReader reader = IOHelper.newReader(IOHelper.getResourceURL("ru/gravit/launchserver/defaults/config.cfg"))) {
2018-11-08 15:30:16 +03:00
newConfig = new Config(TextConfigReader.read(reader, false), dir, this);
2018-09-17 10:07:32 +03:00
}
// Set server address
2018-11-10 20:47:58 +03:00
LogHelper.println("LaunchServer address: ");
newConfig.setAddress(commandHandler.readLine());
2018-09-17 10:07:32 +03:00
// Write LaunchServer config
LogHelper.info("Writing LaunchServer config file");
try (BufferedWriter writer = IOHelper.newWriter(configFile)) {
TextConfigWriter.write(newConfig.block, writer, true);
}
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
@SuppressWarnings("ReturnOfCollectionOrArrayField")
public Collection<SignedObjectHolder<ClientProfile>> getProfiles() {
return profilesList;
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public SignedObjectHolder<HashedDir> getUpdateDir(String name) {
return updatesDirMap.get(name);
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public Set<Entry<String, SignedObjectHolder<HashedDir>>> getUpdateDirs() {
return updatesDirMap.entrySet();
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public void rebindServerSocket() {
serverSocketHandler.close();
CommonHelper.newThread("Server Socket Thread", false, serverSocketHandler).start();
}
@Override
public void run() {
if (started.getAndSet(true))
2018-09-22 17:33:00 +03:00
throw new IllegalStateException("LaunchServer has been already started");
2018-09-17 10:07:32 +03:00
// Add shutdown hook, then start LaunchServer
2018-11-10 20:47:58 +03:00
JVMHelper.RUNTIME.addShutdownHook(CommonHelper.newThread(null, false, this::close));
CommonHelper.newThread("Command Thread", true, commandHandler).start();
2018-09-17 10:07:32 +03:00
rebindServerSocket();
}
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public void syncLauncherBinaries() throws IOException {
LogHelper.info("Syncing launcher binaries");
// Syncing launcher binary
LogHelper.info("Syncing launcher binary file");
if (!launcherBinary.sync()) LogHelper.warning("Missing launcher binary file");
// Syncing launcher EXE binary
LogHelper.info("Syncing launcher EXE binary file");
if (!launcherEXEBinary.sync() && config.launch4j.enabled)
LogHelper.warning("Missing launcher EXE binary file");
}
2018-09-22 17:33:00 +03:00
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public void syncProfilesDir() throws IOException {
LogHelper.info("Syncing profiles dir");
List<SignedObjectHolder<ClientProfile>> newProfies = new LinkedList<>();
IOHelper.walk(profilesDir, new ProfilesFileVisitor(newProfies), false);
// Sort and set new profiles
newProfies.sort(Comparator.comparing(a -> a.object));
profilesList = Collections.unmodifiableList(newProfies);
}
2018-09-22 17:33:00 +03:00
2018-10-13 11:01:10 +03:00
2018-09-17 10:07:32 +03:00
public void syncUpdatesDir(Collection<String> dirs) throws IOException {
LogHelper.info("Syncing updates dir");
Map<String, SignedObjectHolder<HashedDir>> newUpdatesDirMap = new HashMap<>(16);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(updatesDir)) {
for (Path updateDir : dirStream) {
if (Files.isHidden(updateDir))
2018-09-22 17:33:00 +03:00
continue; // Skip hidden
2018-09-17 10:07:32 +03:00
// Resolve name and verify is dir
String name = IOHelper.getFileName(updateDir);
if (!IOHelper.isDir(updateDir)) {
LogHelper.warning("Not update dir: '%s'", name);
continue;
}
// Add from previous map (it's guaranteed to be non-null)
if (dirs != null && !dirs.contains(name)) {
SignedObjectHolder<HashedDir> hdir = updatesDirMap.get(name);
if (hdir != null) {
newUpdatesDirMap.put(name, hdir);
continue;
}
}
// Sync and sign update dir
LogHelper.info("Syncing '%s' update dir", name);
HashedDir updateHDir = new HashedDir(updateDir, null, true, true);
newUpdatesDirMap.put(name, new SignedObjectHolder<>(updateHDir, privateKey));
}
}
updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap);
}
}