Merge branch 'release/5.0.6'
3
.gitmodules
vendored
|
@ -1,6 +1,3 @@
|
|||
[submodule "modules"]
|
||||
path = modules
|
||||
url = git@github.com:GravitLauncher/LauncherModules.git
|
||||
[submodule "Radon"]
|
||||
path = Radon
|
||||
url = git@github.com:GravitLauncher/Radon.git
|
||||
|
|
|
@ -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 {
|
||||
pack project(':LauncherAPI')
|
||||
bundle project(':Radon')
|
||||
bundle 'org.ow2.asm:asm-commons:7.1'
|
||||
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-reader: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 'org.javassist:javassist:3.25.0-GA'
|
||||
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.slf4j:slf4j-simple:1.7.25'
|
||||
|
@ -146,4 +160,35 @@ task dumpClientLibs(type: Copy) {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -13,11 +13,12 @@
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -32,6 +33,10 @@
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
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 pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherConfig;
|
||||
|
@ -41,7 +46,6 @@
|
|||
import pro.gravit.launcher.managers.ConfigManager;
|
||||
import pro.gravit.launcher.managers.GarbageManager;
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
||||
import pro.gravit.launchserver.auth.handler.MemoryAuthHandler;
|
||||
|
@ -61,13 +65,19 @@
|
|||
import pro.gravit.launchserver.binary.JARLauncherBinary;
|
||||
import pro.gravit.launchserver.binary.LauncherBinary;
|
||||
import pro.gravit.launchserver.binary.ProguardConf;
|
||||
import pro.gravit.launchserver.binary.SimpleEXELauncherBinary;
|
||||
import pro.gravit.launchserver.components.AuthLimiterComponent;
|
||||
import pro.gravit.launchserver.components.Component;
|
||||
import pro.gravit.launchserver.components.RegLimiterComponent;
|
||||
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
|
||||
import pro.gravit.launchserver.dao.UserService;
|
||||
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.BuildHookManager;
|
||||
import pro.gravit.launchserver.socket.WebSocketService;
|
||||
|
@ -147,15 +157,8 @@ public AuthProviderPair getAuthProviderPair() {
|
|||
public GuardLicenseConf guardLicense;
|
||||
|
||||
public String whitelistRejectString;
|
||||
|
||||
public boolean genMappings;
|
||||
public LauncherConf launcher;
|
||||
|
||||
public boolean isWarningMissArchJava;
|
||||
public boolean enabledProGuard;
|
||||
public boolean enabledRadon;
|
||||
public boolean stripLineNumbers;
|
||||
public boolean deleteTempFiles;
|
||||
public CertificateConf certificate;
|
||||
|
||||
public String startScript;
|
||||
|
||||
|
@ -253,6 +256,7 @@ public void close() {
|
|||
|
||||
public static class ExeConf {
|
||||
public boolean enabled;
|
||||
public String alternative;
|
||||
public boolean setMaxVersion;
|
||||
public String maxVersion;
|
||||
public String productName;
|
||||
|
@ -267,6 +271,11 @@ public static class ExeConf {
|
|||
public String txtProductVersion;
|
||||
}
|
||||
|
||||
public static class CertificateConf
|
||||
{
|
||||
public boolean enabled;
|
||||
}
|
||||
|
||||
public static class NettyUpdatesBind {
|
||||
public String url;
|
||||
public boolean zip;
|
||||
|
@ -275,6 +284,12 @@ public static class NettyUpdatesBind {
|
|||
public class LauncherConf {
|
||||
public String guardType;
|
||||
public boolean attachLibraryBeforeProGuard;
|
||||
public boolean compress;
|
||||
public boolean warningMissArchJava;
|
||||
public boolean enabledProGuard;
|
||||
public boolean stripLineNumbers;
|
||||
public boolean deleteTempFiles;
|
||||
public boolean proguardGenMappings;
|
||||
}
|
||||
|
||||
public class NettyConfig {
|
||||
|
@ -289,7 +304,6 @@ public class NettyConfig {
|
|||
public NettyPerformanceConfig performance;
|
||||
public NettyBindAddress[] binds;
|
||||
public LogLevel logLevel = LogLevel.DEBUG;
|
||||
public NettyProxyConfig proxy = new NettyProxyConfig();
|
||||
}
|
||||
|
||||
public class NettyPerformanceConfig {
|
||||
|
@ -298,15 +312,6 @@ public class NettyPerformanceConfig {
|
|||
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 String address;
|
||||
public int port;
|
||||
|
@ -355,7 +360,7 @@ public static void main(String... args) throws Throwable {
|
|||
LogHelper.printLicense("LaunchServer");
|
||||
if (!StarterAgent.isAgentStarted()) {
|
||||
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
|
||||
|
@ -394,6 +399,14 @@ public static void main(String... args) throws Throwable {
|
|||
|
||||
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 static LaunchServer server = null;
|
||||
|
@ -448,7 +461,7 @@ public static void main(String... args) throws Throwable {
|
|||
|
||||
// Updates and profiles
|
||||
private volatile List<ClientProfile> profilesList;
|
||||
public volatile Map<String, SignedObjectHolder<HashedDir>> updatesDirMap;
|
||||
public volatile Map<String, HashedDir> updatesDirMap;
|
||||
|
||||
public final Timer taskPool;
|
||||
|
||||
|
@ -482,6 +495,12 @@ public LaunchServer(Path dir, boolean testEnv, String[] args) throws IOException
|
|||
updatesDir = dir.resolve("updates");
|
||||
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
|
||||
AuthHandler.registerHandlers();
|
||||
AuthProvider.registerProviders();
|
||||
|
@ -594,6 +613,43 @@ public LaunchServer(Path dir, boolean testEnv, String[] args) throws IOException
|
|||
authHookManager = new AuthHookManager();
|
||||
configManager = new ConfigManager();
|
||||
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);
|
||||
reloadManager.registerReloadable("launchServer", this);
|
||||
registerObject("permissionsHandler", config.permissionsHandler);
|
||||
|
@ -669,6 +725,19 @@ private LauncherBinary binary() {
|
|||
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 {
|
||||
Class.forName("net.sf.launch4j.Builder");
|
||||
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.enabled = true;
|
||||
newConfig.launch4j.copyright = "© GravitLauncher Team";
|
||||
newConfig.launch4j.alternative = "no";
|
||||
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
|
||||
newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch));
|
||||
newConfig.launch4j.internalName = "Launcher";
|
||||
|
@ -741,19 +811,22 @@ private void generateConfigIfNotExists(boolean testEnv) throws IOException {
|
|||
newConfig.netty.fileServerEnabled = true;
|
||||
newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)};
|
||||
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.workerThread = 8;
|
||||
|
||||
newConfig.launcher = new LauncherConf();
|
||||
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.genMappings = true;
|
||||
newConfig.enabledProGuard = true;
|
||||
newConfig.stripLineNumbers = true;
|
||||
newConfig.deleteTempFiles = true;
|
||||
newConfig.isWarningMissArchJava = true;
|
||||
newConfig.certificate = new CertificateConf();
|
||||
newConfig.certificate.enabled = false;
|
||||
|
||||
newConfig.components = new HashMap<>();
|
||||
AuthLimiterComponent authLimiterComponent = new AuthLimiterComponent();
|
||||
|
@ -808,12 +881,12 @@ public void setProfiles(List<ClientProfile> profilesList) {
|
|||
this.profilesList = Collections.unmodifiableList(profilesList);
|
||||
}
|
||||
|
||||
public SignedObjectHolder<HashedDir> getUpdateDir(String name) {
|
||||
public HashedDir getUpdateDir(String name) {
|
||||
return updatesDirMap.get(name);
|
||||
}
|
||||
|
||||
|
||||
public Set<Entry<String, SignedObjectHolder<HashedDir>>> getUpdateDirs() {
|
||||
public Set<Entry<String, HashedDir>> getUpdateDirs() {
|
||||
return updatesDirMap.entrySet();
|
||||
}
|
||||
|
||||
|
@ -866,7 +939,7 @@ public void syncProfilesDir() throws IOException {
|
|||
|
||||
public void syncUpdatesDir(Collection<String> dirs) throws IOException {
|
||||
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)) {
|
||||
for (final Path updateDir : dirStream) {
|
||||
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)
|
||||
if (dirs != null && !dirs.contains(name)) {
|
||||
SignedObjectHolder<HashedDir> hdir = updatesDirMap.get(name);
|
||||
HashedDir hdir = updatesDirMap.get(name);
|
||||
if (hdir != null) {
|
||||
newUpdatesDirMap.put(name, hdir);
|
||||
continue;
|
||||
|
@ -892,7 +965,7 @@ public void syncUpdatesDir(Collection<String> dirs) throws IOException {
|
|||
// Sync and sign update dir
|
||||
LogHelper.info("Syncing '%s' update dir", name);
|
||||
HashedDir updateHDir = new HashedDir(updateDir, null, true, true);
|
||||
newUpdatesDirMap.put(name, new SignedObjectHolder<>(updateHDir, privateKey));
|
||||
newUpdatesDirMap.put(name, updateHDir);
|
||||
}
|
||||
}
|
||||
updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ public static void registerHandlers() {
|
|||
providers.register("json", JsonAuthHandler.class);
|
||||
providers.register("memory", MemoryAuthHandler.class);
|
||||
providers.register("mysql", MySQLAuthHandler.class);
|
||||
providers.register("postgresql", PostgreSQLAuthHandler.class);
|
||||
providers.register("request", RequestAuthHandler.class);
|
||||
providers.register("hibernate", HibernateAuthHandler.class);
|
||||
registredHandl = true;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -153,11 +153,15 @@ public void onCheckInfo(OshiHWID hwid, String username, Connection c) throws HWI
|
|||
db_hwid.HWDiskSerial = set.getString(hwidFieldHWDiskSerial);
|
||||
db_hwid.totalMemory = Long.valueOf(set.getString(hwidFieldTotalMemory));
|
||||
db_hwid.macAddr = set.getString(hwidFieldMAC);
|
||||
LogHelper.dev("Compare HWID: %s vs %s", hwid.getSerializeString(), db_hwid.getSerializeString());
|
||||
if (LogHelper.isDevEnabled()) {
|
||||
LogHelper.dev("Compare HWID: %s vs %s", hwid.getSerializeString(), db_hwid.getSerializeString());
|
||||
}
|
||||
int compare_point = hwid.compare(db_hwid);
|
||||
if (compare_point < compare) continue;
|
||||
else {
|
||||
LogHelper.debug("User %s hwid check: found compare %d in %d", username, compare_point, set.getInt("id"));
|
||||
if (LogHelper.isDevEnabled()) {
|
||||
LogHelper.debug("User %s hwid check: found compare %d in %d", username, compare_point, set.getInt("id"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oneCompareMode) isOne = true;
|
||||
|
@ -175,7 +179,9 @@ public void onCheckInfo(OshiHWID hwid, String username, Connection c) throws HWI
|
|||
}
|
||||
|
||||
public void setIsBanned(HWID hwid, boolean isBanned) {
|
||||
LogHelper.debug("%s Request HWID: %s", isBanned ? "Ban" : "UnBan", hwid.toString());
|
||||
if (LogHelper.isDebugEnabled()) {
|
||||
LogHelper.debug("%s Request HWID: %s", isBanned ? "Ban" : "UnBan", hwid.toString());
|
||||
}
|
||||
if (hwid instanceof OshiHWID) {
|
||||
OshiHWID oshiHWID = (OshiHWID) hwid;
|
||||
try (Connection c = mySQLHolder.getConnection()) {
|
||||
|
@ -214,7 +220,9 @@ public void unban(List<HWID> list) {
|
|||
public List<HWID> getHwid(String username) {
|
||||
ArrayList<HWID> list = new ArrayList<>();
|
||||
try (Connection c = mySQLHolder.getConnection()) {
|
||||
LogHelper.debug("Try find HWID from username %s", username);
|
||||
if (LogHelper.isDebugEnabled()) {
|
||||
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));
|
||||
s.setString(1, username);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ public static void registerProviders() {
|
|||
providers.register("accept", AcceptAuthProvider.class);
|
||||
providers.register("reject", RejectAuthProvider.class);
|
||||
providers.register("mysql", MySQLAuthProvider.class);
|
||||
providers.register("postgresql", PostgreSQLAuthProvider.class);
|
||||
providers.register("request", RequestAuthProvider.class);
|
||||
providers.register("json", JsonAuthProvider.class);
|
||||
providers.register("hibernate", HibernateAuthProvider.class);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -20,7 +20,9 @@ public RequestTextureProvider(String skinURL, String cloakURL) {
|
|||
}
|
||||
|
||||
private static Texture getTexture(String url, boolean cloak) throws IOException {
|
||||
LogHelper.debug("Getting texture: '%s'", url);
|
||||
if (LogHelper.isDebugEnabled()) {
|
||||
LogHelper.debug("Getting texture: '%s'", url);
|
||||
}
|
||||
try {
|
||||
return new Texture(url, cloak);
|
||||
} catch (FileNotFoundException ignored) {
|
||||
|
|
|
@ -1,109 +1,100 @@
|
|||
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 javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtConstructor;
|
||||
import javassist.CtMethod;
|
||||
import javassist.NotFoundException;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import pro.gravit.launcher.AutogenConfig;
|
||||
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 ClassPool pool;
|
||||
public CtClass ctClass;
|
||||
public CtConstructor ctConstructor;
|
||||
public CtMethod initModuleMethod;
|
||||
String classname;
|
||||
StringBuilder body;
|
||||
StringBuilder moduleBody;
|
||||
int autoincrement;
|
||||
public class JAConfigurator {
|
||||
private static final String modulesManagerName = Type.getInternalName(ModulesManager.class);
|
||||
private static final String registerModDesc = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Module.class));
|
||||
private static final String autoGenConfigName = Type.getInternalName(AutogenConfig.class);
|
||||
private static final String stringName = Type.getInternalName(String.class);
|
||||
private final ClassNode configclass;
|
||||
private final MethodNode constructor;
|
||||
private final MethodNode initModuleMethod;
|
||||
|
||||
public JAConfigurator(String configclass, MainBuildTask jarLauncherBinary) throws NotFoundException {
|
||||
pool = new ClassPool(false);
|
||||
pool.appendSystemPath();
|
||||
classname = configclass;
|
||||
ctClass = pool.get(classname);
|
||||
ctConstructor = ctClass.getDeclaredConstructor(null);
|
||||
initModuleMethod = ctClass.getDeclaredMethod("initModules");
|
||||
body = new StringBuilder("{ isInitModules = false; ");
|
||||
moduleBody = new StringBuilder("{ isInitModules = true; ");
|
||||
autoincrement = 0;
|
||||
public JAConfigurator(ClassNode configclass) {
|
||||
this.configclass = configclass;
|
||||
constructor = configclass.methods.stream().filter(e -> "<init>".equals(e.name)).findFirst().get();
|
||||
constructor.instructions = new InsnList();
|
||||
initModuleMethod = configclass.methods.stream().filter(e -> "initModules".equals(e.name)).findFirst().get();
|
||||
initModuleMethod.instructions = new InsnList();
|
||||
}
|
||||
|
||||
public void addModuleClass(String fullName) {
|
||||
moduleBody.append("pro.gravit.launcher.modules.Module mod");
|
||||
moduleBody.append(autoincrement);
|
||||
moduleBody.append(" = new ");
|
||||
moduleBody.append(fullName);
|
||||
moduleBody.append("();");
|
||||
moduleBody.append("pro.gravit.launcher.Launcher.modulesManager.registerModule( mod");
|
||||
moduleBody.append(autoincrement);
|
||||
moduleBody.append(");");
|
||||
autoincrement++;
|
||||
initModuleMethod.instructions.insert(new MethodInsnNode(Opcodes.INVOKEINTERFACE, modulesManagerName, "registerModule", registerModDesc));
|
||||
initModuleMethod.instructions.insert(new MethodInsnNode(Opcodes.INVOKESPECIAL, fullName.replace('.', '/'), "<init>", "()V"));
|
||||
initModuleMethod.instructions.insert(new TypeInsnNode(Opcodes.NEW, fullName.replace('.', '/')));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
ctClass.defrost();
|
||||
}
|
||||
|
||||
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 byte[] getBytecode(ClassMetadataReader reader) {
|
||||
ClassWriter cw = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
|
||||
configclass.accept(cw);
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
public String getZipEntryPath() {
|
||||
return classname.replace('.', '/').concat(".class");
|
||||
return configclass.name.concat(".class");
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
body.append("this.address = \"");
|
||||
body.append(address);
|
||||
body.append("\";");
|
||||
constructor.instructions.add(new LdcInsnNode(address));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "address", stringName));
|
||||
}
|
||||
|
||||
public void setProjectName(String name) {
|
||||
body.append("this.projectname = \"");
|
||||
body.append(name);
|
||||
body.append("\";");
|
||||
constructor.instructions.add(new LdcInsnNode(name));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "projectname", stringName));
|
||||
}
|
||||
|
||||
public void setSecretKey(String key) {
|
||||
body.append("this.secretKeyClient = \"");
|
||||
body.append(key);
|
||||
body.append("\";");
|
||||
constructor.instructions.add(new LdcInsnNode(key));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "secretKeyClient", stringName));
|
||||
}
|
||||
|
||||
public void setOemUnlockKey(String key) {
|
||||
body.append("this.oemUnlockKey = \"");
|
||||
body.append(key);
|
||||
body.append("\";");
|
||||
constructor.instructions.add(new LdcInsnNode(key));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "oemUnlockKey", stringName));
|
||||
|
||||
}
|
||||
|
||||
public void setGuardType(String key) {
|
||||
body.append("this.guardType = \"");
|
||||
body.append(key);
|
||||
body.append("\";");
|
||||
constructor.instructions.add(new LdcInsnNode(key));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardType", stringName));
|
||||
}
|
||||
|
||||
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) {
|
||||
int i = 2;
|
||||
switch (env) {
|
||||
|
||||
switch (env) {
|
||||
case DEV:
|
||||
i = 0;
|
||||
break;
|
||||
|
@ -117,36 +108,35 @@ public void setEnv(LauncherConfig.LauncherEnvironment env) {
|
|||
i = 3;
|
||||
break;
|
||||
}
|
||||
body.append("this.env = ");
|
||||
body.append(i);
|
||||
body.append(";");
|
||||
push(i);
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "env", Type.INT_TYPE.getInternalName()));
|
||||
}
|
||||
|
||||
public void setClientPort(int port) {
|
||||
body.append("this.clientPort = ");
|
||||
body.append(port);
|
||||
body.append(";");
|
||||
push(port);
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "clientPort", Type.INT_TYPE.getInternalName()));
|
||||
}
|
||||
|
||||
public void setWarningMissArchJava(boolean b) {
|
||||
body.append("this.isWarningMissArchJava = ");
|
||||
body.append(b ? "true" : "false");
|
||||
body.append(";");
|
||||
constructor.instructions.add(new InsnNode(b ? Opcodes.ICONST_1 : Opcodes.ICONST_0));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "isWarningMissArchJava", Type.BOOLEAN_TYPE.getInternalName()));
|
||||
}
|
||||
|
||||
public void setGuardLicense(String name, String key, String encryptKey) {
|
||||
body.append("this.guardLicenseName = \"");
|
||||
body.append(name);
|
||||
body.append("\";");
|
||||
body.append("this.guardLicenseKey = \"");
|
||||
body.append(key);
|
||||
body.append("\";");
|
||||
body.append("this.guardLicenseEncryptKey = \"");
|
||||
body.append(encryptKey);
|
||||
body.append("\";");
|
||||
constructor.instructions.add(new LdcInsnNode(name));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseName", stringName));
|
||||
constructor.instructions.add(new LdcInsnNode(key));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseKey", stringName));
|
||||
constructor.instructions.add(new LdcInsnNode(encryptKey));
|
||||
constructor.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, autoGenConfigName, "guardLicenseEncryptKey", stringName));
|
||||
}
|
||||
|
||||
public ClassPool getPool() {
|
||||
return pool;
|
||||
public void nullGuardLicense() {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,7 @@
|
|||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.binary.tasks.AdditionalFixesApplyTask;
|
||||
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.launchserver.binary.tasks.*;
|
||||
import pro.gravit.utils.helper.CommonHelper;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
@ -51,8 +45,8 @@ public void init() {
|
|||
if (server.config.launcher.attachLibraryBeforeProGuard) tasks.add(new AttachJarsTask(server));
|
||||
tasks.add(new ProGuardBuildTask(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.compress) tasks.add(new CompressBuildTask(server));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -70,12 +64,12 @@ public void build() throws IOException {
|
|||
long time_task_end = System.currentTimeMillis();
|
||||
long time_task = time_task_end - time_this;
|
||||
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();
|
||||
LogHelper.subInfo("Task %s processed from %d millis", task.getName(), time_task);
|
||||
}
|
||||
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);
|
||||
LogHelper.info("Build successful from %d millis", time_end - time_start);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import pro.gravit.launcher.serialize.signed.DigestBytesHolder;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
@ -11,7 +10,7 @@
|
|||
public abstract class LauncherBinary {
|
||||
public final LaunchServer server;
|
||||
public final Path syncBinaryFile;
|
||||
private volatile DigestBytesHolder binary;
|
||||
private volatile byte[] digest;
|
||||
private volatile byte[] sign;
|
||||
|
||||
protected LauncherBinary(LaunchServer server, Path binaryFile) {
|
||||
|
@ -27,8 +26,8 @@ public final boolean exists() {
|
|||
}
|
||||
|
||||
|
||||
public final DigestBytesHolder getBytes() {
|
||||
return binary;
|
||||
public final byte[] getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
public final byte[] getSign() {
|
||||
|
@ -40,7 +39,7 @@ public void init() {
|
|||
|
||||
public final boolean sync() throws IOException {
|
||||
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;
|
||||
|
||||
return exists;
|
||||
|
|
|
@ -44,7 +44,7 @@ public ProguardConf(LaunchServer srv) {
|
|||
public String[] buildConfig(Path inputJar, Path outputJar) {
|
||||
List<String> confStrs = new ArrayList<>();
|
||||
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("-injar \'" + inputJar.toAbsolutePath() + "\'");
|
||||
confStrs.add("-outjar \'" + outputJar.toAbsolutePath() + "\'");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
import pro.gravit.launchserver.asm.ClassMetadataReader;
|
||||
import pro.gravit.launchserver.asm.SafeClassWriter;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
public class AdditionalFixesApplyTask implements LauncherBuildTask {
|
||||
private final LaunchServer server;
|
||||
|
@ -73,7 +74,12 @@ public static void apply(Path inputFile, Path addFile, ZipOutputStream output, L
|
|||
IOHelper.transfer(input, outputStream);
|
||||
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
|
||||
IOHelper.transfer(input, output);
|
||||
e = input.getNextEntry();
|
||||
|
@ -86,7 +92,6 @@ private static byte[] classFix(byte[] bytes, ClassMetadataReader reader, boolean
|
|||
ClassReader cr = new ClassReader(bytes);
|
||||
ClassNode cn = new ClassNode();
|
||||
cr.accept(cn, stripNumbers ? (ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES) : ClassReader.SKIP_FRAMES);
|
||||
|
||||
ClassWriter cw = new SafeClassWriter(reader, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
cn.accept(cw);
|
||||
return cw.toByteArray();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -16,8 +16,9 @@
|
|||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.NotFoundException;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import pro.gravit.launcher.AutogenConfig;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherConfig;
|
||||
|
@ -115,27 +116,22 @@ public String getName() {
|
|||
@Override
|
||||
public Path process(Path inputJar) throws IOException {
|
||||
Path outputJar = server.launcherBinary.nextPath("main");
|
||||
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(outputJar));
|
||||
JAConfigurator jaConfigurator = new JAConfigurator(AutogenConfig.class.getName(), this)) {
|
||||
jaConfigurator.pool.insertClassPath(inputJar.toFile().getAbsolutePath());
|
||||
server.launcherBinary.coreLibs.stream().map(e -> e.toFile().getAbsolutePath())
|
||||
.forEach(t -> {
|
||||
try {
|
||||
jaConfigurator.pool.appendClassPath(t);
|
||||
} catch (NotFoundException e2) {
|
||||
LogHelper.error(e2);
|
||||
}
|
||||
});
|
||||
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(outputJar))) {
|
||||
ClassNode cn = new ClassNode();
|
||||
new ClassReader(IOHelper.getResourceBytes(AutogenConfig.class.getName().replace('.', '/').concat(".class"))).accept(cn, 0);
|
||||
JAConfigurator jaConfigurator = new JAConfigurator(cn);
|
||||
BuildContext context = new BuildContext(output, jaConfigurator, this);
|
||||
server.buildHookManager.hook(context);
|
||||
jaConfigurator.setAddress(server.config.netty.address);
|
||||
if (server.config.guardLicense != null)
|
||||
jaConfigurator.setGuardLicense(server.config.guardLicense.name, server.config.guardLicense.key, server.config.guardLicense.encryptKey);
|
||||
else
|
||||
jaConfigurator.nullGuardLicense();
|
||||
jaConfigurator.setProjectName(server.config.projectName);
|
||||
jaConfigurator.setSecretKey(SecurityHelper.randomStringAESKey());
|
||||
jaConfigurator.setClientPort(32148 + SecurityHelper.newRandom().nextInt(512));
|
||||
jaConfigurator.setGuardType(server.config.launcher.guardType);
|
||||
jaConfigurator.setWarningMissArchJava(server.config.isWarningMissArchJava);
|
||||
jaConfigurator.setWarningMissArchJava(server.config.launcher.warningMissArchJava);
|
||||
jaConfigurator.setEnv(server.config.env);
|
||||
if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken();
|
||||
jaConfigurator.setOemUnlockKey(server.runtime.oemUnlockKey);
|
||||
|
@ -148,11 +144,12 @@ public Path process(Path inputJar) throws IOException {
|
|||
LogHelper.error(e1);
|
||||
}
|
||||
});
|
||||
String zPath = jaConfigurator.getZipEntryPath();
|
||||
try (ZipInputStream input = new ZipInputStream(IOHelper.newInput(inputJar))) {
|
||||
ZipEntry e = input.getNextEntry();
|
||||
while (e != null) {
|
||||
String filename = e.getName();
|
||||
if (server.buildHookManager.isContainsBlacklist(filename) || e.isDirectory()) {
|
||||
if (server.buildHookManager.isContainsBlacklist(filename) || e.isDirectory() || zPath.equals(filename)) {
|
||||
e = input.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
|
@ -204,12 +201,9 @@ public Path process(Path inputJar) throws IOException {
|
|||
// Write launcher config file
|
||||
output.putNextEntry(newZipEntry(Launcher.CONFIG_FILE));
|
||||
output.write(launcherConfigBytes);
|
||||
ZipEntry e = newZipEntry(jaConfigurator.getZipEntryPath());
|
||||
ZipEntry e = newZipEntry(zPath);
|
||||
output.putNextEntry(e);
|
||||
jaConfigurator.compile();
|
||||
output.write(jaConfigurator.getBytecode());
|
||||
} catch (CannotCompileException | NotFoundException e) {
|
||||
throw new IOException(e);
|
||||
output.write(jaConfigurator.getBytecode(reader));
|
||||
}
|
||||
reader.close();
|
||||
return outputJar;
|
||||
|
|
|
@ -26,7 +26,7 @@ public String getName() {
|
|||
@Override
|
||||
public Path process(Path inputFile) throws IOException {
|
||||
Path outputJar = server.launcherBinary.nextLowerPath(this);
|
||||
if (server.config.enabledProGuard) {
|
||||
if (server.config.launcher.enabledProGuard) {
|
||||
Configuration proguard_cfg = new Configuration();
|
||||
ConfigurationParser parser = new ConfigurationParser(server.proguardConf.buildConfig(inputFile, outputJar),
|
||||
server.proguardConf.proguard.toFile(), System.getProperties());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,15 @@
|
|||
package pro.gravit.launchserver.command.basic;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyPair;
|
||||
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import pro.gravit.launcher.events.PingEvent;
|
||||
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.command.Command;
|
||||
import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler;
|
||||
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 TestCommand(LaunchServer 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.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")) {
|
||||
verifyArgs(args, 2);
|
||||
String name = args[1];
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
import java.io.Writer;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -41,7 +42,7 @@ public void invoke(String... args) throws Exception {
|
|||
public void invoke(String... args) throws Exception {
|
||||
verifyArgs(args, 1);
|
||||
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]))) {
|
||||
Launcher.gsonManager.configGson.toJson(clientSet, writer);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.command.Command;
|
||||
import pro.gravit.launchserver.manangers.ReconfigurableManager;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
public class ConfigCommand extends Command {
|
||||
public ConfigCommand(LaunchServer server) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,17 @@
|
|||
package pro.gravit.launchserver.components;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import pro.gravit.launcher.NeedGarbageCollection;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
import pro.gravit.utils.BiHookSet.Hook;
|
||||
import pro.gravit.utils.HookException;
|
||||
|
||||
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;
|
||||
@Override
|
||||
public void preInit(LaunchServer launchServer) {
|
||||
|
@ -21,7 +20,8 @@ public void preInit(LaunchServer launchServer) {
|
|||
|
||||
@Override
|
||||
public void init(LaunchServer launchServer) {
|
||||
launchServer.authHookManager.preHook.registerHook(prA);
|
||||
limiter = new AbstractLimiter<>(rateLimit, rateLimitMilis);
|
||||
launchServer.authHookManager.preHook.registerHook(this::preAuthHook);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,88 +30,25 @@ public void postInit(LaunchServer launchServer) {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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 rateLimitMilis;
|
||||
public String message;
|
||||
|
||||
public transient HashMap<String, AuthEntry> map = new HashMap<>();
|
||||
public List<String> excludeIps = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void garbageCollection() {
|
||||
long time = System.currentTimeMillis();
|
||||
long max_timeout = Math.max(rateLimitMilis, TIMEOUT);
|
||||
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;
|
||||
if(limiter != null)
|
||||
limiter.garbageCollection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
srv.authHookManager.preHook.unregisterHook(prA);
|
||||
srv.authHookManager.preHook.unregisterHook(this::preAuthHook);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,26 @@
|
|||
package pro.gravit.launchserver.components;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import pro.gravit.launcher.NeedGarbageCollection;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.manangers.hook.AuthHookManager;
|
||||
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 static final long TIMEOUT = 12 * 60 * 60 * 1000; //12 часов
|
||||
private transient AbstractLimiter<String> limiter;
|
||||
public transient LaunchServer launchServer;
|
||||
public int rateLimit;
|
||||
public int rateLimitMilis;
|
||||
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<>();
|
||||
|
||||
@Override
|
||||
public void preInit(LaunchServer launchServer) {
|
||||
limiter = new AbstractLimiter<>(rateLimit, rateLimitMilis);
|
||||
this.launchServer = launchServer;
|
||||
}
|
||||
|
||||
|
@ -34,43 +31,24 @@ public void init(LaunchServer launchServer) {
|
|||
|
||||
@Override
|
||||
public void postInit(LaunchServer launchServer) {
|
||||
launchServer.authHookManager.registraion.registerHook(context -> {
|
||||
if (isLimit(context.ip)) {
|
||||
throw new HookException(message);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
launchServer.authHookManager.registraion.registerHook(this::registerHook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void garbageCollection() {
|
||||
long time = System.currentTimeMillis();
|
||||
long max_timeout = Math.max(rateLimitMilis, TIMEOUT);
|
||||
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;
|
||||
public boolean registerHook(AuthHookManager.RegContext context)
|
||||
{
|
||||
if (!limiter.check(context.ip)) {
|
||||
throw new HookException(message);
|
||||
}
|
||||
map.put(ip, new AuthLimiterComponent.AuthEntry(1, System.currentTimeMillis()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void garbageCollection() {
|
||||
if(limiter != null)
|
||||
limiter.garbageCollection();
|
||||
}
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if(hook != null)
|
||||
launchServer.authHookManager.registraion.unregisterHook(hook);
|
||||
launchServer.authHookManager.registraion.unregisterHook(this::registerHook);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,16 @@
|
|||
import java.util.Collection;
|
||||
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.utils.helper.LogHelper;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package pro.gravit.launchserver.dao;
|
||||
|
||||
import pro.gravit.launcher.hwid.OshiHWID;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import pro.gravit.launcher.hwid.OshiHWID;
|
||||
|
||||
public interface UserDAO {
|
||||
User findById(int id);
|
||||
User findByUsername(String username);
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
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.OshiHWID;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users_hwids")
|
||||
public class UserHWID implements HWID {
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
|
||||
public class UserService {
|
||||
|
||||
private final UserDAO usersDao;
|
||||
|
|
|
@ -1,38 +1,18 @@
|
|||
package pro.gravit.launchserver.manangers;
|
||||
|
||||
|
||||
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.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.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.Path;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
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.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
|
@ -41,16 +21,45 @@
|
|||
import java.time.temporal.ChronoUnit;
|
||||
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 X509CertificateHolder ca;
|
||||
public AsymmetricKeyParameter caKey;
|
||||
|
||||
public X509CertificateHolder server;
|
||||
public AsymmetricKeyParameter serverKey;
|
||||
|
||||
|
||||
//public X509CertificateHolder server;
|
||||
//public AsymmetricKeyParameter serverKey;
|
||||
|
||||
public int validDays = 60;
|
||||
public int minusHours = 6;
|
||||
|
||||
public String orgName;
|
||||
|
||||
public X509CertificateHolder generateCertificate(String subjectName, PublicKey subjectPublicKey) throws OperatorCreationException {
|
||||
SubjectPublicKeyInfo subjectPubKeyInfo = SubjectPublicKeyInfo.getInstance(subjectPublicKey.getEncoded());
|
||||
BigInteger serial = BigInteger.valueOf(SecurityHelper.newRandom().nextLong());
|
||||
|
@ -59,6 +68,7 @@ public X509CertificateHolder generateCertificate(String subjectName, PublicKey s
|
|||
|
||||
X500NameBuilder subject = new X500NameBuilder();
|
||||
subject.addRDN(BCStyle.CN, subjectName);
|
||||
subject.addRDN(BCStyle.O, orgName);
|
||||
X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(ca.getSubject(), serial,
|
||||
startDate, endDate, subject.build(), subjectPubKeyInfo);
|
||||
|
||||
|
@ -76,8 +86,12 @@ public void generateCA() throws NoSuchAlgorithmException, IOException, OperatorC
|
|||
KeyPair pair = generator.generateKeyPair();
|
||||
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(
|
||||
new X500Name("CN=ca"),
|
||||
subject.build(),
|
||||
new BigInteger("0"),
|
||||
Date.from(startDate.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 {
|
||||
try (PemWriter writer = new PemWriter(IOHelper.newWriter(file))) {
|
||||
writer.writeObject(new PemObject("PRIVATE KEY", privateKey.getEncoded()));
|
||||
writePrivateKey(IOHelper.newWriter(file), privateKey);
|
||||
}
|
||||
|
||||
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 {
|
||||
writePrivateKey(IOHelper.newWriter(file), key);
|
||||
}
|
||||
|
||||
public void writePrivateKey(Writer writer, AsymmetricKeyParameter key) throws IOException {
|
||||
PrivateKeyInfo info = PrivateKeyInfoFactory.createPrivateKeyInfo(key);
|
||||
try (PemWriter writer = new PemWriter(IOHelper.newWriter(file))) {
|
||||
writer.writeObject(new PemObject("PRIVATE KEY", info.getEncoded()));
|
||||
try (PemWriter writer1 = new PemWriter(writer)) {
|
||||
writer1.writeObject(new PemObject("PRIVATE KEY", info.getEncoded()));
|
||||
}
|
||||
}
|
||||
|
||||
public void writeCertificate(Path file, X509CertificateHolder holder) throws IOException {
|
||||
try (PemWriter writer = new PemWriter(IOHelper.newWriter(file))) {
|
||||
writer.writeObject(new PemObject("CERTIFICATE", holder.toASN1Structure().getEncoded()));
|
||||
writeCertificate(IOHelper.newWriter(file), holder);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,11 @@
|
|||
|
||||
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.HWIDProvider;
|
||||
import pro.gravit.launcher.managers.GsonManager;
|
||||
import pro.gravit.launcher.request.JsonResultSerializeAdapter;
|
||||
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.hwid.HWIDHandler;
|
||||
import pro.gravit.launchserver.auth.permissions.PermissionsHandler;
|
||||
|
|
|
@ -2,13 +2,11 @@
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import pro.gravit.launchserver.Reconfigurable;
|
||||
import pro.gravit.utils.command.Command;
|
||||
import pro.gravit.utils.command.CommandException;
|
||||
import pro.gravit.utils.command.basic.HelpCommand;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.VerifyHelper;
|
||||
|
||||
public class ReconfigurableManager {
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package pro.gravit.launchserver.manangers;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import pro.gravit.launcher.NeedGarbageCollection;
|
||||
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 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) {
|
||||
clientSet.add(client);
|
||||
clientSet.put(client.session, client);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void garbageCollection() {
|
||||
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) {
|
||||
for (Client c : clientSet)
|
||||
if (c.session == session) return c;
|
||||
return null;
|
||||
return clientSet.get(session);
|
||||
}
|
||||
|
||||
|
||||
public Client getOrNewClient(long session) {
|
||||
for (Client c : clientSet)
|
||||
if (c.session == session) return c;
|
||||
Client newClient = new Client(session);
|
||||
clientSet.add(newClient);
|
||||
return newClient;
|
||||
return clientSet.computeIfAbsent(session, Client::new);
|
||||
}
|
||||
|
||||
|
||||
public void updateClient(long session) {
|
||||
for (Client c : clientSet) {
|
||||
if (c.session == session) {
|
||||
c.up();
|
||||
return;
|
||||
}
|
||||
Client c = clientSet.get(session);
|
||||
if (c != null) {
|
||||
c.up();
|
||||
return;
|
||||
}
|
||||
Client newClient = new Client(session);
|
||||
clientSet.add(newClient);
|
||||
clientSet.put(session, newClient);
|
||||
}
|
||||
|
||||
public Set<Client> getSessions() {
|
||||
return clientSet;
|
||||
// TODO: removeme
|
||||
return new HashSet<>(clientSet.values());
|
||||
}
|
||||
|
||||
public void loadSessions(Set<Client> set) {
|
||||
clientSet.addAll(set);
|
||||
clientSet.putAll(set.stream().collect(Collectors.toMap(c -> c.session, Function.identity())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,14 @@ public void registerClientModuleClass(String 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) {
|
||||
CLASS_BLACKLIST.add(clazz);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
public class Client {
|
||||
public long session;
|
||||
public boolean proxy;
|
||||
public String auth_id;
|
||||
public long timestamp;
|
||||
public Type type;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.epoll.Epoll;
|
||||
import io.netty.channel.group.DefaultChannelGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
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.logging.LoggingHandler;
|
||||
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.socket.handlers.NettyIpForwardHandler;
|
||||
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
|
||||
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;
|
||||
|
||||
public class LauncherNettyServer implements AutoCloseable {
|
||||
|
@ -30,6 +28,7 @@ public class LauncherNettyServer implements AutoCloseable {
|
|||
public final EventLoopGroup bossGroup;
|
||||
public final EventLoopGroup workerGroup;
|
||||
public final WebSocketService service;
|
||||
public final BiHookSet<NettyConnectContext,SocketChannel> pipelineHook = new BiHookSet<>();
|
||||
private static final String WEBSOCKET_PATH = "/api";
|
||||
|
||||
public LauncherNettyServer(LaunchServer server) {
|
||||
|
@ -39,16 +38,16 @@ public LauncherNettyServer(LaunchServer server) {
|
|||
{
|
||||
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);
|
||||
workerGroup = NettyObjectFactory.newEventLoopGroup(config.performance.workerThread);
|
||||
serverBootstrap = new ServerBootstrap();
|
||||
service = new WebSocketService(new DefaultChannelGroup(GlobalEventExecutor.INSTANCE), server);
|
||||
serverBootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NettyObjectFactory.getServerSocketChannelClass())
|
||||
.channelFactory(NettyObjectFactory.getServerSocketChannelFactory())
|
||||
.handler(new LoggingHandler(config.logLevel))
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
|
@ -65,19 +64,9 @@ public void initChannel(SocketChannel ch) {
|
|||
if (server.config.netty.fileServerEnabled)
|
||||
pipeline.addLast(new FileServerHandler(server.updatesDir, true));
|
||||
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) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package pro.gravit.launchserver.socket;
|
||||
|
||||
import io.netty.channel.ChannelFactory;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.ServerChannel;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
|
@ -20,12 +21,12 @@ public static EventLoopGroup newEventLoopGroup(int threads)
|
|||
else
|
||||
return new NioEventLoopGroup(threads);
|
||||
}
|
||||
public static Class<? extends ServerChannel> getServerSocketChannelClass()
|
||||
public static ChannelFactory<? extends ServerChannel> getServerSocketChannelFactory()
|
||||
{
|
||||
if(epoll)
|
||||
return EpollServerSocketChannel.class;
|
||||
return EpollServerSocketChannel::new;
|
||||
else
|
||||
return NioServerSocketChannel.class;
|
||||
return NioServerSocketChannel::new;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
|
@ -11,23 +9,26 @@
|
|||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.group.ChannelGroup;
|
||||
import io.netty.channel.group.ChannelMatchers;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.events.ExceptionEvent;
|
||||
import pro.gravit.launcher.events.RequestEvent;
|
||||
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||
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.admin.ProxyRequest;
|
||||
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.WebSocketServerResponse;
|
||||
import pro.gravit.launchserver.socket.response.admin.AddLogListenerResponse;
|
||||
import pro.gravit.launchserver.socket.response.admin.ExecCommandResponse;
|
||||
import pro.gravit.launchserver.socket.response.admin.ProxyCommandResponse;
|
||||
import pro.gravit.launchserver.socket.response.auth.*;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
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.ProfileByUUIDResponse;
|
||||
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.UpdateListResponse;
|
||||
import pro.gravit.launchserver.socket.response.update.UpdateResponse;
|
||||
import pro.gravit.utils.BiHookSet;
|
||||
import pro.gravit.utils.ProviderMap;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
@ -44,6 +46,19 @@
|
|||
public class WebSocketService {
|
||||
public final ChannelGroup channels;
|
||||
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) {
|
||||
this.channels = channels;
|
||||
|
@ -55,68 +70,20 @@ public WebSocketService(ChannelGroup channels, LaunchServer server) {
|
|||
}
|
||||
|
||||
private final LaunchServer server;
|
||||
private static final HashMap<String, Class> responses = new HashMap<>();
|
||||
private final Gson gson;
|
||||
|
||||
public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) {
|
||||
String request = frame.text();
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
SimpleResponse simpleResponse = (SimpleResponse) response;
|
||||
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) {
|
||||
channels.add(channel);
|
||||
}
|
||||
|
@ -168,27 +131,26 @@ public static void registerResponses() {
|
|||
providers.register("getSecureToken", GetSecureTokenResponse.class);
|
||||
providers.register("verifySecureToken", VerifySecureTokenResponse.class);
|
||||
providers.register("getAvailabilityAuth", GetAvailabilityAuthResponse.class);
|
||||
providers.register("proxy", ProxyCommandResponse.class);
|
||||
providers.register("register", RegisterResponse.class);
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type)));
|
||||
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type)), ctx.voidPromise());
|
||||
}
|
||||
|
||||
public void sendObjectAll(Object obj) {
|
||||
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) {
|
||||
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) {
|
||||
channels.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj)));
|
||||
channels.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj)), ChannelMatchers.all(), true);
|
||||
}
|
||||
|
||||
public static class EventResult implements WebSocketEvent {
|
||||
|
|
|
@ -36,7 +36,9 @@ protected void decode(ChannelHandlerContext ctx, HttpRequest msg, List<Object> o
|
|||
realIP = headers.get("X-Real-IP");
|
||||
}
|
||||
if (realIP != null) {
|
||||
LogHelper.dev("Real IP address %s", realIP);
|
||||
if (LogHelper.isDevEnabled()) {
|
||||
LogHelper.dev("Real IP address %s", realIP);
|
||||
}
|
||||
context.ip = realIP;
|
||||
} else LogHelper.error("IpForwarding error. Headers not found");
|
||||
out.add(msg);
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
import pro.gravit.launchserver.socket.LauncherNettyServer;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
@SuppressWarnings({"unused", "rawtypes"})
|
||||
@SuppressWarnings("unused")
|
||||
public final class NettyServerSocketHandler implements Runnable, AutoCloseable {
|
||||
private SSLServerSocketFactory ssf;
|
||||
|
||||
|
|
|
@ -2,22 +2,18 @@
|
|||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
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.PingWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.NettyConnectContext;
|
||||
import pro.gravit.launchserver.socket.WebSocketService;
|
||||
import pro.gravit.utils.helper.CommonHelper;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
|
@ -45,11 +41,14 @@ public void setClient(Client client) {
|
|||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) {
|
||||
LogHelper.dev("New client %s", IOHelper.getIP(ctx.channel().remoteAddress()));
|
||||
if (LogHelper.isDevEnabled()) {
|
||||
LogHelper.dev("New client %s", IOHelper.getIP(ctx.channel().remoteAddress()));
|
||||
}
|
||||
client = new Client(0);
|
||||
service.registerClient(ctx.channel());
|
||||
Channel ch = ctx.channel();
|
||||
service.registerClient(ch);
|
||||
ctx.executor().schedule(() -> {
|
||||
ctx.channel().writeAndFlush(new PingWebSocketFrame());
|
||||
ch.writeAndFlush(new PingWebSocketFrame(), ch.voidPromise());
|
||||
}, 30L, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
@ -77,7 +77,15 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr
|
|||
}
|
||||
|
||||
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) {
|
||||
sendError(ctx, FORBIDDEN);
|
||||
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 void sendListing(ChannelHandlerContext ctx, File dir, String dirPath) {
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -45,12 +45,11 @@ public AuthResponse(String login, String password, String auth_id, OshiHWID hwid
|
|||
}
|
||||
|
||||
public String auth_id;
|
||||
public boolean initProxy;
|
||||
public ConnectTypes authType;
|
||||
public HWID hwid;
|
||||
|
||||
public enum ConnectTypes {
|
||||
SERVER, CLIENT, BOT
|
||||
SERVER, CLIENT, API
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,11 +110,9 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
|||
clientData.username = login;
|
||||
result.accessToken = aresult.accessToken;
|
||||
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) {
|
||||
AuthProvider.authError("authType: SERVER not allowed for this account");
|
||||
return;
|
||||
}
|
||||
if (getSession) {
|
||||
if (clientData.session == 0) {
|
||||
|
@ -124,14 +121,12 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
|||
}
|
||||
result.session = clientData.session;
|
||||
}
|
||||
if (initProxy) {
|
||||
if (!clientData.permissions.canProxy) throw new AuthException("initProxy not allow");
|
||||
clientData.proxy = true;
|
||||
}
|
||||
if (server.config.protectHandler.allowGetAccessToken(context)) {
|
||||
if (authType != ConnectTypes.API && server.config.protectHandler.allowGetAccessToken(context)) {
|
||||
UUID uuid = pair.handler.auth(aresult);
|
||||
result.playerProfile = ProfileByUUIDResponse.getProfile(server, uuid, aresult.username, client, clientData.auth.textureProvider);
|
||||
LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, uuid.toString());
|
||||
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());
|
||||
}
|
||||
}
|
||||
sendResult(result);
|
||||
} catch (AuthException | HWIDException | HookException e) {
|
||||
|
@ -140,10 +135,10 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
|||
}
|
||||
|
||||
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.login = login;
|
||||
this.password_lenght = password_lenght;
|
||||
this.password_length = password_length;
|
||||
this.customText = customText;
|
||||
this.client = client;
|
||||
this.hwid = hwid;
|
||||
|
@ -153,7 +148,7 @@ public AuthContext(long session, String login, int password_lenght, String custo
|
|||
|
||||
public long session;
|
||||
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 hwid;
|
||||
public String customText;
|
||||
|
|
|
@ -26,8 +26,10 @@ public void execute(ChannelHandlerContext ctx, Client pClient) {
|
|||
server.authHookManager.checkServerHook.hook(this, pClient);
|
||||
result.uuid = pClient.auth.handler.checkServer(username, serverID);
|
||||
if (result.uuid != null)
|
||||
result.playerProfile = ProfileByUUIDResponse.getProfile(server, result.uuid, username, client, pClient.auth.textureProvider);
|
||||
LogHelper.debug("checkServer: %s uuid: %s serverID: %s", result.playerProfile.username, result.uuid.toString(), serverID);
|
||||
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);
|
||||
}
|
||||
} catch (AuthException | HookException e) {
|
||||
sendError(e.getMessage());
|
||||
return;
|
||||
|
|
|
@ -27,7 +27,9 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
LogHelper.warning("Client auth is null. Using default.");
|
||||
success = server.config.getAuthProviderPair().handler.joinServer(username, accessToken, serverID);
|
||||
} else success = client.auth.handler.joinServer(username, accessToken, serverID);
|
||||
LogHelper.debug("joinServer: %s accessToken: %s serverID: %s", username, accessToken, serverID);
|
||||
if (LogHelper.isDebugEnabled()) {
|
||||
LogHelper.debug("joinServer: %s accessToken: %s serverID: %s", username, accessToken, serverID);
|
||||
}
|
||||
} catch (AuthException | HookException e) {
|
||||
sendError(e.getMessage());
|
||||
return;
|
||||
|
|
|
@ -32,7 +32,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
|||
LogHelper.warning("Client auth is null. Using default.");
|
||||
uuid = server.config.getAuthProviderPair().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);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import pro.gravit.launcher.events.request.ProfileByUUIDRequestEvent;
|
||||
import pro.gravit.launcher.profiles.PlayerProfile;
|
||||
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.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.SimpleResponse;
|
||||
|
@ -17,7 +17,7 @@ public class ProfileByUUIDResponse extends SimpleResponse {
|
|||
public UUID uuid;
|
||||
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
|
||||
Texture skin;
|
||||
try {
|
||||
|
@ -48,10 +48,19 @@ public String getType() {
|
|||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
String username;
|
||||
AuthProviderPair pair;
|
||||
if (client.auth == null) {
|
||||
LogHelper.warning("Client auth is null. Using default.");
|
||||
username = server.config.getAuthProviderPair().handler.uuidToUsername(uuid);
|
||||
} else username = client.auth.handler.uuidToUsername(uuid);
|
||||
sendResult(new ProfileByUUIDRequestEvent(getProfile(server, uuid, username, this.client, client.auth.textureProvider)));
|
||||
pair = server.config.getAuthProviderPair();
|
||||
} else {
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,6 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
|||
LogHelper.warning("Client auth is null. Using default.");
|
||||
uuid = server.config.getAuthProviderPair().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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
bytes = digest;
|
||||
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 (Arrays.equals(bytes, hash)) {
|
||||
client.checkSign = true;
|
||||
|
@ -39,7 +39,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
}
|
||||
} 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 (Arrays.equals(bytes, hash)) {
|
||||
client.checkSign = true;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import io.netty.channel.ChannelHandlerContext;
|
||||
import pro.gravit.launcher.events.request.UpdateListRequestEvent;
|
||||
import pro.gravit.launcher.hasher.HashedDir;
|
||||
import pro.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.SimpleResponse;
|
||||
|
||||
|
@ -24,7 +23,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
return;
|
||||
}
|
||||
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());
|
||||
sendResult(new UpdateListRequestEvent(set));
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import pro.gravit.launcher.events.request.UpdateRequestEvent;
|
||||
import pro.gravit.launcher.hasher.HashedDir;
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
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) {
|
||||
service.sendObject(ctx, new ErrorRequestEvent(String.format("Directory %s not found", dirName)));
|
||||
return;
|
||||
|
@ -46,6 +45,6 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
url = bind.url;
|
||||
zip = bind.zip;
|
||||
}
|
||||
service.sendObject(ctx, new UpdateRequestEvent(dir.object, url, zip));
|
||||
service.sendObject(ctx, new UpdateRequestEvent(dir, url, zip));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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
|
|
@ -39,7 +39,6 @@
|
|||
dependencies {
|
||||
pack project(':LauncherAuthlib')
|
||||
bundle 'com.github.oshi:oshi-core:3.13.0'
|
||||
bundle 'de.jensd:fontawesomefx:8.9'
|
||||
bundle 'org.apache.httpcomponents:httpclient:4.5.7'
|
||||
pack 'io.netty:netty-codec-http:4.1.36.Final'
|
||||
pack 'org.ow2.asm:asm-tree:7.1'
|
||||
|
@ -58,3 +57,33 @@ task dumpLibs(type: Copy) {
|
|||
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,27 +1,31 @@
|
|||
// ====== LAUNCHER CONFIG ====== //
|
||||
var config = {
|
||||
dir: "GravitLauncher", // Launcher directory
|
||||
title: "GravitLauncher", // Window title
|
||||
icons: [ "favicon.png" ], // Window icon paths
|
||||
//*** Настройки лаунчера ***//
|
||||
dir: "GravitLauncher", // Название папки лаунчера
|
||||
title: "GravitLauncher", // Заголовок окна
|
||||
icons: ["favicon.png"], // Путь/Пути до иконки окна
|
||||
|
||||
// Auth config
|
||||
linkText: "GravitLauncher", // Text for link under "Auth" button
|
||||
linkURL: new java.net.URL("https://gravitlauncher.ml"), // URL for link under "Auth" button
|
||||
//*** Меню авторизации ***//
|
||||
linkText: "GravitLauncher", // Текст ссылки
|
||||
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, // Should autoEnter be enabled by default?
|
||||
fullScreenDefault: false, // Should fullScreen be enabled by default?
|
||||
ramDefault: 1024, // Default RAM amount (0 for auto)
|
||||
//*** Стандартные настройки клиента ***//
|
||||
autoEnterDefault: false, // Автоматический вход на выбранный сервер
|
||||
fullScreenDefault: false, // Клиент в полный экран
|
||||
featureStoreDefault: true, // Поиск файлов в других клиентах (Используется для экономии трафика и ускорения загрузки)
|
||||
ramDefault: 1024, // Количество оперативной памяти выделенной по умолчанию (0 - Автоматически)
|
||||
|
||||
//*** Настройка загрузки JVM ***//
|
||||
/* LaunchServer: guardtype = java */
|
||||
jvm: {
|
||||
enable: false,
|
||||
jvmMustdie32Dir: "jre-8u202-win32",
|
||||
jvmMustdie64Dir: "jre-8u202-win64",
|
||||
}
|
||||
enable: false, // Включение загрузки своей JVM
|
||||
jvmMustdie32Dir: "jre-8u211-win32", // Название папки JVM для Windows x32
|
||||
jvmMustdie64Dir: "jre-8u211-win64", // Название папки JVM для Windows x64
|
||||
},
|
||||
|
||||
settingsMagic: 0xC0DE5, // Магия вне хогвартса
|
||||
};
|
||||
|
||||
DirBridge.dir = DirBridge.getLauncherDir(config.dir);
|
||||
|
|
|
@ -24,9 +24,9 @@ function initLauncher() {
|
|||
|
||||
/* ======== init Login window======== */
|
||||
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) {
|
||||
if(movePoint === null) {
|
||||
if (movePoint === null) {
|
||||
return;
|
||||
}
|
||||
stage.setX(event.getScreenX() - movePoint.getX());
|
||||
|
@ -35,9 +35,9 @@ function initLoginScene() {
|
|||
|
||||
var pane = loginPane.lookup("#bar");
|
||||
bar = pane;
|
||||
loginPane.lookup("#close").setOnAction(function(event){ javafx.application.Platform.exit()});
|
||||
loginPane.lookup("#hide").setOnAction(function(event){ stage.setIconified(true)});
|
||||
loginPane.lookup("#discord").setOnAction(function(){ openURL(config.discord); });
|
||||
loginPane.lookup("#close").setOnAction(function(event) { javafx.application.Platform.exit() });
|
||||
loginPane.lookup("#hide").setOnAction(function(event) { stage.setIconified(true) });
|
||||
loginPane.lookup("#discord").setOnAction(function() { openURL(config.discord); });
|
||||
|
||||
var pane = loginPane.lookup("#authPane");
|
||||
authPane = pane;
|
||||
|
@ -46,14 +46,14 @@ function initLoginScene() {
|
|||
loginPaneLayout = loginLayout;
|
||||
|
||||
loginField = pane.lookup("#login");
|
||||
loginField.setOnMouseMoved(function(event){rootPane.fireEvent(event)});
|
||||
loginField.setOnMouseMoved(function(event) { rootPane.fireEvent(event) });
|
||||
loginField.setOnAction(goAuth);
|
||||
if (settings.login !== null) {
|
||||
loginField.setText(settings.login);
|
||||
}
|
||||
|
||||
passwordField = pane.lookup("#password");
|
||||
passwordField.setOnMouseMoved(function(event){rootPane.fireEvent(event)});
|
||||
passwordField.setOnMouseMoved(function(event) { rootPane.fireEvent(event) });
|
||||
passwordField.setOnAction(goAuth);
|
||||
if (settings.rsaPassword !== null) {
|
||||
passwordField.getStyleClass().add("hasSaved");
|
||||
|
@ -74,9 +74,9 @@ function initLoginScene() {
|
|||
|
||||
/* ======== init Menu window======== */
|
||||
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) {
|
||||
if(movePoint === null) {
|
||||
if (movePoint === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -86,9 +86,9 @@ function initMenuScene() {
|
|||
|
||||
var pane = menuPane.lookup("#bar");
|
||||
bar = pane;
|
||||
pane.lookup("#close").setOnAction(function(event){ javafx.application.Platform.exit()});
|
||||
pane.lookup("#hide").setOnAction(function(event){ stage.setIconified(true)});
|
||||
pane.lookup("#discord").setOnAction(function(){ openURL(config.discord); });
|
||||
pane.lookup("#close").setOnAction(function(event) { javafx.application.Platform.exit() });
|
||||
pane.lookup("#hide").setOnAction(function(event) { stage.setIconified(true) });
|
||||
pane.lookup("#discord").setOnAction(function() { openURL(config.discord); });
|
||||
pane.lookup("#settings").setOnAction(goSettings);
|
||||
pane.lookup("#goConsole").setOnAction(goConsole);
|
||||
|
||||
|
@ -105,10 +105,10 @@ function initMenuScene() {
|
|||
serverEntrance = pane.lookup("#serverentrance");
|
||||
serverStatus = serverEntrance.lookup("#serverStatus");
|
||||
serverLabel = serverEntrance.lookup("#serverLabel");
|
||||
serverEntrance.lookup("#clientLaunch").setOnAction(function(){
|
||||
serverEntrance.lookup("#clientLaunch").setOnAction(function() {
|
||||
doUpdate(profilesList[serverHolder.old], loginData.pp, loginData.accessToken);
|
||||
});
|
||||
pane.lookup("#logout").setOnAction(function(){
|
||||
pane.lookup("#logout").setOnAction(function() {
|
||||
setCurrentScene(loginScene);
|
||||
});
|
||||
|
||||
|
@ -116,9 +116,9 @@ function initMenuScene() {
|
|||
|
||||
/* ======== init Console window======== */
|
||||
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) {
|
||||
if(movePoint === null) {
|
||||
if (movePoint === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -128,21 +128,21 @@ function initConsoleScene() {
|
|||
|
||||
var pane = consoleMenu.lookup("#bar");
|
||||
consoleBar = pane;
|
||||
pane.lookup("#close").setOnAction(function(){
|
||||
pane.lookup("#close").setOnAction(function() {
|
||||
consoleStage.hide();
|
||||
});
|
||||
var text = consoleMenu.lookup("#textField");
|
||||
var output = consoleMenu.lookup("#output");
|
||||
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());
|
||||
if (text.getText() == "clear") {
|
||||
output.setText("");
|
||||
}
|
||||
if (text.getText() == "clear") {
|
||||
output.setText("");
|
||||
}
|
||||
text.setText("");
|
||||
});
|
||||
FunctionalBridge.addPlainOutput(function(string) {
|
||||
appendFunction(string+"\n");
|
||||
appendFunction(string + "\n");
|
||||
})
|
||||
pane.lookup("#hide").setOnAction(function(event) { consoleStage.setIconified(true) });
|
||||
|
||||
|
@ -153,9 +153,9 @@ function initConsoleScene() {
|
|||
|
||||
/* ======== init Options window======== */
|
||||
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) {
|
||||
if(movePoint === null) {
|
||||
if (movePoint === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -165,9 +165,9 @@ function initOptionsScene() {
|
|||
|
||||
var pane = optionsMenu.lookup("#bar");
|
||||
bar = pane;
|
||||
pane.lookup("#close").setOnAction(function(event){ javafx.application.Platform.exit()});
|
||||
pane.lookup("#hide").setOnAction(function(event){ stage.setIconified(true)});
|
||||
pane.lookup("#back").setOnAction(function(){
|
||||
pane.lookup("#close").setOnAction(function(event) { javafx.application.Platform.exit() });
|
||||
pane.lookup("#hide").setOnAction(function(event) { stage.setIconified(true) });
|
||||
pane.lookup("#back").setOnAction(function() {
|
||||
setCurrentScene(menuScene);
|
||||
});
|
||||
}
|
||||
|
@ -202,29 +202,29 @@ function goAuth(event) {
|
|||
return; // No auth selected
|
||||
}
|
||||
|
||||
var rsaPassword = null;
|
||||
var auth = authOptions.getSelectionModel().getSelectedItem();
|
||||
if (auth === null) {
|
||||
var rsaPassword = null;
|
||||
var auth = authOptions.getSelectionModel().getSelectedItem();
|
||||
if (auth === null) {
|
||||
return;
|
||||
}
|
||||
if (!passwordField.isDisable()) {
|
||||
var password = passwordField.getText();
|
||||
if (password !== null && !password.isEmpty()) {
|
||||
rsaPassword = settingsOverlay.setPassword(password);
|
||||
} else if (settings.rsaPassword !== null) {
|
||||
rsaPassword = settings.rsaPassword;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!passwordField.isDisable()) {
|
||||
var password = passwordField.getText();
|
||||
if (password !== null && !password.isEmpty()) {
|
||||
rsaPassword = settingsOverlay.setPassword(password);
|
||||
} else if (settings.rsaPassword !== null) {
|
||||
rsaPassword = settings.rsaPassword;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
settings.rsaPassword = savePasswordBox.isSelected() ? rsaPassword : null;
|
||||
}
|
||||
settings.rsaPassword = savePasswordBox.isSelected() ? rsaPassword : null;
|
||||
}
|
||||
|
||||
settings.login = login;
|
||||
doAuth(login, rsaPassword, authTypes[auth]);
|
||||
}
|
||||
settings.login = login;
|
||||
doAuth(login, rsaPassword, authTypes[auth]);
|
||||
}
|
||||
|
||||
/* ======== Console ======== */
|
||||
/* ======== Console ======== */
|
||||
function goConsole(event) {
|
||||
setConsoleCurrentScene(consoleScene);
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ function verifyLauncher(e) {
|
|||
authOptions.getSelectionModel().select(0);
|
||||
var sm = authOptions.getSelectionModel().selectedIndexProperty();
|
||||
sm.addListener(new javafx.beans.value.ChangeListener({
|
||||
changed: function (observableValue, oldSelection, newSelection) {
|
||||
changed: function(observableValue, oldSelection, newSelection) {
|
||||
settings.auth = authTypes[authOptions.getSelectionModel().getSelectedItem()];
|
||||
}
|
||||
}));
|
||||
|
@ -287,14 +287,18 @@ function verifyLauncher(e) {
|
|||
|
||||
function doAuth(login, rsaPassword, auth_type) {
|
||||
processing.resetOverlay();
|
||||
overlay.show(processing.overlay, function (event) {
|
||||
overlay.show(processing.overlay, function(event) {
|
||||
FunctionalBridge.getHWID.join();
|
||||
makeAuthRequest(login, rsaPassword, auth_type, function (result) {
|
||||
makeAuthRequest(login, rsaPassword, auth_type, function(result) {
|
||||
FunctionalBridge.setAuthParams(result);
|
||||
loginData = { pp: result.playerProfile , accessToken: result.accessToken, permissions: result.permissions,
|
||||
auth_type: settings.auth};
|
||||
loginData = {
|
||||
pp: result.playerProfile,
|
||||
accessToken: result.accessToken,
|
||||
permissions: result.permissions,
|
||||
auth_type: settings.auth
|
||||
};
|
||||
|
||||
overlay.hide(0, function () {
|
||||
overlay.hide(0, function() {
|
||||
setCurrentScene(menuScene);
|
||||
});
|
||||
return result;
|
||||
|
@ -355,10 +359,8 @@ function doUpdate(profile, pp, accessToken) {
|
|||
|
||||
function doLaunchClient(assetDir, assetHDir, clientDir, clientHDir, profile, pp, accessToken) {
|
||||
processing.resetOverlay();
|
||||
overlay.swap(0, processing.overlay, function(event)
|
||||
launchClient(assetHDir, clientHDir, profile, new ClientLauncherParams(settings.lastDigest,
|
||||
assetDir, clientDir, pp, accessToken, settings.autoEnter, settings.fullScreen, settings.ram, 0, 0), doDebugClient)
|
||||
);
|
||||
overlay.swap(0, processing.overlay, function(event) launchClient(assetHDir, clientHDir, profile, new ClientLauncherParams(settings.lastDigest,
|
||||
assetDir, clientDir, pp, accessToken, settings.autoEnter, settings.fullScreen, settings.ram, 0, 0), doDebugClient));
|
||||
}
|
||||
|
||||
function doDebugClient(process) {
|
||||
|
@ -398,10 +400,10 @@ function updateProfilesList(profiles) {
|
|||
if (profile.getOptional() != null) profile.updateOptionalGraph();
|
||||
index++;
|
||||
});
|
||||
LogHelper.debug("Load selected %d profile",settings.profile);
|
||||
if(profiles.length > 0) {
|
||||
if(settings.profile >= profiles.length)
|
||||
settings.profile = profiles.length-1;
|
||||
LogHelper.debug("Load selected %d profile", settings.profile);
|
||||
if (profiles.length > 0) {
|
||||
if (settings.profile >= profiles.length)
|
||||
settings.profile = profiles.length - 1;
|
||||
serverHolder.set(serverList.getChildren().get(settings.profile));
|
||||
}
|
||||
}
|
||||
|
@ -412,11 +414,11 @@ function pingServer(btn) {
|
|||
var task = newTask(function() pingers[profile].ping());
|
||||
task.setOnSucceeded(function(event) {
|
||||
var result = task.getValue();
|
||||
if(btn==serverHolder.old){
|
||||
if (btn == serverHolder.old) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -482,7 +484,7 @@ var overlay = {
|
|||
fade(overlay.current, delay, 1.0, 0.0, function(event) {
|
||||
dimPane.requestFocus();
|
||||
|
||||
if(overlay.current==null){
|
||||
if (overlay.current == null) {
|
||||
overlay.show(newOverlay, onFinished);
|
||||
return;
|
||||
}
|
||||
|
@ -504,13 +506,13 @@ var overlay = {
|
|||
var serverHolder = {
|
||||
old: null,
|
||||
|
||||
set: function(btn){
|
||||
set: function(btn) {
|
||||
pingServer(btn);
|
||||
serverLabel.setText(profilesList[btn]);
|
||||
serverDescription.setText(profilesList[btn].info);
|
||||
btn.setSelected(true);
|
||||
btn.setDisable(true);
|
||||
if(serverHolder.old!=null){
|
||||
if (serverHolder.old != null) {
|
||||
serverHolder.old.setSelected(false);
|
||||
serverHolder.old.setDisable(false);
|
||||
}
|
||||
|
|
BIN
Launcher/runtime/dialog/images/icons/back.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Launcher/runtime/dialog/images/icons/close.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Launcher/runtime/dialog/images/icons/console.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Launcher/runtime/dialog/images/icons/discord.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Launcher/runtime/dialog/images/icons/hide.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
Launcher/runtime/dialog/images/icons/list.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Launcher/runtime/dialog/images/icons/settings.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
|
@ -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>
|
|
@ -4,55 +4,54 @@ Button {
|
|||
}
|
||||
|
||||
/* Outputs */
|
||||
#overlay > #output,
|
||||
#background > #output {
|
||||
#overlay>#output,
|
||||
#background>#output {
|
||||
-fx-background-color: white;
|
||||
-fx-background-radius: 0;
|
||||
-fx-font-family: monospace;
|
||||
-fx-font-size: 8pt;
|
||||
}
|
||||
|
||||
#overlay > #output .content,
|
||||
#background > #output .content {
|
||||
#overlay>#output .content,
|
||||
#background>#output .content {
|
||||
-fx-background-color: white;
|
||||
-fx-background-radius: 0;
|
||||
}
|
||||
|
||||
/* Close button */
|
||||
#overlay > #copy,
|
||||
#overlay > #action.close {
|
||||
#overlay>#copy,
|
||||
#overlay>#action.close {
|
||||
-fx-background-radius: 0;
|
||||
-fx-text-fill: white;
|
||||
-fx-background-position: center;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-background-color: #2d83ce;
|
||||
-fx-pref-width: 150px;
|
||||
-fx-pref-height: 25px;
|
||||
}
|
||||
|
||||
#overlay > #copy:hover,
|
||||
#overlay > #copy:focused,
|
||||
#overlay > #action.close:hover,
|
||||
#overlay > #action.close:focused,
|
||||
#overlay > #copy:pressed,
|
||||
#overlay > #action.close:pressed {
|
||||
#overlay>#copy:hover,
|
||||
#overlay>#copy:focused,
|
||||
#overlay>#action.close:hover,
|
||||
#overlay>#action.close:focused,
|
||||
#overlay>#copy:pressed,
|
||||
#overlay>#action.close:pressed {
|
||||
-fx-background-color: #1568ce;
|
||||
}
|
||||
|
||||
/* Kill button */
|
||||
#overlay > #action.kill {
|
||||
#overlay>#action.kill {
|
||||
-fx-background-radius: 0;
|
||||
-fx-text-fill: white;
|
||||
-fx-background-position: center;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-background-color: #CE5757;
|
||||
-fx-pref-width: 150px;
|
||||
-fx-pref-height: 25px;
|
||||
}
|
||||
|
||||
#overlay > #action.kill:hover,
|
||||
#overlay > #action.kill:focused,
|
||||
#overlay > #action.kill:pressed {
|
||||
#overlay>#action.kill:hover,
|
||||
#overlay>#action.kill:focused,
|
||||
#overlay>#action.kill:pressed {
|
||||
-fx-background-color: #DB5252;
|
||||
}
|
||||
|
||||
/*-- DrLeonardo Design --*/
|
|
@ -1,27 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.TextArea?>
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TextArea?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
|
||||
<!-- 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">
|
||||
<stylesheets>
|
||||
<URL value="@debug.css"/>
|
||||
<URL value="@../../styles.css"/>
|
||||
<URL value="@debug.css" />
|
||||
<URL value="@../../styles.css" />
|
||||
</stylesheets>
|
||||
|
||||
<!-- Debug controls -->
|
||||
<TextArea fx:id="output" prefHeight="450.0" prefWidth="693.0">
|
||||
<TextArea fx:id="output" prefHeight="405.0" prefWidth="693.0">
|
||||
<padding>
|
||||
<Insets left="10.0" right="10.0"/>
|
||||
<Insets left="10.0" right="10.0" />
|
||||
</padding>
|
||||
</TextArea>
|
||||
<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="Убить"/>
|
||||
<Pane layoutY="405.0" prefHeight="45.0" prefWidth="693.0" style="-fx-background-color: #ffffff;" />
|
||||
<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="Убить" />
|
||||
<Label fx:id="version" layoutX="14.0" layoutY="419.0" text="GravitLauncher" />
|
||||
</Pane>
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
var debug = {
|
||||
overlay: null, output: null, action: null, process: null,
|
||||
overlay: null,
|
||||
output: null,
|
||||
action: null,
|
||||
process: null,
|
||||
|
||||
initOverlay: function() {
|
||||
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.setEditable(false);
|
||||
|
||||
|
@ -13,7 +25,7 @@ var debug = {
|
|||
content.putString(debug.output.getText());
|
||||
|
||||
javafx.scene.input.Clipboard.getSystemClipboard().
|
||||
setContent(content);
|
||||
setContent(content);
|
||||
});
|
||||
|
||||
debug.action = debug.overlay.lookup("#action");
|
||||
|
@ -38,8 +50,7 @@ var debug = {
|
|||
|
||||
append: function(text) {
|
||||
//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.appendText(text);
|
||||
|
@ -70,7 +81,7 @@ function debugProcess(process) {
|
|||
var reader = IOHelper.newReader(process.getInputStream(),
|
||||
java.nio.charset.Charset.defaultCharset());
|
||||
var appendFunction = function(line)
|
||||
javafx.application.Platform.runLater(function() debug.append(line));
|
||||
javafx.application.Platform.runLater(function() debug.append(line));
|
||||
for (var length = reader.read(buffer); length >= 0; length = reader.read(buffer)) {
|
||||
appendFunction(new java.lang.String(buffer, 0, length));
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
/*-- DrLeonardo Design --*/
|
||||
#overlay > #description {
|
||||
#overlay>#description {
|
||||
-fx-font-size: 12pt;
|
||||
-fx-text-fill: #fff;
|
||||
-fx-wrap-text: true;
|
||||
}
|
||||
|
||||
#overlay > #description.error {
|
||||
#overlay>#description.error {
|
||||
-fx-text-fill: red;
|
||||
}
|
||||
|
||||
/*-- DrLeonardo Design --*/
|
|
@ -1,6 +1,9 @@
|
|||
var processing = {
|
||||
overlay: null, spinner: null, description: null,
|
||||
processingImage: null, errorImage: null,
|
||||
overlay: null,
|
||||
spinner: null,
|
||||
description: null,
|
||||
processingImage: null,
|
||||
errorImage: null,
|
||||
|
||||
initOverlay: function() {
|
||||
processing.overlay = loadFXML("dialog/overlay/processing/processing.fxml");
|
||||
|
@ -69,6 +72,7 @@ function makeLauncherRequest(callback) {
|
|||
task.updateMessage("Обновление лаунчера");
|
||||
startTask(task);
|
||||
}
|
||||
|
||||
function makeProfilesRequest(callback) {
|
||||
var task = newRequestTask(new ProfilesRequest());
|
||||
|
||||
|
@ -83,6 +87,7 @@ function makeProfilesRequest(callback) {
|
|||
task.updateMessage("Обновление профилей");
|
||||
startTask(task);
|
||||
}
|
||||
|
||||
function makeAuthAvailabilityRequest(callback) {
|
||||
var task = newRequestTask(new GetAvailabilityAuthRequest());
|
||||
|
||||
|
@ -98,6 +103,7 @@ function makeAuthAvailabilityRequest(callback) {
|
|||
task.updateMessage("Обновление способов авторизации");
|
||||
startTask(task);
|
||||
}
|
||||
|
||||
function makeSetProfileRequest(profile, callback) {
|
||||
var task = newRequestTask(new SetProfileRequest(profile));
|
||||
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
/*-- DrLeonardo Design --*/
|
||||
|
||||
#holder {
|
||||
-fx-background-color: #fff;
|
||||
-fx-background-color: #fff;
|
||||
}
|
||||
#holder > #transferDialog {
|
||||
|
||||
#holder>#transferDialog {
|
||||
-fx-background-color: rgba(0, 0, 0, 0.5);
|
||||
-fx-pref-width: 694px;
|
||||
-fx-pref-height: 425px;
|
||||
}
|
||||
|
||||
|
||||
/* Labels */
|
||||
#holder > #settingsTitle {
|
||||
|
||||
#holder>#settingsTitle {
|
||||
-fx-font-size: 14pt;
|
||||
-fx-alignment: baseline-center;
|
||||
}
|
||||
|
@ -19,66 +24,77 @@ #holder #dirChange {
|
|||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/* RAM slider */
|
||||
#holder > #ramSlider > .track {
|
||||
|
||||
#holder>#ramSlider>.track {
|
||||
-fx-background-color: #909090;
|
||||
}
|
||||
|
||||
#holder > #ramSlider > .thumb {
|
||||
#holder>#ramSlider>.thumb {
|
||||
-fx-background-color: #5fd97a;
|
||||
}
|
||||
|
||||
#holder > #ramSlider > .colored-track {
|
||||
#holder>#ramSlider>.colored-track {
|
||||
-fx-background-color: #5fd97a;
|
||||
}
|
||||
|
||||
#holder > #ramSlider > .animated-thumb {
|
||||
#holder>#ramSlider>.animated-thumb {
|
||||
-fx-background-color: #5fd97a;
|
||||
}
|
||||
|
||||
#holder > #ramSlider > .slider-value {
|
||||
#holder>#ramSlider>.slider-value {
|
||||
-fx-fill: white;
|
||||
-fx-stroke: white;
|
||||
}
|
||||
|
||||
|
||||
/* Dir options */
|
||||
#holder > #deleteDir, #cancelTransfer {
|
||||
|
||||
#holder>#deleteDir,
|
||||
#cancelTransfer {
|
||||
-fx-background-radius: 0;
|
||||
-fx-text-fill: white;
|
||||
-fx-background-position: center;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-background-color: #CE5757;
|
||||
-fx-pref-width: 150px;
|
||||
-fx-pref-height: 25px;
|
||||
}
|
||||
|
||||
#holder > #deleteDir:hover,#cancelTransfer:hover,
|
||||
#holder > #deleteDir:focused,#cancelTransfer:focused {
|
||||
#holder>#deleteDir:hover,
|
||||
#cancelTransfer:hover,
|
||||
#holder>#deleteDir:focused,
|
||||
#cancelTransfer:focused {
|
||||
-fx-background-color: #DB5252;
|
||||
}
|
||||
|
||||
#holder > #changeDir {
|
||||
#holder>#changeDir {
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-fill: #909090;
|
||||
-fx-background-radius: 0;
|
||||
}
|
||||
|
||||
#holder > #changeDir:focused,
|
||||
#holder > #changeDir:pressed {
|
||||
#holder>#changeDir:focused,
|
||||
#holder>#changeDir:pressed {
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
#holder > #apply,#applyTransfer{
|
||||
#holder>#apply,
|
||||
#applyTransfer {
|
||||
-fx-background-color: #5fd97a;
|
||||
-fx-background-radius: 0;
|
||||
-fx-text-fill: white;
|
||||
-fx-background-position: center;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-pref-width: 150px;
|
||||
-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;
|
||||
}
|
||||
|
||||
|
||||
/*-- DrLeonardo Design --*/
|
|
@ -1,10 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.control.Slider?>
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.scene.control.Hyperlink?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.Slider?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.shape.Line?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
@ -13,16 +14,22 @@
|
|||
|
||||
<!-- 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>
|
||||
<Pane id="holder" prefHeight="450.0" prefWidth="694.0">
|
||||
<children>
|
||||
<CheckBox fx:id="autoEnter" layoutX="14.0" layoutY="137.0" text="Автовход на сервер" />
|
||||
<Text fill="#8c8c8c" layoutX="40.0" layoutY="153.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Включение авто-входа означает что вы сразу после загрузки клиента попадете на сервер" wrappingWidth="636.9999872148037" y="15.0" />
|
||||
<CheckBox fx:id="fullScreen" layoutX="13.0" layoutY="244.0" text="Клиент в полный экран" />
|
||||
<Text fill="#8c8c8c" layoutX="40.0" layoutY="261.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Включение данной функции позволяет запустить игру сразу в полноэкранном режиме" wrappingWidth="636.9999872148037" y="15.0" />
|
||||
<CheckBox id="debug" layoutX="13.0" layoutY="183.0" text="Режим Отладки" />
|
||||
<Text fill="#8c8c8c" layoutX="40.0" layoutY="198.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Режим отладки позволяет просмотреть лог запуска и работы программы в реальном времени прямо из лаунчера, что упрощает поиск нужной информации" wrappingWidth="637.0000016447157" y="15.0" />
|
||||
<CheckBox fx:id="autoEnter" layoutX="28.0" layoutY="169.0" text="Автовход на сервер">
|
||||
<font>
|
||||
<Font size="13.0" />
|
||||
</font></CheckBox>
|
||||
<CheckBox fx:id="fullScreen" layoutX="28.0" layoutY="229.0" text="Клиент в полный экран">
|
||||
<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">
|
||||
<Text fx:id="ramLabel" />
|
||||
</TextFlow>
|
||||
|
@ -44,8 +51,20 @@
|
|||
</children>
|
||||
</Pane>
|
||||
<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="Поиск файлов в других клиентах" />
|
||||
<Text fill="#8c8c8c" layoutX="40.0" layoutY="309.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Используется для экономии вашего трафика, аналогичные файлы будут скопированы с других игровых клиентов" wrappingWidth="636.9999872148037" y="15.0" />
|
||||
<CheckBox fx:id="featureStore" layoutX="28.0" layoutY="259.0" text="Поиск файлов в других клиентах">
|
||||
<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>
|
||||
</Pane>
|
||||
</children>
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
var settingsOverlay = {
|
||||
/* ===================== OVERLAY ===================== */
|
||||
overlay: null, ramLabel: null, dirLabel: null, transferDialog: null,
|
||||
deleteDirPressedAgain: false, count: 0,
|
||||
/* ===================== OVERLAY ===================== */
|
||||
overlay: null,
|
||||
ramLabel: null,
|
||||
dirLabel: null,
|
||||
transferDialog: null,
|
||||
deleteDirPressedAgain: false,
|
||||
count: 0,
|
||||
descLabel: null,
|
||||
description: null,
|
||||
|
||||
initOverlay: function() {
|
||||
settingsOverlay.overlay = loadFXML("dialog/overlay/settings/settings.fxml");
|
||||
|
@ -12,10 +18,12 @@ var settingsOverlay = {
|
|||
autoEnterBox.setSelected(settings.autoEnter);
|
||||
autoEnterBox.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"](
|
||||
function(o, ov, nv) settings.autoEnter = nv);
|
||||
autoEnterBox.setOnMouseEntered(function() {
|
||||
settingsOverlay.updateDesc(autoEnterBox.getText(), "Включение авто-входа означает что вы сразу после загрузки клиента попадете на сервер");
|
||||
});
|
||||
|
||||
settingsOverlay.dirLabel = holder.lookup("#dirLabel");
|
||||
settingsOverlay.dirLabel.setOnAction(function(event)
|
||||
app.getHostServices().showDocument(settings.updatesDir.toUri()));
|
||||
settingsOverlay.dirLabel.setOnAction(function(event) app.getHostServices().showDocument(settings.updatesDir.toUri()));
|
||||
settingsOverlay.updateDirLabel();
|
||||
|
||||
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");
|
||||
featureStore.setSelected(settings.featureStore);
|
||||
featureStore.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"](
|
||||
function(o, ov, nv) settings.featureStore = nv);
|
||||
featureStore.setOnMouseEntered(function() {
|
||||
settingsOverlay.updateDesc(featureStore.getText(), "Используется для экономии вашего трафика, аналогичные файлы будут скопированы с других игровых клиентов");
|
||||
});
|
||||
|
||||
var fullScreenBox = holder.lookup("#fullScreen");
|
||||
fullScreenBox.setSelected(settings.fullScreen);
|
||||
fullScreenBox.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"](
|
||||
function(o, ov, nv) settings.fullScreen = nv);
|
||||
fullScreenBox.setOnMouseEntered(function() {
|
||||
settingsOverlay.updateDesc(fullScreenBox.getText(), "Включение данной функции позволяет запустить игру сразу в полноэкранном режиме");
|
||||
});
|
||||
|
||||
settingsOverlay.ramLabel = holder.lookup("#ramLabel");
|
||||
settingsOverlay.updateRAMLabel();
|
||||
|
@ -70,31 +87,42 @@ var settingsOverlay = {
|
|||
|
||||
settingsOverlay.deleteUpdatesDir();
|
||||
settingsOverlay.deleteDirPressedAgain = false;
|
||||
settingsOverlay.count = settingsOverlay.count+1;
|
||||
if(settingsOverlay.count>9){
|
||||
javafx.application.Platform.exit();
|
||||
}
|
||||
settingsOverlay.count = settingsOverlay.count + 1;
|
||||
if (settingsOverlay.count > 9) {
|
||||
javafx.application.Platform.exit();
|
||||
}
|
||||
deleteDirButton.setText(
|
||||
settingsOverlay.count>8?"Прощай :(":
|
||||
(settingsOverlay.count>7?"Я умираю!":
|
||||
(settingsOverlay.count>5?"DeathCry, спаси!":
|
||||
(settingsOverlay.count>4?"Умоляю, перестань!":
|
||||
(settingsOverlay.count>3?"Да хорош уже!":"Ещё раз")
|
||||
))));
|
||||
settingsOverlay.count > 8 ? "Прощай :(" :
|
||||
(settingsOverlay.count > 7 ? "Я умираю!" :
|
||||
(settingsOverlay.count > 5 ? "DeathCry, спаси!" :
|
||||
(settingsOverlay.count > 4 ? "Умоляю, перестань!" :
|
||||
(settingsOverlay.count > 3 ? "Да хорош уже!" : "Ещё раз")
|
||||
))));
|
||||
});
|
||||
|
||||
var debugBox = settingsOverlay.overlay.lookup("#debug");
|
||||
debugBox.setSelected(settings.debug);
|
||||
debugBox.selectedProperty()["addListener(javafx.beans.value.ChangeListener)"](
|
||||
function(o, ov, nv) settings.debug = nv);
|
||||
debugBox.setOnMouseEntered(function() {
|
||||
settingsOverlay.updateDesc(debugBox.getText(), "Режим отладки позволяет просмотреть лог запуска и работы программы в реальном времени прямо из лаунчера, что упрощает поиск нужной информации");
|
||||
});
|
||||
|
||||
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) {
|
||||
settingsOverlay.transferDialog.setVisible(true);
|
||||
settingsOverlay.transferDialog.lookup("#cancelTransfer").setOnAction(function(event)
|
||||
{
|
||||
settingsOverlay.transferDialog.lookup("#cancelTransfer").setOnAction(function(event) {
|
||||
settings.updatesDir = newDir;
|
||||
DirBridge.dirUpdates = settings.updatesDir;
|
||||
settingsOverlay.updateDirLabel();
|
||||
|
@ -133,11 +161,11 @@ var settingsOverlay = {
|
|||
|
||||
|
||||
setRAM: function(ram) {
|
||||
if (ram>762&&ram<1024){
|
||||
settings.ram = java.lang.Math["min(int,int)"](ram, FunctionalBridge.getJVMTotalMemory());
|
||||
}else{
|
||||
settings.ram = java.lang.Math["min(int,int)"](((ram / 256) | 0) * 256, FunctionalBridge.getJVMTotalMemory());
|
||||
}
|
||||
if (ram > 762 && ram < 1024) {
|
||||
settings.ram = java.lang.Math["min(int,int)"](ram, FunctionalBridge.getJVMTotalMemory());
|
||||
} else {
|
||||
settings.ram = java.lang.Math["min(int,int)"](((ram / 256) | 0) * 256, FunctionalBridge.getJVMTotalMemory());
|
||||
}
|
||||
},
|
||||
|
||||
updateDirLabel: function() {
|
||||
|
@ -148,9 +176,16 @@ LogHelper.debug("Dir: %s", DirBridge.dir);
|
|||
|
||||
/* ====================== CLI PARAMS ===================== */
|
||||
var cliParams = {
|
||||
login: null, password: null, profile: -1, autoLogin: false,
|
||||
updatesDir: null, autoEnter: null, fullScreen: null, ram: -1,
|
||||
offline: false, featureStore: null,
|
||||
login: null,
|
||||
password: null,
|
||||
profile: -1,
|
||||
autoLogin: false,
|
||||
updatesDir: null,
|
||||
autoEnter: null,
|
||||
fullScreen: null,
|
||||
ram: -1,
|
||||
offline: false,
|
||||
featureStore: null,
|
||||
|
||||
init: function(params) {
|
||||
var named = params.getNamed();
|
||||
|
@ -200,8 +235,7 @@ var cliParams = {
|
|||
if (cliParams.profile >= 0) {
|
||||
settings.profile = cliParams.profile;
|
||||
}
|
||||
if (cliParams.updatesDir !== null) {
|
||||
}
|
||||
if (cliParams.updatesDir !== null) {}
|
||||
if (cliParams.autoEnter !== null) {
|
||||
settings.autoLogin = cliParams.autoEnter;
|
||||
}
|
||||
|
|
|
@ -6,16 +6,16 @@ #overlay {
|
|||
-fx-background-image: url('../../images/downloader/blured.jpg');
|
||||
}
|
||||
|
||||
#overlay > #utitle {
|
||||
#overlay>#utitle {
|
||||
-fx-alignment: top-left;
|
||||
}
|
||||
|
||||
#overlay > #description {
|
||||
#overlay>#description {
|
||||
-fx-alignment: top-left;
|
||||
-fx-wrap-text: true;
|
||||
}
|
||||
|
||||
#overlay > #description.error {
|
||||
#overlay>#description.error {
|
||||
-fx-text-fill: #CE5757;
|
||||
}
|
||||
|
||||
|
@ -29,15 +29,15 @@ .progress-bar {
|
|||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.progress-indicator{
|
||||
-fx-background-color: transparent ;
|
||||
.progress-indicator {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.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-color: transparent;
|
||||
-fx-background-insets: 0;
|
||||
|
@ -45,9 +45,10 @@ .progress-bar > .bar {
|
|||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.progress-bar > .track {
|
||||
.progress-bar>.track {
|
||||
-fx-background-color: transparent;
|
||||
-fx-background-insets: 0;
|
||||
-fx-background-radius: 3px;
|
||||
}
|
||||
|
||||
/*-- DrLeonardo Design --*/
|
|
@ -1,5 +1,8 @@
|
|||
var update = {
|
||||
overlay: null, title: null, description: null, progress: null,
|
||||
overlay: null,
|
||||
title: null,
|
||||
description: null,
|
||||
progress: null,
|
||||
|
||||
initOverlay: function() {
|
||||
update.overlay = loadFXML("dialog/overlay/update/update.fxml");
|
||||
|
@ -41,7 +44,7 @@ var update = {
|
|||
"Примерно осталось: %d:%02d:%02d%n",
|
||||
|
||||
state.filePath,
|
||||
state.getTotalDownloadedMiB() + 0.0, state.getTotalSizeMiB() + 0.0,
|
||||
state.getTotalDownloadedMiB() + 0.0, state.getTotalSizeMiB() + 0.0,
|
||||
bps <= 0.0 ? 0.0 : bps / 1024.0,
|
||||
estimatedHH, estimatedMM, estimatedSS
|
||||
));
|
||||
|
@ -83,10 +86,10 @@ function offlineUpdateRequest(dirName, dir, matcher, digest) {
|
|||
|
||||
/* Export functions */
|
||||
function makeUpdateRequest(dirName, dir, matcher, digest, callback) {
|
||||
var request = settings.offline ? { setStateCallback: function(stateCallback) { } } :
|
||||
new UpdateRequest(dirName, dir, matcher, digest);
|
||||
var task = settings.offline ? newTask(offlineUpdateRequest(dirName, dir, matcher, digest)) :
|
||||
newRequestTask(request);
|
||||
var request = settings.offline ? { setStateCallback: function(stateCallback) {} } :
|
||||
new UpdateRequest(dirName, dir, matcher, digest);
|
||||
var task = settings.offline ? newTask(offlineUpdateRequest(dirName, dir, matcher, digest)) :
|
||||
newRequestTask(request);
|
||||
|
||||
update.setTaskProperties(task, request, callback);
|
||||
task.updateMessage("Состояние: Хеширование");
|
||||
|
|
|
@ -1,54 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.TextArea?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?>
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.shape.Line?>
|
||||
|
||||
<!-- 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>
|
||||
<Pane fx:id="bar" layoutX="692.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
|
||||
<children>
|
||||
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0"
|
||||
text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="close" alignment="CENTER" contentDisplay="CENTER" text=""
|
||||
textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER"/>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/hide.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
<Button id="close" alignment="CENTER" contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/close.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
</children>
|
||||
</Pane>
|
||||
<TextArea fx:id="output" prefHeight="419.0" prefWidth="692.0">
|
||||
<padding>
|
||||
<Insets left="10.0" top="10.0"/>
|
||||
<Insets left="10.0" top="10.0" />
|
||||
</padding>
|
||||
</TextArea>
|
||||
<TextField fx:id="textField" layoutY="420.0" prefHeight="30.0" prefWidth="543.0"
|
||||
promptText="Введите команду...">
|
||||
<TextField fx:id="textField" layoutY="420.0" prefHeight="30.0" prefWidth="543.0" promptText="Введите команду...">
|
||||
<opaqueInsets>
|
||||
<Insets/>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<padding>
|
||||
<Insets left="10.0"/>
|
||||
<Insets left="10.0" />
|
||||
</padding>
|
||||
</TextField>
|
||||
<Button fx:id="send" defaultButton="true" layoutX="542.0" layoutY="420.0" prefHeight="30.0" prefWidth="147.0"
|
||||
text="Выполнить"/>
|
||||
<Line endX="594.0" layoutX="98.0" layoutY="420.0" startX="-100.0" stroke="#5b3636" styleClass="lineHead"/>
|
||||
<Button fx:id="send" defaultButton="true" layoutX="542.0" layoutY="420.0" prefHeight="30.0" prefWidth="147.0" text="Выполнить" />
|
||||
<Line endX="594.0" layoutX="98.0" layoutY="420.0" startX="-100.0" stroke="#5b3636" styleClass="lineHead" />
|
||||
</children>
|
||||
<stylesheets>
|
||||
<URL value="@../../styles.css"/>
|
||||
<URL value="@../../overlay/debug/debug.css"/>
|
||||
<URL value="@../../styles.css" />
|
||||
<URL value="@../../overlay/debug/debug.css" />
|
||||
</stylesheets>
|
||||
</Pane>
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import java.lang.String?>
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.control.ComboBox?>
|
||||
<?import javafx.scene.control.Hyperlink?>
|
||||
<?import javafx.scene.control.PasswordField?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?>
|
||||
<?import java.lang.String?>
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.scene.control.Hyperlink?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
|
||||
<!-- 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>
|
||||
<Pane fx:id="layout" prefWidth="740.0">
|
||||
<children>
|
||||
|
@ -40,21 +41,30 @@
|
|||
<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">
|
||||
<children>
|
||||
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="close" alignment="CENTER" contentDisplay="CENTER" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="370.0" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="#5fd97a" glyphName="MESSAGE_TEXT" size="20" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/hide.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
<Button id="close" alignment="CENTER" contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/close.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
<Button id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="370.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/discord.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
</children>
|
||||
</Pane>
|
||||
</children>
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<?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 javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.FlowPane?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<!-- 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>
|
||||
<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>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<ScrollPane id="serverlist" hbarPolicy="NEVER" layoutX="1.0" prefHeight="450.0" prefWidth="307.0" visible="true">
|
||||
<content>
|
||||
<FlowPane focusTraversable="false" prefHeight="446.0" prefWidth="306.0" prefWrapLength="0.0" rowValignment="TOP" vgap="10.0" visible="true">
|
||||
<Button id="servercontainer" text="">
|
||||
<Button id="servercontainer" text="">
|
||||
<FlowPane.margin>
|
||||
<Insets bottom="10.0" />
|
||||
</FlowPane.margin></Button>
|
||||
|
@ -51,10 +51,14 @@
|
|||
<Font size="22.0" />
|
||||
</font>
|
||||
</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">
|
||||
<graphic>
|
||||
<FontAwesomeIconView fill="WHITE" glyphName="SLIDERS" size="30.0" />
|
||||
</graphic></Button>
|
||||
<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>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/list.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</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">
|
||||
<font>
|
||||
<Font name="System Bold" size="16.0" />
|
||||
|
@ -65,7 +69,7 @@
|
|||
<Font name="System Bold" size="18.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<Button id="logout" alignment="CENTER" contentDisplay="CENTER" layoutX="295.0" layoutY="12.0" prefHeight="25.0" prefWidth="81.0" text="Выйти" textAlignment="CENTER" />
|
||||
<Button id="logout" alignment="CENTER" contentDisplay="CENTER" layoutX="295.0" layoutY="12.0" prefHeight="25.0" prefWidth="81.0" text="Выйти" textAlignment="CENTER" />
|
||||
</children>
|
||||
</Pane>
|
||||
</children>
|
||||
|
@ -74,31 +78,46 @@
|
|||
</Pane>
|
||||
<Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
|
||||
<children>
|
||||
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="close" alignment="CENTER" contentDisplay="CENTER" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="380.0" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="#5fd97a" glyphName="MESSAGE_TEXT" size="20" smooth="false" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="settings" alignment="CENTER" contentDisplay="CENTER" layoutY="90.0" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="SETTINGS" size="20" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="goConsole" alignment="CENTER" contentDisplay="CENTER" layoutY="138.0" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="CONSOLE" size="20" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/hide.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
<Button id="close" alignment="CENTER" contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/close.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
<Button id="discord" alignment="CENTER" contentDisplay="CENTER" layoutY="380.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/discord.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
<Button id="settings" alignment="CENTER" contentDisplay="CENTER" layoutY="90.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/settings.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
<Button id="goConsole" alignment="CENTER" contentDisplay="CENTER" layoutY="138.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/console.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
</children>
|
||||
</Pane>
|
||||
<Pane id="mask" opacity="0.0" prefHeight="450.0" prefWidth="694.0" visible="false" />
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView?>
|
||||
<?import java.net.URL?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.shape.Line?>
|
||||
|
@ -13,7 +14,7 @@
|
|||
|
||||
<!-- 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>
|
||||
<Pane id="optionsPane" prefHeight="450.0" prefWidth="692.0" styleClass="optionsPane">
|
||||
<children>
|
||||
|
@ -38,21 +39,30 @@
|
|||
</Pane>
|
||||
<Pane fx:id="bar" layoutX="694.0" prefHeight="425.0" prefWidth="43.0" styleClass="bar">
|
||||
<children>
|
||||
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="MINUS" size="30" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="close" alignment="CENTER" contentDisplay="CENTER" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="CLOSE" size="30" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="back" alignment="CENTER" contentDisplay="CENTER" layoutY="405.0" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<MaterialDesignIconView fill="WHITE" glyphName="CHEVRON_LEFT" size="30" textAlignment="CENTER" />
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button id="hide" alignment="CENTER" contentDisplay="CENTER" layoutY="45.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/hide.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
<Button id="close" alignment="CENTER" contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/close.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
<Button id="back" alignment="CENTER" contentDisplay="CENTER" layoutY="405.0" minHeight="-Infinity" minWidth="-Infinity" text="" textAlignment="CENTER">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/icons/back.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic></Button>
|
||||
</children>
|
||||
</Pane>
|
||||
</children>
|
||||
|
|
|
@ -5,7 +5,7 @@ var options = {
|
|||
LogHelper.debug("Loading options file");
|
||||
try {
|
||||
tryWithResources(new HInput(IOHelper.newInput(options.file)), options.read);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
},
|
||||
|
@ -14,7 +14,7 @@ var options = {
|
|||
LogHelper.debug("Saving options file");
|
||||
try {
|
||||
tryWithResources(new HOutput(IOHelper.newOutput(options.file)), options.write);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
},
|
||||
|
@ -24,33 +24,27 @@ var options = {
|
|||
if (magic != config.settingsMagic) {
|
||||
throw new java.io.IOException("options magic mismatch: " + java.lang.Integer.toString(magic, 16));
|
||||
}
|
||||
var profilesCount = input.readInt();
|
||||
LogHelper.debug("Load options. ProfilesCount %d",profilesCount);
|
||||
for(var i = 0;i<profilesCount;i++)
|
||||
{
|
||||
var profilesCount = input.readInt();
|
||||
LogHelper.debug("Load options. ProfilesCount %d", profilesCount);
|
||||
for (var i = 0; i < profilesCount; i++) {
|
||||
var listSize = input.readInt();
|
||||
var sortIndex = input.readInt();
|
||||
var profile = null;
|
||||
settings.lastProfiles.forEach(function(hprofile,i,arr) {
|
||||
if(hprofile.getSortIndex() == sortIndex)
|
||||
{
|
||||
settings.lastProfiles.forEach(function(hprofile, i, arr) {
|
||||
if (hprofile.getSortIndex() == sortIndex) {
|
||||
profile = hprofile;
|
||||
}
|
||||
});
|
||||
for(var j = 0; j < listSize; j++)
|
||||
{
|
||||
for (var j = 0; j < listSize; j++) {
|
||||
var mark = input.readBoolean();
|
||||
var modType = OptionalFile.readType(input);
|
||||
var modFile = input.readString(0);
|
||||
if(mark)
|
||||
{
|
||||
profile.markOptional(modFile,modType);
|
||||
LogHelper.debug("Load options %s marked",modFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
profile.unmarkOptional(modFile,modType);
|
||||
LogHelper.debug("Load options %s unmarked",modFile);
|
||||
if (mark) {
|
||||
profile.markOptional(modFile, modType);
|
||||
LogHelper.debug("Load options %s marked", modFile);
|
||||
} else {
|
||||
profile.unmarkOptional(modFile, modType);
|
||||
LogHelper.debug("Load options %s unmarked", modFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,13 +53,13 @@ var options = {
|
|||
write: function(output) {
|
||||
output.writeInt(config.settingsMagic);
|
||||
output.writeInt(settings.lastProfiles.length);
|
||||
settings.lastProfiles.forEach(function(hprofile,i,arr) {
|
||||
settings.lastProfiles.forEach(function(hprofile, i, arr) {
|
||||
var profile = hprofile;
|
||||
LogHelper.debug("Save options %s",profile.getTitle());
|
||||
LogHelper.debug("Save options %s", profile.getTitle());
|
||||
var list = profile.getOptional();
|
||||
output.writeInt(list.size());
|
||||
output.writeInt(profile.getSortIndex());
|
||||
list.forEach(function(modFile,j,arr2) {
|
||||
list.forEach(function(modFile, j, arr2) {
|
||||
output.writeBoolean(modFile.mark);
|
||||
modFile.writeType(output);
|
||||
output.writeString(modFile.name, 0);
|
||||
|
@ -80,80 +74,77 @@ var options = {
|
|||
optionsPane = pane;
|
||||
|
||||
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) {
|
||||
if(node instanceof javafx.scene.control.CheckBox)
|
||||
nodelist.add(node);
|
||||
modlist.getChildren().forEach(function(node, i, arr) {
|
||||
if (node instanceof javafx.scene.control.CheckBox)
|
||||
nodelist.add(node);
|
||||
});
|
||||
nodelist.forEach(function(node, i, arr) {
|
||||
modlist.getChildren().remove(node);
|
||||
});
|
||||
var profile = profilesList[serverHolder.old];
|
||||
var list = profile.getOptional();
|
||||
var checkBoxList = new java.util.ArrayList;
|
||||
list.forEach(function(modFile) {
|
||||
var modName = modFile.name,
|
||||
modDescription = "",
|
||||
subLevel = 1;
|
||||
if (!modFile.visible) {
|
||||
LogHelper.debug("optionalMod %s hidden", modFile.name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (modFile.permissions != 0 && ((loginData.permissions.toLong() & modFile.permissions) == 0)) {
|
||||
LogHelper.debug("optionalMod %s permissions deny", modFile.name);
|
||||
return;
|
||||
}
|
||||
if (modFile.info != null)
|
||||
modDescription = modFile.info;
|
||||
if (modFile.subTreeLevel != null && modFile.subTreeLevel > 1)
|
||||
subLevel = modFile.subTreeLevel;
|
||||
var testMod = new javafx.scene.control.CheckBox(modName);
|
||||
testMod.getStyleClass().add("checkboxOpt");
|
||||
|
||||
if (subLevel > 1)
|
||||
for (var i = 1; i < subLevel; i++)
|
||||
testMod.setTranslateX(25 * i);
|
||||
|
||||
testMod.setSelected(modFile.mark);
|
||||
testMod.setOnAction(function(event) {
|
||||
var isSelected = event.getSource().isSelected();
|
||||
if (isSelected) {
|
||||
profile.markOptional(modFile);
|
||||
LogHelper.debug("Selected mod %s", modFile.name);
|
||||
} else {
|
||||
profile.unmarkOptional(modFile);
|
||||
LogHelper.debug("Unselected mod %s", modFile.name);
|
||||
}
|
||||
options.update();
|
||||
});
|
||||
nodelist.forEach(function(node,i,arr) {
|
||||
modlist.getChildren().remove(node);
|
||||
});
|
||||
var profile = profilesList[serverHolder.old];
|
||||
var list = profile.getOptional();
|
||||
var checkBoxList = new java.util.ArrayList;
|
||||
list.forEach(function(modFile) {
|
||||
var modName = modFile.name, modDescription = "", subLevel = 1;
|
||||
if(!modFile.visible)
|
||||
{
|
||||
LogHelper.debug("optionalMod %s hidden",modFile.name);
|
||||
return;
|
||||
}
|
||||
|
||||
if(modFile.permissions != 0 && ((loginData.permissions.toLong() & modFile.permissions) == 0))
|
||||
{
|
||||
LogHelper.debug("optionalMod %s permissions deny",modFile.name);
|
||||
return;
|
||||
}
|
||||
if(modFile.info != null)
|
||||
modDescription = modFile.info;
|
||||
if(modFile.subTreeLevel != null && modFile.subTreeLevel > 1)
|
||||
subLevel = modFile.subTreeLevel;
|
||||
var testMod = new javafx.scene.control.CheckBox(modName);
|
||||
testMod.getStyleClass().add("checkboxOpt");
|
||||
|
||||
if(subLevel > 1)
|
||||
for(var i = 1; i < subLevel; i++)
|
||||
testMod.setTranslateX(25*i);
|
||||
|
||||
testMod.setSelected(modFile.mark);
|
||||
testMod.setOnAction(function(event) {
|
||||
var isSelected = event.getSource().isSelected();
|
||||
if(isSelected)
|
||||
{
|
||||
profile.markOptional(modFile);
|
||||
LogHelper.debug("Selected mod %s", modFile.name);
|
||||
}
|
||||
else
|
||||
{
|
||||
profile.unmarkOptional(modFile);
|
||||
LogHelper.debug("Unselected mod %s", modFile.name);
|
||||
}
|
||||
options.update();
|
||||
});
|
||||
testMod.setFocusTraversable(false);
|
||||
checkBoxList.add(testMod);
|
||||
testMod.getStyleClass().add("modname");
|
||||
if(modDescription != "") {
|
||||
textDescr = new javafx.scene.text.Text(modDescription);
|
||||
if(subLevel > 1) {
|
||||
for(var i = 1; i < subLevel; i++){
|
||||
textDescr.setWrappingWidth(620-(25*i));
|
||||
textDescr.setTranslateX(25+(25*i));
|
||||
}
|
||||
} else {
|
||||
textDescr.setWrappingWidth(620);
|
||||
textDescr.setTranslateX(25);
|
||||
}
|
||||
textDescr.setTextAlignment(javafx.scene.text.TextAlignment.JUSTIFY);
|
||||
textDescr.getStyleClass().add("description-text");
|
||||
checkBoxList.add(textDescr);
|
||||
}
|
||||
sep = new javafx.scene.control.Separator();
|
||||
sep.getStyleClass().add("separator");
|
||||
checkBoxList.add(sep);
|
||||
});
|
||||
modlist.getChildren().clear();
|
||||
modlist.getChildren().addAll(checkBoxList);
|
||||
testMod.setFocusTraversable(false);
|
||||
checkBoxList.add(testMod);
|
||||
testMod.getStyleClass().add("modname");
|
||||
if (modDescription != "") {
|
||||
textDescr = new javafx.scene.text.Text(modDescription);
|
||||
if (subLevel > 1) {
|
||||
for (var i = 1; i < subLevel; i++) {
|
||||
textDescr.setWrappingWidth(620 - (25 * i));
|
||||
textDescr.setTranslateX(25 + (25 * i));
|
||||
}
|
||||
} else {
|
||||
textDescr.setWrappingWidth(620);
|
||||
textDescr.setTranslateX(25);
|
||||
}
|
||||
textDescr.setTextAlignment(javafx.scene.text.TextAlignment.JUSTIFY);
|
||||
textDescr.getStyleClass().add("description-text");
|
||||
checkBoxList.add(textDescr);
|
||||
}
|
||||
sep = new javafx.scene.control.Separator();
|
||||
sep.getStyleClass().add("separator");
|
||||
checkBoxList.add(sep);
|
||||
});
|
||||
modlist.getChildren().clear();
|
||||
modlist.getChildren().addAll(checkBoxList);
|
||||
}
|
||||
};
|
|
@ -1,8 +1,7 @@
|
|||
/*-- DrLeonardo Design --*/
|
||||
|
||||
/* Server buttons */
|
||||
|
||||
.server-button {
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-font-weight: bold;
|
||||
-fx-font-size: 16pt;
|
||||
-fx-background-color: transparent;
|
||||
|
@ -17,14 +16,14 @@ .server-button {
|
|||
}
|
||||
|
||||
.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-Example {
|
||||
-fx-background-image: url('images/servers/example.png');
|
||||
}
|
||||
|
||||
|
||||
/*-- DrLeonardo Design --*/
|
|
@ -1,6 +1,6 @@
|
|||
/*-- Without jfoenix --*/
|
||||
Button {
|
||||
-fx-background-color: TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT;
|
||||
-fx-background-color: TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT;
|
||||
-fx-background-radius: 3px;
|
||||
-fx-background-insets: 0px;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ CheckBox .box-container {
|
|||
|
||||
CheckBox .box,
|
||||
CheckBox:indeterminate .box,
|
||||
CheckBox:indeterminate:selected .box{
|
||||
CheckBox:indeterminate:selected .box {
|
||||
-fx-pref-width: 18;
|
||||
-fx-pref-height: 18;
|
||||
|
||||
|
@ -27,11 +27,11 @@ CheckBox:indeterminate:selected .box{
|
|||
|
||||
CheckBox:selected .box {
|
||||
-fx-border-color: #5fd97a;
|
||||
-fx-background-color: #5fd97a;
|
||||
-fx-background-color: #5fd97a;
|
||||
}
|
||||
|
||||
CheckBox .mark {
|
||||
-fx-background-color: TRANSPARENT;
|
||||
-fx-background-color: TRANSPARENT;
|
||||
}
|
||||
|
||||
CheckBox:selected .mark {
|
||||
|
@ -46,7 +46,7 @@ CheckBox:selected .mark {
|
|||
-fx-border-radius: 2;
|
||||
}
|
||||
|
||||
CheckBox .indeterminate-mark{
|
||||
CheckBox .indeterminate-mark {
|
||||
-fx-background-color: #5fd97a;
|
||||
-fx-background-radius: 2;
|
||||
-fx-border-width: 0;
|
||||
|
@ -56,8 +56,11 @@ CheckBox .indeterminate-mark{
|
|||
|
||||
|
||||
/*-- DrLeonardo Design --*/
|
||||
Button, CheckBox, ComboBox, RadioButton {
|
||||
-fx-cursor: hand;
|
||||
Button,
|
||||
CheckBox,
|
||||
ComboBox,
|
||||
RadioButton {
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
|
||||
|
@ -69,20 +72,22 @@ #layout {
|
|||
-fx-pref-height: 450px;
|
||||
-fx-background-image: url('images/background.jpg');
|
||||
}
|
||||
|
||||
#background {
|
||||
-fx-background-color: #fff;
|
||||
}
|
||||
|
||||
/** Labels **/
|
||||
#background > #settingsTitle {
|
||||
#background>#settingsTitle {
|
||||
-fx-font-size: 14pt;
|
||||
-fx-alignment: baseline-center;
|
||||
}
|
||||
|
||||
#serverLabel{
|
||||
#serverLabel {
|
||||
-fx-text-fill: #323232;
|
||||
}
|
||||
|
||||
#serverStatus{
|
||||
#serverStatus {
|
||||
-fx-text-fill: #323232;
|
||||
-fx-pref-width: 120px;
|
||||
-fx-pref-height: 25px;
|
||||
|
@ -90,17 +95,17 @@ #serverStatus{
|
|||
|
||||
/* 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 **/
|
||||
#errormessage{
|
||||
#errormessage {
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-alignment: center;
|
||||
-fx-text-fill: #CE5757;
|
||||
}
|
||||
|
||||
.error{
|
||||
.error {
|
||||
-fx-text-fill: #CE5757;
|
||||
}
|
||||
|
||||
|
@ -110,51 +115,58 @@ #bar {
|
|||
-fx-pref-width: 46px;
|
||||
-fx-pref-height: 450px;
|
||||
}
|
||||
|
||||
/** buttons in bar **/
|
||||
#close {
|
||||
-fx-background-position: center;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-background-radius: 0;
|
||||
-fx-background-color: #CE5757;
|
||||
-fx-pref-width: 46px;
|
||||
-fx-pref-height: 45px;
|
||||
}
|
||||
#hide, #back, #goConsole, #settings, #discord {
|
||||
|
||||
#hide,
|
||||
#back,
|
||||
#goConsole,
|
||||
#settings,
|
||||
#discord {
|
||||
-fx-background-position: center;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-background-radius: 0;
|
||||
-fx-pref-width: 46px;
|
||||
-fx-pref-height: 45px;
|
||||
}
|
||||
|
||||
#logout{
|
||||
-fx-text-fill:#323232;
|
||||
-fx-font-size:12;
|
||||
-fx-font-weight:normal;
|
||||
-fx-border-color:#CE5757;
|
||||
-fx-border-width:1;
|
||||
-fx-background-color:transparent;
|
||||
-fx-padding:0;
|
||||
#logout {
|
||||
-fx-text-fill: #323232;
|
||||
-fx-font-size: 12;
|
||||
-fx-font-weight: normal;
|
||||
-fx-border-color: #CE5757;
|
||||
-fx-border-width: 1;
|
||||
-fx-background-color: transparent;
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
#logout:hover,
|
||||
#logout:focus{
|
||||
-fx-text-fill:#ff6a5e;
|
||||
#logout:focus {
|
||||
-fx-text-fill: #ff6a5e;
|
||||
}
|
||||
#logout:pressed{
|
||||
-fx-border-color:#cb4d43;
|
||||
|
||||
#logout:pressed {
|
||||
-fx-border-color: #cb4d43;
|
||||
}
|
||||
|
||||
#send {
|
||||
-fx-background-radius: 0;
|
||||
-fx-text-fill: black;
|
||||
-fx-background-position: center;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-background-color: #ffffff;
|
||||
-fx-pref-width: 150px;
|
||||
-fx-pref-height: 30px;
|
||||
}
|
||||
|
||||
#send:pressed { -fx-background-color: #d8d8d8; }
|
||||
#send:pressed {
|
||||
-fx-background-color: #d8d8d8;
|
||||
}
|
||||
|
||||
/* LoginMenu */
|
||||
#authPane {
|
||||
|
@ -172,7 +184,6 @@ #logo {
|
|||
|
||||
/** Buttons & textarea**/
|
||||
.auth {
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-font-weight: bold;
|
||||
-fx-font-size: 13pt;
|
||||
-fx-background-radius: 0;
|
||||
|
@ -180,18 +191,22 @@ .auth {
|
|||
-fx-text-fill: #ffffff;
|
||||
-fx-pref-width: 200px;
|
||||
-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-pref-width: 200px;
|
||||
-fx-pref-height: 30px;
|
||||
}
|
||||
|
||||
.text-input{
|
||||
.text-input {
|
||||
-fx-focus-color: transparent;
|
||||
-fx-background-repeat: no-repeat;
|
||||
-fx-text-fill: #909090;
|
||||
|
@ -212,21 +227,26 @@ #link {
|
|||
-fx-pref-height: 17px;
|
||||
}
|
||||
|
||||
#link:hover, #link:pressed { -fx-opacity: 0.8; }
|
||||
#link:hover,
|
||||
#link:pressed {
|
||||
-fx-opacity: 0.8;
|
||||
}
|
||||
|
||||
/** CheckBox & ComboBox**/
|
||||
#rememberchb{
|
||||
#rememberchb {
|
||||
-fx-font-size: 13;
|
||||
-fx-text-fill: #909090;
|
||||
-fx-pref-width: 145px;
|
||||
-fx-pref-height: 30px;
|
||||
}
|
||||
|
||||
#combologin {
|
||||
-fx-text-fill: #909090;
|
||||
-fx-prompt-text-fill: #909090;
|
||||
-fx-pref-width: 200px;
|
||||
-fx-pref-height: 30px;
|
||||
}
|
||||
|
||||
.combologin,
|
||||
.combologin {
|
||||
-fx-font-size: 13px;
|
||||
|
@ -244,7 +264,7 @@ .combologin .list-cell {
|
|||
.combologin-popup .list-view {
|
||||
-fx-background-color: white, white;
|
||||
-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,
|
||||
|
@ -257,20 +277,18 @@ .combologin .arrow {
|
|||
-fx-background-color: #5fd97a;
|
||||
}
|
||||
|
||||
.combologin-popup .list-view .list-cell
|
||||
{
|
||||
.combologin-popup .list-view .list-cell {
|
||||
-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-color: -fx-selection-bar;
|
||||
-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-text-fill: #909090;
|
||||
}
|
||||
|
@ -290,31 +308,23 @@ .serverentrance {
|
|||
}
|
||||
|
||||
/** buttons **/
|
||||
.clientLaunch{
|
||||
-jfx-button-type: FLAT;
|
||||
.clientLaunch {
|
||||
-fx-font-weight: bold;
|
||||
-fx-font-size: 16pt;
|
||||
-fx-background-radius: 0;
|
||||
-fx-background-color: #5fd97a;
|
||||
-fx-text-fill: #ffffff;
|
||||
}
|
||||
.clientSettings{
|
||||
|
||||
.clientSettings {
|
||||
-fx-background-position: center;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-background-color: #5fd97a;
|
||||
-fx-background-radius: 0;
|
||||
}
|
||||
|
||||
.clientLaunch:hover, .clientLaunch:pressed { -fx-background-color: #75e18c; }
|
||||
|
||||
/* Pressets options */
|
||||
.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;
|
||||
.clientLaunch:hover,
|
||||
.clientLaunch:pressed {
|
||||
-fx-background-color: #75e18c;
|
||||
}
|
||||
|
||||
/* Scrolls */
|
||||
|
@ -356,11 +366,11 @@ .scroll-pane>.corner {
|
|||
}
|
||||
|
||||
/* OptionsPane */
|
||||
#optionsPane > #modlist {
|
||||
#optionsPane>#modlist {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
#optionsPane > #modlist > .viewport {
|
||||
#optionsPane>#modlist>.viewport {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
|
@ -384,39 +394,42 @@ .separator *.line {
|
|||
-fx-border-width: 0 0 10 0;
|
||||
}
|
||||
|
||||
#serverlist{
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
#serverlist > .viewport {
|
||||
#serverlist {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
#serverdesc{
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
#serverdesc > .viewport {
|
||||
#serverlist>.viewport {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
#serverinfo{
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
#serverinfo > .viewport {
|
||||
#serverdesc {
|
||||
-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;
|
||||
-jfx-button-type: FLAT;
|
||||
-fx-pref-width: 282px;
|
||||
-fx-pref-height: 75px;
|
||||
}
|
||||
|
||||
.toggle-button:disabled{
|
||||
.toggle-button:disabled {
|
||||
-fx-opacity: 1.0;
|
||||
}
|
||||
|
||||
.heading{
|
||||
.heading {
|
||||
-fx-text-fill: #555555;
|
||||
}
|
||||
|
||||
/*-- DrLeonardo Design --*/
|
|
@ -41,8 +41,6 @@ var HInput = HInputClass.static;
|
|||
var HOutput = HOutputClass.static;
|
||||
var StreamObject = StreamObjectClass.static;
|
||||
var StreamObjectAdapter = StreamObjectAdapterClass.static;
|
||||
var SignedBytesHolder = SignedBytesHolderClass.static;
|
||||
var SignedObjectHolder = SignedObjectHolderClass.static;
|
||||
var EnumSerializer = EnumSerializerClass.static;
|
||||
var OptionalFile = OptionalFileClass.static;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ var settingsManagerClass = Java.extend(SettingsManagerClass.static, {
|
|||
new_settings.fullScreen = config.fullScreenDefault;
|
||||
new_settings.ram = config.ramDefault;
|
||||
|
||||
new_settings.featureStore = config.featureStoreDefault;
|
||||
new_settings.lastDigest = null;
|
||||
new_settings.lastProfiles.clear();
|
||||
new_settings.lastHDirs.clear();
|
||||
|
|
|
@ -9,7 +9,8 @@ var LauncherApp = Java.extend(JSApplication, {
|
|||
settings = SettingsManager.settings;
|
||||
settingsManager.loadHDirStore();
|
||||
cliParams.applySettings();
|
||||
}, start: function(primaryStage) {
|
||||
},
|
||||
start: function(primaryStage) {
|
||||
stage = primaryStage;
|
||||
stage.initStyle(javafx.stage.StageStyle.TRANSPARENT);
|
||||
stage.setResizable(false);
|
||||
|
@ -45,7 +46,8 @@ var LauncherApp = Java.extend(JSApplication, {
|
|||
setCurrentScene(loginScene);
|
||||
initLauncher();
|
||||
|
||||
}, stop: function() {
|
||||
},
|
||||
stop: function() {
|
||||
settingsManager.saveConfig();
|
||||
settingsManager.saveHDirStore();
|
||||
options.save();
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -22,6 +21,7 @@
|
|||
import cpw.mods.fml.SafeExitJVMLegacy;
|
||||
import net.minecraftforge.fml.SafeExitJVM;
|
||||
import pro.gravit.launcher.utils.NativeJVMHalt;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
@LauncherAPI
|
||||
|
@ -46,19 +46,20 @@ public static void premain(String agentArgument, Instrumentation instrumentation
|
|||
NativeJVMHalt.class.getName();
|
||||
NativeJVMHalt.initFunc();
|
||||
isAgentStarted = true;
|
||||
boolean pb = true;
|
||||
boolean rt = true;
|
||||
if (agentArgument != null) {
|
||||
String trimmedArg = agentArgument.trim();
|
||||
if (!trimmedArg.isEmpty()) {
|
||||
if (trimmedArg.contains("p")) pb = false;
|
||||
if (trimmedArg.contains("r")) rt = false;
|
||||
}
|
||||
}
|
||||
if (System.getProperty("java.vm.name").toUpperCase(Locale.US).contains("HOTSPOT"))
|
||||
try {
|
||||
if (ManagementFactory.getOperatingSystemMXBean().getName().startsWith("Windows")) replaceClasses(pb, rt);
|
||||
else replaceClasses(false, false);
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
|
||||
boolean pb = true;
|
||||
boolean rt = true;
|
||||
if (agentArgument != null) {
|
||||
String trimmedArg = agentArgument.trim();
|
||||
if (!trimmedArg.isEmpty()) {
|
||||
if (trimmedArg.contains("p")) pb = false;
|
||||
if (trimmedArg.contains("r")) rt = false;
|
||||
}
|
||||
}
|
||||
replaceClasses(pb, rt);
|
||||
} else replaceClasses(false, false);
|
||||
} catch (Error e) {
|
||||
NativeJVMHalt.haltA(294);
|
||||
throw e;
|
||||
|
|
|
@ -14,11 +14,15 @@
|
|||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
|
@ -55,16 +59,16 @@
|
|||
|
||||
public final class ClientLauncher {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
|
||||
result.add(file);
|
||||
result.accept(file);
|
||||
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());
|
||||
}
|
||||
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
|
||||
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);
|
||||
JVMHelper.fullGC();
|
||||
// Invoke main method
|
||||
|
@ -295,6 +306,7 @@ private static void launch(ClientProfile profile, Params params) throws Throwabl
|
|||
private static Process process = null;
|
||||
private static boolean clientStarted = false;
|
||||
private static Thread writeParamsThread;
|
||||
public static PlayerProfile playerProfile;
|
||||
|
||||
@LauncherAPI
|
||||
public static Process launch(
|
||||
|
@ -407,7 +419,7 @@ public static Process launch(
|
|||
Thread.sleep(200);
|
||||
}
|
||||
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;
|
||||
|
@ -450,6 +462,7 @@ public static void main(String... args) throws Throwable {
|
|||
return;
|
||||
}
|
||||
Launcher.profile = profile;
|
||||
playerProfile = params.pp;
|
||||
Request.setSession(params.session);
|
||||
checkJVMBitsAndVersion();
|
||||
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 {
|
||||
Collection<Path> result = new LinkedList<>();
|
||||
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);
|
||||
return resolveClassPathStream(clientDir, classPath).map(IOHelper::toURL).toArray(URL[]::new);
|
||||
}
|
||||
|
||||
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) {
|
||||
Path path = clientDir.resolve(IOHelper.toPath(classPathEntry));
|
||||
if (IOHelper.isDir(path)) { // Recursive walking and adding
|
||||
IOHelper.walk(path, new ClassPathFileVisitor(result), false);
|
||||
IOHelper.walk(path, new ClassPathFileVisitor(builder), false);
|
||||
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.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 {
|
||||
if (matcher != null)
|
||||
matcher = matcher.verifyOnly();
|
||||
//if (matcher != null)
|
||||
// matcher = matcher.verifyOnly();
|
||||
|
||||
// Hash directory and compare (ignore update-only matcher entries, it will break offline-mode)
|
||||
HashedDir currentHDir = new HashedDir(dir, matcher, true, digest);
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
import pro.gravit.launcher.managers.SimpleModuleManager;
|
||||
|
||||
public class ClientModuleManager extends SimpleModuleManager {
|
||||
public ClientModuleManager(LauncherEngine engine) {
|
||||
|
||||
public ClientModuleManager(LauncherEngine engine) {
|
||||
context = new ClientModuleContext(engine);
|
||||
modules = new ArrayList<>();
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
import pro.gravit.launcher.managers.HasherManager;
|
||||
import pro.gravit.launcher.managers.HasherStore;
|
||||
import pro.gravit.launcher.request.Request;
|
||||
import pro.gravit.launcher.serialize.signed.SignedObjectHolder;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.Version;
|
||||
|
||||
public class FunctionalBridge {
|
||||
@LauncherAPI
|
||||
|
@ -33,12 +33,12 @@ public class FunctionalBridge {
|
|||
private static long cachedMemorySize = -1;
|
||||
|
||||
@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 () -> {
|
||||
if (hdir == null) {
|
||||
Request.requestError(java.lang.String.format("Директории '%s' нет в кэше", dirName));
|
||||
}
|
||||
ClientLauncher.verifyHDir(dir, hdir.object, matcher, digest);
|
||||
ClientLauncher.verifyHDir(dir, hdir, matcher, digest);
|
||||
return hdir;
|
||||
};
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ public static void setAuthParams(AuthRequestEvent event) {
|
|||
|
||||
@FunctionalInterface
|
||||
public interface HashedDirRunnable {
|
||||
SignedObjectHolder<HashedDir> run() throws Exception;
|
||||
HashedDir run() throws Exception;
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
|
@ -112,4 +112,14 @@ public static void evalCommand(String cmd) {
|
|||
public static void addPlainOutput(LogHelper.Output output) {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,9 @@ public void postDiff(UpdateRequest request, UpdateRequestEvent e, HashedDir.Diff
|
|||
//if(file.isSame(ret, true))
|
||||
{
|
||||
Path source = request.getDir().resolve(path);
|
||||
LogHelper.debug("Copy file %s to %s", ret.toAbsolutePath().toString(), source.toAbsolutePath().toString());
|
||||
if (LogHelper.isDebugEnabled()) {
|
||||
LogHelper.debug("Copy file %s to %s", ret.toAbsolutePath().toString(), source.toAbsolutePath().toString());
|
||||
}
|
||||
//Let's go!
|
||||
Files.copy(ret, source);
|
||||
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;
|
||||
HashedFile tfile = (HashedFile) entry;
|
||||
if (tfile.isSame(file)) {
|
||||
LogHelper.dev("[DIR:%s] Found file %s in %s", en.name, name, path);
|
||||
if (LogHelper.isDevEnabled()) {
|
||||
LogHelper.dev("[DIR:%s] Found file %s in %s", en.name, name, path);
|
||||
}
|
||||
Path tdir = Paths.get(en.fullPath).resolve(path);
|
||||
try {
|
||||
if (tfile.isSame(tdir, true)) {
|
||||
LogHelper.dev("[DIR:%s] Confirmed file %s in %s", en.name, name, path);
|
||||
if (LogHelper.isDevEnabled()) {
|
||||
LogHelper.dev("[DIR:%s] Confirmed file %s in %s", en.name, name, path);
|
||||
}
|
||||
ret.set(tdir);
|
||||
return HashedDir.WalkAction.STOP;
|
||||
}
|
||||
|
|
|
@ -49,8 +49,6 @@
|
|||
import pro.gravit.launcher.request.uuid.ProfileByUsernameRequest;
|
||||
import pro.gravit.launcher.serialize.HInput;
|
||||
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.StreamObject;
|
||||
import pro.gravit.utils.HTTPRequest;
|
||||
|
@ -64,7 +62,7 @@
|
|||
|
||||
public class JSRuntimeProvider implements RuntimeProvider {
|
||||
|
||||
private final ScriptEngine engine = CommonHelper.newScriptEngine();
|
||||
public final ScriptEngine engine = CommonHelper.newScriptEngine();
|
||||
private boolean isPreLoaded = false;
|
||||
|
||||
@LauncherAPI
|
||||
|
@ -112,8 +110,6 @@ public static void addLauncherClassBindings(Map<String, Object> bindings) {
|
|||
bindings.put("HOutputClass", HOutput.class);
|
||||
bindings.put("StreamObjectClass", StreamObject.class);
|
||||
bindings.put("StreamObjectAdapterClass", StreamObject.Adapter.class);
|
||||
bindings.put("SignedBytesHolderClass", SignedBytesHolder.class);
|
||||
bindings.put("SignedObjectHolderClass", SignedObjectHolder.class);
|
||||
bindings.put("EnumSerializerClass", EnumSerializer.class);
|
||||
bindings.put("OptionalFileClass", OptionalFile.class);
|
||||
bindings.put("UserSettingsClass", UserSettings.class);
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import oshi.hardware.HWDiskStore;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
import oshi.hardware.NetworkIF;
|
||||
import oshi.hardware.SoundCard;
|
||||
import oshi.hardware.UsbDevice;
|
||||
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() {
|
||||
for (NetworkIF networkIF : hardware.getNetworkIFs()) {
|
||||
if (networkIF.getNetworkInterface().isVirtual()) continue;
|
||||
for (String ipv4 : networkIF.getIPv4addr()) {
|
||||
if (ipv4.startsWith("127.")) continue;
|
||||
if (ipv4.startsWith("10.")) continue;
|
||||
|
@ -119,9 +112,6 @@ public void printHardwareInformation() {
|
|||
LogHelper.subDebug("IPv4: %s", ipv4);
|
||||
}
|
||||
}
|
||||
for (SoundCard soundcard : hardware.getSoundCards()) {
|
||||
LogHelper.debug("SoundCard %s", soundcard.getName());
|
||||
}
|
||||
CentralProcessor processor = hardware.getProcessor();
|
||||
LogHelper.debug("Processor Model: %s ID: %s", processor.getModel(), processor.getProcessorID());
|
||||
} catch (Throwable e) {
|
||||
|
|
|
@ -27,15 +27,6 @@
|
|||
|
||||
public final class DirWatcher implements Runnable, AutoCloseable {
|
||||
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
|
||||
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
|
||||
path.add(IOHelper.getFileName(dir));
|
||||
//if (matcher != null && !matcher.shouldVerify(path)) {
|
||||
// return FileVisitResult.SKIP_SUBTREE;
|
||||
//}
|
||||
|
|
|
@ -6,3 +6,36 @@ compile project(':LauncherCore')
|
|||
compileOnly 'org.apache.httpcomponents:httpclient:4.5.7'
|
||||
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
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ public class AutogenConfig {
|
|||
public String projectname;
|
||||
public String address;
|
||||
public int clientPort;
|
||||
@SuppressWarnings("unused")
|
||||
private boolean isInitModules;
|
||||
public String guardType;
|
||||
public String secretKeyClient;
|
||||
public String oemUnlockKey;
|
||||
|
@ -23,5 +21,6 @@ public class AutogenConfig {
|
|||
}
|
||||
|
||||
public void initModules() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,6 @@ public class ClientPermissions {
|
|||
public boolean canUSR3;
|
||||
@LauncherAPI
|
||||
public boolean canBot;
|
||||
@LauncherAPI
|
||||
public boolean canProxy;
|
||||
|
||||
public ClientPermissions(HInput input) throws IOException {
|
||||
this(input.readLong());
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
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.ZipInputStream;
|
||||
|
||||
|
@ -19,10 +22,16 @@
|
|||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.client.LaxRedirectStrategy;
|
||||
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.*;
|
||||
|
||||
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
|
||||
public interface DownloadCallback {
|
||||
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()
|
||||
.setRedirectStrategy(new LaxRedirectStrategy())
|
||||
.build()) {
|
||||
|
||||
HttpGet get = null;
|
||||
applies.sort((a,b) -> Long.compare(a.size, b.size));
|
||||
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) {
|
||||
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);
|
||||
Path targetPath = dstDirFile.resolve(apply.apply);
|
||||
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);
|
||||
else {
|
||||
get.reset();
|
||||
get.setURI(u);
|
||||
}
|
||||
httpclient.execute(get, new FileDownloadResponseHandler(targetPath, apply, callback, totalCallback, false));
|
||||
toExec.add(() -> {
|
||||
if (LogHelper.isDebugEnabled())
|
||||
LogHelper.debug("Download URL: %s to file %s dir: %s", u.toString(), targetPath.toAbsolutePath().toString(), dstDirFile.toAbsolutePath().toString());
|
||||
try {
|
||||
httpclient.execute(new HttpGet(u), new FileDownloadResponseHandler(targetPath, apply, callback, totalCallback, false));
|
||||
} catch (IOException e) {
|
||||
excs.add(e);
|
||||
}
|
||||
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;
|
||||
URI u = new URL(url).toURI();
|
||||
LogHelper.debug("Download URL: %s", u.toString());
|
||||
if (LogHelper.isDebugEnabled()) {
|
||||
LogHelper.debug("Download URL: %s", u.toString());
|
||||
}
|
||||
get = new HttpGet(u);
|
||||
httpclient.execute(get, new FileDownloadResponseHandler(target.toAbsolutePath()));
|
||||
}
|
||||
|
@ -161,7 +196,9 @@ public Path handleResponse(HttpResponse response) throws IOException {
|
|||
if (callback != null) {
|
||||
callback.stateChanged(entry.getName(), 0, entry.getSize());
|
||||
}
|
||||
LogHelper.dev("Resolved filename %s to %s", filename, target.toAbsolutePath().toString());
|
||||
if (LogHelper.isDevEnabled()) {
|
||||
LogHelper.dev("Resolved filename %s to %s", filename, target.toAbsolutePath().toString());
|
||||
}
|
||||
transfer(source, target, filename, size, callback, totalCallback);
|
||||
entry = input.getNextEntry();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
//Набор стандартных событий
|
||||
public class ControlEvent {
|
||||
private static final UUID uuid = UUID.fromString("f1051a64-0cd0-4ed8-8430-d856a196e91f");
|
||||
@SuppressWarnings("unused")
|
||||
private static final UUID uuid = UUID.fromString("f1051a64-0cd0-4ed8-8430-d856a196e91f");
|
||||
|
||||
public enum ControlCommand {
|
||||
STOP, START, PAUSE, CONTINUE, CRASH
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package pro.gravit.launcher.events.request;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import pro.gravit.launcher.ClientPermissions;
|
||||
import pro.gravit.launcher.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.events.RequestEvent;
|
||||
|
|