Compare commits

..

No commits in common. "f1559183a121593f79803651eeae31138b9b6320" and "f297a5878d636f4db79d86b90f5b78849ad647f1" have entirely different histories.

45 changed files with 253 additions and 863 deletions

View file

@ -70,7 +70,6 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
/** /**
* The path to the folder with updates/webroot * The path to the folder with updates/webroot
*/ */
@Deprecated
public final Path updatesDir; public final Path updatesDir;
// Constant paths // Constant paths
@ -360,7 +359,9 @@ public void run() {
syncProfilesDir(); syncProfilesDir();
// Sync updates dir // Sync updates dir
config.updatesProvider.syncInitially(); if (!IOHelper.isDir(updatesDir))
Files.createDirectory(updatesDir);
updatesManager.readUpdatesDir();
modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this)); modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this));

View file

@ -16,7 +16,6 @@
import pro.gravit.launchserver.auth.profiles.ProfileProvider; import pro.gravit.launchserver.auth.profiles.ProfileProvider;
import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.texture.TextureProvider; import pro.gravit.launchserver.auth.texture.TextureProvider;
import pro.gravit.launchserver.auth.updates.UpdatesProvider;
import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
@ -181,7 +180,6 @@ public static void registerAll() {
OptionalTrigger.registerProviders(); OptionalTrigger.registerProviders();
MixProvider.registerProviders(); MixProvider.registerProviders();
ProfileProvider.registerProviders(); ProfileProvider.registerProviders();
UpdatesProvider.registerProviders();
} }
private static void printExperimentalBranch() { private static void printExperimentalBranch() {

View file

@ -9,22 +9,15 @@
import java.util.function.Consumer; import java.util.function.Consumer;
public class HikariSQLSourceConfig implements SQLSourceConfig { public class HikariSQLSourceConfig implements SQLSourceConfig {
private transient volatile HikariDataSource dataSource; private transient HikariDataSource dataSource;
private String dsClass; private String dsClass;
private Properties dsProps; private Properties dsProps;
private String driverClass; private String driverClass;
private String jdbcUrl; private String jdbcUrl;
private String username; private String username;
private String password; private String password;
private boolean initializeAtStart;
public void init() { public void init() {
if(initializeAtStart) {
initializeConnection();
}
}
private void initializeConnection() {
if (dataSource != null) { if (dataSource != null) {
return; return;
} }
@ -41,11 +34,6 @@ private void initializeConnection() {
@Override @Override
public Connection getConnection() throws SQLException { public Connection getConnection() throws SQLException {
if(dataSource == null && !initializeAtStart) {
synchronized (this) {
initializeConnection();
}
}
return dataSource.getConnection(); return dataSource.getConnection();
} }

View file

@ -295,9 +295,7 @@ private SQLUser queryUser(String sql, String value) throws SQLException {
user = constructUser(set); user = constructUser(set);
} }
} }
if(user != null) { user.permissions = requestPermissions(user.uuid.toString());
user.permissions = requestPermissions(user.uuid.toString());
}
return user; return user;
} }

View file

@ -44,7 +44,6 @@ public SQLSourceConfig getSQLConfig() {
@Override @Override
public void init(LaunchServer server, AuthProviderPair pair) { public void init(LaunchServer server, AuthProviderPair pair) {
super.init(server, pair); super.init(server, pair);
logger.warn("Method 'mysql' deprecated and may be removed in future release. Please use new 'sql' method: https://gravitlauncher.com/auth");
String userInfoCols = makeUserCols(); String userInfoCols = makeUserCols();
String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey"; String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey";
if (sqlFindHardwareByPublicKey == null) if (sqlFindHardwareByPublicKey == null)

View file

@ -1,7 +1,5 @@
package pro.gravit.launchserver.auth.core; package pro.gravit.launchserver.auth.core;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig; import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
import pro.gravit.launchserver.auth.SQLSourceConfig; import pro.gravit.launchserver.auth.SQLSourceConfig;
@ -12,10 +10,4 @@ public class PostgresSQLCoreProvider extends AbstractSQLCoreProvider {
public SQLSourceConfig getSQLConfig() { public SQLSourceConfig getSQLConfig() {
return postgresSQLHolder; return postgresSQLHolder;
} }
@Override
public void init(LaunchServer server, AuthProviderPair pair) {
super.init(server, pair);
logger.warn("Method 'postgresql' deprecated and may be removed in future release. Please use new 'sql' method: https://gravitlauncher.com/auth");
}
} }

View file

@ -1,27 +1,11 @@
package pro.gravit.launchserver.auth.core; package pro.gravit.launchserver.auth.core;
import pro.gravit.launcher.base.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.HikariSQLSourceConfig; import pro.gravit.launchserver.auth.HikariSQLSourceConfig;
import pro.gravit.launchserver.auth.MySQLSourceConfig;
import pro.gravit.launchserver.auth.SQLSourceConfig; import pro.gravit.launchserver.auth.SQLSourceConfig;
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportExtendedCheckServer;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportHardware;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.helper.IOHelper;
import java.io.ByteArrayInputStream; public class SQLCoreProvider extends AbstractSQLCoreProvider {
import java.io.IOException;
import java.sql.*;
import java.util.Base64;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
public class SQLCoreProvider extends AbstractSQLCoreProvider implements AuthSupportHardware, AuthSupportExtendedCheckServer {
public HikariSQLSourceConfig holder; public HikariSQLSourceConfig holder;
@Override @Override
@ -30,364 +14,14 @@ public void close() {
holder.close(); holder.close();
} }
@Override
public SQLSourceConfig getSQLConfig() {
return holder;
}
public String hardwareIdColumn;
public String tableHWID = "hwids";
public String tableHWIDLog = "hwidLog";
public double criticalCompareLevel = 1.0;
private transient String sqlFindHardwareByPublicKey;
private transient String sqlFindHardwareByData;
private transient String sqlFindHardwareById;
private transient String sqlCreateHardware;
private transient String sqlCreateHWIDLog;
private transient String sqlUpdateHardwarePublicKey;
private transient String sqlUpdateHardwareBanned;
private transient String sqlUpdateUsers;
private transient String sqlUsersByHwidId;
@Override @Override
public void init(LaunchServer server, AuthProviderPair pair) { public void init(LaunchServer server, AuthProviderPair pair) {
holder.init(); holder.init();
super.init(server, pair); super.init(server, pair);
String userInfoCols = makeUserCols();
String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey";
if (sqlFindHardwareByPublicKey == null)
sqlFindHardwareByPublicKey = "SELECT %s FROM %s WHERE publicKey = ?".formatted(hardwareInfoCols, tableHWID);
if (sqlFindHardwareById == null)
sqlFindHardwareById = "SELECT %s FROM %s WHERE id = ?".formatted(hardwareInfoCols, tableHWID);
if (sqlUsersByHwidId == null)
sqlUsersByHwidId = "SELECT %s FROM %s WHERE %s = ?".formatted(userInfoCols, table, hardwareIdColumn);
if (sqlFindHardwareByData == null)
sqlFindHardwareByData = "SELECT %s FROM %s".formatted(hardwareInfoCols, tableHWID);
if (sqlCreateHardware == null)
sqlCreateHardware = "INSERT INTO %s (publickey, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, graphicCard, battery, banned) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0')".formatted(tableHWID);
if (sqlCreateHWIDLog == null)
sqlCreateHWIDLog = "INSERT INTO %s (hwidId, newPublicKey) VALUES (?, ?)".formatted(tableHWIDLog);
if (sqlUpdateHardwarePublicKey == null)
sqlUpdateHardwarePublicKey = "UPDATE %s SET publicKey = ? WHERE id = ?".formatted(tableHWID);
sqlUpdateHardwareBanned = "UPDATE %s SET banned = ? WHERE id = ?".formatted(tableHWID);
sqlUpdateUsers = "UPDATE %s SET %s = ? WHERE %s = ?".formatted(table, hardwareIdColumn, uuidColumn);
} }
@Override @Override
protected String makeUserCols() { public SQLSourceConfig getSQLConfig() {
return super.makeUserCols().concat(", ").concat(hardwareIdColumn); return holder;
}
@Override
protected SQLUser constructUser(ResultSet set) throws SQLException {
return set.next() ? new SQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), set.getLong(hardwareIdColumn)) : null;
}
private SQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException, IOException {
HardwareReportRequest.HardwareInfo hardwareInfo = new HardwareReportRequest.HardwareInfo();
hardwareInfo.hwDiskId = set.getString("hwDiskId");
hardwareInfo.baseboardSerialNumber = set.getString("baseboardSerialNumber");
byte[] displayId = set.getBytes("displayId");
hardwareInfo.displayId = displayId == null ? null : displayId;
hardwareInfo.bitness = set.getInt("bitness");
hardwareInfo.totalMemory = set.getLong("totalMemory");
hardwareInfo.logicalProcessors = set.getInt("logicalProcessors");
hardwareInfo.physicalProcessors = set.getInt("physicalProcessors");
hardwareInfo.processorMaxFreq = set.getLong("processorMaxFreq");
hardwareInfo.battery = set.getBoolean("battery");
hardwareInfo.graphicCard = set.getString("graphicCard");
byte[] publicKey = set.getBytes("publicKey");
long id = set.getLong("id");
boolean banned = set.getBoolean("banned");
return new SQLUserHardware(hardwareInfo, publicKey, id, banned);
}
private void setUserHardwareId(Connection connection, UUID uuid, long hwidId) throws SQLException {
PreparedStatement s = connection.prepareStatement(sqlUpdateUsers);
s.setLong(1, hwidId);
s.setString(2, uuid.toString());
s.executeUpdate();
}
@Override
public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlFindHardwareByPublicKey);
s.setBytes(1, publicKey);
try (ResultSet set = s.executeQuery()) {
if (set.next()) {
connection.commit();
return fetchHardwareInfo(set);
} else {
connection.commit();
return null;
}
}
} catch (SQLException | IOException throwables) {
logger.error("SQL Error", throwables);
return null;
}
}
@Override
public UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info) {
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlFindHardwareByData);
try (ResultSet set = s.executeQuery()) {
while (set.next()) {
SQLUserHardware hw = fetchHardwareInfo(set);
AuthSupportHardware.HardwareInfoCompareResult result = compareHardwareInfo(hw.getHardwareInfo(), info);
if (result.compareLevel > criticalCompareLevel) {
connection.commit();
return hw;
} else {
connection.commit();
}
}
}
} catch (SQLException | IOException throwables) {
logger.error("SQL Error", throwables);
}
return null;
}
@Override
public UserHardware getHardwareInfoById(String id) {
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlFindHardwareById);
s.setLong(1, Long.parseLong(id));
try (ResultSet set = s.executeQuery()) {
if (set.next()) {
connection.commit();
return fetchHardwareInfo(set);
} else {
connection.commit();
return null;
}
}
} catch (SQLException | IOException throwables) {
logger.error("SQL Error", throwables);
return null;
}
}
@Override
public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey) {
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlCreateHardware, Statement.RETURN_GENERATED_KEYS);
s.setBytes(1, publicKey);
s.setString(2, hardwareInfo.hwDiskId);
s.setString(3, hardwareInfo.baseboardSerialNumber);
s.setBytes(4, hardwareInfo.displayId == null ? null : hardwareInfo.displayId);
s.setInt(5, hardwareInfo.bitness);
s.setLong(6, hardwareInfo.totalMemory);
s.setInt(7, hardwareInfo.logicalProcessors);
s.setInt(8, hardwareInfo.physicalProcessors);
s.setLong(9, hardwareInfo.processorMaxFreq);
s.setString(10, hardwareInfo.graphicCard);
s.setBoolean(11, hardwareInfo.battery);
s.executeUpdate();
try (ResultSet generatedKeys = s.getGeneratedKeys()) {
if (generatedKeys.next()) {
//writeHwidLog(connection, generatedKeys.getLong(1), publicKey);
long id = generatedKeys.getLong(1);
connection.commit();
return new SQLUserHardware(hardwareInfo, publicKey, id, false);
}
}
connection.commit();
return null;
} catch (SQLException throwables) {
logger.error("SQL Error", throwables);
return null;
}
}
@Override
public void connectUserAndHardware(UserSession userSession, UserHardware hardware) {
AbstractSQLCoreProvider.SQLUserSession SQLUserSession = (AbstractSQLCoreProvider.SQLUserSession) userSession;
SQLUser SQLUser = (SQLUser) SQLUserSession.getUser();
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
if (SQLUser.hwidId == SQLUserHardware.id) return;
SQLUser.hwidId = SQLUserHardware.id;
try (Connection connection = holder.getConnection()) {
setUserHardwareId(connection, SQLUser.getUUID(), SQLUserHardware.id);
} catch (SQLException throwables) {
logger.error("SQL Error", throwables);
}
}
@Override
public void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey) {
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
SQLUserHardware.publicKey = publicKey;
try (Connection connection = holder.getConnection()) {
connection.setAutoCommit(false);
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwarePublicKey);
s.setBytes(1, publicKey);
s.setLong(2, SQLUserHardware.id);
s.executeUpdate();
connection.commit();
} catch (SQLException e) {
logger.error("SQL error", e);
}
}
@Override
public Iterable<User> getUsersByHardwareInfo(UserHardware hardware) {
List<User> users = new LinkedList<>();
try (Connection c = holder.getConnection()) {
c.setAutoCommit(false);
PreparedStatement s = c.prepareStatement(sqlUsersByHwidId);
s.setLong(1, Long.parseLong(hardware.getId()));
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
try (ResultSet set = s.executeQuery()) {
while (!set.isLast()) {
users.add(constructUser(set));
}
}
c.commit();
} catch (SQLException e) {
logger.error("SQL error", e);
return null;
}
return users;
}
@Override
public void banHardware(UserHardware hardware) {
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
SQLUserHardware.banned = true;
try (Connection connection = holder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned);
s.setBoolean(1, true);
s.setLong(2, SQLUserHardware.id);
s.executeUpdate();
} catch (SQLException e) {
logger.error("SQL Error", e);
}
}
@Override
public void unbanHardware(UserHardware hardware) {
SQLUserHardware SQLUserHardware = (SQLUserHardware) hardware;
SQLUserHardware.banned = false;
try (Connection connection = holder.getConnection()) {
PreparedStatement s = connection.prepareStatement(sqlUpdateHardwareBanned);
s.setBoolean(1, false);
s.setLong(2, SQLUserHardware.id);
s.executeUpdate();
} catch (SQLException e) {
logger.error("SQL error", e);
}
}
@Override
protected AbstractSQLCoreProvider.SQLUserSession createSession(AbstractSQLCoreProvider.SQLUser user) {
return new SQLUserSession(user);
}
@Override
public UserSession extendedCheckServer(Client client, String username, String serverID) throws IOException {
AbstractSQLCoreProvider.SQLUser user = (AbstractSQLCoreProvider.SQLUser) getUserByUsername(username);
if (user == null) {
return null;
}
if (user.getUsername().equals(username) && user.getServerId().equals(serverID)) {
return createSession(user);
}
return null;
}
public class SQLUserSession extends AbstractSQLCoreProvider.SQLUserSession implements UserSessionSupportHardware {
private transient SQLUser SQLUser;
protected transient SQLUserHardware hardware;
public SQLUserSession(AbstractSQLCoreProvider.SQLUser user) {
super(user);
SQLUser = (SQLUser) user;
}
@Override
public String getHardwareId() {
return SQLUser.hwidId == 0 ? null : String.valueOf(SQLUser.hwidId);
}
@Override
public UserHardware getHardware() {
if(hardware == null) {
hardware = (SQLUserHardware) getHardwareInfoById(String.valueOf(SQLUser.hwidId));
}
return hardware;
}
}
public static class SQLUserHardware implements UserHardware {
private final HardwareReportRequest.HardwareInfo hardwareInfo;
private final long id;
private byte[] publicKey;
private boolean banned;
public SQLUserHardware(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, long id, boolean banned) {
this.hardwareInfo = hardwareInfo;
this.publicKey = publicKey;
this.id = id;
this.banned = banned;
}
@Override
public HardwareReportRequest.HardwareInfo getHardwareInfo() {
return hardwareInfo;
}
@Override
public byte[] getPublicKey() {
return publicKey;
}
@Override
public String getId() {
return String.valueOf(id);
}
@Override
public boolean isBanned() {
return banned;
}
@Override
public String toString() {
return "SQLUserHardware{" +
"hardwareInfo=" + hardwareInfo +
", publicKey=" + (publicKey == null ? null : new String(Base64.getEncoder().encode(publicKey))) +
", id=" + id +
", banned=" + banned +
'}';
}
}
public static class SQLUser extends AbstractSQLCoreProvider.SQLUser {
protected long hwidId;
public SQLUser(UUID uuid, String username, String accessToken, String serverId, String password, long hwidId) {
super(uuid, username, accessToken, serverId, password);
this.hwidId = hwidId;
}
@Override
public String toString() {
return "SQLUser{" +
"uuid=" + uuid +
", username='" + username + '\'' +
", permissions=" + permissions +
", hwidId=" + hwidId +
'}';
}
} }
} }

