mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-04-23 08:23:04 +03:00
Compare commits
137 commits
Author | SHA1 | Date | |
---|---|---|---|
|
b16281e04a | ||
|
06f0bc873a | ||
|
834fbab12b | ||
|
822872992f | ||
|
ac43034d45 | ||
|
00446b40f0 | ||
|
9e29053afa | ||
|
2e93102106 | ||
|
d4b69195b3 | ||
|
a67fbac8bc | ||
|
a7abb9cbfc | ||
|
689478ee0f | ||
|
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 | ||
|
ede9ab2c85 | ||
|
c8c83c0dba | ||
|
1bd5d8854c | ||
|
992d31c883 | ||
|
accbbe6b13 | ||
|
0894e0b9c3 | ||
|
925007015f | ||
|
ec526a343d | ||
|
1bebd8de2c | ||
|
c7781b30be | ||
|
df9d05a49c | ||
|
03d53d4a09 | ||
|
c261496af8 | ||
|
8c11ab0cbe | ||
|
78b4f1e3aa | ||
|
bbc5f1722b | ||
|
51411c5838 | ||
|
3b22b76278 | ||
|
ef9cbfe0da | ||
|
353b663e12 | ||
|
78be606029 | ||
|
584acdb8c3 | ||
|
7f6a645dd7 | ||
|
c6930ded74 | ||
|
c2a6a408c4 | ||
|
01cd50840a | ||
|
bb4d5b99c6 | ||
|
ff2f647b50 | ||
|
79fc42e86a | ||
|
5b8aa8cd5e | ||
|
a4e5ef9d01 | ||
|
1d5044c24a | ||
|
b84911d445 | ||
|
6a173b9b1b | ||
|
6a53891c6a | ||
|
7c637e078d | ||
|
c9a81da60c | ||
|
6c0ead015b | ||
|
b5457ee866 | ||
|
52c9196dcc | ||
|
095a5aef8b | ||
|
765f1a9d8f | ||
|
9bd65c797b | ||
|
8908710ad6 | ||
|
748612783c |
105 changed files with 2665 additions and 821 deletions
12
.github/workflows/push.yml
vendored
12
.github/workflows/push.yml
vendored
|
@ -6,26 +6,28 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: gravit-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-launcher
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: temurin
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@417ae3ccd767c252f5661f1ace9f835f9654f2b5
|
||||
|
||||
|
@ -42,7 +44,7 @@ jobs:
|
|||
cp modules/*_lmodule/build/libs/*.jar artifacts/modules || true
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Launcher
|
||||
path: artifacts
|
||||
|
@ -63,7 +65,7 @@ jobs:
|
|||
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.event.ref, 'refs/tags')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -61,7 +65,6 @@
|
|||
dependsOn jar
|
||||
archiveClassifier.set('clean')
|
||||
manifest.attributes("Main-Class": mainClassName,
|
||||
"Premain-Class": mainAgentName,
|
||||
"Automatic-Module-Name": "launchserver"
|
||||
)
|
||||
from sourceSets.main.output
|
||||
|
@ -70,68 +73,60 @@
|
|||
|
||||
dependencies {
|
||||
pack project(':LauncherAPI')
|
||||
bundle group: 'me.tongfei', name: 'progressbar', version: '0.9.2'
|
||||
bundle group: 'com.github.Marcono1234', name: 'gson-record-type-adapter-factory', version: 'v0.2.0'
|
||||
bundle group: 'me.tongfei', name: 'progressbar', version: '0.10.1'
|
||||
bundle group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
|
||||
bundle group: 'org.jline', name: 'jline', version: rootProject['verJline']
|
||||
bundle group: 'org.jline', name: 'jline-reader', version: rootProject['verJline']
|
||||
bundle group: 'org.jline', name: 'jline-terminal', version: rootProject['verJline']
|
||||
bundle group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: rootProject['verBcpkix']
|
||||
bundle group: 'org.bouncycastle', name: 'bcprov-jdk18on', version: rootProject['verBcpkix']
|
||||
bundle group: 'org.bouncycastle', name: 'bcpkix-jdk18on', version: rootProject['verBcpkix']
|
||||
bundle group: 'org.ow2.asm', name: 'asm-commons', version: rootProject['verAsm']
|
||||
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']
|
||||
bundle group: 'org.postgresql', name: 'postgresql', version: rootProject['verPostgreSQLConn']
|
||||
bundle group: 'com.h2database', name: 'h2', version: rootProject['verH2Conn']
|
||||
bundle group: 'com.guardsquare', name: 'proguard-base', version: rootProject['verProguard']
|
||||
bundle group: 'org.apache.logging.log4j', name: 'log4j-core', version: rootProject['verLog4j']
|
||||
bundle group: 'org.apache.logging.log4j', name: 'log4j-slf4j2-impl', version: rootProject['verLog4j']
|
||||
bundle group: 'io.jsonwebtoken', name: 'jjwt-api', version: rootProject['verJwt']
|
||||
bundle group: 'io.jsonwebtoken', name: 'jjwt-impl', version: rootProject['verJwt']
|
||||
bundle group: 'io.jsonwebtoken', name: 'jjwt-gson', version: rootProject['verJwt']
|
||||
bundle group: 'com.google.code.gson', name: 'gson', version: rootProject['verGson']
|
||||
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.8.4'
|
||||
hikari('com.zaxxer:HikariCP:5.0.1') {
|
||||
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'
|
||||
}
|
||||
|
||||
compileOnlyA group: 'com.google.guava', name: 'guava', version: rootProject['verGuavaC']
|
||||
// Do not update (laggy deps).
|
||||
compileOnlyA 'log4j:log4j:1.2.17'
|
||||
compileOnlyA 'org.apache.logging.log4j:log4j-core:2.14.1'
|
||||
}
|
||||
|
||||
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' }
|
||||
}
|
||||
|
@ -142,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 {
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.base.events.RequestEvent;
|
||||
import pro.gravit.launcher.base.events.request.ProfilesRequestEvent;
|
||||
import pro.gravit.launcher.base.modules.events.ClosePhase;
|
||||
|
@ -21,21 +20,18 @@
|
|||
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;
|
||||
import pro.gravit.utils.command.Command;
|
||||
import pro.gravit.utils.command.CommandHandler;
|
||||
import pro.gravit.utils.command.SubCommand;
|
||||
import pro.gravit.utils.helper.CommonHelper;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.KeyStore;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
@ -70,6 +66,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,8 +77,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
|
||||
*/
|
||||
|
@ -110,14 +110,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;
|
||||
|
@ -126,7 +125,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;
|
||||
|
@ -136,6 +134,10 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
|||
launcherLibraries = directories.launcherLibrariesDir;
|
||||
launcherLibrariesCompile = directories.launcherLibrariesCompileDir;
|
||||
launcherPack = directories.launcherPackDir;
|
||||
modulesDir = directories.modules;
|
||||
launcherModulesDir = directories.launcherModules;
|
||||
librariesDir = directories.librariesDir;
|
||||
controlFile = directories.controlFile;
|
||||
this.shardId = shardId;
|
||||
if(!Files.isDirectory(launcherPack)) {
|
||||
Files.createDirectories(launcherPack);
|
||||
|
@ -187,6 +189,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);
|
||||
|
@ -320,12 +323,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() {
|
||||
|
@ -348,17 +353,17 @@ 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 {
|
||||
if (!IOHelper.isDir(updatesDir))
|
||||
Files.createDirectory(updatesDir);
|
||||
updatesManager.readUpdatesDir();
|
||||
|
||||
// Sync profiles dir
|
||||
if (!IOHelper.isDir(profilesDir))
|
||||
Files.createDirectory(profilesDir);
|
||||
syncProfilesDir();
|
||||
|
||||
// Sync updates dir
|
||||
config.updatesProvider.syncInitially();
|
||||
|
||||
|
||||
modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this));
|
||||
} catch (IOException e) {
|
||||
logger.error("Updates/Profiles not synced", e);
|
||||
|
@ -393,12 +398,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();
|
||||
}
|
||||
|
@ -413,7 +413,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);
|
||||
});
|
||||
|
@ -459,38 +459,12 @@ 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";
|
||||
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;
|
||||
public Path launcherPackDir;
|
||||
|
@ -498,17 +472,23 @@ public static class LaunchServerDirectories {
|
|||
public Path dir;
|
||||
public Path trustStore;
|
||||
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)
|
||||
launcherLibrariesCompileDir = getPath(LAUNCHERLIBRARIESCOMPILE_NAME);
|
||||
if(launcherPackDir == null)
|
||||
if (launcherPackDir == null)
|
||||
launcherPackDir = getPath(LAUNCHERPACK_NAME);
|
||||
if (keyDirectory == null) keyDirectory = getPath(KEY_NAME);
|
||||
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;
|
||||
|
@ -51,13 +53,16 @@ public static void main(String[] args) throws Exception {
|
|||
try {
|
||||
Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
} catch (ClassNotFoundException ex) {
|
||||
} catch (ClassNotFoundException | NoClassDefFoundError ex) {
|
||||
LogHelper.error("Library BouncyCastle not found! Is directory 'libraries' empty?");
|
||||
return;
|
||||
}
|
||||
LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
|
||||
directories.dir = dir;
|
||||
directories.collect();
|
||||
CertificateManager certificateManager = new CertificateManager();
|
||||
try {
|
||||
certificateManager.readTrustStore(dir.resolve("truststore"));
|
||||
certificateManager.readTrustStore(directories.trustStore);
|
||||
} catch (CertificateException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
@ -78,7 +83,7 @@ public static void main(String[] args) throws Exception {
|
|||
LaunchServerRuntimeConfig runtimeConfig;
|
||||
LaunchServerConfig config;
|
||||
LaunchServer.LaunchServerEnv env = LaunchServer.LaunchServerEnv.PRODUCTION;
|
||||
LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(dir.resolve("modules"), dir.resolve("config"), certificateManager.trustManager);
|
||||
LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(directories.modules, dir.resolve("config"), certificateManager.trustManager);
|
||||
modulesManager.autoload();
|
||||
modulesManager.initModules(null);
|
||||
registerAll();
|
||||
|
@ -123,8 +128,6 @@ public static void main(String[] args) throws Exception {
|
|||
}
|
||||
|
||||
LaunchServer.LaunchServerConfigManager launchServerConfigManager = new BasicLaunchServerConfigManager(configFile, runtimeConfigFile);
|
||||
LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
|
||||
directories.dir = dir;
|
||||
LaunchServer server = new LaunchServerBuilder()
|
||||
.setDirectories(directories)
|
||||
.setEnv(env)
|
||||
|
@ -135,7 +138,24 @@ public static void main(String[] args) throws Exception {
|
|||
.setLaunchServerConfigManager(launchServerConfigManager)
|
||||
.setCertificateManager(certificateManager)
|
||||
.build();
|
||||
if (!prepareMode) {
|
||||
List<String> allArgs = List.of(args);
|
||||
boolean isPrepareMode = prepareMode || allArgs.contains("--prepare");
|
||||
boolean isRunCommand = false;
|
||||
String runCommand = null;
|
||||
for(var e : allArgs) {
|
||||
if(e.equals("--run")) {
|
||||
isRunCommand = true;
|
||||
continue;
|
||||
}
|
||||
if(isRunCommand) {
|
||||
runCommand = e;
|
||||
isRunCommand = false;
|
||||
}
|
||||
}
|
||||
if(runCommand != null) {
|
||||
localCommandHandler.eval(runCommand, false);
|
||||
}
|
||||
if (!isPrepareMode) {
|
||||
server.run();
|
||||
} else {
|
||||
server.close();
|
||||
|
@ -159,6 +179,8 @@ public static void registerAll() {
|
|||
OptionalAction.registerProviders();
|
||||
OptionalTrigger.registerProviders();
|
||||
MixProvider.registerProviders();
|
||||
ProfileProvider.registerProviders();
|
||||
UpdatesProvider.registerProviders();
|
||||
}
|
||||
|
||||
private static void printExperimentalBranch() {
|
||||
|
@ -201,7 +223,7 @@ public static void generateConfigIfNotExists(Path configFile, CommandHandler com
|
|||
address = System.getProperty("launchserver.address", null);
|
||||
}
|
||||
if (address == null) {
|
||||
System.out.println("LaunchServer address(default: localhost): ");
|
||||
System.out.println("External launchServer address:port (default: localhost:9274): ");
|
||||
address = commandHandler.readLine();
|
||||
}
|
||||
String projectName = System.getenv("PROJECTNAME");
|
||||
|
@ -215,18 +237,29 @@ public static void generateConfigIfNotExists(Path configFile, CommandHandler com
|
|||
newConfig.setProjectName(projectName);
|
||||
}
|
||||
if (address == null || address.isEmpty()) {
|
||||
logger.error("Address null. Using localhost");
|
||||
address = "localhost";
|
||||
logger.error("Address null. Using localhost:9274");
|
||||
address = "localhost:9274";
|
||||
}
|
||||
if (newConfig.projectName == null || newConfig.projectName.isEmpty()) {
|
||||
logger.error("ProjectName null. Using MineCraft");
|
||||
newConfig.projectName = "MineCraft";
|
||||
}
|
||||
|
||||
newConfig.netty.address = "ws://" + address + ":9274/api";
|
||||
newConfig.netty.downloadURL = "http://" + address + ":9274/%dirname%/";
|
||||
newConfig.netty.launcherURL = "http://" + address + ":9274/Launcher.jar";
|
||||
newConfig.netty.launcherEXEURL = "http://" + address + ":9274/Launcher.exe";
|
||||
int port = 9274;
|
||||
if(address.contains(":")) {
|
||||
String portString = address.substring(address.indexOf(':')+1);
|
||||
try {
|
||||
port = Integer.parseInt(portString);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.warn("Unknown port {}, using 9274", portString);
|
||||
}
|
||||
} else {
|
||||
logger.info("Address {} doesn't contains port (you want to use nginx?)", address);
|
||||
}
|
||||
newConfig.netty.address = "ws://" + address + "/api";
|
||||
newConfig.netty.downloadURL = "http://" + address + "/%dirname%/";
|
||||
newConfig.netty.launcherURL = "http://" + address + "/Launcher.jar";
|
||||
newConfig.netty.launcherEXEURL = "http://" + address + "/Launcher.exe";
|
||||
newConfig.netty.binds[0].port = port;
|
||||
|
||||
// Write LaunchServer config
|
||||
logger.info("Writing LaunchServer config file");
|
||||
|
|
|
@ -18,8 +18,10 @@
|
|||
import java.util.stream.Stream;
|
||||
|
||||
public class Main {
|
||||
private static final List<String> classpathOnly = List.of("proguard", "jline", "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";
|
||||
private static boolean isClasspathOnly(Path path) {
|
||||
var fileName = path.getFileName().toString();
|
||||
for(var e : classpathOnly) {
|
||||
|
@ -55,8 +57,9 @@ public static void main(String[] args) throws Throwable {
|
|||
ModuleLaunch launch = new ModuleLaunch();
|
||||
LaunchOptions options = new LaunchOptions();
|
||||
options.moduleConf = new LaunchOptions.ModuleConf();
|
||||
Path librariesPath = Path.of(System.getProperty(LIBRARIES_PROPERTY, "libraries"));
|
||||
List<Path> libraries;
|
||||
try(Stream<Path> files = Files.walk(Path.of("libraries"), FileVisitOption.FOLLOW_LINKS)) {
|
||||
try(Stream<Path> files = Files.walk(librariesPath, FileVisitOption.FOLLOW_LINKS)) {
|
||||
libraries = new ArrayList<>(files.filter(e -> e.getFileName().toString().endsWith(".jar")).toList());
|
||||
}
|
||||
List<Path> classpath = new ArrayList<>();
|
||||
|
@ -78,6 +81,14 @@ public static void main(String[] args) throws Throwable {
|
|||
ModuleLayer.Controller controller = (ModuleLayer.Controller) control.getJava9ModuleController();
|
||||
LaunchServerControlHolder.setControl(control);
|
||||
LaunchServerControlHolder.setController(controller);
|
||||
if(Boolean.getBoolean(DEBUG_PROPERTY)) {
|
||||
for(var e : controller.layer().modules()) {
|
||||
System.out.printf("Module %s\n", e.getName());
|
||||
for(var p : e.getPackages()) {
|
||||
System.out.printf("Package %s\n", p);
|
||||
}
|
||||
}
|
||||
}
|
||||
launch.launch("pro.gravit.launchserver.LaunchServerStarter", null, Arrays.asList(args));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@ public AuthException(String message) {
|
|||
super(message);
|
||||
}
|
||||
|
||||
public AuthException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public static AuthException need2FA() {
|
||||
return new AuthException(AuthRequestEvent.TWO_FACTOR_NEED_ERROR_MESSAGE);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package pro.gravit.launchserver.auth;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class HikariSQLSourceConfig implements SQLSourceConfig {
|
||||
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;
|
||||
}
|
||||
HikariConfig config = new HikariConfig();
|
||||
consumeIfNotNull(config::setDataSourceClassName, dsClass);
|
||||
consumeIfNotNull(config::setDataSourceProperties, dsProps);
|
||||
consumeIfNotNull(config::setDriverClassName, driverClass);
|
||||
consumeIfNotNull(config::setJdbcUrl, jdbcUrl);
|
||||
consumeIfNotNull(config::setUsername, username);
|
||||
consumeIfNotNull(config::setPassword, password);
|
||||
|
||||
this.dataSource = new HikariDataSource(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
if(dataSource == null && !initializeAtStart) {
|
||||
synchronized (this) {
|
||||
initializeConnection();
|
||||
}
|
||||
}
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
dataSource.close();
|
||||
}
|
||||
|
||||
private static <T> void consumeIfNotNull(Consumer<T> consumer, T val) {
|
||||
if (val != null) {
|
||||
consumer.accept(val);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -174,7 +174,7 @@ public AuthManager.AuthReport sudo(User user, boolean shadow) throws IOException
|
|||
}
|
||||
|
||||
@Override
|
||||
public User checkServer(Client client, String username, String serverID) throws IOException {
|
||||
public User checkServer(Client client, String username, String serverID) {
|
||||
SQLUser user = (SQLUser) getUserByUsername(username);
|
||||
if (user == null) {
|
||||
return null;
|
||||
|
@ -276,7 +276,7 @@ public void close() {
|
|||
|
||||
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), requestPermissions(set.getString(uuidColumn))) : null;
|
||||
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn)) : null;
|
||||
}
|
||||
|
||||
public ClientPermissions requestPermissions (String uuid) throws SQLException
|
||||
|
@ -286,14 +286,19 @@ public ClientPermissions requestPermissions (String uuid) throws SQLException
|
|||
}
|
||||
|
||||
private SQLUser queryUser(String sql, String value) throws SQLException {
|
||||
SQLUser user;
|
||||
try (Connection c = getSQLConfig().getConnection()) {
|
||||
PreparedStatement s = c.prepareStatement(sql);
|
||||
s.setString(1, value);
|
||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||
try (ResultSet set = s.executeQuery()) {
|
||||
return constructUser(set);
|
||||
user = constructUser(set);
|
||||
}
|
||||
}
|
||||
if(user != null) {
|
||||
user.permissions = requestPermissions(user.uuid.toString());
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private List<String> queryPermissions(String sql, String value) throws SQLException {
|
||||
|
@ -340,15 +345,14 @@ public static class SQLUser implements User {
|
|||
protected String accessToken;
|
||||
protected String serverId;
|
||||
protected final String password;
|
||||
protected final ClientPermissions permissions;
|
||||
protected ClientPermissions permissions;
|
||||
|
||||
public SQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions) {
|
||||
public SQLUser(UUID uuid, String username, String accessToken, String serverId, String password) {
|
||||
this.uuid = uuid;
|
||||
this.username = username;
|
||||
this.accessToken = accessToken;
|
||||
this.serverId = serverId;
|
||||
this.password = password;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportRegistration;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportSudo;
|
||||
import pro.gravit.launchserver.auth.core.openid.OpenIDAuthCoreProvider;
|
||||
import pro.gravit.launchserver.manangers.AuthManager;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
|
@ -53,6 +54,8 @@ public static void registerProviders() {
|
|||
providers.register("postgresql", PostgresSQLCoreProvider.class);
|
||||
providers.register("memory", MemoryAuthCoreProvider.class);
|
||||
providers.register("merge", MergeAuthCoreProvider.class);
|
||||
providers.register("openid", OpenIDAuthCoreProvider.class);
|
||||
providers.register("sql", SQLCoreProvider.class);
|
||||
registredProviders = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package pro.gravit.launchserver.auth.core;
|
||||
|
||||
import pro.gravit.launcher.base.ClientPermissions;
|
||||
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||
|
@ -44,6 +43,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)
|
||||
|
@ -72,7 +72,7 @@ protected String makeUserCols() {
|
|||
@Override
|
||||
protected MySQLUser constructUser(ResultSet set) throws SQLException {
|
||||
return set.next() ? new MySQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
|
||||
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), requestPermissions(set.getString(uuidColumn)), set.getLong(hardwareIdColumn)) : null;
|
||||
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), set.getLong(hardwareIdColumn)) : null;
|
||||
}
|
||||
|
||||
private MySQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException, IOException {
|
||||
|
@ -336,8 +336,8 @@ public String toString() {
|
|||
public static class MySQLUser extends SQLUser {
|
||||
protected long hwidId;
|
||||
|
||||
public MySQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions, long hwidId) {
|
||||
super(uuid, username, accessToken, serverId, password, permissions);
|
||||
public MySQLUser(UUID uuid, String username, String accessToken, String serverId, String password, long hwidId) {
|
||||
super(uuid, username, accessToken, serverId, password);
|
||||
this.hwidId = hwidId;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,12 +41,12 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
|
|||
}
|
||||
|
||||
@Override
|
||||
public User checkServer(Client client, String username, String serverID) throws IOException {
|
||||
public User checkServer(Client client, String username, String serverID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) throws IOException {
|
||||
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,391 @@
|
|||
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 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
|
||||
public void close() {
|
||||
super.close();
|
||||
holder.close();
|
||||
}
|
||||
|
||||
@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 {
|
||||
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 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 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 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) {
|
||||
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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,5 +6,5 @@
|
|||
import java.io.IOException;
|
||||
|
||||
public interface AuthSupportExtendedCheckServer {
|
||||
UserSession extendedCheckServer(Client client, String username, String serverID) throws IOException;
|
||||
UserSession extendedCheckServer(Client client, String username, String serverID);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public record AccessTokenResponse(@SerializedName("access_token") String accessToken,
|
||||
@SerializedName("expires_in") Long expiresIn,
|
||||
@SerializedName("refresh_expires_in") Long refreshExpiresIn,
|
||||
@SerializedName("refresh_token") String refreshToken,
|
||||
@SerializedName("token_type") String tokenType,
|
||||
@SerializedName("id_token") String idToken,
|
||||
@SerializedName("not-before-policy") Integer notBeforePolicy,
|
||||
@SerializedName("session_state") String sessionState,
|
||||
@SerializedName("scope") String scope) {
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import pro.gravit.launcher.base.ClientPermissions;
|
||||
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
|
||||
import pro.gravit.launcher.base.request.auth.AuthRequest;
|
||||
import pro.gravit.launcher.base.request.auth.password.AuthCodePassword;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.auth.AuthException;
|
||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||
import pro.gravit.launchserver.auth.HikariSQLSourceConfig;
|
||||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.core.User;
|
||||
import pro.gravit.launchserver.auth.core.UserSession;
|
||||
import pro.gravit.launchserver.manangers.AuthManager;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class OpenIDAuthCoreProvider extends AuthCoreProvider {
|
||||
private transient SQLUserStore sqlUserStore;
|
||||
private transient SQLServerSessionStore sqlSessionStore;
|
||||
private transient OpenIDAuthenticator openIDAuthenticator;
|
||||
|
||||
private OpenIDConfig openIDConfig;
|
||||
private HikariSQLSourceConfig sqlSourceConfig;
|
||||
|
||||
@Override
|
||||
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
||||
return openIDAuthenticator.getDetails();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByUsername(String username) {
|
||||
return sqlUserStore.getByUsername(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByUUID(UUID uuid) {
|
||||
return sqlUserStore.getUserByUUID(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
||||
return openIDAuthenticator.getUserSessionByOAuthAccessToken(accessToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthManager.AuthReport refreshAccessToken(String oldRefreshToken, AuthResponse.AuthContext context) {
|
||||
var tokens = openIDAuthenticator.refreshAccessToken(oldRefreshToken);
|
||||
var accessToken = tokens.accessToken();
|
||||
var refreshToken = tokens.refreshToken();
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(tokens.accessTokenExpiresIn());
|
||||
|
||||
UserSession session;
|
||||
try {
|
||||
session = openIDAuthenticator.getUserSessionByOAuthAccessToken(accessToken);
|
||||
} catch (OAuthAccessTokenExpired e) {
|
||||
throw new RuntimeException("invalid token", e);
|
||||
}
|
||||
|
||||
|
||||
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken,
|
||||
expiresIn, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
|
||||
if (password == null) {
|
||||
throw AuthException.wrongPassword();
|
||||
}
|
||||
var authCodePassword = (AuthCodePassword) password;
|
||||
|
||||
var tokens = openIDAuthenticator.authorize(authCodePassword);
|
||||
|
||||
var accessToken = tokens.accessToken();
|
||||
var refreshToken = tokens.refreshToken();
|
||||
var user = openIDAuthenticator.createUserFromToken(accessToken);
|
||||
long expiresIn = TimeUnit.SECONDS.toMillis(tokens.accessTokenExpiresIn());
|
||||
|
||||
sqlUserStore.createOrUpdateUser(user);
|
||||
|
||||
UserSession session;
|
||||
try {
|
||||
session = openIDAuthenticator.getUserSessionByOAuthAccessToken(accessToken);
|
||||
} catch (OAuthAccessTokenExpired e) {
|
||||
throw new AuthException("invalid token", e);
|
||||
}
|
||||
|
||||
if (minecraftAccess) {
|
||||
var minecraftToken = generateMinecraftToken(user);
|
||||
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftToken, accessToken, refreshToken,
|
||||
expiresIn, session);
|
||||
} else {
|
||||
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken,
|
||||
expiresIn, session);
|
||||
}
|
||||
}
|
||||
|
||||
private String generateMinecraftToken(User user) {
|
||||
return Jwts.builder()
|
||||
.issuer("LaunchServer")
|
||||
.subject(user.getUUID().toString())
|
||||
.claim("preferred_username", user.getUsername())
|
||||
.expiration(Date.from(Instant.now().plus(24, ChronoUnit.HOURS)))
|
||||
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
|
||||
.compact();
|
||||
}
|
||||
|
||||
private User createUserFromMinecraftToken(String accessToken) throws AuthException {
|
||||
try {
|
||||
var parser = Jwts.parser()
|
||||
.requireIssuer("LaunchServer")
|
||||
.verifyWith(server.keyAgreementManager.ecdsaPublicKey)
|
||||
.build();
|
||||
var claims = parser.parseSignedClaims(accessToken);
|
||||
var username = claims.getPayload().get("preferred_username", String.class);
|
||||
var uuid = UUID.fromString(claims.getPayload().getSubject());
|
||||
return new UserEntity(username, uuid, new ClientPermissions());
|
||||
} catch (JwtException e) {
|
||||
throw new AuthException("Bad minecraft token", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer server, AuthProviderPair pair) {
|
||||
super.init(server, pair);
|
||||
this.sqlSourceConfig.init();
|
||||
this.sqlUserStore = new SQLUserStore(sqlSourceConfig);
|
||||
this.sqlUserStore.init();
|
||||
this.sqlSessionStore = new SQLServerSessionStore(sqlSourceConfig);
|
||||
this.sqlSessionStore.init();
|
||||
this.openIDAuthenticator = new OpenIDAuthenticator(openIDConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User checkServer(Client client, String username, String serverID) {
|
||||
var savedServerId = sqlSessionStore.getServerIdByUsername(username);
|
||||
if (!serverID.equals(savedServerId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sqlUserStore.getByUsername(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) {
|
||||
User user;
|
||||
try {
|
||||
user = createUserFromMinecraftToken(accessToken);
|
||||
} catch (AuthException e) {
|
||||
LogHelper.error(e);
|
||||
return false;
|
||||
}
|
||||
if (!user.getUUID().equals(uuid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlUserStore.createOrUpdateUser(user);
|
||||
|
||||
return sqlSessionStore.joinServer(user.getUUID(), user.getUsername(), serverID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
sqlSourceConfig.close();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import io.jsonwebtoken.*;
|
||||
import io.jsonwebtoken.security.Jwk;
|
||||
import io.jsonwebtoken.security.JwkSet;
|
||||
import io.jsonwebtoken.security.Jwks;
|
||||
import pro.gravit.launcher.base.ClientPermissions;
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
|
||||
import pro.gravit.launcher.base.request.auth.details.AuthWebViewDetails;
|
||||
import pro.gravit.launcher.base.request.auth.password.AuthCodePassword;
|
||||
import pro.gravit.launchserver.auth.AuthException;
|
||||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.core.User;
|
||||
import pro.gravit.launchserver.auth.core.UserSession;
|
||||
import pro.gravit.utils.helper.CommonHelper;
|
||||
import pro.gravit.utils.helper.QueryHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.security.Key;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class OpenIDAuthenticator {
|
||||
private static final HttpClient CLIENT = HttpClient.newBuilder().build();
|
||||
private final OpenIDConfig openIDConfig;
|
||||
private final JwtParser jwtParser;
|
||||
|
||||
public OpenIDAuthenticator(OpenIDConfig openIDConfig) {
|
||||
this.openIDConfig = openIDConfig;
|
||||
var keyLocator = loadKeyLocator(openIDConfig);
|
||||
this.jwtParser = Jwts.parser()
|
||||
.keyLocator(keyLocator)
|
||||
.requireIssuer(openIDConfig.issuer())
|
||||
.require("azp", openIDConfig.clientId())
|
||||
.build();
|
||||
}
|
||||
|
||||
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails() {
|
||||
var state = UUID.randomUUID().toString();
|
||||
var uri = QueryBuilder.get(openIDConfig.authorizationEndpoint())
|
||||
.addQuery("response_type", "code")
|
||||
.addQuery("client_id", openIDConfig.clientId())
|
||||
.addQuery("redirect_uri", openIDConfig.redirectUri())
|
||||
.addQuery("scope", openIDConfig.scopes())
|
||||
.addQuery("state", state)
|
||||
.toUriString();
|
||||
|
||||
return List.of(new AuthWebViewDetails(uri, openIDConfig.redirectUri()));
|
||||
}
|
||||
|
||||
public TokenResponse refreshAccessToken(String oldRefreshToken) {
|
||||
var postBody = QueryBuilder.post()
|
||||
.addQuery("grant_type", "refresh_token")
|
||||
.addQuery("refresh_token", oldRefreshToken)
|
||||
.addQuery("client_id", openIDConfig.clientId())
|
||||
.addQuery("client_secret", openIDConfig.clientSecret())
|
||||
.toString();
|
||||
|
||||
var accessTokenResponse = requestToken(postBody);
|
||||
var accessToken = accessTokenResponse.accessToken();
|
||||
var refreshToken = accessTokenResponse.refreshToken();
|
||||
|
||||
try {
|
||||
readAndVerifyToken(accessToken);
|
||||
} catch (AuthException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
var accessTokenExpiresIn = Objects.requireNonNullElse(accessTokenResponse.expiresIn(), 0L);
|
||||
var refreshTokenExpiresIn = Objects.requireNonNullElse(accessTokenResponse.refreshExpiresIn(), 0L);
|
||||
|
||||
return new TokenResponse(accessToken, accessTokenExpiresIn,
|
||||
refreshToken, refreshTokenExpiresIn);
|
||||
}
|
||||
|
||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws AuthCoreProvider.OAuthAccessTokenExpired {
|
||||
Jws<Claims> token;
|
||||
try {
|
||||
token = readAndVerifyToken(accessToken);
|
||||
} catch (AuthException e) {
|
||||
throw new AuthCoreProvider.OAuthAccessTokenExpired("Can't read token", e);
|
||||
}
|
||||
var user = createUserFromToken(token);
|
||||
long expiresIn = 0;
|
||||
var expDate = token.getPayload().getExpiration();
|
||||
if (expDate != null) {
|
||||
expiresIn = expDate.toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
return new OpenIDUserSession(user, accessToken, expiresIn);
|
||||
}
|
||||
|
||||
public TokenResponse authorize(AuthCodePassword authCode) throws IOException {
|
||||
var uri = URI.create(authCode.uri);
|
||||
var queries = QueryHelper.splitUriQuery(uri);
|
||||
|
||||
String code = CommonHelper.multimapFirstOrNullValue("code", queries);
|
||||
String error = CommonHelper.multimapFirstOrNullValue("error", queries);
|
||||
String errorDescription = CommonHelper.multimapFirstOrNullValue("error_description", queries);
|
||||
|
||||
if (error != null && !error.isBlank()) {
|
||||
throw new AuthException("Auth error. Error: %s, description: %s".formatted(error, errorDescription));
|
||||
}
|
||||
|
||||
|
||||
var postBody = QueryBuilder.post()
|
||||
.addQuery("grant_type", "authorization_code")
|
||||
.addQuery("code", code)
|
||||
.addQuery("redirect_uri", openIDConfig.redirectUri())
|
||||
.addQuery("client_id", openIDConfig.clientId())
|
||||
.addQuery("client_secret", openIDConfig.clientSecret())
|
||||
.toString();
|
||||
|
||||
var accessTokenResponse = requestToken(postBody);
|
||||
var accessToken = accessTokenResponse.accessToken();
|
||||
var refreshToken = accessTokenResponse.refreshToken();
|
||||
|
||||
readAndVerifyToken(accessToken);
|
||||
|
||||
var accessTokenExpiresIn = Objects.requireNonNullElse(accessTokenResponse.expiresIn(), 0L);
|
||||
var refreshTokenExpiresIn = Objects.requireNonNullElse(accessTokenResponse.refreshExpiresIn(), 0L);
|
||||
|
||||
return new TokenResponse(accessToken, accessTokenExpiresIn,
|
||||
refreshToken, refreshTokenExpiresIn);
|
||||
}
|
||||
|
||||
public User createUserFromToken(String accessToken) throws AuthException {
|
||||
return createUserFromToken(readAndVerifyToken(accessToken));
|
||||
}
|
||||
|
||||
private Jws<Claims> readAndVerifyToken(String accessToken) throws AuthException {
|
||||
if (accessToken == null) {
|
||||
throw new AuthException("Token is null");
|
||||
}
|
||||
|
||||
try {
|
||||
return jwtParser.parseSignedClaims(accessToken);
|
||||
} catch (JwtException e) {
|
||||
throw new AuthException("Bad token", e);
|
||||
}
|
||||
}
|
||||
|
||||
private User createUserFromToken(Jws<Claims> token) {
|
||||
var username = token.getPayload().get(openIDConfig.extractorConfig().usernameClaim(), String.class);
|
||||
var uuidStr = token.getPayload().get(openIDConfig.extractorConfig().uuidClaim(), String.class);
|
||||
var uuid = UUID.fromString(uuidStr);
|
||||
return new UserEntity(username, uuid, new ClientPermissions());
|
||||
}
|
||||
|
||||
private AccessTokenResponse requestToken(String postBody) {
|
||||
var request = HttpRequest.newBuilder()
|
||||
.uri(openIDConfig.tokenUri())
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.header("Accept", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(postBody))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> resp;
|
||||
try {
|
||||
resp = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return Launcher.gsonManager.gson.fromJson(resp.body(), AccessTokenResponse.class);
|
||||
}
|
||||
|
||||
private static KeyLocator loadKeyLocator(OpenIDConfig openIDConfig) {
|
||||
var request = HttpRequest.newBuilder(openIDConfig.jwksUri()).GET().build();
|
||||
HttpResponse<String> response;
|
||||
try {
|
||||
response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
var jwks = Jwks.setParser().build().parse(response.body());
|
||||
return new KeyLocator(jwks);
|
||||
}
|
||||
|
||||
private static class KeyLocator extends LocatorAdapter<Key> {
|
||||
private final Map<String, Key> keys;
|
||||
|
||||
public KeyLocator(JwkSet jwks) {
|
||||
this.keys = jwks.getKeys().stream().collect(
|
||||
Collectors.toMap(jwk -> String.valueOf(jwk.get("kid")), Jwk::toKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Key locate(JweHeader header) {
|
||||
return super.locate(header);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Key locate(JwsHeader header) {
|
||||
return keys.get(header.getKeyId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Key doLocate(Header header) {
|
||||
return super.doLocate(header);
|
||||
}
|
||||
}
|
||||
|
||||
record OpenIDUserSession(User user, String token, long expiresIn) implements UserSession {
|
||||
@Override
|
||||
public String getID() {
|
||||
return user.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMinecraftAccessToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getExpireIn() {
|
||||
return expiresIn;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
public record OpenIDConfig(URI tokenUri, String authorizationEndpoint, String clientId, String clientSecret,
|
||||
String redirectUri, URI jwksUri, String scopes, String issuer,
|
||||
ClaimExtractorConfig extractorConfig) {
|
||||
|
||||
public record ClaimExtractorConfig(String usernameClaim, String uuidClaim) {}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Xakep_SDK
|
||||
*/
|
||||
public class QueryBuilder {
|
||||
private final String uri;
|
||||
private final StringBuilder query = new StringBuilder();
|
||||
|
||||
public QueryBuilder(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public static QueryBuilder get(String uri) {
|
||||
Objects.requireNonNull(uri, "uri");
|
||||
if (uri.endsWith("/")) {
|
||||
uri = uri.substring(0, uri.length() - 1);
|
||||
}
|
||||
return new QueryBuilder(uri);
|
||||
}
|
||||
|
||||
public static QueryBuilder post() {
|
||||
return new QueryBuilder(null);
|
||||
}
|
||||
|
||||
public QueryBuilder addQuery(String key, String value) {
|
||||
if (!query.isEmpty()) {
|
||||
query.append('&');
|
||||
}
|
||||
query.append(URLEncoder.encode(key, StandardCharsets.UTF_8))
|
||||
.append('=')
|
||||
.append(URLEncoder.encode(value, StandardCharsets.UTF_8));
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toUriString() {
|
||||
if (uri != null) {
|
||||
if (query. isEmpty()) {
|
||||
return uri;
|
||||
}
|
||||
return uri + '?' + query;
|
||||
}
|
||||
return toQueryString();
|
||||
}
|
||||
|
||||
public String toQueryString() {
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toUriString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import pro.gravit.launchserver.auth.SQLSourceConfig;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SQLServerSessionStore implements ServerSessionStore {
|
||||
private static final String CREATE_TABLE = """
|
||||
create table if not exists `gravit_server_session` (
|
||||
id int auto_increment,
|
||||
uuid varchar(36),
|
||||
username varchar(255),
|
||||
server_id varchar(41),
|
||||
primary key (id),
|
||||
unique (uuid),
|
||||
unique (username)
|
||||
);
|
||||
""";
|
||||
private static final String DELETE_SERVER_ID = """
|
||||
delete from `gravit_server_session` where uuid = ?
|
||||
""";
|
||||
private static final String INSERT_SERVER_ID = """
|
||||
insert into `gravit_server_session` (uuid, username, server_id) values (?, ?, ?)
|
||||
""";
|
||||
private static final String SELECT_SERVER_ID_BY_USERNAME = """
|
||||
select server_id from `gravit_server_session` where username = ?
|
||||
""";
|
||||
|
||||
private final SQLSourceConfig sqlSourceConfig;
|
||||
|
||||
public SQLServerSessionStore(SQLSourceConfig sqlSourceConfig) {
|
||||
this.sqlSourceConfig = sqlSourceConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean joinServer(UUID uuid, String username, String serverId) {
|
||||
try (var connection = sqlSourceConfig.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
var savepoint = connection.setSavepoint();
|
||||
try (var deleteServerIdStmt = connection.prepareStatement(DELETE_SERVER_ID);
|
||||
var insertServerIdStmt = connection.prepareStatement(INSERT_SERVER_ID)) {
|
||||
deleteServerIdStmt.setString(1, uuid.toString());
|
||||
deleteServerIdStmt.execute();
|
||||
insertServerIdStmt.setString(1, uuid.toString());
|
||||
insertServerIdStmt.setString(2, username);
|
||||
insertServerIdStmt.setString(3, serverId);
|
||||
insertServerIdStmt.execute();
|
||||
connection.commit();
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
connection.rollback(savepoint);
|
||||
throw e;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogHelper.debug("Can't join server. Username: %s".formatted(username));
|
||||
LogHelper.error(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerIdByUsername(String username) {
|
||||
try (var connection = sqlSourceConfig.getConnection();
|
||||
var selectServerId = connection.prepareStatement(SELECT_SERVER_ID_BY_USERNAME)) {
|
||||
selectServerId.setString(1, username);
|
||||
try (var rs = selectServerId.executeQuery()) {
|
||||
if (!rs.next()) {
|
||||
return null;
|
||||
}
|
||||
return rs.getString("server_id");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogHelper.debug("Can't find server id by username. Username: %s".formatted(username));
|
||||
LogHelper.error(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
try (var connection = sqlSourceConfig.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
var savepoint = connection.setSavepoint();
|
||||
try (var createTableStmt = connection.prepareStatement(CREATE_TABLE)) {
|
||||
createTableStmt.execute();
|
||||
connection.commit();
|
||||
} catch (Exception e) {
|
||||
connection.rollback(savepoint);
|
||||
throw e;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import pro.gravit.launcher.base.ClientPermissions;
|
||||
import pro.gravit.launchserver.auth.HikariSQLSourceConfig;
|
||||
import pro.gravit.launchserver.auth.core.User;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SQLUserStore implements UserStore {
|
||||
private static final String CREATE_USER_TABLE = """
|
||||
create table if not exists `gravit_user` (
|
||||
id int auto_increment,
|
||||
uuid varchar(36),
|
||||
username varchar(255),
|
||||
primary key (id),
|
||||
unique (uuid),
|
||||
unique (username)
|
||||
)
|
||||
""";
|
||||
private static final String INSERT_USER = """
|
||||
insert into `gravit_user` (uuid, username) values (?, ?)
|
||||
""";
|
||||
private static final String DELETE_USER_BY_NAME = """
|
||||
delete `gravit_user` where username = ?
|
||||
""";
|
||||
private static final String SELECT_USER_BY_NAME = """
|
||||
select uuid, username from `gravit_user` where username = ?
|
||||
""";
|
||||
private static final String SELECT_USER_BY_UUID = """
|
||||
select uuid, username from `gravit_user` where uuid = ?
|
||||
""";
|
||||
|
||||
private final HikariSQLSourceConfig sqlSourceConfig;
|
||||
|
||||
public SQLUserStore(HikariSQLSourceConfig sqlSourceConfig) {
|
||||
this.sqlSourceConfig = sqlSourceConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getByUsername(String username) {
|
||||
try (var connection = sqlSourceConfig.getConnection();
|
||||
var selectUserStmt = connection.prepareStatement(SELECT_USER_BY_NAME)) {
|
||||
selectUserStmt.setString(1, username);
|
||||
try (var rs = selectUserStmt.executeQuery()) {
|
||||
if (!rs.next()) {
|
||||
LogHelper.debug("User not found, username: %s".formatted(username));
|
||||
return null;
|
||||
}
|
||||
return new UserEntity(rs.getString("username"),
|
||||
UUID.fromString(rs.getString("uuid")),
|
||||
new ClientPermissions());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getUserByUUID(UUID uuid) {
|
||||
try (var connection = sqlSourceConfig.getConnection();
|
||||
var selectUserStmt = connection.prepareStatement(SELECT_USER_BY_UUID)) {
|
||||
selectUserStmt.setString(1, uuid.toString());
|
||||
try (var rs = selectUserStmt.executeQuery()) {
|
||||
if (!rs.next()) {
|
||||
LogHelper.debug("User not found, UUID: %s".formatted(uuid));
|
||||
return null;
|
||||
}
|
||||
return new UserEntity(rs.getString("username"),
|
||||
UUID.fromString(rs.getString("uuid")),
|
||||
new ClientPermissions());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createOrUpdateUser(User user) {
|
||||
try (var connection = sqlSourceConfig.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
var savepoint = connection.setSavepoint();
|
||||
try (var deleteUserStmt = connection.prepareStatement(DELETE_USER_BY_NAME);
|
||||
var insertUserStmt = connection.prepareStatement(INSERT_USER)) {
|
||||
deleteUserStmt.setString(1, user.getUsername());
|
||||
deleteUserStmt.execute();
|
||||
insertUserStmt.setString(1, user.getUUID().toString());
|
||||
insertUserStmt.setString(2, user.getUsername());
|
||||
insertUserStmt.execute();
|
||||
connection.commit();
|
||||
LogHelper.debug("User saved. UUID: %s, username: %s".formatted(user.getUUID(), user.getUsername()));
|
||||
} catch (Exception e) {
|
||||
connection.rollback(savepoint);
|
||||
throw e;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
LogHelper.debug("Failed to save user");
|
||||
LogHelper.error(e);
|
||||
throw new RuntimeException("Failed to save user", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void init() {
|
||||
try (var connection = sqlSourceConfig.getConnection()) {
|
||||
connection.setAutoCommit(false);
|
||||
var savepoint = connection.setSavepoint();
|
||||
try (var createUserTableStmt = connection.prepareStatement(CREATE_USER_TABLE)) {
|
||||
createUserTableStmt.execute();
|
||||
connection.commit();
|
||||
} catch (Exception e) {
|
||||
connection.rollback(savepoint);
|
||||
throw e;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface ServerSessionStore {
|
||||
boolean joinServer(UUID uuid, String username, String serverId);
|
||||
String getServerIdByUsername(String username);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
public record TokenResponse(String accessToken, long accessTokenExpiresIn,
|
||||
String refreshToken, long refreshTokenExpiresIn) {
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import pro.gravit.launcher.base.ClientPermissions;
|
||||
import pro.gravit.launchserver.auth.core.User;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
record UserEntity(String username, UUID uuid, ClientPermissions permissions) implements User {
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientPermissions getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package pro.gravit.launchserver.auth.core.openid;
|
||||
|
||||
import pro.gravit.launchserver.auth.core.User;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface UserStore {
|
||||
User getByUsername(String username);
|
||||
|
||||
User getUserByUUID(UUID uuid);
|
||||
|
||||
void createOrUpdateUser(User user);
|
||||
}
|
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
|
@ -4,9 +4,7 @@
|
|||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launchserver.binary.tasks.LauncherBuildTask;
|
||||
import pro.gravit.utils.helper.CommonHelper;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -14,11 +12,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 +78,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() {
|
||||
|
@ -37,10 +56,14 @@ public final byte[] getDigest() {
|
|||
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;
|
||||
public final boolean sync() {
|
||||
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;
|
||||
|
@ -37,8 +34,10 @@ public Path process(Path inputFile) throws IOException {
|
|||
server.launcherBinary.addonLibs.clear();
|
||||
server.launcherBinary.files.clear();
|
||||
IOHelper.walk(server.launcherLibraries, new ListFileVisitor(server.launcherBinary.coreLibs), false);
|
||||
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();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfileBuilder;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfileVersions;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.command.Command;
|
||||
|
@ -12,7 +13,6 @@
|
|||
import pro.gravit.utils.command.CommandException;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
|
@ -61,9 +61,11 @@ public void invoke(String... args) throws IOException, CommandException {
|
|||
try {
|
||||
JsonElement clientJson = server.mirrorManager.jsonRequest(null, "GET", "clients/%s.json", versionName);
|
||||
clientProfile = Launcher.gsonManager.configGson.fromJson(clientJson, ClientProfile.class);
|
||||
clientProfile.setTitle(dirName);
|
||||
clientProfile.setDir(dirName);
|
||||
clientProfile.setUUID(UUID.randomUUID());
|
||||
var builder = new ClientProfileBuilder(clientProfile);
|
||||
builder.setTitle(dirName);
|
||||
builder.setDir(dirName);
|
||||
builder.setUuid(UUID.randomUUID());
|
||||
clientProfile = builder.createClientProfile();
|
||||
if (clientProfile.getServers() != null) {
|
||||
ClientProfile.ServerProfile serverProfile = clientProfile.getDefaultServerProfile();
|
||||
if (serverProfile != null) {
|
||||
|
@ -94,10 +96,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();
|
||||
|
|
|
@ -2,15 +2,13 @@
|
|||
|
||||
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.launcher.base.profiles.ClientProfileBuilder;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.command.Command;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
@ -25,7 +23,7 @@ public CloneProfileCommand(LaunchServer server) {
|
|||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[profile file name] [new profile title]";
|
||||
return "[profile title/uuid] [new profile title]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -36,16 +34,16 @@ 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]);
|
||||
}
|
||||
profile.setTitle(args[1]);
|
||||
profile.setUUID(UUID.randomUUID());
|
||||
var builder = new ClientProfileBuilder(profile);
|
||||
builder.setTitle(args[1]);
|
||||
builder.setUuid(UUID.randomUUID());
|
||||
if(profile.getServers().size() == 1) {
|
||||
profile.getServers().getFirst().name = args[1];
|
||||
}
|
||||
|
@ -61,11 +59,9 @@ public void invoke(String... args) throws Exception {
|
|||
}
|
||||
});
|
||||
}
|
||||
profile.setDir(args[1]);
|
||||
var targetPath = server.profilesDir.resolve(args[1].concat(".json"));
|
||||
try(Writer writer = IOHelper.newWriter(targetPath)) {
|
||||
Launcher.gsonManager.gson.toJson(profile, writer);
|
||||
}
|
||||
builder.setDir(args[1]);
|
||||
profile = builder.createClientProfile();
|
||||
server.config.profileProvider.addProfile(profile);
|
||||
logger.info("Profile {} cloned from {}", args[1], args[0]);
|
||||
server.syncProfilesDir();
|
||||
server.syncUpdatesDir(List.of(args[1]));
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.command.Command;
|
||||
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 +27,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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public String getUsageDescription() {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
public void invoke(String... args) {
|
||||
for(var profile : server.getProfiles()) {
|
||||
logger.info("{} ({}) {}", profile.getTitle(), profile.getVersion().toString(), profile.isLimited() ? "limited" : "");
|
||||
}
|
||||
|
|
|
@ -2,14 +2,10 @@
|
|||
|
||||
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.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.command.Command;
|
||||
import pro.gravit.launchserver.helper.MakeProfileHelper;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.Writer;
|
||||
|
||||
public class MakeProfileCommand extends Command {
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
|
@ -37,9 +33,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();
|
||||
}
|
||||
|
|
|
@ -2,17 +2,10 @@
|
|||
|
||||
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.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.command.Command;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SaveProfilesCommand extends Command {
|
||||
|
@ -22,21 +15,6 @@ public SaveProfilesCommand(LaunchServer server) {
|
|||
super(server);
|
||||
}
|
||||
|
||||
public static void saveProfile(ClientProfile profile, Path path) throws IOException {
|
||||
if (profile.getUUID() == null) profile.setUUID(UUID.randomUUID());
|
||||
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...]";
|
||||
|
@ -52,17 +30,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();
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ public void invoke(String... args) throws Exception {
|
|||
Files.deleteIfExists(proguardConf.mappings);
|
||||
}
|
||||
});
|
||||
return null;
|
||||
return commands;
|
||||
}
|
||||
|
||||
public static class ProGuardMultiReleaseFixer implements LauncherBuildTask {
|
||||
|
@ -213,7 +213,7 @@ public Path process(Path inputFile) throws IOException {
|
|||
args.add(IOHelper.resolveJavaBin(IOHelper.JVM_DIR).toAbsolutePath().toString());
|
||||
args.addAll(component.jvmArgs);
|
||||
args.add("-cp");
|
||||
try(Stream<Path> files = Files.walk(Path.of("libraries"), FileVisitOption.FOLLOW_LINKS)) {
|
||||
try(Stream<Path> files = Files.walk(server.librariesDir, FileVisitOption.FOLLOW_LINKS)) {
|
||||
args.add(files
|
||||
.filter(e -> e.getFileName().toString().endsWith(".jar"))
|
||||
.map(path -> path.toAbsolutePath().toString())
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package pro.gravit.launchserver.config;
|
||||
|
||||
import io.netty.channel.epoll.Epoll;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
@ -9,12 +8,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 +34,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 +64,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 +85,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 +167,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 +215,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 +272,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;
|
||||
|
|
|
@ -49,7 +49,6 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
|
|||
// Official Mojang launcher java arguments
|
||||
if (version.compareTo(ClientProfileVersions.MINECRAFT_1_12_2) <= 0) {
|
||||
// lwjgl3ify arguments https://github.com/GTNewHorizons/lwjgl3ify
|
||||
jvmArgs.add("-Djava.security.manager=allow");
|
||||
jvmArgs.add("--add-opens");
|
||||
jvmArgs.add("java.base/jdk.internal.loader=ALL-UNNAMED");
|
||||
jvmArgs.add("--add-opens");
|
||||
|
@ -103,6 +102,7 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
|
|||
}
|
||||
if (fabric.isPresent()) {
|
||||
builder.setAltClassPath(fabric.orElseThrow().getAltClassPath());
|
||||
jvmArgs.add("-Dsodium.checks.issue2561=false"); // Please don't check LWJL3 version (Sodium: https://github.com/CaffeineMC/sodium-fabric/issues/2561 )
|
||||
}
|
||||
if(quilt.isPresent()) {
|
||||
builder.setClassLoaderConfig(ClientProfile.ClassLoaderConfig.SYSTEM_ARGS);
|
||||
|
@ -203,7 +203,7 @@ public static String getMainClassByVersion(ClientProfile.Version version, MakePr
|
|||
return "cpw.mods.modlauncher.Launcher";
|
||||
}
|
||||
if (findOption(options, MakeProfileOptionFabric.class).isPresent()) {
|
||||
return "net.fabricmc.loader.launch.knot.KnotClient";
|
||||
return "net.fabricmc.loader.impl.launch.knot.KnotClient";
|
||||
}
|
||||
if(findOption(options, MakeProfilesOptionsQuilt.class).isPresent()) {
|
||||
return "org.quiltmc.loader.impl.launch.knot.KnotClient";
|
||||
|
|
|
@ -33,7 +33,7 @@ public class LauncherModuleLoader {
|
|||
|
||||
public LauncherModuleLoader(LaunchServer server) {
|
||||
this.server = server;
|
||||
modulesDir = server.dir.resolve("launcher-modules");
|
||||
modulesDir = server.launcherModulesDir;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package pro.gravit.launchserver.manangers;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import marcono1234.gson.recordadapter.RecordTypeAdapterFactory;
|
||||
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
|
||||
import pro.gravit.launcher.core.managers.GsonManager;
|
||||
import pro.gravit.launcher.base.modules.events.PreGsonPhase;
|
||||
|
@ -15,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;
|
||||
|
@ -34,9 +35,6 @@ public LaunchServerGsonManager(LaunchServerModulesManager modulesManager) {
|
|||
@Override
|
||||
public void registerAdapters(GsonBuilder builder) {
|
||||
super.registerAdapters(builder);
|
||||
builder.registerTypeAdapterFactory(RecordTypeAdapterFactory.builder()
|
||||
.allowMissingComponentValues()
|
||||
.create());
|
||||
builder.registerTypeAdapter(ClientProfile.Version.class, new ClientProfile.Version.GsonSerializer());
|
||||
builder.registerTypeAdapter(TextureProvider.class, new UniversalJsonAdapter<>(TextureProvider.providers));
|
||||
builder.registerTypeAdapter(AuthCoreProvider.class, new UniversalJsonAdapter<>(AuthCoreProvider.providers));
|
||||
|
@ -50,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);
|
||||
}
|
||||
|
|
|
@ -1,129 +1,45 @@
|
|||
package pro.gravit.launchserver.manangers;
|
||||
|
||||
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.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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());
|
||||
@Deprecated
|
||||
public void readUpdatesFromCache() {
|
||||
|
||||
}
|
||||
|
||||
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(cacheFile);
|
||||
}
|
||||
|
||||
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 readUpdatesDir() {
|
||||
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.epoll.Epoll;
|
||||
import io.netty.channel.group.DefaultChannelGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
|
@ -36,14 +35,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,58 @@
|
|||
|
||||
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.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.Epoll;
|
||||
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,94 @@
|
|||
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.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();
|
||||
try (channel) {
|
||||
channel.configureBlocking(true);
|
||||
String command = null;
|
||||
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();
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.error("Unix command socket server error", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@
|
|||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.UnsupportedTemporalTypeException;
|
||||
import java.util.Arrays;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
public interface WebSocketServerResponse extends WebSocketRequest {
|
||||
String getType();
|
||||
|
||||
void execute(ChannelHandlerContext ctx, Client client) throws Exception;
|
||||
void execute(ChannelHandlerContext ctx, Client client);
|
||||
|
||||
default ThreadSafeStatus getThreadSafeStatus() {
|
||||
return ThreadSafeStatus.READ;
|
||||
|
|
|
@ -23,7 +23,7 @@ public String getType() {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
public void execute(ChannelHandlerContext ctx, Client client) {
|
||||
sendResult(new CurrentUserRequestEvent(collectUserInfoFromClient(server, client)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ public String getType() {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
public void execute(ChannelHandlerContext ctx, Client client) {
|
||||
if (accessToken == null && !client.isAuth && needUserInfo) {
|
||||
sendError("Invalid request");
|
||||
return;
|
||||
|
|
|
@ -12,7 +12,7 @@ public String getType() {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
public void execute(ChannelHandlerContext ctx, Client client) {
|
||||
if(!client.isAuth || client.auth == null || client.getUser() == null) {
|
||||
sendError("Access denied");
|
||||
return;
|
||||
|
|
|
@ -14,7 +14,7 @@ public String getType() {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
public void execute(ChannelHandlerContext ctx, Client client) {
|
||||
if(!client.isAuth || client.auth == null || client.getUser() == null) {
|
||||
sendError("Access denied");
|
||||
return;
|
||||
|
|
|
@ -12,7 +12,7 @@ public String getType() {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
public void execute(ChannelHandlerContext ctx, Client client) {
|
||||
sendResult(new GetConnectUUIDRequestEvent(connectUUID, server.shardId));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ClientLauncherProcess {
|
||||
public final List<String> pre = new LinkedList<>();
|
||||
|
@ -109,14 +108,14 @@ public static String getPathSeparator() {
|
|||
|
||||
private void applyClientProfile() {
|
||||
this.systemClassPath.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString());
|
||||
Collections.addAll(this.jvmArgs, this.params.profile.getJvmArgs());
|
||||
this.jvmArgs.addAll(this.params.profile.getJvmArgs());
|
||||
for (OptionalAction a : this.params.actions) {
|
||||
if (a instanceof OptionalActionJvmArgs) {
|
||||
this.jvmArgs.addAll(((OptionalActionJvmArgs) a).args);
|
||||
}
|
||||
}
|
||||
this.systemEnv.put("JAVA_HOME", javaVersion.jvmDir.toString());
|
||||
Collections.addAll(this.systemClassPath, this.params.profile.getAlternativeClassPath());
|
||||
this.systemClassPath.addAll(this.params.profile.getAlternativeClassPath());
|
||||
if (params.ram > 0) {
|
||||
this.jvmArgs.add("-Xmx" + params.ram + 'M');
|
||||
}
|
||||
|
@ -128,8 +127,6 @@ private void applyClientProfile() {
|
|||
this.params.oauthExpiredTime = Request.getTokenExpiredTime();
|
||||
this.params.extendedTokens = Request.getExtendedTokens();
|
||||
}
|
||||
this.jvmModules.addAll(this.params.profile.getModules());
|
||||
this.jvmModulesPaths.addAll(this.params.profile.getModulePath());
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderCreateEvent(this));
|
||||
}
|
||||
|
||||
|
@ -144,11 +141,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)
|
||||
.filter(x -> !params.profile.getModulePath().contains(workDir.relativize(x).toString()))
|
||||
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 {
|
||||
|
|
|
@ -12,13 +12,11 @@
|
|||
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
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
|
||||
|
@ -41,8 +39,6 @@ public final class ClientProfile implements Comparable<ClientProfile> {
|
|||
@LauncherNetworkAPI
|
||||
private List<String> updateExclusions;
|
||||
@LauncherNetworkAPI
|
||||
private List<String> updateShared;
|
||||
@LauncherNetworkAPI
|
||||
private List<String> updateVerify;
|
||||
@LauncherNetworkAPI
|
||||
private Set<OptionalFile> updateOptional;
|
||||
|
@ -51,10 +47,6 @@ public final class ClientProfile implements Comparable<ClientProfile> {
|
|||
@LauncherNetworkAPI
|
||||
private List<String> classPath;
|
||||
@LauncherNetworkAPI
|
||||
private List<String> modulePath = new ArrayList<>();
|
||||
@LauncherNetworkAPI
|
||||
private List<String> modules = new ArrayList<>();
|
||||
@LauncherNetworkAPI
|
||||
private List<String> altClassPath;
|
||||
@LauncherNetworkAPI
|
||||
private List<String> clientArgs;
|
||||
|
@ -89,54 +81,37 @@ public final class ClientProfile implements Comparable<ClientProfile> {
|
|||
@LauncherNetworkAPI
|
||||
private LaunchOptions.ModuleConf moduleConf;
|
||||
|
||||
public ClientProfile() {
|
||||
update = new ArrayList<>();
|
||||
updateExclusions = new ArrayList<>();
|
||||
updateShared = new ArrayList<>();
|
||||
updateVerify = new ArrayList<>();
|
||||
updateOptional = new HashSet<>();
|
||||
jvmArgs = new ArrayList<>();
|
||||
classPath = new ArrayList<>();
|
||||
modulePath = new ArrayList<>();
|
||||
altClassPath = new ArrayList<>();
|
||||
clientArgs = new ArrayList<>();
|
||||
compatClasses = new ArrayList<>();
|
||||
properties = new HashMap<>();
|
||||
servers = new ArrayList<>(1);
|
||||
classLoaderConfig = ClassLoaderConfig.LAUNCHER;
|
||||
flags = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ClientProfile(List<String> update, List<String> updateExclusions, List<String> updateShared, List<String> updateVerify, Set<OptionalFile> updateOptional, List<String> jvmArgs, List<String> classPath, List<String> modulePath, List<String> modules, List<String> altClassPath, List<String> clientArgs, List<String> compatClasses, Map<String, String> properties, List<ServerProfile> servers, ClassLoaderConfig classLoaderConfig, List<CompatibilityFlags> flags, Version version, String assetIndex, String dir, String assetDir, int recommendJavaVersion, int minJavaVersion, int maxJavaVersion, ProfileDefaultSettings settings, int sortIndex, UUID uuid, String title, String info, String mainClass) {
|
||||
public ClientProfile(String title, UUID uuid, Version version, String info, String dir, int sortIndex, String assetIndex, String assetDir, List<String> update, List<String> updateExclusions, List<String> updateVerify, Set<OptionalFile> updateOptional, List<String> jvmArgs, List<String> classPath, List<String> altClassPath, List<String> clientArgs, List<String> compatClasses, List<String> loadNatives, Map<String, String> properties, List<ServerProfile> servers, ClassLoaderConfig classLoaderConfig, List<CompatibilityFlags> flags, int recommendJavaVersion, int minJavaVersion, int maxJavaVersion, ProfileDefaultSettings settings, boolean limited, String mainClass, String mainModule, LaunchOptions.ModuleConf moduleConf) {
|
||||
this.title = title;
|
||||
this.uuid = uuid;
|
||||
this.version = version;
|
||||
this.info = info;
|
||||
this.dir = dir;
|
||||
this.sortIndex = sortIndex;
|
||||
this.assetIndex = assetIndex;
|
||||
this.assetDir = assetDir;
|
||||
this.update = update;
|
||||
this.updateExclusions = updateExclusions;
|
||||
this.updateShared = updateShared;
|
||||
this.updateVerify = updateVerify;
|
||||
this.updateOptional = updateOptional;
|
||||
this.jvmArgs = jvmArgs;
|
||||
this.classPath = classPath;
|
||||
this.modulePath = modulePath;
|
||||
this.modules = modules;
|
||||
this.altClassPath = altClassPath;
|
||||
this.clientArgs = clientArgs;
|
||||
this.compatClasses = compatClasses;
|
||||
this.loadNatives = loadNatives;
|
||||
this.properties = properties;
|
||||
this.servers = servers;
|
||||
this.classLoaderConfig = classLoaderConfig;
|
||||
this.version = version;
|
||||
this.assetIndex = assetIndex;
|
||||
this.dir = dir;
|
||||
this.assetDir = assetDir;
|
||||
this.flags = flags;
|
||||
this.recommendJavaVersion = recommendJavaVersion;
|
||||
this.minJavaVersion = minJavaVersion;
|
||||
this.maxJavaVersion = maxJavaVersion;
|
||||
this.settings = settings;
|
||||
this.sortIndex = sortIndex;
|
||||
this.uuid = uuid;
|
||||
this.title = title;
|
||||
this.info = info;
|
||||
this.limited = limited;
|
||||
this.mainClass = mainClass;
|
||||
this.flags = flags;
|
||||
this.mainModule = mainModule;
|
||||
this.moduleConf = moduleConf;
|
||||
}
|
||||
|
||||
public ServerProfile getDefaultServerProfile() {
|
||||
|
@ -159,34 +134,22 @@ public FileNameMatcher getAssetUpdateMatcher() {
|
|||
return getVersion().compareTo(ClientProfileVersions.MINECRAFT_1_7_10) >= 0 ? ASSET_MATCHER : null;
|
||||
}
|
||||
|
||||
public String[] getClassPath() {
|
||||
return classPath.toArray(new String[0]);
|
||||
public List<String> getClassPath() {
|
||||
return Collections.unmodifiableList(classPath);
|
||||
}
|
||||
|
||||
public List<String> getModulePath() {
|
||||
return Collections.unmodifiableList(modulePath);
|
||||
public List<String> getAlternativeClassPath() {
|
||||
return Collections.unmodifiableList(altClassPath);
|
||||
}
|
||||
|
||||
public List<String> getModules() {
|
||||
return Collections.unmodifiableList(modules);
|
||||
}
|
||||
|
||||
public String[] getAlternativeClassPath() {
|
||||
return altClassPath.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public String[] getClientArgs() {
|
||||
return clientArgs.toArray(new String[0]);
|
||||
public List<String> getClientArgs() {
|
||||
return Collections.unmodifiableList(clientArgs);
|
||||
}
|
||||
|
||||
public String getDir() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
public void setDir(String dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
public String getAssetDir() {
|
||||
return assetDir;
|
||||
}
|
||||
|
@ -195,24 +158,25 @@ public List<String> getUpdateExclusions() {
|
|||
return Collections.unmodifiableList(updateExclusions);
|
||||
}
|
||||
|
||||
public FileNameMatcher getClientUpdateMatcher(/*boolean excludeOptional*/) {
|
||||
public List<String> getUpdate() {
|
||||
return Collections.unmodifiableList(update);
|
||||
}
|
||||
|
||||
public List<String> getUpdateVerify() {
|
||||
return Collections.unmodifiableList(updateVerify);
|
||||
}
|
||||
|
||||
public FileNameMatcher getClientUpdateMatcher() {
|
||||
String[] updateArray = update.toArray(new String[0]);
|
||||
String[] verifyArray = updateVerify.toArray(new String[0]);
|
||||
List<String> excludeList;
|
||||
//if(excludeOptional)
|
||||
//{
|
||||
// excludeList = new ArrayList<>();
|
||||
// excludeList.addAll(updateExclusions);
|
||||
// excludeList.addAll(updateOptional);
|
||||
//}
|
||||
//else
|
||||
excludeList = updateExclusions;
|
||||
String[] exclusionsArray = excludeList.toArray(new String[0]);
|
||||
return new FileNameMatcher(updateArray, verifyArray, exclusionsArray);
|
||||
}
|
||||
|
||||
public String[] getJvmArgs() {
|
||||
return jvmArgs.toArray(new String[0]);
|
||||
public List<String> getJvmArgs() {
|
||||
return Collections.unmodifiableList(jvmArgs);
|
||||
}
|
||||
|
||||
public String getMainClass() {
|
||||
|
@ -289,10 +253,6 @@ public OptionalFile getOptionalFile(String file) {
|
|||
return null;
|
||||
}
|
||||
|
||||
public Collection<String> getShared() {
|
||||
return updateShared;
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
ServerProfile profile = getDefaultServerProfile();
|
||||
return profile == null ? 25565 : profile.serverPort;
|
||||
|
@ -306,26 +266,14 @@ public String getTitle() {
|
|||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Version version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean isUpdateFastCheck() {
|
||||
return true;
|
||||
|
@ -340,10 +288,6 @@ public UUID getUUID() {
|
|||
return uuid;
|
||||
}
|
||||
|
||||
public void setUUID(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public boolean hasFlag(CompatibilityFlags flag) {
|
||||
return flags.contains(flag);
|
||||
}
|
||||
|
@ -413,18 +357,6 @@ public String getProperty(String name) {
|
|||
return properties.get(name);
|
||||
}
|
||||
|
||||
public void putProperty(String name, String value) {
|
||||
properties.put(name, value);
|
||||
}
|
||||
|
||||
public boolean containsProperty(String name) {
|
||||
return properties.containsKey(name);
|
||||
}
|
||||
|
||||
public void clearProperties() {
|
||||
properties.clear();
|
||||
}
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
return Collections.unmodifiableMap(properties);
|
||||
}
|
||||
|
@ -450,10 +382,6 @@ public ClassLoaderConfig getClassLoaderConfig() {
|
|||
return classLoaderConfig;
|
||||
}
|
||||
|
||||
public void setClassLoaderConfig(ClassLoaderConfig classLoaderConfig) {
|
||||
this.classLoaderConfig = classLoaderConfig;
|
||||
}
|
||||
|
||||
public boolean isLimited() {
|
||||
return limited;
|
||||
}
|
||||
|
@ -462,16 +390,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,136 +1,118 @@
|
|||
package pro.gravit.launcher.base.profiles;
|
||||
|
||||
import pro.gravit.launcher.base.profiles.optional.OptionalFile;
|
||||
import pro.gravit.utils.launch.LaunchOptions;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ClientProfileBuilder {
|
||||
private List<String> update = new ArrayList<>();
|
||||
private List<String> updateExclusions = new ArrayList<>();
|
||||
private List<String> updateShared = new ArrayList<>();
|
||||
private List<String> updateVerify = new ArrayList<>();
|
||||
private Set<OptionalFile> updateOptional = new HashSet<>();
|
||||
private List<String> jvmArgs = new ArrayList<>();
|
||||
private List<String> classPath = new ArrayList<>();
|
||||
private List<String> modulePath = new ArrayList<>();
|
||||
private List<String> modules = new ArrayList<>();
|
||||
private List<String> altClassPath = new ArrayList<>();
|
||||
private List<String> clientArgs = new ArrayList<>();
|
||||
private List<String> compatClasses = new ArrayList<>();
|
||||
private Map<String, String> properties = new HashMap<>();
|
||||
private List<ClientProfile.ServerProfile> servers = new ArrayList<>();
|
||||
private ClientProfile.ClassLoaderConfig classLoaderConfig = ClientProfile.ClassLoaderConfig.LAUNCHER;
|
||||
private List<ClientProfile.CompatibilityFlags> flags = new ArrayList<>();
|
||||
private ClientProfile.Version version;
|
||||
private String assetIndex;
|
||||
private String dir;
|
||||
private String assetDir;
|
||||
private int recommendJavaVersion = 8;
|
||||
private int minJavaVersion = 8;
|
||||
private int maxJavaVersion = 999;
|
||||
private ClientProfile.ProfileDefaultSettings settings = new ClientProfile.ProfileDefaultSettings();
|
||||
private int sortIndex;
|
||||
private UUID uuid;
|
||||
private String title;
|
||||
private UUID uuid;
|
||||
private ClientProfile.Version version;
|
||||
private String info;
|
||||
private String dir;
|
||||
private int sortIndex;
|
||||
private String assetIndex;
|
||||
private String assetDir;
|
||||
private List<String> update;
|
||||
private List<String> updateExclusions;
|
||||
private List<String> updateVerify;
|
||||
private Set<OptionalFile> updateOptional;
|
||||
private List<String> jvmArgs;
|
||||
private List<String> classPath;
|
||||
private List<String> altClassPath;
|
||||
private List<String> clientArgs;
|
||||
private List<String> compatClasses;
|
||||
private List<String> loadNatives;
|
||||
private Map<String, String> properties;
|
||||
private List<ClientProfile.ServerProfile> servers;
|
||||
private ClientProfile.ClassLoaderConfig classLoaderConfig;
|
||||
private List<ClientProfile.CompatibilityFlags> flags;
|
||||
private int recommendJavaVersion;
|
||||
private int minJavaVersion;
|
||||
private int maxJavaVersion;
|
||||
private ClientProfile.ProfileDefaultSettings settings;
|
||||
private boolean limited;
|
||||
private String mainClass;
|
||||
private String mainModule;
|
||||
private LaunchOptions.ModuleConf moduleConf;
|
||||
|
||||
public void setUpdate(List<String> update) {
|
||||
this.update = update;
|
||||
public ClientProfileBuilder() {
|
||||
this.update = new ArrayList<>();
|
||||
this.updateExclusions = new ArrayList<>();
|
||||
this.updateVerify = new ArrayList<>();
|
||||
this.updateOptional = new HashSet<>();
|
||||
this.jvmArgs = new ArrayList<>();
|
||||
this.classPath = new ArrayList<>();
|
||||
this.altClassPath = new ArrayList<>();
|
||||
this.clientArgs = new ArrayList<>();
|
||||
this.compatClasses = new ArrayList<>();
|
||||
this.loadNatives = new ArrayList<>();
|
||||
this.properties = new HashMap<>();
|
||||
this.servers = new ArrayList<>();
|
||||
this.flags = new ArrayList<>();
|
||||
this.settings = new ClientProfile.ProfileDefaultSettings();
|
||||
this.recommendJavaVersion = 21;
|
||||
this.minJavaVersion = 17;
|
||||
this.maxJavaVersion = 999;
|
||||
this.classLoaderConfig = ClientProfile.ClassLoaderConfig.LAUNCHER;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setUpdateExclusions(List<String> updateExclusions) {
|
||||
this.updateExclusions = updateExclusions;
|
||||
public ClientProfileBuilder(ClientProfile profile) {
|
||||
this.title = profile.getTitle();
|
||||
this.uuid = profile.getUUID();
|
||||
this.version = profile.getVersion();
|
||||
this.info = profile.getInfo();
|
||||
this.dir = profile.getDir();
|
||||
this.sortIndex = profile.getSortIndex();
|
||||
this.assetIndex = profile.getAssetIndex();
|
||||
this.assetDir = profile.getAssetDir();
|
||||
this.update = new ArrayList<>(profile.getUpdate());
|
||||
this.updateExclusions = new ArrayList<>(profile.getUpdateExclusions());
|
||||
this.updateVerify = new ArrayList<>(profile.getUpdateVerify());
|
||||
this.updateOptional = new HashSet<>(profile.getOptional());
|
||||
this.jvmArgs = new ArrayList<>(profile.getJvmArgs());
|
||||
this.classPath = new ArrayList<>(profile.getClassPath());
|
||||
this.altClassPath = new ArrayList<>(profile.getAlternativeClassPath());
|
||||
this.clientArgs = new ArrayList<>(profile.getClientArgs());
|
||||
this.compatClasses = new ArrayList<>(profile.getCompatClasses());
|
||||
this.loadNatives = new ArrayList<>(profile.getLoadNatives());
|
||||
this.properties = new HashMap<>(profile.getProperties());
|
||||
this.servers = new ArrayList<>(profile.getServers());
|
||||
this.classLoaderConfig = profile.getClassLoaderConfig();
|
||||
this.flags = new ArrayList<>(profile.getFlags());
|
||||
this.recommendJavaVersion = profile.getRecommendJavaVersion();
|
||||
this.minJavaVersion = profile.getMinJavaVersion();
|
||||
this.maxJavaVersion = profile.getMaxJavaVersion();
|
||||
this.settings = profile.getSettings();
|
||||
this.limited = profile.isLimited();
|
||||
this.mainClass = profile.getMainClass();
|
||||
this.mainModule = profile.getMainModule();
|
||||
this.moduleConf = profile.getModuleConf();
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setTitle(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setUpdateShared(List<String> updateShared) {
|
||||
this.updateShared = updateShared;
|
||||
public ClientProfileBuilder setUuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setUpdateVerify(List<String> updateVerify) {
|
||||
this.updateVerify = updateVerify;
|
||||
}
|
||||
|
||||
public void setUpdateOptional(Set<OptionalFile> updateOptional) {
|
||||
this.updateOptional = updateOptional;
|
||||
}
|
||||
|
||||
public void setJvmArgs(List<String> jvmArgs) {
|
||||
this.jvmArgs = jvmArgs;
|
||||
}
|
||||
|
||||
public void setClassPath(List<String> classPath) {
|
||||
this.classPath = classPath;
|
||||
}
|
||||
|
||||
public void setAltClassPath(List<String> altClassPath) {
|
||||
this.altClassPath = altClassPath;
|
||||
}
|
||||
|
||||
public void setClientArgs(List<String> clientArgs) {
|
||||
this.clientArgs = clientArgs;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setCompatClasses(List<String> compatClasses) {
|
||||
this.compatClasses = compatClasses;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setProperties(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setServers(List<ClientProfile.ServerProfile> servers) {
|
||||
this.servers = servers;
|
||||
}
|
||||
|
||||
public void setClassLoaderConfig(ClientProfile.ClassLoaderConfig classLoaderConfig) {
|
||||
this.classLoaderConfig = classLoaderConfig;
|
||||
}
|
||||
|
||||
public void setVersion(ClientProfile.Version version) {
|
||||
public ClientProfileBuilder setVersion(ClientProfile.Version version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setAssetIndex(String assetIndex) {
|
||||
this.assetIndex = assetIndex;
|
||||
public ClientProfileBuilder setInfo(String info) {
|
||||
this.info = info;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setDir(String dir) {
|
||||
public ClientProfileBuilder setDir(String dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
public void setAssetDir(String assetDir) {
|
||||
this.assetDir = assetDir;
|
||||
}
|
||||
|
||||
public void setRecommendJavaVersion(int recommendJavaVersion) {
|
||||
this.recommendJavaVersion = recommendJavaVersion;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setModulePath(List<String> modulePath) {
|
||||
this.modulePath = modulePath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setModules(List<String> modules) {
|
||||
this.modules = modules;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setMinJavaVersion(int minJavaVersion) {
|
||||
this.minJavaVersion = minJavaVersion;
|
||||
}
|
||||
|
||||
public void setMaxJavaVersion(int maxJavaVersion) {
|
||||
this.maxJavaVersion = maxJavaVersion;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setSettings(ClientProfile.ProfileDefaultSettings settings) {
|
||||
this.settings = settings;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -139,20 +121,140 @@ public ClientProfileBuilder setSortIndex(int sortIndex) {
|
|||
return this;
|
||||
}
|
||||
|
||||
public void setUuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
public ClientProfileBuilder setAssetIndex(String assetIndex) {
|
||||
this.assetIndex = assetIndex;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
public ClientProfileBuilder setAssetDir(String assetDir) {
|
||||
this.assetDir = assetDir;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setInfo(String info) {
|
||||
this.info = info;
|
||||
public ClientProfileBuilder setUpdate(List<String> update) {
|
||||
this.update = update;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setMainClass(String mainClass) {
|
||||
this.mainClass = mainClass;
|
||||
public ClientProfileBuilder update(String value) {
|
||||
this.update.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setUpdateExclusions(List<String> updateExclusions) {
|
||||
this.updateExclusions = updateExclusions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder updateExclusions(String value) {
|
||||
this.updateExclusions.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setUpdateVerify(List<String> updateVerify) {
|
||||
this.updateVerify = updateVerify;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder updateVerify(String value) {
|
||||
this.updateVerify.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setUpdateOptional(Set<OptionalFile> updateOptional) {
|
||||
this.updateOptional = updateOptional;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder updateOptional(OptionalFile value) {
|
||||
this.updateOptional.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setJvmArgs(List<String> jvmArgs) {
|
||||
this.jvmArgs = jvmArgs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder jvmArg(String value) {
|
||||
this.jvmArgs.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ClientProfileBuilder setClassPath(List<String> classPath) {
|
||||
this.classPath = classPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder classPath(String value) {
|
||||
this.classPath.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setAltClassPath(List<String> altClassPath) {
|
||||
this.altClassPath = altClassPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder altClassPath(String value) {
|
||||
this.altClassPath.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setClientArgs(List<String> clientArgs) {
|
||||
this.clientArgs = clientArgs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder clientArg(String value) {
|
||||
this.clientArgs.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setCompatClasses(List<String> compatClasses) {
|
||||
this.compatClasses = compatClasses;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder compatClass(String value) {
|
||||
this.compatClasses.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setLoadNatives(List<String> loadNatives) {
|
||||
this.loadNatives = loadNatives;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder loadNatives(String value) {
|
||||
this.loadNatives.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setProperties(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder property(String name, String value) {
|
||||
this.properties.put(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setServers(List<ClientProfile.ServerProfile> servers) {
|
||||
this.servers = servers;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder server(ClientProfile.ServerProfile value) {
|
||||
this.servers.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setClassLoaderConfig(ClientProfile.ClassLoaderConfig classLoaderConfig) {
|
||||
this.classLoaderConfig = classLoaderConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setFlags(List<ClientProfile.CompatibilityFlags> flags) {
|
||||
|
@ -160,7 +262,52 @@ public ClientProfileBuilder setFlags(List<ClientProfile.CompatibilityFlags> flag
|
|||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder flag(ClientProfile.CompatibilityFlags value) {
|
||||
this.flags.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setRecommendJavaVersion(int recommendJavaVersion) {
|
||||
this.recommendJavaVersion = recommendJavaVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setMinJavaVersion(int minJavaVersion) {
|
||||
this.minJavaVersion = minJavaVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setMaxJavaVersion(int maxJavaVersion) {
|
||||
this.maxJavaVersion = maxJavaVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setSettings(ClientProfile.ProfileDefaultSettings settings) {
|
||||
this.settings = settings;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setLimited(boolean limited) {
|
||||
this.limited = limited;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setMainClass(String mainClass) {
|
||||
this.mainClass = mainClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setMainModule(String mainModule) {
|
||||
this.mainModule = mainModule;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setModuleConf(LaunchOptions.ModuleConf moduleConf) {
|
||||
this.moduleConf = moduleConf;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfile createClientProfile() {
|
||||
return new ClientProfile(update, updateExclusions, updateShared, updateVerify, updateOptional, jvmArgs, classPath, modulePath, modules, altClassPath, clientArgs, compatClasses, properties, servers, classLoaderConfig, flags, version, assetIndex, dir, assetDir, recommendJavaVersion, minJavaVersion, maxJavaVersion, settings, sortIndex, uuid, title, info, mainClass);
|
||||
return new ClientProfile(title, uuid, version, info, dir, sortIndex, assetIndex, assetDir, update, updateExclusions, updateVerify, updateOptional, jvmArgs, classPath, altClassPath, clientArgs, compatClasses, loadNatives, properties, servers, classLoaderConfig, flags, recommendJavaVersion, minJavaVersion, maxJavaVersion, settings, limited, mainClass, mainModule, moduleConf);
|
||||
}
|
||||
}
|
|
@ -18,4 +18,5 @@ private ClientProfileVersions() {
|
|||
public static final ClientProfile.Version MINECRAFT_1_20 = ClientProfile.Version.of("1.20");
|
||||
public static final ClientProfile.Version MINECRAFT_1_20_2 = ClientProfile.Version.of("1.20.2");
|
||||
public static final ClientProfile.Version MINECRAFT_1_20_3 = ClientProfile.Version.of("1.20.3");
|
||||
public static final ClientProfile.Version MINECRAFT_1_20_5 = ClientProfile.Version.of("1.20.5");
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ public Set<OptionalAction> getEnabledActions() {
|
|||
public void fixDependencies() {
|
||||
Set<OptionalFile> disabled = all.stream().filter(t -> !isEnabled(t)).collect(Collectors.toSet());
|
||||
for (OptionalFile file : disabled) {
|
||||
if (file.group != null && Arrays.stream(file.group).noneMatch(this::isEnabled)) {
|
||||
if (file.group != null && file.group.length > 0 && Arrays.stream(file.group).noneMatch(this::isEnabled)) {
|
||||
enable(file.group[0], false, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
package pro.gravit.launcher.base.profiles.optional.actions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OptionalActionClassPath extends OptionalAction {
|
||||
public String[] args;
|
||||
public List<String> args;
|
||||
public boolean useAltClasspath = false;
|
||||
|
||||
public OptionalActionClassPath() {
|
||||
}
|
||||
|
||||
public OptionalActionClassPath(String[] args) {
|
||||
public OptionalActionClassPath(List<String> args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public OptionalActionClassPath(String[] args, boolean useAltClasspath) {
|
||||
public OptionalActionClassPath(List<String> args, boolean useAltClasspath) {
|
||||
this.args = args;
|
||||
this.useAltClasspath = useAltClasspath;
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ public static String getRefreshToken() {
|
|||
|
||||
public static void reconnect() throws Exception {
|
||||
|
||||
getRequestService().open();
|
||||
getRequestService().connect();
|
||||
restore();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import java.io.IOException;
|
||||
|
||||
public final class RequestException extends IOException {
|
||||
private static final long serialVersionUID = 7558237657082664821L;
|
||||
|
||||
|
||||
public RequestException(String message) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
public interface RequestService {
|
||||
<T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request) throws IOException;
|
||||
void open() throws Exception;
|
||||
void connect() throws Exception;
|
||||
|
||||
void registerEventHandler(EventHandler handler);
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.WebSocket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
@ -33,7 +34,7 @@ public abstract class ClientJSONPoint implements WebSocket.Listener {
|
|||
private final Object sendSyncObject = new Object();
|
||||
private volatile StringBuilder builder = new StringBuilder();
|
||||
|
||||
public ClientJSONPoint(final String uri) throws SSLException {
|
||||
public ClientJSONPoint(final String uri) {
|
||||
this(URI.create(uri));
|
||||
}
|
||||
|
||||
|
@ -63,7 +64,7 @@ public ClientJSONPoint(URI uri) {
|
|||
}
|
||||
}
|
||||
|
||||
public void open() throws Exception {
|
||||
public void connect() throws Exception {
|
||||
webSocket = webSocketBuilder.buildAsync(uri, this).get();
|
||||
}
|
||||
|
||||
|
@ -97,6 +98,17 @@ public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String re
|
|||
return WebSocket.Listener.super.onClose(webSocket, statusCode, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket) {
|
||||
onOpen();
|
||||
WebSocket.Listener.super.onOpen(webSocket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer data, boolean last) {
|
||||
return WebSocket.Listener.super.onBinary(webSocket, data, last);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(WebSocket webSocket, Throwable error) {
|
||||
LogHelper.error(error);
|
||||
|
@ -114,7 +126,7 @@ public void send(String text) {
|
|||
|
||||
abstract void onOpen();
|
||||
|
||||
public void close() throws InterruptedException {
|
||||
public void close() {
|
||||
webSocket.abort();
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ public abstract class ClientWebSocketService extends ClientJSONPoint {
|
|||
public OnCloseCallback onCloseCallback;
|
||||
public ReconnectCallback reconnectCallback;
|
||||
|
||||
public ClientWebSocketService(String address) throws SSLException {
|
||||
public ClientWebSocketService(String address) {
|
||||
super(createURL(address));
|
||||
this.gson = Launcher.gsonManager.gson;
|
||||
this.onConnect = true;
|
||||
|
|
|
@ -45,7 +45,7 @@ public <T extends WebSocketEvent> CompletableFuture<T> request(Request<T> reques
|
|||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
public void connect() {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.io.IOException;
|
||||
import java.net.http.WebSocket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
|
@ -40,11 +43,7 @@ public static CompletableFuture<StdWebSocketService> initWebSockets(String addre
|
|||
service.openAsync(() -> {
|
||||
future.complete(service);
|
||||
JVMHelper.RUNTIME.addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
service.close();
|
||||
} catch (InterruptedException e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
}));
|
||||
}, future::completeExceptionally);
|
||||
return future;
|
||||
|
|
|
@ -26,7 +26,7 @@ public <T extends WebSocketEvent> CompletableFuture<T> request(Request<T> reques
|
|||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
public void connect() {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
public class ClientVersionTest {
|
||||
@Test
|
||||
public void parseTest() {
|
||||
Assertions.assertEquals(ClientProfile.Version.of("1.0.0").toCleanString(), "1.0.0");
|
||||
Assertions.assertEquals(ClientProfile.Version.of("1.0.0-1").toCleanString(), "1.0.0.1");
|
||||
Assertions.assertEquals(ClientProfile.Version.of("-----1.0.0").toCleanString(), "1.0.0");
|
||||
Assertions.assertEquals("1.0.0", ClientProfile.Version.of("1.0.0").toCleanString());
|
||||
Assertions.assertEquals("1.0.0.1", ClientProfile.Version.of("1.0.0-1").toCleanString());
|
||||
Assertions.assertEquals("1.0.0", ClientProfile.Version.of("-----1.0.0").toCleanString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -49,12 +49,12 @@ public void dependenciesTest() {
|
|||
moduleManager.loadModule(new Depend3Module());
|
||||
moduleManager.loadModule(new MainModule());
|
||||
moduleManager.initModules(null);
|
||||
Assertions.assertEquals(moduleManager.getModule("depend1").getInitStatus(), LauncherModule.InitStatus.FINISH);
|
||||
Assertions.assertEquals(moduleManager.getModule("depend2").getInitStatus(), LauncherModule.InitStatus.FINISH);
|
||||
Assertions.assertEquals(moduleManager.getModule("depend3").getInitStatus(), LauncherModule.InitStatus.FINISH);
|
||||
Assertions.assertEquals(moduleManager.getModule("internal").getInitStatus(), LauncherModule.InitStatus.FINISH);
|
||||
Assertions.assertEquals(moduleManager.getModule("virtual").getInitStatus(), LauncherModule.InitStatus.FINISH);
|
||||
Assertions.assertEquals(moduleManager.getModule("main").getInitStatus(), LauncherModule.InitStatus.FINISH);
|
||||
Assertions.assertEquals(LauncherModule.InitStatus.FINISH, moduleManager.getModule("depend1").getInitStatus());
|
||||
Assertions.assertEquals(LauncherModule.InitStatus.FINISH, moduleManager.getModule("depend2").getInitStatus());
|
||||
Assertions.assertEquals(LauncherModule.InitStatus.FINISH, moduleManager.getModule("depend3").getInitStatus());
|
||||
Assertions.assertEquals(LauncherModule.InitStatus.FINISH, moduleManager.getModule("internal").getInitStatus());
|
||||
Assertions.assertEquals(LauncherModule.InitStatus.FINISH, moduleManager.getModule("virtual").getInitStatus());
|
||||
Assertions.assertEquals(LauncherModule.InitStatus.FINISH, moduleManager.getModule("main").getInitStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -62,7 +62,7 @@ public void cyclicTest() {
|
|||
moduleManager.loadModule(new CyclicDependModule());
|
||||
moduleManager.loadModule(new Cyclic2DependModule());
|
||||
moduleManager.initModules(null);
|
||||
Assertions.assertEquals(moduleManager.getModule("cyclic1").getInitStatus(), LauncherModule.InitStatus.FINISH);
|
||||
Assertions.assertEquals(moduleManager.getModule("cyclic2").getInitStatus(), LauncherModule.InitStatus.FINISH);
|
||||
Assertions.assertEquals(LauncherModule.InitStatus.FINISH, moduleManager.getModule("cyclic1").getInitStatus());
|
||||
Assertions.assertEquals(LauncherModule.InitStatus.FINISH, moduleManager.getModule("cyclic2").getInitStatus());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@ public Depend1Module() {
|
|||
@Override
|
||||
public void init(LauncherInitContext initContext) {
|
||||
InternalModule module = modulesManager.getModule(InternalModule.class);
|
||||
Assertions.assertEquals(module.getInitStatus(), InitStatus.FINISH);
|
||||
Assertions.assertEquals(InitStatus.FINISH, module.getInitStatus());
|
||||
Depend3Module module1 = modulesManager.getModule(Depend3Module.class);
|
||||
Assertions.assertEquals(module1.getInitStatus(), InitStatus.FINISH);
|
||||
Assertions.assertEquals(InitStatus.FINISH, module1.getInitStatus());
|
||||
VirtualInterface virtualInterface = modulesManager.getModuleByInterface(VirtualInterface.class);
|
||||
Assertions.assertEquals(((LauncherModule) virtualInterface).getInitStatus(), InitStatus.FINISH);
|
||||
Assertions.assertEquals(InitStatus.FINISH, ((LauncherModule) virtualInterface).getInitStatus());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ public MainModule() {
|
|||
@Override
|
||||
public void init(LauncherInitContext initContext) {
|
||||
Depend1Module module = modulesManager.getModule(Depend1Module.class);
|
||||
Assertions.assertEquals(module.getInitStatus(), InitStatus.FINISH);
|
||||
Assertions.assertEquals(InitStatus.FINISH, module.getInitStatus());
|
||||
Depend2Module module2 = modulesManager.getModule(Depend2Module.class);
|
||||
Assertions.assertEquals(module2.getInitStatus(), InitStatus.FINISH);
|
||||
Assertions.assertEquals(InitStatus.FINISH, module2.getInitStatus());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -120,14 +118,13 @@ private static void realMain(String[] args) throws Throwable {
|
|||
LogHelper.debug("Verifying ClientLauncher sign and classpath");
|
||||
Set<Path> ignoredPath = new HashSet<>();
|
||||
List<Path> classpath = resolveClassPath(ignoredPath, clientDir, params.actions, params.profile)
|
||||
.filter(x -> !profile.getModulePath().contains(clientDir.relativize(x).toString()))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
if(LogHelper.isDevEnabled()) {
|
||||
for(var e : classpath) {
|
||||
LogHelper.dev("Classpath entry %s", e);
|
||||
}
|
||||
}
|
||||
List<URL> classpathURLs = classpath.stream().map(IOHelper::toURL).collect(Collectors.toList());
|
||||
List<URL> classpathURLs = classpath.stream().map(IOHelper::toURL).toList();
|
||||
// Start client with WatchService monitoring
|
||||
RequestService service;
|
||||
if (params.offlineMode) {
|
||||
|
@ -161,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 {
|
||||
|
@ -171,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;
|
||||
|
@ -253,11 +242,11 @@ public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher,
|
|||
}
|
||||
}
|
||||
|
||||
private static LinkedList<Path> resolveClassPathList(Set<Path> ignorePaths, Path clientDir, String... classPath) throws IOException {
|
||||
private static LinkedList<Path> resolveClassPathList(Set<Path> ignorePaths, Path clientDir, List<String> classPath) throws IOException {
|
||||
return resolveClassPathStream(ignorePaths, clientDir, classPath).collect(Collectors.toCollection(LinkedList::new));
|
||||
}
|
||||
|
||||
private static Stream<Path> resolveClassPathStream(Set<Path> ignorePaths, Path clientDir, String... classPath) throws IOException {
|
||||
private static Stream<Path> resolveClassPathStream(Set<Path> ignorePaths, Path clientDir, List<String> classPath) throws IOException {
|
||||
Stream.Builder<Path> builder = Stream.builder();
|
||||
for (String classPathEntry : classPath) {
|
||||
Path path = clientDir.resolve(IOHelper.toPath(classPathEntry.replace(IOHelper.CROSS_SEPARATOR, IOHelper.PLATFORM_SEPARATOR)));
|
||||
|
@ -301,7 +290,7 @@ private static void launch(ClientProfile profile, ClientParams params) throws Th
|
|||
params.addClientLegacyArgs(args);
|
||||
System.setProperty("minecraft.applet.TargetDirectory", params.clientDir);
|
||||
}
|
||||
Collections.addAll(args, profile.getClientArgs());
|
||||
args.addAll(profile.getClientArgs());
|
||||
for (OptionalAction action : params.actions) {
|
||||
if (action instanceof OptionalActionClientArgs) {
|
||||
args.addAll(((OptionalActionClientArgs) action).args);
|
||||
|
|
|
@ -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,6 @@
|
|||
compileOnly group: 'org.jline', name: 'jline', version: rootProject['verJline']
|
||||
compileOnly group: 'org.jline', name: 'jline-reader', version: rootProject['verJline']
|
||||
compileOnly group: 'org.jline', name: 'jline-terminal', version: rootProject['verJline']
|
||||
compileOnly group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: rootProject['verBcprov']
|
||||
compileOnly group: 'org.slf4j', name: 'slf4j-api', version: rootProject['verSlf4j']
|
||||
api group: 'com.google.code.gson', name: 'gson', version: rootProject['verGson']
|
||||
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: rootProject['verJunit']
|
||||
|
|
|
@ -282,7 +282,7 @@ public enum WalkAction {
|
|||
|
||||
@FunctionalInterface
|
||||
public interface WalkCallback {
|
||||
WalkAction walked(String path, String name, HashedEntry entry) throws IOException;
|
||||
WalkAction walked(String path, String name, HashedEntry entry);
|
||||
}
|
||||
|
||||
public static class FindRecursiveResult {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package pro.gravit.utils;
|
||||
|
||||
public class HookException extends RuntimeException {
|
||||
private static final long serialVersionUID = -529141998961943161L;
|
||||
|
||||
public HookException(String message) {
|
||||
super(message);
|
||||
|
|
|
@ -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 = 1;
|
||||
public static final int PATCH = 11;
|
||||
public static final int BUILD = 1;
|
||||
public static final Version.Type RELEASE = Type.STABLE;
|
||||
public final int major;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package pro.gravit.utils.command;
|
||||
|
||||
public final class CommandException extends Exception {
|
||||
private static final long serialVersionUID = -6588814993972117772L;
|
||||
|
||||
|
||||
public CommandException(String message) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -221,38 +221,40 @@ public static ArgsParseResult parseJavaArgs(List<String> args) {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
if(arg.equals("--module-path") || arg.equals("-p")) {
|
||||
switch (arg) {
|
||||
case "--module-path", "-p" -> {
|
||||
prevArgType = PrevArgType.MODULE_PATH;
|
||||
continue;
|
||||
}
|
||||
if(arg.equals("--classpath") || arg.equals("-cp")) {
|
||||
case "--classpath", "-cp" -> {
|
||||
prevArgType = PrevArgType.CLASSPATH;
|
||||
continue;
|
||||
}
|
||||
if(arg.equals("--add-modules")) {
|
||||
case "--add-modules" -> {
|
||||
prevArgType = PrevArgType.ADD_MODULES;
|
||||
continue;
|
||||
}
|
||||
if(arg.equals("--add-opens")) {
|
||||
case "--add-opens" -> {
|
||||
prevArgType = PrevArgType.ADD_OPENS;
|
||||
continue;
|
||||
}
|
||||
if(arg.equals("--add-exports")) {
|
||||
case "--add-exports" -> {
|
||||
prevArgType = PrevArgType.ADD_EXPORTS;
|
||||
continue;
|
||||
}
|
||||
if(arg.equals("--add-reads")) {
|
||||
case "--add-reads" -> {
|
||||
prevArgType = PrevArgType.ADD_READS;
|
||||
continue;
|
||||
}
|
||||
if(arg.equals("--module") || arg.equals("-m")) {
|
||||
case "--module", "-m" -> {
|
||||
prevArgType = PrevArgType.MODULE;
|
||||
continue;
|
||||
}
|
||||
if(arg.equals("-jar")) {
|
||||
case "-jar" -> {
|
||||
prevArgType = PrevArgType.JAR;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
jvmArgs.add(arg);
|
||||
}
|
||||
return new ArgsParseResult(conf, classpath, jvmArgs, mainClass, mainModule, jarFile, args);
|
||||
|
|
|
@ -83,7 +83,7 @@ public static byte[] getClassBytes(Class<?> clazz, ClassLoader classLoader) thro
|
|||
return IOHelper.read(classLoader.getResourceAsStream(getClassFile(clazz)));
|
||||
}
|
||||
|
||||
public static InputStream getClassBytesStream(Class<?> clazz) throws IOException {
|
||||
public static InputStream getClassBytesStream(Class<?> clazz) {
|
||||
return getClassBytesStream(clazz, clazz.getClassLoader());
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ public void addTransformer(ClassTransformer transformer) {
|
|||
}
|
||||
instrumentation.addTransformer(new ClassFileTransformer() {
|
||||
@Override
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
|
||||
if(transformer.filter(null, className)) {
|
||||
return transformer.transform(null, className, protectionDomain, classfileBuffer);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,8 +30,8 @@ public void main() {
|
|||
String json2 = gson.toJson(new MyTestClass2("BBBB"), TestInterface.class);
|
||||
TestInterface test1 = gson.fromJson(json, TestInterface.class);
|
||||
TestInterface test2 = gson.fromJson(json2, TestInterface.class);
|
||||
Assertions.assertEquals(test1.get(), "AAAA");
|
||||
Assertions.assertEquals(test2.get(), "BBBB");
|
||||
Assertions.assertEquals("AAAA", test1.get());
|
||||
Assertions.assertEquals("BBBB", test2.get());
|
||||
}
|
||||
|
||||
public interface TestInterface {
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ServerWrapper extends JsonConfigurable<ServerWrapper.Config> {
|
||||
|
@ -121,6 +122,7 @@ public void run(String... args) throws Throwable {
|
|||
}
|
||||
LogHelper.debug("Read ServerWrapperConfig.json");
|
||||
loadConfig();
|
||||
config.applyEnv();
|
||||
updateLauncherConfig();
|
||||
Launcher.applyLauncherEnv(Objects.requireNonNullElse(config.env, LauncherConfig.LauncherEnvironment.STD));
|
||||
StdWebSocketService service = StdWebSocketService.initWebSockets(config.address).get();
|
||||
|
@ -304,5 +306,38 @@ public void apply() {
|
|||
ConfigService.checkServerConfig.needProperties = checkServerNeedProperties;
|
||||
}
|
||||
}
|
||||
|
||||
public void applyEnv() {
|
||||
this.authId = applyEnvOrDefault("SERVERWRAPPER_AUTH_ID", this.authId);
|
||||
this.address = applyEnvOrDefault("SERVERWRAPPER_ADDRESS", this.address);
|
||||
this.serverName = applyEnvOrDefault("SERVERWRAPPER_SERVER_NAME", this.serverName);
|
||||
this.encodedServerEcPublicKey = applyEnvOrDefault("SERVERWRAPPER_EC_PUBLIC_KEY", Base64.getUrlDecoder()::decode, null);
|
||||
this.encodedServerRsaPublicKey = applyEnvOrDefault("SERVERWRAPPER_RSA_PUBLIC_KEY", Base64.getUrlDecoder()::decode, null);
|
||||
{
|
||||
String token = System.getenv("SERVERWRAPPER_CHECK_SERVER_TOKEN");
|
||||
if(token != null) {
|
||||
if(extendedTokens == null) {
|
||||
extendedTokens = new HashMap<>();
|
||||
}
|
||||
extendedTokens.put("checkServer", new Request.ExtendedToken(token, 0L));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String applyEnvOrDefault(String envName, String def) {
|
||||
String value = System.getenv(envName);
|
||||
if(value == null) {
|
||||
return def;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static<T> T applyEnvOrDefault(String envName, Function<String, T> mappingFunction, T def) {
|
||||
String value = System.getenv(envName);
|
||||
if(value == null) {
|
||||
return def;
|
||||
}
|
||||
return mappingFunction.apply(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,11 @@ public ServerWrapperSetup() throws IOException {
|
|||
|
||||
public void run() throws Exception {
|
||||
ServerWrapper wrapper = ServerWrapper.wrapper;
|
||||
String jarName = System.getenv("SERVERWRAPPER_JAR_NAME");
|
||||
if(jarName == null) {
|
||||
System.out.println("Print server jar filename:");
|
||||
String jarName = commands.commandHandler.readLine();
|
||||
jarName = commands.commandHandler.readLine();
|
||||
}
|
||||
Path jarPath = Paths.get(jarName);
|
||||
String mainClassName;
|
||||
String agentClassName;
|
||||
|
@ -56,14 +59,18 @@ public void run() throws Exception {
|
|||
if (agentClassName != null) {
|
||||
LogHelper.info("Found PremainClass %s", agentClassName);
|
||||
}
|
||||
if(wrapper.config.serverName == null || wrapper.config.serverName.isEmpty()) {
|
||||
System.out.println("Print your server name:");
|
||||
wrapper.config.serverName = commands.commandHandler.readLine();
|
||||
}
|
||||
wrapper.config.mainclass = mainClassName;
|
||||
boolean altMode = false;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if(!Request.isAvailable() || Request.getRequestService().isClosed()) {
|
||||
if(wrapper.config.address == null || wrapper.config.address.isEmpty()) {
|
||||
System.out.println("Print launchserver websocket host( ws://host:port/api ):");
|
||||
wrapper.config.address = commands.commandHandler.readLine();
|
||||
}
|
||||
StdWebSocketService service;
|
||||
try {
|
||||
service = StdWebSocketService.initWebSockets(wrapper.config.address).get();
|
||||
|
@ -73,9 +80,11 @@ public void run() throws Exception {
|
|||
}
|
||||
Request.setRequestService(service);
|
||||
}
|
||||
if(wrapper.config.extendedTokens == null || wrapper.config.extendedTokens.get("checkServer") == null) {
|
||||
System.out.println("Print server token:");
|
||||
String checkServerToken = commands.commandHandler.readLine();
|
||||
wrapper.config.extendedTokens.put("checkServer", new Request.ExtendedToken(checkServerToken, 0));
|
||||
}
|
||||
wrapper.updateLauncherConfig();
|
||||
try {
|
||||
wrapper.restore();
|
||||
|
@ -93,7 +102,9 @@ public void run() throws Exception {
|
|||
}
|
||||
if(wrapper.profile != null && wrapper.profile.getVersion().compareTo(ClientProfileVersions.MINECRAFT_1_18) >= 0) {
|
||||
LogHelper.info("Switch to alternative start mode (1.18)");
|
||||
if(!wrapper.config.classpath.contains(jarName)) {
|
||||
wrapper.config.classpath.add(jarName);
|
||||
}
|
||||
wrapper.config.classLoaderConfig = ClientProfile.ClassLoaderConfig.LAUNCHER;
|
||||
altMode = true;
|
||||
}
|
||||
|
|
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.1'
|
||||
version = '5.6.11'
|
||||
|
||||
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.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue