mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-03-26 11:08:15 +03:00
Compare commits
80 commits
Author | SHA1 | Date | |
---|---|---|---|
|
df77a1ebd6 | ||
|
641796b44e | ||
|
b7ed56b27e | ||
|
f119bd4b30 | ||
|
bbff0eac64 | ||
|
41f93b9f8d | ||
|
263cf26258 | ||
|
b9ad7c0f26 | ||
|
5ba32e3405 | ||
|
907332ff06 | ||
|
1d63fbbd93 | ||
|
2f4667f5a6 | ||
|
92ada65079 | ||
|
29d98defff | ||
|
d7a379383b | ||
|
0111b2ca2b | ||
|
4671dfe49d | ||
|
bb63aaa0ab | ||
|
912caa6b8a | ||
|
926094076c | ||
|
a1af61a599 | ||
|
81b16cb54e | ||
|
a486f21fa2 | ||
|
86ea247f07 | ||
|
d26b179006 | ||
|
c4d1251429 | ||
|
f16f5fbc6d | ||
|
abe904d73c | ||
|
eaf685897f | ||
|
c8934d887a | ||
|
070a5d9b69 | ||
|
cc2bce4300 | ||
|
c7f4d8ac49 | ||
|
7dcb08fdaf | ||
|
9d870849a1 | ||
|
dda3ebc7b4 | ||
|
537623afaf | ||
|
0cff6e247a | ||
|
b85075c559 | ||
|
3e654f4d79 | ||
|
46f1f7b69e | ||
|
981f2ac3dd | ||
|
8509cbb6b5 | ||
|
1119625d12 | ||
|
9a69426547 | ||
|
31cbfe2919 | ||
|
27ebadcd19 | ||
|
b3349044b5 | ||
|
c43edeb982 | ||
|
1ff58099bd | ||
|
9fba637f83 | ||
|
ca70ee78d1 | ||
|
5e116a81e5 | ||
|
b0f799d194 | ||
|
63f9f8e21d | ||
|
b8ccbc5e48 | ||
|
a687c5afd8 | ||
|
3969d81db7 | ||
|
a30d0624a1 | ||
|
d5abe0d411 | ||
|
1e7a856a99 | ||
|
e6f5b585a7 | ||
|
2ed4abf9b0 | ||
|
af2dcec8cd | ||
|
9bffe07d36 | ||
|
4be299f6ca | ||
|
ef4f14f9b4 | ||
|
d720328bc4 | ||
|
88f1eaf750 | ||
|
a5ef86b105 | ||
|
b1a5ecdc13 | ||
|
68e9affbe0 | ||
|
7d7485afdc | ||
|
c2926b5b40 | ||
|
9c82d76781 | ||
|
450774de7e | ||
|
f88c0308f8 | ||
|
20f713be05 | ||
|
5bf92d9a00 | ||
|
903c4d40c5 |
53 changed files with 1263 additions and 411 deletions
|
@ -13,20 +13,24 @@
|
|||
maven {
|
||||
url "https://jitpack.io/"
|
||||
}
|
||||
maven {
|
||||
url 'https://maven.gravit-support.ru/repository/jitpack'
|
||||
credentials {
|
||||
username = 'gravitlauncher'
|
||||
password = 'gravitlauncher'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceCompatibility = '21'
|
||||
targetCompatibility = '21'
|
||||
|
||||
configurations {
|
||||
compileOnlyA
|
||||
bundleOnly
|
||||
bundle
|
||||
hikari
|
||||
pack
|
||||
launch4j
|
||||
bundleOnly.extendsFrom bundle
|
||||
api.extendsFrom bundle, hikari, pack, launch4j
|
||||
api.extendsFrom bundle, pack
|
||||
}
|
||||
|
||||
jar {
|
||||
|
@ -80,6 +84,14 @@ pack project(':LauncherAPI')
|
|||
bundle group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty']
|
||||
bundle group: 'io.netty', name: 'netty-transport-classes-epoll', version: rootProject['verNetty']
|
||||
bundle group: 'io.netty', name: 'netty-transport-native-epoll', version: rootProject['verNetty'], classifier: 'linux-x86_64'
|
||||
bundle group: 'io.netty', name: 'netty-transport-native-epoll', version: rootProject['verNetty'], classifier: 'linux-aarch_64'
|
||||
bundle group: 'io.netty', name: 'netty-transport-classes-io_uring', version: rootProject['verNetty']
|
||||
bundle group: 'io.netty', name: 'netty-transport-native-io_uring', version: rootProject['verNetty'], classifier: 'linux-x86_64'
|
||||
bundle group: 'io.netty', name: 'netty-transport-native-io_uring', version: rootProject['verNetty'], classifier: 'linux-aarch_64'
|
||||
// Netty
|
||||
bundle 'org.jboss.marshalling:jboss-marshalling:1.4.11.Final'
|
||||
bundle 'com.google.protobuf.nano:protobuf-javanano:3.1.0'
|
||||
//
|
||||
bundle group: 'org.slf4j', name: 'slf4j-api', version: rootProject['verSlf4j']
|
||||
bundle group: 'com.mysql', name: 'mysql-connector-j', version: rootProject['verMySQLConn']
|
||||
bundle group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: rootProject['verMariaDBConn']
|
||||
|
@ -95,40 +107,26 @@ pack project(':LauncherAPI')
|
|||
annotationProcessor(group: 'org.apache.logging.log4j', name: 'log4j-core', version: rootProject['verLog4j'])
|
||||
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: rootProject['verJunit']
|
||||
|
||||
hikari 'io.micrometer:micrometer-core:1.13.1'
|
||||
hikari('com.zaxxer:HikariCP:5.1.0') {
|
||||
bundle 'io.micrometer:micrometer-core:1.14.4'
|
||||
bundle('com.zaxxer:HikariCP:6.2.1') {
|
||||
exclude group: 'javassist'
|
||||
exclude group: 'io.micrometer'
|
||||
exclude group: 'org.slf4j'
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('hikari', Copy) {
|
||||
duplicatesStrategy = 'EXCLUDE'
|
||||
into "$buildDir/libs/libraries/hikaricp"
|
||||
from configurations.hikari
|
||||
}
|
||||
|
||||
tasks.register('dumpLibs', Copy) {
|
||||
duplicatesStrategy = 'EXCLUDE'
|
||||
dependsOn tasks.hikari
|
||||
into "$buildDir/libs/libraries"
|
||||
from configurations.bundleOnly
|
||||
}
|
||||
|
||||
tasks.register('dumpCompileOnlyLibs', Copy) {
|
||||
duplicatesStrategy = 'EXCLUDE'
|
||||
into "$buildDir/libs/launcher-libraries-compile"
|
||||
from configurations.compileOnlyA
|
||||
}
|
||||
|
||||
tasks.register('bundle', Zip) {
|
||||
duplicatesStrategy = 'EXCLUDE'
|
||||
dependsOn parent.childProjects.Launcher.tasks.build, tasks.dumpLibs, tasks.dumpCompileOnlyLibs, tasks.jar
|
||||
dependsOn parent.childProjects.Launcher.tasks.build, tasks.dumpLibs, tasks.jar
|
||||
archiveFileName = 'LaunchServer.zip'
|
||||
destinationDirectory = file("$buildDir")
|
||||
from(tasks.dumpLibs.destinationDir) { into 'libraries' }
|
||||
from(tasks.dumpCompileOnlyLibs.destinationDir) { into 'launcher-libraries-compile' }
|
||||
from(tasks.jar)
|
||||
from(parent.childProjects.Launcher.tasks.dumpLibs) { into 'launcher-libraries' }
|
||||
}
|
||||
|
@ -139,7 +137,7 @@ pack project(':LauncherAPI')
|
|||
from parent.childProjects.Launcher.tasks.dumpLibs
|
||||
}
|
||||
|
||||
assemble.dependsOn tasks.dumpLibs, tasks.dumpCompileOnlyLibs, tasks.dumpClientLibs, tasks.bundle, tasks.cleanjar
|
||||
assemble.dependsOn tasks.dumpLibs, tasks.dumpClientLibs, tasks.bundle, tasks.cleanjar
|
||||
|
||||
|
||||
publishing {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
import pro.gravit.launchserver.modules.events.*;
|
||||
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.SocketCommandServer;
|
||||
import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler;
|
||||
import pro.gravit.launchserver.socket.response.auth.ProfilesResponse;
|
||||
import pro.gravit.launchserver.socket.response.auth.RestoreResponse;
|
||||
|
@ -70,6 +71,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
|
|||
/**
|
||||
* The path to the folder with updates/webroot
|
||||
*/
|
||||
@Deprecated
|
||||
public final Path updatesDir;
|
||||
|
||||
// Constant paths
|
||||
|
@ -80,11 +82,11 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
|
|||
/**
|
||||
* The path to the folder with profiles
|
||||
*/
|
||||
public final Path profilesDir;
|
||||
public final Path tmpDir;
|
||||
public final Path modulesDir;
|
||||
public final Path launcherModulesDir;
|
||||
public final Path librariesDir;
|
||||
public final Path controlFile;
|
||||
/**
|
||||
* This object contains runtime configuration
|
||||
*/
|
||||
|
@ -113,14 +115,13 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
|
|||
// Server
|
||||
public final CommandHandler commandHandler;
|
||||
public final NettyServerSocketHandler nettyServerSocketHandler;
|
||||
public final SocketCommandServer socketCommandServer;
|
||||
public final ScheduledExecutorService service;
|
||||
public final AtomicBoolean started = new AtomicBoolean(false);
|
||||
public final LauncherModuleLoader launcherModuleLoader;
|
||||
private final Logger logger = LogManager.getLogger();
|
||||
public final int shardId;
|
||||
public LaunchServerConfig config;
|
||||
// Updates and profiles
|
||||
private volatile Set<ClientProfile> profilesList;
|
||||
|
||||
public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, KeyAgreementManager keyAgreementManager, CommandHandler commandHandler, CertificateManager certificateManager, int shardId) throws IOException {
|
||||
this.dir = directories.dir;
|
||||
|
@ -129,7 +130,6 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
|||
this.config = config;
|
||||
this.launchServerConfigManager = launchServerConfigManager;
|
||||
this.modulesManager = modulesManager;
|
||||
this.profilesDir = directories.profilesDir;
|
||||
this.updatesDir = directories.updatesDir;
|
||||
this.keyAgreementManager = keyAgreementManager;
|
||||
this.commandHandler = commandHandler;
|
||||
|
@ -141,7 +141,8 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
|||
launcherPack = directories.launcherPackDir;
|
||||
modulesDir = directories.modules;
|
||||
launcherModulesDir = directories.launcherModules;
|
||||
librariesDir = directories.launcherLibrariesDir;
|
||||
librariesDir = directories.librariesDir;
|
||||
controlFile = directories.controlFile;
|
||||
this.shardId = shardId;
|
||||
if(!Files.isDirectory(launcherPack)) {
|
||||
Files.createDirectories(launcherPack);
|
||||
|
@ -193,6 +194,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
|||
}
|
||||
launcherModuleLoader.init();
|
||||
nettyServerSocketHandler = new NettyServerSocketHandler(this);
|
||||
socketCommandServer = new SocketCommandServer(commandHandler, controlFile);
|
||||
if(config.sign.checkCertificateExpired) {
|
||||
checkCertificateExpired();
|
||||
service.scheduleAtFixedRate(this::checkCertificateExpired, 24, 24, TimeUnit.HOURS);
|
||||
|
@ -326,12 +328,14 @@ public void close() throws Exception {
|
|||
logger.info("LaunchServer stopped");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Set<ClientProfile> getProfiles() {
|
||||
return profilesList;
|
||||
return config.profileProvider.getProfiles();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setProfiles(Set<ClientProfile> profilesList) {
|
||||
this.profilesList = Collections.unmodifiableSet(profilesList);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void rebindNettyServerSocket() {
|
||||
|
@ -354,18 +358,15 @@ public void run() {
|
|||
}
|
||||
}));
|
||||
CommonHelper.newThread("Command Thread", true, commandHandler).start();
|
||||
CommonHelper.newThread("Socket Command Thread", true, socketCommandServer).start();
|
||||
// Sync updates dir
|
||||
CommonHelper.newThread("Profiles and updates sync", true, () -> {
|
||||
try {
|
||||
// Sync profiles dir
|
||||
if (!IOHelper.isDir(profilesDir))
|
||||
Files.createDirectory(profilesDir);
|
||||
syncProfilesDir();
|
||||
|
||||
// Sync updates dir
|
||||
if (!IOHelper.isDir(updatesDir))
|
||||
Files.createDirectory(updatesDir);
|
||||
updatesManager.readUpdatesDir();
|
||||
config.updatesProvider.syncInitially();
|
||||
|
||||
|
||||
modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this));
|
||||
|
@ -402,12 +403,7 @@ public void syncLauncherBinaries() throws IOException {
|
|||
|
||||
public void syncProfilesDir() throws IOException {
|
||||
logger.info("Syncing profiles dir");
|
||||
List<ClientProfile> newProfies = new LinkedList<>();
|
||||
IOHelper.walk(profilesDir, new ProfilesFileVisitor(newProfies), false);
|
||||
|
||||
// Sort and set new profiles
|
||||
newProfies.sort(Comparator.comparing(a -> a));
|
||||
profilesList = Set.copyOf(newProfies);
|
||||
config.profileProvider.sync();
|
||||
if (config.netty.sendProfileUpdatesEvent) {
|
||||
sendUpdateProfilesEvent();
|
||||
}
|
||||
|
@ -422,7 +418,7 @@ private void sendUpdateProfilesEvent() {
|
|||
if (client == null || !client.isAuth) {
|
||||
return;
|
||||
}
|
||||
ProfilesRequestEvent event = new ProfilesRequestEvent(ProfilesResponse.getListVisibleProfiles(this, client));
|
||||
ProfilesRequestEvent event = new ProfilesRequestEvent(config.profileProvider.getProfiles(client));
|
||||
event.requestUUID = RequestEvent.eventUUID;
|
||||
handler.service.sendObject(ch, event);
|
||||
});
|
||||
|
@ -468,38 +464,11 @@ public interface LaunchServerConfigManager {
|
|||
void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException;
|
||||
}
|
||||
|
||||
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
|
||||
private final Collection<ClientProfile> result;
|
||||
private final Logger logger = LogManager.getLogger();
|
||||
|
||||
private ProfilesFileVisitor(Collection<ClientProfile> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
logger.info("Syncing '{}' profile", IOHelper.getFileName(file));
|
||||
|
||||
// Read profile
|
||||
ClientProfile profile;
|
||||
try (BufferedReader reader = IOHelper.newReader(file)) {
|
||||
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
|
||||
}
|
||||
profile.verify();
|
||||
profile.setProfileFilePath(file);
|
||||
|
||||
// Add SIGNED profile to result list
|
||||
result.add(profile);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
public static class LaunchServerDirectories {
|
||||
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles",
|
||||
public static final String UPDATES_NAME = "updates",
|
||||
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
|
||||
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", LAUNCHERPACK_NAME = "launcher-pack", KEY_NAME = ".keys", MODULES = "modules", LAUNCHER_MODULES = "launcher-modules", LIBRARIES = "libraries";
|
||||
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", LAUNCHERPACK_NAME = "launcher-pack", KEY_NAME = ".keys", MODULES = "modules", LAUNCHER_MODULES = "launcher-modules", LIBRARIES = "libraries", CONTROL_FILE = "control-file";
|
||||
public Path updatesDir;
|
||||
public Path profilesDir;
|
||||
public Path librariesDir;
|
||||
public Path launcherLibrariesDir;
|
||||
public Path launcherLibrariesCompileDir;
|
||||
|
@ -510,10 +479,10 @@ public static class LaunchServerDirectories {
|
|||
public Path tmpDir;
|
||||
public Path modules;
|
||||
public Path launcherModules;
|
||||
public Path controlFile;
|
||||
|
||||
public void collect() {
|
||||
if (updatesDir == null) updatesDir = getPath(UPDATES_NAME);
|
||||
if (profilesDir == null) profilesDir = getPath(PROFILES_NAME);
|
||||
if (trustStore == null) trustStore = getPath(TRUSTSTORE_NAME);
|
||||
if (launcherLibrariesDir == null) launcherLibrariesDir = getPath(LAUNCHERLIBRARIES_NAME);
|
||||
if (launcherLibrariesCompileDir == null)
|
||||
|
@ -524,6 +493,7 @@ public void collect() {
|
|||
if (modules == null) modules = getPath(MODULES);
|
||||
if (launcherModules == null) launcherModules = getPath(LAUNCHER_MODULES);
|
||||
if (librariesDir == null) librariesDir = getPath(LIBRARIES);
|
||||
if (controlFile == null) controlFile = getPath(CONTROL_FILE);
|
||||
if (tmpDir == null)
|
||||
tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve("launchserver-%s".formatted(SecurityHelper.randomStringToken()));
|
||||
}
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.mix.MixProvider;
|
||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||
import pro.gravit.launchserver.auth.profiles.ProfileProvider;
|
||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
||||
import pro.gravit.launchserver.auth.updates.UpdatesProvider;
|
||||
import pro.gravit.launchserver.components.Component;
|
||||
import pro.gravit.launchserver.config.LaunchServerConfig;
|
||||
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
|
||||
|
@ -61,7 +63,7 @@ public static void main(String[] args) throws Exception {
|
|||
directories.collect();
|
||||
CertificateManager certificateManager = new CertificateManager();
|
||||
try {
|
||||
certificateManager.readTrustStore(dir.resolve("truststore"));
|
||||
certificateManager.readTrustStore(directories.trustStore);
|
||||
} catch (CertificateException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
@ -178,6 +180,8 @@ public static void registerAll() {
|
|||
OptionalAction.registerProviders();
|
||||
OptionalTrigger.registerProviders();
|
||||
MixProvider.registerProviders();
|
||||
ProfileProvider.registerProviders();
|
||||
UpdatesProvider.registerProviders();
|
||||
}
|
||||
|
||||
private static void printExperimentalBranch() {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
import java.util.stream.Stream;
|
||||
|
||||
public class Main {
|
||||
private static final List<String> classpathOnly = List.of("proguard", "jline", "progressbar", "kotlin", "epoll");
|
||||
private static final List<String> classpathOnly = List.of("proguard", "jline", "progressbar", "kotlin");
|
||||
private static final String LOG4J_PROPERTY = "log4j2.configurationFile";
|
||||
private static final String DEBUG_PROPERTY = "launchserver.main.debug";
|
||||
private static final String LIBRARIES_PROPERTY = "launchserver.dir.libraries";
|
||||
|
|
|
@ -9,15 +9,22 @@
|
|||
import java.util.function.Consumer;
|
||||
|
||||
public class HikariSQLSourceConfig implements SQLSourceConfig {
|
||||
private transient HikariDataSource dataSource;
|
||||
private transient volatile HikariDataSource dataSource;
|
||||
private String dsClass;
|
||||
private Properties dsProps;
|
||||
private String driverClass;
|
||||
private String jdbcUrl;
|
||||
private String username;
|
||||
private String password;
|
||||
private boolean initializeAtStart;
|
||||
|
||||
public void init() {
|
||||
if(initializeAtStart) {
|
||||
initializeConnection();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeConnection() {
|
||||
if (dataSource != null) {
|
||||
return;
|
||||
}
|
||||
|
@ -34,6 +41,11 @@ public void init() {
|
|||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
if(dataSource == null && !initializeAtStart) {
|
||||
synchronized (this) {
|
||||
initializeConnection();
|
||||
}
|
||||
}
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
|
|
|
@ -295,7 +295,9 @@ private SQLUser queryUser(String sql, String value) throws SQLException {
|
|||
user = constructUser(set);
|
||||
}
|
||||
}
|
||||
user.permissions = requestPermissions(user.uuid.toString());
|
||||
if(user != null) {
|
||||
user.permissions = requestPermissions(user.uuid.toString());
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ public SQLSourceConfig getSQLConfig() {
|
|||
@Override
|
||||
public void init(LaunchServer server, AuthProviderPair pair) {
|
||||
super.init(server, pair);
|
||||
logger.warn("Method 'mysql' deprecated and may be removed in future release. Please use new 'sql' method: https://gravitlauncher.com/auth");
|
||||
String userInfoCols = makeUserCols();
|
||||
String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey";
|
||||
if (sqlFindHardwareByPublicKey == null)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package pro.gravit.launchserver.auth.core;
|
||||
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
|
||||
import pro.gravit.launchserver.auth.SQLSourceConfig;
|
||||
|
||||
|
@ -10,4 +12,10 @@ public class PostgresSQLCoreProvider extends AbstractSQLCoreProvider {
|
|||
public SQLSourceConfig getSQLConfig() {
|
||||
return postgresSQLHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer server, AuthProviderPair pair) {
|
||||
super.init(server, pair);
|
||||
logger.warn("Method 'postgresql' deprecated and may be removed in future release. Please use new 'sql' method: https://gravitlauncher.com/auth");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
package pro.gravit.launchserver.auth.core;
|
||||
|
||||
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||
import pro.gravit.launchserver.auth.HikariSQLSourceConfig;
|
||||
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
||||
import pro.gravit.launchserver.auth.SQLSourceConfig;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportExtendedCheckServer;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportHardware;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
public class SQLCoreProvider extends AbstractSQLCoreProvider {
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.sql.*;
|
||||
import java.util.Base64;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SQLCoreProvider extends AbstractSQLCoreProvider implements AuthSupportHardware, AuthSupportExtendedCheckServer {
|
||||
public HikariSQLSourceConfig holder;
|
||||
|
||||
@Override
|
||||
|
@ -14,14 +30,364 @@ public void close() {
|
|||
holder.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer server, AuthProviderPair pair) {
|
||||
holder.init();
|
||||
super.init(server, pair);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLSourceConfig getSQLConfig() {
|
||||
return holder;
|
||||
}
|
||||
|
||||
|
||||
public String hardwareIdColumn;
|
||||
public String tableHWID = "hwids";
|
||||
public String tableHWIDLog = "hwidLog";
|
||||
public double criticalCompareLevel = 1.0;
|
||||
private transient String sqlFindHardwareByPublicKey;
|
||||
private transient String sqlFindHardwareByData;
|
||||
private transient String sqlFindHardwareById;
|
||||
private transient String sqlCreateHardware;
|
||||
private transient String sqlCreateHWIDLog;
|
||||
private transient String sqlUpdateHardwarePublicKey;
|
||||
private transient String sqlUpdateHardwareBanned;
|
||||
private transient String sqlUpdateUsers;
|
||||
private transient String sqlUsersByHwidId;
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer server, AuthProviderPair pair) {
|
||||
holder.init();
|
||||
super.init(server, pair);
|
||||
String userInfoCols = makeUserCols();
|
||||
String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey";
|
||||
if (sqlFindHardwareByPublicKey == null)
|
||||
sqlFindHardwareByPublicKey = "SELECT %s FROM %s WHERE publicKey = ?".formatted(hardwareInfoCols, tableHWID);
|
||||
if (sqlFindHardwareById == null)
|
||||
sqlFindHardwareById = "SELECT %s FROM %s WHERE id = ?".formatted(hardwareInfoCols, tableHWID);
|
||||
if (sqlUsersByHwidId == null)
|
||||
sqlUsersByHwidId = "SELECT %s FROM %s WHERE %s = ?".formatted(userInfoCols, table, hardwareIdColumn);
|
||||
if (sqlFindHardwareByData == null)
|
||||
sqlFindHardwareByData = "SELECT %s FROM %s".formatted(hardwareInfoCols, tableHWID);
|
||||
if (sqlCreateHardware == null)
|
||||
sqlCreateHardware = "INSERT INTO %s (publickey, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, graphicCard, battery, banned) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0')".formatted(tableHWID);
|
||||
if (sqlCreateHWIDLog == null)
|
||||
sqlCreateHWIDLog = "INSERT INTO %s (hwidId, newPublicKey) VALUES (?, ?)".formatted(tableHWIDLog);
|
||||
if (sqlUpdateHardwarePublicKey == null)
|
||||
sqlUpdateHardwarePublicKey = "UPDATE %s SET publicKey = ? WHERE id = ?".formatted(tableHWID);
|
||||
sqlUpdateHardwareBanned = "UPDATE %s SET banned = ? WHERE id = ?".formatted(tableHWID);
|
||||
sqlUpdateUsers = "UPDATE %s SET %s = ? WHERE %s = ?".formatted(table, hardwareIdColumn, uuidColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String makeUserCols() {
|
||||
return super.makeUserCols().concat(", ").concat(hardwareIdColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SQLUser constructUser(ResultSet set) throws SQLException {
|
||||
return set.next() ? new SQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
|
||||
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), set.getLong(hardwareIdColumn)) : null;
|
||||
}
|
||||
|
||||
private SQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException, IOException {
|
||||
HardwareReportRequest.HardwareInfo hardwareInfo = new HardwareReportRequest.HardwareInfo();
|
||||
hardwareInfo.hwDiskId = set.getString("hwDiskId");
|
||||
hardwareInfo.baseboardSerialNumber = set.getString("baseboardSerialNumber");
|
||||
byte[] displayId = set.getBytes("displayId");
|
||||
hardwareInfo.displayId = displayId == null ? null : displayId;
|
||||
hardwareInfo.bitness = set.getInt("bitness");
|
||||
hardwareInfo.totalMemory = set.getLong("totalMemory");
|
||||
hardwareInfo.logicalProcessors = set.getInt("logicalProcessors");
|
||||
hardwareInfo.physicalProcessors = set.getInt("physicalProcessors");
|
||||
hardwareInfo.processorMaxFreq = set.getLong("processorMaxFreq");
|
||||
hardwareInfo.battery = set.getBoolean("battery");
|
||||
hardwareInfo.graphicCard = set.getString("graphicCard");
|
||||
byte[] publicKey = set.getBytes("publicKey");
|
||||
long id = set.getLong("id");
|
||||
boolean banned = set.getBoolean("banned");
|
||||
return new SQLUserHardware(hardwareInfo, publicKey, id, banned);
|
||||
}
|
||||
|
||||
private void setUserHardwareId(Connection connection, UUID uuid, long hwidId) throws SQLException {
|
||||
PreparedStatement s = connection.prepareStatement(sqlUpdateUsers);
|
||||
s.setLong(1, hwidId);
|
||||
s.setString(2, uuid.toString());
|
||||
s.executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
|
||||
try (Connection connection = holder.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
PreparedStatement s = connection.prepareStatement(sqlFindHardwareByPublicKey);
|
||||
s.setBytes(1, publicKey);
|
||||
try (ResultSet set = s.executeQuery()) {
|
||||
if (set.next()) {
|
||||
connection.commit();
|
||||
return fetchHardwareInfo(set);
|
||||
} else {
|
||||
connection.commit();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (SQLException | IOException throwables) {
|
||||
logger.error("SQL Error", throwables);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info) {
|
||||
try (Connection connection = holder.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
PreparedStatement s = connection.prepareStatement(sqlFindHardwareByData);
|
||||
try (ResultSet set = s.executeQuery()) {
|
||||
while (set.next()) {
|
||||
SQLUserHardware hw = fetchHardwareInfo(set);
|
||||
AuthSupportHardware.HardwareInfoCompareResult result = compareHardwareInfo(hw.getHardwareInfo(), info);
|
||||
if (result.compareLevel > criticalCompareLevel) {
|
||||
connection.commit();
|
||||
return hw;
|
||||
} else {
|
||||
connection.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException | IOException throwables) {
|
||||
logger.error("SQL Error", throwables);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware getHardwareInfoById(String id) {
|
||||
try (Connection connection = holder.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
PreparedStatement s = connection.prepareStatement(sqlFindHardwareById);
|
||||
s.setLong(1, Long.parseLong(id));
|
||||
try (ResultSet set = s.executeQuery()) {
|
||||
if (set.next()) {
|
||||
connection.commit();
|
||||
return fetchHardwareInfo(set);
|
||||
} else {
|
||||
connection.commit();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (SQLException | IOException throwables) {
|
||||
logger.error("SQL Error", throwables);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey) {
|
||||
try (Connection connection = holder.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
PreparedStatement s = connection.prepareStatement(sqlCreateHardware, Statement.RETURN_GENERATED_KEYS);
|
||||
s.setBytes(1, publicKey);
|
||||
s.setString(2, hardwareInfo.hwDiskId);
|
||||
s.setString(3, hardwareInfo.baseboardSerialNumber);
|
||||
s.setBytes(4, hardwareInfo.displayId == null ? null : hardwareInfo.displayId);
|
||||
s.setInt(5, hardwareInfo.bitness);
|
||||
s.setLong(6, hardwareInfo.totalMemory);
|
||||
s.setInt(7, hardwareInfo.logicalProcessors);
|
||||
s.setInt(8, hardwareInfo.physicalProcessors);
|
||||
s.setLong(9, hardwareInfo.processorMaxFreq);
|
||||
s.setString(10, hardwareInfo.graphicCard);
|
||||
s.setBoolean(11, hardwareInfo.battery);
|
||||
s.executeUpdate();
|
||||
try (ResultSet generatedKeys = s.getGeneratedKeys()) {
|
||||
if (generatedKeys.next()) {
|
||||
//writeHwidLog(connection, generatedKeys.getLong(1), publicKey);
|
||||
long id = generatedKeys.getLong(1);
|
||||
connection.commit();
|
||||
return new SQLUserHardware(hardwareInfo, publicKey, id, false);
|
||||
}
|
||||
}
|
||||
connection.commit();
|
||||
return null;
|
||||
} catch (SQLException throwables) {
|
||||
logger.error("SQL Error", throwables);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectUserAndHardware(UserSession userSession, UserHardware hardware) {
|
||||
AbstractSQLCoreProvider.SQLUserSession SQLUserSession = (AbstractSQLCoreProvider.SQLUserSession) userSession;
|
||||
SQLUser SQLUser = (SQLUser) SQLUserSession.getUser();
|
||||
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
|
||||
if (SQLUser.hwidId == SQLUserHardware.id) return;
|
||||
SQLUser.hwidId = SQLUserHardware.id;
|
||||
try (Connection connection = holder.getConnection()) {
|
||||
setUserHardwareId(connection, SQLUser.getUUID(), SQLUserHardware.id);
|
||||
} catch (SQLException throwables) {
|
||||
logger.error("SQL Error", throwables);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey) {
|
||||
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
|
||||
SQLUserHardware.publicKey = publicKey;
|
||||
try (Connection connection = holder.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwarePublicKey);
|
||||
s.setBytes(1, publicKey);
|
||||
s.setLong(2, SQLUserHardware.id);
|
||||
s.executeUpdate();
|
||||
connection.commit();
|
||||
} catch (SQLException e) {
|
||||
logger.error("SQL error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<User> getUsersByHardwareInfo(UserHardware hardware) {
|
||||
List<User> users = new LinkedList<>();
|
||||
try (Connection c = holder.getConnection()) {
|
||||
c.setAutoCommit(false);
|
||||
PreparedStatement s = c.prepareStatement(sqlUsersByHwidId);
|
||||
s.setLong(1, Long.parseLong(hardware.getId()));
|
||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||
try (ResultSet set = s.executeQuery()) {
|
||||
while (!set.isLast()) {
|
||||
users.add(constructUser(set));
|
||||
}
|
||||
}
|
||||
c.commit();
|
||||
} catch (SQLException e) {
|
||||
logger.error("SQL error", e);
|
||||
return null;
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void banHardware(UserHardware hardware) {
|
||||
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
|
||||
SQLUserHardware.banned = true;
|
||||
try (Connection connection = holder.getConnection()) {
|
||||
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned);
|
||||
s.setBoolean(1, true);
|
||||
s.setLong(2, SQLUserHardware.id);
|
||||
s.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
logger.error("SQL Error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbanHardware(UserHardware hardware) {
|
||||
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
|
||||
SQLUserHardware.banned = false;
|
||||
try (Connection connection = holder.getConnection()) {
|
||||
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned);
|
||||
s.setBoolean(1, false);
|
||||
s.setLong(2, SQLUserHardware.id);
|
||||
s.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
logger.error("SQL error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractSQLCoreProvider.SQLUserSession createSession(AbstractSQLCoreProvider.SQLUser user) {
|
||||
return new SQLUserSession(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSession extendedCheckServer(Client client, String username, String serverID) throws IOException {
|
||||
AbstractSQLCoreProvider.SQLUser user = (AbstractSQLCoreProvider.SQLUser) getUserByUsername(username);
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
if (user.getUsername().equals(username) && user.getServerId().equals(serverID)) {
|
||||
return createSession(user);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public class SQLUserSession extends AbstractSQLCoreProvider.SQLUserSession implements UserSessionSupportHardware {
|
||||
private transient SQLUser SQLUser;
|
||||
protected transient SQLUserHardware hardware;
|
||||
|
||||
public SQLUserSession(AbstractSQLCoreProvider.SQLUser user) {
|
||||
super(user);
|
||||
SQLUser = (SQLUser) user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHardwareId() {
|
||||
return SQLUser.hwidId == 0 ? null : String.valueOf(SQLUser.hwidId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware getHardware() {
|
||||
if(hardware == null) {
|
||||
hardware = (SQLUserHardware) getHardwareInfoById(String.valueOf(SQLUser.hwidId));
|
||||
}
|
||||
return hardware;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SQLUserHardware implements UserHardware {
|
||||
private final HardwareReportRequest.HardwareInfo hardwareInfo;
|
||||
private final long id;
|
||||
private byte[] publicKey;
|
||||
private boolean banned;
|
||||
|
||||
public SQLUserHardware(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, long id, boolean banned) {
|
||||
this.hardwareInfo = hardwareInfo;
|
||||
this.publicKey = publicKey;
|
||||
this.id = id;
|
||||
this.banned = banned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HardwareReportRequest.HardwareInfo getHardwareInfo() {
|
||||
return hardwareInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return String.valueOf(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBanned() {
|
||||
return banned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SQLUserHardware{" +
|
||||
"hardwareInfo=" + hardwareInfo +
|
||||
", publicKey=" + (publicKey == null ? null : new String(Base64.getEncoder().encode(publicKey))) +
|
||||
", id=" + id +
|
||||
", banned=" + banned +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class SQLUser extends AbstractSQLCoreProvider.SQLUser {
|
||||
protected long hwidId;
|
||||
|
||||
public SQLUser(UUID uuid, String username, String accessToken, String serverId, String password, long hwidId) {
|
||||
super(uuid, username, accessToken, serverId, password);
|
||||
this.hwidId = hwidId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SQLUser{" +
|
||||
"uuid=" + uuid +
|
||||
", username='" + username + '\'' +
|
||||
", permissions=" + permissions +
|
||||
", hwidId=" + hwidId +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
package pro.gravit.launchserver.auth.profiles;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.*;
|
||||
|
||||
public class LocalProfileProvider extends ProfileProvider {
|
||||
public String profilesDir = "profiles";
|
||||
private transient volatile Map<Path, ClientProfile> profilesMap;
|
||||
private transient volatile Set<ClientProfile> profilesList; // Cache
|
||||
@Override
|
||||
public void sync() throws IOException {
|
||||
Path profilesDirPath = Path.of(profilesDir);
|
||||
if (!IOHelper.isDir(profilesDirPath))
|
||||
Files.createDirectory(profilesDirPath);
|
||||
Map<Path, ClientProfile> newProfiles = new HashMap<>();
|
||||
IOHelper.walk(profilesDirPath, new ProfilesFileVisitor(newProfiles), false);
|
||||
Set<ClientProfile> newProfilesList = new HashSet<>(newProfiles.values());
|
||||
profilesMap = newProfiles;
|
||||
profilesList = newProfilesList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ClientProfile> getProfiles() {
|
||||
return profilesList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addProfile(ClientProfile profile) throws IOException {
|
||||
Path profilesDirPath = Path.of(profilesDir);
|
||||
ClientProfile oldProfile;
|
||||
Path target = null;
|
||||
for(var e : profilesMap.entrySet()) {
|
||||
if(e.getValue().getUUID().equals(profile.getUUID())) {
|
||||
target = e.getKey();
|
||||
}
|
||||
}
|
||||
if(target == null) {
|
||||
target = profilesDirPath.resolve(profile.getTitle()+".json");
|
||||
oldProfile = profilesMap.get(target);
|
||||
if(oldProfile != null && !oldProfile.getUUID().equals(profile.getUUID())) {
|
||||
throw new FileAlreadyExistsException(target.toString());
|
||||
}
|
||||
}
|
||||
try (BufferedWriter writer = IOHelper.newWriter(target)) {
|
||||
Launcher.gsonManager.configGson.toJson(profile, writer);
|
||||
}
|
||||
addProfile(target, profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteProfile(ClientProfile profile) throws IOException {
|
||||
for(var e : profilesMap.entrySet()) {
|
||||
if(e.getValue().getUUID().equals(profile.getUUID())) {
|
||||
Files.deleteIfExists(e.getKey());
|
||||
profilesMap.remove(e.getKey());
|
||||
profilesList.remove(e.getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addProfile(Path path, ClientProfile profile) {
|
||||
for(var e : profilesMap.entrySet()) {
|
||||
if(e.getValue().getUUID().equals(profile.getUUID())) {
|
||||
profilesMap.remove(e.getKey());
|
||||
profilesList.remove(e.getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
profilesMap.put(path, profile);
|
||||
profilesList.add(profile);
|
||||
}
|
||||
|
||||
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
|
||||
private final Map<Path, ClientProfile> result;
|
||||
private final Logger logger = LogManager.getLogger();
|
||||
|
||||
private ProfilesFileVisitor(Map<Path, ClientProfile> result) {
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
logger.info("Syncing '{}' profile", IOHelper.getFileName(file));
|
||||
|
||||
// Read profile
|
||||
ClientProfile profile;
|
||||
try (BufferedReader reader = IOHelper.newReader(file)) {
|
||||
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
|
||||
}
|
||||
profile.verify();
|
||||
|
||||
// Add SIGNED profile to result list
|
||||
result.put(file.toAbsolutePath(), profile);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package pro.gravit.launchserver.auth.profiles;
|
||||
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.auth.protect.interfaces.ProfilesProtectHandler;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.utils.ProviderMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class ProfileProvider {
|
||||
public static final ProviderMap<ProfileProvider> providers = new ProviderMap<>("ProfileProvider");
|
||||
private static boolean registredProviders = false;
|
||||
protected transient LaunchServer server;
|
||||
|
||||
public static void registerProviders() {
|
||||
if (!registredProviders) {
|
||||
providers.register("local", LocalProfileProvider.class);
|
||||
registredProviders = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void init(LaunchServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public abstract void sync() throws IOException;
|
||||
|
||||
public abstract Set<ClientProfile> getProfiles();
|
||||
|
||||
public abstract void addProfile(ClientProfile profile) throws IOException;
|
||||
|
||||
public abstract void deleteProfile(ClientProfile profile) throws IOException;
|
||||
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
public ClientProfile getProfile(UUID uuid) {
|
||||
for(var e : getProfiles()) {
|
||||
if(e.getUUID().equals(uuid)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ClientProfile getProfile(String title) {
|
||||
for(var e : getProfiles()) {
|
||||
if(e.getTitle().equals(title)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ClientProfile> getProfiles(Client client) {
|
||||
List<ClientProfile> profileList;
|
||||
Set<ClientProfile> serverProfiles = getProfiles();
|
||||
if (server.config.protectHandler instanceof ProfilesProtectHandler protectHandler) {
|
||||
profileList = new ArrayList<>(4);
|
||||
for (ClientProfile profile : serverProfiles) {
|
||||
if (protectHandler.canGetProfile(profile, client)) {
|
||||
profileList.add(profile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
profileList = List.copyOf(serverProfiles);
|
||||
}
|
||||
return profileList;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
package pro.gravit.launchserver.auth.updates;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.core.hasher.HashedDir;
|
||||
import pro.gravit.launcher.core.serialize.HInput;
|
||||
import pro.gravit.launcher.core.serialize.HOutput;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.modules.events.LaunchServerUpdatesSyncEvent;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class LocalUpdatesProvider extends UpdatesProvider {
|
||||
private final transient Logger logger = LogManager.getLogger();
|
||||
public String cacheFile = ".updates-cache";
|
||||
public String updatesDir = "updates";
|
||||
public boolean cacheUpdates = true;
|
||||
private volatile transient Map<String, HashedDir> updatesDirMap;
|
||||
|
||||
private void writeCache(Path file) throws IOException {
|
||||
try (HOutput output = new HOutput(IOHelper.newOutput(file))) {
|
||||
output.writeLength(updatesDirMap.size(), 0);
|
||||
for (Map.Entry<String, HashedDir> entry : updatesDirMap.entrySet()) {
|
||||
output.writeString(entry.getKey(), 0);
|
||||
entry.getValue().write(output);
|
||||
}
|
||||
}
|
||||
logger.debug("Saved {} updates to cache", updatesDirMap.size());
|
||||
}
|
||||
|
||||
private void readCache(Path file) throws IOException {
|
||||
Map<String, HashedDir> updatesDirMap = new HashMap<>(16);
|
||||
try (HInput input = new HInput(IOHelper.newInput(file))) {
|
||||
int size = input.readLength(0);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
String name = input.readString(0);
|
||||
HashedDir dir = new HashedDir(input);
|
||||
updatesDirMap.put(name, dir);
|
||||
}
|
||||
}
|
||||
logger.debug("Found {} updates from cache", updatesDirMap.size());
|
||||
this.updatesDirMap = Collections.unmodifiableMap(updatesDirMap);
|
||||
}
|
||||
|
||||
public void readUpdatesFromCache() throws IOException {
|
||||
readCache(Path.of(cacheFile));
|
||||
}
|
||||
|
||||
public void readUpdatesDir() throws IOException {
|
||||
var cacheFilePath = Path.of(cacheFile);
|
||||
if (cacheUpdates) {
|
||||
if (Files.exists(cacheFilePath)) {
|
||||
try {
|
||||
readCache(cacheFilePath);
|
||||
return;
|
||||
} catch (Throwable e) {
|
||||
logger.error("Read updates cache failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
sync(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer server) {
|
||||
super.init(server);
|
||||
try {
|
||||
if (!IOHelper.isDir(Path.of(updatesDir)))
|
||||
Files.createDirectory(Path.of(updatesDir));
|
||||
} catch (IOException e) {
|
||||
logger.error("Updates not synced", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncInitially() throws IOException {
|
||||
readUpdatesDir();
|
||||
}
|
||||
|
||||
public void sync(Collection<String> dirs) throws IOException {
|
||||
logger.info("Syncing updates dir");
|
||||
Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16);
|
||||
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(Path.of(updatesDir))) {
|
||||
for (final Path updateDir : dirStream) {
|
||||
if (Files.isHidden(updateDir))
|
||||
continue; // Skip hidden
|
||||
|
||||
// Resolve name and verify is dir
|
||||
String name = IOHelper.getFileName(updateDir);
|
||||
if (!IOHelper.isDir(updateDir)) {
|
||||
if (!IOHelper.isFile(updateDir) && Stream.of(".jar", ".exe", ".hash").noneMatch(e -> updateDir.toString().endsWith(e)))
|
||||
logger.warn("Not update dir: '{}'", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add from previous map (it's guaranteed to be non-null)
|
||||
if (dirs != null && !dirs.contains(name)) {
|
||||
HashedDir hdir = updatesDirMap.get(name);
|
||||
if (hdir != null) {
|
||||
newUpdatesDirMap.put(name, hdir);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Sync and sign update dir
|
||||
logger.info("Syncing '{}' update dir", name);
|
||||
HashedDir updateHDir = new HashedDir(updateDir, null, true, true);
|
||||
newUpdatesDirMap.put(name, updateHDir);
|
||||
}
|
||||
}
|
||||
updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap);
|
||||
if (cacheUpdates) {
|
||||
try {
|
||||
writeCache(Path.of(cacheFile));
|
||||
} catch (Throwable e) {
|
||||
logger.error("Write updates cache failed", e);
|
||||
}
|
||||
}
|
||||
server.modulesManager.invokeEvent(new LaunchServerUpdatesSyncEvent(server));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashedDir getUpdatesDir(String updateName) {
|
||||
return updatesDirMap.get(updateName);
|
||||
}
|
||||
|
||||
private Path resolveUpdateName(String updateName) {
|
||||
if(updateName == null) {
|
||||
return Path.of(updatesDir);
|
||||
}
|
||||
return Path.of(updatesDir).resolve(updateName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(String updateName, Map<String, Path> files, boolean deleteAfterUpload) throws IOException {
|
||||
var path = resolveUpdateName(updateName);
|
||||
for(var e : files.entrySet()) {
|
||||
var target = path.resolve(e.getKey());
|
||||
var source = e.getValue();
|
||||
IOHelper.createParentDirs(target);
|
||||
if(deleteAfterUpload) {
|
||||
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
} else {
|
||||
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Path> download(String updateName, List<String> files) {
|
||||
var path = resolveUpdateName(updateName);
|
||||
Map<String, Path> map = new HashMap<>();
|
||||
for(var e : files) {
|
||||
map.put(e, path.resolve(e));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String updateName, List<String> files) throws IOException {
|
||||
var path = resolveUpdateName(updateName);
|
||||
for(var e : files) {
|
||||
var target = path.resolve(e);
|
||||
Files.delete(target);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String updateName) throws IOException {
|
||||
var path = resolveUpdateName(updateName);
|
||||
IOHelper.deleteDir(path, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(String updateName) throws IOException {
|
||||
var path = resolveUpdateName(updateName);
|
||||
Files.createDirectories(path);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package pro.gravit.launchserver.auth.updates;
|
||||
|
||||
import pro.gravit.launcher.core.hasher.HashedDir;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.utils.ProviderMap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class UpdatesProvider {
|
||||
public static final ProviderMap<UpdatesProvider> providers = new ProviderMap<>("UpdatesProvider");
|
||||
private static boolean registredProviders = false;
|
||||
protected transient LaunchServer server;
|
||||
|
||||
public static void registerProviders() {
|
||||
if (!registredProviders) {
|
||||
providers.register("local", LocalUpdatesProvider.class);
|
||||
registredProviders = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void init(LaunchServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public void sync() throws IOException {
|
||||
sync(null);
|
||||
}
|
||||
|
||||
public abstract void syncInitially() throws IOException;
|
||||
|
||||
public abstract void sync(Collection<String> updateNames) throws IOException;
|
||||
|
||||
public abstract HashedDir getUpdatesDir(String updateName);
|
||||
|
||||
public abstract void upload(String updateName, Map<String, Path> files, boolean deleteAfterUpload) throws IOException;
|
||||
|
||||
public abstract Map<String, Path> download(String updateName, List<String> files);
|
||||
|
||||
public abstract void delete(String updateName, List<String> files) throws IOException;
|
||||
|
||||
public abstract void delete(String updateName) throws IOException;
|
||||
|
||||
public abstract void create(String updateName) throws IOException;
|
||||
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
|
@ -14,11 +14,11 @@
|
|||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BinaryPipeline {
|
||||
public abstract class BinaryPipeline {
|
||||
public final List<LauncherBuildTask> tasks = new ArrayList<>();
|
||||
public final Path buildDir;
|
||||
public final String nameFormat;
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
protected transient final Logger logger = LogManager.getLogger();
|
||||
|
||||
public BinaryPipeline(Path buildDir, String nameFormat) {
|
||||
this.buildDir = buildDir;
|
||||
|
@ -80,27 +80,6 @@ public Optional<LauncherBuildTask> getTaskBefore(Predicate<LauncherBuildTask> pr
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void build(Path target, boolean deleteTempFiles) throws IOException {
|
||||
logger.info("Building launcher binary file");
|
||||
Path thisPath = null;
|
||||
long time_start = System.currentTimeMillis();
|
||||
long time_this = time_start;
|
||||
for (LauncherBuildTask task : tasks) {
|
||||
logger.info("Task {}", task.getName());
|
||||
Path oldPath = thisPath;
|
||||
thisPath = task.process(oldPath);
|
||||
long time_task_end = System.currentTimeMillis();
|
||||
long time_task = time_task_end - time_this;
|
||||
time_this = time_task_end;
|
||||
logger.info("Task {} processed from {} millis", task.getName(), time_task);
|
||||
}
|
||||
long time_end = System.currentTimeMillis();
|
||||
if (deleteTempFiles) IOHelper.move(thisPath, target);
|
||||
else IOHelper.copy(thisPath, target);
|
||||
IOHelper.deleteDir(buildDir, false);
|
||||
logger.info("Build successful from {} millis", time_end - time_start);
|
||||
}
|
||||
|
||||
public String nextName(String taskName) {
|
||||
return nameFormat.formatted(taskName);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package pro.gravit.launchserver.binary;
|
||||
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.binary.tasks.LauncherBuildTask;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class LauncherBinary extends BinaryPipeline {
|
||||
public final LaunchServer server;
|
||||
|
@ -19,11 +22,27 @@ protected LauncherBinary(LaunchServer server, Path binaryFile, String nameFormat
|
|||
}
|
||||
|
||||
public static Path resolve(LaunchServer server, String ext) {
|
||||
return server.config.copyBinaries ? server.updatesDir.resolve(server.config.binaryName + ext) : server.dir.resolve(server.config.binaryName + ext);
|
||||
return Path.of(server.config.binaryName + ext);
|
||||
}
|
||||
|
||||
public void build() throws IOException {
|
||||
build(syncBinaryFile, server.config.launcher.deleteTempFiles);
|
||||
logger.info("Building launcher binary file");
|
||||
Path thisPath = null;
|
||||
long time_start = System.currentTimeMillis();
|
||||
long time_this = time_start;
|
||||
for (LauncherBuildTask task : tasks) {
|
||||
logger.info("Task {}", task.getName());
|
||||
Path oldPath = thisPath;
|
||||
thisPath = task.process(oldPath);
|
||||
long time_task_end = System.currentTimeMillis();
|
||||
long time_task = time_task_end - time_this;
|
||||
time_this = time_task_end;
|
||||
logger.info("Task {} processed from {} millis", task.getName(), time_task);
|
||||
}
|
||||
long time_end = System.currentTimeMillis();
|
||||
server.config.updatesProvider.upload(null, Map.of(syncBinaryFile.toString(), thisPath), true);
|
||||
IOHelper.deleteDir(buildDir, false);
|
||||
logger.info("Build successful from {} millis", time_end - time_start);
|
||||
}
|
||||
|
||||
public final boolean exists() {
|
||||
|
@ -38,9 +57,13 @@ public void init() {
|
|||
}
|
||||
|
||||
public final boolean sync() throws IOException {
|
||||
boolean exists = exists();
|
||||
digest = exists ? SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(syncBinaryFile)) : null;
|
||||
|
||||
return exists;
|
||||
try {
|
||||
var target = syncBinaryFile.toString();
|
||||
var path = server.config.updatesProvider.download(null, List.of(target)).get(target);
|
||||
digest = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(path));
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,7 @@
|
|||
import pro.gravit.utils.helper.UnpackHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -40,7 +37,7 @@ public Path process(Path inputFile) throws IOException {
|
|||
if(Files.isDirectory(server.launcherLibrariesCompile)) {
|
||||
IOHelper.walk(server.launcherLibrariesCompile, new ListFileVisitor(server.launcherBinary.addonLibs), false);
|
||||
}
|
||||
try(Stream<Path> stream = Files.walk(server.launcherPack).filter((e) -> {
|
||||
try(Stream<Path> stream = Files.walk(server.launcherPack, FileVisitOption.FOLLOW_LINKS).filter((e) -> {
|
||||
try {
|
||||
return !Files.isDirectory(e) && !Files.isHidden(e);
|
||||
} catch (IOException ex) {
|
||||
|
|
|
@ -32,6 +32,7 @@ public void invoke(String... args) throws Exception {
|
|||
boolean value = Boolean.parseBoolean(args[0]);
|
||||
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
|
||||
Configuration config = ctx.getConfiguration();
|
||||
config.getWatchManager().setIntervalSeconds(-1);
|
||||
LoggerConfig loggerConfig = config.getLoggerConfig("pro.gravit");
|
||||
loggerConfig.setLevel(value ? Level.TRACE : Level.DEBUG);
|
||||
ctx.updateLoggers();
|
||||
|
|
|
@ -97,10 +97,7 @@ public void invoke(String... args) throws IOException, CommandException {
|
|||
isMirrorClientDownload = true;
|
||||
}
|
||||
}
|
||||
try (BufferedWriter writer = IOHelper.newWriter(IOHelper.resolveIncremental(server.profilesDir,
|
||||
dirName, "json"))) {
|
||||
Launcher.gsonManager.configGson.toJson(clientProfile, writer);
|
||||
}
|
||||
server.config.profileProvider.addProfile(clientProfile);
|
||||
|
||||
// Finished
|
||||
server.syncProfilesDir();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import pro.gravit.launcher.base.profiles.ClientProfileBuilder;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.command.Command;
|
||||
import pro.gravit.utils.helper.CommonHelper;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -26,7 +27,7 @@ public CloneProfileCommand(LaunchServer server) {
|
|||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[profile file name] [new profile title]";
|
||||
return "[profile title/uuid] [new profile title]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -37,13 +38,12 @@ public String getUsageDescription() {
|
|||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
verifyArgs(args, 2);
|
||||
var profilePath = server.profilesDir.resolve(args[0].concat(".json"));
|
||||
if(!Files.exists(profilePath)) {
|
||||
logger.error("File {} not found", profilePath);
|
||||
}
|
||||
ClientProfile profile;
|
||||
try(Reader reader = IOHelper.newReader(profilePath)) {
|
||||
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
|
||||
try {
|
||||
UUID uuid = UUID.fromString(args[0]);
|
||||
profile = server.config.profileProvider.getProfile(uuid);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
profile = server.config.profileProvider.getProfile(args[0]);
|
||||
}
|
||||
var builder = new ClientProfileBuilder(profile);
|
||||
builder.setTitle(args[1]);
|
||||
|
@ -65,10 +65,7 @@ public void invoke(String... args) throws Exception {
|
|||
}
|
||||
builder.setDir(args[1]);
|
||||
profile = builder.createClientProfile();
|
||||
var targetPath = server.profilesDir.resolve(args[1].concat(".json"));
|
||||
try(Writer writer = IOHelper.newWriter(targetPath)) {
|
||||
Launcher.gsonManager.gson.toJson(profile, writer);
|
||||
}
|
||||
server.config.profileProvider.addProfile(profile);
|
||||
logger.info("Profile {} cloned from {}", args[1], args[0]);
|
||||
server.syncProfilesDir();
|
||||
server.syncUpdatesDir(List.of(args[1]));
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DeleteProfileCommand extends Command {
|
||||
private final transient Logger logger = LogManager.getLogger(ListProfilesCommand.class);
|
||||
|
@ -28,29 +29,24 @@ public String getUsageDescription() {
|
|||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
verifyArgs(args, 1);
|
||||
ClientProfile profile = null;
|
||||
for(var p : server.getProfiles()) {
|
||||
if(p.getUUID().toString().equals(args[0]) || p.getTitle().equals(args[0])) {
|
||||
profile = p;
|
||||
break;
|
||||
}
|
||||
ClientProfile profile;
|
||||
try {
|
||||
UUID uuid = UUID.fromString(args[0]);
|
||||
profile = server.config.profileProvider.getProfile(uuid);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
profile = server.config.profileProvider.getProfile(args[0]);
|
||||
}
|
||||
if(profile == null) {
|
||||
logger.error("Profile {} not found", args[0]);
|
||||
return;
|
||||
}
|
||||
var clientDir = server.updatesDir.resolve(profile.getDir()).toAbsolutePath();
|
||||
logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", clientDir);
|
||||
logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", profile.getDir());
|
||||
if(!showApplyDialog("Continue?")) {
|
||||
return;
|
||||
}
|
||||
logger.info("Delete {}", clientDir);
|
||||
IOHelper.deleteDir(clientDir, true);
|
||||
var profileFile = profile.getProfileFilePath();
|
||||
if(profileFile == null) {
|
||||
profileFile = server.profilesDir.resolve(profile.getTitle().concat(".json"));
|
||||
}
|
||||
logger.info("Delete {}", profileFile);
|
||||
Files.deleteIfExists(profileFile);
|
||||
logger.info("Delete {} ({})", profile.getTitle(), profile.getUUID());
|
||||
server.config.profileProvider.deleteProfile(profile);
|
||||
logger.info("Delete {}", profile.getDir());
|
||||
server.config.updatesProvider.delete(profile.getDir());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,9 +37,7 @@ public void invoke(String... args) throws Exception {
|
|||
logger.info("Detected option {}", option);
|
||||
}
|
||||
ClientProfile profile = MakeProfileHelper.makeProfile(version, args[0], options);
|
||||
try (Writer writer = IOHelper.newWriter(server.profilesDir.resolve(args[0].concat(".json")))) {
|
||||
Launcher.gsonManager.configGson.toJson(profile, writer);
|
||||
}
|
||||
server.config.profileProvider.addProfile(profile);
|
||||
logger.info("Profile {} created", args[0]);
|
||||
server.syncProfilesDir();
|
||||
}
|
||||
|
|
|
@ -22,20 +22,6 @@ public SaveProfilesCommand(LaunchServer server) {
|
|||
super(server);
|
||||
}
|
||||
|
||||
public static void saveProfile(ClientProfile profile, Path path) throws IOException {
|
||||
if (profile.getServers().isEmpty()) {
|
||||
ClientProfile.ServerProfile serverProfile = new ClientProfile.ServerProfile();
|
||||
serverProfile.isDefault = true;
|
||||
serverProfile.name = profile.getTitle();
|
||||
serverProfile.serverAddress = profile.getServerAddress();
|
||||
serverProfile.serverPort = profile.getServerPort();
|
||||
profile.getServers().add(serverProfile);
|
||||
}
|
||||
try (Writer w = IOHelper.newWriter(path)) {
|
||||
Launcher.gsonManager.configGson.toJson(profile, w);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[profile names...]";
|
||||
|
@ -51,17 +37,14 @@ public void invoke(String... args) throws Exception {
|
|||
verifyArgs(args, 1);
|
||||
if (args.length > 0) {
|
||||
for (String profileName : args) {
|
||||
Path profilePath = server.profilesDir.resolve(profileName.concat(".json"));
|
||||
if (!Files.exists(profilePath)) {
|
||||
logger.error("Profile {} not found", profilePath.toString());
|
||||
return;
|
||||
}
|
||||
ClientProfile profile;
|
||||
try (Reader reader = IOHelper.newReader(profilePath)) {
|
||||
profile = Launcher.gsonManager.configGson.fromJson(reader, ClientProfile.class);
|
||||
try {
|
||||
UUID uuid = UUID.fromString(profileName);
|
||||
profile = server.config.profileProvider.getProfile(uuid);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
profile = server.config.profileProvider.getProfile(profileName);
|
||||
}
|
||||
saveProfile(profile, profilePath);
|
||||
logger.info("Profile {} save successful", profilePath.toString());
|
||||
server.config.profileProvider.addProfile(profile);
|
||||
}
|
||||
server.syncProfilesDir();
|
||||
}
|
||||
|
|
|
@ -9,12 +9,17 @@
|
|||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||
import pro.gravit.launchserver.auth.core.RejectAuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.profiles.LocalProfileProvider;
|
||||
import pro.gravit.launchserver.auth.profiles.ProfileProvider;
|
||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||
import pro.gravit.launchserver.auth.protect.StdProtectHandler;
|
||||
import pro.gravit.launchserver.auth.texture.RequestTextureProvider;
|
||||
import pro.gravit.launchserver.auth.updates.LocalUpdatesProvider;
|
||||
import pro.gravit.launchserver.auth.updates.UpdatesProvider;
|
||||
import pro.gravit.launchserver.components.AuthLimiterComponent;
|
||||
import pro.gravit.launchserver.components.Component;
|
||||
import pro.gravit.launchserver.components.ProGuardComponent;
|
||||
import pro.gravit.launchserver.socket.NettyObjectFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
@ -30,12 +35,13 @@ public final class LaunchServerConfig {
|
|||
public String[] mirrors;
|
||||
public String binaryName;
|
||||
public boolean copyBinaries = true;
|
||||
public boolean cacheUpdates = true;
|
||||
public LauncherConfig.LauncherEnvironment env;
|
||||
public Map<String, AuthProviderPair> auth;
|
||||
// Handlers & Providers
|
||||
public ProtectHandler protectHandler;
|
||||
public Map<String, Component> components;
|
||||
public ProfileProvider profileProvider = new LocalProfileProvider();
|
||||
public UpdatesProvider updatesProvider = new LocalUpdatesProvider();
|
||||
public NettyConfig netty;
|
||||
public LauncherConf launcher;
|
||||
public JarSignerConf sign;
|
||||
|
@ -59,12 +65,7 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
|
|||
newConfig.netty.fileServerEnabled = true;
|
||||
newConfig.netty.binds = new NettyBindAddress[]{new NettyBindAddress("0.0.0.0", 9274)};
|
||||
newConfig.netty.performance = new NettyPerformanceConfig();
|
||||
try {
|
||||
newConfig.netty.performance.usingEpoll = Epoll.isAvailable();
|
||||
} catch (Throwable e) {
|
||||
// Epoll class line 51+ catch (Exception) but Error will be thrown by System.load
|
||||
newConfig.netty.performance.usingEpoll = false;
|
||||
} // such as on ARM
|
||||
newConfig.netty.performance.mode = NettyObjectFactory.NettyFactoryMode.AUTO;
|
||||
newConfig.netty.performance.bossThread = 2;
|
||||
newConfig.netty.performance.workerThread = 8;
|
||||
newConfig.netty.performance.schedulerThread = 2;
|
||||
|
@ -85,6 +86,7 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
|
|||
newConfig.components.put("authLimiter", authLimiterComponent);
|
||||
ProGuardComponent proGuardComponent = new ProGuardComponent();
|
||||
newConfig.components.put("proguard", proGuardComponent);
|
||||
newConfig.profileProvider = new LocalProfileProvider();
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
|
@ -166,6 +168,14 @@ public void init(LaunchServer.ReloadType type) {
|
|||
server.registerObject("protectHandler", protectHandler);
|
||||
protectHandler.init(server);
|
||||
}
|
||||
if(profileProvider != null) {
|
||||
server.registerObject("profileProvider", profileProvider);
|
||||
profileProvider.init(server);
|
||||
}
|
||||
if(updatesProvider != null) {
|
||||
server.registerObject("updatesProvider", updatesProvider);
|
||||
updatesProvider.init(server);
|
||||
}
|
||||
if (components != null) {
|
||||
components.forEach((k, v) -> server.registerObject("component.".concat(k), v));
|
||||
}
|
||||
|
@ -206,6 +216,14 @@ public void close(LaunchServer.ReloadType type) {
|
|||
server.unregisterObject("protectHandler", protectHandler);
|
||||
protectHandler.close();
|
||||
}
|
||||
if(profileProvider != null) {
|
||||
server.unregisterObject("profileProvider", profileProvider);
|
||||
profileProvider.close();
|
||||
}
|
||||
if(updatesProvider != null) {
|
||||
server.unregisterObject("updatesProvider", updatesProvider);
|
||||
updatesProvider.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static class JarSignerConf {
|
||||
|
@ -255,7 +273,7 @@ public static class NettyConfig {
|
|||
}
|
||||
|
||||
public static class NettyPerformanceConfig {
|
||||
public boolean usingEpoll;
|
||||
public NettyObjectFactory.NettyFactoryMode mode = NettyObjectFactory.NettyFactoryMode.AUTO;
|
||||
public int bossThread;
|
||||
public int workerThread;
|
||||
public int schedulerThread;
|
||||
|
|
|
@ -197,9 +197,6 @@ public static String getMainClassByVersion(ClientProfile.Version version, MakePr
|
|||
if(version.compareTo(ClientProfileVersions.MINECRAFT_1_7_10) == 0) {
|
||||
return "com.gtnewhorizons.retrofuturabootstrap.Main";
|
||||
}
|
||||
if(version.compareTo(ClientProfileVersions.MINECRAFT_1_12_2) == 0) {
|
||||
return "top.outlands.foundation.boot.Foundation"; // Cleanroom
|
||||
}
|
||||
if (findOption(options, MakeProfileOptionLaunchWrapper.class).isPresent()) {
|
||||
return "net.minecraft.launchwrapper.Launch";
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
public class CertificateManager {
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
public LauncherTrustManager trustManager;
|
||||
private Path truststorePath;
|
||||
|
||||
public void writePrivateKey(Path file, PrivateKey privateKey) throws IOException {
|
||||
writePrivateKey(IOHelper.newWriter(file), privateKey);
|
||||
|
@ -91,6 +92,7 @@ public X509CertificateHolder readCertificate(Reader reader) throws IOException {
|
|||
}
|
||||
|
||||
public void readTrustStore(Path dir) throws IOException, CertificateException {
|
||||
this.truststorePath = dir;
|
||||
if (!IOHelper.isDir(dir)) {
|
||||
Files.createDirectories(dir);
|
||||
try {
|
||||
|
@ -131,4 +133,8 @@ public LauncherTrustManager.CheckClassResult checkClass(Class<?> clazz) {
|
|||
X509Certificate[] certificates = JVMHelper.getCertificates(clazz);
|
||||
return trustManager.checkCertificates(certificates, trustManager::stdCertificateChecker);
|
||||
}
|
||||
|
||||
public Path getTruststorePath() {
|
||||
return truststorePath;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ public class KeyAgreementManager {
|
|||
public final RSAPublicKey rsaPublicKey;
|
||||
public final RSAPrivateKey rsaPrivateKey;
|
||||
public final String legacySalt;
|
||||
public final Path keyDirectory;
|
||||
|
||||
public KeyAgreementManager(ECPublicKey ecdsaPublicKey, ECPrivateKey ecdsaPrivateKey, RSAPublicKey rsaPublicKey, RSAPrivateKey rsaPrivateKey, String legacySalt) {
|
||||
this.ecdsaPublicKey = ecdsaPublicKey;
|
||||
|
@ -29,9 +30,11 @@ public KeyAgreementManager(ECPublicKey ecdsaPublicKey, ECPrivateKey ecdsaPrivate
|
|||
this.rsaPublicKey = rsaPublicKey;
|
||||
this.rsaPrivateKey = rsaPrivateKey;
|
||||
this.legacySalt = legacySalt;
|
||||
this.keyDirectory = null;
|
||||
}
|
||||
|
||||
public KeyAgreementManager(Path keyDirectory) throws IOException, InvalidKeySpecException {
|
||||
this.keyDirectory = keyDirectory;
|
||||
Path ecdsaPublicKeyPath = keyDirectory.resolve("ecdsa_id.pub"), ecdsaPrivateKeyPath = keyDirectory.resolve("ecdsa_id");
|
||||
Logger logger = LogManager.getLogger();
|
||||
if (IOHelper.isFile(ecdsaPublicKeyPath) && IOHelper.isFile(ecdsaPrivateKeyPath)) {
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.mix.MixProvider;
|
||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||
import pro.gravit.launchserver.auth.profiles.ProfileProvider;
|
||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
||||
import pro.gravit.launchserver.auth.updates.UpdatesProvider;
|
||||
import pro.gravit.launchserver.components.Component;
|
||||
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
|
||||
import pro.gravit.launchserver.socket.WebSocketService;
|
||||
|
@ -46,6 +48,8 @@ public void registerAdapters(GsonBuilder builder) {
|
|||
builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers));
|
||||
builder.registerTypeAdapter(OptionalTrigger.class, new UniversalJsonAdapter<>(OptionalTrigger.providers));
|
||||
builder.registerTypeAdapter(MixProvider.class, new UniversalJsonAdapter<>(MixProvider.providers));
|
||||
builder.registerTypeAdapter(ProfileProvider.class, new UniversalJsonAdapter<>(ProfileProvider.providers));
|
||||
builder.registerTypeAdapter(UpdatesProvider.class, new UniversalJsonAdapter<>(UpdatesProvider.providers));
|
||||
modulesManager.invokeEvent(new PreGsonPhase(builder));
|
||||
//ClientWebSocketService.appendTypeAdapters(builder);
|
||||
}
|
||||
|
|
|
@ -18,112 +18,38 @@
|
|||
|
||||
public class UpdatesManager {
|
||||
private final LaunchServer server;
|
||||
private final Logger logger = LogManager.getLogger();
|
||||
private final Path cacheFile;
|
||||
private volatile Map<String, HashedDir> updatesDirMap;
|
||||
|
||||
public UpdatesManager(LaunchServer server) {
|
||||
this.server = server;
|
||||
this.cacheFile = server.dir.resolve(".updates-cache");
|
||||
}
|
||||
|
||||
private void writeCache(Path file) throws IOException {
|
||||
try (HOutput output = new HOutput(IOHelper.newOutput(file))) {
|
||||
output.writeLength(updatesDirMap.size(), 0);
|
||||
for (Map.Entry<String, HashedDir> entry : updatesDirMap.entrySet()) {
|
||||
output.writeString(entry.getKey(), 0);
|
||||
entry.getValue().write(output);
|
||||
}
|
||||
}
|
||||
logger.debug("Saved {} updates to cache", updatesDirMap.size());
|
||||
}
|
||||
|
||||
private void readCache(Path file) throws IOException {
|
||||
Map<String, HashedDir> updatesDirMap = new HashMap<>(16);
|
||||
try (HInput input = new HInput(IOHelper.newInput(file))) {
|
||||
int size = input.readLength(0);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
String name = input.readString(0);
|
||||
HashedDir dir = new HashedDir(input);
|
||||
updatesDirMap.put(name, dir);
|
||||
}
|
||||
}
|
||||
logger.debug("Found {} updates from cache", updatesDirMap.size());
|
||||
this.updatesDirMap = Collections.unmodifiableMap(updatesDirMap);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void readUpdatesFromCache() throws IOException {
|
||||
readCache(cacheFile);
|
||||
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void readUpdatesDir() throws IOException {
|
||||
if (server.config.cacheUpdates) {
|
||||
if (Files.exists(cacheFile)) {
|
||||
try {
|
||||
readCache(cacheFile);
|
||||
return;
|
||||
} catch (Throwable e) {
|
||||
logger.error("Read updates cache failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
syncUpdatesDir(null);
|
||||
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void syncUpdatesDir(Collection<String> dirs) throws IOException {
|
||||
logger.info("Syncing updates dir");
|
||||
Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16);
|
||||
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(server.updatesDir)) {
|
||||
for (final Path updateDir : dirStream) {
|
||||
if (Files.isHidden(updateDir))
|
||||
continue; // Skip hidden
|
||||
|
||||
// Resolve name and verify is dir
|
||||
String name = IOHelper.getFileName(updateDir);
|
||||
if (!IOHelper.isDir(updateDir)) {
|
||||
if (!IOHelper.isFile(updateDir) && Stream.of(".jar", ".exe", ".hash").noneMatch(e -> updateDir.toString().endsWith(e)))
|
||||
logger.warn("Not update dir: '{}'", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add from previous map (it's guaranteed to be non-null)
|
||||
if (dirs != null && !dirs.contains(name)) {
|
||||
HashedDir hdir = updatesDirMap.get(name);
|
||||
if (hdir != null) {
|
||||
newUpdatesDirMap.put(name, hdir);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Sync and sign update dir
|
||||
logger.info("Syncing '{}' update dir", name);
|
||||
HashedDir updateHDir = new HashedDir(updateDir, null, true, true);
|
||||
newUpdatesDirMap.put(name, updateHDir);
|
||||
}
|
||||
}
|
||||
updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap);
|
||||
if (server.config.cacheUpdates) {
|
||||
try {
|
||||
writeCache(cacheFile);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Write updates cache failed", e);
|
||||
}
|
||||
}
|
||||
server.modulesManager.invokeEvent(new LaunchServerUpdatesSyncEvent(server));
|
||||
server.config.updatesProvider.sync(dirs);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public HashSet<String> getUpdatesList() {
|
||||
HashSet<String> set = new HashSet<>();
|
||||
for (Map.Entry<String, HashedDir> entry : updatesDirMap.entrySet())
|
||||
set.add(entry.getKey());
|
||||
return set;
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public HashedDir getUpdate(String name) {
|
||||
return updatesDirMap.get(name);
|
||||
return server.config.updatesProvider.getUpdatesDir(name);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void addUpdate(String name, HashedDir dir) {
|
||||
updatesDirMap.put(name, dir);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,14 +36,9 @@ public class LauncherNettyServer implements AutoCloseable {
|
|||
|
||||
public LauncherNettyServer(LaunchServer server) {
|
||||
LaunchServerConfig.NettyConfig config = server.config.netty;
|
||||
NettyObjectFactory.setUsingEpoll(config.performance.usingEpoll);
|
||||
NettyObjectFactory.setMode(config.performance.mode);
|
||||
Logger logger = LogManager.getLogger();
|
||||
if (config.performance.usingEpoll) {
|
||||
logger.debug("Netty: Epoll enabled");
|
||||
}
|
||||
if (config.performance.usingEpoll && !Epoll.isAvailable()) {
|
||||
logger.error("Epoll is not available: (netty,perfomance.usingEpoll configured wrongly)", Epoll.unavailabilityCause());
|
||||
}
|
||||
logger.info("Netty usage {} transport mode", NettyObjectFactory.getMode());
|
||||
bossGroup = NettyObjectFactory.newEventLoopGroup(config.performance.bossThread, "LauncherNettyServer.bossGroup");
|
||||
workerGroup = NettyObjectFactory.newEventLoopGroup(config.performance.workerThread, "LauncherNettyServer.workerGroup");
|
||||
serverBootstrap = new ServerBootstrap();
|
||||
|
|
|
@ -2,30 +2,60 @@
|
|||
|
||||
import io.netty.channel.ChannelFactory;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.MultiThreadIoEventLoopGroup;
|
||||
import io.netty.channel.ServerChannel;
|
||||
import io.netty.channel.epoll.Epoll;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollIoHandler;
|
||||
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.nio.NioIoHandler;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.uring.IoUring;
|
||||
import io.netty.channel.uring.IoUringIoHandler;
|
||||
import io.netty.channel.uring.IoUringServerSocketChannel;
|
||||
|
||||
public class NettyObjectFactory {
|
||||
private static boolean epoll = false;
|
||||
private static NettyFactoryMode mode;
|
||||
|
||||
public static void setUsingEpoll(boolean value) {
|
||||
epoll = value;
|
||||
public static void setMode(NettyFactoryMode mode) {
|
||||
NettyObjectFactory.mode = mode;
|
||||
if(mode == NettyFactoryMode.AUTO) {
|
||||
if(IoUring.isAvailable()) {
|
||||
NettyObjectFactory.mode = NettyFactoryMode.IO_URING;
|
||||
return;
|
||||
}
|
||||
if(Epoll.isAvailable()) {
|
||||
NettyObjectFactory.mode = NettyFactoryMode.EPOLL;
|
||||
return;
|
||||
}
|
||||
NettyObjectFactory.mode = NettyFactoryMode.NIO;
|
||||
}
|
||||
}
|
||||
|
||||
public static NettyFactoryMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public static EventLoopGroup newEventLoopGroup(int threads, String poolName) {
|
||||
if (epoll)
|
||||
return new EpollEventLoopGroup(threads);
|
||||
else
|
||||
return new NioEventLoopGroup(threads);
|
||||
return switch (mode) {
|
||||
case AUTO -> null;
|
||||
case NIO -> new MultiThreadIoEventLoopGroup(threads, NioIoHandler.newFactory());
|
||||
case EPOLL -> new MultiThreadIoEventLoopGroup(threads, EpollIoHandler.newFactory());
|
||||
case IO_URING -> new MultiThreadIoEventLoopGroup(threads, IoUringIoHandler.newFactory());
|
||||
};
|
||||
}
|
||||
|
||||
public static ChannelFactory<? extends ServerChannel> getServerSocketChannelFactory() {
|
||||
if (epoll)
|
||||
return EpollServerSocketChannel::new;
|
||||
else
|
||||
return NioServerSocketChannel::new;
|
||||
return switch (mode) {
|
||||
case AUTO -> null;
|
||||
case NIO -> NioServerSocketChannel::new;
|
||||
case EPOLL -> EpollServerSocketChannel::new;
|
||||
case IO_URING -> IoUringServerSocketChannel::new;
|
||||
};
|
||||
}
|
||||
|
||||
public enum NettyFactoryMode {
|
||||
AUTO, NIO, EPOLL, IO_URING
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package pro.gravit.launchserver.socket;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launchserver.config.log4j.LogAppender;
|
||||
import pro.gravit.utils.command.CommandHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.UnixDomainSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class SocketCommandServer implements Runnable {
|
||||
private final Logger logger = LogManager.getLogger(SocketCommandServer.class);
|
||||
private ServerSocketChannel channel;
|
||||
private Path path;
|
||||
private UnixDomainSocketAddress address;
|
||||
private ServerSocketChannel serverChannel;
|
||||
private CommandHandler commandHandler;
|
||||
private transient SocketChannel clientChannel;
|
||||
|
||||
public SocketCommandServer(CommandHandler commandHandler, Path path) {
|
||||
this.commandHandler = commandHandler;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
private void runCommand(SocketChannel channel, String command) {
|
||||
logger.info("Command '{}' from socket", command);
|
||||
clientChannel = channel;
|
||||
try {
|
||||
commandHandler.evalNative(command, false);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Error when execute command", e);
|
||||
} finally {
|
||||
clientChannel = null;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Files.deleteIfExists(path);
|
||||
this.address = UnixDomainSocketAddress.of(path);
|
||||
serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX);
|
||||
serverChannel.configureBlocking(true);
|
||||
serverChannel.bind(address);
|
||||
LogAppender.getInstance().addListener((logEvent -> {
|
||||
if(clientChannel != null && clientChannel.isOpen()) {
|
||||
try {
|
||||
String s = logEvent.getMessage().getFormattedMessage()+"\n";
|
||||
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
clientChannel.write(buffer);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}));
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||
while (true) {
|
||||
SocketChannel channel = serverChannel.accept();
|
||||
channel.configureBlocking(true);
|
||||
String command = null;
|
||||
try {
|
||||
mark:
|
||||
while (true) {
|
||||
int bytesRead = channel.read(buffer);
|
||||
if (bytesRead < 0) {
|
||||
break;
|
||||
}
|
||||
for (var i=0;i<buffer.limit();i++) {
|
||||
if(buffer.get(i) == '\n') {
|
||||
command = new String(buffer.array(), 0, i);
|
||||
break mark;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if(command != null) {
|
||||
runCommand(channel, command);
|
||||
}
|
||||
} finally {
|
||||
buffer.clear();
|
||||
channel.close();
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error("Unix command socket server error", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@
|
|||
import java.util.Set;
|
||||
|
||||
public class ProfilesResponse extends SimpleResponse {
|
||||
@Deprecated
|
||||
public static List<ClientProfile> getListVisibleProfiles(LaunchServer server, Client client) {
|
||||
List<ClientProfile> profileList;
|
||||
Set<ClientProfile> serverProfiles = server.getProfiles();
|
||||
|
@ -40,6 +41,6 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
sendError("Access denied");
|
||||
return;
|
||||
}
|
||||
sendResult(new ProfilesRequestEvent(getListVisibleProfiles(server, client)));
|
||||
sendResult(new ProfilesRequestEvent(server.config.profileProvider.getProfiles(client)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
client.checkSign = true;
|
||||
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
|
||||
} else {
|
||||
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
|
||||
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, null, 0));
|
||||
}
|
||||
} else if (launcher_type == 2) //EXE
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
client.checkSign = true;
|
||||
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
|
||||
} else {
|
||||
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
|
||||
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, null, 0));
|
||||
}
|
||||
} else sendError("Request launcher type error");
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
|
||||
String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper"
|
||||
String mainAgentName = "pro.gravit.launcher.runtime.LauncherAgent"
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
|
@ -20,7 +19,6 @@
|
|||
jar {
|
||||
archiveClassifier.set('clean')
|
||||
manifest.attributes("Main-Class": mainClassName,
|
||||
"Premain-Class": mainAgentName,
|
||||
"Multi-Release": "true",
|
||||
"Automatic-Module-Name": "GravitLauncher")
|
||||
}
|
||||
|
|
|
@ -124,7 +124,6 @@ public static void main(String... args) throws Throwable {
|
|||
LogHelper.printLicense("Launcher");
|
||||
LauncherEngine.checkClass(LauncherEngineWrapper.class);
|
||||
LauncherEngine.checkClass(LauncherEngine.class);
|
||||
LauncherEngine.checkClass(LauncherAgent.class);
|
||||
LauncherEngine.checkClass(ClientLauncherEntryPoint.class);
|
||||
LauncherEngine.modulesManager = new RuntimeModuleManager();
|
||||
LauncherEngine.modulesManager.loadModule(new RuntimeLauncherCoreModule());
|
||||
|
|
|
@ -142,10 +142,41 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
|
|||
}
|
||||
//ADD CLASSPATH
|
||||
processArgs.add(JVMHelper.jvmProperty("java.library.path", this.params.nativesDir));
|
||||
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.AGENT) {
|
||||
processArgs.add("-javaagent:".concat(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString()));
|
||||
} else if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
|
||||
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(new HashSet<>(), workDir, params.actions, params.profile)
|
||||
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
|
||||
Set<Path> ignorePath = new HashSet<>();
|
||||
var moduleConf = params.profile.getModuleConf();
|
||||
if(moduleConf != null) {
|
||||
if(moduleConf.modulePath != null && !moduleConf.modulePath.isEmpty()) {
|
||||
processArgs.add("-p");
|
||||
for(var e : moduleConf.modulePath) {
|
||||
ignorePath.add(Path.of(e));
|
||||
}
|
||||
processArgs.add(String.join(File.pathSeparator, moduleConf.modulePath));
|
||||
}
|
||||
if(moduleConf.modules != null && !moduleConf.modules.isEmpty()) {
|
||||
processArgs.add("--add-modules");
|
||||
processArgs.add(String.join(",", moduleConf.modules));
|
||||
}
|
||||
if(moduleConf.exports != null && !moduleConf.exports.isEmpty()) {
|
||||
for(var e : moduleConf.exports.entrySet()) {
|
||||
processArgs.add("--add-exports");
|
||||
processArgs.add(String.format("%s=%s", e.getKey(), e.getValue()));
|
||||
}
|
||||
}
|
||||
if(moduleConf.opens != null && !moduleConf.opens.isEmpty()) {
|
||||
for(var e : moduleConf.opens.entrySet()) {
|
||||
processArgs.add("--add-opens");
|
||||
processArgs.add(String.format("%s=%s", e.getKey(), e.getValue()));
|
||||
}
|
||||
}
|
||||
if(moduleConf.reads != null && !moduleConf.reads.isEmpty()) {
|
||||
for(var e : moduleConf.reads.entrySet()) {
|
||||
processArgs.add("--add-reads");
|
||||
processArgs.add(String.format("%s=%s", e.getKey(), e.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(ignorePath, workDir, params.actions, params.profile)
|
||||
.map(Path::toString)
|
||||
.toList());
|
||||
}
|
||||
|
|
|
@ -141,7 +141,6 @@ public void cancel() {
|
|||
}
|
||||
}
|
||||
tasks.clear();
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
|
@ -256,7 +255,7 @@ protected HttpRequest makeHttpRequest(URI baseUri, String filePath) throws URISy
|
|||
}
|
||||
|
||||
protected ProgressTrackingBodyHandler<Path> makeBodyHandler(Path file, DownloadCallback callback) {
|
||||
return new ProgressTrackingBodyHandler<>(HttpResponse.BodyHandlers.ofFile(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE), callback);
|
||||
return new ProgressTrackingBodyHandler<>(HttpResponse.BodyHandlers.ofFile(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING), callback);
|
||||
}
|
||||
|
||||
public interface DownloadCallback {
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
public final class ClientProfile implements Comparable<ClientProfile> {
|
||||
private static final FileNameMatcher ASSET_MATCHER = new FileNameMatcher(
|
||||
new String[0], new String[]{"indexes", "objects"}, new String[0]);
|
||||
private transient Path profileFilePath;
|
||||
@LauncherNetworkAPI
|
||||
private String title;
|
||||
@LauncherNetworkAPI
|
||||
|
@ -392,16 +391,8 @@ public List<CompatibilityFlags> getFlags() {
|
|||
return flags;
|
||||
}
|
||||
|
||||
public Path getProfileFilePath() {
|
||||
return profileFilePath;
|
||||
}
|
||||
|
||||
public void setProfileFilePath(Path profileFilePath) {
|
||||
this.profileFilePath = profileFilePath;
|
||||
}
|
||||
|
||||
public enum ClassLoaderConfig {
|
||||
AGENT, LAUNCHER, MODULE, SYSTEM_ARGS
|
||||
LAUNCHER, MODULE, SYSTEM_ARGS
|
||||
}
|
||||
|
||||
public enum CompatibilityFlags {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
apply plugin: 'org.openjfx.javafxplugin'
|
||||
|
||||
String mainClassName = "pro.gravit.launcher.ClientLauncherWrapper"
|
||||
String mainAgentName = "pro.gravit.launcher.LauncherAgent"
|
||||
String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper"
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
|
@ -14,7 +13,6 @@
|
|||
jar {
|
||||
archiveClassifier.set('clean')
|
||||
manifest.attributes("Main-Class": mainClassName,
|
||||
"Premain-Class": mainAgentName,
|
||||
"Multi-Release": "true")
|
||||
}
|
||||
|
||||
|
|
|
@ -86,11 +86,9 @@ private static void realMain(String[] args) throws Throwable {
|
|||
modulesManager.invokeEvent(new PreConfigPhase());
|
||||
LogHelper.debug("Reading ClientLauncher params");
|
||||
ClientParams params = readParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort));
|
||||
if (params.profile.getClassLoaderConfig() != ClientProfile.ClassLoaderConfig.AGENT) {
|
||||
ClientLauncherMethods.verifyNoAgent();
|
||||
}
|
||||
ClientLauncherMethods.verifyNoAgent();
|
||||
if(params.timestamp > System.currentTimeMillis() || params.timestamp + 30*1000 < System.currentTimeMillis() ) {
|
||||
LogHelper.error("Timestamp failed. Exit");
|
||||
LogHelper.error("Timestamp failed: current %d | params %d | diff %d", System.currentTimeMillis(), params.timestamp, System.currentTimeMillis() - params.timestamp);
|
||||
ClientLauncherMethods.exitLauncher(-662);
|
||||
return;
|
||||
}
|
||||
|
@ -160,7 +158,7 @@ private static void realMain(String[] args) throws Throwable {
|
|||
System.load(Paths.get(params.nativesDir).resolve(ClientService.findLibrary(e)).toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) {
|
||||
if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER || classLoaderConfig == ClientProfile.ClassLoaderConfig.MODULE) {
|
||||
if(JVMHelper.JVM_VERSION <= 11) {
|
||||
launch = new LegacyLaunch();
|
||||
} else {
|
||||
|
@ -170,20 +168,12 @@ private static void realMain(String[] args) throws Throwable {
|
|||
System.setProperty("java.class.path", classpath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator)));
|
||||
modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, classLoaderControl, profile));
|
||||
ClientService.baseURLs = classLoaderControl.getURLs();
|
||||
} else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.AGENT) {
|
||||
launch = new BasicLaunch(LauncherAgent.inst);
|
||||
classpathURLs.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toUri().toURL());
|
||||
classLoaderControl = launch.init(classpath, params.nativesDir, options);
|
||||
for (URL url : classpathURLs) {
|
||||
LauncherAgent.addJVMClassPath(Paths.get(url.toURI()));
|
||||
}
|
||||
ClientService.instrumentation = LauncherAgent.inst;
|
||||
modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, null, profile));
|
||||
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
|
||||
} else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
|
||||
launch = new BasicLaunch();
|
||||
classLoaderControl = launch.init(classpath, params.nativesDir, options);
|
||||
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
|
||||
} else {
|
||||
throw new UnsupportedOperationException(String.format("Unknown classLoaderConfig %s", classLoaderConfig));
|
||||
}
|
||||
if(profile.hasFlag(ClientProfile.CompatibilityFlags.CLASS_CONTROL_API)) {
|
||||
ClientService.classLoaderControl = classLoaderControl;
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
package pro.gravit.launcher.client;
|
||||
|
||||
import pro.gravit.launcher.client.utils.NativeJVMHalt;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.nio.file.Path;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
|
||||
public final class LauncherAgent {
|
||||
public static Instrumentation inst;
|
||||
private static boolean isAgentStarted = false;
|
||||
|
||||
public static void addJVMClassPath(String path) throws IOException {
|
||||
LogHelper.debug("Launcher Agent addJVMClassPath");
|
||||
inst.appendToSystemClassLoaderSearch(new JarFile(new File(path)));
|
||||
}
|
||||
|
||||
public static void addJVMClassPath(Path path) throws IOException {
|
||||
LogHelper.debug("Launcher Agent addJVMClassPath");
|
||||
inst.appendToSystemClassLoaderSearch(new JarFile(path.toFile()));
|
||||
}
|
||||
|
||||
public static void premain(String agentArgument, Instrumentation instrumentation) {
|
||||
System.out.println("Launcher Agent");
|
||||
checkAgentStacktrace();
|
||||
inst = instrumentation;
|
||||
NativeJVMHalt.initFunc();
|
||||
isAgentStarted = true;
|
||||
}
|
||||
|
||||
public static void checkAgentStacktrace() {
|
||||
RuntimeException ex = new SecurityException("Error check agent stacktrace");
|
||||
boolean isFoundNative = false;
|
||||
boolean foundPreMain = false;
|
||||
for (StackTraceElement e : ex.getStackTrace()) {
|
||||
if (e.isNativeMethod()) {
|
||||
if (!isFoundNative) isFoundNative = true;
|
||||
else throw ex;
|
||||
}
|
||||
if (e.getMethodName().equals("premain")) {
|
||||
if (!foundPreMain) foundPreMain = true;
|
||||
else throw ex;
|
||||
}
|
||||
}
|
||||
if (!isFoundNative || !foundPreMain) throw ex;
|
||||
}
|
||||
|
||||
public static boolean isStarted() {
|
||||
return isAgentStarted;
|
||||
}
|
||||
|
||||
public boolean isAgentStarted() {
|
||||
return isAgentStarted;
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ public final class Version implements Comparable<Version> {
|
|||
|
||||
public static final int MAJOR = 5;
|
||||
public static final int MINOR = 6;
|
||||
public static final int PATCH = 3;
|
||||
public static final int PATCH = 10;
|
||||
public static final int BUILD = 1;
|
||||
public static final Version.Type RELEASE = Type.STABLE;
|
||||
public final int major;
|
||||
|
|
|
@ -10,8 +10,18 @@
|
|||
import java.util.Map;
|
||||
|
||||
public abstract class CommandHandler implements Runnable {
|
||||
private final List<Category> categories = new ArrayList<>();
|
||||
private final CommandCategory baseCategory = new BaseCommandCategory();
|
||||
protected final List<Category> categories;
|
||||
protected final CommandCategory baseCategory;
|
||||
|
||||
public CommandHandler() {
|
||||
this.categories = new ArrayList<>();
|
||||
this.baseCategory = new BaseCommandCategory();
|
||||
}
|
||||
|
||||
protected CommandHandler(List<Category> categories, CommandCategory baseCategory) {
|
||||
this.categories = categories;
|
||||
this.baseCategory = baseCategory;
|
||||
}
|
||||
|
||||
public void eval(String line, boolean bell) {
|
||||
LogHelper.info("Command '%s'", line);
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class StdCommandHandler extends CommandHandler {
|
||||
private final BufferedReader reader;
|
||||
|
@ -14,6 +16,31 @@ public StdCommandHandler(boolean readCommands) {
|
|||
reader = readCommands ? IOHelper.newReader(System.in) : null;
|
||||
}
|
||||
|
||||
public StdCommandHandler(InputStream stream) {
|
||||
super();
|
||||
this.reader = IOHelper.newReader(stream);
|
||||
}
|
||||
|
||||
public StdCommandHandler(BufferedReader reader) {
|
||||
super();
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
protected StdCommandHandler(List<Category> categories, CommandCategory baseCategory, boolean readCommands) {
|
||||
super(categories, baseCategory);
|
||||
this.reader = readCommands ? IOHelper.newReader(System.in) : null;
|
||||
}
|
||||
|
||||
protected StdCommandHandler(List<Category> categories, CommandCategory baseCategory, InputStream stream) {
|
||||
super(categories, baseCategory);
|
||||
this.reader = IOHelper.newReader(stream);
|
||||
}
|
||||
|
||||
protected StdCommandHandler(List<Category> categories, CommandCategory baseCategory, BufferedReader reader) {
|
||||
super(categories, baseCategory);
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bell() {
|
||||
}
|
||||
|
@ -37,4 +64,16 @@ public void clear() throws IOException {
|
|||
public String readLine() throws IOException {
|
||||
return reader == null ? null : reader.readLine();
|
||||
}
|
||||
|
||||
public StdCommandHandler ofHandler(CommandHandler commandHandler, boolean readCommands) {
|
||||
return new StdCommandHandler(commandHandler.categories, commandHandler.baseCategory, readCommands);
|
||||
}
|
||||
|
||||
public StdCommandHandler ofHandler(CommandHandler commandHandler, InputStream stream) {
|
||||
return new StdCommandHandler(commandHandler.categories, commandHandler.baseCategory, stream);
|
||||
}
|
||||
|
||||
public StdCommandHandler ofHandler(CommandHandler commandHandler, BufferedReader reader) {
|
||||
return new StdCommandHandler(commandHandler.categories, commandHandler.baseCategory, reader);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,10 @@ private class LegacyClassLoader extends URLClassLoader {
|
|||
private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>();
|
||||
private String nativePath;
|
||||
|
||||
static {
|
||||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
|
||||
private final List<String> packages = new ArrayList<>();
|
||||
public LegacyClassLoader(URL[] urls) {
|
||||
super(urls);
|
||||
|
@ -119,6 +123,9 @@ protected Class<?> findClass(String name) throws ClassNotFoundException {
|
|||
|
||||
@Override
|
||||
public String findLibrary(String name) {
|
||||
if(nativePath == null) {
|
||||
return null;
|
||||
}
|
||||
return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION);
|
||||
}
|
||||
|
||||
|
|
|
@ -164,6 +164,11 @@ private class ModuleClassLoader extends URLClassLoader {
|
|||
private String nativePath;
|
||||
|
||||
private final List<String> packages = new ArrayList<>();
|
||||
|
||||
static {
|
||||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
|
||||
public ModuleClassLoader(URL[] urls, ClassLoader parent) {
|
||||
super("LAUNCHER", urls, parent);
|
||||
packages.add("pro.gravit.launcher.");
|
||||
|
@ -257,6 +262,9 @@ protected Class<?> findClass(String moduleName, String name) {
|
|||
|
||||
@Override
|
||||
public String findLibrary(String name) {
|
||||
if(nativePath == null) {
|
||||
return null;
|
||||
}
|
||||
return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION);
|
||||
}
|
||||
|
||||
|
|
11
build.gradle
11
build.gradle
|
@ -1,11 +1,11 @@
|
|||
plugins {
|
||||
id 'com.github.johnrengelman.shadow' version '7.1.2' apply false
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'maven-publish'
|
||||
id 'signing'
|
||||
id 'org.openjfx.javafxplugin' version '0.1.0' apply false
|
||||
}
|
||||
group = 'pro.gravit.launcher'
|
||||
version = '5.6.3'
|
||||
version = '5.6.10'
|
||||
|
||||
apply from: 'props.gradle'
|
||||
|
||||
|
@ -30,6 +30,13 @@
|
|||
maven {
|
||||
url "https://jitpack.io/"
|
||||
}
|
||||
maven {
|
||||
url 'https://maven.gravit-support.ru/repository/jitpack'
|
||||
credentials {
|
||||
username = 'gravitlauncher'
|
||||
password = 'gravitlauncher'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
6
gradlew
vendored
6
gradlew
vendored
|
@ -15,6 +15,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
|
@ -55,7 +57,7 @@
|
|||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
@ -84,7 +86,7 @@ done
|
|||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
|
2
gradlew.bat
vendored
2
gradlew.bat
vendored
|
@ -13,6 +13,8 @@
|
|||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
|||
Subproject commit 0b9e8819db5c3d7db874b334cfa629a73daabf01
|
||||
Subproject commit aa5ab332d65dd14ce5321453aa809e30b87a186d
|
30
props.gradle
30
props.gradle
|
@ -1,18 +1,18 @@
|
|||
project.ext {
|
||||
verAsm = '9.7'
|
||||
verNetty = '4.1.111.Final'
|
||||
verOshiCore = '6.6.1'
|
||||
verJunit = '5.10.2'
|
||||
verAsm = '9.7.1'
|
||||
verNetty = '4.2.0.RC3'
|
||||
verOshiCore = '6.6.6'
|
||||
verJunit = '5.11.4'
|
||||
verJansi = '2.4.1'
|
||||
verJline = '3.26.1'
|
||||
verJwt = '0.12.5'
|
||||
verGson = '2.11.0'
|
||||
verBcpkix = '1.78.1'
|
||||
verSlf4j = '2.0.13'
|
||||
verLog4j = '2.23.1'
|
||||
verMySQLConn = '9.0.0'
|
||||
verMariaDBConn = '3.4.0'
|
||||
verPostgreSQLConn = '42.7.3'
|
||||
verH2Conn = '2.2.224'
|
||||
verProguard = '7.5.0'
|
||||
verJline = '3.29.0'
|
||||
verJwt = '0.12.6'
|
||||
verGson = '2.12.1'
|
||||
verBcpkix = '1.80'
|
||||
verSlf4j = '2.0.16'
|
||||
verLog4j = '2.24.3'
|
||||
verMySQLConn = '9.2.0'
|
||||
verMariaDBConn = '3.5.1'
|
||||
verPostgreSQLConn = '42.7.5'
|
||||
verH2Conn = '2.3.232'
|
||||
verProguard = '7.6.1'
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue