Merge branch 'release/5.0.6'

This commit is contained in:
Gravit 2019-08-22 11:37:00 +07:00
commit 434fc8af05
No known key found for this signature in database
GPG key ID: 061981E1E85D3216
147 changed files with 2006 additions and 1540 deletions

3
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "modules"] [submodule "modules"]
path = modules path = modules
url = git@github.com:GravitLauncher/LauncherModules.git url = git@github.com:GravitLauncher/LauncherModules.git
[submodule "Radon"]
path = Radon
url = git@github.com:GravitLauncher/Radon.git

View file

@ -40,10 +40,24 @@
) )
} }
task cleanjar(type: Jar, dependsOn: jar) {
classifier = 'clean'
manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName,
"Can-Redefine-Classes": "true",
"Can-Retransform-Classes": "true",
"Can-Set-Native-Method-Prefix": "true"
)
from sourceSets.main.output
}
dependencies { dependencies {
pack project(':LauncherAPI') pack project(':LauncherAPI')
bundle project(':Radon') bundle 'org.ow2.asm:asm-commons:7.1'
bundle 'mysql:mysql-connector-java:8.0.16' bundle 'mysql:mysql-connector-java:8.0.16'
bundle 'org.postgresql:postgresql:42.2.6'
bundle 'org.jline:jline:3.11.0' bundle 'org.jline:jline:3.11.0'
bundle 'org.jline:jline-reader:3.11.0' bundle 'org.jline:jline-reader:3.11.0'
bundle 'org.jline:jline-terminal:3.11.0' bundle 'org.jline:jline-terminal:3.11.0'
@ -53,7 +67,7 @@ bundle project(':Radon')
bundle 'commons-codec:commons-codec:1.12' bundle 'commons-codec:commons-codec:1.12'
bundle 'org.javassist:javassist:3.25.0-GA' bundle 'org.javassist:javassist:3.25.0-GA'
bundle 'io.netty:netty-all:4.1.36.Final' bundle 'io.netty:netty-all:4.1.36.Final'
bundle 'org.hibernate:hibernate-core:5.4.3.Final' bundle 'org.hibernate:hibernate-core:5.4.4.Final'
bundle 'org.bouncycastle:bcpkix-jdk15on:1.61' bundle 'org.bouncycastle:bcpkix-jdk15on:1.61'
bundle 'org.slf4j:slf4j-simple:1.7.25' bundle 'org.slf4j:slf4j-simple:1.7.25'
@ -146,4 +160,35 @@ task dumpClientLibs(type: Copy) {
from parent.childProjects.Launcher.tasks.dumpLibs.destinationDir from parent.childProjects.Launcher.tasks.dumpLibs.destinationDir
} }
build.dependsOn tasks.dumpLibs, tasks.dumpCompileOnlyLibs, tasks.dumpClientLibs, tasks.bundle build.dependsOn tasks.dumpLibs, tasks.dumpCompileOnlyLibs, tasks.dumpClientLibs, tasks.bundle, tasks.cleanjar
publishing {
publications {
launchserverapi(MavenPublication) {
artifactId = 'launchserver-api'
artifact cleanjar
pom {
name = 'GravitLauncher LaunchServer API'
description = 'GravitLauncher LaunchServer Module API'
url = 'https://launcher.gravit.pro'
licenses {
license {
name = 'GNU General Public License, Version 3.0'
url = 'https://www.gnu.org/licenses/gpl-3.0.html'
}
}
scm {
connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git'
developerConnection = 'scm:git:ssh://git@github.com:GravitLauncher/Launcher.git'
url = 'https://launcher.gravit.pro/'
}
}
}
}
}
signing {
sign publishing.publications.launchserverapi
}

View file

@ -13,11 +13,12 @@
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -32,6 +33,10 @@
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import io.netty.channel.epoll.Epoll;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.operator.OperatorCreationException;
import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LogLevel;
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherConfig; import pro.gravit.launcher.LauncherConfig;
@ -41,7 +46,6 @@
import pro.gravit.launcher.managers.ConfigManager; import pro.gravit.launcher.managers.ConfigManager;
import pro.gravit.launcher.managers.GarbageManager; import pro.gravit.launcher.managers.GarbageManager;
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.serialize.signed.SignedObjectHolder;
import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.handler.AuthHandler; import pro.gravit.launchserver.auth.handler.AuthHandler;
import pro.gravit.launchserver.auth.handler.MemoryAuthHandler; import pro.gravit.launchserver.auth.handler.MemoryAuthHandler;
@ -61,13 +65,19 @@
import pro.gravit.launchserver.binary.JARLauncherBinary; import pro.gravit.launchserver.binary.JARLauncherBinary;
import pro.gravit.launchserver.binary.LauncherBinary; import pro.gravit.launchserver.binary.LauncherBinary;
import pro.gravit.launchserver.binary.ProguardConf; import pro.gravit.launchserver.binary.ProguardConf;
import pro.gravit.launchserver.binary.SimpleEXELauncherBinary;
import pro.gravit.launchserver.components.AuthLimiterComponent; import pro.gravit.launchserver.components.AuthLimiterComponent;
import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.components.RegLimiterComponent; import pro.gravit.launchserver.components.RegLimiterComponent;
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
import pro.gravit.launchserver.dao.UserService;
import pro.gravit.launchserver.dao.provider.DaoProvider; import pro.gravit.launchserver.dao.provider.DaoProvider;
import pro.gravit.launchserver.manangers.*; import pro.gravit.launchserver.manangers.CertificateManager;
import pro.gravit.launchserver.manangers.LaunchServerGsonManager;
import pro.gravit.launchserver.manangers.MirrorManager;
import pro.gravit.launchserver.manangers.ModulesManager;
import pro.gravit.launchserver.manangers.ReconfigurableManager;
import pro.gravit.launchserver.manangers.ReloadManager;
import pro.gravit.launchserver.manangers.SessionManager;
import pro.gravit.launchserver.manangers.hook.AuthHookManager; import pro.gravit.launchserver.manangers.hook.AuthHookManager;
import pro.gravit.launchserver.manangers.hook.BuildHookManager; import pro.gravit.launchserver.manangers.hook.BuildHookManager;
import pro.gravit.launchserver.socket.WebSocketService; import pro.gravit.launchserver.socket.WebSocketService;
@ -147,15 +157,8 @@ public AuthProviderPair getAuthProviderPair() {
public GuardLicenseConf guardLicense; public GuardLicenseConf guardLicense;
public String whitelistRejectString; public String whitelistRejectString;
public boolean genMappings;
public LauncherConf launcher; public LauncherConf launcher;
public CertificateConf certificate;
public boolean isWarningMissArchJava;
public boolean enabledProGuard;
public boolean enabledRadon;
public boolean stripLineNumbers;
public boolean deleteTempFiles;
public String startScript; public String startScript;
@ -253,6 +256,7 @@ public void close() {
public static class ExeConf { public static class ExeConf {
public boolean enabled; public boolean enabled;
public String alternative;
public boolean setMaxVersion; public boolean setMaxVersion;
public String maxVersion; public String maxVersion;
public String productName; public String productName;
@ -267,6 +271,11 @@ public static class ExeConf {
public String txtProductVersion; public String txtProductVersion;
} }
public static class CertificateConf
{
public boolean enabled;
}
public static class NettyUpdatesBind { public static class NettyUpdatesBind {
public String url; public String url;
public boolean zip; public boolean zip;
@ -275,6 +284,12 @@ public static class NettyUpdatesBind {
public class LauncherConf { public class LauncherConf {
public String guardType; public String guardType;
public boolean attachLibraryBeforeProGuard; public boolean attachLibraryBeforeProGuard;
public boolean compress;
public boolean warningMissArchJava;
public boolean enabledProGuard;
public boolean stripLineNumbers;
public boolean deleteTempFiles;
public boolean proguardGenMappings;
} }
public class NettyConfig { public class NettyConfig {
@ -289,7 +304,6 @@ public class NettyConfig {
public NettyPerformanceConfig performance; public NettyPerformanceConfig performance;
public NettyBindAddress[] binds; public NettyBindAddress[] binds;
public LogLevel logLevel = LogLevel.DEBUG; public LogLevel logLevel = LogLevel.DEBUG;
public NettyProxyConfig proxy = new NettyProxyConfig();
} }
public class NettyPerformanceConfig { public class NettyPerformanceConfig {
@ -298,15 +312,6 @@ public class NettyPerformanceConfig {
public int workerThread; public int workerThread;
} }
public class NettyProxyConfig {
public boolean enabled;
public String address = "ws://localhost:9275/api";
public String login = "login";
public String password = "password";
public String auth_id = "std";
public ArrayList<String> requests = new ArrayList<>();
}
public class NettyBindAddress { public class NettyBindAddress {
public String address; public String address;
public int port; public int port;
@ -355,7 +360,7 @@ public static void main(String... args) throws Throwable {
LogHelper.printLicense("LaunchServer"); LogHelper.printLicense("LaunchServer");
if (!StarterAgent.isAgentStarted()) { if (!StarterAgent.isAgentStarted()) {
LogHelper.error("StarterAgent is not started!"); LogHelper.error("StarterAgent is not started!");
LogHelper.error("Your should add to JVM options this option: `-javaagent:LaunchServer.jar`"); LogHelper.error("You should add to JVM options this option: `-javaagent:LaunchServer.jar`");
} }
// Start LaunchServer // Start LaunchServer
@ -394,6 +399,14 @@ public static void main(String... args) throws Throwable {
public final Path privateKeyFile; public final Path privateKeyFile;
public final Path caCertFile;
public final Path caKeyFile;
public final Path serverCertFile;
public final Path serverKeyFile;
public final Path updatesDir; public final Path updatesDir;
//public static LaunchServer server = null; //public static LaunchServer server = null;
@ -448,7 +461,7 @@ public static void main(String... args) throws Throwable {
// Updates and profiles // Updates and profiles
private volatile List<ClientProfile> profilesList; private volatile List<ClientProfile> profilesList;
public volatile Map<String, SignedObjectHolder<HashedDir>> updatesDirMap; public volatile Map<String, HashedDir> updatesDirMap;
public final Timer taskPool; public final Timer taskPool;
@ -482,6 +495,12 @@ public LaunchServer(Path dir, boolean testEnv, String[] args) throws IOException
updatesDir = dir.resolve("updates"); updatesDir = dir.resolve("updates");
profilesDir = dir.resolve("profiles"); profilesDir = dir.resolve("profiles");
caCertFile = dir.resolve("ca.crt");
caKeyFile = dir.resolve("ca.key");
serverCertFile = dir.resolve("server.crt");
serverKeyFile = dir.resolve("server.key");
//Registration handlers and providers //Registration handlers and providers
AuthHandler.registerHandlers(); AuthHandler.registerHandlers();
AuthProvider.registerProviders(); AuthProvider.registerProviders();
@ -594,6 +613,43 @@ public LaunchServer(Path dir, boolean testEnv, String[] args) throws IOException
authHookManager = new AuthHookManager(); authHookManager = new AuthHookManager();
configManager = new ConfigManager(); configManager = new ConfigManager();
certificateManager = new CertificateManager(); certificateManager = new CertificateManager();
//Generate or set new Certificate API
certificateManager.orgName = config.projectName;
if(config.certificate != null && config.certificate.enabled)
{
if(IOHelper.isFile(caCertFile) && IOHelper.isFile(caKeyFile))
{
certificateManager.ca = certificateManager.readCertificate(caCertFile);
certificateManager.caKey = certificateManager.readPrivateKey(caKeyFile);
}
else
{
try {
certificateManager.generateCA();
certificateManager.writeCertificate(caCertFile, certificateManager.ca);
certificateManager.writePrivateKey(caKeyFile, certificateManager.caKey);
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | OperatorCreationException e) {
LogHelper.error(e);
}
}
if(IOHelper.isFile(serverCertFile) && IOHelper.isFile(serverKeyFile))
{
certificateManager.server = certificateManager.readCertificate(serverCertFile);
certificateManager.serverKey = certificateManager.readPrivateKey(serverKeyFile);
}
else
{
try {
KeyPair pair = certificateManager.generateKeyPair();
certificateManager.server = certificateManager.generateCertificate(config.projectName.concat(" Server"), pair.getPublic());
certificateManager.serverKey = PrivateKeyFactory.createKey(pair.getPrivate().getEncoded());
certificateManager.writePrivateKey(serverKeyFile, pair.getPrivate());
certificateManager.writeCertificate(serverCertFile, certificateManager.server);
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | OperatorCreationException e) {
LogHelper.error(e);
}
}
}
GarbageManager.registerNeedGC(sessionManager); GarbageManager.registerNeedGC(sessionManager);
reloadManager.registerReloadable("launchServer", this); reloadManager.registerReloadable("launchServer", this);
registerObject("permissionsHandler", config.permissionsHandler); registerObject("permissionsHandler", config.permissionsHandler);
@ -669,6 +725,19 @@ private LauncherBinary binary() {
LogHelper.error(e); LogHelper.error(e);
} }
} }
if(config.launch4j.alternative != null)
{
switch (config.launch4j.alternative) {
case "simple":
return new SimpleEXELauncherBinary(this);
case "no":
//None
break;
default:
LogHelper.warning("Alternative %s not found", config.launch4j.alternative);
break;
}
}
try { try {
Class.forName("net.sf.launch4j.Builder"); Class.forName("net.sf.launch4j.Builder");
if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this); if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this);
@ -714,6 +783,7 @@ private void generateConfigIfNotExists(boolean testEnv) throws IOException {
newConfig.launch4j = new ExeConf(); newConfig.launch4j = new ExeConf();
newConfig.launch4j.enabled = true; newConfig.launch4j.enabled = true;
newConfig.launch4j.copyright = "© GravitLauncher Team"; newConfig.launch4j.copyright = "© GravitLauncher Team";
newConfig.launch4j.alternative = "no";
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString()); newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch)); newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch));
newConfig.launch4j.internalName = "Launcher"; newConfig.launch4j.internalName = "Launcher";
@ -741,19 +811,22 @@ private void generateConfigIfNotExists(boolean testEnv) throws IOException {
newConfig.netty.fileServerEnabled = true; newConfig.netty.fileServerEnabled = true;
newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)}; newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)};
newConfig.netty.performance = new NettyPerformanceConfig(); newConfig.netty.performance = new NettyPerformanceConfig();
newConfig.netty.performance.usingEpoll = JVMHelper.OS_TYPE == JVMHelper.OS.LINUX; //Only linux newConfig.netty.performance.usingEpoll = Epoll.isAvailable();
newConfig.netty.performance.bossThread = 2; newConfig.netty.performance.bossThread = 2;
newConfig.netty.performance.workerThread = 8; newConfig.netty.performance.workerThread = 8;
newConfig.launcher = new LauncherConf(); newConfig.launcher = new LauncherConf();
newConfig.launcher.guardType = "no"; newConfig.launcher.guardType = "no";
newConfig.launcher.compress = true;
newConfig.launcher.warningMissArchJava = true;
newConfig.launcher.attachLibraryBeforeProGuard = false;
newConfig.launcher.deleteTempFiles = true;
newConfig.launcher.enabledProGuard = true;
newConfig.launcher.stripLineNumbers = true;
newConfig.launcher.proguardGenMappings = true;
newConfig.enabledRadon = true; newConfig.certificate = new CertificateConf();
newConfig.genMappings = true; newConfig.certificate.enabled = false;
newConfig.enabledProGuard = true;
newConfig.stripLineNumbers = true;
newConfig.deleteTempFiles = true;
newConfig.isWarningMissArchJava = true;
newConfig.components = new HashMap<>(); newConfig.components = new HashMap<>();
AuthLimiterComponent authLimiterComponent = new AuthLimiterComponent(); AuthLimiterComponent authLimiterComponent = new AuthLimiterComponent();
@ -808,12 +881,12 @@ public void setProfiles(List<ClientProfile> profilesList) {
this.profilesList = Collections.unmodifiableList(profilesList); this.profilesList = Collections.unmodifiableList(profilesList);
} }
public SignedObjectHolder<HashedDir> getUpdateDir(String name) { public HashedDir getUpdateDir(String name) {
return updatesDirMap.get(name); return updatesDirMap.get(name);
} }
public Set<Entry<String, SignedObjectHolder<HashedDir>>> getUpdateDirs() { public Set<Entry<String, HashedDir>> getUpdateDirs() {
return updatesDirMap.entrySet(); return updatesDirMap.entrySet();
} }
@ -866,7 +939,7 @@ public void syncProfilesDir() throws IOException {
public void syncUpdatesDir(Collection<String> dirs) throws IOException { public void syncUpdatesDir(Collection<String> dirs) throws IOException {
LogHelper.info("Syncing updates dir"); LogHelper.info("Syncing updates dir");
Map<String, SignedObjectHolder<HashedDir>> newUpdatesDirMap = new HashMap<>(16); Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(updatesDir)) { try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(updatesDir)) {
for (final Path updateDir : dirStream) { for (final Path updateDir : dirStream) {
if (Files.isHidden(updateDir)) if (Files.isHidden(updateDir))
@ -882,7 +955,7 @@ public void syncUpdatesDir(Collection<String> dirs) throws IOException {
// Add from previous map (it's guaranteed to be non-null) // Add from previous map (it's guaranteed to be non-null)
if (dirs != null && !dirs.contains(name)) { if (dirs != null && !dirs.contains(name)) {
SignedObjectHolder<HashedDir> hdir = updatesDirMap.get(name); HashedDir hdir = updatesDirMap.get(name);
if (hdir != null) { if (hdir != null) {
newUpdatesDirMap.put(name, hdir); newUpdatesDirMap.put(name, hdir);
continue; continue;
@ -892,7 +965,7 @@ public void syncUpdatesDir(Collection<String> dirs) throws IOException {
// Sync and sign update dir // Sync and sign update dir
LogHelper.info("Syncing '%s' update dir", name); LogHelper.info("Syncing '%s' update dir", name);
HashedDir updateHDir = new HashedDir(updateDir, null, true, true); HashedDir updateHDir = new HashedDir(updateDir, null, true, true);
newUpdatesDirMap.put(name, new SignedObjectHolder<>(updateHDir, privateKey)); newUpdatesDirMap.put(name, updateHDir);
} }
} }
updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap); updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap);

View file

@ -0,0 +1,81 @@
package pro.gravit.launchserver.auth;
import com.zaxxer.hikari.HikariDataSource;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.VerifyHelper;
import org.postgresql.ds.PGSimpleDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public final class PostgreSQLSourceConfig implements AutoCloseable {
public static final int TIMEOUT = VerifyHelper.verifyInt(
Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.idleTimeout", Integer.toString(5000))),
VerifyHelper.POSITIVE, "launcher.postgresql.idleTimeout can't be <= 5000");
private static final int MAX_POOL_SIZE = VerifyHelper.verifyInt(
Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.maxPoolSize", Integer.toString(3))),
VerifyHelper.POSITIVE, "launcher.postgresql.maxPoolSize can't be <= 0");
// Instance
private String poolName;
// Config
private String address;
private int port;
private String username;
private String password;
private String database;
// Cache
private DataSource source;
private boolean hikari;
@Override
public synchronized void close() {
if (hikari) { // Shutdown hikari pool
((HikariDataSource) source).close();
}
}
public synchronized Connection getConnection() throws SQLException {
if (source == null) { // New data source
PGSimpleDataSource postgresqlSource = new PGSimpleDataSource();
// Set credentials
postgresqlSource.setServerName(address);
postgresqlSource.setPortNumber(port);
postgresqlSource.setUser(username);
postgresqlSource.setPassword(password);
postgresqlSource.setDatabaseName(database);
// Try using HikariCP
source = postgresqlSource;
//noinspection Duplicates
try {
Class.forName("com.zaxxer.hikari.HikariDataSource");
hikari = true; // Used for shutdown. Not instanceof because of possible classpath error
// Set HikariCP pool
HikariDataSource hikariSource = new HikariDataSource();
hikariSource.setDataSource(source);
// Set pool settings
hikariSource.setPoolName(poolName);
hikariSource.setMinimumIdle(0);
hikariSource.setMaximumPoolSize(MAX_POOL_SIZE);
hikariSource.setIdleTimeout(TIMEOUT * 1000L);
// Replace source with hds
source = hikariSource;
LogHelper.info("HikariCP pooling enabled for '%s'", poolName);
} catch (ClassNotFoundException ignored) {
LogHelper.warning("HikariCP isn't in classpath for '%s'", poolName);
}
}
return source.getConnection();
}
}

View file

@ -24,6 +24,7 @@ public static void registerHandlers() {
providers.register("json", JsonAuthHandler.class); providers.register("json", JsonAuthHandler.class);
providers.register("memory", MemoryAuthHandler.class); providers.register("memory", MemoryAuthHandler.class);
providers.register("mysql", MySQLAuthHandler.class); providers.register("mysql", MySQLAuthHandler.class);
providers.register("postgresql", PostgreSQLAuthHandler.class);
providers.register("request", RequestAuthHandler.class); providers.register("request", RequestAuthHandler.class);
providers.register("hibernate", HibernateAuthHandler.class); providers.register("hibernate", HibernateAuthHandler.class);
registredHandl = true; registredHandl = true;

View file

@ -0,0 +1,116 @@
package pro.gravit.launchserver.auth.handler;
import org.postgresql.util.PGobject;
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
import java.io.IOException;
import java.sql.*;
import java.util.UUID;
public final class PostgreSQLAuthHandler extends CachedAuthHandler {
private PostgreSQLSourceConfig postgreSQLHolder;
private String uuidColumn;
private String usernameColumn;
private String accessTokenColumn;
private String serverIDColumn;
private String queryByUUIDSQL;
private String queryByUsernameSQL;
private String updateAuthSQL;
private String updateServerIDSQL;
@Override
public void close() {
postgreSQLHolder.close();
}
private Entry constructEntry(ResultSet set) throws SQLException {
return set.next() ? new Entry(UUID.fromString(set.getString(uuidColumn)),
set.getString(usernameColumn), set.getString(accessTokenColumn), set.getString(serverIDColumn)) : null;
}
@Override
protected Entry fetchEntry(String username) throws IOException {
return query(queryByUsernameSQL, username);
}
@Override
protected Entry fetchEntry(UUID uuid) throws IOException {
return query(queryByUUIDSQL, uuid);
}
@Override
protected boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException {
try (Connection c = postgreSQLHolder.getConnection();
PreparedStatement s = c.prepareStatement(updateAuthSQL)) {
s.setString(1, username); // Username case
s.setString(2, accessToken);
PGobject uuidObject = new PGobject();
uuidObject.setType("uuid");
uuidObject.setValue(uuid.toString());
s.setObject(3, uuidObject);
// Execute update
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
return s.executeUpdate() > 0;
} catch (SQLException e) {
throw new IOException(e);
}
}
@Override
protected boolean updateServerID(UUID uuid, String serverID) throws IOException {
try (Connection c = postgreSQLHolder.getConnection();
PreparedStatement s = c.prepareStatement(updateServerIDSQL)) {
s.setString(1, serverID);
PGobject uuidObject = new PGobject();
uuidObject.setType("uuid");
uuidObject.setValue(uuid.toString());
s.setObject(2, uuidObject);
// Execute update
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
return s.executeUpdate() > 0;
} catch (SQLException e) {
throw new IOException(e);
}
}
private Entry query(String sql, String value) throws IOException {
try (Connection c = postgreSQLHolder.getConnection();
PreparedStatement s = c.prepareStatement(sql)) {
s.setString(1, value);
// Execute query
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
return constructEntry(set);
}
} catch (SQLException e) {
throw new IOException(e);
}
}
private Entry query(String sql, UUID value) throws IOException {
try (Connection c = postgreSQLHolder.getConnection();
PreparedStatement s = c.prepareStatement(sql)) {
PGobject uuidObject = new PGobject();
uuidObject.setType("uuid");
uuidObject.setValue(value.toString());
s.setObject(1, uuidObject);
// Execute query
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
return constructEntry(set);
}
} catch (SQLException e) {
throw new IOException(e);
}
}
}

View file

@ -153,13 +153,17 @@ public void onCheckInfo(OshiHWID hwid, String username, Connection c) throws HWI
db_hwid.HWDiskSerial = set.getString(hwidFieldHWDiskSerial); db_hwid.HWDiskSerial = set.getString(hwidFieldHWDiskSerial);
db_hwid.totalMemory = Long.valueOf(set.getString(hwidFieldTotalMemory)); db_hwid.totalMemory = Long.valueOf(set.getString(hwidFieldTotalMemory));
db_hwid.macAddr = set.getString(hwidFieldMAC); db_hwid.macAddr = set.getString(hwidFieldMAC);
if (LogHelper.isDevEnabled()) {
LogHelper.dev("Compare HWID: %s vs %s", hwid.getSerializeString(), db_hwid.getSerializeString()); LogHelper.dev("Compare HWID: %s vs %s", hwid.getSerializeString(), db_hwid.getSerializeString());
}
int compare_point = hwid.compare(db_hwid); int compare_point = hwid.compare(db_hwid);
if (compare_point < compare) continue; if (compare_point < compare) continue;
else { else {
if (LogHelper.isDevEnabled()) {
LogHelper.debug("User %s hwid check: found compare %d in %d", username, compare_point, set.getInt("id")); LogHelper.debug("User %s hwid check: found compare %d in %d", username, compare_point, set.getInt("id"));
} }
} }
}
if (oneCompareMode) isOne = true; if (oneCompareMode) isOne = true;
boolean isBanned = set.getBoolean(hwidFieldBanned); boolean isBanned = set.getBoolean(hwidFieldBanned);
if (isBanned) { if (isBanned) {
@ -175,7 +179,9 @@ public void onCheckInfo(OshiHWID hwid, String username, Connection c) throws HWI
} }
public void setIsBanned(HWID hwid, boolean isBanned) { public void setIsBanned(HWID hwid, boolean isBanned) {
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("%s Request HWID: %s", isBanned ? "Ban" : "UnBan", hwid.toString()); LogHelper.debug("%s Request HWID: %s", isBanned ? "Ban" : "UnBan", hwid.toString());
}
if (hwid instanceof OshiHWID) { if (hwid instanceof OshiHWID) {
OshiHWID oshiHWID = (OshiHWID) hwid; OshiHWID oshiHWID = (OshiHWID) hwid;
try (Connection c = mySQLHolder.getConnection()) { try (Connection c = mySQLHolder.getConnection()) {
@ -214,7 +220,9 @@ public void unban(List<HWID> list) {
public List<HWID> getHwid(String username) { public List<HWID> getHwid(String username) {
ArrayList<HWID> list = new ArrayList<>(); ArrayList<HWID> list = new ArrayList<>();
try (Connection c = mySQLHolder.getConnection()) { try (Connection c = mySQLHolder.getConnection()) {
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("Try find HWID from username %s", username); LogHelper.debug("Try find HWID from username %s", username);
}
PreparedStatement s = c.prepareStatement(String.format("SELECT %s, %s FROM `%s` WHERE `%s` = ? LIMIT 1", userFieldHwid, userFieldLogin, tableUsers, userFieldLogin)); PreparedStatement s = c.prepareStatement(String.format("SELECT %s, %s FROM `%s` WHERE `%s` = ? LIMIT 1", userFieldHwid, userFieldLogin, tableUsers, userFieldLogin));
s.setString(1, username); s.setString(1, username);

View file

@ -20,6 +20,7 @@ public static void registerProviders() {
providers.register("accept", AcceptAuthProvider.class); providers.register("accept", AcceptAuthProvider.class);
providers.register("reject", RejectAuthProvider.class); providers.register("reject", RejectAuthProvider.class);
providers.register("mysql", MySQLAuthProvider.class); providers.register("mysql", MySQLAuthProvider.class);
providers.register("postgresql", PostgreSQLAuthProvider.class);
providers.register("request", RequestAuthProvider.class); providers.register("request", RequestAuthProvider.class);
providers.register("json", JsonAuthProvider.class); providers.register("json", JsonAuthProvider.class);
providers.register("hibernate", HibernateAuthProvider.class); providers.register("hibernate", HibernateAuthProvider.class);

View file

@ -0,0 +1,42 @@
package pro.gravit.launchserver.auth.provider;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.SecurityHelper;
public final class PostgreSQLAuthProvider extends AuthProvider {
private PostgreSQLSourceConfig postgreSQLHolder;
private String query;
private String message;
private String[] queryParams;
private boolean usePermission;
@Override
public AuthProviderResult auth(String login, String password, String ip) throws SQLException, AuthException {
try (Connection c = postgreSQLHolder.getConnection(); PreparedStatement s = c.prepareStatement(query)) {
String[] replaceParams = {"login", login, "password", password, "ip", ip};
for (int i = 0; i < queryParams.length; i++) {
s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams));
}
// Execute SQL query
s.setQueryTimeout(PostgreSQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), usePermission ? new ClientPermissions(set.getLong(2)) : srv.config.permissionsHandler.getPermissions(set.getString(1))) : authError(message);
}
}
}
@Override
public void close() {
postgreSQLHolder.close();
}
}

View file

@ -20,7 +20,9 @@ public RequestTextureProvider(String skinURL, String cloakURL) {
} }
private static Texture getTexture(String url, boolean cloak) throws IOException { private static Texture getTexture(String url, boolean cloak) throws IOException {
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("Getting texture: '%s'", url); LogHelper.debug("Getting texture: '%s'", url);
}
try { try {
return new Texture(url, cloak); return new Texture(url, cloak);
} catch (FileNotFoundException ignored) { } catch (FileNotFoundException ignored) {

View file

@ -1,109 +1,100 @@
package pro.gravit.launchserver.binary; package pro.gravit.launchserver.binary;
import java.io.IOException; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import javassist.CannotCompileException; import org.objectweb.asm.Type;
import javassist.ClassPool;
import javassist.CtClass; import pro.gravit.launcher.AutogenConfig;
import javassist.CtConstructor;
import javassist.CtMethod;
import javassist.NotFoundException;
import pro.gravit.launcher.LauncherConfig; import pro.gravit.launcher.LauncherConfig;
import pro.gravit.launchserver.binary.tasks.MainBuildTask; import pro.gravit.launcher.modules.Module;
import pro.gravit.launcher.modules.ModulesManager;
import pro.gravit.launchserver.asm.ClassMetadataReader;
import pro.gravit.launchserver.asm.SafeClassWriter;
public class JAConfigurator implements AutoCloseable { public class JAConfigurator {
public ClassPool pool; private static final String modulesManagerName = Type.getInternalName(ModulesManager.class);
public CtClass ctClass; private static final String registerModDesc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Module.class));
public CtConstructor ctConstructor; private static final String autoGenConfigName = Type.getInternalName(AutogenConfig.class);
public CtMethod initModuleMethod; private static final String stringName = Type.getInternalName(String.class);
String classname; private final ClassNode configclass;
StringBuilder body; private final MethodNode constructor;
StringBuilder moduleBody; private final MethodNode initModuleMethod;
int autoincrement;
public JAConfigurator(String configclass, MainBuildTask jarLauncherBinary) throws NotFoundException { public JAConfigurator(ClassNode configclass) {
pool = new ClassPool(false); this.configclass = configclass;
pool.appendSystemPath(); constructor = configclass.methods.stream().filter(e -> "<init>".equals(e.name)).findFirst().get();
classname = configclass; constructor.instructions = new InsnList();
ctClass = pool.get(classname); initModuleMethod = configclass.methods.stream().filter(e -> "initModules".equals(e.name)).findFirst().get();
ctConstructor = ctClass.getDeclaredConstructor(null); initModuleMethod.instructions = new InsnList();
initModuleMethod = ctClass.getDeclaredMethod("initModules");
body = new StringBuilder("{ isInitModules = false; ");
moduleBody = new StringBuilder("{ isInitModules = true; ");
autoincrement = 0;
} }
public void addModuleClass(String fullName) { public void addModuleClass(String fullName) {
moduleBody.append("pro.gravit.launcher.modules.Module mod"); initModuleMethod.instructions.insert(new MethodInsnNode(Opcodes.INVOKEINTERFACE, modulesManagerName, "registerModule", registerModDesc));
moduleBody.append(autoincrement); initModuleMethod.instructions.insert(new MethodInsnNode(Opcodes.INVOKESPECIAL, fullName.replace('.', '/'), "<init>", "()V"));
moduleBody.append(" = new "); initModuleMethod.instructions.insert(new TypeInsnNode(Opcodes.NEW, fullName.replace('.', '/')));
moduleBody.append(fullName);
moduleBody.append("();");
moduleBody.append("pro.gravit.launcher.Launcher.modulesManager.registerModule( mod");
moduleBody.append(autoincrement);
moduleBody.append(");");
autoincrement++;
} }
@Override public byte[] getBytecode(ClassMetadataReader reader) {
public void close() { ClassWriter cw = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
ctClass.defrost(); configclass.accept(cw);
} return cw.toByteArray();
public CtClass getCtClass() {
return ctClass;
}
public byte[] getBytecode() throws IOException, CannotCompileException {
return ctClass.toBytecode();
}
public void compile() throws CannotCompileException {
body.append("}");
moduleBody.append("}");
ctConstructor.setBody(body.toString());
initModuleMethod.insertAfter(moduleBody.toString());
if (ctClass.isFrozen()) ctClass.defrost();
} }
public String getZipEntryPath() { public String getZipEntryPath() {
return classname.replace('.', '/').concat(".class"); return configclass.name.concat(".class");
} }
public void setAddress(String address) { public void setAddress(String address) {
body.append("this.address = \""); constructor.instructions.add(new LdcInsnNode(address));
body.append(address); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "address", stringName));
body.append("\";");
} }
public void setProjectName(String name) { public void setProjectName(String name) {
body.append("this.projectname = \""); constructor.instructions.add(new LdcInsnNode(name));
body.append(name); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "projectname", stringName));
body.append("\";");
} }
public void setSecretKey(String key) { public void setSecretKey(String key) {
body.append("this.secretKeyClient = \""); constructor.instructions.add(new LdcInsnNode(key));
body.append(key); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "secretKeyClient", stringName));
body.append("\";");
} }
public void setOemUnlockKey(String key) { public void setOemUnlockKey(String key) {
body.append("this.oemUnlockKey = \""); constructor.instructions.add(new LdcInsnNode(key));
body.append(key); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "oemUnlockKey", stringName));
body.append("\";");
} }
public void setGuardType(String key) { public void setGuardType(String key) {
body.append("this.guardType = \""); constructor.instructions.add(new LdcInsnNode(key));
body.append(key); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardType", stringName));
body.append("\";"); }
public void push(final int value) {
if (value >= -1 && value <= 5)
constructor.instructions.add(new InsnNode(Opcodes.ICONST_0 + value));
else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)
constructor.instructions.add(new IntInsnNode(Opcodes.BIPUSH, value));
else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
constructor.instructions.add(new IntInsnNode(Opcodes.SIPUSH, value));
else
constructor.instructions.add(new LdcInsnNode(value));
} }
public void setEnv(LauncherConfig.LauncherEnvironment env) { public void setEnv(LauncherConfig.LauncherEnvironment env) {
int i = 2; int i = 2;
switch (env) {
switch (env) {
case DEV: case DEV:
i = 0; i = 0;
break; break;
@ -117,36 +108,35 @@ public void setEnv(LauncherConfig.LauncherEnvironment env) {
i = 3; i = 3;
break; break;
} }
body.append("this.env = "); push(i);
body.append(i); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "env", Type.INT_TYPE.getInternalName()));
body.append(";");
} }
public void setClientPort(int port) { public void setClientPort(int port) {
body.append("this.clientPort = "); push(port);
body.append(port); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "clientPort", Type.INT_TYPE.getInternalName()));
body.append(";");
} }
public void setWarningMissArchJava(boolean b) { public void setWarningMissArchJava(boolean b) {
body.append("this.isWarningMissArchJava = "); constructor.instructions.add(new InsnNode(b ? Opcodes.ICONST_1 : Opcodes.ICONST_0));
body.append(b ? "true" : "false"); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "isWarningMissArchJava", Type.BOOLEAN_TYPE.getInternalName()));
body.append(";");
} }
public void setGuardLicense(String name, String key, String encryptKey) { public void setGuardLicense(String name, String key, String encryptKey) {
body.append("this.guardLicenseName = \""); constructor.instructions.add(new LdcInsnNode(name));
body.append(name); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseName", stringName));
body.append("\";"); constructor.instructions.add(new LdcInsnNode(key));
body.append("this.guardLicenseKey = \""); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseKey", stringName));
body.append(key); constructor.instructions.add(new LdcInsnNode(encryptKey));
body.append("\";"); constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseEncryptKey", stringName));
body.append("this.guardLicenseEncryptKey = \"");
body.append(encryptKey);
body.append("\";");
} }
public ClassPool getPool() { public void nullGuardLicense() {
return pool; constructor.instructions.add(new InsnNode(Opcodes.ACONST_NULL));
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseName", stringName));
constructor.instructions.add(new InsnNode(Opcodes.ACONST_NULL));
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseKey", stringName));
constructor.instructions.add(new InsnNode(Opcodes.ACONST_NULL));
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseEncryptKey", stringName));
} }
} }

