Compare commits

...

41 commits

Author SHA1 Message Date
Gravita
f297a5878d
Merge branch 'dev' 2024-07-25 22:42:35 +07:00
Gravita
af2dcec8cd
[FIX] ProfileProvider support overwrite profile 2024-07-25 22:28:26 +07:00
Gravita
9bffe07d36
[FEATURE] ProfileProvider 2024-07-25 22:20:48 +07:00
Gravita
4be299f6ca
[FIX] Add ClassLoader.registerAsParallelCapable() 2024-07-25 16:43:00 +07:00
Gravita
ef4f14f9b4
[FIX] Add netty epoll lib for aarch64 (ARM) 2024-07-25 16:33:49 +07:00
Gravita
d720328bc4
[FEATURE] moduleConf in SYSTEM_ARGS 2024-07-25 15:52:39 +07:00
Gravita
88f1eaf750
[ANY] Update modules 2024-07-21 20:39:30 +07:00
Gravita
a5ef86b105
Merge tag 'v5.6.5' into dev
5.6.5-stable
2024-07-21 20:18:08 +07:00
Gravita
b1a5ecdc13
Merge branch 'release/5.6.5' 2024-07-21 20:18:00 +07:00
Gravita
68e9affbe0
[ANY] 5.6.5-stable 2024-07-21 20:17:51 +07:00
Gravita
7d7485afdc
[FIX] Downloader downloadList truncate file 2024-07-21 19:48:55 +07:00
Gravita
c2926b5b40
[ANY] Update modules 2024-07-20 19:20:12 +07:00
Gravita
9c82d76781
[FEATURE][REFACTOR] Refactoring GenerateCertificateModule 2024-07-20 19:19:41 +07:00
Gravita
450774de7e
Merge tag 'v5.6.4' into dev
5.6.4-stable hotfix
2024-07-18 20:35:57 +07:00
Gravita
f88c0308f8
Merge branch 'release/5.6.4' 2024-07-18 20:35:46 +07:00
Gravita
20f713be05
5.6.4-stable 2024-07-18 20:35:26 +07:00
Gravita
5bf92d9a00
[FIX] ProGuard librariesDir 2024-07-18 20:34:31 +07:00
Gravita
903c4d40c5
Merge tag 'v5.6.3' into dev
5.6.3-stable
2024-07-18 15:55:07 +07:00
Gravita
ede9ab2c85
Merge branch 'release/5.6.3' 2024-07-18 15:54:58 +07:00
Gravita
c8c83c0dba
[ANY] 5.6.3-stable 2024-07-18 15:54:45 +07:00
Gravita
1bd5d8854c
[FIX] ProGuard and launchserver.dir.libraries 2024-07-17 01:32:51 +07:00
Gravita
992d31c883
[FEATURE] Set netty bind port in first run 2024-07-16 22:05:13 +07:00
Gravita
accbbe6b13
[FEATURE] Support port in launchserver address in first run 2024-07-16 21:54:59 +07:00
Gravita
0894e0b9c3
[FIX] Sync profiles before updates 2024-07-16 17:11:12 +07:00
Gravita
925007015f
[ANY] Update modules 2024-07-09 20:57:52 +07:00
Gravita
ec526a343d
[FIX] OptionalView.fixDependencies throw out of range 2024-07-08 21:29:40 +07:00
Gravita
1bebd8de2c
[FIX] classLoaderConfig is null 2024-07-08 21:22:44 +07:00
Gravita
c7781b30be
[FIX] launcher-libraries-compile not found 2024-07-06 22:34:20 +07:00
Gravita
df9d05a49c
[FEATURE] Support -Dlaunchserver.dir.modules and -Dlaunchserver.dir.launcher-modules 2024-07-06 03:24:02 +07:00
Gravita
03d53d4a09
[FEATURE] Support -Dlaunchserver.dir.libraries 2024-07-06 02:59:36 +07:00
Gravita
c261496af8
[ANY] Update modules 2024-07-06 01:55:10 +07:00
Gravita
8c11ab0cbe
[FIX] Disable Sodium lwjgl check 2024-07-05 16:27:50 +07:00
Gravita
78b4f1e3aa
[FIX] Gson version 2024-07-05 15:55:54 +07:00
Gravita
bbc5f1722b
[ANY] Update modules 2024-07-05 15:30:03 +07:00
Gravita
51411c5838
[ANY] Update modules 2024-07-05 14:55:21 +07:00
Gravita
3b22b76278
[FIX] Cleanroom install 2024-07-05 14:55:10 +07:00
Gravita
ef9cbfe0da
[ANY] Update modules 2024-07-05 14:38:57 +07:00
Gravita
353b663e12
[FEATURE] Support command execute by args 2024-07-05 14:38:45 +07:00
Gravita
78be606029
[FIX] FabricLoader main class 2024-07-05 14:27:45 +07:00
Gravita
584acdb8c3
[ANY] Update dependencies 2024-07-05 14:15:22 +07:00
Gravita
d4d6491e52
[FIX][EXPERIMENTAL] API bug fixes 2024-07-05 13:52:44 +07:00
34 changed files with 451 additions and 161 deletions