View file

@ -45,7 +45,8 @@ public void addProfile(ClientProfile profile) throws IOException {
} }
} }
if(target == null) { if(target == null) {
target = profilesDirPath.resolve(profile.getTitle()+".json"); target = IOHelper.resolveIncremental(profilesDirPath,
profile.getTitle(), "json");
oldProfile = profilesMap.get(target); oldProfile = profilesMap.get(target);
if(oldProfile != null && !oldProfile.getUUID().equals(profile.getUUID())) { if(oldProfile != null && !oldProfile.getUUID().equals(profile.getUUID())) {
throw new FileAlreadyExistsException(target.toString()); throw new FileAlreadyExistsException(target.toString());

View file

@ -1,186 +0,0 @@
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

@ -1,52 +0,0 @@
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.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public abstract class BinaryPipeline { public class BinaryPipeline {
public final List<LauncherBuildTask> tasks = new ArrayList<>(); public final List<LauncherBuildTask> tasks = new ArrayList<>();
public final Path buildDir; public final Path buildDir;
public final String nameFormat; public final String nameFormat;
protected transient final Logger logger = LogManager.getLogger(); private transient final Logger logger = LogManager.getLogger();
public BinaryPipeline(Path buildDir, String nameFormat) { public BinaryPipeline(Path buildDir, String nameFormat) {
this.buildDir = buildDir; this.buildDir = buildDir;
@ -80,6 +80,27 @@ public Optional<LauncherBuildTask> getTaskBefore(Predicate<LauncherBuildTask> pr
return Optional.empty(); 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) { public String nextName(String taskName) {
return nameFormat.formatted(taskName); return nameFormat.formatted(taskName);
} }

View file

@ -1,14 +1,11 @@
package pro.gravit.launchserver.binary; package pro.gravit.launchserver.binary;
import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.binary.tasks.LauncherBuildTask;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List;
import java.util.Map;
public abstract class LauncherBinary extends BinaryPipeline { public abstract class LauncherBinary extends BinaryPipeline {
public final LaunchServer server; public final LaunchServer server;
@ -22,27 +19,11 @@ protected LauncherBinary(LaunchServer server, Path binaryFile, String nameFormat
} }
public static Path resolve(LaunchServer server, String ext) { public static Path resolve(LaunchServer server, String ext) {
return Path.of(server.config.binaryName + ext); return server.config.copyBinaries ? server.updatesDir.resolve(server.config.binaryName + ext) : server.dir.resolve(server.config.binaryName + ext);
} }
public void build() throws IOException { public void build() throws IOException {
logger.info("Building launcher binary file"); build(syncBinaryFile, server.config.launcher.deleteTempFiles);
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() { public final boolean exists() {
@ -57,13 +38,9 @@ public void init() {
} }
public final boolean sync() throws IOException { public final boolean sync() throws IOException {
try { boolean exists = exists();
var target = syncBinaryFile.toString(); digest = exists ? SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(syncBinaryFile)) : null;
var path = server.config.updatesProvider.download(null, List.of(target)).get(target);
digest = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(path)); return exists;
return true;
} catch (Throwable e) {
return false;
}
} }
} }

View file

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

View file

@ -40,13 +40,14 @@ public void invoke(String... args) throws Exception {
logger.error("Profile {} not found", args[0]); logger.error("Profile {} not found", args[0]);
return; return;
} }
logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", profile.getDir()); var clientDir = server.updatesDir.resolve(profile.getDir()).toAbsolutePath();
logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", clientDir);
if(!showApplyDialog("Continue?")) { if(!showApplyDialog("Continue?")) {
return; return;
} }
logger.info("Delete {} ({})", profile.getTitle(), profile.getUUID()); logger.info("Delete {} ({})", profile.getTitle(), profile.getUUID());
server.config.profileProvider.deleteProfile(profile); server.config.profileProvider.deleteProfile(profile);
logger.info("Delete {}", profile.getDir()); logger.info("Delete {}", clientDir);
server.config.updatesProvider.delete(profile.getDir()); IOHelper.deleteDir(clientDir, true);
} }
} }

View file

@ -14,8 +14,6 @@
import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.protect.StdProtectHandler; import pro.gravit.launchserver.auth.protect.StdProtectHandler;
import pro.gravit.launchserver.auth.texture.RequestTextureProvider; 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.AuthLimiterComponent;
import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.components.ProGuardComponent; import pro.gravit.launchserver.components.ProGuardComponent;
@ -34,13 +32,13 @@ public final class LaunchServerConfig {
public String[] mirrors; public String[] mirrors;
public String binaryName; public String binaryName;
public boolean copyBinaries = true; public boolean copyBinaries = true;
public boolean cacheUpdates = true;
public LauncherConfig.LauncherEnvironment env; public LauncherConfig.LauncherEnvironment env;
public Map<String, AuthProviderPair> auth; public Map<String, AuthProviderPair> auth;
// Handlers & Providers // Handlers & Providers
public ProtectHandler protectHandler; public ProtectHandler protectHandler;
public Map<String, Component> components; public Map<String, Component> components;
public ProfileProvider profileProvider = new LocalProfileProvider(); public ProfileProvider profileProvider = new LocalProfileProvider();
public UpdatesProvider updatesProvider = new LocalUpdatesProvider();
public NettyConfig netty; public NettyConfig netty;
public LauncherConf launcher; public LauncherConf launcher;
public JarSignerConf sign; public JarSignerConf sign;
@ -176,10 +174,6 @@ public void init(LaunchServer.ReloadType type) {
server.registerObject("profileProvider", profileProvider); server.registerObject("profileProvider", profileProvider);
profileProvider.init(server); profileProvider.init(server);
} }
if(updatesProvider != null) {
server.registerObject("updatesProvider", updatesProvider);
updatesProvider.init(server);
}
if (components != null) { if (components != null) {
components.forEach((k, v) -> server.registerObject("component.".concat(k), v)); components.forEach((k, v) -> server.registerObject("component.".concat(k), v));
} }
@ -224,10 +218,6 @@ public void close(LaunchServer.ReloadType type) {
server.unregisterObject("profileProvider", profileProvider); server.unregisterObject("profileProvider", profileProvider);
profileProvider.close(); profileProvider.close();
} }
if(updatesProvider != null) {
server.unregisterObject("updatesProvider", updatesProvider);
updatesProvider.close();
}
} }
public static class JarSignerConf { public static class JarSignerConf {

View file

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

View file

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

View file

@ -18,38 +18,112 @@
public class UpdatesManager { public class UpdatesManager {
private final LaunchServer server; private final LaunchServer server;
private final Logger logger = LogManager.getLogger();
private final Path cacheFile;
private volatile Map<String, HashedDir> updatesDirMap;
public UpdatesManager(LaunchServer server) { public UpdatesManager(LaunchServer server) {
this.server = 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 { public void readUpdatesFromCache() throws IOException {
readCache(cacheFile);
} }
@Deprecated
public void readUpdatesDir() throws IOException { 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 { public void syncUpdatesDir(Collection<String> dirs) throws IOException {
server.config.updatesProvider.sync(dirs); 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));
} }
@Deprecated
public HashSet<String> getUpdatesList() { public HashSet<String> getUpdatesList() {
return new HashSet<>(); HashSet<String> set = new HashSet<>();
for (Map.Entry<String, HashedDir> entry : updatesDirMap.entrySet())
set.add(entry.getKey());
return set;
} }
@Deprecated
public HashedDir getUpdate(String name) { public HashedDir getUpdate(String name) {
return server.config.updatesProvider.getUpdatesDir(name); return updatesDirMap.get(name);
} }
@Deprecated
public void addUpdate(String name, HashedDir dir) { public void addUpdate(String name, HashedDir dir) {
throw new UnsupportedOperationException(); updatesDirMap.put(name, dir);
} }
} }

View file

@ -13,7 +13,6 @@
import java.util.Set; import java.util.Set;
public class ProfilesResponse extends SimpleResponse { public class ProfilesResponse extends SimpleResponse {
@Deprecated
public static List<ClientProfile> getListVisibleProfiles(LaunchServer server, Client client) { public static List<ClientProfile> getListVisibleProfiles(LaunchServer server, Client client) {
List<ClientProfile> profileList; List<ClientProfile> profileList;
Set<ClientProfile> serverProfiles = server.getProfiles(); Set<ClientProfile> serverProfiles = server.getProfiles();
@ -41,6 +40,6 @@ public void execute(ChannelHandlerContext ctx, Client client) {
sendError("Access denied"); sendError("Access denied");
return; return;
} }
sendResult(new ProfilesRequestEvent(server.config.profileProvider.getProfiles(client))); sendResult(new ProfilesRequestEvent(getListVisibleProfiles(server, client)));
} }
} }

View file

@ -52,7 +52,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
client.checkSign = true; client.checkSign = true;
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); sendResult(new LauncherRequestEvent(false, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
} else { } else {
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, null, 0)); sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
} }
} else if (launcher_type == 2) //EXE } else if (launcher_type == 2) //EXE
{ {
@ -62,7 +62,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
client.checkSign = true; client.checkSign = true;
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); sendResult(new LauncherRequestEvent(false, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
} else { } else {
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, null, 0)); sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
} }
} else sendError("Request launcher type error"); } else sendError("Request launcher type error");
} }

