Merge branch 'release/5.6.6'

This commit is contained in:
Gravita 2024-08-18 23:57:08 +07:00
commit 0cff6e247a
No known key found for this signature in database
GPG key ID: 543A8F335C9CD633
38 changed files with 602 additions and 331 deletions

View file

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

View file

@ -70,6 +70,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,7 +81,6 @@ 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;
@ -119,8 +119,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;
@ -129,7 +127,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;
@ -326,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() {
@ -358,14 +357,10 @@ public void run() {
CommonHelper.newThread("Profiles and updates sync", true, () -> {
try {
// Sync profiles dir
if (!IOHelper.isDir(profilesDir))
Files.createDirectory(profilesDir);
syncProfilesDir();
// Sync updates dir
if (!IOHelper.isDir(updatesDir))
Files.createDirectory(updatesDir);
updatesManager.readUpdatesDir();
config.updatesProvider.syncInitially();
modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this));
@ -402,12 +397,7 @@ public void syncLauncherBinaries() throws IOException {
public void syncProfilesDir() throws IOException {
logger.info("Syncing profiles dir");
List<ClientProfile> newProfies = new LinkedList<>();
IOHelper.walk(profilesDir, new ProfilesFileVisitor(newProfies), false);
// Sort and set new profiles
newProfies.sort(Comparator.comparing(a -> a));
profilesList = Set.copyOf(newProfies);
config.profileProvider.sync();
if (config.netty.sendProfileUpdatesEvent) {
sendUpdateProfilesEvent();
}
@ -422,7 +412,7 @@ private void sendUpdateProfilesEvent() {
if (client == null || !client.isAuth) {
return;
}
ProfilesRequestEvent event = new ProfilesRequestEvent(ProfilesResponse.getListVisibleProfiles(this, client));
ProfilesRequestEvent event = new ProfilesRequestEvent(config.profileProvider.getProfiles(client));
event.requestUUID = RequestEvent.eventUUID;
handler.service.sendObject(ch, event);
});
@ -468,38 +458,11 @@ public interface LaunchServerConfigManager {
void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException;
}
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
private final Collection<ClientProfile> result;
private final Logger logger = LogManager.getLogger();
private ProfilesFileVisitor(Collection<ClientProfile> result) {
this.result = result;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
logger.info("Syncing '{}' profile", IOHelper.getFileName(file));
// Read profile
ClientProfile profile;
try (BufferedReader reader = IOHelper.newReader(file)) {
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
}
profile.verify();
profile.setProfileFilePath(file);
// Add SIGNED profile to result list
result.add(profile);
return super.visitFile(file, attrs);
}
}
public static class LaunchServerDirectories {
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles",
public static final String UPDATES_NAME = "updates",
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", LAUNCHERPACK_NAME = "launcher-pack", KEY_NAME = ".keys", MODULES = "modules", LAUNCHER_MODULES = "launcher-modules", LIBRARIES = "libraries";
public Path updatesDir;
public Path profilesDir;
public Path librariesDir;
public Path launcherLibrariesDir;
public Path launcherLibrariesCompileDir;
@ -513,7 +476,6 @@ public static class LaunchServerDirectories {
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)

View file

@ -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;
@ -178,6 +180,8 @@ public static void registerAll() {
OptionalAction.registerProviders();
OptionalTrigger.registerProviders();
MixProvider.registerProviders();
ProfileProvider.registerProviders();
UpdatesProvider.registerProviders();
}
private static void printExperimentalBranch() {

View file

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

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

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

View file

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

View file

@ -14,11 +14,11 @@
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class BinaryPipeline {
public abstract class BinaryPipeline {
public final List<LauncherBuildTask> tasks = new ArrayList<>();
public final Path buildDir;
public final String nameFormat;
private transient final Logger logger = LogManager.getLogger();
protected transient final Logger logger = LogManager.getLogger();
public BinaryPipeline(Path buildDir, String nameFormat) {
this.buildDir = buildDir;
@ -80,27 +80,6 @@ public Optional<LauncherBuildTask> getTaskBefore(Predicate<LauncherBuildTask> pr
return Optional.empty();
}
public void build(Path target, boolean deleteTempFiles) throws IOException {
logger.info("Building launcher binary file");
Path thisPath = null;
long time_start = System.currentTimeMillis();
long time_this = time_start;
for (LauncherBuildTask task : tasks) {
logger.info("Task {}", task.getName());
Path oldPath = thisPath;
thisPath = task.process(oldPath);
long time_task_end = System.currentTimeMillis();
long time_task = time_task_end - time_this;
time_this = time_task_end;
logger.info("Task {} processed from {} millis", task.getName(), time_task);
}
long time_end = System.currentTimeMillis();
if (deleteTempFiles) IOHelper.move(thisPath, target);
else IOHelper.copy(thisPath, target);
IOHelper.deleteDir(buildDir, false);
logger.info("Build successful from {} millis", time_end - time_start);
}
public String nextName(String taskName) {
return nameFormat.formatted(taskName);
}

View file

@ -1,11 +1,14 @@
package pro.gravit.launchserver.binary;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.binary.tasks.LauncherBuildTask;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
public abstract class LauncherBinary extends BinaryPipeline {
public final LaunchServer server;
@ -19,11 +22,27 @@ protected LauncherBinary(LaunchServer server, Path binaryFile, String nameFormat
}
public static Path resolve(LaunchServer server, String ext) {
return server.config.copyBinaries ? server.updatesDir.resolve(server.config.binaryName + ext) : server.dir.resolve(server.config.binaryName + ext);
return Path.of(server.config.binaryName + ext);
}
public void build() throws IOException {
build(syncBinaryFile, server.config.launcher.deleteTempFiles);
logger.info("Building launcher binary file");
Path thisPath = null;
long time_start = System.currentTimeMillis();
long time_this = time_start;
for (LauncherBuildTask task : tasks) {
logger.info("Task {}", task.getName());
Path oldPath = thisPath;
thisPath = task.process(oldPath);
long time_task_end = System.currentTimeMillis();
long time_task = time_task_end - time_this;
time_this = time_task_end;
logger.info("Task {} processed from {} millis", task.getName(), time_task);
}
long time_end = System.currentTimeMillis();
server.config.updatesProvider.upload(null, Map.of(syncBinaryFile.toString(), thisPath), true);
IOHelper.deleteDir(buildDir, false);
logger.info("Build successful from {} millis", time_end - time_start);
}
public final boolean exists() {
@ -38,9 +57,13 @@ public void init() {
}
public final boolean sync() throws IOException {
boolean exists = exists();
digest = exists ? SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(syncBinaryFile)) : null;
return exists;
try {
var target = syncBinaryFile.toString();
var path = server.config.updatesProvider.download(null, List.of(target)).get(target);
digest = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(path));
return true;
} catch (Throwable e) {
return false;
}
}
}

View file

@ -7,10 +7,7 @@
import pro.gravit.utils.helper.UnpackHelper;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.stream.Collectors;
@ -40,7 +37,7 @@ public Path process(Path inputFile) throws IOException {
if(Files.isDirectory(server.launcherLibrariesCompile)) {
IOHelper.walk(server.launcherLibrariesCompile, new ListFileVisitor(server.launcherBinary.addonLibs), false);
}
try(Stream<Path> stream = Files.walk(server.launcherPack).filter((e) -> {
try(Stream<Path> stream = Files.walk(server.launcherPack, FileVisitOption.FOLLOW_LINKS).filter((e) -> {
try {
return !Files.isDirectory(e) && !Files.isHidden(e);
} catch (IOException ex) {

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,29 +29,24 @@ public String getUsageDescription() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
ClientProfile profile = null;
for(var p : server.getProfiles()) {
if(p.getUUID().toString().equals(args[0]) || p.getTitle().equals(args[0])) {
profile = p;
break;
}
ClientProfile profile;
try {
UUID uuid = UUID.fromString(args[0]);
profile = server.config.profileProvider.getProfile(uuid);
} catch (IllegalArgumentException ex) {
profile = server.config.profileProvider.getProfile(args[0]);
}
if(profile == null) {
logger.error("Profile {} not found", args[0]);
return;
}
var clientDir = server.updatesDir.resolve(profile.getDir()).toAbsolutePath();
logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", clientDir);
logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", profile.getDir());
if(!showApplyDialog("Continue?")) {
return;
}
logger.info("Delete {}", clientDir);
IOHelper.deleteDir(clientDir, true);
var profileFile = profile.getProfileFilePath();
if(profileFile == null) {
profileFile = server.profilesDir.resolve(profile.getTitle().concat(".json"));
}
logger.info("Delete {}", profileFile);
Files.deleteIfExists(profileFile);
logger.info("Delete {} ({})", profile.getTitle(), profile.getUUID());
server.config.profileProvider.deleteProfile(profile);
logger.info("Delete {}", profile.getDir());
server.config.updatesProvider.delete(profile.getDir());
}
}

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

@ -9,9 +9,13 @@
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;
@ -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;
@ -85,6 +90,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 +172,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 +220,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 {

View file

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

View file

@ -14,8 +14,10 @@
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.mix.MixProvider;
import pro.gravit.launchserver.auth.password.PasswordVerifier;
import pro.gravit.launchserver.auth.profiles.ProfileProvider;
import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.texture.TextureProvider;
import pro.gravit.launchserver.auth.updates.UpdatesProvider;
import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
import pro.gravit.launchserver.socket.WebSocketService;
@ -46,6 +48,8 @@ public void registerAdapters(GsonBuilder builder) {
builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers));
builder.registerTypeAdapter(OptionalTrigger.class, new UniversalJsonAdapter<>(OptionalTrigger.providers));
builder.registerTypeAdapter(MixProvider.class, new UniversalJsonAdapter<>(MixProvider.providers));
builder.registerTypeAdapter(ProfileProvider.class, new UniversalJsonAdapter<>(ProfileProvider.providers));
builder.registerTypeAdapter(UpdatesProvider.class, new UniversalJsonAdapter<>(UpdatesProvider.providers));
modulesManager.invokeEvent(new PreGsonPhase(builder));
//ClientWebSocketService.appendTypeAdapters(builder);
}

View file

@ -18,112 +18,38 @@
public class UpdatesManager {
private final LaunchServer server;
private final Logger logger = LogManager.getLogger();
private final Path cacheFile;
private volatile Map<String, HashedDir> updatesDirMap;
public UpdatesManager(LaunchServer server) {
this.server = server;
this.cacheFile = server.dir.resolve(".updates-cache");
}
private void writeCache(Path file) throws IOException {
try (HOutput output = new HOutput(IOHelper.newOutput(file))) {
output.writeLength(updatesDirMap.size(), 0);
for (Map.Entry<String, HashedDir> entry : updatesDirMap.entrySet()) {
output.writeString(entry.getKey(), 0);
entry.getValue().write(output);
}
}
logger.debug("Saved {} updates to cache", updatesDirMap.size());
}
private void readCache(Path file) throws IOException {
Map<String, HashedDir> updatesDirMap = new HashMap<>(16);
try (HInput input = new HInput(IOHelper.newInput(file))) {
int size = input.readLength(0);
for (int i = 0; i < size; ++i) {
String name = input.readString(0);
HashedDir dir = new HashedDir(input);
updatesDirMap.put(name, dir);
}
}
logger.debug("Found {} updates from cache", updatesDirMap.size());
this.updatesDirMap = Collections.unmodifiableMap(updatesDirMap);
}
@Deprecated
public void readUpdatesFromCache() throws IOException {
readCache(cacheFile);
}
@Deprecated
public void readUpdatesDir() throws IOException {
if (server.config.cacheUpdates) {
if (Files.exists(cacheFile)) {
try {
readCache(cacheFile);
return;
} catch (Throwable e) {
logger.error("Read updates cache failed", e);
}
}
}
syncUpdatesDir(null);
}
@Deprecated
public void syncUpdatesDir(Collection<String> dirs) throws IOException {
logger.info("Syncing updates dir");
Map<String, HashedDir> newUpdatesDirMap = new HashMap<>(16);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(server.updatesDir)) {
for (final Path updateDir : dirStream) {
if (Files.isHidden(updateDir))
continue; // Skip hidden
// Resolve name and verify is dir
String name = IOHelper.getFileName(updateDir);
if (!IOHelper.isDir(updateDir)) {
if (!IOHelper.isFile(updateDir) && Stream.of(".jar", ".exe", ".hash").noneMatch(e -> updateDir.toString().endsWith(e)))
logger.warn("Not update dir: '{}'", name);
continue;
}
// Add from previous map (it's guaranteed to be non-null)
if (dirs != null && !dirs.contains(name)) {
HashedDir hdir = updatesDirMap.get(name);
if (hdir != null) {
newUpdatesDirMap.put(name, hdir);
continue;
}
}
// Sync and sign update dir
logger.info("Syncing '{}' update dir", name);
HashedDir updateHDir = new HashedDir(updateDir, null, true, true);
newUpdatesDirMap.put(name, updateHDir);
}
}
updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap);
if (server.config.cacheUpdates) {
try {
writeCache(cacheFile);
} catch (Throwable e) {
logger.error("Write updates cache failed", e);
}
}
server.modulesManager.invokeEvent(new LaunchServerUpdatesSyncEvent(server));
server.config.updatesProvider.sync(dirs);
}
@Deprecated
public HashSet<String> getUpdatesList() {
HashSet<String> set = new HashSet<>();
for (Map.Entry<String, HashedDir> entry : updatesDirMap.entrySet())
set.add(entry.getKey());
return set;
return new HashSet<>();
}
@Deprecated
public HashedDir getUpdate(String name) {
return updatesDirMap.get(name);
return server.config.updatesProvider.getUpdatesDir(name);
}
@Deprecated
public void addUpdate(String name, HashedDir dir) {
updatesDirMap.put(name, dir);
throw new UnsupportedOperationException();
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -142,10 +142,41 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
}
//ADD CLASSPATH
processArgs.add(JVMHelper.jvmProperty("java.library.path", this.params.nativesDir));
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.AGENT) {
processArgs.add("-javaagent:".concat(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString()));
} else if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(new HashSet<>(), workDir, params.actions, params.profile)
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
Set<Path> ignorePath = new HashSet<>();
var moduleConf = params.profile.getModuleConf();
if(moduleConf != null) {
if(moduleConf.modulePath != null && !moduleConf.modulePath.isEmpty()) {
processArgs.add("-p");
for(var e : moduleConf.modulePath) {
ignorePath.add(Path.of(e));
}
processArgs.add(String.join(File.pathSeparator, moduleConf.modulePath));
}
if(moduleConf.modules != null && !moduleConf.modules.isEmpty()) {
processArgs.add("--add-modules");
processArgs.add(String.join(",", moduleConf.modules));
}
if(moduleConf.exports != null && !moduleConf.exports.isEmpty()) {
for(var e : moduleConf.exports.entrySet()) {
processArgs.add("--add-exports");
processArgs.add(String.format("%s=%s", e.getKey(), e.getValue()));
}
}
if(moduleConf.opens != null && !moduleConf.opens.isEmpty()) {
for(var e : moduleConf.opens.entrySet()) {
processArgs.add("--add-opens");
processArgs.add(String.format("%s=%s", e.getKey(), e.getValue()));
}
}
if(moduleConf.reads != null && !moduleConf.reads.isEmpty()) {
for(var e : moduleConf.reads.entrySet()) {
processArgs.add("--add-reads");
processArgs.add(String.format("%s=%s", e.getKey(), e.getValue()));
}
}
}
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(ignorePath, workDir, params.actions, params.profile)
.map(Path::toString)
.toList());
}

View file

@ -18,7 +18,6 @@
public final class ClientProfile implements Comparable<ClientProfile> {
private static final FileNameMatcher ASSET_MATCHER = new FileNameMatcher(
new String[0], new String[]{"indexes", "objects"}, new String[0]);
private transient Path profileFilePath;
@LauncherNetworkAPI
private String title;
@LauncherNetworkAPI
@ -392,16 +391,8 @@ public List<CompatibilityFlags> getFlags() {
return flags;
}
public Path getProfileFilePath() {
return profileFilePath;
}
public void setProfileFilePath(Path profileFilePath) {
this.profileFilePath = profileFilePath;
}
public enum ClassLoaderConfig {
AGENT, LAUNCHER, MODULE, SYSTEM_ARGS
LAUNCHER, MODULE, SYSTEM_ARGS
}
public enum CompatibilityFlags {

View file

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

View file

@ -86,9 +86,7 @@ private static void realMain(String[] args) throws Throwable {
modulesManager.invokeEvent(new PreConfigPhase());
LogHelper.debug("Reading ClientLauncher params");
ClientParams params = readParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort));
if (params.profile.getClassLoaderConfig() != ClientProfile.ClassLoaderConfig.AGENT) {
ClientLauncherMethods.verifyNoAgent();
}
ClientLauncherMethods.verifyNoAgent();
if(params.timestamp > System.currentTimeMillis() || params.timestamp + 30*1000 < System.currentTimeMillis() ) {
LogHelper.error("Timestamp failed. Exit");
ClientLauncherMethods.exitLauncher(-662);
@ -160,7 +158,7 @@ private static void realMain(String[] args) throws Throwable {
System.load(Paths.get(params.nativesDir).resolve(ClientService.findLibrary(e)).toAbsolutePath().toString());
}
}
if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) {
if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER || classLoaderConfig == ClientProfile.ClassLoaderConfig.MODULE) {
if(JVMHelper.JVM_VERSION <= 11) {
launch = new LegacyLaunch();
} else {
@ -170,20 +168,12 @@ private static void realMain(String[] args) throws Throwable {
System.setProperty("java.class.path", classpath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator)));
modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, classLoaderControl, profile));
ClientService.baseURLs = classLoaderControl.getURLs();
} else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.AGENT) {
launch = new BasicLaunch(LauncherAgent.inst);
classpathURLs.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toUri().toURL());
classLoaderControl = launch.init(classpath, params.nativesDir, options);
for (URL url : classpathURLs) {
LauncherAgent.addJVMClassPath(Paths.get(url.toURI()));
}
ClientService.instrumentation = LauncherAgent.inst;
modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, null, profile));
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
} else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
launch = new BasicLaunch();
classLoaderControl = launch.init(classpath, params.nativesDir, options);
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
} else {
throw new UnsupportedOperationException(String.format("Unknown classLoaderConfig %s", classLoaderConfig));
}
if(profile.hasFlag(ClientProfile.CompatibilityFlags.CLASS_CONTROL_API)) {
ClientService.classLoaderControl = classLoaderControl;

View file

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

View file

@ -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 = 5;
public static final int PATCH = 6;
public static final int BUILD = 1;
public static final Version.Type RELEASE = Type.STABLE;
public final int major;

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

View file

@ -5,7 +5,7 @@
id 'org.openjfx.javafxplugin' version '0.1.0' apply false
}
group = 'pro.gravit.launcher'
version = '5.6.5'
version = '5.6.6'
apply from: 'props.gradle'

Binary file not shown.

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

7
gradlew vendored
View file

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -84,7 +86,8 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum

2
gradlew.bat vendored
View file

@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################

@ -1 +1 @@
Subproject commit 9f1ccd0bbc767d04a8a2d2909cc050980c8a458f
Subproject commit 65f882bd90623aa1581ad24cfa4f52563ea95bc0

View file

@ -1,7 +1,7 @@
project.ext {
verAsm = '9.7'
verNetty = '4.1.111.Final'
verOshiCore = '6.6.1'
verOshiCore = '6.6.2'
verJunit = '5.10.2'
verJansi = '2.4.1'
verJline = '3.26.1'