Compare commits

...

47 commits

Author SHA1 Message Date
Gravita
f1559183a1
[FIX} New API bug fixes 2024-11-08 15:33:53 +07:00
Gravita
c90a13af71
Merge with dev 2024-11-08 07:37:17 +07:00
Gravita
912caa6b8a
[FIX] Add more logging to timestamp failed error 2024-11-05 08:52:59 +07:00
Gravita
926094076c
[FIX] Fix NPE with nativePath PR #719 2024-11-05 07:08:31 +07:00
Gravita
a1af61a599
[ANY] Update modules 2024-10-22 17:14:36 +07:00
Gravita
81b16cb54e
[ANY] Update gradle 2024-10-15 18:53:03 +07:00
Gravita
a486f21fa2
[ANY] Update dependencies 2024-10-15 18:48:20 +07:00
Gravita
86ea247f07
Merge tag 'v5.6.8' into dev
v5.6.8 stable
2024-10-13 19:19:00 +07:00
Gravita
d26b179006
Merge branch 'release/5.6.8' 2024-10-13 19:18:50 +07:00
Gravita
c4d1251429
[ANY] 5.6.8 stable 2024-10-13 19:05:35 +07:00
Gravita
f16f5fbc6d
[FIX] NPE: User not found and permissions enabled 2024-10-06 22:59:49 +07:00
Gravita
abe904d73c
[FEATURE] ExtendedCheckServer support in SQLAuthCoreProvider 2024-10-02 20:50:36 +07:00
Gravita
eaf685897f
Merge tag 'v5.6.7' into dev
5.6.7 stable release
2024-09-28 20:31:33 +07:00
Gravita
c8934d887a
Merge branch 'release/5.6.7' 2024-09-28 20:31:23 +07:00
Gravita
070a5d9b69
[ANY] 5.6.7 release 2024-09-28 20:31:10 +07:00
Gravita
cc2bce4300
[FIX] initializeAtStart thread safety 2024-09-28 02:29:19 +07:00
Gravita
c7f4d8ac49
[FEATURE] Support initializeAtStart 2024-09-28 02:27:42 +07:00
Gravita
7dcb08fdaf
[FEATURE] Support HWID in SQLCoreProvider 2024-09-28 01:47:55 +07:00
Gravita
9d870849a1
[ANY] Update dependencies 2024-09-18 16:36:15 +07:00
microwin7
dda3ebc7b4 [FIX] RejectedExecutionException after cancelled Download task 2024-08-23 22:02:13 +03:00
Gravita
537623afaf
Merge tag 'v5.6.6' into dev
5.6.6 stable
2024-08-18 23:57:14 +07:00
Gravita
0cff6e247a
Merge branch 'release/5.6.6' 2024-08-18 23:57:08 +07:00
Gravita
b85075c559
[ANY] 5.6.6 Stable 2024-08-18 23:57:00 +07:00
Gravita
3e654f4d79
[FEATURE] Add support opens, exports, reads to SYSTEM_CLASS_LOADER. Remove AGENT 2024-08-18 23:10:58 +07:00
Gravita
46f1f7b69e
[FIX] Symlink in launcher-pack 2024-08-18 19:18:53 +07:00
microwin7
981f2ac3dd [ANY] Update modules and update version oshi 2024-08-18 01:00:08 +03:00
microwin7
8509cbb6b5 [ANY] Update modules 2024-08-14 00:13:45 +03:00
microwin7
1119625d12 [ANY] Update modules 2024-08-13 05:44:32 +03:00
microwin7
9a69426547 [ANY] Update modules 2024-08-12 07:03:17 +03:00
microwin7
31cbfe2919 [ANY][FEATURE] Update modules. Removed mainClass for Cleanroom
In MakeProfileHelper
2024-08-12 06:01:08 +03:00
microwin7
27ebadcd19 [ANY] Update modules 2024-08-12 02:00:49 +03:00
Antoni
b3349044b5
[ANY] Update patch Version and Type release 2024-08-07 01:27:18 +03:00
Antoni
c43edeb982
[FIX] UpdatesProvider register 2024-08-06 03:25:02 +03:00
Gravita
1ff58099bd
[FIX] UpdatesProvider register 2024-08-06 06:19:12 +07:00
Gravita
9fba637f83
[FIX] Launcher update token 2024-08-05 05:36:53 +07:00
Gravita
ca70ee78d1
[FIX] Launcher update 2024-08-05 05:34:45 +07:00
Gravita
5e116a81e5
[ANY] Update modules 2024-08-05 03:25:05 +07:00
Gravita
b0f799d194
[FIX] UpdatesProvider init 2024-08-05 03:24:42 +07:00
Gravita
63f9f8e21d
[FEATURE] UpdatesProvider Part 2 2024-07-31 00:36:04 +07:00
Gravita
b8ccbc5e48
[FEATURE] UpdatesProvider Part 1 2024-07-31 00:15:08 +07:00
Gravita
a687c5afd8
[ANY] Update gradle 2024-07-30 23:20:35 +07:00
Gravita
3969d81db7
[ANY] Update modules 2024-07-30 23:06:54 +07:00
Gravita
a30d0624a1
[ANY] Update modules 2024-07-30 21:05:37 +07:00
Gravita
d5abe0d411
[ANY] Update modules 2024-07-30 17:34:20 +07:00
Gravita
1e7a856a99
[FIX] Profile duplicate 2024-07-30 15:43:22 +07:00
Gravita
e6f5b585a7
[ANY] Update modules 2024-07-30 15:33:46 +07:00
Gravita
2ed4abf9b0
[FIX] Usage profilesProvider 2024-07-26 01:27:01 +07:00
45 changed files with 867 additions and 257 deletions