View file

@ -9,13 +9,7 @@
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.Launcher;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.binary.tasks.AdditionalFixesApplyTask; import pro.gravit.launchserver.binary.tasks.*;
import pro.gravit.launchserver.binary.tasks.AttachJarsTask;
import pro.gravit.launchserver.binary.tasks.LauncherBuildTask;
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
import pro.gravit.launchserver.binary.tasks.PrepareBuildTask;
import pro.gravit.launchserver.binary.tasks.ProGuardBuildTask;
import pro.gravit.launchserver.binary.tasks.RadonBuildTask;
import pro.gravit.utils.helper.CommonHelper; import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
@ -51,8 +45,8 @@ public void init() {
if (server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server)); if (server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server));
tasks.add(new ProGuardBuildTask(server)); tasks.add(new ProGuardBuildTask(server));
tasks.add(new AdditionalFixesApplyTask(server)); tasks.add(new AdditionalFixesApplyTask(server));
tasks.add(new RadonBuildTask(server));
if (!server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server)); if (!server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server));
if(server.config.launcher.compress) tasks.add(new CompressBuildTask(server));
} }
@Override @Override
@ -70,12 +64,12 @@ public void build() throws IOException {
long time_task_end = System.currentTimeMillis(); long time_task_end = System.currentTimeMillis();
long time_task = time_task_end - time_this; long time_task = time_task_end - time_this;
time_this = time_task_end; time_this = time_task_end;
if (isNeedDelete && server.config.deleteTempFiles) Files.deleteIfExists(oldPath); if (isNeedDelete && server.config.launcher.deleteTempFiles) Files.deleteIfExists(oldPath);
isNeedDelete = task.allowDelete(); isNeedDelete = task.allowDelete();
LogHelper.subInfo("Task %s processed from %d millis", task.getName(), time_task); LogHelper.subInfo("Task %s processed from %d millis", task.getName(), time_task);
} }
long time_end = System.currentTimeMillis(); long time_end = System.currentTimeMillis();
if (isNeedDelete && server.config.deleteTempFiles) IOHelper.move(thisPath, syncBinaryFile); if (isNeedDelete && server.config.launcher.deleteTempFiles) IOHelper.move(thisPath, syncBinaryFile);
else IOHelper.copy(thisPath, syncBinaryFile); else IOHelper.copy(thisPath, syncBinaryFile);
LogHelper.info("Build successful from %d millis", time_end - time_start); LogHelper.info("Build successful from %d millis", time_end - time_start);
} }

View file

