Merge branch 'release/5.0.6'

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

3
.gitmodules vendored
View file

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

View file

@ -40,10 +40,24 @@
)
}
task cleanjar(type: Jar, dependsOn: jar) {
classifier = 'clean'
manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName,
"Can-Redefine-Classes": "true",
"Can-Retransform-Classes": "true",
"Can-Set-Native-Method-Prefix": "true"
)
from sourceSets.main.output
}
dependencies {
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
}

View file

@ -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);

View file

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

View file

@ -24,6 +24,7 @@ public static void registerHandlers() {
providers.register("json", JsonAuthHandler.class);
providers.register("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;

View file

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

View file

@ -153,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);

View file

@ -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);

View file

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

View file

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

View file

@ -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));
}
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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() + "\'");

View file

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

View file

@ -19,6 +19,7 @@
import pro.gravit.launchserver.asm.ClassMetadataReader;
import pro.gravit.launchserver.asm.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();

View file

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

View file

@ -16,8 +16,9 @@
import java.util.zip.ZipInputStream;
import java.util.zip.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;

View file

@ -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());

View file

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

View file

@ -1,16 +1,15 @@
package pro.gravit.launchserver.command.basic;
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];

View file

@ -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);
}

View file

@ -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) {

View file

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

View file

@ -1,18 +1,17 @@
package pro.gravit.launchserver.components;
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);
}
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);

View file

@ -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 {

View file

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

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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 {

View file

@ -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())));
}
}

View file

@ -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);
}

View file

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

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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 {

View file

@ -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);

View file

@ -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;

View file

@ -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);
}

View file

@ -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) {

View file

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

View file

@ -45,12 +45,11 @@ public AuthResponse(String login, String password, String auth_id, OshiHWID hwid
}
public String auth_id;
public 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;

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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)));
}
}

View file

@ -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)));
}
}

View file

@ -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;

View file

@ -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));
}

View file

@ -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));
}
}

View file

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

View file

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

View file

@ -39,7 +39,6 @@
dependencies {
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
}

View file

@ -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);

View file

@ -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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

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

View file

@ -4,55 +4,54 @@ Button {
}
/* Outputs */
#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 --*/

View file

@ -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>

View file

@ -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));
}

View file

@ -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 --*/

View file

@ -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));

View file

@ -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 --*/

View file

@ -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>

View file

@ -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;
}

View file

@ -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 --*/

View file

@ -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("Состояние: Хеширование");

View file

@ -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>

View file

@ -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>

View file

@ -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" />

View file

@ -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>

View file

@ -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);
}
};

View file

@ -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 --*/

View file

@ -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 --*/

View file

@ -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;

View file

@ -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();

View file

@ -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();

View file

@ -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;

View file

@ -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);

View file

@ -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<>();
}

View file

@ -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
);
}
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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) {

View file

@ -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;
//}

View file

@ -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
}

View file

@ -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() {
}
}

View file

@ -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());

View file

@ -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();
}

View file

@ -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

View file

@ -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;

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