View file

@ -70,6 +70,7 @@ 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
@ -359,9 +360,7 @@ public void run() {
syncProfilesDir(); syncProfilesDir();
// Sync updates dir // Sync updates dir
if (!IOHelper.isDir(updatesDir)) config.updatesProvider.syncInitially();
Files.createDirectory(updatesDir);
updatesManager.readUpdatesDir();
modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this)); modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this));

View file

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

View file

@ -45,8 +45,7 @@ public void addProfile(ClientProfile profile) throws IOException {
} }
} }
if(target == null) { if(target == null) {
target = IOHelper.resolveIncremental(profilesDirPath, target = profilesDirPath.resolve(profile.getTitle()+".json");
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

@ -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.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class BinaryPipeline { public abstract 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;
private transient final Logger logger = LogManager.getLogger(); protected 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,27 +80,6 @@ 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,11 +1,14 @@
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;
@ -19,11 +22,27 @@ 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 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 { 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() { public final boolean exists() {
@ -38,9 +57,13 @@ public void init() {
} }
public final boolean sync() throws IOException { public final boolean sync() throws IOException {
boolean exists = exists(); try {
digest = exists ? SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(syncBinaryFile)) : null; var target = syncBinaryFile.toString();
var path = server.config.updatesProvider.download(null, List.of(target)).get(target);
return exists; 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 pro.gravit.utils.helper.UnpackHelper;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileVisitResult; import java.nio.file.*;
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;
@ -40,7 +37,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).filter((e) -> { try(Stream<Path> stream = Files.walk(server.launcherPack, FileVisitOption.FOLLOW_LINKS).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,14 +40,13 @@ public void invoke(String... args) throws Exception {
logger.error("Profile {} not found", args[0]); logger.error("Profile {} not found", args[0]);
return; return;
} }
var clientDir = server.updatesDir.resolve(profile.getDir()).toAbsolutePath(); logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", profile.getDir());
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 {}", clientDir); logger.info("Delete {}", profile.getDir());
IOHelper.deleteDir(clientDir, true); server.config.updatesProvider.delete(profile.getDir());
} }
} }

View file

@ -14,6 +14,8 @@
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;
@ -32,13 +34,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;
@ -174,6 +176,10 @@ 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));
} }
@ -218,6 +224,10 @@ 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,9 +197,6 @@ 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,6 +17,7 @@
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;
@ -48,6 +49,7 @@ 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,112 +18,38 @@
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 {
logger.info("Syncing updates dir"); server.config.updatesProvider.sync(dirs);
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() {
HashSet<String> set = new HashSet<>(); return 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 updatesDirMap.get(name); return server.config.updatesProvider.getUpdatesDir(name);
} }
@Deprecated
public void addUpdate(String name, HashedDir dir) { public void addUpdate(String name, HashedDir dir) {
updatesDirMap.put(name, dir); throw new UnsupportedOperationException();
} }
} }

View file

@ -13,6 +13,7 @@
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();
@ -40,6 +41,6 @@ public void execute(ChannelHandlerContext ctx, Client client) {
sendError("Access denied"); sendError("Access denied");
return; 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; 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, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, null, 0));
} }
} 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, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, null, 0));
} }
} else sendError("Request launcher type error"); } else sendError("Request launcher type error");
} }

View file