@ -3,7 +3,6 @@
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import pro.gravit.launcher.serialize.signed.DigestBytesHolder;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
@ -11,7 +10,7 @@
public abstract class LauncherBinary { public abstract class LauncherBinary {
public final LaunchServer server; public final LaunchServer server;
public final Path syncBinaryFile; public final Path syncBinaryFile;
private volatile DigestBytesHolder binary; private volatile byte[] digest;
private volatile byte[] sign; private volatile byte[] sign;
protected LauncherBinary(LaunchServer server, Path binaryFile) { protected LauncherBinary(LaunchServer server, Path binaryFile) {
@ -27,8 +26,8 @@ public final boolean exists() {
} }
public final DigestBytesHolder getBytes() { public final byte[] getDigest() {
return binary; return digest;
} }
public final byte[] getSign() { public final byte[] getSign() {
@ -40,7 +39,7 @@ public void init() {
public final boolean sync() throws IOException { public final boolean sync() throws IOException {
boolean exists = exists(); boolean exists = exists();
binary = exists ? new DigestBytesHolder(IOHelper.read(syncBinaryFile), SecurityHelper.DigestAlgorithm.SHA512) : null; digest = exists ? SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(syncBinaryFile)) : null;
sign = exists ? SecurityHelper.sign(IOHelper.read(syncBinaryFile), server.privateKey) : null; sign = exists ? SecurityHelper.sign(IOHelper.read(syncBinaryFile), server.privateKey) : null;
return exists; return exists;

View file

@ -44,7 +44,7 @@ public ProguardConf(LaunchServer srv) {
public String[] buildConfig(Path inputJar, Path outputJar) { public String[] buildConfig(Path inputJar, Path outputJar) {
List<String> confStrs = new ArrayList<>(); List<String> confStrs = new ArrayList<>();
prepare(false); prepare(false);
if (srv.config.genMappings) confStrs.add("-printmapping \'" + mappings.toFile().getName() + "\'"); if (srv.config.launcher.proguardGenMappings) confStrs.add("-printmapping \'" + mappings.toFile().getName() + "\'");
confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'"); confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'");
confStrs.add("-injar \'" + inputJar.toAbsolutePath() + "\'"); confStrs.add("-injar \'" + inputJar.toAbsolutePath() + "\'");
confStrs.add("-outjar \'" + outputJar.toAbsolutePath() + "\'"); confStrs.add("-outjar \'" + outputJar.toAbsolutePath() + "\'");

View file

@ -0,0 +1,31 @@
package pro.gravit.launchserver.binary;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
public class SimpleEXELauncherBinary extends LauncherBinary {
public Path exeTemplate;
public SimpleEXELauncherBinary(LaunchServer server) {
super(server, LauncherBinary.resolve(server, ".exe"));
exeTemplate = server.dir.resolve("SimpleTemplate.exe");
}
@Override
public void build() throws IOException {
if(!IOHelper.isFile(exeTemplate))
{
LogHelper.warning("[SimpleEXEBinary] File %s not found. %s not created", exeTemplate.toString(), syncBinaryFile.toString());
return;
}
try(OutputStream output = IOHelper.newOutput(syncBinaryFile))
{
IOHelper.transfer(exeTemplate, output);
IOHelper.transfer(server.launcherBinary.syncBinaryFile, output);
}
}
}

View file

@ -19,6 +19,7 @@
import pro.gravit.launchserver.asm.ClassMetadataReader; import pro.gravit.launchserver.asm.ClassMetadataReader;
import pro.gravit.launchserver.asm.SafeClassWriter; import pro.gravit.launchserver.asm.SafeClassWriter;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
public class AdditionalFixesApplyTask implements LauncherBuildTask { public class AdditionalFixesApplyTask implements LauncherBuildTask {
private final LaunchServer server; private final LaunchServer server;
@ -73,7 +74,12 @@ public static void apply(Path inputFile, Path addFile, ZipOutputStream output, L
IOHelper.transfer(input, outputStream); IOHelper.transfer(input, outputStream);
bytes = outputStream.toByteArray(); bytes = outputStream.toByteArray();
} }
output.write(classFix(bytes, reader, srv.config.stripLineNumbers)); try {
bytes = classFix(bytes, reader, srv.config.launcher.stripLineNumbers);
} catch (Throwable t) {
LogHelper.subWarning("Error on fixing class: " + t);
}
output.write(bytes);
} else } else
IOHelper.transfer(input, output); IOHelper.transfer(input, output);
e = input.getNextEntry(); e = input.getNextEntry();
@ -86,7 +92,6 @@ private static byte[] classFix(byte[] bytes, ClassMetadataReader reader, boolean
ClassReader cr = new ClassReader(bytes); ClassReader cr = new ClassReader(bytes);
ClassNode cn = new ClassNode(); ClassNode cn = new ClassNode();
cr.accept(cn, stripNumbers ? (ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES) : ClassReader.SKIP_FRAMES); cr.accept(cn, stripNumbers ? (ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES) : ClassReader.SKIP_FRAMES);
ClassWriter cw = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); ClassWriter cw = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cn.accept(cw); cn.accept(cw);
return cw.toByteArray(); return cw.toByteArray();

View file

@ -0,0 +1,53 @@
package pro.gravit.launchserver.binary.tasks;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.helper.IOHelper;
import java.io.IOException;
import java.nio.file.Path;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class CompressBuildTask implements LauncherBuildTask {
public transient final LaunchServer server;
public CompressBuildTask(LaunchServer server) {
this.server = server;
}
@Override
public String getName() {
return "compress";
}
@Override
public Path process(Path inputFile) throws IOException {
Path output = server.launcherBinary.nextPath(this);
try(ZipOutputStream outputStream = new ZipOutputStream(IOHelper.newOutput(output)))
{
outputStream.setMethod(ZipOutputStream.DEFLATED);
outputStream.setLevel(Deflater.BEST_COMPRESSION);
try(ZipInputStream input = IOHelper.newZipInput(inputFile))
{
ZipEntry e = input.getNextEntry();
while (e != null) {
if (e.isDirectory()) {
e = input.getNextEntry();
continue;
}
outputStream.putNextEntry(IOHelper.newZipEntry(e));
IOHelper.transfer(input, outputStream);
e = input.getNextEntry();
}
}
}
return output;
}
@Override
public boolean allowDelete() {
return true;
}
}

View file

@ -16,8 +16,9 @@
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import javassist.CannotCompileException; import org.objectweb.asm.ClassReader;
import javassist.NotFoundException; import org.objectweb.asm.tree.ClassNode;
import pro.gravit.launcher.AutogenConfig; import pro.gravit.launcher.AutogenConfig;
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherConfig; import pro.gravit.launcher.LauncherConfig;
@ -115,27 +116,22 @@ public String getName() {
@Override @Override
public Path process(Path inputJar) throws IOException { public Path process(Path inputJar) throws IOException {
Path outputJar = server.launcherBinary.nextPath("main"); Path outputJar = server.launcherBinary.nextPath("main");
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(outputJar)); try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(outputJar))) {
JAConfigurator jaConfigurator = new JAConfigurator(AutogenConfig.class.getName(), this)) { ClassNode cn = new ClassNode();
jaConfigurator.pool.insertClassPath(inputJar.toFile().getAbsolutePath()); new ClassReader(IOHelper.getResourceBytes(AutogenConfig.class.getName().replace('.', '/').concat(".class"))).accept(cn, 0);
server.launcherBinary.coreLibs.stream().map(e -> e.toFile().getAbsolutePath()) JAConfigurator jaConfigurator = new JAConfigurator(cn);
.forEach(t -> {
try {
jaConfigurator.pool.appendClassPath(t);
} catch (NotFoundException e2) {
LogHelper.error(e2);
}
});
BuildContext context = new BuildContext(output, jaConfigurator, this); BuildContext context = new BuildContext(output, jaConfigurator, this);
server.buildHookManager.hook(context); server.buildHookManager.hook(context);
jaConfigurator.setAddress(server.config.netty.address); jaConfigurator.setAddress(server.config.netty.address);
if (server.config.guardLicense != null) if (server.config.guardLicense != null)
jaConfigurator.setGuardLicense(server.config.guardLicense.name, server.config.guardLicense.key, server.config.guardLicense.encryptKey); jaConfigurator.setGuardLicense(server.config.guardLicense.name, server.config.guardLicense.key, server.config.guardLicense.encryptKey);
else
jaConfigurator.nullGuardLicense();
jaConfigurator.setProjectName(server.config.projectName); jaConfigurator.setProjectName(server.config.projectName);
jaConfigurator.setSecretKey(SecurityHelper.randomStringAESKey()); jaConfigurator.setSecretKey(SecurityHelper.randomStringAESKey());
jaConfigurator.setClientPort(32148 + SecurityHelper.newRandom().nextInt(512)); jaConfigurator.setClientPort(32148 + SecurityHelper.newRandom().nextInt(512));
jaConfigurator.setGuardType(server.config.launcher.guardType); jaConfigurator.setGuardType(server.config.launcher.guardType);
jaConfigurator.setWarningMissArchJava(server.config.isWarningMissArchJava); jaConfigurator.setWarningMissArchJava(server.config.launcher.warningMissArchJava);
jaConfigurator.setEnv(server.config.env); jaConfigurator.setEnv(server.config.env);
if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken(); if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken();
jaConfigurator.setOemUnlockKey(server.runtime.oemUnlockKey); jaConfigurator.setOemUnlockKey(server.runtime.oemUnlockKey);
@ -148,11 +144,12 @@ public Path process(Path inputJar) throws IOException {
LogHelper.error(e1); LogHelper.error(e1);
} }
}); });
String zPath = jaConfigurator.getZipEntryPath();
try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(inputJar))) { try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(inputJar))) {
ZipEntry e = input.getNextEntry(); ZipEntry e = input.getNextEntry();
while (e != null) { while (e != null) {
String filename = e.getName(); String filename = e.getName();
if (server.buildHookManager.isContainsBlacklist(filename) || e.isDirectory()) { if (server.buildHookManager.isContainsBlacklist(filename) || e.isDirectory() || zPath.equals(filename)) {
e = input.getNextEntry(); e = input.getNextEntry();
continue; continue;
} }
@ -204,12 +201,9 @@ public Path process(Path inputJar) throws IOException {
// Write launcher config file // Write launcher config file
output.putNextEntry(newZipEntry(Launcher.CONFIG_FILE)); output.putNextEntry(newZipEntry(Launcher.CONFIG_FILE));
output.write(launcherConfigBytes); output.write(launcherConfigBytes);
ZipEntry e = newZipEntry(jaConfigurator.getZipEntryPath()); ZipEntry e = newZipEntry(zPath);
output.putNextEntry(e); output.putNextEntry(e);
jaConfigurator.compile(); output.write(jaConfigurator.getBytecode(reader));
output.write(jaConfigurator.getBytecode());
} catch (CannotCompileException | NotFoundException e) {
throw new IOException(e);
} }
reader.close(); reader.close();
return outputJar; return outputJar;

View file

@ -26,7 +26,7 @@ public String getName() {
@Override @Override
public Path process(Path inputFile) throws IOException { public Path process(Path inputFile) throws IOException {
Path outputJar = server.launcherBinary.nextLowerPath(this); Path outputJar = server.launcherBinary.nextLowerPath(this);
if (server.config.enabledProGuard) { if (server.config.launcher.enabledProGuard) {
Configuration proguard_cfg = new Configuration(); Configuration proguard_cfg = new Configuration();
ConfigurationParser parser = new ConfigurationParser(server.proguardConf.buildConfig(inputFile, outputJar), ConfigurationParser parser = new ConfigurationParser(server.proguardConf.buildConfig(inputFile, outputJar),
server.proguardConf.proguard.toFile(), System.getProperties()); server.proguardConf.proguard.toFile(), System.getProperties());

View file

@ -1,57 +0,0 @@
package pro.gravit.launchserver.binary.tasks;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
import me.itzsomebody.radon.Radon;
import me.itzsomebody.radon.SessionInfo;
import me.itzsomebody.radon.config.ConfigurationParser;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.UnpackHelper;
public class RadonBuildTask implements LauncherBuildTask {
private final LaunchServer srv;
public final Path config;
public RadonBuildTask(LaunchServer srv) {
this.srv = srv;
config = this.srv.dir.resolve("radon.yml");
System.setProperty("radon.useJVMCP", "true");
}
@Override
public String getName() {
return "Radon";
}
@Override
public Path process(Path inputFile) throws IOException {
Path outputFile = srv.launcherBinary.nextLowerPath(this);
Files.deleteIfExists(outputFile);
if (srv.config.enabledRadon) {
if (!IOHelper.isFile(config))
UnpackHelper.unpack(IOHelper.getResourceURL("pro/gravit/launchserver/defaults/radon.cfg"), config);
ConfigurationParser p = new ConfigurationParser(IOHelper.newInput(config));
SessionInfo info = p.createSessionFromConfig();
info.setInput(inputFile.toFile());
info.setOutput(outputFile.toFile());
List<File> libs = srv.launcherBinary.coreLibs.stream().map(Path::toFile).collect(Collectors.toList());
libs.addAll(srv.launcherBinary.addonLibs.stream().map(Path::toFile).collect(Collectors.toList()));
info.setLibraries(libs);
Radon r = new Radon(info);
r.run();
} else
IOHelper.copy(inputFile, outputFile);
return outputFile;
}
@Override
public boolean allowDelete() {
return true;
}
}

View file

@ -1,16 +1,15 @@
package pro.gravit.launchserver.command.basic; package pro.gravit.launchserver.command.basic;
import java.nio.file.Paths;
import java.security.KeyPair;
import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509CertificateHolder;
import pro.gravit.launcher.events.PingEvent;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.command.Command; import pro.gravit.launchserver.command.Command;
import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler; import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler;
import pro.gravit.utils.helper.CommonHelper; import pro.gravit.utils.helper.CommonHelper;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
public class TestCommand extends Command { public class TestCommand extends Command {
public TestCommand(LaunchServer server) { public TestCommand(LaunchServer server) {
super(server); super(server);
@ -44,6 +43,10 @@ public void invoke(String... args) throws Exception {
server.certificateManager.writePrivateKey(Paths.get("ca.key"), server.certificateManager.caKey); server.certificateManager.writePrivateKey(Paths.get("ca.key"), server.certificateManager.caKey);
server.certificateManager.writeCertificate(Paths.get("ca.crt"), server.certificateManager.ca); server.certificateManager.writeCertificate(Paths.get("ca.crt"), server.certificateManager.ca);
} }
if(args[0].equals("readCA")) {
server.certificateManager.ca = server.certificateManager.readCertificate(Paths.get("ca.crt"));
server.certificateManager.caKey = server.certificateManager.readPrivateKey(Paths.get("ca.key"));
}
if(args[0].equals("genCert")) { if(args[0].equals("genCert")) {
verifyArgs(args, 2); verifyArgs(args, 2);
String name = args[1]; String name = args[1];

View file

@ -4,6 +4,7 @@
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -41,7 +42,7 @@ public void invoke(String... args) throws Exception {
public void invoke(String... args) throws Exception { public void invoke(String... args) throws Exception {
verifyArgs(args, 1); verifyArgs(args, 1);
LogHelper.info("Sessions write to %s", args[0]); LogHelper.info("Sessions write to %s", args[0]);
Set<Client> clientSet = server.sessionManager.getSessions(); Collection<Client> clientSet = server.sessionManager.getSessions();
try (Writer writer = IOHelper.newWriter(Paths.get(args[0]))) { try (Writer writer = IOHelper.newWriter(Paths.get(args[0]))) {
Launcher.gsonManager.configGson.toJson(clientSet, writer); Launcher.gsonManager.configGson.toJson(clientSet, writer);
} }

View file

@ -2,8 +2,6 @@
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.command.Command; import pro.gravit.launchserver.command.Command;
import pro.gravit.launchserver.manangers.ReconfigurableManager;
import pro.gravit.utils.helper.LogHelper;
public class ConfigCommand extends Command { public class ConfigCommand extends Command {
public ConfigCommand(LaunchServer server) { public ConfigCommand(LaunchServer server) {

View file

@ -0,0 +1,65 @@
package pro.gravit.launchserver.components;
import java.util.HashMap;
import java.util.Map;
import pro.gravit.launcher.NeedGarbageCollection;
public class AbstractLimiter<T> implements NeedGarbageCollection {
public final int maxTrys;
public final int banMillis;
public AbstractLimiter(int maxTrys, int banMillis) {
this.maxTrys = maxTrys;
this.banMillis = banMillis;
}
@Override
public void garbageCollection() {
long time = System.currentTimeMillis();
map.entrySet().removeIf((e) -> e.getValue().time + banMillis < time);
}
class LimitEntry
{
long time;
int trys;
public LimitEntry(long time, int trys) {
this.time = time;
this.trys = trys;
}
public LimitEntry() {
time = System.currentTimeMillis();
trys = 0;
}
}
protected Map<T, LimitEntry> map = new HashMap<>();
public boolean check(T address)
{
LimitEntry entry = map.get(address);
if(entry == null)
{
map.put(address, new LimitEntry());
return true;
}
else
{
long time = System.currentTimeMillis();
if(entry.trys < maxTrys)
{
entry.trys++;
entry.time = time;
return true;
}
if(entry.time + banMillis < time)
{
entry.trys = 1;
entry.time = time;
return true;
}
return false;
}
}
}

View file

@ -1,18 +1,17 @@
package pro.gravit.launchserver.components; package pro.gravit.launchserver.components;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import pro.gravit.launcher.NeedGarbageCollection; import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.BiHookSet.Hook;
import pro.gravit.utils.HookException; import pro.gravit.utils.HookException;
public class AuthLimiterComponent extends Component implements NeedGarbageCollection, AutoCloseable { public class AuthLimiterComponent extends Component implements NeedGarbageCollection, AutoCloseable {
private transient final Hook<AuthResponse.AuthContext, Client> prA = this::preAuthHook;
private transient AbstractLimiter<String> limiter;
private transient LaunchServer srv; private transient LaunchServer srv;
@Override @Override
public void preInit(LaunchServer launchServer) { public void preInit(LaunchServer launchServer) {
@ -21,7 +20,8 @@ public void preInit(LaunchServer launchServer) {
@Override @Override
public void init(LaunchServer launchServer) { public void init(LaunchServer launchServer) {
launchServer.authHookManager.preHook.registerHook(prA); limiter = new AbstractLimiter<>(rateLimit, rateLimitMilis);
launchServer.authHookManager.preHook.registerHook(this::preAuthHook);
} }
@Override @Override
@ -30,88 +30,25 @@ public void postInit(LaunchServer launchServer) {
} }
public boolean preAuthHook(AuthResponse.AuthContext context, Client client) { public boolean preAuthHook(AuthResponse.AuthContext context, Client client) {
if (isLimit(context.ip)) { if (!excludeIps.contains(context.ip) && !limiter.check(context.ip)) {
throw new HookException(message); throw new HookException(message);
} }
return false; return false;
} }
static class AuthEntry {
public int value;
public long ts;
public AuthEntry(int i, long l) {
value = i;
ts = l;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof AuthEntry))
return false;
AuthEntry other = (AuthEntry) obj;
if (ts != other.ts)
return false;
return value == other.value;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (ts ^ ts >>> 32);
result = prime * result + value;
return result;
}
@Override
public String toString() {
return String.format("AuthEntry {value=%s, ts=%s}", value, ts);
}
}
public static final long TIMEOUT = 10 * 60 * 1000; //10 минут
public int rateLimit; public int rateLimit;
public int rateLimitMilis; public int rateLimitMilis;
public String message; public String message;
public transient HashMap<String, AuthEntry> map = new HashMap<>();
public List<String> excludeIps = new ArrayList<>(); public List<String> excludeIps = new ArrayList<>();
@Override @Override
public void garbageCollection() { public void garbageCollection() {
long time = System.currentTimeMillis(); if(limiter != null)
long max_timeout = Math.max(rateLimitMilis, TIMEOUT); limiter.garbageCollection();
map.entrySet().removeIf(e -> e.getValue().ts + max_timeout < time);
}
public boolean isLimit(String ip) {
if (excludeIps.contains(ip)) return false;
if (map.containsKey(ip)) {
AuthEntry rate = map.get(ip);
long currenttime = System.currentTimeMillis();
if (rate.ts + rateLimitMilis < currenttime) rate.value = 0;
if (rate.value >= rateLimit && rateLimit > 0) {
rate.value++;
rate.ts = currenttime;
return true;
}
rate.value++;
rate.ts = currenttime;
return false;
}
map.put(ip, new AuthEntry(1, System.currentTimeMillis()));
return false;
} }
@Override @Override
public void close() throws Exception { public void close() throws Exception {
srv.authHookManager.preHook.unregisterHook(prA); srv.authHookManager.preHook.unregisterHook(this::preAuthHook);
} }
} }

View file

@ -1,29 +1,26 @@
package pro.gravit.launchserver.components; package pro.gravit.launchserver.components;
import java.util.ArrayList;
import java.util.List;
import pro.gravit.launcher.NeedGarbageCollection; import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.manangers.hook.AuthHookManager; import pro.gravit.launchserver.manangers.hook.AuthHookManager;
import pro.gravit.utils.HookException; import pro.gravit.utils.HookException;
import pro.gravit.utils.HookSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class RegLimiterComponent extends Component implements NeedGarbageCollection, AutoCloseable { public class RegLimiterComponent extends Component implements NeedGarbageCollection, AutoCloseable {
public static final long TIMEOUT = 12 * 60 * 60 * 1000; //12 часов private transient AbstractLimiter<String> limiter;
public transient LaunchServer launchServer; public transient LaunchServer launchServer;
public int rateLimit; public int rateLimit;
public int rateLimitMilis; public int rateLimitMilis;
public String message; public String message;
public transient HookSet.Hook<AuthHookManager.RegContext> hook;
public transient HashMap<String, AuthLimiterComponent.AuthEntry> map = new HashMap<>();
public List<String> excludeIps = new ArrayList<>(); public List<String> excludeIps = new ArrayList<>();
@Override @Override
public void preInit(LaunchServer launchServer) { public void preInit(LaunchServer launchServer) {
limiter = new AbstractLimiter<>(rateLimit, rateLimitMilis);
this.launchServer = launchServer; this.launchServer = launchServer;
} }
@ -34,43 +31,24 @@ public void init(LaunchServer launchServer) {
@Override @Override
public void postInit(LaunchServer launchServer) { public void postInit(LaunchServer launchServer) {
launchServer.authHookManager.registraion.registerHook(context -> { launchServer.authHookManager.registraion.registerHook(this::registerHook);
if (isLimit(context.ip)) { }
public boolean registerHook(AuthHookManager.RegContext context)
{
if (!limiter.check(context.ip)) {
throw new HookException(message); throw new HookException(message);
} }
return false; return false;
});
} }
@Override @Override
public void garbageCollection() { public void garbageCollection() {
long time = System.currentTimeMillis(); if(limiter != null)
long max_timeout = Math.max(rateLimitMilis, TIMEOUT); limiter.garbageCollection();
map.entrySet().removeIf(e -> e.getValue().ts + max_timeout < time);
} }
public boolean isLimit(String ip) {
if (excludeIps.contains(ip)) return false;
if (map.containsKey(ip)) {
AuthLimiterComponent.AuthEntry rate = map.get(ip);
long currenttime = System.currentTimeMillis();
if (rate.ts + rateLimitMilis < currenttime) rate.value = 0;
if (rate.value >= rateLimit && rateLimit > 0) {
rate.value++;
rate.ts = currenttime;
return true;
}
rate.value++;
rate.ts = currenttime;
return false;
}
map.put(ip, new AuthLimiterComponent.AuthEntry(1, System.currentTimeMillis()));
return false;
}
@Override @Override
public void close() throws Exception { public void close() throws Exception {
if(hook != null) launchServer.authHookManager.registraion.unregisterHook(this::registerHook);
launchServer.authHookManager.registraion.unregisterHook(hook);
} }
} }

View file

@ -7,7 +7,16 @@
import java.util.Collection; import java.util.Collection;
import java.util.UUID; import java.util.UUID;
import javax.persistence.*; import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import pro.gravit.launcher.ClientPermissions; import pro.gravit.launcher.ClientPermissions;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;

View file

@ -1,10 +1,10 @@
package pro.gravit.launchserver.dao; package pro.gravit.launchserver.dao;
import pro.gravit.launcher.hwid.OshiHWID;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import pro.gravit.launcher.hwid.OshiHWID;
public interface UserDAO { public interface UserDAO {
User findById(int id); User findById(int id);
User findByUsername(String username); User findByUsername(String username);

View file

@ -1,11 +1,16 @@
package pro.gravit.launchserver.dao; package pro.gravit.launchserver.dao;
import java.util.function.Supplier;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import pro.gravit.launcher.hwid.HWID; import pro.gravit.launcher.hwid.HWID;
import pro.gravit.launcher.hwid.OshiHWID; import pro.gravit.launcher.hwid.OshiHWID;
import javax.persistence.*;
import java.util.function.Supplier;
@Entity @Entity
@Table(name = "users_hwids") @Table(name = "users_hwids")
public class UserHWID implements HWID { public class UserHWID implements HWID {

View file

@ -3,8 +3,6 @@
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import pro.gravit.launchserver.LaunchServer;
public class UserService { public class UserService {
private final UserDAO usersDao; private final UserDAO usersDao;

View file

@ -1,38 +1,18 @@
package pro.gravit.launchserver.manangers; package pro.gravit.launchserver.manangers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import java.io.ByteArrayInputStream;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.CertIOException;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ECKeyParameters;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcECContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.file.Path; import java.nio.file.Path;
import java.security.*; import java.security.InvalidAlgorithmParameterException;
import java.security.cert.CertificateException; import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.ECGenParameterSpec; import java.security.spec.ECGenParameterSpec;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
@ -41,16 +21,45 @@
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.Date; import java.util.Date;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcECContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.bouncycastle.util.io.pem.PemWriter;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.SecurityHelper;
public class CertificateManager { public class CertificateManager {
public X509CertificateHolder ca; public X509CertificateHolder ca;
public AsymmetricKeyParameter caKey; public AsymmetricKeyParameter caKey;
public X509CertificateHolder server;
public AsymmetricKeyParameter serverKey;
//public X509CertificateHolder server; //public X509CertificateHolder server;
//public AsymmetricKeyParameter serverKey; //public AsymmetricKeyParameter serverKey;
public int validDays = 60; public int validDays = 60;
public int minusHours = 6; public int minusHours = 6;
public String orgName;
public X509CertificateHolder generateCertificate(String subjectName, PublicKey subjectPublicKey) throws OperatorCreationException { public X509CertificateHolder generateCertificate(String subjectName, PublicKey subjectPublicKey) throws OperatorCreationException {
SubjectPublicKeyInfo subjectPubKeyInfo = SubjectPublicKeyInfo.getInstance(subjectPublicKey.getEncoded()); SubjectPublicKeyInfo subjectPubKeyInfo = SubjectPublicKeyInfo.getInstance(subjectPublicKey.getEncoded());
BigInteger serial = BigInteger.valueOf(SecurityHelper.newRandom().nextLong()); BigInteger serial = BigInteger.valueOf(SecurityHelper.newRandom().nextLong());
@ -59,6 +68,7 @@ public X509CertificateHolder generateCertificate(String subjectName, PublicKey s
X500NameBuilder subject = new X500NameBuilder(); X500NameBuilder subject = new X500NameBuilder();
subject.addRDN(BCStyle.CN, subjectName); subject.addRDN(BCStyle.CN, subjectName);
subject.addRDN(BCStyle.O, orgName);
X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(ca.getSubject(), serial, X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(ca.getSubject(), serial,
startDate, endDate, subject.build(), subjectPubKeyInfo); startDate, endDate, subject.build(), subjectPubKeyInfo);
@ -76,8 +86,12 @@ public void generateCA() throws NoSuchAlgorithmException, IOException, OperatorC
KeyPair pair = generator.generateKeyPair(); KeyPair pair = generator.generateKeyPair();
LocalDateTime startDate = LocalDate.now().atStartOfDay(); LocalDateTime startDate = LocalDate.now().atStartOfDay();
X500NameBuilder subject = new X500NameBuilder();
subject.addRDN(BCStyle.CN, orgName.concat(" CA"));
subject.addRDN(BCStyle.O, orgName);
X509v3CertificateBuilder builder= new X509v3CertificateBuilder( X509v3CertificateBuilder builder= new X509v3CertificateBuilder(
new X500Name("CN=ca"), subject.build(),
new BigInteger("0"), new BigInteger("0"),
Date.from(startDate.atZone(ZoneId.systemDefault()).toInstant()), Date.from(startDate.atZone(ZoneId.systemDefault()).toInstant()),
Date.from(startDate.plusDays(3650).atZone(ZoneId.systemDefault()).toInstant()), Date.from(startDate.plusDays(3650).atZone(ZoneId.systemDefault()).toInstant()),
@ -97,21 +111,65 @@ public KeyPair generateKeyPair() throws InvalidAlgorithmParameterException, NoSu
} }
public void writePrivateKey(Path file, PrivateKey privateKey) throws IOException { public void writePrivateKey(Path file, PrivateKey privateKey) throws IOException {
try (PemWriter writer = new PemWriter(IOHelper.newWriter(file))) { writePrivateKey(IOHelper.newWriter(file), privateKey);
writer.writeObject(new PemObject("PRIVATE KEY", privateKey.getEncoded())); }
public void writePrivateKey(Writer writer, PrivateKey privateKey) throws IOException {
try (PemWriter writer1 = new PemWriter(writer)) {
writer1.writeObject(new PemObject("PRIVATE KEY", privateKey.getEncoded()));
} }
} }
public void writePrivateKey(Path file, AsymmetricKeyParameter key) throws IOException { public void writePrivateKey(Path file, AsymmetricKeyParameter key) throws IOException {
writePrivateKey(IOHelper.newWriter(file), key);
}
public void writePrivateKey(Writer writer, AsymmetricKeyParameter key) throws IOException {
PrivateKeyInfo info = PrivateKeyInfoFactory.createPrivateKeyInfo(key); PrivateKeyInfo info = PrivateKeyInfoFactory.createPrivateKeyInfo(key);
try (PemWriter writer = new PemWriter(IOHelper.newWriter(file))) { try (PemWriter writer1 = new PemWriter(writer)) {
writer.writeObject(new PemObject("PRIVATE KEY", info.getEncoded())); writer1.writeObject(new PemObject("PRIVATE KEY", info.getEncoded()));
} }
} }
public void writeCertificate(Path file, X509CertificateHolder holder) throws IOException { public void writeCertificate(Path file, X509CertificateHolder holder) throws IOException {
try (PemWriter writer = new PemWriter(IOHelper.newWriter(file))) { writeCertificate(IOHelper.newWriter(file), holder);
writer.writeObject(new PemObject("CERTIFICATE", holder.toASN1Structure().getEncoded()));
} }
public void writeCertificate(Writer writer, X509CertificateHolder holder) throws IOException {
try (PemWriter writer1 = new PemWriter(writer)) {
writer1.writeObject(new PemObject("CERTIFICATE", holder.toASN1Structure().getEncoded()));
}
}
public AsymmetricKeyParameter readPrivateKey(Path file) throws IOException {
return readPrivateKey(IOHelper.newReader(file));
}
public AsymmetricKeyParameter readPrivateKey(Reader reader) throws IOException {
AsymmetricKeyParameter ret;
try(PemReader reader1 = new PemReader(reader))
{
byte[] bytes = reader1.readPemObject().getContent();
try(ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes))
{
ret = PrivateKeyFactory.createKey(inputStream);
}
}
return ret;
}
public X509CertificateHolder readCertificate(Path file) throws IOException {
return readCertificate(IOHelper.newReader(file));
}
public X509CertificateHolder readCertificate(Reader reader) throws IOException {
X509CertificateHolder ret;
try(PemReader reader1 = new PemReader(reader))
{
byte[] bytes = reader1.readPemObject().getContent();
ret = new X509CertificateHolder(bytes);
}
return ret;
} }
} }

View file

@ -2,14 +2,11 @@
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import pro.gravit.launcher.hasher.HashedEntry;
import pro.gravit.launcher.hasher.HashedEntryAdapter;
import pro.gravit.launcher.hwid.HWID; import pro.gravit.launcher.hwid.HWID;
import pro.gravit.launcher.hwid.HWIDProvider; import pro.gravit.launcher.hwid.HWIDProvider;
import pro.gravit.launcher.managers.GsonManager; import pro.gravit.launcher.managers.GsonManager;
import pro.gravit.launcher.request.JsonResultSerializeAdapter; import pro.gravit.launcher.request.JsonResultSerializeAdapter;
import pro.gravit.launcher.request.WebSocketEvent; import pro.gravit.launcher.request.WebSocketEvent;
import pro.gravit.launcher.request.websockets.ClientWebSocketService;
import pro.gravit.launchserver.auth.handler.AuthHandler; import pro.gravit.launchserver.auth.handler.AuthHandler;
import pro.gravit.launchserver.auth.hwid.HWIDHandler; import pro.gravit.launchserver.auth.hwid.HWIDHandler;
import pro.gravit.launchserver.auth.permissions.PermissionsHandler; import pro.gravit.launchserver.auth.permissions.PermissionsHandler;

View file

@ -2,13 +2,11 @@
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import pro.gravit.launchserver.Reconfigurable; import pro.gravit.launchserver.Reconfigurable;
import pro.gravit.utils.command.Command; import pro.gravit.utils.command.Command;
import pro.gravit.utils.command.CommandException; import pro.gravit.utils.command.CommandException;
import pro.gravit.utils.command.basic.HelpCommand; import pro.gravit.utils.command.basic.HelpCommand;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.VerifyHelper; import pro.gravit.utils.helper.VerifyHelper;
public class ReconfigurableManager { public class ReconfigurableManager {

View file

@ -1,7 +1,11 @@
package pro.gravit.launchserver.manangers; package pro.gravit.launchserver.manangers;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import pro.gravit.launcher.NeedGarbageCollection; import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
@ -10,53 +14,50 @@ public class SessionManager implements NeedGarbageCollection {
public static final long SESSION_TIMEOUT = 3 * 60 * 60 * 1000; // 3 часа public static final long SESSION_TIMEOUT = 3 * 60 * 60 * 1000; // 3 часа
public static final boolean GARBAGE_SERVER = Boolean.parseBoolean(System.getProperty("launcher.garbageSessionsServer", "false")); public static final boolean GARBAGE_SERVER = Boolean.parseBoolean(System.getProperty("launcher.garbageSessionsServer", "false"));
private HashSet<Client> clientSet = new HashSet<>(128); private Map<Long, Client> clientSet = new HashMap<>(128);
public boolean addClient(Client client) { public boolean addClient(Client client) {
clientSet.add(client); clientSet.put(client.session, client);
return true; return true;
} }
@Override @Override
public void garbageCollection() { public void garbageCollection() {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
clientSet.removeIf(c -> (c.timestamp + SESSION_TIMEOUT < time) && ((c.type == Client.Type.USER) || ((c.type == Client.Type.SERVER) && GARBAGE_SERVER))); clientSet.entrySet().removeIf(entry -> {
Client c = entry.getValue();
return (c.timestamp + SESSION_TIMEOUT < time) && ((c.type == Client.Type.USER) || ((c.type == Client.Type.SERVER) && GARBAGE_SERVER));
});
} }
public Client getClient(long session) { public Client getClient(long session) {
for (Client c : clientSet) return clientSet.get(session);
if (c.session == session) return c;
return null;
} }
public Client getOrNewClient(long session) { public Client getOrNewClient(long session) {
for (Client c : clientSet) return clientSet.computeIfAbsent(session, Client::new);
if (c.session == session) return c;
Client newClient = new Client(session);
clientSet.add(newClient);
return newClient;
} }
public void updateClient(long session) { public void updateClient(long session) {
for (Client c : clientSet) { Client c = clientSet.get(session);
if (c.session == session) { if (c != null) {
c.up(); c.up();
return; return;
} }
}
Client newClient = new Client(session); Client newClient = new Client(session);
clientSet.add(newClient); clientSet.put(session, newClient);
} }
public Set<Client> getSessions() { public Set<Client> getSessions() {
return clientSet; // TODO: removeme
return new HashSet<>(clientSet.values());
} }
public void loadSessions(Set<Client> set) { public void loadSessions(Set<Client> set) {
clientSet.addAll(set); clientSet.putAll(set.stream().collect(Collectors.toMap(c -> c.session, Function.identity())));
} }
} }

View file

@ -87,6 +87,14 @@ public void registerClientModuleClass(String clazz) {
MODULE_CLASS.add(clazz); MODULE_CLASS.add(clazz);
} }
public void unregisterClientModuleClass(String clazz) {
MODULE_CLASS.remove(clazz);
}
public void clearClientModuleClassList() {
MODULE_CLASS.clear();
}
public void registerIgnoredClass(String clazz) { public void registerIgnoredClass(String clazz) {
CLASS_BLACKLIST.add(clazz); CLASS_BLACKLIST.add(clazz);
} }

View file

@ -8,7 +8,6 @@
public class Client { public class Client {
public long session; public long session;
public boolean proxy;
public String auth_id; public String auth_id;
public long timestamp; public long timestamp;
public Type type; public Type type;

View file

@ -7,6 +7,7 @@
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.group.DefaultChannelGroup; import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpObjectAggregator;
@ -15,14 +16,11 @@
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.logging.LoggingHandler; import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.concurrent.GlobalEventExecutor;
import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.websockets.StandartClientWebSocketService;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.handlers.NettyIpForwardHandler; import pro.gravit.launchserver.socket.handlers.NettyIpForwardHandler;
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler; import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
import pro.gravit.launchserver.socket.handlers.fileserver.FileServerHandler; import pro.gravit.launchserver.socket.handlers.fileserver.FileServerHandler;
import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.BiHookSet;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
public class LauncherNettyServer implements AutoCloseable { public class LauncherNettyServer implements AutoCloseable {
@ -30,6 +28,7 @@ public class LauncherNettyServer implements AutoCloseable {
public final EventLoopGroup bossGroup; public final EventLoopGroup bossGroup;
public final EventLoopGroup workerGroup; public final EventLoopGroup workerGroup;
public final WebSocketService service; public final WebSocketService service;
public final BiHookSet<NettyConnectContext,SocketChannel> pipelineHook = new BiHookSet<>();
private static final String WEBSOCKET_PATH = "/api"; private static final String WEBSOCKET_PATH = "/api";
public LauncherNettyServer(LaunchServer server) { public LauncherNettyServer(LaunchServer server) {
@ -39,16 +38,16 @@ public LauncherNettyServer(LaunchServer server) {
{ {
LogHelper.debug("Netty: Epoll enabled"); LogHelper.debug("Netty: Epoll enabled");
} }
if(config.performance.usingEpoll && JVMHelper.OS_TYPE != JVMHelper.OS.LINUX) if(config.performance.usingEpoll && !Epoll.isAvailable())
{ {
LogHelper.error("netty,perfomance.usingEpoll work only Linux systems"); LogHelper.error("Epoll is not available: (netty,perfomance.usingEpoll configured wrongly)", Epoll.unavailabilityCause());
} }
bossGroup = NettyObjectFactory.newEventLoopGroup(config.performance.bossThread); bossGroup = NettyObjectFactory.newEventLoopGroup(config.performance.bossThread);
workerGroup = NettyObjectFactory.newEventLoopGroup(config.performance.workerThread); workerGroup = NettyObjectFactory.newEventLoopGroup(config.performance.workerThread);
serverBootstrap = new ServerBootstrap(); serverBootstrap = new ServerBootstrap();
service = new WebSocketService(new DefaultChannelGroup(GlobalEventExecutor.INSTANCE), server); service = new WebSocketService(new DefaultChannelGroup(GlobalEventExecutor.INSTANCE), server);
serverBootstrap.group(bossGroup, workerGroup) serverBootstrap.group(bossGroup, workerGroup)
.channel(NettyObjectFactory.getServerSocketChannelClass()) .channelFactory(NettyObjectFactory.getServerSocketChannelFactory())
.handler(new LoggingHandler(config.logLevel)) .handler(new LoggingHandler(config.logLevel))
.childHandler(new ChannelInitializer<SocketChannel>() { .childHandler(new ChannelInitializer<SocketChannel>() {
@Override @Override
@ -65,19 +64,9 @@ public void initChannel(SocketChannel ch) {
if (server.config.netty.fileServerEnabled) if (server.config.netty.fileServerEnabled)
pipeline.addLast(new FileServerHandler(server.updatesDir, true)); pipeline.addLast(new FileServerHandler(server.updatesDir, true));
pipeline.addLast(new WebSocketFrameHandler(context, server, service)); pipeline.addLast(new WebSocketFrameHandler(context, server, service));
pipelineHook.hook(context, ch);
} }
}); });
if (config.proxy != null && config.proxy.enabled) {
LogHelper.info("Connect to main server %s");
Request.service = StandartClientWebSocketService.initWebSockets(config.proxy.address, false);
AuthRequest authRequest = new AuthRequest(config.proxy.login, config.proxy.password, config.proxy.auth_id, AuthRequest.ConnectTypes.PROXY);
authRequest.initProxy = true;
try {
authRequest.request();
} catch (Exception e) {
LogHelper.error(e);
}
}
} }
public ChannelFuture bind(InetSocketAddress address) { public ChannelFuture bind(InetSocketAddress address) {

View file

@ -1,5 +1,6 @@
package pro.gravit.launchserver.socket; package pro.gravit.launchserver.socket;
import io.netty.channel.ChannelFactory;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel; import io.netty.channel.ServerChannel;
import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollEventLoopGroup;
@ -20,12 +21,12 @@ public static EventLoopGroup newEventLoopGroup(int threads)
else else
return new NioEventLoopGroup(threads); return new NioEventLoopGroup(threads);
} }
public static Class<? extends ServerChannel> getServerSocketChannelClass() public static ChannelFactory<? extends ServerChannel> getServerSocketChannelFactory()
{ {
if(epoll) if(epoll)
return EpollServerSocketChannel.class; return EpollServerSocketChannel::new;
else else
return NioServerSocketChannel.class; return NioServerSocketChannel::new;
} }
} }

View file

@ -2,8 +2,6 @@
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.HashMap; import java.util.HashMap;
import java.util.Random;
import java.util.UUID;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -11,23 +9,26 @@
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.ChannelMatchers;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import pro.gravit.launcher.Launcher; import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.events.ExceptionEvent; import pro.gravit.launcher.events.ExceptionEvent;
import pro.gravit.launcher.events.RequestEvent; import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.events.request.ErrorRequestEvent; import pro.gravit.launcher.events.request.ErrorRequestEvent;
import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.RequestException;
import pro.gravit.launcher.request.WebSocketEvent; import pro.gravit.launcher.request.WebSocketEvent;
import pro.gravit.launcher.request.admin.ProxyRequest;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.response.WebSocketServerResponse;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.WebSocketServerResponse;
import pro.gravit.launchserver.socket.response.admin.AddLogListenerResponse; import pro.gravit.launchserver.socket.response.admin.AddLogListenerResponse;
import pro.gravit.launchserver.socket.response.admin.ExecCommandResponse; import pro.gravit.launchserver.socket.response.admin.ExecCommandResponse;
import pro.gravit.launchserver.socket.response.admin.ProxyCommandResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.launchserver.socket.response.auth.*; import pro.gravit.launchserver.socket.response.auth.CheckServerResponse;
import pro.gravit.launchserver.socket.response.auth.GetAvailabilityAuthResponse;
import pro.gravit.launchserver.socket.response.auth.JoinServerResponse;
import pro.gravit.launchserver.socket.response.auth.ProfilesResponse;
import pro.gravit.launchserver.socket.response.auth.RegisterResponse;
import pro.gravit.launchserver.socket.response.auth.RestoreSessionResponse;
import pro.gravit.launchserver.socket.response.auth.SetProfileResponse;
import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername; import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername;
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse; import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
import pro.gravit.launchserver.socket.response.profile.ProfileByUsername; import pro.gravit.launchserver.socket.response.profile.ProfileByUsername;
@ -36,6 +37,7 @@
import pro.gravit.launchserver.socket.response.update.LauncherResponse; import pro.gravit.launchserver.socket.response.update.LauncherResponse;
import pro.gravit.launchserver.socket.response.update.UpdateListResponse; import pro.gravit.launchserver.socket.response.update.UpdateListResponse;
import pro.gravit.launchserver.socket.response.update.UpdateResponse; import pro.gravit.launchserver.socket.response.update.UpdateResponse;
import pro.gravit.utils.BiHookSet;
import pro.gravit.utils.ProviderMap; import pro.gravit.utils.ProviderMap;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
@ -44,6 +46,19 @@
public class WebSocketService { public class WebSocketService {
public final ChannelGroup channels; public final ChannelGroup channels;
public static ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>(); public static ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>();
public static class WebSocketRequestContext
{
public WebSocketServerResponse response;
public Client client;
public String ip;
public WebSocketRequestContext(WebSocketServerResponse response, Client client, String ip) {
this.response = response;
this.client = client;
this.ip = ip;
}
}
public final BiHookSet<WebSocketRequestContext, ChannelHandlerContext> hook = new BiHookSet<>();
public WebSocketService(ChannelGroup channels, LaunchServer server) { public WebSocketService(ChannelGroup channels, LaunchServer server) {
this.channels = channels; this.channels = channels;
@ -55,68 +70,20 @@ public WebSocketService(ChannelGroup channels, LaunchServer server) {
} }
private final LaunchServer server; private final LaunchServer server;
private static final HashMap<String, Class> responses = new HashMap<>();
private final Gson gson; private final Gson gson;
public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) { public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) {
String request = frame.text(); String request = frame.text();
WebSocketServerResponse response = gson.fromJson(request, WebSocketServerResponse.class); WebSocketServerResponse response = gson.fromJson(request, WebSocketServerResponse.class);
if (server.config.netty.proxy.enabled) {
if (server.config.netty.proxy.requests.contains(response.getType())) {
UUID origRequestUUID = null;
if (response instanceof SimpleResponse) {
SimpleResponse simpleResponse = (SimpleResponse) response;
simpleResponse.server = server;
simpleResponse.service = this;
simpleResponse.ctx = ctx;
if (ip != null) simpleResponse.ip = ip;
else simpleResponse.ip = IOHelper.getIP(ctx.channel().remoteAddress());
origRequestUUID = simpleResponse.requestUUID;
}
LogHelper.debug("Proxy %s request", response.getType());
if (client.session == 0) client.session = new Random().nextLong();
ProxyRequest proxyRequest = new ProxyRequest(response, client.session);
if (response instanceof SimpleResponse) {
((SimpleResponse) response).requestUUID = proxyRequest.requestUUID;
}
proxyRequest.isCheckSign = client.checkSign;
try {
WebSocketEvent result = proxyRequest.request();
if (result instanceof AuthRequestEvent) {
LogHelper.debug("Client auth params get successful");
AuthRequestEvent authRequestEvent = (AuthRequestEvent) result;
client.isAuth = true;
client.session = authRequestEvent.session;
if (authRequestEvent.playerProfile != null)
client.username = authRequestEvent.playerProfile.username;
}
if (result instanceof Request && response instanceof SimpleResponse) {
((Request) result).requestUUID = origRequestUUID;
}
sendObject(ctx, result);
} catch (RequestException e) {
sendObject(ctx, new ErrorRequestEvent(e.getMessage()));
} catch (Exception e) {
LogHelper.error(e);
RequestEvent event;
if (server.config.netty.sendExceptionEnabled) {
event = new ExceptionEvent(e);
} else {
event = new ErrorRequestEvent("Fatal server error. Contact administrator");
}
if (response instanceof SimpleResponse) {
event.requestUUID = ((SimpleResponse) response).requestUUID;
}
sendObject(ctx, event);
}
return;
}
}
process(ctx, response, client, ip); process(ctx, response, client, ip);
} }
void process(ChannelHandlerContext ctx, WebSocketServerResponse response, Client client, String ip) { void process(ChannelHandlerContext ctx, WebSocketServerResponse response, Client client, String ip) {
WebSocketRequestContext context = new WebSocketRequestContext(response, client, ip);
if(hook.hook(context, ctx))
{
return;
}
if (response instanceof SimpleResponse) { if (response instanceof SimpleResponse) {
SimpleResponse simpleResponse = (SimpleResponse) response; SimpleResponse simpleResponse = (SimpleResponse) response;
simpleResponse.server = server; simpleResponse.server = server;
@ -142,10 +109,6 @@ void process(ChannelHandlerContext ctx, WebSocketServerResponse response, Client
} }
} }
public Class getResponseClass(String type) {
return responses.get(type);
}
public void registerClient(Channel channel) { public void registerClient(Channel channel) {
channels.add(channel); channels.add(channel);
} }
@ -168,27 +131,26 @@ public static void registerResponses() {
providers.register("getSecureToken", GetSecureTokenResponse.class); providers.register("getSecureToken", GetSecureTokenResponse.class);
providers.register("verifySecureToken", VerifySecureTokenResponse.class); providers.register("verifySecureToken", VerifySecureTokenResponse.class);
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class); providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
providers.register("proxy", ProxyCommandResponse.class);
providers.register("register", RegisterResponse.class); providers.register("register", RegisterResponse.class);
} }
public void sendObject(ChannelHandlerContext ctx, Object obj) { public void sendObject(ChannelHandlerContext ctx, Object obj) {
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class))); ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class)), ctx.voidPromise());
} }
public void sendObject(ChannelHandlerContext ctx, Object obj, Type type) { public void sendObject(ChannelHandlerContext ctx, Object obj, Type type) {
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type))); ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type)), ctx.voidPromise());
} }
public void sendObjectAll(Object obj) { public void sendObjectAll(Object obj) {
for (Channel ch : channels) { for (Channel ch : channels) {
ch.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class))); ch.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class)), ch.voidPromise());
} }
} }
public void sendObjectAll(Object obj, Type type) { public void sendObjectAll(Object obj, Type type) {
for (Channel ch : channels) { for (Channel ch : channels) {
ch.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type))); ch.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type)), ch.voidPromise());
} }
} }
@ -201,7 +163,7 @@ public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj, Type type)
} }
public void sendEvent(EventResult obj) { public void sendEvent(EventResult obj) {
channels.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj))); channels.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj)), ChannelMatchers.all(), true);
} }
public static class EventResult implements WebSocketEvent { public static class EventResult implements WebSocketEvent {

View file

@ -36,7 +36,9 @@ protected void decode(ChannelHandlerContext ctx, HttpRequest msg, List<Object> o
realIP = headers.get("X-Real-IP"); realIP = headers.get("X-Real-IP");
} }
if (realIP != null) { if (realIP != null) {
if (LogHelper.isDevEnabled()) {
LogHelper.dev("Real IP address %s", realIP); LogHelper.dev("Real IP address %s", realIP);
}
context.ip = realIP; context.ip = realIP;
} else LogHelper.error("IpForwarding error. Headers not found"); } else LogHelper.error("IpForwarding error. Headers not found");
out.add(msg); out.add(msg);

View file

@ -23,7 +23,7 @@
import pro.gravit.launchserver.socket.LauncherNettyServer; import pro.gravit.launchserver.socket.LauncherNettyServer;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
@SuppressWarnings({"unused", "rawtypes"}) @SuppressWarnings("unused")
public final class NettyServerSocketHandler implements Runnable, AutoCloseable { public final class NettyServerSocketHandler implements Runnable, AutoCloseable {
private SSLServerSocketFactory ssf; private SSLServerSocketFactory ssf;

View file

@ -2,22 +2,18 @@
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import com.google.gson.GsonBuilder; import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.NettyConnectContext; import pro.gravit.launchserver.socket.NettyConnectContext;
import pro.gravit.launchserver.socket.WebSocketService; import pro.gravit.launchserver.socket.WebSocketService;
import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
@ -45,11 +41,14 @@ public void setClient(Client client) {
@Override @Override
public void channelActive(ChannelHandlerContext ctx) { public void channelActive(ChannelHandlerContext ctx) {
if (LogHelper.isDevEnabled()) {
LogHelper.dev("New client %s", IOHelper.getIP(ctx.channel().remoteAddress())); LogHelper.dev("New client %s", IOHelper.getIP(ctx.channel().remoteAddress()));
}
client = new Client(0); client = new Client(0);
service.registerClient(ctx.channel()); Channel ch = ctx.channel();
service.registerClient(ch);
ctx.executor().schedule(() -> { ctx.executor().schedule(() -> {
ctx.channel().writeAndFlush(new PingWebSocketFrame()); ch.writeAndFlush(new PingWebSocketFrame(), ch.voidPromise());
}, 30L, TimeUnit.SECONDS); }, 30L, TimeUnit.SECONDS);
} }

View file

@ -14,8 +14,8 @@
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException; import java.net.URI;
import java.net.URLDecoder; import java.net.URISyntaxException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -77,7 +77,15 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr
} }
final String uri = request.uri(); final String uri = request.uri();
final String path = sanitizeUri(uri); final String path;
try {
path = Paths.get(new URI(uri).getPath()).normalize().toString().substring(1);
} catch (URISyntaxException e) {
sendError(ctx, BAD_REQUEST);
return;
}
if (path == null) { if (path == null) {
sendError(ctx, FORBIDDEN); sendError(ctx, FORBIDDEN);
return; return;
@ -172,26 +180,6 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
} }
} }
private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
private static String sanitizeUri(String uri) {
// Decode the path.
try {
uri = URLDecoder.decode(uri, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
if (uri.isEmpty() || uri.charAt(0) != '/') {
return null;
}
// Convert file separators.
uri = uri.replace(File.separatorChar, '/');
return Paths.get(uri).normalize().toString().substring(1);
}
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*"); private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*");
private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath) { private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath) {

View file

@ -1,28 +0,0 @@
package pro.gravit.launchserver.socket.response.admin;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.WebSocketServerResponse;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class ProxyCommandResponse extends SimpleResponse {
public WebSocketServerResponse response;
public long session;
public boolean isCheckSign;
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if (!client.proxy) {
sendError("Proxy server error");
return;
}
Client real_client = server.sessionManager.getOrNewClient(session);
real_client.checkSign = isCheckSign;
response.execute(ctx, real_client);
}
@Override
public String getType() {
return "proxy";
}
}

