2018-09-17 10:07:32 +03:00
|
|
|
|
package ru.gravit.launchserver;
|
|
|
|
|
|
2018-12-24 13:10:53 +03:00
|
|
|
|
import com.google.gson.Gson;
|
2018-12-23 19:26:57 +03:00
|
|
|
|
import com.google.gson.GsonBuilder;
|
2018-12-23 19:47:07 +03:00
|
|
|
|
import com.google.gson.annotations.Expose;
|
2018-09-22 17:22:39 +03:00
|
|
|
|
import ru.gravit.launcher.Launcher;
|
2018-12-19 14:24:50 +03:00
|
|
|
|
import ru.gravit.launcher.LauncherConfig;
|
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.signed.SignedObjectHolder;
|
|
|
|
|
import ru.gravit.launchserver.auth.AuthLimiter;
|
|
|
|
|
import ru.gravit.launchserver.auth.handler.AuthHandler;
|
2018-12-23 20:19:43 +03:00
|
|
|
|
import ru.gravit.launchserver.auth.handler.MemoryAuthHandler;
|
|
|
|
|
import ru.gravit.launchserver.auth.hwid.AcceptHWIDHandler;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
import ru.gravit.launchserver.auth.hwid.HWIDHandler;
|
2018-12-24 13:00:09 +03:00
|
|
|
|
import ru.gravit.launchserver.auth.permissions.JsonFilePermissionsHandler;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
import ru.gravit.launchserver.auth.provider.AuthProvider;
|
2018-12-23 20:19:43 +03:00
|
|
|
|
import ru.gravit.launchserver.auth.provider.RejectAuthProvider;
|
2018-12-20 18:45:01 +03:00
|
|
|
|
import ru.gravit.launchserver.binary.EXEL4JLauncherBinary;
|
|
|
|
|
import ru.gravit.launchserver.binary.EXELauncherBinary;
|
|
|
|
|
import ru.gravit.launchserver.binary.JARLauncherBinary;
|
|
|
|
|
import ru.gravit.launchserver.binary.LauncherBinary;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
import ru.gravit.launchserver.command.handler.CommandHandler;
|
|
|
|
|
import ru.gravit.launchserver.command.handler.JLineCommandHandler;
|
|
|
|
|
import ru.gravit.launchserver.command.handler.StdCommandHandler;
|
2018-12-23 20:19:43 +03:00
|
|
|
|
import ru.gravit.launchserver.config.AuthHandlerAdapter;
|
|
|
|
|
import ru.gravit.launchserver.config.AuthProviderAdapter;
|
|
|
|
|
import ru.gravit.launchserver.config.HWIDHandlerAdapter;
|
|
|
|
|
import ru.gravit.launchserver.config.TextureProviderAdapter;
|
2018-12-24 13:00:09 +03:00
|
|
|
|
import ru.gravit.launchserver.manangers.*;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
import ru.gravit.launchserver.response.Response;
|
|
|
|
|
import ru.gravit.launchserver.socket.ServerSocketHandler;
|
2018-12-24 08:51:47 +03:00
|
|
|
|
import ru.gravit.launchserver.texture.RequestTextureProvider;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
import ru.gravit.launchserver.texture.TextureProvider;
|
2018-12-20 18:45:01 +03:00
|
|
|
|
import ru.gravit.utils.helper.*;
|
|
|
|
|
|
|
|
|
|
import java.io.BufferedReader;
|
|
|
|
|
import java.io.BufferedWriter;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.net.InetSocketAddress;
|
|
|
|
|
import java.net.MalformedURLException;
|
|
|
|
|
import java.net.SocketAddress;
|
|
|
|
|
import java.nio.file.*;
|
|
|
|
|
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;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.Map.Entry;
|
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
import java.util.zip.CRC32;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
|
|
public final class LaunchServer implements Runnable, AutoCloseable {
|
2018-12-23 18:50:31 +03:00
|
|
|
|
public static final class Config {
|
|
|
|
|
public int port;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
|
|
// Handlers & Providers
|
2018-10-13 11:01:10 +03:00
|
|
|
|
|
2018-12-24 08:51:47 +03:00
|
|
|
|
public AuthHandler authHandler;
|
2018-10-13 11:01:10 +03:00
|
|
|
|
|
2018-12-23 18:50:31 +03:00
|
|
|
|
public AuthProvider[] authProvider;
|
2018-10-13 11:01:10 +03:00
|
|
|
|
|
2018-12-23 18:50:31 +03:00
|
|
|
|
public TextureProvider textureProvider;
|
2018-10-13 11:01:10 +03:00
|
|
|
|
|
2018-12-23 18:50:31 +03:00
|
|
|
|
public HWIDHandler hwidHandler;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
|
|
// Misc options
|
2018-12-23 18:50:31 +03:00
|
|
|
|
public int threadCount;
|
|
|
|
|
|
|
|
|
|
public int threadCoreCount;
|
|
|
|
|
|
|
|
|
|
public ExeConf launch4j;
|
|
|
|
|
|
|
|
|
|
public PostBuildTransformConf buildPostTransform;
|
|
|
|
|
|
|
|
|
|
public boolean compress;
|
|
|
|
|
|
|
|
|
|
public int authRateLimit;
|
|
|
|
|
|
|
|
|
|
public int authRateLimitMilis;
|
|
|
|
|
|
|
|
|
|
public String[] authLimitExclusions;
|
|
|
|
|
|
|
|
|
|
public String authRejectString;
|
|
|
|
|
|
|
|
|
|
public String projectName;
|
|
|
|
|
|
|
|
|
|
public String whitelistRejectString;
|
|
|
|
|
|
|
|
|
|
public boolean genMappings;
|
|
|
|
|
public boolean isUsingWrapper;
|
|
|
|
|
public boolean isDownloadJava;
|
|
|
|
|
|
|
|
|
|
public String[] mirrors;
|
|
|
|
|
public String binaryName;
|
2018-12-23 19:47:07 +03:00
|
|
|
|
@Expose
|
2018-12-23 18:50:31 +03:00
|
|
|
|
private String address;
|
2018-12-23 19:47:07 +03:00
|
|
|
|
@Expose
|
2018-12-23 18:50:31 +03:00
|
|
|
|
private String bindAddress;
|
|
|
|
|
public LauncherConfig.LauncherEnvironment env;
|
|
|
|
|
public boolean isWarningMissArchJava;
|
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() {
|
2018-12-23 18:50:31 +03:00
|
|
|
|
return address;
|
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 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) {
|
2018-12-23 18:50:31 +03:00
|
|
|
|
this.address = address;
|
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 verify() {
|
|
|
|
|
VerifyHelper.verify(getAddress(), VerifyHelper.NOT_EMPTY, "LaunchServer address can't be empty");
|
2018-12-24 08:51:47 +03:00
|
|
|
|
if(authHandler == null)
|
|
|
|
|
{
|
|
|
|
|
throw new NullPointerException("AuthHandler must not be null");
|
|
|
|
|
}
|
|
|
|
|
if(authProvider == null || authProvider[0] == null)
|
|
|
|
|
{
|
|
|
|
|
throw new NullPointerException("AuthProvider must not be null");
|
|
|
|
|
}
|
|
|
|
|
if(textureProvider == null)
|
|
|
|
|
{
|
|
|
|
|
throw new NullPointerException("TextureProvider must not be null");
|
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
|
|
2018-12-23 18:50:31 +03:00
|
|
|
|
public static class ExeConf {
|
|
|
|
|
public boolean enabled;
|
|
|
|
|
public String productName;
|
|
|
|
|
public String productVer;
|
|
|
|
|
public String fileDesc;
|
|
|
|
|
public String fileVer;
|
|
|
|
|
public String internalName;
|
|
|
|
|
public String copyright;
|
|
|
|
|
public String trademarks;
|
|
|
|
|
|
|
|
|
|
public String txtFileVersion;
|
|
|
|
|
public String txtProductVersion;
|
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> {
|
2018-12-23 18:57:40 +03:00
|
|
|
|
private final Collection<ClientProfile> result;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
2018-12-23 18:57:40 +03:00
|
|
|
|
private ProfilesFileVisitor(Collection<ClientProfile> result) {
|
2018-09-17 10:07:32 +03:00
|
|
|
|
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)) {
|
2018-12-23 18:57:40 +03:00
|
|
|
|
profile = Launcher.gson.fromJson(reader,ClientProfile.class);
|
2018-09-17 10:07:32 +03:00
|
|
|
|
}
|
|
|
|
|
profile.verify();
|
|
|
|
|
|
|
|
|
|
// Add SIGNED profile to result list
|
2018-12-23 18:57:40 +03:00
|
|
|
|
result.add(profile);
|
2018-09-17 10:07:32 +03:00
|
|
|
|
return super.visitFile(file, attrs);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
|
|
2018-12-23 18:50:31 +03:00
|
|
|
|
public static class PostBuildTransformConf {
|
|
|
|
|
public boolean enabled;
|
2018-12-02 15:21:27 +03:00
|
|
|
|
public List<String> script;
|
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 {
|
2018-10-22 14:05:10 +03:00
|
|
|
|
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");
|
2018-12-24 13:16:57 +03:00
|
|
|
|
LogHelper.printLicense("LaunchServer");
|
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;
|
|
|
|
|
|
2018-10-18 12:39:36 +03:00
|
|
|
|
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
|
2018-12-23 18:57:40 +03:00
|
|
|
|
private volatile List<ClientProfile> profilesList;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
2018-10-02 16:25:07 +03:00
|
|
|
|
public volatile Map<String, SignedObjectHolder<HashedDir>> updatesDirMap;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
2018-12-24 13:10:53 +03:00
|
|
|
|
public static Gson gson;
|
|
|
|
|
public static GsonBuilder gsonBuilder;
|
|
|
|
|
|
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;
|
2018-12-24 13:16:57 +03:00
|
|
|
|
configFile = dir.resolve("LaunchServer.conf");
|
2018-09-17 10:07:32 +03:00
|
|
|
|
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();
|
2018-10-18 12:39:36 +03:00
|
|
|
|
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-12-23 19:26:57 +03:00
|
|
|
|
initGson();
|
2018-12-23 19:47:07 +03:00
|
|
|
|
LogHelper.setStacktraceEnabled(true);
|
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-12-23 18:57:40 +03:00
|
|
|
|
config = Launcher.gson.fromJson(reader,Config.class);
|
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();
|
2018-10-18 12:39:36 +03:00
|
|
|
|
mirrorManager = new MirrorManager();
|
2018-09-17 10:07:32 +03:00
|
|
|
|
GarbageManager.registerNeedGC(sessionManager);
|
|
|
|
|
GarbageManager.registerNeedGC(limiter);
|
2018-12-23 18:50:31 +03:00
|
|
|
|
Arrays.stream(config.mirrors).forEach(s -> {
|
2018-10-18 12:39:36 +03:00
|
|
|
|
try {
|
|
|
|
|
mirrorManager.addMirror(s);
|
|
|
|
|
} catch (MalformedURLException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
});
|
2018-12-24 13:00:09 +03:00
|
|
|
|
JsonFilePermissionsHandler.init();
|
|
|
|
|
PermissionsManager.registerPermissionsFunction(JsonFilePermissionsHandler::getPermissions);
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-23 19:26:57 +03:00
|
|
|
|
public static void initGson()
|
|
|
|
|
{
|
|
|
|
|
if(Launcher.gson != null) return;
|
|
|
|
|
Launcher.gsonBuilder = new GsonBuilder();
|
2018-12-23 20:19:43 +03:00
|
|
|
|
Launcher.gsonBuilder.registerTypeAdapter(AuthProvider.class, new AuthProviderAdapter());
|
|
|
|
|
Launcher.gsonBuilder.registerTypeAdapter(TextureProvider.class, new TextureProviderAdapter());
|
|
|
|
|
Launcher.gsonBuilder.registerTypeAdapter(AuthHandler.class, new AuthHandlerAdapter());
|
|
|
|
|
Launcher.gsonBuilder.registerTypeAdapter(HWIDHandler.class, new HWIDHandlerAdapter());
|
2018-12-23 19:26:57 +03:00
|
|
|
|
Launcher.gson = Launcher.gsonBuilder.create();
|
2018-12-24 13:10:53 +03:00
|
|
|
|
|
|
|
|
|
//Human readable
|
|
|
|
|
LaunchServer.gsonBuilder = new GsonBuilder();
|
|
|
|
|
LaunchServer.gsonBuilder.setPrettyPrinting();
|
|
|
|
|
LaunchServer.gsonBuilder.registerTypeAdapter(AuthProvider.class, new AuthProviderAdapter());
|
|
|
|
|
LaunchServer.gsonBuilder.registerTypeAdapter(TextureProvider.class, new TextureProviderAdapter());
|
|
|
|
|
LaunchServer.gsonBuilder.registerTypeAdapter(AuthHandler.class, new AuthHandlerAdapter());
|
|
|
|
|
LaunchServer.gsonBuilder.registerTypeAdapter(HWIDHandler.class, new HWIDHandlerAdapter());
|
|
|
|
|
LaunchServer.gson = LaunchServer.gsonBuilder.create();
|
2018-12-23 19:26:57 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
|
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-12-24 08:51:47 +03:00
|
|
|
|
config.authHandler.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");
|
2018-12-23 19:47:07 +03:00
|
|
|
|
Config newConfig = new Config();
|
2018-12-24 15:12:39 +03:00
|
|
|
|
newConfig.mirrors = new String[]{"http://mirror.gravitlauncher.ml/"};
|
2018-12-23 19:47:07 +03:00
|
|
|
|
newConfig.launch4j = new ExeConf();
|
|
|
|
|
newConfig.buildPostTransform = new PostBuildTransformConf();
|
|
|
|
|
newConfig.env = LauncherConfig.LauncherEnvironment.STD;
|
2018-12-24 08:51:47 +03:00
|
|
|
|
newConfig.authHandler = new MemoryAuthHandler();
|
2018-12-23 20:19:43 +03:00
|
|
|
|
newConfig.hwidHandler = new AcceptHWIDHandler();
|
2018-12-24 08:51:47 +03:00
|
|
|
|
|
2018-12-24 13:16:57 +03:00
|
|
|
|
newConfig.authProvider = new AuthProvider[]{new RejectAuthProvider("Настройте authProvider")};
|
2018-12-24 08:51:47 +03:00
|
|
|
|
newConfig.textureProvider = new RequestTextureProvider("http://example.com/skins/%username%.png","http://example.com/cloaks/%username%.png");
|
2018-12-23 20:19:43 +03:00
|
|
|
|
newConfig.port = 7420;
|
|
|
|
|
newConfig.bindAddress = "0.0.0.0";
|
2018-12-24 13:16:57 +03:00
|
|
|
|
newConfig.authRejectString = "Превышен лимит авторизаций";
|
|
|
|
|
newConfig.binaryName = "Launcher";
|
|
|
|
|
newConfig.whitelistRejectString = "Вас нет в белом списке";
|
2018-12-23 19:47:07 +03:00
|
|
|
|
//try (BufferedReader reader = IOHelper.newReader(IOHelper.getResourceURL("ru/gravit/launchserver/defaults/config.cfg"))) {
|
|
|
|
|
// newConfig = Launcher.gson.fromJson(reader,Config.class);
|
|
|
|
|
//}
|
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)) {
|
2018-12-24 13:10:53 +03:00
|
|
|
|
LaunchServer.gson.toJson(newConfig,writer);
|
2018-09-17 10:07:32 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-13 11:01:10 +03:00
|
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
|
@SuppressWarnings("ReturnOfCollectionOrArrayField")
|
2018-12-23 18:57:40 +03:00
|
|
|
|
public Collection<ClientProfile> getProfiles() {
|
2018-09-17 10:07:32 +03:00
|
|
|
|
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");
|
2018-12-23 18:57:40 +03:00
|
|
|
|
List<ClientProfile> newProfies = new LinkedList<>();
|
2018-09-17 10:07:32 +03:00
|
|
|
|
IOHelper.walk(profilesDir, new ProfilesFileVisitor(newProfies), false);
|
|
|
|
|
|
|
|
|
|
// Sort and set new profiles
|
2018-12-23 18:57:40 +03:00
|
|
|
|
newProfies.sort(Comparator.comparing(a -> a));
|
2018-09-17 10:07:32 +03:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|