@ -1,7 +1,6 @@
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 {
@ -20,7 +19,6 @@
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,7 +130,6 @@ 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,6 +1,7 @@
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;
@ -19,5 +20,24 @@ 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,7 +37,8 @@ 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 downloadDir(profile.getDir(), profile.getClientUpdateMatcher(), settings.view, callback).thenCompose((clientDir -> { return LauncherAPIHolder.profile().changeCurrentProfile(profile)
.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 -> {
@ -53,7 +54,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); Path assetIndexPath = targetDir.resolve("indexes").resolve(assetIndexString+".json");
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);
@ -116,7 +117,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 realFiles.diff(updateInfo.getHashedDir(), matcher); return updateInfo.getHashedDir().diff(realFiles, matcher);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -165,7 +166,12 @@ 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) {
Files.createDirectory(dir.resolve(path)); var dirPath = 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,6 +16,7 @@
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;
@ -73,6 +74,7 @@ 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
@ -102,6 +104,14 @@ 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;
@ -175,9 +185,11 @@ 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;
} }
@ -211,6 +223,7 @@ public CompletableFuture<List<Java>> getAvailableJava() {
return e; return e;
}); });
} }
return availableJavasFuture;
} }
return CompletableFuture.completedFuture(availableJavas); return CompletableFuture.completedFuture(availableJavas);
} }

View file

@ -12,6 +12,7 @@
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;
@ -120,6 +121,30 @@ 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;
@ -138,6 +163,7 @@ 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);
@ -155,8 +181,10 @@ 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) {
this.backend = backend; this.backend = backend;

View file

@ -1,9 +1,11 @@
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;
@ -61,9 +63,14 @@ public void run(LauncherBackendAPI.RunCallback callback) {
builder.setUpdateExclusions(new ArrayList<>()); builder.setUpdateExclusions(new ArrayList<>());
profile = builder.createClientProfile(); profile = builder.createClientProfile();
} }
process = new ClientLauncherProcess(clientDir.path(), assetDir.path(), settings.getSelectedJava(), clientDir.path().resolve("resourcepacks"), var java = settings.getSelectedJava();
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,6 +2,7 @@
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;
@ -70,6 +71,12 @@ 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);
@ -78,6 +85,8 @@ 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");
@ -120,10 +129,8 @@ 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) {
throw new UnsupportedOperationException("Legacy session not supported"); this.params.oauth = Request.getOAuth();
} 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();
@ -142,9 +149,7 @@ 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.AGENT) { if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
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) {
@ -159,6 +164,24 @@ 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,7 +141,6 @@ public void cancel() {
} }
} }
tasks.clear(); tasks.clear();
executor.shutdownNow();
} }
public boolean isCanceled() { public boolean isCanceled() {

View file

@ -87,6 +87,12 @@ 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 {
AGENT, LAUNCHER, MODULE, SYSTEM_ARGS LAUNCHER, MODULE, SYSTEM_ARGS
} }
public enum CompatibilityFlags { public enum CompatibilityFlags {

View file

@ -1,6 +1,7 @@
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;
@ -89,6 +90,8 @@ 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();
@ -164,6 +167,11 @@ 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,7 +1,6 @@
apply plugin: 'org.openjfx.javafxplugin' apply plugin: 'org.openjfx.javafxplugin'
String mainClassName = "pro.gravit.launcher.ClientLauncherWrapper" String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper"
String mainAgentName = "pro.gravit.launcher.LauncherAgent"
repositories { repositories {
maven { maven {
@ -14,7 +13,6 @@
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,11 +86,9 @@ 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));
if (params.profile.getClassLoaderConfig() != ClientProfile.ClassLoaderConfig.AGENT) {
ClientLauncherMethods.verifyNoAgent(); 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. Exit"); LogHelper.error("Timestamp failed: current %d | params %d | diff %d", System.currentTimeMillis(), params.timestamp, System.currentTimeMillis() - params.timestamp);
ClientLauncherMethods.exitLauncher(-662); ClientLauncherMethods.exitLauncher(-662);
return; return;
} }
@ -160,7 +158,7 @@ private static void realMain(String[] args) throws Throwable {
System.load(Paths.get(params.nativesDir).resolve(ClientService.findLibrary(e)).toAbsolutePath().toString()); 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) { if(JVMHelper.JVM_VERSION <= 11) {
launch = new LegacyLaunch(); launch = new LegacyLaunch();
} else { } 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))); 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

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

@ -9,6 +9,7 @@
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,6 +63,7 @@ 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,6 +123,9 @@ 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,6 +262,9 @@ 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.7-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

7
gradlew vendored
View file

@ -15,6 +15,8 @@
# 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
#
############################################################################## ##############################################################################
# #
@ -55,7 +57,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/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. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -84,7 +86,8 @@ 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 "${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. # 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,6 +13,8 @@
@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 c5087d40c9dd5b065fccedeac21afb967a25e112 Subproject commit 0d8cef927b1fda3097dc88c3adcffc4d0e33dd69

View file

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