View file

@ -1,6 +1,7 @@
apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'com.github.johnrengelman.shadow'
String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper" String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper"
String mainAgentName = "pro.gravit.launcher.runtime.LauncherAgent"
repositories { repositories {
maven { maven {
@ -19,6 +20,7 @@
jar { jar {
archiveClassifier.set('clean') archiveClassifier.set('clean')
manifest.attributes("Main-Class": mainClassName, manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName,
"Multi-Release": "true", "Multi-Release": "true",
"Automatic-Module-Name": "GravitLauncher") "Automatic-Module-Name": "GravitLauncher")
} }

View file

@ -130,6 +130,7 @@ public static void main(String... args) throws Throwable {
LogHelper.printLicense("Launcher"); LogHelper.printLicense("Launcher");
LauncherEngine.checkClass(LauncherEngineWrapper.class); LauncherEngine.checkClass(LauncherEngineWrapper.class);
LauncherEngine.checkClass(LauncherEngine.class); LauncherEngine.checkClass(LauncherEngine.class);
LauncherEngine.checkClass(LauncherAgent.class);
LauncherEngine.checkClass(ClientLauncherEntryPoint.class); LauncherEngine.checkClass(ClientLauncherEntryPoint.class);
LauncherEngine.modulesManager = new RuntimeModuleManager(); LauncherEngine.modulesManager = new RuntimeModuleManager();
LauncherEngine.modulesManager.loadModule(new RuntimeLauncherCoreModule()); LauncherEngine.modulesManager.loadModule(new RuntimeLauncherCoreModule());

View file

@ -1,7 +1,6 @@
package pro.gravit.launcher.runtime.backend; package pro.gravit.launcher.runtime.backend;
import pro.gravit.launcher.core.LauncherNetworkAPI; import pro.gravit.launcher.core.LauncherNetworkAPI;
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
import pro.gravit.launcher.core.backend.UserSettings; import pro.gravit.launcher.core.backend.UserSettings;
import java.util.HashMap; import java.util.HashMap;
@ -20,24 +19,5 @@ public static class AuthorizationData {
public String refreshToken; public String refreshToken;
@LauncherNetworkAPI @LauncherNetworkAPI
public long expireIn; public long expireIn;
public AuthFeatureAPI.AuthToken toToken() {
return new AuthFeatureAPI.AuthToken() {
@Override
public String getAccessToken() {
return accessToken;
}
@Override
public String getRefreshToken() {
return refreshToken;
}
@Override
public long getExpire() {
return expireIn;
}
};
}
} }
} }

View file

@ -37,8 +37,7 @@ CompletableFuture<LauncherBackendAPI.ReadyProfile> downloadProfile(ClientProfile
AtomicReference<DownloadedDir> clientRef = new AtomicReference<>(); AtomicReference<DownloadedDir> clientRef = new AtomicReference<>();
AtomicReference<DownloadedDir> assetRef = new AtomicReference<>(); AtomicReference<DownloadedDir> assetRef = new AtomicReference<>();
AtomicReference<DownloadedDir> javaRef = new AtomicReference<>(); AtomicReference<DownloadedDir> javaRef = new AtomicReference<>();
return LauncherAPIHolder.profile().changeCurrentProfile(profile) return downloadDir(profile.getDir(), profile.getClientUpdateMatcher(), settings.view, callback).thenCompose((clientDir -> {
.thenCompose(vv -> downloadDir(profile.getDir(), profile.getClientUpdateMatcher(), settings.view, callback)).thenCompose((clientDir -> {
clientRef.set(clientDir); clientRef.set(clientDir);
return downloadAsset(profile.getAssetDir(), profile.getAssetUpdateMatcher(), profile.getAssetIndex(), callback); return downloadAsset(profile.getAssetDir(), profile.getAssetUpdateMatcher(), profile.getAssetIndex(), callback);
})).thenCompose(assetDir -> { })).thenCompose(assetDir -> {
@ -54,7 +53,7 @@ CompletableFuture<LauncherBackendAPI.ReadyProfile> downloadProfile(ClientProfile
CompletableFuture<DownloadedDir> downloadAsset(String dirName, FileNameMatcher matcher, String assetIndexString, LauncherBackendAPI.DownloadCallback callback) { CompletableFuture<DownloadedDir> downloadAsset(String dirName, FileNameMatcher matcher, String assetIndexString, LauncherBackendAPI.DownloadCallback callback) {
Path targetDir = DirBridge.dirUpdates.resolve(dirName); Path targetDir = DirBridge.dirUpdates.resolve(dirName);
Path assetIndexPath = targetDir.resolve("indexes").resolve(assetIndexString+".json"); Path assetIndexPath = targetDir.resolve("indexes").resolve(assetIndexString);
return LauncherAPIHolder.profile().fetchUpdateInfo(dirName).thenComposeAsync((response) -> { return LauncherAPIHolder.profile().fetchUpdateInfo(dirName).thenComposeAsync((response) -> {
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_ASSET_VERIFY); callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_ASSET_VERIFY);
return verifyAssetIndex(assetIndexString, response, assetIndexPath, targetDir); return verifyAssetIndex(assetIndexString, response, assetIndexPath, targetDir);
@ -117,7 +116,7 @@ CompletableFuture<DownloadedDir> downloadDir(Path targetDir, ProfileFeatureAPI.U
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_HASHING); callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_HASHING);
HashedDir realFiles = new HashedDir(targetDir, matcher, false, true); HashedDir realFiles = new HashedDir(targetDir, matcher, false, true);
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_DIFF); callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_DIFF);
return updateInfo.getHashedDir().diff(realFiles, matcher); return realFiles.diff(updateInfo.getHashedDir(), matcher);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -166,12 +165,7 @@ private List<Downloader.SizedFile> collectFilesAndCreateDirectories(Path dir, Ha
List<Downloader.SizedFile> files = new ArrayList<>(); List<Downloader.SizedFile> files = new ArrayList<>();
mismatch.walk(File.separator, (path, name, entry) -> { mismatch.walk(File.separator, (path, name, entry) -> {
if(entry.getType() == HashedEntry.Type.DIR) { if(entry.getType() == HashedEntry.Type.DIR) {
var dirPath = dir.resolve(path); Files.createDirectory(dir.resolve(path));
if(!Files.exists(dirPath)) {
Files.createDirectory(dirPath);
} else if (!Files.isDirectory(dirPath)) {
throw new IOException(String.format("%s is not a directory", path));
}
return HashedDir.WalkAction.CONTINUE; return HashedDir.WalkAction.CONTINUE;
} }
String pathFixed = path.replace(File.separatorChar, '/'); String pathFixed = path.replace(File.separatorChar, '/');

View file

@ -16,7 +16,6 @@
import pro.gravit.launcher.core.backend.exceptions.LauncherBackendException; import pro.gravit.launcher.core.backend.exceptions.LauncherBackendException;
import pro.gravit.launcher.core.backend.extensions.Extension; import pro.gravit.launcher.core.backend.extensions.Extension;
import pro.gravit.launcher.runtime.NewLauncherSettings; import pro.gravit.launcher.runtime.NewLauncherSettings;
import pro.gravit.launcher.runtime.client.DirBridge;
import pro.gravit.launcher.runtime.client.ServerPinger; import pro.gravit.launcher.runtime.client.ServerPinger;
import pro.gravit.launcher.runtime.debug.DebugMain; import pro.gravit.launcher.runtime.debug.DebugMain;
import pro.gravit.launcher.runtime.managers.SettingsManager; import pro.gravit.launcher.runtime.managers.SettingsManager;
@ -74,7 +73,6 @@ private void doInit() throws Exception {
allSettings = settingsManager.getConfig(); allSettings = settingsManager.getConfig();
backendSettings = (BackendSettings) getUserSettings("backend", (k) -> new BackendSettings()); backendSettings = (BackendSettings) getUserSettings("backend", (k) -> new BackendSettings());
permissions = new ClientPermissions(); permissions = new ClientPermissions();
DirBridge.dirUpdates = DirBridge.defaultUpdatesDir;
} }
@Override @Override
@ -104,14 +102,6 @@ public CompletableFuture<LauncherInitData> init() {
}, executorService); }, executorService);
} }
public AuthFeatureAPI.AuthToken getAuthToken() {
return backendSettings.auth.toToken();
}
public AuthMethod getAuthMethod() {
return authMethod;
}
@Override @Override
public void selectAuthMethod(AuthMethod method) { public void selectAuthMethod(AuthMethod method) {
this.authMethod = method; this.authMethod = method;
@ -185,11 +175,9 @@ public ClientProfileSettings makeClientProfileSettings(ProfileFeatureAPI.ClientP
var settings = backendSettings.settings.get(profile.getUUID()); var settings = backendSettings.settings.get(profile.getUUID());
if(settings == null) { if(settings == null) {
settings = new ProfileSettingsImpl((ClientProfile) profile); settings = new ProfileSettingsImpl((ClientProfile) profile);
settings.backend = this;
settings.updateEnabledMods();
} else { } else {
settings = settings.copy(); settings = settings.copy();
//settings.initAfterGson((ClientProfile) profile, this); settings.initAfterGson((ClientProfile) profile, this);
} }
return settings; return settings;
} }
@ -223,7 +211,6 @@ public CompletableFuture<List<Java>> getAvailableJava() {
return e; return e;
}); });
} }
return availableJavasFuture;
} }
return CompletableFuture.completedFuture(availableJavas); return CompletableFuture.completedFuture(availableJavas);
} }