View file

@ -45,12 +45,11 @@ public AuthResponse(String login, String password, String auth_id, OshiHWID hwid
} }
public String auth_id; public String auth_id;
public boolean initProxy;
public ConnectTypes authType; public ConnectTypes authType;
public HWID hwid; public HWID hwid;
public enum ConnectTypes { public enum ConnectTypes {
SERVER, CLIENT, BOT SERVER, CLIENT, API
} }
@Override @Override
@ -111,11 +110,9 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
clientData.username = login; clientData.username = login;
result.accessToken = aresult.accessToken; result.accessToken = aresult.accessToken;
result.permissions = clientData.permissions; result.permissions = clientData.permissions;
if (authType == ConnectTypes.BOT && !clientData.permissions.canBot) {
AuthProvider.authError("authType: BOT not allowed for this account");
}
if (authType == ConnectTypes.SERVER && !clientData.permissions.canServer) { if (authType == ConnectTypes.SERVER && !clientData.permissions.canServer) {
AuthProvider.authError("authType: SERVER not allowed for this account"); AuthProvider.authError("authType: SERVER not allowed for this account");
return;
} }
if (getSession) { if (getSession) {
if (clientData.session == 0) { if (clientData.session == 0) {
@ -124,15 +121,13 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
} }
result.session = clientData.session; result.session = clientData.session;
} }
if (initProxy) { if (authType != ConnectTypes.API && server.config.protectHandler.allowGetAccessToken(context)) {
if (!clientData.permissions.canProxy) throw new AuthException("initProxy not allow");
clientData.proxy = true;
}
if (server.config.protectHandler.allowGetAccessToken(context)) {
UUID uuid = pair.handler.auth(aresult); UUID uuid = pair.handler.auth(aresult);
result.playerProfile = ProfileByUUIDResponse.getProfile(server, uuid, aresult.username, client, clientData.auth.textureProvider); result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, aresult.username, client, clientData.auth.textureProvider);
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, uuid.toString()); LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, uuid.toString());
} }
}
sendResult(result); sendResult(result);
} catch (AuthException | HWIDException | HookException e) { } catch (AuthException | HWIDException | HookException e) {
sendError(e.getMessage()); sendError(e.getMessage());
@ -140,10 +135,10 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
} }
public static class AuthContext { public static class AuthContext {
public AuthContext(long session, String login, int password_lenght, String customText, String client, String hwid, String ip, ConnectTypes authType) { public AuthContext(long session, String login, int password_length, String customText, String client, String hwid, String ip, ConnectTypes authType) {
this.session = session; this.session = session;
this.login = login; this.login = login;
this.password_lenght = password_lenght; this.password_length = password_length;
this.customText = customText; this.customText = customText;
this.client = client; this.client = client;
this.hwid = hwid; this.hwid = hwid;
@ -153,7 +148,7 @@ public AuthContext(long session, String login, int password_lenght, String custo
public long session; public long session;
public String login; public String login;
public int password_lenght; //Use AuthProvider for get password public int password_length; //Use AuthProvider for get password
public String client; public String client;
public String hwid; public String hwid;
public String customText; public String customText;

View file

@ -26,8 +26,10 @@ public void execute(ChannelHandlerContext ctx, Client pClient) {
server.authHookManager.checkServerHook.hook(this, pClient); server.authHookManager.checkServerHook.hook(this, pClient);
result.uuid = pClient.auth.handler.checkServer(username, serverID); result.uuid = pClient.auth.handler.checkServer(username, serverID);
if (result.uuid != null) if (result.uuid != null)
result.playerProfile = ProfileByUUIDResponse.getProfile(server, result.uuid, username, client, pClient.auth.textureProvider); result.playerProfile = ProfileByUUIDResponse.getProfile(result.uuid, username, client, pClient.auth.textureProvider);
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("checkServer: %s uuid: %s serverID: %s", result.playerProfile.username, result.uuid.toString(), serverID); LogHelper.debug("checkServer: %s uuid: %s serverID: %s", result.playerProfile.username, result.uuid.toString(), serverID);
}
} catch (AuthException | HookException e) { } catch (AuthException | HookException e) {
sendError(e.getMessage()); sendError(e.getMessage());
return; return;

View file

@ -27,7 +27,9 @@ public void execute(ChannelHandlerContext ctx, Client client) {
LogHelper.warning("Client auth is null. Using default."); LogHelper.warning("Client auth is null. Using default.");
success = server.config.getAuthProviderPair().handler.joinServer(username, accessToken, serverID); success = server.config.getAuthProviderPair().handler.joinServer(username, accessToken, serverID);
} else success = client.auth.handler.joinServer(username, accessToken, serverID); } else success = client.auth.handler.joinServer(username, accessToken, serverID);
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("joinServer: %s accessToken: %s serverID: %s", username, accessToken, serverID); LogHelper.debug("joinServer: %s accessToken: %s serverID: %s", username, accessToken, serverID);
}
} catch (AuthException | HookException e) { } catch (AuthException | HookException e) {
sendError(e.getMessage()); sendError(e.getMessage());
return; return;

View file

@ -32,7 +32,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
LogHelper.warning("Client auth is null. Using default."); LogHelper.warning("Client auth is null. Using default.");
uuid = server.config.getAuthProviderPair().handler.usernameToUUID(list[i].username); uuid = server.config.getAuthProviderPair().handler.usernameToUUID(list[i].username);
} else uuid = client.auth.handler.usernameToUUID(list[i].username); } else uuid = client.auth.handler.usernameToUUID(list[i].username);
result.playerProfiles[i] = ProfileByUUIDResponse.getProfile(server, uuid, list[i].username, list[i].client, client.auth.textureProvider); result.playerProfiles[i] = ProfileByUUIDResponse.getProfile(uuid, list[i].username, list[i].client, client.auth.textureProvider);
} }
sendResult(result); sendResult(result);
} }

View file

@ -7,7 +7,7 @@
import pro.gravit.launcher.events.request.ProfileByUUIDRequestEvent; import pro.gravit.launcher.events.request.ProfileByUUIDRequestEvent;
import pro.gravit.launcher.profiles.PlayerProfile; import pro.gravit.launcher.profiles.PlayerProfile;
import pro.gravit.launcher.profiles.Texture; import pro.gravit.launcher.profiles.Texture;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.texture.TextureProvider; import pro.gravit.launchserver.auth.texture.TextureProvider;
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;
@ -17,7 +17,7 @@ public class ProfileByUUIDResponse extends SimpleResponse {
public UUID uuid; public UUID uuid;
public String client; public String client;
public static PlayerProfile getProfile(LaunchServer server, UUID uuid, String username, String client, TextureProvider textureProvider) { public static PlayerProfile getProfile(UUID uuid, String username, String client, TextureProvider textureProvider) {
// Get skin texture // Get skin texture
Texture skin; Texture skin;
try { try {
@ -48,10 +48,19 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
String username; String username;
AuthProviderPair pair;
if (client.auth == null) { if (client.auth == null) {
LogHelper.warning("Client auth is null. Using default."); LogHelper.warning("Client auth is null. Using default.");
username = server.config.getAuthProviderPair().handler.uuidToUsername(uuid); pair = server.config.getAuthProviderPair();
} else username = client.auth.handler.uuidToUsername(uuid); } else {
sendResult(new ProfileByUUIDRequestEvent(getProfile(server, uuid, username, this.client, client.auth.textureProvider))); pair = client.auth;
}
if(pair == null)
{
sendError("ProfileByUUIDResponse: AuthProviderPair is null");
return;
}
username = pair.handler.uuidToUsername(uuid);
sendResult(new ProfileByUUIDRequestEvent(getProfile(uuid, username, this.client, client.auth.textureProvider)));
} }
} }

View file

@ -24,6 +24,6 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
LogHelper.warning("Client auth is null. Using default."); LogHelper.warning("Client auth is null. Using default.");
uuid = server.config.getAuthProviderPair().handler.usernameToUUID(username); uuid = server.config.getAuthProviderPair().handler.usernameToUUID(username);
} else uuid = client.auth.handler.usernameToUUID(username); } else uuid = client.auth.handler.usernameToUUID(username);
sendResult(new ProfileByUsernameRequestEvent(ProfileByUUIDResponse.getProfile(server, uuid, username, this.client, client.auth.textureProvider))); sendResult(new ProfileByUsernameRequestEvent(ProfileByUUIDResponse.getProfile(uuid, username, this.client, client.auth.textureProvider)));
} }
} }

View file

@ -29,7 +29,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
bytes = digest; bytes = digest;
if (launcher_type == 1) // JAR if (launcher_type == 1) // JAR
{ {
byte[] hash = server.launcherBinary.getBytes().getDigest(); byte[] hash = server.launcherBinary.getDigest();
if (hash == null) service.sendObjectAndClose(ctx, new LauncherRequestEvent(true, server.config.netty.launcherURL)); if (hash == null) service.sendObjectAndClose(ctx, new LauncherRequestEvent(true, server.config.netty.launcherURL));
if (Arrays.equals(bytes, hash)) { if (Arrays.equals(bytes, hash)) {
client.checkSign = true; client.checkSign = true;
@ -39,7 +39,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
} }
} else if (launcher_type == 2) //EXE } else if (launcher_type == 2) //EXE
{ {
byte[] hash = server.launcherEXEBinary.getBytes().getDigest(); byte[] hash = server.launcherEXEBinary.getDigest();
if (hash == null) sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL)); if (hash == null) sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL));
if (Arrays.equals(bytes, hash)) { if (Arrays.equals(bytes, hash)) {
client.checkSign = true; client.checkSign = true;

View file

@ -6,7 +6,6 @@
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.launcher.hasher.HashedDir;
import pro.gravit.launcher.serialize.signed.SignedObjectHolder;
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;
@ -24,7 +23,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
return; return;
} }
HashSet<String> set = new HashSet<>(); HashSet<String> set = new HashSet<>();
for (Map.Entry<String, SignedObjectHolder<HashedDir>> entry : server.updatesDirMap.entrySet()) for (Map.Entry<String, HashedDir> entry : server.updatesDirMap.entrySet())
set.add(entry.getKey()); set.add(entry.getKey());
sendResult(new UpdateListRequestEvent(set)); sendResult(new UpdateListRequestEvent(set));
} }

View file

@ -5,7 +5,6 @@
import pro.gravit.launcher.events.request.UpdateRequestEvent; import pro.gravit.launcher.events.request.UpdateRequestEvent;
import pro.gravit.launcher.hasher.HashedDir; import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.serialize.signed.SignedObjectHolder;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
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;
@ -34,7 +33,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
} }
} }
} }
SignedObjectHolder<HashedDir> dir = server.updatesDirMap.get(dirName); HashedDir dir = server.updatesDirMap.get(dirName);
if (dir == null) { if (dir == null) {
service.sendObject(ctx, new ErrorRequestEvent(String.format("Directory %s not found", dirName))); service.sendObject(ctx, new ErrorRequestEvent(String.format("Directory %s not found", dirName)));
return; return;
@ -46,6 +45,6 @@ public void execute(ChannelHandlerContext ctx, Client client) {
url = bind.url; url = bind.url;
zip = bind.zip; zip = bind.zip;
} }
service.sendObject(ctx, new UpdateRequestEvent(dir.object, url, zip)); service.sendObject(ctx, new UpdateRequestEvent(dir, url, zip));
} }
} }