View file

@ -61,7 +61,6 @@
dependsOn jar
archiveClassifier.set('clean')
manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName,
"Automatic-Module-Name": "launchserver"
)
from sourceSets.main.output
@ -71,7 +70,6 @@
dependencies {
pack project(':LauncherAPI')
bundle group: 'me.tongfei', name: 'progressbar', version: '0.10.1'
bundle group: 'com.github.Marcono1234', name: 'gson-record-type-adapter-factory', version: 'v0.3.0'
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']
@ -82,6 +80,7 @@ pack project(':LauncherAPI')
bundle group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty']
bundle group: 'io.netty', name: 'netty-transport-classes-epoll', version: rootProject['verNetty']
bundle group: 'io.netty', name: 'netty-transport-native-epoll', version: rootProject['verNetty'], classifier: 'linux-x86_64'
bundle group: 'io.netty', name: 'netty-transport-native-epoll', version: rootProject['verNetty'], classifier: 'linux-aarch_64'
bundle group: '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']
@ -93,20 +92,16 @@ pack project(':LauncherAPI')
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') {
hikari 'io.micrometer:micrometer-core:1.13.1'
hikari('com.zaxxer:HikariCP:5.1.0') {
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) {

View file

@ -80,8 +80,10 @@ 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;
/**
* This object contains runtime configuration
*/
@ -116,8 +118,6 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
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 +126,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 +135,9 @@ 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;
this.shardId = shardId;
if(!Files.isDirectory(launcherPack)) {
Files.createDirectories(launcherPack);
@ -320,12 +322,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() {
@ -351,14 +355,15 @@ public void run() {
// Sync updates dir
CommonHelper.newThread("Profiles and updates sync", true, () -> {
try {
// Sync profiles dir
syncProfilesDir();
// Sync updates dir
if (!IOHelper.isDir(updatesDir))
Files.createDirectory(updatesDir);
updatesManager.readUpdatesDir();
// Sync profiles dir
if (!IOHelper.isDir(profilesDir))
Files.createDirectory(profilesDir);
syncProfilesDir();
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";
public Path updatesDir;
public Path profilesDir;
public Path librariesDir;
public Path launcherLibrariesDir;
public Path launcherLibrariesCompileDir;
public Path launcherPackDir;
@ -498,17 +472,21 @@ public static class LaunchServerDirectories {
public Path dir;
public Path trustStore;
public Path tmpDir;
public Path modules;
public Path launcherModules;
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 (tmpDir == null)
tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve("launchserver-%s".formatted(SecurityHelper.randomStringToken()));
}

View file

@ -13,6 +13,7 @@
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.components.Component;
@ -34,6 +35,7 @@
import java.nio.file.Path;
import java.security.Security;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
public class LaunchServerStarter {
@ -51,10 +53,13 @@ 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"));
@ -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,7 @@ public static void registerAll() {
OptionalAction.registerProviders();
OptionalTrigger.registerProviders();
MixProvider.registerProviders();
ProfileProvider.registerProviders();
}
private static void printExperimentalBranch() {
@ -201,7 +222,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 +236,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");

View file

@ -21,6 +21,7 @@ public class Main {
private static final List<String> classpathOnly = List.of("proguard", "jline", "progressbar", "kotlin", "epoll");
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) {
@ -56,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<>();

View file

@ -0,0 +1,109 @@
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 = IOHelper.resolveIncremental(profilesDirPath,
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);
}
}
}

View file

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

View file

@ -37,7 +37,9 @@ 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 {
return !Files.isDirectory(e) && !Files.isHidden(e);

View file

@ -97,10 +97,7 @@ public void invoke(String... args) throws IOException, CommandException {
isMirrorClientDownload = true;
}
}
try (BufferedWriter writer = IOHelper.newWriter(IOHelper.resolveIncremental(server.profilesDir,
dirName, "json"))) {
Launcher.gsonManager.configGson.toJson(clientProfile, writer);
}
server.config.profileProvider.addProfile(clientProfile);
// Finished
server.syncProfilesDir();

View file

@ -7,6 +7,7 @@
import pro.gravit.launcher.base.profiles.ClientProfileBuilder;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.command.Command;
import pro.gravit.utils.helper.CommonHelper;
import pro.gravit.utils.helper.IOHelper;
import java.io.IOException;
@ -26,7 +27,7 @@ public CloneProfileCommand(LaunchServer server) {
@Override
public String getArgsDescription() {
return "[profile file name] [new profile title]";
return "[profile title/uuid] [new profile title]";
}
@Override
@ -37,13 +38,12 @@ public String getUsageDescription() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 2);
var profilePath = server.profilesDir.resolve(args[0].concat(".json"));
if(!Files.exists(profilePath)) {
logger.error("File {} not found", profilePath);
}
ClientProfile profile;
try(Reader reader = IOHelper.newReader(profilePath)) {
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
try {
UUID uuid = UUID.fromString(args[0]);
profile = server.config.profileProvider.getProfile(uuid);
} catch (IllegalArgumentException ex) {
profile = server.config.profileProvider.getProfile(args[0]);
}
var builder = new ClientProfileBuilder(profile);
builder.setTitle(args[1]);
@ -65,10 +65,7 @@ public void invoke(String... args) throws Exception {
}
builder.setDir(args[1]);
profile = builder.createClientProfile();
var targetPath = server.profilesDir.resolve(args[1].concat(".json"));
try(Writer writer = IOHelper.newWriter(targetPath)) {
Launcher.gsonManager.gson.toJson(profile, writer);
}
server.config.profileProvider.addProfile(profile);
logger.info("Profile {} cloned from {}", args[1], args[0]);
server.syncProfilesDir();
server.syncUpdatesDir(List.of(args[1]));

View file

@ -8,6 +8,7 @@
import pro.gravit.utils.helper.IOHelper;
import java.nio.file.Files;
import java.util.UUID;
public class DeleteProfileCommand extends Command {
private final transient Logger logger = LogManager.getLogger(ListProfilesCommand.class);
@ -28,12 +29,12 @@ 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]);
@ -44,13 +45,9 @@ public void invoke(String... args) throws Exception {
if(!showApplyDialog("Continue?")) {
return;
}
logger.info("Delete {} ({})", profile.getTitle(), profile.getUUID());
server.config.profileProvider.deleteProfile(profile);
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);
}
}

View file

@ -37,9 +37,7 @@ public void invoke(String... args) throws Exception {
logger.info("Detected option {}", option);
}
ClientProfile profile = MakeProfileHelper.makeProfile(version, args[0], options);
try (Writer writer = IOHelper.newWriter(server.profilesDir.resolve(args[0].concat(".json")))) {
Launcher.gsonManager.configGson.toJson(profile, writer);
}
server.config.profileProvider.addProfile(profile);
logger.info("Profile {} created", args[0]);
server.syncProfilesDir();
}

View file

@ -22,20 +22,6 @@ public SaveProfilesCommand(LaunchServer server) {
super(server);
}
public static void saveProfile(ClientProfile profile, Path path) throws IOException {
if (profile.getServers().isEmpty()) {
ClientProfile.ServerProfile serverProfile = new ClientProfile.ServerProfile();
serverProfile.isDefault = true;
serverProfile.name = profile.getTitle();
serverProfile.serverAddress = profile.getServerAddress();
serverProfile.serverPort = profile.getServerPort();
profile.getServers().add(serverProfile);
}
try (Writer w = IOHelper.newWriter(path)) {
Launcher.gsonManager.configGson.toJson(profile, w);
}
}
@Override
public String getArgsDescription() {
return "[profile names...]";
@ -51,17 +37,14 @@ public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
if (args.length > 0) {
for (String profileName : args) {
Path profilePath = server.profilesDir.resolve(profileName.concat(".json"));
if (!Files.exists(profilePath)) {
logger.error("Profile {} not found", profilePath.toString());
return;
}
ClientProfile profile;
try (Reader reader = IOHelper.newReader(profilePath)) {
profile = Launcher.gsonManager.configGson.fromJson(reader, ClientProfile.class);
try {
UUID uuid = UUID.fromString(profileName);
profile = server.config.profileProvider.getProfile(uuid);
} catch (IllegalArgumentException ex) {
profile = server.config.profileProvider.getProfile(profileName);
}
saveProfile(profile, profilePath);
logger.info("Profile {} save successful", profilePath.toString());
server.config.profileProvider.addProfile(profile);
}
server.syncProfilesDir();
}

View file

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

View file

@ -9,6 +9,8 @@
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;
@ -36,6 +38,7 @@ public final class LaunchServerConfig {
// Handlers & Providers
public ProtectHandler protectHandler;
public Map<String, Component> components;
public ProfileProvider profileProvider = new LocalProfileProvider();
public NettyConfig netty;
public LauncherConf launcher;
public JarSignerConf sign;
@ -85,6 +88,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 +170,10 @@ public void init(LaunchServer.ReloadType type) {
server.registerObject("protectHandler", protectHandler);
protectHandler.init(server);
}
if(profileProvider != null) {
server.registerObject("profileProvider", profileProvider);
profileProvider.init(server);
}
if (components != null) {
components.forEach((k, v) -> server.registerObject("component.".concat(k), v));
}
@ -206,6 +214,10 @@ public void close(LaunchServer.ReloadType type) {
server.unregisterObject("protectHandler", protectHandler);
protectHandler.close();
}
if(profileProvider != null) {
server.unregisterObject("profileProvider", profileProvider);
profileProvider.close();
}
}
public static class JarSignerConf {

View file

@ -103,6 +103,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);
@ -196,6 +197,9 @@ public static String getMainClassByVersion(ClientProfile.Version version, MakePr
if(version.compareTo(ClientProfileVersions.MINECRAFT_1_7_10) == 0) {
return "com.gtnewhorizons.retrofuturabootstrap.Main";
}
if(version.compareTo(ClientProfileVersions.MINECRAFT_1_12_2) == 0) {
return "top.outlands.foundation.boot.Foundation"; // Cleanroom
}
if (findOption(options, MakeProfileOptionLaunchWrapper.class).isPresent()) {
return "net.minecraft.launchwrapper.Launch";
}
@ -203,7 +207,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";

View file

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

View file

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

View file

@ -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,6 +14,7 @@
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.components.Component;
@ -34,9 +34,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 +47,7 @@ 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));
modulesManager.invokeEvent(new PreGsonPhase(builder));
//ClientWebSocketService.appendTypeAdapters(builder);
}

View file

@ -3,6 +3,7 @@
import pro.gravit.launcher.core.LauncherNetworkAPI;
import pro.gravit.launcher.core.backend.UserSettings;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@ -10,7 +11,7 @@ public class BackendSettings extends UserSettings {
@LauncherNetworkAPI
public AuthorizationData auth;
@LauncherNetworkAPI
public Map<UUID, ProfileSettingsImpl> settings;
public Map<UUID, ProfileSettingsImpl> settings = new HashMap<>();
public static class AuthorizationData {
@LauncherNetworkAPI
public String accessToken;

View file

@ -4,6 +4,7 @@
import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launcher.core.api.LauncherAPIHolder;
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
import pro.gravit.launcher.core.api.features.CoreFeatureAPI;
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
import pro.gravit.launcher.core.api.method.AuthMethod;
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
@ -15,6 +16,8 @@
import pro.gravit.launcher.core.backend.exceptions.LauncherBackendException;
import pro.gravit.launcher.core.backend.extensions.Extension;
import pro.gravit.launcher.runtime.NewLauncherSettings;
import pro.gravit.launcher.runtime.client.ServerPinger;
import pro.gravit.launcher.runtime.debug.DebugMain;
import pro.gravit.launcher.runtime.managers.SettingsManager;
import pro.gravit.launcher.runtime.utils.LauncherUpdater;
import pro.gravit.utils.helper.JavaHelper;
@ -27,7 +30,10 @@
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
@ -47,6 +53,7 @@ public class LauncherBackendImpl implements LauncherBackendAPI {
private volatile SelfUser selfUser;
private volatile List<Java> availableJavas;
private volatile CompletableFuture<List<Java>> availableJavasFuture;
private final Map<UUID, CompletableFuture<ServerPingInfo>> pingFutures = new ConcurrentHashMap<>();
@Override
public void setCallback(MainCallback callback) {
@ -75,7 +82,13 @@ public CompletableFuture<LauncherInitData> init() {
} catch (Throwable e) {
return CompletableFuture.failedFuture(e);
}
return LauncherAPIHolder.core().checkUpdates().thenCombineAsync(LauncherAPIHolder.core().getAuthMethods(), (updatesInfo, authMethods) -> {
CompletableFuture<CoreFeatureAPI.LauncherUpdateInfo> feature;
if(isTestMode()) {
feature = CompletableFuture.completedFuture(new CoreFeatureAPI.LauncherUpdateInfo(null, "Unknown", false, false));
} else {
feature = LauncherAPIHolder.core().checkUpdates();
}
return feature.thenCombineAsync(LauncherAPIHolder.core().getAuthMethods(), (updatesInfo, authMethods) -> {
if(updatesInfo.required()) {
try {
LauncherUpdater.prepareUpdate(URI.create(updatesInfo.url()).toURL());
@ -131,6 +144,7 @@ private void setAuthToken(AuthFeatureAPI.AuthToken authToken) {
}
private void onAuthorize(SelfUser selfUser) {
this.selfUser = selfUser;
permissions = selfUser.getPermissions();
callback.onAuthorize(selfUser);
}
@ -182,7 +196,7 @@ public CompletableFuture<ReadyProfile> downloadProfile(ProfileFeatureAPI.ClientP
@Override
public CompletableFuture<byte[]> fetchTexture(Texture texture) {
return null;
return CompletableFuture.failedFuture(new UnsupportedOperationException());
}
@SuppressWarnings({"rawtypes", "unchecked"})
@ -201,6 +215,22 @@ public CompletableFuture<List<Java>> getAvailableJava() {
return CompletableFuture.completedFuture(availableJavas);
}
@Override
public CompletableFuture<ServerPingInfo> pingServer(ProfileFeatureAPI.ClientProfile profile) {
return pingFutures.computeIfAbsent(profile.getUUID(), (k) -> {
CompletableFuture<ServerPingInfo> future = new CompletableFuture<>();
executorService.submit(() -> {
try {
ServerPinger pinger = new ServerPinger((ClientProfile) profile);
future.complete(pinger.ping());
} catch (Throwable e) {
future.completeExceptionally(e);
}
});
return future;
});
}
@Override
public void registerUserSettings(String name, Class<? extends UserSettings> clazz) {
UserSettings.providers.register(name, clazz);
@ -231,6 +261,15 @@ public SelfUser getSelfUser() {
return selfUser;
}
@Override
public boolean isTestMode() {
try {
return DebugMain.IS_DEBUG.get();
} catch (Throwable ex) {
return false;
}
}
@Override
public <T extends Extension> T getExtension(Class<T> clazz) {
return null;
@ -238,11 +277,15 @@ public <T extends Extension> T getExtension(Class<T> clazz) {
@Override
public void shutdown() {
if(executorService != null) {
executorService.shutdownNow();
}
if(settingsManager != null) {
try {
settingsManager.saveConfig();
} catch (IOException e) {
LogHelper.error("Config not saved", e);
}
}
}
}

View file

@ -145,7 +145,22 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
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)
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));
}
}
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(ignorePath, workDir, params.actions, params.profile)
.map(Path::toString)
.toList());
}

View file

@ -5,6 +5,7 @@
import com.google.gson.JsonParser;
import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launcher.base.profiles.ClientProfileVersions;
import pro.gravit.launcher.core.backend.LauncherBackendAPI;
import pro.gravit.launcher.core.serialize.HInput;
import pro.gravit.launcher.core.serialize.HOutput;
import pro.gravit.utils.helper.IOHelper;
@ -18,6 +19,7 @@
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
@ -208,7 +210,7 @@ public Result ping() throws IOException {
}
}
public static final class Result {
public static final class Result implements LauncherBackendAPI.ServerPingInfo {
public final int onlinePlayers;
@ -228,5 +230,20 @@ public Result(int onlinePlayers, int maxPlayers, String raw) {
public boolean isOverfilled() {
return onlinePlayers >= maxPlayers;
}
@Override
public int getMaxOnline() {
return maxPlayers;
}
@Override
public int getOnline() {
return onlinePlayers;
}
@Override
public List<String> getPlayerNames() {
return List.of();
}
}
}

View file

@ -261,7 +261,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 {

View file

@ -20,7 +20,6 @@
public final class ClientProfile implements Comparable<ClientProfile>, ProfileFeatureAPI.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
@ -380,6 +379,11 @@ public Map<String, String> getProperties() {
return Collections.unmodifiableMap(properties);
}
@Override
public ServerInfo getServer() {
return getDefaultServerProfile();
}
public List<String> getCompatClasses() {
return Collections.unmodifiableList(compatClasses);
}
@ -409,14 +413,6 @@ 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
}
@ -525,7 +521,7 @@ public JsonElement serialize(Version src, Type typeOfSrc, JsonSerializationConte
}
}
public static class ServerProfile {
public static class ServerProfile implements ServerInfo {
public String name;
public String serverAddress;
public int serverPort;
@ -552,6 +548,16 @@ public ServerProfile(String name, String serverAddress, int serverPort, boolean
public InetSocketAddress toSocketAddress() {
return InetSocketAddress.createUnresolved(serverAddress, serverPort);
}
@Override
public String getAddress() {
return serverAddress;
}
@Override
public int getPort() {
return serverPort;
}
}
public static class ProfileDefaultSettings {

View file

@ -79,6 +79,7 @@ public ClientProfileBuilder(ClientProfile profile) {
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();

View file

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

View file

@ -42,7 +42,11 @@ public CompletableFuture<SelfUser> getCurrentUser() {
@Override
public CompletableFuture<AuthResponse> auth(String login, AuthMethodPassword password) {
return request.request(new AuthRequest(login, convertAuthPasswordAll(password), authId, false, AuthRequest.ConnectTypes.CLIENT))
AuthRequest.ConnectTypes connectType = AuthRequest.ConnectTypes.API;
if(Request.getExtendedTokens() != null && Request.getExtendedTokens().get("launcher") != null) {
connectType = AuthRequest.ConnectTypes.CLIENT;
}
return request.request(new AuthRequest(login, convertAuthPasswordAll(password), authId, false, connectType))
.thenApply(response -> new AuthResponse(response.makeUserInfo(), response.oauth));
}

View file

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

View file

@ -23,6 +23,12 @@ interface ClientProfile {
List<OptionalMod> getOptionalMods();
String getProperty(String name);
Map<String, String> getProperties();
ServerInfo getServer();
interface ServerInfo {
String getAddress();
int getPort();
}
}
interface OptionalMod {

View file

@ -28,6 +28,7 @@ public interface LauncherBackendAPI {
// Tools
CompletableFuture<byte[]> fetchTexture(Texture texture);
CompletableFuture<List<Java>> getAvailableJava();
CompletableFuture<ServerPingInfo> pingServer(ProfileFeatureAPI.ClientProfile profile);
// Settings
void registerUserSettings(String name, Class<? extends UserSettings> clazz);
UserSettings getUserSettings(String name, Function<String, UserSettings> ifNotExist);
@ -36,6 +37,7 @@ public interface LauncherBackendAPI {
boolean hasPermission(String permission);
String getUsername();
SelfUser getSelfUser();
boolean isTestMode();
// Extensions
<T extends Extension> T getExtension(Class<T> clazz);
void shutdown();
@ -163,4 +165,10 @@ interface Java {
int getMajorVersion();
Path getPath();
}
interface ServerPingInfo {
int getMaxOnline();
int getOnline();
List<String> getPlayerNames();
}
}

View file

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

View file

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

@ -1 +1 @@
Subproject commit a52b9cc8552445167b95e8933f4289bbaa70677d
Subproject commit c5087d40c9dd5b065fccedeac21afb967a25e112

View file

@ -1,18 +1,16 @@
project.ext {
verAsm = '9.7'
verNetty = '4.1.110.Final'
verNetty = '4.1.111.Final'
verOshiCore = '6.6.1'
verJunit = '5.10.2'
verGuavaC = '30.1.1-jre'
verJansi = '2.4.1'
verJline = '3.26.1'
verJwt = '0.12.5'
verBcprov = '1.70'
verGson = '2.11.0'
verBcpkix = '1.78.1'
verSlf4j = '2.0.13'
verLog4j = '2.23.1'
verMySQLConn = '8.4.0'
verMySQLConn = '9.0.0'
verMariaDBConn = '3.4.0'
verPostgreSQLConn = '42.7.3'
verH2Conn = '2.2.224'