View file

@ -12,7 +12,6 @@
import pro.gravit.utils.helper.JavaHelper; import pro.gravit.utils.helper.JavaHelper;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutionException;
public class ProfileSettingsImpl implements LauncherBackendAPI.ClientProfileSettings { public class ProfileSettingsImpl implements LauncherBackendAPI.ClientProfileSettings {
transient ClientProfile profile; transient ClientProfile profile;
@ -121,30 +120,6 @@ public JavaHelper.JavaVersion getSelectedJava() {
return selectedJava; return selectedJava;
} }
@Override
public JavaHelper.JavaVersion getRecommendedJava() {
JavaHelper.JavaVersion result = null;
try {
for(var java : backend.getAvailableJava().get()) {
if(isRecommended(java)) {
return (JavaHelper.JavaVersion) java;
}
if(isCompatible(java)) {
if(result == null) {
result = (JavaHelper.JavaVersion) java;
continue;
}
if(result.getMajorVersion() < java.getMajorVersion()) {
result = (JavaHelper.JavaVersion) java;
}
}
}
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
return result;
}
@Override @Override
public void setSelectedJava(LauncherBackendAPI.Java java) { public void setSelectedJava(LauncherBackendAPI.Java java) {
selectedJava = (JavaHelper.JavaVersion) java; selectedJava = (JavaHelper.JavaVersion) java;
@ -163,7 +138,6 @@ public boolean isCompatible(LauncherBackendAPI.Java java) {
@Override @Override
public ProfileSettingsImpl copy() { public ProfileSettingsImpl copy() {
ProfileSettingsImpl cloned = new ProfileSettingsImpl(); ProfileSettingsImpl cloned = new ProfileSettingsImpl();
cloned.backend = backend;
cloned.profile = profile; cloned.profile = profile;
cloned.ram = new HashMap<>(ram); cloned.ram = new HashMap<>(ram);
cloned.flags = new HashSet<>(flags); cloned.flags = new HashSet<>(flags);
@ -181,9 +155,7 @@ public void updateEnabledMods() {
for(var e : view.enabled) { for(var e : view.enabled) {
enabled.add(e.name); enabled.add(e.name);
} }
if(selectedJava != null) { saveJavaPath = selectedJava.getPath().toAbsolutePath().toString();
saveJavaPath = selectedJava.getPath().toAbsolutePath().toString();
}
} }
public void initAfterGson(ClientProfile profile, LauncherBackendImpl backend) { public void initAfterGson(ClientProfile profile, LauncherBackendImpl backend) {

View file

@ -1,11 +1,9 @@
package pro.gravit.launcher.runtime.backend; package pro.gravit.launcher.runtime.backend;
import pro.gravit.launcher.base.Launcher; import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.base.events.request.AuthRequestEvent;
import pro.gravit.launcher.base.profiles.ClientProfile; import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launcher.base.profiles.ClientProfileBuilder; import pro.gravit.launcher.base.profiles.ClientProfileBuilder;
import pro.gravit.launcher.base.profiles.PlayerProfile; import pro.gravit.launcher.base.profiles.PlayerProfile;
import pro.gravit.launcher.core.api.LauncherAPIHolder;
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI; import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
import pro.gravit.launcher.core.backend.LauncherBackendAPI; import pro.gravit.launcher.core.backend.LauncherBackendAPI;
import pro.gravit.launcher.runtime.client.ClientLauncherProcess; import pro.gravit.launcher.runtime.client.ClientLauncherProcess;
@ -63,14 +61,9 @@ public void run(LauncherBackendAPI.RunCallback callback) {
builder.setUpdateExclusions(new ArrayList<>()); builder.setUpdateExclusions(new ArrayList<>());
profile = builder.createClientProfile(); profile = builder.createClientProfile();
} }
var java = settings.getSelectedJava(); process = new ClientLauncherProcess(clientDir.path(), assetDir.path(), settings.getSelectedJava(), clientDir.path().resolve("resourcepacks"),
if(java == null) {
java = settings.getRecommendedJava();
}
process = new ClientLauncherProcess(clientDir.path(), assetDir.path(), java, clientDir.path().resolve("resourcepacks"),
profile, new PlayerProfile(backend.getSelfUser()), settings.view, backend.getSelfUser().getAccessToken(), profile, new PlayerProfile(backend.getSelfUser()), settings.view, backend.getSelfUser().getAccessToken(),
clientDir.dir(), assetDir.dir(), javaDir == null ? null : javaDir.dir(), clientDir.dir(), assetDir.dir(), javaDir == null ? null : javaDir.dir());
new AuthRequestEvent.OAuthRequestEvent(backend.getAuthToken()), backend.getAuthMethod().getName());
process.params.ram = (int) (settings.getReservedMemoryBytes(LauncherBackendAPI.ClientProfileSettings.MemoryClass.TOTAL) >> 20); process.params.ram = (int) (settings.getReservedMemoryBytes(LauncherBackendAPI.ClientProfileSettings.MemoryClass.TOTAL) >> 20);
if (process.params.ram > 0) { if (process.params.ram > 0) {
process.jvmArgs.add("-Xms" + process.params.ram + 'M'); process.jvmArgs.add("-Xms" + process.params.ram + 'M');

View file

@ -2,7 +2,6 @@
import pro.gravit.launcher.base.Launcher; import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.base.LauncherConfig; import pro.gravit.launcher.base.LauncherConfig;
import pro.gravit.launcher.base.events.request.AuthRequestEvent;
import pro.gravit.launcher.client.ClientLauncherEntryPoint; import pro.gravit.launcher.client.ClientLauncherEntryPoint;
import pro.gravit.launcher.client.ClientParams; import pro.gravit.launcher.client.ClientParams;
import pro.gravit.launcher.runtime.LauncherEngine; import pro.gravit.launcher.runtime.LauncherEngine;
@ -71,12 +70,6 @@ public ClientLauncherProcess(Path clientDir, Path assetDir,
public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersion javaVersion, Path resourcePackDir, public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersion javaVersion, Path resourcePackDir,
ClientProfile profile, PlayerProfile playerProfile, OptionalView view, String accessToken, ClientProfile profile, PlayerProfile playerProfile, OptionalView view, String accessToken,
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) { HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
this(clientDir, assetDir, javaVersion, resourcePackDir, profile, playerProfile, view, accessToken, clientHDir, assetHDir, jvmHDir, null, null);
}
public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersion javaVersion, Path resourcePackDir,
ClientProfile profile, PlayerProfile playerProfile, OptionalView view, String accessToken,
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir, AuthRequestEvent.OAuthRequestEvent oAuthRequestEvent, String authId) {
this.javaVersion = javaVersion; this.javaVersion = javaVersion;
this.workDir = clientDir.toAbsolutePath(); this.workDir = clientDir.toAbsolutePath();
this.executeFile = IOHelper.resolveJavaBin(this.javaVersion.jvmDir); this.executeFile = IOHelper.resolveJavaBin(this.javaVersion.jvmDir);
@ -85,8 +78,6 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersi
this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString(); this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString();
this.params.assetDir = assetDir.toAbsolutePath().toString(); this.params.assetDir = assetDir.toAbsolutePath().toString();
this.params.timestamp = System.currentTimeMillis(); this.params.timestamp = System.currentTimeMillis();
this.params.oauth = oAuthRequestEvent;
this.params.authId = authId;
Path nativesPath; Path nativesPath;
if(profile.hasFlag(ClientProfile.CompatibilityFlags.LEGACY_NATIVES_DIR)) { if(profile.hasFlag(ClientProfile.CompatibilityFlags.LEGACY_NATIVES_DIR)) {
nativesPath = workDir.resolve("natives"); nativesPath = workDir.resolve("natives");
@ -129,8 +120,10 @@ private void applyClientProfile() {
if (params.ram > 0) { if (params.ram > 0) {
this.jvmArgs.add("-Xmx" + params.ram + 'M'); this.jvmArgs.add("-Xmx" + params.ram + 'M');
} }
this.params.oauth = Request.getOAuth();
if (this.params.oauth == null) { if (this.params.oauth == null) {
this.params.oauth = Request.getOAuth(); throw new UnsupportedOperationException("Legacy session not supported");
} else {
this.params.authId = Request.getAuthId(); this.params.authId = Request.getAuthId();
this.params.oauthExpiredTime = Request.getTokenExpiredTime(); this.params.oauthExpiredTime = Request.getTokenExpiredTime();
this.params.extendedTokens = Request.getExtendedTokens(); this.params.extendedTokens = Request.getExtendedTokens();
@ -149,7 +142,9 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
} }
//ADD CLASSPATH //ADD CLASSPATH
processArgs.add(JVMHelper.jvmProperty("java.library.path", this.params.nativesDir)); processArgs.add(JVMHelper.jvmProperty("java.library.path", this.params.nativesDir));
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) { 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) {
Set<Path> ignorePath = new HashSet<>(); Set<Path> ignorePath = new HashSet<>();
var moduleConf = params.profile.getModuleConf(); var moduleConf = params.profile.getModuleConf();
if(moduleConf != null) { if(moduleConf != null) {
@ -164,24 +159,6 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
processArgs.add("--add-modules"); processArgs.add("--add-modules");
processArgs.add(String.join(",", moduleConf.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) systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(ignorePath, workDir, params.actions, params.profile)
.map(Path::toString) .map(Path::toString)

View file

@ -141,6 +141,7 @@ public void cancel() {
} }
} }
tasks.clear(); tasks.clear();
executor.shutdownNow();
} }
public boolean isCanceled() { public boolean isCanceled() {

View file

@ -87,12 +87,6 @@ public OAuthRequestEvent(String accessToken, String refreshToken, long expire) {
this.expire = expire; this.expire = expire;
} }
public OAuthRequestEvent(AuthFeatureAPI.AuthToken token) {
this.accessToken = token.getAccessToken();
this.refreshToken = token.getRefreshToken();
this.expire = token.getExpire();
}
@Override @Override
public String getAccessToken() { public String getAccessToken() {
return accessToken; return accessToken;

View file

@ -414,7 +414,7 @@ public List<CompatibilityFlags> getFlags() {
} }
public enum ClassLoaderConfig { public enum ClassLoaderConfig {
LAUNCHER, MODULE, SYSTEM_ARGS AGENT, LAUNCHER, MODULE, SYSTEM_ARGS
} }
public enum CompatibilityFlags { public enum CompatibilityFlags {

View file

@ -1,7 +1,6 @@
package pro.gravit.launcher.base.request; package pro.gravit.launcher.base.request;
import pro.gravit.launcher.base.Launcher; import pro.gravit.launcher.base.Launcher;
import pro.gravit.launcher.base.profiles.ClientProfile;
import pro.gravit.launcher.base.request.auth.*; import pro.gravit.launcher.base.request.auth.*;
import pro.gravit.launcher.base.request.auth.password.*; import pro.gravit.launcher.base.request.auth.password.*;
import pro.gravit.launcher.base.request.update.ProfilesRequest; import pro.gravit.launcher.base.request.update.ProfilesRequest;
@ -90,8 +89,6 @@ private AuthRequest.AuthPasswordInterface convertAuthPassword(AuthMethodPassword
return new AuthCodePassword(oauth.redirectUrl()); return new AuthCodePassword(oauth.redirectUrl());
} else if(password instanceof AuthRequest.AuthPasswordInterface custom) { } else if(password instanceof AuthRequest.AuthPasswordInterface custom) {
return custom; return custom;
} else if(password == null) {
return null;
} }
else { else {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
@ -167,11 +164,6 @@ public CompletableFuture<List<ProfileFeatureAPI.ClientProfile>> getProfiles() {
return request.request(new ProfilesRequest()).thenApply(response -> (List) response.profiles); return request.request(new ProfilesRequest()).thenApply(response -> (List) response.profiles);
} }
@Override
public CompletableFuture<Void> changeCurrentProfile(ClientProfile profile) {
return request.request(new SetProfileRequest((pro.gravit.launcher.base.profiles.ClientProfile) profile)).thenApply(response -> null);
}
@Override @Override
public CompletableFuture<UpdateInfo> fetchUpdateInfo(String dirName) { public CompletableFuture<UpdateInfo> fetchUpdateInfo(String dirName) {
return request.request(new UpdateRequest(dirName)).thenApply(response -> new UpdateInfoData(response.hdir, response.url)); return request.request(new UpdateRequest(dirName)).thenApply(response -> new UpdateInfoData(response.hdir, response.url));

View file

@ -1,6 +1,7 @@
apply plugin: 'org.openjfx.javafxplugin' apply plugin: 'org.openjfx.javafxplugin'
String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper" String mainClassName = "pro.gravit.launcher.ClientLauncherWrapper"
String mainAgentName = "pro.gravit.launcher.LauncherAgent"
repositories { repositories {
maven { maven {
@ -13,6 +14,7 @@
jar { jar {
archiveClassifier.set('clean') archiveClassifier.set('clean')
manifest.attributes("Main-Class": mainClassName, manifest.attributes("Main-Class": mainClassName,
"Premain-Class": mainAgentName,
"Multi-Release": "true") "Multi-Release": "true")
} }

View file

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

View file

@ -0,0 +1,59 @@
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

@ -9,7 +9,6 @@
public interface ProfileFeatureAPI extends FeatureAPI { public interface ProfileFeatureAPI extends FeatureAPI {
CompletableFuture<List<ClientProfile>> getProfiles(); CompletableFuture<List<ClientProfile>> getProfiles();
CompletableFuture<Void> changeCurrentProfile(ClientProfile profile);
CompletableFuture<UpdateInfo> fetchUpdateInfo(String dirName); CompletableFuture<UpdateInfo> fetchUpdateInfo(String dirName);
interface UpdateInfo { interface UpdateInfo {

View file

@ -63,7 +63,6 @@ interface ClientProfileSettings {
void enableOptional(ProfileFeatureAPI.OptionalMod mod, ChangedOptionalStatusCallback callback); void enableOptional(ProfileFeatureAPI.OptionalMod mod, ChangedOptionalStatusCallback callback);
void disableOptional(ProfileFeatureAPI.OptionalMod mod, ChangedOptionalStatusCallback callback); void disableOptional(ProfileFeatureAPI.OptionalMod mod, ChangedOptionalStatusCallback callback);
Java getSelectedJava(); Java getSelectedJava();
Java getRecommendedJava();
void setSelectedJava(Java java); void setSelectedJava(Java java);
boolean isRecommended(Java java); boolean isRecommended(Java java);
boolean isCompatible(Java java); boolean isCompatible(Java java);

View file

@ -123,9 +123,6 @@ protected Class<?> findClass(String name) throws ClassNotFoundException {
@Override @Override
public String findLibrary(String name) { public String findLibrary(String name) {
if(nativePath == null) {
return null;
}
return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION); return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION);
} }

View file

@ -262,9 +262,6 @@ protected Class<?> findClass(String moduleName, String name) {
@Override @Override
public String findLibrary(String name) { public String findLibrary(String name) {
if(nativePath == null) {
return null;
}
return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION); return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION);
} }

Binary file not shown.

View file

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

7
gradlew vendored
View file

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

2
gradlew.bat vendored
View file

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

@ -1 +1 @@
Subproject commit 0d8cef927b1fda3097dc88c3adcffc4d0e33dd69 Subproject commit c5087d40c9dd5b065fccedeac21afb967a25e112

View file

@ -1,18 +1,18 @@
project.ext { project.ext {
verAsm = '9.7.1' verAsm = '9.7'
verNetty = '4.1.114.Final' verNetty = '4.1.111.Final'
verOshiCore = '6.6.5' verOshiCore = '6.6.1'
verJunit = '5.11.2' verJunit = '5.10.2'
verJansi = '2.4.1' verJansi = '2.4.1'
verJline = '3.26.3' verJline = '3.26.1'
verJwt = '0.12.6' verJwt = '0.12.5'
verGson = '2.11.0' verGson = '2.11.0'
verBcpkix = '1.78.1' verBcpkix = '1.78.1'
verSlf4j = '2.0.16' verSlf4j = '2.0.13'
verLog4j = '2.24.1' verLog4j = '2.23.1'
verMySQLConn = '9.0.0' verMySQLConn = '9.0.0'
verMariaDBConn = '3.4.1' verMariaDBConn = '3.4.0'
verPostgreSQLConn = '42.7.4' verPostgreSQLConn = '42.7.3'
verH2Conn = '2.3.232' verH2Conn = '2.2.224'
verProguard = '7.6.0' verProguard = '7.5.0'
} }