View file

@ -1,63 +0,0 @@
{
"port": 7240,
"address": "xx.xx",
"bindAddress": "0.0.0.0",
"projectName": "XXX",
"mirrors": [
"http://mirror.gravitlauncher.ml/"
],
"binaryName": "Launcher",
"env": "STD",
"authProvider": [
{
"message": "Настройте authProvider",
"type": "reject"
}
],
"authHandler": {
"type": "memory"
},
"permissionsHandler": {
"filename": "permissions.json",
"type": "json"
},
"textureProvider": {
"skinURL": "http://example.com/skins/%username%.png",
"cloakURL": "http://example.com/cloaks/%username%.png",
"type": "request"
},
"hwidHandler": {
"type": "accept"
},
"threadCount": 2,
"threadCoreCount": 0,
"launch4j": {
"enabled": false,
"productName": "GravitLauncher",
"productVer": "4.2.0.0",
"fileDesc": "GravitLauncher 4.2.0",
"fileVer": "4.2.0.0",
"internalName": "Launcher",
"copyright": "© GravitLauncher Team",
"trademarks": "This product is licensed under GPLv3",
"txtFileVersion": "%s, build %d",
"txtProductVersion": "%s, build %d"
},
"buildPostTransform": {
"enabled": false
},
"compress": false,
"authRateLimit": 0,
"authRateLimitMilis": 0,
"authRejectString": "Превышен лимит авторизаций",
"whitelistRejectString": "Вас нет в белом списке",
"genMappings": true,
"isUsingWrapper": false,
"isDownloadJava": false,
"isWarningMissArchJava": true,
"enabledProGuard": true,
"updatesNotify": true,
"stripLineNumbers": true,
"deleteTempFiles": true,
"startScript": ".\\start.sh"
}

View file

@ -1,21 +0,0 @@
StringEncryption:
Enabled: true
Mode: Normal
StringPool: false
InvokeDynamic: None
NumberObfuscation: Normal
FlowObfuscation: Normal
HideCode: false
Shuffler: true
Crasher: false
Optimizer:
Enabled: true
InlineGotoGoto: true
InlineGotoReturn: true
RemoveNopInstructions: true
Watermarker:
Enabled: false
Message: "This copy belongs to GravitLauncher"
Key: "SuperSecureKey"
Dictionary: Spaces
TrashClasses: 500

View file

@ -39,7 +39,6 @@
dependencies { dependencies {
pack project(':LauncherAuthlib') pack project(':LauncherAuthlib')
bundle 'com.github.oshi:oshi-core:3.13.0' bundle 'com.github.oshi:oshi-core:3.13.0'
bundle 'de.jensd:fontawesomefx:8.9'
bundle 'org.apache.httpcomponents:httpclient:4.5.7' bundle 'org.apache.httpcomponents:httpclient:4.5.7'
pack 'io.netty:netty-codec-http:4.1.36.Final' pack 'io.netty:netty-codec-http:4.1.36.Final'
pack 'org.ow2.asm:asm-tree:7.1' pack 'org.ow2.asm:asm-tree:7.1'
@ -58,3 +57,33 @@ task dumpLibs(type: Copy) {
build.dependsOn tasks.genRuntimeJS, tasks.dumpLibs, tasks.shadowJar build.dependsOn tasks.genRuntimeJS, tasks.dumpLibs, tasks.shadowJar
publishing {
publications {
launcherclientapi(MavenPublication) {
artifactId = 'launcher-client-api'
artifact jar
pom {
name = 'GravitLauncher Client API'
description = 'GravitLauncher Client Module API'
url = 'https://launcher.gravit.pro'
licenses {
license {
name = 'GNU General Public License, Version 3.0'
url = 'https://www.gnu.org/licenses/gpl-3.0.html'
}
}
scm {
connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git'
developerConnection = 'scm:git:ssh://git@github.com:GravitLauncher/Launcher.git'
url = 'https://launcher.gravit.pro/'
}
}
}
}
}
signing {
sign publishing.publications.launcherclientapi
}

View file

@ -1,27 +1,31 @@
// ====== LAUNCHER CONFIG ====== //
var config = { var config = {
dir: "GravitLauncher", // Launcher directory //*** Настройки лаунчера ***//
title: "GravitLauncher", // Window title dir: "GravitLauncher", // Название папки лаунчера
icons: [ "favicon.png" ], // Window icon paths title: "GravitLauncher", // Заголовок окна
icons: ["favicon.png"], // Путь/Пути до иконки окна
// Auth config //*** Меню авторизации ***//
linkText: "GravitLauncher", // Text for link under "Auth" button linkText: "GravitLauncher", // Текст ссылки
linkURL: new java.net.URL("https://gravitlauncher.ml"), // URL for link under "Auth" button linkURL: new java.net.URL("https://gravit.pro"), // Ссылка
// Menu config //*** Меню выбора серверов ***//
discord: new java.net.URL("https://discord.gg/aJK6nMN"), discord: new java.net.URL("https://discord.gg/aJK6nMN"), // Ссылка
// Settings defaults //*** Стандартные настройки клиента ***//
settingsMagic: 0xC0DE5, // Magic, don't touch autoEnterDefault: false, // Автоматический вход на выбранный сервер
autoEnterDefault: false, // Should autoEnter be enabled by default? fullScreenDefault: false, // Клиент в полный экран
fullScreenDefault: false, // Should fullScreen be enabled by default? featureStoreDefault: true, // Поиск файлов в других клиентах (Используется для экономии трафика и ускорения загрузки)
ramDefault: 1024, // Default RAM amount (0 for auto) ramDefault: 1024, // Количество оперативной памяти выделенной по умолчанию (0 - Автоматически)
//*** Настройка загрузки JVM ***//
/* LaunchServer: guardtype = java */
jvm: { jvm: {
enable: false, enable: false, // Включение загрузки своей JVM
jvmMustdie32Dir: "jre-8u202-win32", jvmMustdie32Dir: "jre-8u211-win32", // Название папки JVM для Windows x32
jvmMustdie64Dir: "jre-8u202-win64", jvmMustdie64Dir: "jre-8u211-win64", // Название папки JVM для Windows x64
} },
settingsMagic: 0xC0DE5, // Магия вне хогвартса
}; };
DirBridge.dir = DirBridge.getLauncherDir(config.dir); DirBridge.dir = DirBridge.getLauncherDir(config.dir);

View file

@ -24,9 +24,9 @@ function initLauncher() {
/* ======== init Login window======== */ /* ======== init Login window======== */
function initLoginScene() { function initLoginScene() {
loginPane.setOnMousePressed(function(event){ movePoint = new javafx.geometry.Point2D(event.getSceneX(), event.getSceneY())}); loginPane.setOnMousePressed(function(event) { movePoint = new javafx.geometry.Point2D(event.getSceneX(), event.getSceneY()) });
loginPane.setOnMouseDragged(function(event) { loginPane.setOnMouseDragged(function(event) {
if(movePoint === null) { if (movePoint === null) {
return; return;
} }
stage.setX(event.getScreenX() - movePoint.getX()); stage.setX(event.getScreenX() - movePoint.getX());
@ -35,9 +35,9 @@ function initLoginScene() {
var pane = loginPane.lookup("#bar"); var pane = loginPane.lookup("#bar");
bar = pane; bar = pane;
loginPane.lookup("#close").setOnAction(function(event){ javafx.application.Platform.exit()}); loginPane.lookup("#close").setOnAction(function(event) { javafx.application.Platform.exit() });
loginPane.lookup("#hide").setOnAction(function(event){ stage.setIconified(true)}); loginPane.lookup("#hide").setOnAction(function(event) { stage.setIconified(true) });
loginPane.lookup("#discord").setOnAction(function(){ openURL(config.discord); }); loginPane.lookup("#discord").setOnAction(function() { openURL(config.discord); });
var pane = loginPane.lookup("#authPane"); var pane = loginPane.lookup("#authPane");
authPane = pane; authPane = pane;
@ -46,14 +46,14 @@ function initLoginScene() {
loginPaneLayout = loginLayout; loginPaneLayout = loginLayout;
loginField = pane.lookup("#login"); loginField = pane.lookup("#login");
loginField.setOnMouseMoved(function(event){rootPane.fireEvent(event)}); loginField.setOnMouseMoved(function(event) { rootPane.fireEvent(event) });
loginField.setOnAction(goAuth); loginField.setOnAction(goAuth);
if (settings.login !== null) { if (settings.login !== null) {
loginField.setText(settings.login); loginField.setText(settings.login);
} }
passwordField = pane.lookup("#password"); passwordField = pane.lookup("#password");
passwordField.setOnMouseMoved(function(event){rootPane.fireEvent(event)}); passwordField.setOnMouseMoved(function(event) { rootPane.fireEvent(event) });
passwordField.setOnAction(goAuth); passwordField.setOnAction(goAuth);
if (settings.rsaPassword !== null) { if (settings.rsaPassword !== null) {
passwordField.getStyleClass().add("hasSaved"); passwordField.getStyleClass().add("hasSaved");
@ -74,9 +74,9 @@ function initLoginScene() {
/* ======== init Menu window======== */ /* ======== init Menu window======== */
function initMenuScene() { function initMenuScene() {
menuPane.setOnMousePressed(function(event){ movePoint = new javafx.geometry.Point2D(event.getSceneX(), event.getSceneY())}); menuPane.setOnMousePressed(function(event) { movePoint = new javafx.geometry.Point2D(event.getSceneX(), event.getSceneY()) });
menuPane.setOnMouseDragged(function(event) { menuPane.setOnMouseDragged(function(event) {
if(movePoint === null) { if (movePoint === null) {
return; return;
} }
@ -86,9 +86,9 @@ function initMenuScene() {
var pane = menuPane.lookup("#bar"); var pane = menuPane.lookup("#bar");
bar = pane; bar = pane;
pane.lookup("#close").setOnAction(function(event){ javafx.application.Platform.exit()}); pane.lookup("#close").setOnAction(function(event) { javafx.application.Platform.exit() });
pane.lookup("#hide").setOnAction(function(event){ stage.setIconified(true)}); pane.lookup("#hide").setOnAction(function(event) { stage.setIconified(true) });
pane.lookup("#discord").setOnAction(function(){ openURL(config.discord); }); pane.lookup("#discord").setOnAction(function() { openURL(config.discord); });
pane.lookup("#settings").setOnAction(goSettings); pane.lookup("#settings").setOnAction(goSettings);
pane.lookup("#goConsole").setOnAction(goConsole); pane.lookup("#goConsole").setOnAction(goConsole);
@ -105,10 +105,10 @@ function initMenuScene() {
serverEntrance = pane.lookup("#serverentrance"); serverEntrance = pane.lookup("#serverentrance");
serverStatus = serverEntrance.lookup("#serverStatus"); serverStatus = serverEntrance.lookup("#serverStatus");
serverLabel = serverEntrance.lookup("#serverLabel"); serverLabel = serverEntrance.lookup("#serverLabel");
serverEntrance.lookup("#clientLaunch").setOnAction(function(){ serverEntrance.lookup("#clientLaunch").setOnAction(function() {
doUpdate(profilesList[serverHolder.old], loginData.pp, loginData.accessToken); doUpdate(profilesList[serverHolder.old], loginData.pp, loginData.accessToken);
}); });
pane.lookup("#logout").setOnAction(function(){ pane.lookup("#logout").setOnAction(function() {
setCurrentScene(loginScene); setCurrentScene(loginScene);
}); });
@ -116,9 +116,9 @@ function initMenuScene() {
/* ======== init Console window======== */ /* ======== init Console window======== */
function initConsoleScene() { function initConsoleScene() {
consoleMenu.setOnMousePressed(function(event){ movePoint = new javafx.geometry.Point2D(event.getSceneX(), event.getSceneY())}); consoleMenu.setOnMousePressed(function(event) { movePoint = new javafx.geometry.Point2D(event.getSceneX(), event.getSceneY()) });
consoleMenu.setOnMouseDragged(function(event) { consoleMenu.setOnMouseDragged(function(event) {
if(movePoint === null) { if (movePoint === null) {
return; return;
} }
@ -128,13 +128,13 @@ function initConsoleScene() {
var pane = consoleMenu.lookup("#bar"); var pane = consoleMenu.lookup("#bar");
consoleBar = pane; consoleBar = pane;
pane.lookup("#close").setOnAction(function(){ pane.lookup("#close").setOnAction(function() {
consoleStage.hide(); consoleStage.hide();
}); });
var text = consoleMenu.lookup("#textField"); var text = consoleMenu.lookup("#textField");
var output = consoleMenu.lookup("#output"); var output = consoleMenu.lookup("#output");
var appendFunction = function(line) javafx.application.Platform.runLater(function() output.appendText(line)); var appendFunction = function(line) javafx.application.Platform.runLater(function() output.appendText(line));
consoleMenu.lookup("#send").setOnAction(function(){ consoleMenu.lookup("#send").setOnAction(function() {
execCommand(text.getText()); execCommand(text.getText());
if (text.getText() == "clear") { if (text.getText() == "clear") {
output.setText(""); output.setText("");
@ -142,7 +142,7 @@ function initConsoleScene() {
text.setText(""); text.setText("");
}); });
FunctionalBridge.addPlainOutput(function(string) { FunctionalBridge.addPlainOutput(function(string) {
appendFunction(string+"\n"); appendFunction(string + "\n");
}) })
pane.lookup("#hide").setOnAction(function(event) { consoleStage.setIconified(true) }); pane.lookup("#hide").setOnAction(function(event) { consoleStage.setIconified(true) });
@ -153,9 +153,9 @@ function initConsoleScene() {
/* ======== init Options window======== */ /* ======== init Options window======== */
function initOptionsScene() { function initOptionsScene() {
optionsMenu.setOnMousePressed(function(event){ movePoint = new javafx.geometry.Point2D(event.getSceneX(), event.getSceneY())}); optionsMenu.setOnMousePressed(function(event) { movePoint = new javafx.geometry.Point2D(event.getSceneX(), event.getSceneY()) });
optionsMenu.setOnMouseDragged(function(event) { optionsMenu.setOnMouseDragged(function(event) {
if(movePoint === null) { if (movePoint === null) {
return; return;
} }
@ -165,9 +165,9 @@ function initOptionsScene() {
var pane = optionsMenu.lookup("#bar"); var pane = optionsMenu.lookup("#bar");
bar = pane; bar = pane;
pane.lookup("#close").setOnAction(function(event){ javafx.application.Platform.exit()}); pane.lookup("#close").setOnAction(function(event) { javafx.application.Platform.exit() });
pane.lookup("#hide").setOnAction(function(event){ stage.setIconified(true)}); pane.lookup("#hide").setOnAction(function(event) { stage.setIconified(true) });
pane.lookup("#back").setOnAction(function(){ pane.lookup("#back").setOnAction(function() {
setCurrentScene(menuScene); setCurrentScene(menuScene);
}); });
} }
@ -222,9 +222,9 @@ function goAuth(event) {
settings.login = login; settings.login = login;
doAuth(login, rsaPassword, authTypes[auth]); doAuth(login, rsaPassword, authTypes[auth]);
} }
/* ======== Console ======== */ /* ======== Console ======== */
function goConsole(event) { function goConsole(event) {
setConsoleCurrentScene(consoleScene); setConsoleCurrentScene(consoleScene);
} }
@ -267,7 +267,7 @@ function verifyLauncher(e) {
authOptions.getSelectionModel().select(0); authOptions.getSelectionModel().select(0);
var sm = authOptions.getSelectionModel().selectedIndexProperty(); var sm = authOptions.getSelectionModel().selectedIndexProperty();
sm.addListener(new javafx.beans.value.ChangeListener({ sm.addListener(new javafx.beans.value.ChangeListener({
changed: function (observableValue, oldSelection, newSelection) { changed: function(observableValue, oldSelection, newSelection) {
settings.auth = authTypes[authOptions.getSelectionModel().getSelectedItem()]; settings.auth = authTypes[authOptions.getSelectionModel().getSelectedItem()];
} }
})); }));
@ -287,14 +287,18 @@ function verifyLauncher(e) {
function doAuth(login, rsaPassword, auth_type) { function doAuth(login, rsaPassword, auth_type) {
processing.resetOverlay(); processing.resetOverlay();
overlay.show(processing.overlay, function (event) { overlay.show(processing.overlay, function(event) {
FunctionalBridge.getHWID.join(); FunctionalBridge.getHWID.join();
makeAuthRequest(login, rsaPassword, auth_type, function (result) { makeAuthRequest(login, rsaPassword, auth_type, function(result) {
FunctionalBridge.setAuthParams(result); FunctionalBridge.setAuthParams(result);
loginData = { pp: result.playerProfile , accessToken: result.accessToken, permissions: result.permissions, loginData = {
auth_type: settings.auth}; pp: result.playerProfile,
accessToken: result.accessToken,
permissions: result.permissions,
auth_type: settings.auth
};
overlay.hide(0, function () { overlay.hide(0, function() {
setCurrentScene(menuScene); setCurrentScene(menuScene);
}); });
return result; return result;
@ -355,10 +359,8 @@ function doUpdate(profile, pp, accessToken) {
function doLaunchClient(assetDir, assetHDir, clientDir, clientHDir, profile, pp, accessToken) { function doLaunchClient(assetDir, assetHDir, clientDir, clientHDir, profile, pp, accessToken) {
processing.resetOverlay(); processing.resetOverlay();
overlay.swap(0, processing.overlay, function(event) overlay.swap(0, processing.overlay, function(event) launchClient(assetHDir, clientHDir, profile, new ClientLauncherParams(settings.lastDigest,
launchClient(assetHDir, clientHDir, profile, new ClientLauncherParams(settings.lastDigest, assetDir, clientDir, pp, accessToken, settings.autoEnter, settings.fullScreen, settings.ram, 0, 0), doDebugClient));
assetDir, clientDir, pp, accessToken, settings.autoEnter, settings.fullScreen, settings.ram, 0, 0), doDebugClient)
);
} }
function doDebugClient(process) { function doDebugClient(process) {
@ -398,10 +400,10 @@ function updateProfilesList(profiles) {
if (profile.getOptional() != null) profile.updateOptionalGraph(); if (profile.getOptional() != null) profile.updateOptionalGraph();
index++; index++;
}); });
LogHelper.debug("Load selected %d profile",settings.profile); LogHelper.debug("Load selected %d profile", settings.profile);
if(profiles.length > 0) { if (profiles.length > 0) {
if(settings.profile >= profiles.length) if (settings.profile >= profiles.length)
settings.profile = profiles.length-1; settings.profile = profiles.length - 1;
serverHolder.set(serverList.getChildren().get(settings.profile)); serverHolder.set(serverList.getChildren().get(settings.profile));
} }
} }
@ -412,11 +414,11 @@ function pingServer(btn) {
var task = newTask(function() pingers[profile].ping()); var task = newTask(function() pingers[profile].ping());
task.setOnSucceeded(function(event) { task.setOnSucceeded(function(event) {
var result = task.getValue(); var result = task.getValue();
if(btn==serverHolder.old){ if (btn == serverHolder.old) {
setServerStatus(java.lang.String.format("%d из %d", result.onlinePlayers, result.maxPlayers)); setServerStatus(java.lang.String.format("%d из %d", result.onlinePlayers, result.maxPlayers));
} }
}); });
task.setOnFailed(function(event){ if(btn==serverHolder.old){setServerStatus("Недоступен")}}); task.setOnFailed(function(event) { if (btn == serverHolder.old) { setServerStatus("Недоступен") } });
startTask(task); startTask(task);
} }
@ -482,7 +484,7 @@ var overlay = {
fade(overlay.current, delay, 1.0, 0.0, function(event) { fade(overlay.current, delay, 1.0, 0.0, function(event) {
dimPane.requestFocus(); dimPane.requestFocus();
if(overlay.current==null){ if (overlay.current == null) {
overlay.show(newOverlay, onFinished); overlay.show(newOverlay, onFinished);
return; return;
} }
@ -504,13 +506,13 @@ var overlay = {
var serverHolder = { var serverHolder = {
old: null, old: null,
set: function(btn){ set: function(btn) {
pingServer(btn); pingServer(btn);
serverLabel.setText(profilesList[btn]); serverLabel.setText(profilesList[btn]);
serverDescription.setText(profilesList[btn].info); serverDescription.setText(profilesList[btn].info);
btn.setSelected(true); btn.setSelected(true);
btn.setDisable(true); btn.setDisable(true);
if(serverHolder.old!=null){ if (serverHolder.old != null) {
serverHolder.old.setSelected(false); serverHolder.old.setSelected(false);
serverHolder.old.setDisable(false); serverHolder.old.setDisable(false);
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Offline-режим</title>
</head>
<body style="color:red">
<h2>Offline-режим</h2>
Лаунчер запущен в Offline-режиме. В этом режиме Вы можете запустить любой ранее загруженный клиент
с любым именем пользователя, при этом вход на серверы с авторизацией, а так же система скинов и плащей <b>может не
работать</b>.
Скорее всего, проблема вызвана сбоем на сервере или неполадками в интернет-подключении.
Проверьте состояние интернет-подключения или обратитесь к администратору сервера.
</body>
</html>

View file

@ -4,55 +4,54 @@ Button {
} }
/* Outputs */ /* Outputs */
#overlay > #output, #overlay>#output,
#background > #output { #background>#output {
-fx-background-color: white; -fx-background-color: white;
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-font-family: monospace; -fx-font-family: monospace;
-fx-font-size: 8pt; -fx-font-size: 8pt;
} }
#overlay > #output .content, #overlay>#output .content,
#background > #output .content { #background>#output .content {
-fx-background-color: white; -fx-background-color: white;
-fx-background-radius: 0; -fx-background-radius: 0;
} }
/* Close button */ /* Close button */
#overlay > #copy, #overlay>#copy,
#overlay > #action.close { #overlay>#action.close {
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-text-fill: white; -fx-text-fill: white;
-fx-background-position: center; -fx-background-position: center;
-jfx-button-type: FLAT;
-fx-background-color: #2d83ce; -fx-background-color: #2d83ce;
-fx-pref-width: 150px; -fx-pref-width: 150px;
-fx-pref-height: 25px; -fx-pref-height: 25px;
} }
#overlay > #copy:hover, #overlay>#copy:hover,
#overlay > #copy:focused, #overlay>#copy:focused,
#overlay > #action.close:hover, #overlay>#action.close:hover,
#overlay > #action.close:focused, #overlay>#action.close:focused,
#overlay > #copy:pressed, #overlay>#copy:pressed,
#overlay > #action.close:pressed { #overlay>#action.close:pressed {
-fx-background-color: #1568ce; -fx-background-color: #1568ce;
} }
/* Kill button */ /* Kill button */
#overlay > #action.kill { #overlay>#action.kill {
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-text-fill: white; -fx-text-fill: white;
-fx-background-position: center; -fx-background-position: center;
-jfx-button-type: FLAT;
-fx-background-color: #CE5757; -fx-background-color: #CE5757;
-fx-pref-width: 150px; -fx-pref-width: 150px;
-fx-pref-height: 25px; -fx-pref-height: 25px;
} }
#overlay > #action.kill:hover, #overlay>#action.kill:hover,
#overlay > #action.kill:focused, #overlay>#action.kill:focused,
#overlay > #action.kill:pressed { #overlay>#action.kill:pressed {
-fx-background-color: #DB5252; -fx-background-color: #DB5252;
} }
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/

View file

@ -1,27 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextArea?>
<?import java.net.URL?> <?import java.net.URL?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="overlay" prefHeight="450.0" prefWidth="693.0" xmlns="http://javafx.com/javafx/8.0.201" <Pane fx:id="overlay" prefHeight="450.0" prefWidth="693.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
xmlns:fx="http://javafx.com/fxml/1">
<stylesheets> <stylesheets>
<URL value="@debug.css"/> <URL value="@debug.css" />
<URL value="@../../styles.css"/> <URL value="@../../styles.css" />
</stylesheets> </stylesheets>
<!-- Debug controls --> <!-- Debug controls -->
<TextArea fx:id="output" prefHeight="450.0" prefWidth="693.0"> <TextArea fx:id="output" prefHeight="405.0" prefWidth="693.0">
<padding> <padding>
<Insets left="10.0" right="10.0"/> <Insets left="10.0" right="10.0" />
</padding> </padding>
</TextArea> </TextArea>
<Button fx:id="copy" defaultButton="true" layoutX="373.0" layoutY="415.0" prefHeight="30.0" prefWidth="100.0" <Pane layoutY="405.0" prefHeight="45.0" prefWidth="693.0" style="-fx-background-color: #ffffff;" />
text="Копировать"/> <Button fx:id="copy" defaultButton="true" layoutX="373.0" layoutY="415.0" prefHeight="30.0" prefWidth="100.0" text="Копировать" />
<Button fx:id="action" layoutX="533.0" layoutY="415.0" prefHeight="25.0" prefWidth="150.0" text="Убить"/> <Button fx:id="action" layoutX="533.0" layoutY="415.0" prefHeight="25.0" prefWidth="150.0" text="Убить" />
<Label fx:id="version" layoutX="14.0" layoutY="419.0" text="GravitLauncher" />
</Pane> </Pane>

View file

@ -1,9 +1,21 @@
var debug = { var debug = {
overlay: null, output: null, action: null, process: null, overlay: null,
output: null,
action: null,
process: null,
initOverlay: function() { initOverlay: function() {
debug.overlay = loadFXML("dialog/overlay/debug/debug.fxml"); debug.overlay = loadFXML("dialog/overlay/debug/debug.fxml");
debug.overlay.lookup("#version").setText(
java.lang.String.format(
"%s | Java %s x%s",
FunctionalBridge.getLauncherVersion(),
java.lang.System.getProperty("java.version"),
JVMHelper.JVM_BITS
)
);
debug.output = debug.overlay.lookup("#output"); debug.output = debug.overlay.lookup("#output");
debug.output.setEditable(false); debug.output.setEditable(false);
@ -38,8 +50,7 @@ var debug = {
append: function(text) { append: function(text) {
//Experimental Feature //Experimental Feature
if(debug.output.getText().length() > 32000 /* Max length */) if (debug.output.getText().length() > 32000 /* Max length */ ) {
{
debug.output.deleteText(0, text.length()); debug.output.deleteText(0, text.length());
} }
debug.output.appendText(text); debug.output.appendText(text);

View file

@ -1,11 +1,12 @@
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/
#overlay > #description { #overlay>#description {
-fx-font-size: 12pt; -fx-font-size: 12pt;
-fx-text-fill: #fff; -fx-text-fill: #fff;
-fx-wrap-text: true; -fx-wrap-text: true;
} }
#overlay > #description.error { #overlay>#description.error {
-fx-text-fill: red; -fx-text-fill: red;
} }
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/

View file

@ -1,6 +1,9 @@
var processing = { var processing = {
overlay: null, spinner: null, description: null, overlay: null,
processingImage: null, errorImage: null, spinner: null,
description: null,
processingImage: null,
errorImage: null,
initOverlay: function() { initOverlay: function() {
processing.overlay = loadFXML("dialog/overlay/processing/processing.fxml"); processing.overlay = loadFXML("dialog/overlay/processing/processing.fxml");
@ -69,6 +72,7 @@ function makeLauncherRequest(callback) {
task.updateMessage("Обновление лаунчера"); task.updateMessage("Обновление лаунчера");
startTask(task); startTask(task);
} }
function makeProfilesRequest(callback) { function makeProfilesRequest(callback) {
var task = newRequestTask(new ProfilesRequest()); var task = newRequestTask(new ProfilesRequest());
@ -83,6 +87,7 @@ function makeProfilesRequest(callback) {
task.updateMessage("Обновление профилей"); task.updateMessage("Обновление профилей");
startTask(task); startTask(task);
} }
function makeAuthAvailabilityRequest(callback) { function makeAuthAvailabilityRequest(callback) {
var task = newRequestTask(new GetAvailabilityAuthRequest()); var task = newRequestTask(new GetAvailabilityAuthRequest());
@ -98,6 +103,7 @@ function makeAuthAvailabilityRequest(callback) {
task.updateMessage("Обновление способов авторизации"); task.updateMessage("Обновление способов авторизации");
startTask(task); startTask(task);
} }
function makeSetProfileRequest(profile, callback) { function makeSetProfileRequest(profile, callback) {
var task = newRequestTask(new SetProfileRequest(profile)); var task = newRequestTask(new SetProfileRequest(profile));

View file

@ -1,14 +1,19 @@
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/
#holder { #holder {
-fx-background-color: #fff; -fx-background-color: #fff;
} }
#holder > #transferDialog {
#holder>#transferDialog {
-fx-background-color: rgba(0, 0, 0, 0.5); -fx-background-color: rgba(0, 0, 0, 0.5);
-fx-pref-width: 694px; -fx-pref-width: 694px;
-fx-pref-height: 425px; -fx-pref-height: 425px;
} }
/* Labels */ /* Labels */
#holder > #settingsTitle {
#holder>#settingsTitle {
-fx-font-size: 14pt; -fx-font-size: 14pt;
-fx-alignment: baseline-center; -fx-alignment: baseline-center;
} }
@ -19,66 +24,77 @@ #holder #dirChange {
-fx-font-weight: bold; -fx-font-weight: bold;
} }
/* RAM slider */ /* RAM slider */
#holder > #ramSlider > .track {
#holder>#ramSlider>.track {
-fx-background-color: #909090; -fx-background-color: #909090;
} }
#holder > #ramSlider > .thumb { #holder>#ramSlider>.thumb {
-fx-background-color: #5fd97a; -fx-background-color: #5fd97a;
} }
#holder > #ramSlider > .colored-track { #holder>#ramSlider>.colored-track {
-fx-background-color: #5fd97a; -fx-background-color: #5fd97a;
} }
#holder > #ramSlider > .animated-thumb { #holder>#ramSlider>.animated-thumb {
-fx-background-color: #5fd97a; -fx-background-color: #5fd97a;
} }
#holder > #ramSlider > .slider-value { #holder>#ramSlider>.slider-value {
-fx-fill: white; -fx-fill: white;
-fx-stroke: white; -fx-stroke: white;
} }
/* Dir options */ /* Dir options */
#holder > #deleteDir, #cancelTransfer {
#holder>#deleteDir,
#cancelTransfer {
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-text-fill: white; -fx-text-fill: white;
-fx-background-position: center; -fx-background-position: center;
-jfx-button-type: FLAT;
-fx-background-color: #CE5757; -fx-background-color: #CE5757;
-fx-pref-width: 150px; -fx-pref-width: 150px;
-fx-pref-height: 25px; -fx-pref-height: 25px;
} }
#holder > #deleteDir:hover,#cancelTransfer:hover, #holder>#deleteDir:hover,
#holder > #deleteDir:focused,#cancelTransfer:focused { #cancelTransfer:hover,
#holder>#deleteDir:focused,
#cancelTransfer:focused {
-fx-background-color: #DB5252; -fx-background-color: #DB5252;
} }
#holder > #changeDir { #holder>#changeDir {
-fx-background-color: transparent; -fx-background-color: transparent;
-fx-text-fill: #909090; -fx-text-fill: #909090;
-fx-background-radius: 0; -fx-background-radius: 0;
} }
#holder > #changeDir:focused, #holder>#changeDir:focused,
#holder > #changeDir:pressed { #holder>#changeDir:pressed {
-fx-font-weight: bold; -fx-font-weight: bold;
} }
#holder > #apply,#applyTransfer{ #holder>#apply,
#applyTransfer {
-fx-background-color: #5fd97a; -fx-background-color: #5fd97a;
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-text-fill: white; -fx-text-fill: white;
-fx-background-position: center; -fx-background-position: center;
-jfx-button-type: FLAT;
-fx-pref-width: 150px; -fx-pref-width: 150px;
-fx-pref-height: 25px; -fx-pref-height: 25px;
} }
#holder > #apply:hover,#applyTransfer:hover,
#holder > #apply:focused,#applyTransfer:focused{ #holder>#apply:hover,
#applyTransfer:hover,
#holder>#apply:focused,
#applyTransfer:focused {
-fx-background-color: #75e18c; -fx-background-color: #75e18c;
} }
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/

View file

@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import java.net.URL?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?> <?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Slider?>
<?import java.net.URL?>
<?import javafx.scene.control.Hyperlink?> <?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<?import javafx.scene.shape.Line?> <?import javafx.scene.shape.Line?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
@ -13,16 +14,22 @@
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="overlay" prefHeight="450.0" prefWidth="693.0" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1"> <Pane fx:id="overlay" prefHeight="450.0" prefWidth="693.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<Pane id="holder" prefHeight="450.0" prefWidth="694.0"> <Pane id="holder" prefHeight="450.0" prefWidth="694.0">
<children> <children>
<CheckBox fx:id="autoEnter" layoutX="14.0" layoutY="137.0" text="Автовход на сервер" /> <CheckBox fx:id="autoEnter" layoutX="28.0" layoutY="169.0" text="Автовход на сервер">
<Text fill="#8c8c8c" layoutX="40.0" layoutY="153.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Включение авто-входа означает что вы сразу после загрузки клиента попадете на сервер" wrappingWidth="636.9999872148037" y="15.0" /> <font>
<CheckBox fx:id="fullScreen" layoutX="13.0" layoutY="244.0" text="Клиент в полный экран" /> <Font size="13.0" />
<Text fill="#8c8c8c" layoutX="40.0" layoutY="261.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Включение данной функции позволяет запустить игру сразу в полноэкранном режиме" wrappingWidth="636.9999872148037" y="15.0" /> </font></CheckBox>
<CheckBox id="debug" layoutX="13.0" layoutY="183.0" text="Режим Отладки" /> <CheckBox fx:id="fullScreen" layoutX="28.0" layoutY="229.0" text="Клиент в полный экран">
<Text fill="#8c8c8c" layoutX="40.0" layoutY="198.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Режим отладки позволяет просмотреть лог запуска и работы программы в реальном времени прямо из лаунчера, что упрощает поиск нужной информации" wrappingWidth="637.0000016447157" y="15.0" /> <font>
<Font size="13.0" />
</font></CheckBox>
<CheckBox id="debug" layoutX="28.0" layoutY="199.0" text="Режим Отладки">
<font>
<Font size="13.0" />
</font></CheckBox>
<TextFlow layoutX="126.0" layoutY="15.0" prefHeight="16.0" prefWidth="112.0"> <TextFlow layoutX="126.0" layoutY="15.0" prefHeight="16.0" prefWidth="112.0">
<Text fx:id="ramLabel" /> <Text fx:id="ramLabel" />
</TextFlow> </TextFlow>
@ -44,8 +51,20 @@
</children> </children>
</Pane> </Pane>
<Line endX="594.0" layoutX="100.0" layoutY="420.0" startX="-100.0" stroke="#5b3636" styleClass="lineHead" /> <Line endX="594.0" layoutX="100.0" layoutY="420.0" startX="-100.0" stroke="#5b3636" styleClass="lineHead" />
<CheckBox fx:id="featureStore" layoutX="13.0" layoutY="292.0" text="Поиск файлов в других клиентах" /> <CheckBox fx:id="featureStore" layoutX="28.0" layoutY="259.0" text="Поиск файлов в других клиентах">
<Text fill="#8c8c8c" layoutX="40.0" layoutY="309.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Используется для экономии вашего трафика, аналогичные файлы будут скопированы с других игровых клиентов" wrappingWidth="636.9999872148037" y="15.0" /> <font>
<Font size="13.0" />
</font></CheckBox>
<Text fx:id="description" layoutX="317.0" layoutY="208.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Режим отладки позволяет просмотреть лог запуска и работы программы в реальном времени прямо из лаунчера, что упрощает поиск нужной информации" wrappingWidth="260.888671875">
<font>
<Font size="13.0" />
</font>
</Text>
<Label fx:id="descLabel" layoutX="317.0" layoutY="169.0" text="Режим отладки">
<font>
<Font name="System Bold" size="14.0" />
</font>
</Label>
</children> </children>
</Pane> </Pane>
</children> </children>

View file

@ -1,7 +1,13 @@
var settingsOverlay = { var settingsOverlay = {
/* ===================== OVERLAY ===================== */ /* ===================== OVERLAY ===================== */
overlay: null, ramLabel: null, dirLabel: null, transferDialog: null, overlay: null,
deleteDirPressedAgain: false, count: 0, ramLabel: null,
dirLabel: null,
transferDialog: null,
deleteDirPressedAgain: false,
count: 0,
descLabel: null,
description: null,
initOverlay: function() { initOverlay: function() {
settingsOverlay.overlay = loadFXML("dialog/overlay/settings/settings.fxml"); settingsOverlay.overlay = loadFXML("dialog/overlay/settings/settings.fxml");
@ -12,10 +18,12 @@ var settingsOverlay = {
autoEnterBox.setSelected(settings.autoEnter); autoEnterBox.setSelected(settings.autoEnter);
autoEnterBox.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"]( autoEnterBox.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"](
function(o, ov, nv) settings.autoEnter = nv); function(o, ov, nv) settings.autoEnter = nv);
autoEnterBox.setOnMouseEntered(function() {
settingsOverlay.updateDesc(autoEnterBox.getText(), "Включение авто-входа означает что вы сразу после загрузки клиента попадете на сервер");
});
settingsOverlay.dirLabel = holder.lookup("#dirLabel"); settingsOverlay.dirLabel = holder.lookup("#dirLabel");
settingsOverlay.dirLabel.setOnAction(function(event) settingsOverlay.dirLabel.setOnAction(function(event) app.getHostServices().showDocument(settings.updatesDir.toUri()));
app.getHostServices().showDocument(settings.updatesDir.toUri()));
settingsOverlay.updateDirLabel(); settingsOverlay.updateDirLabel();
settingsOverlay.transferDialog = holder.lookup("#transferDialog"); settingsOverlay.transferDialog = holder.lookup("#transferDialog");
@ -33,15 +41,24 @@ var settingsOverlay = {
} }
}); });
this.descLabel = holder.lookup("#descLabel");
this.description = holder.lookup("#description");
var featureStore = holder.lookup("#featureStore"); var featureStore = holder.lookup("#featureStore");
featureStore.setSelected(settings.featureStore); featureStore.setSelected(settings.featureStore);
featureStore.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"]( featureStore.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"](
function(o, ov, nv) settings.featureStore = nv); function(o, ov, nv) settings.featureStore = nv);
featureStore.setOnMouseEntered(function() {
settingsOverlay.updateDesc(featureStore.getText(), "Используется для экономии вашего трафика, аналогичные файлы будут скопированы с других игровых клиентов");
});
var fullScreenBox = holder.lookup("#fullScreen"); var fullScreenBox = holder.lookup("#fullScreen");
fullScreenBox.setSelected(settings.fullScreen); fullScreenBox.setSelected(settings.fullScreen);
fullScreenBox.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"]( fullScreenBox.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"](
function(o, ov, nv) settings.fullScreen = nv); function(o, ov, nv) settings.fullScreen = nv);
fullScreenBox.setOnMouseEntered(function() {
settingsOverlay.updateDesc(fullScreenBox.getText(), "Включение данной функции позволяет запустить игру сразу в полноэкранном режиме");
});
settingsOverlay.ramLabel = holder.lookup("#ramLabel"); settingsOverlay.ramLabel = holder.lookup("#ramLabel");
settingsOverlay.updateRAMLabel(); settingsOverlay.updateRAMLabel();
@ -70,16 +87,16 @@ var settingsOverlay = {
settingsOverlay.deleteUpdatesDir(); settingsOverlay.deleteUpdatesDir();
settingsOverlay.deleteDirPressedAgain = false; settingsOverlay.deleteDirPressedAgain = false;
settingsOverlay.count = settingsOverlay.count+1; settingsOverlay.count = settingsOverlay.count + 1;
if(settingsOverlay.count>9){ if (settingsOverlay.count > 9) {
javafx.application.Platform.exit(); javafx.application.Platform.exit();
} }
deleteDirButton.setText( deleteDirButton.setText(
settingsOverlay.count>8?"Прощай :(": settingsOverlay.count > 8 ? "Прощай :(" :
(settingsOverlay.count>7?"Я умираю!": (settingsOverlay.count > 7 ? "Я умираю!" :
(settingsOverlay.count>5?"DeathCry, спаси!": (settingsOverlay.count > 5 ? "DeathCry, спаси!" :
(settingsOverlay.count>4?"Умоляю, перестань!": (settingsOverlay.count > 4 ? "Умоляю, перестань!" :
(settingsOverlay.count>3?"Да хорош уже!":"Ещё раз") (settingsOverlay.count > 3 ? "Да хорош уже!" : "Ещё раз")
)))); ))));
}); });
@ -87,14 +104,25 @@ var settingsOverlay = {
debugBox.setSelected(settings.debug); debugBox.setSelected(settings.debug);
debugBox.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"]( debugBox.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"](
function(o, ov, nv) settings.debug = nv); function(o, ov, nv) settings.debug = nv);
debugBox.setOnMouseEntered(function() {
settingsOverlay.updateDesc(debugBox.getText(), "Режим отладки позволяет просмотреть лог запуска и работы программы в реальном времени прямо из лаунчера, что упрощает поиск нужной информации");
});
holder.lookup("#apply").setOnAction(function(event) overlay.hide(0, null)); holder.lookup("#apply").setOnAction(function(event) overlay.hide(0, null));
}, },
updateDesc: function(label, desc) {
//На случай если человек решил избавится от этой фишки
if (this.descLabel == null) return;
if (this.description == null) return;
this.descLabel.setText(label);
this.description.setText(desc);
},
transferCatalogDialog: function(newDir) { transferCatalogDialog: function(newDir) {
settingsOverlay.transferDialog.setVisible(true); settingsOverlay.transferDialog.setVisible(true);
settingsOverlay.transferDialog.lookup("#cancelTransfer").setOnAction(function(event) settingsOverlay.transferDialog.lookup("#cancelTransfer").setOnAction(function(event) {
{
settings.updatesDir = newDir; settings.updatesDir = newDir;
DirBridge.dirUpdates = settings.updatesDir; DirBridge.dirUpdates = settings.updatesDir;
settingsOverlay.updateDirLabel(); settingsOverlay.updateDirLabel();
@ -133,9 +161,9 @@ var settingsOverlay = {
setRAM: function(ram) { setRAM: function(ram) {
if (ram>762&&ram<1024){ if (ram > 762 && ram < 1024) {
settings.ram = java.lang.Math["min(int,int)"](ram, FunctionalBridge.getJVMTotalMemory()); settings.ram = java.lang.Math["min(int,int)"](ram, FunctionalBridge.getJVMTotalMemory());
}else{ } else {
settings.ram = java.lang.Math["min(int,int)"](((ram / 256) | 0) * 256, FunctionalBridge.getJVMTotalMemory()); settings.ram = java.lang.Math["min(int,int)"](((ram / 256) | 0) * 256, FunctionalBridge.getJVMTotalMemory());
} }
}, },
@ -148,9 +176,16 @@ LogHelper.debug("Dir: %s", DirBridge.dir);
/* ====================== CLI PARAMS ===================== */ /* ====================== CLI PARAMS ===================== */
var cliParams = { var cliParams = {
login: null, password: null, profile: -1, autoLogin: false, login: null,
updatesDir: null, autoEnter: null, fullScreen: null, ram: -1, password: null,
offline: false, featureStore: null, profile: -1,
autoLogin: false,
updatesDir: null,
autoEnter: null,
fullScreen: null,
ram: -1,
offline: false,
featureStore: null,
init: function(params) { init: function(params) {
var named = params.getNamed(); var named = params.getNamed();
@ -200,8 +235,7 @@ var cliParams = {
if (cliParams.profile >= 0) { if (cliParams.profile >= 0) {
settings.profile = cliParams.profile; settings.profile = cliParams.profile;
} }
if (cliParams.updatesDir !== null) { if (cliParams.updatesDir !== null) {}
}
if (cliParams.autoEnter !== null) { if (cliParams.autoEnter !== null) {
settings.autoLogin = cliParams.autoEnter; settings.autoLogin = cliParams.autoEnter;
} }

View file

@ -6,16 +6,16 @@ #overlay {
-fx-background-image: url('../../images/downloader/blured.jpg'); -fx-background-image: url('../../images/downloader/blured.jpg');
} }
#overlay > #utitle { #overlay>#utitle {
-fx-alignment: top-left; -fx-alignment: top-left;
} }
#overlay > #description { #overlay>#description {
-fx-alignment: top-left; -fx-alignment: top-left;
-fx-wrap-text: true; -fx-wrap-text: true;
} }
#overlay > #description.error { #overlay>#description.error {
-fx-text-fill: #CE5757; -fx-text-fill: #CE5757;
} }
@ -29,15 +29,15 @@ .progress-bar {
-fx-background-color: transparent; -fx-background-color: transparent;
} }
.progress-indicator{ .progress-indicator {
-fx-background-color: transparent ; -fx-background-color: transparent;
} }
.progress-indicator .indicator { .progress-indicator .indicator {
-fx-background-color: transparent ; -fx-background-color: transparent;
} }
.progress-bar > .bar { .progress-bar>.bar {
-fx-background-image: url("../../images/downloader/line.png"); -fx-background-image: url("../../images/downloader/line.png");
-fx-background-color: transparent; -fx-background-color: transparent;
-fx-background-insets: 0; -fx-background-insets: 0;
@ -45,9 +45,10 @@ .progress-bar > .bar {
-fx-padding: 0; -fx-padding: 0;
} }
.progress-bar > .track { .progress-bar>.track {
-fx-background-color: transparent; -fx-background-color: transparent;
-fx-background-insets: 0; -fx-background-insets: 0;
-fx-background-radius: 3px; -fx-background-radius: 3px;
} }
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/

View file

@ -1,5 +1,8 @@
var update = { var update = {
overlay: null, title: null, description: null, progress: null, overlay: null,
title: null,
description: null,
progress: null,
initOverlay: function() { initOverlay: function() {
update.overlay = loadFXML("dialog/overlay/update/update.fxml"); update.overlay = loadFXML("dialog/overlay/update/update.fxml");
@ -83,7 +86,7 @@ function offlineUpdateRequest(dirName, dir, matcher, digest) {
/* Export functions */ /* Export functions */
function makeUpdateRequest(dirName, dir, matcher, digest, callback) { function makeUpdateRequest(dirName, dir, matcher, digest, callback) {
var request = settings.offline ? { setStateCallback: function(stateCallback) { } } : var request = settings.offline ? { setStateCallback: function(stateCallback) {} } :
new UpdateRequest(dirName, dir, matcher, digest); new UpdateRequest(dirName, dir, matcher, digest);
var task = settings.offline ? newTask(offlineUpdateRequest(dirName, dir, matcher, digest)) : var task = settings.offline ? newTask(offlineUpdateRequest(dirName, dir, matcher, digest)) :
newRequestTask(request); newRequestTask(request);

View file

@ -1,54 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import java.net.URL?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextArea?> <?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?> <?import javafx.scene.control.TextField?>
<?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?> <?import javafx.scene.image.Image?>
<?import java.net.URL?> <?import javafx.scene.image.ImageView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<?import javafx.scene.shape.Line?> <?import javafx.scene.shape.Line?>
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="background" prefWidth="738.0" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1"> <Pane fx:id="background" prefWidth="738.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<Pane fx:id="bar" layoutX="692.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar"> <Pane fx:id="bar" layoutX="692.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
<children> <children>
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" <Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER"/> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/hide.png" />
<Button id="close" alignment="CENTER" contentDisplay="CENTER" text="" </image>
textAlignment="CENTER"> </ImageView>
</graphic></Button>
<Button id="close" alignment="CENTER" contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER"/> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/close.png" />
</image>
</ImageView>
</graphic></Button>
</children> </children>
</Pane> </Pane>
<TextArea fx:id="output" prefHeight="419.0" prefWidth="692.0"> <TextArea fx:id="output" prefHeight="419.0" prefWidth="692.0">
<padding> <padding>
<Insets left="10.0" top="10.0"/> <Insets left="10.0" top="10.0" />
</padding> </padding>
</TextArea> </TextArea>
<TextField fx:id="textField" layoutY="420.0" prefHeight="30.0" prefWidth="543.0" <TextField fx:id="textField" layoutY="420.0" prefHeight="30.0" prefWidth="543.0" promptText="Введите команду...">
promptText="Введите команду...">
<opaqueInsets> <opaqueInsets>
<Insets/> <Insets />
</opaqueInsets> </opaqueInsets>
<padding> <padding>
<Insets left="10.0"/> <Insets left="10.0" />
</padding> </padding>
</TextField> </TextField>
<Button fx:id="send" defaultButton="true" layoutX="542.0" layoutY="420.0" prefHeight="30.0" prefWidth="147.0" <Button fx:id="send" defaultButton="true" layoutX="542.0" layoutY="420.0" prefHeight="30.0" prefWidth="147.0" text="Выполнить" />
text="Выполнить"/> <Line endX="594.0" layoutX="98.0" layoutY="420.0" startX="-100.0" stroke="#5b3636" styleClass="lineHead" />
<Line endX="594.0" layoutX="98.0" layoutY="420.0" startX="-100.0" stroke="#5b3636" styleClass="lineHead"/>
</children> </children>
<stylesheets> <stylesheets>
<URL value="@../../styles.css"/> <URL value="@../../styles.css" />
<URL value="@../../overlay/debug/debug.css"/> <URL value="@../../overlay/debug/debug.css" />
</stylesheets> </stylesheets>
</Pane> </Pane>

View file

@ -1,19 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import java.net.URL?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?> <?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.ComboBox?> <?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.PasswordField?> <?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?> <?import javafx.scene.control.TextField?>
<?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?> <?import javafx.scene.image.Image?>
<?import java.lang.String?> <?import javafx.scene.image.ImageView?>
<?import java.net.URL?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="loginPane" prefWidth="740.0" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1"> <Pane fx:id="loginPane" prefWidth="740.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<Pane fx:id="layout" prefWidth="740.0"> <Pane fx:id="layout" prefWidth="740.0">
<children> <children>
@ -40,21 +41,30 @@
<Pane id="mask" opacity="0.0" prefHeight="450.0" prefWidth="694.0" visible="false" /> <Pane id="mask" opacity="0.0" prefHeight="450.0" prefWidth="694.0" visible="false" />
<Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar"> <Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
<children> <children>
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" text="" textAlignment="CENTER"> <Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/hide.png" />
<Button id="close" alignment="CENTER" contentDisplay="CENTER" text="" textAlignment="CENTER"> </image>
</ImageView>
</graphic></Button>
<Button id="close" alignment="CENTER" contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/close.png" />
<Button id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="370.0" text="" textAlignment="CENTER"> </image>
</ImageView>
</graphic></Button>
<Button id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="370.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="#5fd97a" glyphName="MESSAGE_TEXT" size="20" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/discord.png" />
</image>
</ImageView>
</graphic></Button>
</children> </children>
</Pane> </Pane>
</children> </children>

View file

@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?>
<?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?>
<?import java.net.URL?> <?import java.net.URL?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?> <?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.FlowPane?> <?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="serverPaneLayout" maxHeight="-1.0" maxWidth="-1.0" prefWidth="740.0" visible="true" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1"> <Pane fx:id="serverPaneLayout" maxHeight="-1.0" maxWidth="-1.0" prefWidth="740.0" visible="true" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<Pane fx:id="layout" maxHeight="-1.0" maxWidth="-1.0" prefWidth="740.0" visible="true" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1"> <Pane fx:id="layout" maxHeight="-1.0" maxWidth="-1.0" prefWidth="740.0" visible="true" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
@ -51,9 +51,13 @@
<Font size="22.0" /> <Font size="22.0" />
</font> </font>
</Button> </Button>
<Button id="clientSettings" alignment="CENTER" centerShape="false" contentDisplay="CENTER" layoutX="305.0" layoutY="380.0" prefHeight="51.0" prefWidth="60.0" styleClass="clientSettings" text="" textAlignment="CENTER"> <Button id="clientSettings" alignment="CENTER" centerShape="false" contentDisplay="CENTER" layoutX="305.0" layoutY="380.0" minHeight="-Infinity" minWidth="-Infinity" prefHeight="51.0" prefWidth="60.0" styleClass="clientSettings" text="" textAlignment="CENTER">
<graphic> <graphic>
<FontAwesomeIconView fill="WHITE" glyphName="SLIDERS" size="30.0" /> <ImageView>
<image>
<Image url="@../../images/icons/list.png" />
</image>
</ImageView>
</graphic></Button> </graphic></Button>
<Label id="serverStatus" alignment="TOP_RIGHT" contentDisplay="RIGHT" layoutX="165.0" layoutY="12.0" prefHeight="25.0" prefWidth="97.0" text="12/100" textAlignment="RIGHT" textFill="WHITE"> <Label id="serverStatus" alignment="TOP_RIGHT" contentDisplay="RIGHT" layoutX="165.0" layoutY="12.0" prefHeight="25.0" prefWidth="97.0" text="12/100" textAlignment="RIGHT" textFill="WHITE">
<font> <font>
@ -74,31 +78,46 @@
</Pane> </Pane>
<Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar"> <Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
<children> <children>
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" text="" textAlignment="CENTER"> <Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/hide.png" />
<Button id="close" alignment="CENTER" contentDisplay="CENTER" text="" textAlignment="CENTER"> </image>
</ImageView>
</graphic></Button>
<Button id="close" alignment="CENTER" contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/close.png" />
<Button id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="380.0" text="" textAlignment="CENTER"> </image>
</ImageView>
</graphic></Button>
<Button id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="380.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="#5fd97a" glyphName="MESSAGE_TEXT" size="20" smooth="false" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/discord.png" />
<Button id="settings" alignment="CENTER" contentDisplay="CENTER" layoutY="90.0" text="" textAlignment="CENTER"> </image>
</ImageView>
</graphic></Button>
<Button id="settings" alignment="CENTER" contentDisplay="CENTER" layoutY="90.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="SETTINGS" size="20" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/settings.png" />
<Button id="goConsole" alignment="CENTER" contentDisplay="CENTER" layoutY="138.0" text="" textAlignment="CENTER"> </image>
</ImageView>
</graphic></Button>
<Button id="goConsole" alignment="CENTER" contentDisplay="CENTER" layoutY="138.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="CONSOLE" size="20" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/console.png" />
</image>
</ImageView>
</graphic></Button>
</children> </children>
</Pane> </Pane>
<Pane id="mask" opacity="0.0" prefHeight="450.0" prefWidth="694.0" visible="false" /> <Pane id="mask" opacity="0.0" prefHeight="450.0" prefWidth="694.0" visible="false" />

View file

@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?>
<?import java.net.URL?> <?import java.net.URL?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ScrollPane?> <?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Line?> <?import javafx.scene.shape.Line?>
@ -13,7 +14,7 @@
<!-- DrLeonardo Design --> <!-- DrLeonardo Design -->
<Pane fx:id="background" prefHeight="450.0" prefWidth="740.0" xmlns="http://javafx.com/javafx/8.0.201" xmlns:fx="http://javafx.com/fxml/1"> <Pane fx:id="background" prefHeight="450.0" prefWidth="740.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<Pane id="optionsPane" prefHeight="450.0" prefWidth="692.0" styleClass="optionsPane"> <Pane id="optionsPane" prefHeight="450.0" prefWidth="692.0" styleClass="optionsPane">
<children> <children>
@ -38,21 +39,30 @@
</Pane> </Pane>
<Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar"> <Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
<children> <children>
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" text="" textAlignment="CENTER"> <Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/hide.png" />
<Button id="close" alignment="CENTER" contentDisplay="CENTER" text="" textAlignment="CENTER"> </image>
</ImageView>
</graphic></Button>
<Button id="close" alignment="CENTER" contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/close.png" />
<Button id="back" alignment="CENTER" contentDisplay="CENTER" layoutY="405.0" text="" textAlignment="CENTER"> </image>
</ImageView>
</graphic></Button>
<Button id="back" alignment="CENTER" contentDisplay="CENTER" layoutY="405.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
<graphic> <graphic>
<MaterialDesignIconView fill="WHITE" glyphName="CHEVRON_LEFT" size="30" textAlignment="CENTER" /> <ImageView>
</graphic> <image>
</Button> <Image url="@../../images/icons/back.png" />
</image>
</ImageView>
</graphic></Button>
</children> </children>
</Pane> </Pane>
</children> </children>

View file

@ -5,7 +5,7 @@ var options = {
LogHelper.debug("Loading options file"); LogHelper.debug("Loading options file");
try { try {
tryWithResources(new HInput(IOHelper.newInput(options.file)), options.read); tryWithResources(new HInput(IOHelper.newInput(options.file)), options.read);
} catch(e) { } catch (e) {
LogHelper.error(e); LogHelper.error(e);
} }
}, },
@ -14,7 +14,7 @@ var options = {
LogHelper.debug("Saving options file"); LogHelper.debug("Saving options file");
try { try {
tryWithResources(new HOutput(IOHelper.newOutput(options.file)), options.write); tryWithResources(new HOutput(IOHelper.newOutput(options.file)), options.write);
} catch(e) { } catch (e) {
LogHelper.error(e); LogHelper.error(e);
} }
}, },
@ -25,32 +25,26 @@ var options = {
throw new java.io.IOException("options magic mismatch: " + java.lang.Integer.toString(magic, 16)); throw new java.io.IOException("options magic mismatch: " + java.lang.Integer.toString(magic, 16));
} }
var profilesCount = input.readInt(); var profilesCount = input.readInt();
LogHelper.debug("Load options. ProfilesCount %d",profilesCount); LogHelper.debug("Load options. ProfilesCount %d", profilesCount);
for(var i = 0;i<profilesCount;i++) for (var i = 0; i < profilesCount; i++) {
{
var listSize = input.readInt(); var listSize = input.readInt();
var sortIndex = input.readInt(); var sortIndex = input.readInt();
var profile = null; var profile = null;
settings.lastProfiles.forEach(function(hprofile,i,arr) { settings.lastProfiles.forEach(function(hprofile, i, arr) {
if(hprofile.getSortIndex() == sortIndex) if (hprofile.getSortIndex() == sortIndex) {
{
profile = hprofile; profile = hprofile;
} }
}); });
for(var j = 0; j < listSize; j++) for (var j = 0; j < listSize; j++) {
{
var mark = input.readBoolean(); var mark = input.readBoolean();
var modType = OptionalFile.readType(input); var modType = OptionalFile.readType(input);
var modFile = input.readString(0); var modFile = input.readString(0);
if(mark) if (mark) {
{ profile.markOptional(modFile, modType);
profile.markOptional(modFile,modType); LogHelper.debug("Load options %s marked", modFile);
LogHelper.debug("Load options %s marked",modFile); } else {
} profile.unmarkOptional(modFile, modType);
else LogHelper.debug("Load options %s unmarked", modFile);
{
profile.unmarkOptional(modFile,modType);
LogHelper.debug("Load options %s unmarked",modFile);
} }
} }
} }
@ -59,13 +53,13 @@ var options = {
write: function(output) { write: function(output) {
output.writeInt(config.settingsMagic); output.writeInt(config.settingsMagic);
output.writeInt(settings.lastProfiles.length); output.writeInt(settings.lastProfiles.length);
settings.lastProfiles.forEach(function(hprofile,i,arr) { settings.lastProfiles.forEach(function(hprofile, i, arr) {
var profile = hprofile; var profile = hprofile;
LogHelper.debug("Save options %s",profile.getTitle()); LogHelper.debug("Save options %s", profile.getTitle());
var list = profile.getOptional(); var list = profile.getOptional();
output.writeInt(list.size()); output.writeInt(list.size());
output.writeInt(profile.getSortIndex()); output.writeInt(profile.getSortIndex());
list.forEach(function(modFile,j,arr2) { list.forEach(function(modFile, j, arr2) {
output.writeBoolean(modFile.mark); output.writeBoolean(modFile.mark);
modFile.writeType(output); modFile.writeType(output);
output.writeString(modFile.name, 0); output.writeString(modFile.name, 0);
@ -82,50 +76,47 @@ var options = {
var modlist = pane.lookup("#modlist").getContent(); var modlist = pane.lookup("#modlist").getContent();
var nodelist = new java.util.ArrayList; var nodelist = new java.util.ArrayList;
modlist.getChildren().forEach(function(node,i,arr) { modlist.getChildren().forEach(function(node, i, arr) {
if(node instanceof javafx.scene.control.CheckBox) if (node instanceof javafx.scene.control.CheckBox)
nodelist.add(node); nodelist.add(node);
}); });
nodelist.forEach(function(node,i,arr) { nodelist.forEach(function(node, i, arr) {
modlist.getChildren().remove(node); modlist.getChildren().remove(node);
}); });
var profile = profilesList[serverHolder.old]; var profile = profilesList[serverHolder.old];
var list = profile.getOptional(); var list = profile.getOptional();
var checkBoxList = new java.util.ArrayList; var checkBoxList = new java.util.ArrayList;
list.forEach(function(modFile) { list.forEach(function(modFile) {
var modName = modFile.name, modDescription = "", subLevel = 1; var modName = modFile.name,
if(!modFile.visible) modDescription = "",
{ subLevel = 1;
LogHelper.debug("optionalMod %s hidden",modFile.name); if (!modFile.visible) {
LogHelper.debug("optionalMod %s hidden", modFile.name);
return; return;
} }
if(modFile.permissions != 0 && ((loginData.permissions.toLong() & modFile.permissions) == 0)) if (modFile.permissions != 0 && ((loginData.permissions.toLong() & modFile.permissions) == 0)) {
{ LogHelper.debug("optionalMod %s permissions deny", modFile.name);
LogHelper.debug("optionalMod %s permissions deny",modFile.name);
return; return;
} }
if(modFile.info != null) if (modFile.info != null)
modDescription = modFile.info; modDescription = modFile.info;
if(modFile.subTreeLevel != null && modFile.subTreeLevel > 1) if (modFile.subTreeLevel != null && modFile.subTreeLevel > 1)
subLevel = modFile.subTreeLevel; subLevel = modFile.subTreeLevel;
var testMod = new javafx.scene.control.CheckBox(modName); var testMod = new javafx.scene.control.CheckBox(modName);
testMod.getStyleClass().add("checkboxOpt"); testMod.getStyleClass().add("checkboxOpt");
if(subLevel > 1) if (subLevel > 1)
for(var i = 1; i < subLevel; i++) for (var i = 1; i < subLevel; i++)
testMod.setTranslateX(25*i); testMod.setTranslateX(25 * i);
testMod.setSelected(modFile.mark); testMod.setSelected(modFile.mark);
testMod.setOnAction(function(event) { testMod.setOnAction(function(event) {
var isSelected = event.getSource().isSelected(); var isSelected = event.getSource().isSelected();
if(isSelected) if (isSelected) {
{
profile.markOptional(modFile); profile.markOptional(modFile);
LogHelper.debug("Selected mod %s", modFile.name); LogHelper.debug("Selected mod %s", modFile.name);
} } else {
else
{
profile.unmarkOptional(modFile); profile.unmarkOptional(modFile);
LogHelper.debug("Unselected mod %s", modFile.name); LogHelper.debug("Unselected mod %s", modFile.name);
} }
@ -134,12 +125,12 @@ var options = {
testMod.setFocusTraversable(false); testMod.setFocusTraversable(false);
checkBoxList.add(testMod); checkBoxList.add(testMod);
testMod.getStyleClass().add("modname"); testMod.getStyleClass().add("modname");
if(modDescription != "") { if (modDescription != "") {
textDescr = new javafx.scene.text.Text(modDescription); textDescr = new javafx.scene.text.Text(modDescription);
if(subLevel > 1) { if (subLevel > 1) {
for(var i = 1; i < subLevel; i++){ for (var i = 1; i < subLevel; i++) {
textDescr.setWrappingWidth(620-(25*i)); textDescr.setWrappingWidth(620 - (25 * i));
textDescr.setTranslateX(25+(25*i)); textDescr.setTranslateX(25 + (25 * i));
} }
} else { } else {
textDescr.setWrappingWidth(620); textDescr.setWrappingWidth(620);

View file

@ -1,8 +1,7 @@
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/
/* Server buttons */ /* Server buttons */
.server-button { .server-button {
-jfx-button-type: FLAT;
-fx-font-weight: bold; -fx-font-weight: bold;
-fx-font-size: 16pt; -fx-font-size: 16pt;
-fx-background-color: transparent; -fx-background-color: transparent;
@ -17,14 +16,14 @@ .server-button {
} }
.server-button:selected { .server-button:selected {
-fx-effect: dropshadow(gaussian, rgba(23, 25, 29, 0.3), 15,0,0,3); -fx-effect: dropshadow(gaussian, rgba(23, 25, 29, 0.3), 15, 0, 0, 3);
} }
/** server-button-<your profile name> **/ /** server-button-<your profile name> **/
.server-button-Example { .server-button-Example {
-fx-background-image: url('images/servers/example.png'); -fx-background-image: url('images/servers/example.png');
} }
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/

View file

@ -12,7 +12,7 @@ CheckBox .box-container {
CheckBox .box, CheckBox .box,
CheckBox:indeterminate .box, CheckBox:indeterminate .box,
CheckBox:indeterminate:selected .box{ CheckBox:indeterminate:selected .box {
-fx-pref-width: 18; -fx-pref-width: 18;
-fx-pref-height: 18; -fx-pref-height: 18;
@ -46,7 +46,7 @@ CheckBox:selected .mark {
-fx-border-radius: 2; -fx-border-radius: 2;
} }
CheckBox .indeterminate-mark{ CheckBox .indeterminate-mark {
-fx-background-color: #5fd97a; -fx-background-color: #5fd97a;
-fx-background-radius: 2; -fx-background-radius: 2;
-fx-border-width: 0; -fx-border-width: 0;
@ -56,7 +56,10 @@ CheckBox .indeterminate-mark{
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/
Button, CheckBox, ComboBox, RadioButton { Button,
CheckBox,
ComboBox,
RadioButton {
-fx-cursor: hand; -fx-cursor: hand;
} }
@ -69,20 +72,22 @@ #layout {
-fx-pref-height: 450px; -fx-pref-height: 450px;
-fx-background-image: url('images/background.jpg'); -fx-background-image: url('images/background.jpg');
} }
#background { #background {
-fx-background-color: #fff; -fx-background-color: #fff;
} }
/** Labels **/ /** Labels **/
#background > #settingsTitle { #background>#settingsTitle {
-fx-font-size: 14pt; -fx-font-size: 14pt;
-fx-alignment: baseline-center; -fx-alignment: baseline-center;
} }
#serverLabel{ #serverLabel {
-fx-text-fill: #323232; -fx-text-fill: #323232;
} }
#serverStatus{ #serverStatus {
-fx-text-fill: #323232; -fx-text-fill: #323232;
-fx-pref-width: 120px; -fx-pref-width: 120px;
-fx-pref-height: 25px; -fx-pref-height: 25px;
@ -90,17 +95,17 @@ #serverStatus{
/* Mask */ /* Mask */
#mask { #mask {
-fx-effect: DropShadow( gaussian , rgba(255,255,255,0.5) , 0,0,0,1 ); -fx-effect: DropShadow(gaussian, rgba(255, 255, 255, 0.5), 0, 0, 0, 1);
} }
/** Errors **/ /** Errors **/
#errormessage{ #errormessage {
-fx-background-color: transparent; -fx-background-color: transparent;
-fx-text-alignment: center; -fx-text-alignment: center;
-fx-text-fill: #CE5757; -fx-text-fill: #CE5757;
} }
.error{ .error {
-fx-text-fill: #CE5757; -fx-text-fill: #CE5757;
} }
@ -110,51 +115,58 @@ #bar {
-fx-pref-width: 46px; -fx-pref-width: 46px;
-fx-pref-height: 450px; -fx-pref-height: 450px;
} }
/** buttons in bar **/ /** buttons in bar **/
#close { #close {
-fx-background-position: center; -fx-background-position: center;
-jfx-button-type: FLAT;
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-background-color: #CE5757; -fx-background-color: #CE5757;
-fx-pref-width: 46px; -fx-pref-width: 46px;
-fx-pref-height: 45px; -fx-pref-height: 45px;
} }
#hide, #back, #goConsole, #settings, #discord {
#hide,
#back,
#goConsole,
#settings,
#discord {
-fx-background-position: center; -fx-background-position: center;
-jfx-button-type: FLAT;
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-pref-width: 46px; -fx-pref-width: 46px;
-fx-pref-height: 45px; -fx-pref-height: 45px;
} }
#logout{ #logout {
-fx-text-fill:#323232; -fx-text-fill: #323232;
-fx-font-size:12; -fx-font-size: 12;
-fx-font-weight:normal; -fx-font-weight: normal;
-fx-border-color:#CE5757; -fx-border-color: #CE5757;
-fx-border-width:1; -fx-border-width: 1;
-fx-background-color:transparent; -fx-background-color: transparent;
-fx-padding:0; -fx-padding: 0;
} }
#logout:hover, #logout:hover,
#logout:focus{ #logout:focus {
-fx-text-fill:#ff6a5e; -fx-text-fill: #ff6a5e;
} }
#logout:pressed{
-fx-border-color:#cb4d43; #logout:pressed {
-fx-border-color: #cb4d43;
} }
#send { #send {
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-text-fill: black; -fx-text-fill: black;
-fx-background-position: center; -fx-background-position: center;
-jfx-button-type: FLAT;
-fx-background-color: #ffffff; -fx-background-color: #ffffff;
-fx-pref-width: 150px; -fx-pref-width: 150px;
-fx-pref-height: 30px; -fx-pref-height: 30px;
} }
#send:pressed { -fx-background-color: #d8d8d8; } #send:pressed {
-fx-background-color: #d8d8d8;
}
/* LoginMenu */ /* LoginMenu */
#authPane { #authPane {
@ -172,7 +184,6 @@ #logo {
/** Buttons & textarea**/ /** Buttons & textarea**/
.auth { .auth {
-jfx-button-type: FLAT;
-fx-font-weight: bold; -fx-font-weight: bold;
-fx-font-size: 13pt; -fx-font-size: 13pt;
-fx-background-radius: 0; -fx-background-radius: 0;
@ -180,18 +191,22 @@ .auth {
-fx-text-fill: #ffffff; -fx-text-fill: #ffffff;
-fx-pref-width: 200px; -fx-pref-width: 200px;
-fx-pref-height: 45px; -fx-pref-height: 45px;
-fx-effect: dropshadow(gaussian, rgba(23, 25, 29, 0.3), 15,0,0,3); -fx-effect: dropshadow(gaussian, rgba(23, 25, 29, 0.3), 15, 0, 0, 3);
} }
.auth:hover, .auth:pressed { -fx-background-color: #75e18c; } .auth:hover,
.auth:pressed {
-fx-background-color: #75e18c;
}
#password, #login { #password,
#login {
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-pref-width: 200px; -fx-pref-width: 200px;
-fx-pref-height: 30px; -fx-pref-height: 30px;
} }
.text-input{ .text-input {
-fx-focus-color: transparent; -fx-focus-color: transparent;
-fx-background-repeat: no-repeat; -fx-background-repeat: no-repeat;
-fx-text-fill: #909090; -fx-text-fill: #909090;
@ -212,21 +227,26 @@ #link {
-fx-pref-height: 17px; -fx-pref-height: 17px;
} }
#link:hover, #link:pressed { -fx-opacity: 0.8; } #link:hover,
#link:pressed {
-fx-opacity: 0.8;
}
/** CheckBox & ComboBox**/ /** CheckBox & ComboBox**/
#rememberchb{ #rememberchb {
-fx-font-size: 13; -fx-font-size: 13;
-fx-text-fill: #909090; -fx-text-fill: #909090;
-fx-pref-width: 145px; -fx-pref-width: 145px;
-fx-pref-height: 30px; -fx-pref-height: 30px;
} }
#combologin { #combologin {
-fx-text-fill: #909090; -fx-text-fill: #909090;
-fx-prompt-text-fill: #909090; -fx-prompt-text-fill: #909090;
-fx-pref-width: 200px; -fx-pref-width: 200px;
-fx-pref-height: 30px; -fx-pref-height: 30px;
} }
.combologin, .combologin,
.combologin { .combologin {
-fx-font-size: 13px; -fx-font-size: 13px;
@ -244,7 +264,7 @@ .combologin .list-cell {
.combologin-popup .list-view { .combologin-popup .list-view {
-fx-background-color: white, white; -fx-background-color: white, white;
-fx-background-insets: 0, 1; -fx-background-insets: 0, 1;
-fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 8, 0.0 , 0 , 0 ); -fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.6), 8, 0.0, 0, 0);
} }
.combologin .list-cell:filled:selected .text, .combologin .list-cell:filled:selected .text,
@ -257,20 +277,18 @@ .combologin .arrow {
-fx-background-color: #5fd97a; -fx-background-color: #5fd97a;
} }
.combologin-popup .list-view .list-cell .combologin-popup .list-view .list-cell {
{
-fx-background-color: white; -fx-background-color: white;
} }
.combologin-popup .list-view .list-cell:filled:selected, .combologin-popup .list-view .list-cell:filled:selected:hover .combologin-popup .list-view .list-cell:filled:selected,
{ .combologin-popup .list-view .list-cell:filled:selected:hover {
-fx-background: -fx-accent; -fx-background: -fx-accent;
-fx-background-color: -fx-selection-bar; -fx-background-color: -fx-selection-bar;
-fx-text-fill: #909090; -fx-text-fill: #909090;
} }
.combologin-popup .list-view .list-cell:filled:hover .combologin-popup .list-view .list-cell:filled:hover {
{
-fx-background-color: white; -fx-background-color: white;
-fx-text-fill: #909090; -fx-text-fill: #909090;
} }
@ -290,31 +308,23 @@ .serverentrance {
} }
/** buttons **/ /** buttons **/
.clientLaunch{ .clientLaunch {
-jfx-button-type: FLAT;
-fx-font-weight: bold; -fx-font-weight: bold;
-fx-font-size: 16pt; -fx-font-size: 16pt;
-fx-background-radius: 0; -fx-background-radius: 0;
-fx-background-color: #5fd97a; -fx-background-color: #5fd97a;
-fx-text-fill: #ffffff; -fx-text-fill: #ffffff;
} }
.clientSettings{
.clientSettings {
-fx-background-position: center; -fx-background-position: center;
-jfx-button-type: FLAT;
-fx-background-color: #5fd97a; -fx-background-color: #5fd97a;
-fx-background-radius: 0; -fx-background-radius: 0;
} }
.clientLaunch:hover, .clientLaunch:pressed { -fx-background-color: #75e18c; } .clientLaunch:hover,
.clientLaunch:pressed {
/* Pressets options */ -fx-background-color: #75e18c;
.pressetLight, .pressetMedium, .pressetHigh {
-jfx-toggle-color: #5fd97a;
-jfx-untoggle-color: #FAFAFA;
-jfx-toggle-line-color: rgba(116, 192, 133, 0.79);
-jfx-untoggle-line-color: #999999;
-jfx-size: 10.0;
-jfx-disable-visual-focus: false;
} }
/* Scrolls */ /* Scrolls */
@ -356,11 +366,11 @@ .scroll-pane>.corner {
} }
/* OptionsPane */ /* OptionsPane */
#optionsPane > #modlist { #optionsPane>#modlist {
-fx-background-color: transparent; -fx-background-color: transparent;
} }
#optionsPane > #modlist > .viewport { #optionsPane>#modlist>.viewport {
-fx-background-color: transparent; -fx-background-color: transparent;
} }
@ -384,39 +394,42 @@ .separator *.line {
-fx-border-width: 0 0 10 0; -fx-border-width: 0 0 10 0;
} }
#serverlist{ #serverlist {
-fx-background-color: transparent;
}
#serverlist > .viewport {
-fx-background-color: transparent; -fx-background-color: transparent;
} }
#serverdesc{ #serverlist>.viewport {
-fx-background-color: transparent;
}
#serverdesc > .viewport {
-fx-background-color: transparent; -fx-background-color: transparent;
} }
#serverinfo{ #serverdesc {
-fx-background-color: transparent;
}
#serverinfo > .viewport {
-fx-background-color: transparent; -fx-background-color: transparent;
} }
#servercontainer{ #serverdesc>.viewport {
-fx-background-color: transparent;
}
#serverinfo {
-fx-background-color: transparent;
}
#serverinfo>.viewport {
-fx-background-color: transparent;
}
#servercontainer {
-fx-background-color: transparent; -fx-background-color: transparent;
-jfx-button-type: FLAT;
-fx-pref-width: 282px; -fx-pref-width: 282px;
-fx-pref-height: 75px; -fx-pref-height: 75px;
} }
.toggle-button:disabled{ .toggle-button:disabled {
-fx-opacity: 1.0; -fx-opacity: 1.0;
} }
.heading{ .heading {
-fx-text-fill: #555555; -fx-text-fill: #555555;
} }
/*-- DrLeonardo Design --*/ /*-- DrLeonardo Design --*/

View file

@ -41,8 +41,6 @@ var HInput = HInputClass.static;
var HOutput = HOutputClass.static; var HOutput = HOutputClass.static;
var StreamObject = StreamObjectClass.static; var StreamObject = StreamObjectClass.static;
var StreamObjectAdapter = StreamObjectAdapterClass.static; var StreamObjectAdapter = StreamObjectAdapterClass.static;
var SignedBytesHolder = SignedBytesHolderClass.static;
var SignedObjectHolder = SignedObjectHolderClass.static;
var EnumSerializer = EnumSerializerClass.static; var EnumSerializer = EnumSerializerClass.static;
var OptionalFile = OptionalFileClass.static; var OptionalFile = OptionalFileClass.static;

View file

@ -10,6 +10,7 @@ var settingsManagerClass = Java.extend(SettingsManagerClass.static, {
new_settings.fullScreen = config.fullScreenDefault; new_settings.fullScreen = config.fullScreenDefault;
new_settings.ram = config.ramDefault; new_settings.ram = config.ramDefault;
new_settings.featureStore = config.featureStoreDefault;
new_settings.lastDigest = null; new_settings.lastDigest = null;
new_settings.lastProfiles.clear(); new_settings.lastProfiles.clear();
new_settings.lastHDirs.clear(); new_settings.lastHDirs.clear();

View file

@ -9,7 +9,8 @@ var LauncherApp = Java.extend(JSApplication, {
settings = SettingsManager.settings; settings = SettingsManager.settings;
settingsManager.loadHDirStore(); settingsManager.loadHDirStore();
cliParams.applySettings(); cliParams.applySettings();
}, start: function(primaryStage) { },
start: function(primaryStage) {
stage = primaryStage; stage = primaryStage;
stage.initStyle(javafx.stage.StageStyle.TRANSPARENT); stage.initStyle(javafx.stage.StageStyle.TRANSPARENT);
stage.setResizable(false); stage.setResizable(false);
@ -45,7 +46,8 @@ var LauncherApp = Java.extend(JSApplication, {
setCurrentScene(loginScene); setCurrentScene(loginScene);
initLauncher(); initLauncher();
}, stop: function() { },
stop: function() {
settingsManager.saveConfig(); settingsManager.saveConfig();
settingsManager.saveHDirStore(); settingsManager.saveHDirStore();
options.save(); options.save();

View file

@ -7,7 +7,6 @@
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.instrument.Instrumentation; import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -22,6 +21,7 @@
import cpw.mods.fml.SafeExitJVMLegacy; import cpw.mods.fml.SafeExitJVMLegacy;
import net.minecraftforge.fml.SafeExitJVM; import net.minecraftforge.fml.SafeExitJVM;
import pro.gravit.launcher.utils.NativeJVMHalt; import pro.gravit.launcher.utils.NativeJVMHalt;
import pro.gravit.utils.helper.JVMHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
@LauncherAPI @LauncherAPI
@ -46,6 +46,9 @@ public static void premain(String agentArgument, Instrumentation instrumentation
NativeJVMHalt.class.getName(); NativeJVMHalt.class.getName();
NativeJVMHalt.initFunc(); NativeJVMHalt.initFunc();
isAgentStarted = true; isAgentStarted = true;
if (System.getProperty("java.vm.name").toUpperCase(Locale.US).contains("HOTSPOT"))
try {
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
boolean pb = true; boolean pb = true;
boolean rt = true; boolean rt = true;
if (agentArgument != null) { if (agentArgument != null) {
@ -55,10 +58,8 @@ public static void premain(String agentArgument, Instrumentation instrumentation
if (trimmedArg.contains("r")) rt = false; if (trimmedArg.contains("r")) rt = false;
} }
} }
if (System.getProperty("java.vm.name").toUpperCase(Locale.US).contains("HOTSPOT")) replaceClasses(pb, rt);
try { } else replaceClasses(false, false);
if (ManagementFactory.getOperatingSystemMXBean().getName().startsWith("Windows")) replaceClasses(pb, rt);
else replaceClasses(false, false);
} catch (Error e) { } catch (Error e) {
NativeJVMHalt.haltA(294); NativeJVMHalt.haltA(294);
throw e; throw e;

View file

@ -14,11 +14,15 @@
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
@ -55,16 +59,16 @@
public final class ClientLauncher { public final class ClientLauncher {
private static final class ClassPathFileVisitor extends SimpleFileVisitor<Path> { private static final class ClassPathFileVisitor extends SimpleFileVisitor<Path> {
private final Collection<Path> result; private final Stream.Builder<Path> result;
private ClassPathFileVisitor(Collection<Path> result) { private ClassPathFileVisitor(Stream.Builder<Path> result) {
this.result = result; this.result = result;
} }
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip")) if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
result.add(file); result.accept(file);
return super.visitFile(file, attrs); return super.visitFile(file, attrs);
} }
} }
@ -282,10 +286,17 @@ private static void launch(ClientProfile profile, Params params) throws Throwabl
System.setProperty("minecraft.applet.TargetDirectory", params.clientDir.toString()); System.setProperty("minecraft.applet.TargetDirectory", params.clientDir.toString());
} }
Collections.addAll(args, profile.getClientArgs()); Collections.addAll(args, profile.getClientArgs());
LogHelper.debug("Args: " + args); List<String> copy = new ArrayList<>(args);
for (int i = 0, l = copy.size(); i < l; i++) {
String s = copy.get(i);
if ( i + 1 < l && ("--accessToken".equals(s) || "--session".equals(s))) {
copy.set(i + 1, "censored");
}
}
LogHelper.debug("Args: " + copy);
// Resolve main class and method // Resolve main class and method
Class<?> mainClass = classLoader.loadClass(profile.getMainClass()); Class<?> mainClass = classLoader.loadClass(profile.getMainClass());
MethodHandle mainMethod = MethodHandles.publicLookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)).asFixedArity(); MethodHandle mainMethod = MethodHandles.publicLookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
Launcher.LAUNCHED.set(true); Launcher.LAUNCHED.set(true);
JVMHelper.fullGC(); JVMHelper.fullGC();
// Invoke main method // Invoke main method
@ -295,6 +306,7 @@ private static void launch(ClientProfile profile, Params params) throws Throwabl
private static Process process = null; private static Process process = null;
private static boolean clientStarted = false; private static boolean clientStarted = false;
private static Thread writeParamsThread; private static Thread writeParamsThread;
public static PlayerProfile playerProfile;
@LauncherAPI @LauncherAPI
public static Process launch( public static Process launch(
@ -407,7 +419,7 @@ public static Process launch(
Thread.sleep(200); Thread.sleep(200);
} }
if (!clientStarted) { if (!clientStarted) {
LogHelper.error("Write Client Params not successful. Using debug mode for more information"); LogHelper.error("Client did not start properly. Enable debug mode for more information");
} }
} }
clientStarted = false; clientStarted = false;
@ -450,6 +462,7 @@ public static void main(String... args) throws Throwable {
return; return;
} }
Launcher.profile = profile; Launcher.profile = profile;
playerProfile = params.pp;
Request.setSession(params.session); Request.setSession(params.session);
checkJVMBitsAndVersion(); checkJVMBitsAndVersion();
Launcher.modulesManager.initModules(); Launcher.modulesManager.initModules();
@ -515,32 +528,27 @@ public static void main(String... args) throws Throwable {
} }
private static URL[] resolveClassPath(Path clientDir, String... classPath) throws IOException { private static URL[] resolveClassPath(Path clientDir, String... classPath) throws IOException {
Collection<Path> result = new LinkedList<>(); return resolveClassPathStream(clientDir, classPath).map(IOHelper::toURL).toArray(URL[]::new);
for (String classPathEntry : classPath) {
Path path = clientDir.resolve(IOHelper.toPath(classPathEntry));
if (IOHelper.isDir(path)) { // Recursive walking and adding
IOHelper.walk(path, new ClassPathFileVisitor(result), false);
continue;
}
result.add(path);
}
return result.stream().map(IOHelper::toURL).toArray(URL[]::new);
} }
private static LinkedList<Path> resolveClassPathList(Path clientDir, String... classPath) throws IOException { private static LinkedList<Path> resolveClassPathList(Path clientDir, String... classPath) throws IOException {
LinkedList<Path> result = new LinkedList<>(); return resolveClassPathStream(clientDir, classPath).collect(Collectors.toCollection(LinkedList::new));
}
private static Stream<Path> resolveClassPathStream(Path clientDir, String... classPath) throws IOException {
Stream.Builder<Path> builder = Stream.builder();
for (String classPathEntry : classPath) { for (String classPathEntry : classPath) {
Path path = clientDir.resolve(IOHelper.toPath(classPathEntry)); Path path = clientDir.resolve(IOHelper.toPath(classPathEntry));
if (IOHelper.isDir(path)) { // Recursive walking and adding if (IOHelper.isDir(path)) { // Recursive walking and adding
IOHelper.walk(path, new ClassPathFileVisitor(result), false); IOHelper.walk(path, new ClassPathFileVisitor(builder), false);
continue; continue;
} }
result.add(path); builder.accept(path);
} }
return result; return builder.build();
} }
public static void initGson() { private static void initGson() {
Launcher.gsonManager = new ClientGsonManager(); Launcher.gsonManager = new ClientGsonManager();
Launcher.gsonManager.initGson(); Launcher.gsonManager.initGson();
} }
@ -552,8 +560,8 @@ public static void setProfile(ClientProfile profile) {
} }
public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException { public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
if (matcher != null) //if (matcher != null)
matcher = matcher.verifyOnly(); // matcher = matcher.verifyOnly();
// Hash directory and compare (ignore update-only matcher entries, it will break offline-mode) // Hash directory and compare (ignore update-only matcher entries, it will break offline-mode)
HashedDir currentHDir = new HashedDir(dir, matcher, true, digest); HashedDir currentHDir = new HashedDir(dir, matcher, true, digest);

View file

@ -7,6 +7,7 @@
import pro.gravit.launcher.managers.SimpleModuleManager; import pro.gravit.launcher.managers.SimpleModuleManager;
public class ClientModuleManager extends SimpleModuleManager { public class ClientModuleManager extends SimpleModuleManager {
public ClientModuleManager(LauncherEngine engine) { public ClientModuleManager(LauncherEngine engine) {
context = new ClientModuleContext(engine); context = new ClientModuleContext(engine);
modules = new ArrayList<>(); modules = new ArrayList<>();

View file

@ -17,8 +17,8 @@
import pro.gravit.launcher.managers.HasherManager; import pro.gravit.launcher.managers.HasherManager;
import pro.gravit.launcher.managers.HasherStore; import pro.gravit.launcher.managers.HasherStore;
import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.serialize.signed.SignedObjectHolder;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.Version;
public class FunctionalBridge { public class FunctionalBridge {
@LauncherAPI @LauncherAPI
@ -33,12 +33,12 @@ public class FunctionalBridge {
private static long cachedMemorySize = -1; private static long cachedMemorySize = -1;
@LauncherAPI @LauncherAPI
public static HashedDirRunnable offlineUpdateRequest(String dirName, Path dir, SignedObjectHolder<HashedDir> hdir, FileNameMatcher matcher, boolean digest) { public static HashedDirRunnable offlineUpdateRequest(String dirName, Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) {
return () -> { return () -> {
if (hdir == null) { if (hdir == null) {
Request.requestError(java.lang.String.format("Директории '%s' нет в кэше", dirName)); Request.requestError(java.lang.String.format("Директории '%s' нет в кэше", dirName));
} }
ClientLauncher.verifyHDir(dir, hdir.object, matcher, digest); ClientLauncher.verifyHDir(dir, hdir, matcher, digest);
return hdir; return hdir;
}; };
} }
@ -100,7 +100,7 @@ public static void setAuthParams(AuthRequestEvent event) {
@FunctionalInterface @FunctionalInterface
public interface HashedDirRunnable { public interface HashedDirRunnable {
SignedObjectHolder<HashedDir> run() throws Exception; HashedDir run() throws Exception;
} }
@LauncherAPI @LauncherAPI
@ -112,4 +112,14 @@ public static void evalCommand(String cmd) {
public static void addPlainOutput(LogHelper.Output output) { public static void addPlainOutput(LogHelper.Output output) {
LogHelper.addOutput(output, LogHelper.OutputTypes.PLAIN); LogHelper.addOutput(output, LogHelper.OutputTypes.PLAIN);
} }
@LauncherAPI
public static String getLauncherVersion() {
return String.format("GravitLauncher v%d.%d.%d build %d",
Version.MAJOR,
Version.MINOR,
Version.PATCH,
Version.BUILD
);
}
} }

View file

@ -72,7 +72,9 @@ public void postDiff(UpdateRequest request, UpdateRequestEvent e, HashedDir.Diff
//if(file.isSame(ret, true)) //if(file.isSame(ret, true))
{ {
Path source = request.getDir().resolve(path); Path source = request.getDir().resolve(path);
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("Copy file %s to %s", ret.toAbsolutePath().toString(), source.toAbsolutePath().toString()); LogHelper.debug("Copy file %s to %s", ret.toAbsolutePath().toString(), source.toAbsolutePath().toString());
}
//Let's go! //Let's go!
Files.copy(ret, source); Files.copy(ret, source);
try (InputStream input = IOHelper.newInput(ret)) { try (InputStream input = IOHelper.newInput(ret)) {
@ -93,11 +95,15 @@ public Path tryFind(NewLauncherSettings.HashedStoreEntry en, HashedFile file) th
if (entry.getType() == HashedEntry.Type.DIR) return HashedDir.WalkAction.CONTINUE; if (entry.getType() == HashedEntry.Type.DIR) return HashedDir.WalkAction.CONTINUE;
HashedFile tfile = (HashedFile) entry; HashedFile tfile = (HashedFile) entry;
if (tfile.isSame(file)) { if (tfile.isSame(file)) {
if (LogHelper.isDevEnabled()) {
LogHelper.dev("[DIR:%s] Found file %s in %s", en.name, name, path); LogHelper.dev("[DIR:%s] Found file %s in %s", en.name, name, path);
}
Path tdir = Paths.get(en.fullPath).resolve(path); Path tdir = Paths.get(en.fullPath).resolve(path);
try { try {
if (tfile.isSame(tdir, true)) { if (tfile.isSame(tdir, true)) {
if (LogHelper.isDevEnabled()) {
LogHelper.dev("[DIR:%s] Confirmed file %s in %s", en.name, name, path); LogHelper.dev("[DIR:%s] Confirmed file %s in %s", en.name, name, path);
}
ret.set(tdir); ret.set(tdir);
return HashedDir.WalkAction.STOP; return HashedDir.WalkAction.STOP;
} }

View file

@ -49,8 +49,6 @@
import pro.gravit.launcher.request.uuid.ProfileByUsernameRequest; import pro.gravit.launcher.request.uuid.ProfileByUsernameRequest;
import pro.gravit.launcher.serialize.HInput; import pro.gravit.launcher.serialize.HInput;
import pro.gravit.launcher.serialize.HOutput; import pro.gravit.launcher.serialize.HOutput;
import pro.gravit.launcher.serialize.signed.SignedBytesHolder;
import pro.gravit.launcher.serialize.signed.SignedObjectHolder;
import pro.gravit.launcher.serialize.stream.EnumSerializer; import pro.gravit.launcher.serialize.stream.EnumSerializer;
import pro.gravit.launcher.serialize.stream.StreamObject; import pro.gravit.launcher.serialize.stream.StreamObject;
import pro.gravit.utils.HTTPRequest; import pro.gravit.utils.HTTPRequest;
@ -64,7 +62,7 @@
public class JSRuntimeProvider implements RuntimeProvider { public class JSRuntimeProvider implements RuntimeProvider {
private final ScriptEngine engine = CommonHelper.newScriptEngine(); public final ScriptEngine engine = CommonHelper.newScriptEngine();
private boolean isPreLoaded = false; private boolean isPreLoaded = false;
@LauncherAPI @LauncherAPI
@ -112,8 +110,6 @@ public static void addLauncherClassBindings(Map<String, Object> bindings) {
bindings.put("HOutputClass", HOutput.class); bindings.put("HOutputClass", HOutput.class);
bindings.put("StreamObjectClass", StreamObject.class); bindings.put("StreamObjectClass", StreamObject.class);
bindings.put("StreamObjectAdapterClass", StreamObject.Adapter.class); bindings.put("StreamObjectAdapterClass", StreamObject.Adapter.class);
bindings.put("SignedBytesHolderClass", SignedBytesHolder.class);
bindings.put("SignedObjectHolderClass", SignedObjectHolder.class);
bindings.put("EnumSerializerClass", EnumSerializer.class); bindings.put("EnumSerializerClass", EnumSerializer.class);
bindings.put("OptionalFileClass", OptionalFile.class); bindings.put("OptionalFileClass", OptionalFile.class);
bindings.put("UserSettingsClass", UserSettings.class); bindings.put("UserSettingsClass", UserSettings.class);

View file

@ -8,7 +8,6 @@
import oshi.hardware.HWDiskStore; import oshi.hardware.HWDiskStore;
import oshi.hardware.HardwareAbstractionLayer; import oshi.hardware.HardwareAbstractionLayer;
import oshi.hardware.NetworkIF; import oshi.hardware.NetworkIF;
import oshi.hardware.SoundCard;
import oshi.hardware.UsbDevice; import oshi.hardware.UsbDevice;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
@ -67,15 +66,9 @@ public String getHWDisk() {
} }
} }
public String getSoundCardInfo() {
for (SoundCard soundcard : hardware.getSoundCards()) {
return soundcard.getName();
}
return "";
}
public String getMacAddr() { public String getMacAddr() {
for (NetworkIF networkIF : hardware.getNetworkIFs()) { for (NetworkIF networkIF : hardware.getNetworkIFs()) {
if (networkIF.getNetworkInterface().isVirtual()) continue;
for (String ipv4 : networkIF.getIPv4addr()) { for (String ipv4 : networkIF.getIPv4addr()) {
if (ipv4.startsWith("127.")) continue; if (ipv4.startsWith("127.")) continue;
if (ipv4.startsWith("10.")) continue; if (ipv4.startsWith("10.")) continue;
@ -119,9 +112,6 @@ public void printHardwareInformation() {
LogHelper.subDebug("IPv4: %s", ipv4); LogHelper.subDebug("IPv4: %s", ipv4);
} }
} }
for (SoundCard soundcard : hardware.getSoundCards()) {
LogHelper.debug("SoundCard %s", soundcard.getName());
}
CentralProcessor processor = hardware.getProcessor(); CentralProcessor processor = hardware.getProcessor();
LogHelper.debug("Processor Model: %s ID: %s", processor.getModel(), processor.getProcessorID()); LogHelper.debug("Processor Model: %s ID: %s", processor.getModel(), processor.getProcessorID());
} catch (Throwable e) { } catch (Throwable e) {

View file

@ -27,15 +27,6 @@
public final class DirWatcher implements Runnable, AutoCloseable { public final class DirWatcher implements Runnable, AutoCloseable {
private final class RegisterFileVisitor extends SimpleFileVisitor<Path> { private final class RegisterFileVisitor extends SimpleFileVisitor<Path> {
private final Deque<String> path = new LinkedList<>();
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
FileVisitResult result = super.postVisitDirectory(dir, exc);
if (!DirWatcher.this.dir.equals(dir))
path.removeLast();
return result;
}
@Override @Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
@ -46,7 +37,6 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th
} }
// Maybe it's unnecessary to go deeper // Maybe it's unnecessary to go deeper
path.add(IOHelper.getFileName(dir));
//if (matcher != null && !matcher.shouldVerify(path)) { //if (matcher != null && !matcher.shouldVerify(path)) {
// return FileVisitResult.SKIP_SUBTREE; // return FileVisitResult.SKIP_SUBTREE;
//} //}

View file

@ -6,3 +6,36 @@ compile project(':LauncherCore')
compileOnly 'org.apache.httpcomponents:httpclient:4.5.7' compileOnly 'org.apache.httpcomponents:httpclient:4.5.7'
compileOnly 'io.netty:netty-codec-http:4.1.36.Final' compileOnly 'io.netty:netty-codec-http:4.1.36.Final'
} }
jar {
classifier = 'clean'
}
publishing {
publications {
launcherwsapi(MavenPublication) {
artifactId = 'launcher-ws-api'
artifact jar
pom {
name = 'GravitLauncher WebSocket API'
description = 'GravitLauncher WebSocket Module API'
url = 'https://launcher.gravit.pro'
licenses {
license {
name = 'GNU General Public License, Version 3.0'
url = 'https://www.gnu.org/licenses/gpl-3.0.html'
}
}
scm {
connection = 'scm:git:https://github.com/GravitLauncher/Launcher.git'
developerConnection = 'scm:git:ssh://git@github.com:GravitLauncher/Launcher.git'
url = 'https://launcher.gravit.pro/'
}
}
}
}
}
signing {
sign publishing.publications.launcherwsapi
}

View file

@ -4,8 +4,6 @@ public class AutogenConfig {
public String projectname; public String projectname;
public String address; public String address;
public int clientPort; public int clientPort;
@SuppressWarnings("unused")
private boolean isInitModules;
public String guardType; public String guardType;
public String secretKeyClient; public String secretKeyClient;
public String oemUnlockKey; public String oemUnlockKey;
@ -23,5 +21,6 @@ public class AutogenConfig {
} }
public void initModules() { public void initModules() {
} }
} }

View file

@ -20,8 +20,6 @@ public class ClientPermissions {
public boolean canUSR3; public boolean canUSR3;
@LauncherAPI @LauncherAPI
public boolean canBot; public boolean canBot;
@LauncherAPI
public boolean canProxy;
public ClientPermissions(HInput input) throws IOException { public ClientPermissions(HInput input) throws IOException {
this(input.readLong()); this(input.readLong());

View file

@ -9,6 +9,9 @@
import java.net.URL; import java.net.URL;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
@ -19,10 +22,16 @@
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy; import org.apache.http.impl.client.LaxRedirectStrategy;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.*;
import pro.gravit.utils.helper.LogHelper;
public class ListDownloader { public class ListDownloader {
private static final AtomicInteger COUNTER_THR = new AtomicInteger(0);
private static final ThreadFactory FACTORY = r -> CommonHelper.newThread("Downloader Thread #" + COUNTER_THR.incrementAndGet(), true, r);
private static ExecutorService newExecutor() {
return new ThreadPoolExecutor(0, VerifyHelper.verifyInt(Integer.parseInt(System.getProperty("launcher.downloadThreads", "3")), VerifyHelper.POSITIVE, "Thread max count must be positive."), 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), FACTORY);
}
@FunctionalInterface @FunctionalInterface
public interface DownloadCallback { public interface DownloadCallback {
void stateChanged(String filename, long downloadedSize, long size); void stateChanged(String filename, long downloadedSize, long size);
@ -47,19 +56,43 @@ public void download(String base, List<DownloadTask> applies, Path dstDirFile, D
try (CloseableHttpClient httpclient = HttpClients.custom() try (CloseableHttpClient httpclient = HttpClients.custom()
.setRedirectStrategy(new LaxRedirectStrategy()) .setRedirectStrategy(new LaxRedirectStrategy())
.build()) { .build()) {
applies.sort((a,b) -> Long.compare(a.size, b.size));
HttpGet get = null; List<Callable<Void>> toExec = new ArrayList<>();
URI baseUri = new URI(base);
String scheme = baseUri.getScheme();
String host = baseUri.getHost();
int port = baseUri.getPort();
if (port != -1)
host = host + ":" + port;
String path = baseUri.getPath();
List<IOException> excs = new CopyOnWriteArrayList<>();
for (DownloadTask apply : applies) { for (DownloadTask apply : applies) {
URI u = new URL(base.concat(IOHelper.urlEncode(apply.apply).replace("%2F", "/"))).toURI(); URI u = new URI(scheme, host, path + apply.apply, "", "");
callback.stateChanged(apply.apply, 0L, apply.size); callback.stateChanged(apply.apply, 0L, apply.size);
Path targetPath = dstDirFile.resolve(apply.apply); Path targetPath = dstDirFile.resolve(apply.apply);
toExec.add(() -> {
if (LogHelper.isDebugEnabled())
LogHelper.debug("Download URL: %s to file %s dir: %s", u.toString(), targetPath.toAbsolutePath().toString(), dstDirFile.toAbsolutePath().toString()); LogHelper.debug("Download URL: %s to file %s dir: %s", u.toString(), targetPath.toAbsolutePath().toString(), dstDirFile.toAbsolutePath().toString());
if (get == null) get = new HttpGet(u); try {
else { httpclient.execute(new HttpGet(u), new FileDownloadResponseHandler(targetPath, apply, callback, totalCallback, false));
get.reset(); } catch (IOException e) {
get.setURI(u); excs.add(e);
} }
httpclient.execute(get, new FileDownloadResponseHandler(targetPath, apply, callback, totalCallback, false)); return null;
});
}
try {
ExecutorService e = newExecutor();
e.invokeAll(toExec);
e.shutdown();
e.awaitTermination(4, TimeUnit.HOURS);
} catch (InterruptedException t) {
LogHelper.error(t);
}
if (!excs.isEmpty()) {
IOException toThrow = excs.remove(0);
excs.forEach(toThrow::addSuppressed);
throw toThrow;
} }
} }
} }
@ -97,7 +130,9 @@ public void downloadOne(String url, Path target) throws IOException, URISyntaxEx
HttpGet get; HttpGet get;
URI u = new URL(url).toURI(); URI u = new URL(url).toURI();
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("Download URL: %s", u.toString()); LogHelper.debug("Download URL: %s", u.toString());
}
get = new HttpGet(u); get = new HttpGet(u);
httpclient.execute(get, new FileDownloadResponseHandler(target.toAbsolutePath())); httpclient.execute(get, new FileDownloadResponseHandler(target.toAbsolutePath()));
} }
@ -161,7 +196,9 @@ public Path handleResponse(HttpResponse response) throws IOException {
if (callback != null) { if (callback != null) {
callback.stateChanged(entry.getName(), 0, entry.getSize()); callback.stateChanged(entry.getName(), 0, entry.getSize());
} }
if (LogHelper.isDevEnabled()) {
LogHelper.dev("Resolved filename %s to %s", filename, target.toAbsolutePath().toString()); LogHelper.dev("Resolved filename %s to %s", filename, target.toAbsolutePath().toString());
}
transfer(source, target, filename, size, callback, totalCallback); transfer(source, target, filename, size, callback, totalCallback);
entry = input.getNextEntry(); entry = input.getNextEntry();
} }

View file

@ -4,6 +4,7 @@
//Набор стандартных событий //Набор стандартных событий
public class ControlEvent { public class ControlEvent {
@SuppressWarnings("unused")
private static final UUID uuid = UUID.fromString("f1051a64-0cd0-4ed8-8430-d856a196e91f"); private static final UUID uuid = UUID.fromString("f1051a64-0cd0-4ed8-8430-d856a196e91f");
public enum ControlCommand { public enum ControlCommand {

View file

@ -1,7 +1,5 @@
package pro.gravit.launcher.events.request; package pro.gravit.launcher.events.request;
import java.util.UUID;
import pro.gravit.launcher.ClientPermissions; import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.LauncherNetworkAPI; import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.events.RequestEvent; import pro.gravit.launcher.events.RequestEvent;

Some files were not shown because too many files have changed in this diff Show more