diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java index 595d1cdb..a117dd48 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java @@ -70,6 +70,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab /** * The path to the folder with updates/webroot */ + @Deprecated public final Path updatesDir; // Constant paths @@ -359,9 +360,7 @@ public void run() { syncProfilesDir(); // Sync updates dir - if (!IOHelper.isDir(updatesDir)) - Files.createDirectory(updatesDir); - updatesManager.readUpdatesDir(); + config.updatesProvider.syncInitially(); modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this)); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java index ac26c218..3db4c2be 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServerStarter.java @@ -16,6 +16,7 @@ import pro.gravit.launchserver.auth.profiles.ProfileProvider; import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.texture.TextureProvider; +import pro.gravit.launchserver.auth.updates.UpdatesProvider; import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; @@ -180,6 +181,7 @@ public static void registerAll() { OptionalTrigger.registerProviders(); MixProvider.registerProviders(); ProfileProvider.registerProviders(); + UpdatesProvider.registerProviders(); } private static void printExperimentalBranch() { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/HikariSQLSourceConfig.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/HikariSQLSourceConfig.java index 974ce17e..ba4924dc 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/HikariSQLSourceConfig.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/HikariSQLSourceConfig.java @@ -9,15 +9,22 @@ import java.util.function.Consumer; public class HikariSQLSourceConfig implements SQLSourceConfig { - private transient HikariDataSource dataSource; + private transient volatile HikariDataSource dataSource; private String dsClass; private Properties dsProps; private String driverClass; private String jdbcUrl; private String username; private String password; + private boolean initializeAtStart; public void init() { + if(initializeAtStart) { + initializeConnection(); + } + } + + private void initializeConnection() { if (dataSource != null) { return; } @@ -34,6 +41,11 @@ public void init() { @Override public Connection getConnection() throws SQLException { + if(dataSource == null && !initializeAtStart) { + synchronized (this) { + initializeConnection(); + } + } return dataSource.getConnection(); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java index 4e732542..bdb4a4c5 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AbstractSQLCoreProvider.java @@ -295,7 +295,9 @@ private SQLUser queryUser(String sql, String value) throws SQLException { user = constructUser(set); } } - user.permissions = requestPermissions(user.uuid.toString()); + if(user != null) { + user.permissions = requestPermissions(user.uuid.toString()); + } return user; } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java index af1045ca..85dbd336 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java @@ -44,6 +44,7 @@ public SQLSourceConfig getSQLConfig() { @Override public void init(LaunchServer server, AuthProviderPair pair) { super.init(server, pair); + logger.warn("Method 'mysql' deprecated and may be removed in future release. Please use new 'sql' method: https://gravitlauncher.com/auth"); String userInfoCols = makeUserCols(); String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey"; if (sqlFindHardwareByPublicKey == null) diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java index 98e36940..9b6122f8 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/PostgresSQLCoreProvider.java @@ -1,5 +1,7 @@ package pro.gravit.launchserver.auth.core; +import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.PostgreSQLSourceConfig; import pro.gravit.launchserver.auth.SQLSourceConfig; @@ -10,4 +12,10 @@ public class PostgresSQLCoreProvider extends AbstractSQLCoreProvider { public SQLSourceConfig getSQLConfig() { return postgresSQLHolder; } + + @Override + public void init(LaunchServer server, AuthProviderPair pair) { + super.init(server, pair); + logger.warn("Method 'postgresql' deprecated and may be removed in future release. Please use new 'sql' method: https://gravitlauncher.com/auth"); + } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/SQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/SQLCoreProvider.java index 49161f85..990840a0 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/SQLCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/SQLCoreProvider.java @@ -1,11 +1,27 @@ package pro.gravit.launchserver.auth.core; +import pro.gravit.launcher.base.request.secure.HardwareReportRequest; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.HikariSQLSourceConfig; +import pro.gravit.launchserver.auth.MySQLSourceConfig; import pro.gravit.launchserver.auth.SQLSourceConfig; +import pro.gravit.launchserver.auth.core.interfaces.UserHardware; +import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportExtendedCheckServer; +import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware; +import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportHardware; +import pro.gravit.launchserver.socket.Client; +import 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; @Override @@ -14,14 +30,364 @@ public void close() { holder.close(); } - @Override - public void init(LaunchServer server, AuthProviderPair pair) { - holder.init(); - super.init(server, pair); - } - @Override public SQLSourceConfig getSQLConfig() { return holder; } + + + public String hardwareIdColumn; + public String tableHWID = "hwids"; + public String tableHWIDLog = "hwidLog"; + public double criticalCompareLevel = 1.0; + private transient String sqlFindHardwareByPublicKey; + private transient String sqlFindHardwareByData; + private transient String sqlFindHardwareById; + private transient String sqlCreateHardware; + private transient String sqlCreateHWIDLog; + private transient String sqlUpdateHardwarePublicKey; + private transient String sqlUpdateHardwareBanned; + private transient String sqlUpdateUsers; + private transient String sqlUsersByHwidId; + + @Override + public void init(LaunchServer server, AuthProviderPair pair) { + holder.init(); + super.init(server, pair); + String userInfoCols = makeUserCols(); + String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey"; + if (sqlFindHardwareByPublicKey == null) + sqlFindHardwareByPublicKey = "SELECT %s FROM %s WHERE publicKey = ?".formatted(hardwareInfoCols, tableHWID); + if (sqlFindHardwareById == null) + sqlFindHardwareById = "SELECT %s FROM %s WHERE id = ?".formatted(hardwareInfoCols, tableHWID); + if (sqlUsersByHwidId == null) + sqlUsersByHwidId = "SELECT %s FROM %s WHERE %s = ?".formatted(userInfoCols, table, hardwareIdColumn); + if (sqlFindHardwareByData == null) + sqlFindHardwareByData = "SELECT %s FROM %s".formatted(hardwareInfoCols, tableHWID); + if (sqlCreateHardware == null) + sqlCreateHardware = "INSERT INTO %s (publickey, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, graphicCard, battery, banned) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '0')".formatted(tableHWID); + if (sqlCreateHWIDLog == null) + sqlCreateHWIDLog = "INSERT INTO %s (hwidId, newPublicKey) VALUES (?, ?)".formatted(tableHWIDLog); + if (sqlUpdateHardwarePublicKey == null) + sqlUpdateHardwarePublicKey = "UPDATE %s SET publicKey = ? WHERE id = ?".formatted(tableHWID); + sqlUpdateHardwareBanned = "UPDATE %s SET banned = ? WHERE id = ?".formatted(tableHWID); + sqlUpdateUsers = "UPDATE %s SET %s = ? WHERE %s = ?".formatted(table, hardwareIdColumn, uuidColumn); + } + + @Override + protected String makeUserCols() { + return super.makeUserCols().concat(", ").concat(hardwareIdColumn); + } + + @Override + protected SQLUser constructUser(ResultSet set) throws SQLException { + return set.next() ? new SQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn), + set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), set.getLong(hardwareIdColumn)) : null; + } + + private SQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException, 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 getUsersByHardwareInfo(UserHardware hardware) { + List 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 + + '}'; + } + } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/LocalProfileProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/LocalProfileProvider.java index 5c3725ea..c938a1f8 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/LocalProfileProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/profiles/LocalProfileProvider.java @@ -45,8 +45,7 @@ public void addProfile(ClientProfile profile) throws IOException { } } if(target == null) { - target = IOHelper.resolveIncremental(profilesDirPath, - profile.getTitle(), "json"); + target = profilesDirPath.resolve(profile.getTitle()+".json"); oldProfile = profilesMap.get(target); if(oldProfile != null && !oldProfile.getUUID().equals(profile.getUUID())) { throw new FileAlreadyExistsException(target.toString()); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/LocalUpdatesProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/LocalUpdatesProvider.java new file mode 100644 index 00000000..581b35ac --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/LocalUpdatesProvider.java @@ -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 updatesDirMap; + + private void writeCache(Path file) throws IOException { + try (HOutput output = new HOutput(IOHelper.newOutput(file))) { + output.writeLength(updatesDirMap.size(), 0); + for (Map.Entry 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 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 dirs) throws IOException { + logger.info("Syncing updates dir"); + Map newUpdatesDirMap = new HashMap<>(16); + try (DirectoryStream 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 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 download(String updateName, List files) { + var path = resolveUpdateName(updateName); + Map map = new HashMap<>(); + for(var e : files) { + map.put(e, path.resolve(e)); + } + return map; + } + + @Override + public void delete(String updateName, List 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); + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/UpdatesProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/UpdatesProvider.java new file mode 100644 index 00000000..f7677ea8 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/updates/UpdatesProvider.java @@ -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 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 updateNames) throws IOException; + + public abstract HashedDir getUpdatesDir(String updateName); + + public abstract void upload(String updateName, Map files, boolean deleteAfterUpload) throws IOException; + + public abstract Map download(String updateName, List files); + + public abstract void delete(String updateName, List files) throws IOException; + + public abstract void delete(String updateName) throws IOException; + + public abstract void create(String updateName) throws IOException; + + public void close() { + + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/BinaryPipeline.java b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/BinaryPipeline.java index 86fbf9c3..c735d0e0 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/BinaryPipeline.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/BinaryPipeline.java @@ -14,11 +14,11 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -public class BinaryPipeline { +public abstract class BinaryPipeline { public final List tasks = new ArrayList<>(); public final Path buildDir; public final String nameFormat; - private transient final Logger logger = LogManager.getLogger(); + protected transient final Logger logger = LogManager.getLogger(); public BinaryPipeline(Path buildDir, String nameFormat) { this.buildDir = buildDir; @@ -80,27 +80,6 @@ public Optional getTaskBefore(Predicate pr return Optional.empty(); } - public void build(Path target, boolean deleteTempFiles) throws IOException { - logger.info("Building launcher binary file"); - Path thisPath = null; - long time_start = System.currentTimeMillis(); - long time_this = time_start; - for (LauncherBuildTask task : tasks) { - logger.info("Task {}", task.getName()); - Path oldPath = thisPath; - thisPath = task.process(oldPath); - long time_task_end = System.currentTimeMillis(); - long time_task = time_task_end - time_this; - time_this = time_task_end; - logger.info("Task {} processed from {} millis", task.getName(), time_task); - } - long time_end = System.currentTimeMillis(); - if (deleteTempFiles) IOHelper.move(thisPath, target); - else IOHelper.copy(thisPath, target); - IOHelper.deleteDir(buildDir, false); - logger.info("Build successful from {} millis", time_end - time_start); - } - public String nextName(String taskName) { return nameFormat.formatted(taskName); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/LauncherBinary.java b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/LauncherBinary.java index a4ba76c0..a336c439 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/LauncherBinary.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/LauncherBinary.java @@ -1,11 +1,14 @@ package pro.gravit.launchserver.binary; import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.binary.tasks.LauncherBuildTask; import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.SecurityHelper; import java.io.IOException; import java.nio.file.Path; +import java.util.List; +import java.util.Map; public abstract class LauncherBinary extends BinaryPipeline { public final LaunchServer server; @@ -19,11 +22,27 @@ protected LauncherBinary(LaunchServer server, Path binaryFile, String nameFormat } public static Path resolve(LaunchServer server, String ext) { - return server.config.copyBinaries ? server.updatesDir.resolve(server.config.binaryName + ext) : server.dir.resolve(server.config.binaryName + ext); + return Path.of(server.config.binaryName + ext); } public void build() throws IOException { - build(syncBinaryFile, server.config.launcher.deleteTempFiles); + logger.info("Building launcher binary file"); + Path thisPath = null; + long time_start = System.currentTimeMillis(); + long time_this = time_start; + for (LauncherBuildTask task : tasks) { + logger.info("Task {}", task.getName()); + Path oldPath = thisPath; + thisPath = task.process(oldPath); + long time_task_end = System.currentTimeMillis(); + long time_task = time_task_end - time_this; + time_this = time_task_end; + logger.info("Task {} processed from {} millis", task.getName(), time_task); + } + long time_end = System.currentTimeMillis(); + server.config.updatesProvider.upload(null, Map.of(syncBinaryFile.toString(), thisPath), true); + IOHelper.deleteDir(buildDir, false); + logger.info("Build successful from {} millis", time_end - time_start); } public final boolean exists() { @@ -38,9 +57,13 @@ public void init() { } public final boolean sync() throws IOException { - boolean exists = exists(); - digest = exists ? SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(syncBinaryFile)) : null; - - return exists; + try { + var target = syncBinaryFile.toString(); + var path = server.config.updatesProvider.download(null, List.of(target)).get(target); + digest = SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA512, IOHelper.read(path)); + return true; + } catch (Throwable e) { + return false; + } } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/PrepareBuildTask.java b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/PrepareBuildTask.java index e509edf3..753f48c5 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/PrepareBuildTask.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/PrepareBuildTask.java @@ -7,10 +7,7 @@ import pro.gravit.utils.helper.UnpackHelper; import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; +import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; import java.util.stream.Collectors; @@ -40,7 +37,7 @@ public Path process(Path inputFile) throws IOException { if(Files.isDirectory(server.launcherLibrariesCompile)) { IOHelper.walk(server.launcherLibrariesCompile, new ListFileVisitor(server.launcherBinary.addonLibs), false); } - try(Stream stream = Files.walk(server.launcherPack).filter((e) -> { + try(Stream stream = Files.walk(server.launcherPack, FileVisitOption.FOLLOW_LINKS).filter((e) -> { try { return !Files.isDirectory(e) && !Files.isHidden(e); } catch (IOException ex) { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/DeleteProfileCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/DeleteProfileCommand.java index 034d791b..f6a36659 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/DeleteProfileCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/profiles/DeleteProfileCommand.java @@ -40,14 +40,13 @@ public void invoke(String... args) throws Exception { logger.error("Profile {} not found", args[0]); return; } - var clientDir = server.updatesDir.resolve(profile.getDir()).toAbsolutePath(); - logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", clientDir); + logger.warn("THIS ACTION DELETE PROFILE AND ALL FILES IN {}", profile.getDir()); if(!showApplyDialog("Continue?")) { return; } logger.info("Delete {} ({})", profile.getTitle(), profile.getUUID()); server.config.profileProvider.deleteProfile(profile); - logger.info("Delete {}", clientDir); - IOHelper.deleteDir(clientDir, true); + logger.info("Delete {}", profile.getDir()); + server.config.updatesProvider.delete(profile.getDir()); } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java b/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java index 562460c7..f2121ba7 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java @@ -14,6 +14,8 @@ import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.protect.StdProtectHandler; import pro.gravit.launchserver.auth.texture.RequestTextureProvider; +import pro.gravit.launchserver.auth.updates.LocalUpdatesProvider; +import pro.gravit.launchserver.auth.updates.UpdatesProvider; import pro.gravit.launchserver.components.AuthLimiterComponent; import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.components.ProGuardComponent; @@ -32,13 +34,13 @@ public final class LaunchServerConfig { public String[] mirrors; public String binaryName; public boolean copyBinaries = true; - public boolean cacheUpdates = true; public LauncherConfig.LauncherEnvironment env; public Map auth; // Handlers & Providers public ProtectHandler protectHandler; public Map components; public ProfileProvider profileProvider = new LocalProfileProvider(); + public UpdatesProvider updatesProvider = new LocalUpdatesProvider(); public NettyConfig netty; public LauncherConf launcher; public JarSignerConf sign; @@ -174,6 +176,10 @@ public void init(LaunchServer.ReloadType type) { server.registerObject("profileProvider", profileProvider); profileProvider.init(server); } + if(updatesProvider != null) { + server.registerObject("updatesProvider", updatesProvider); + updatesProvider.init(server); + } if (components != null) { components.forEach((k, v) -> server.registerObject("component.".concat(k), v)); } @@ -218,6 +224,10 @@ public void close(LaunchServer.ReloadType type) { server.unregisterObject("profileProvider", profileProvider); profileProvider.close(); } + if(updatesProvider != null) { + server.unregisterObject("updatesProvider", updatesProvider); + updatesProvider.close(); + } } public static class JarSignerConf { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java b/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java index 5b139d12..5b375301 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java @@ -197,9 +197,6 @@ public static String getMainClassByVersion(ClientProfile.Version version, MakePr if(version.compareTo(ClientProfileVersions.MINECRAFT_1_7_10) == 0) { return "com.gtnewhorizons.retrofuturabootstrap.Main"; } - if(version.compareTo(ClientProfileVersions.MINECRAFT_1_12_2) == 0) { - return "top.outlands.foundation.boot.Foundation"; // Cleanroom - } if (findOption(options, MakeProfileOptionLaunchWrapper.class).isPresent()) { return "net.minecraft.launchwrapper.Launch"; } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java index 74a6ba98..4970e4f0 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/LaunchServerGsonManager.java @@ -17,6 +17,7 @@ import pro.gravit.launchserver.auth.profiles.ProfileProvider; import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.texture.TextureProvider; +import pro.gravit.launchserver.auth.updates.UpdatesProvider; import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; import pro.gravit.launchserver.socket.WebSocketService; @@ -48,6 +49,7 @@ public void registerAdapters(GsonBuilder builder) { builder.registerTypeAdapter(OptionalTrigger.class, new UniversalJsonAdapter<>(OptionalTrigger.providers)); builder.registerTypeAdapter(MixProvider.class, new UniversalJsonAdapter<>(MixProvider.providers)); builder.registerTypeAdapter(ProfileProvider.class, new UniversalJsonAdapter<>(ProfileProvider.providers)); + builder.registerTypeAdapter(UpdatesProvider.class, new UniversalJsonAdapter<>(UpdatesProvider.providers)); modulesManager.invokeEvent(new PreGsonPhase(builder)); //ClientWebSocketService.appendTypeAdapters(builder); } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java index c2642d2e..717c65a7 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/UpdatesManager.java @@ -18,112 +18,38 @@ public class UpdatesManager { private final LaunchServer server; - private final Logger logger = LogManager.getLogger(); - private final Path cacheFile; - private volatile Map updatesDirMap; public UpdatesManager(LaunchServer server) { this.server = server; - this.cacheFile = server.dir.resolve(".updates-cache"); - } - - private void writeCache(Path file) throws IOException { - try (HOutput output = new HOutput(IOHelper.newOutput(file))) { - output.writeLength(updatesDirMap.size(), 0); - for (Map.Entry 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 updatesDirMap = new HashMap<>(16); - try (HInput input = new HInput(IOHelper.newInput(file))) { - int size = input.readLength(0); - for (int i = 0; i < size; ++i) { - String name = input.readString(0); - HashedDir dir = new HashedDir(input); - updatesDirMap.put(name, dir); - } - } - logger.debug("Found {} updates from cache", updatesDirMap.size()); - this.updatesDirMap = Collections.unmodifiableMap(updatesDirMap); } + @Deprecated public void readUpdatesFromCache() throws IOException { - readCache(cacheFile); + } + @Deprecated public void readUpdatesDir() throws IOException { - if (server.config.cacheUpdates) { - if (Files.exists(cacheFile)) { - try { - readCache(cacheFile); - return; - } catch (Throwable e) { - logger.error("Read updates cache failed", e); - } - } - } - syncUpdatesDir(null); + } + @Deprecated public void syncUpdatesDir(Collection dirs) throws IOException { - logger.info("Syncing updates dir"); - Map newUpdatesDirMap = new HashMap<>(16); - try (DirectoryStream dirStream = Files.newDirectoryStream(server.updatesDir)) { - for (final Path updateDir : dirStream) { - if (Files.isHidden(updateDir)) - continue; // Skip hidden - - // Resolve name and verify is dir - String name = IOHelper.getFileName(updateDir); - if (!IOHelper.isDir(updateDir)) { - if (!IOHelper.isFile(updateDir) && Stream.of(".jar", ".exe", ".hash").noneMatch(e -> updateDir.toString().endsWith(e))) - logger.warn("Not update dir: '{}'", name); - continue; - } - - // Add from previous map (it's guaranteed to be non-null) - if (dirs != null && !dirs.contains(name)) { - HashedDir hdir = updatesDirMap.get(name); - if (hdir != null) { - newUpdatesDirMap.put(name, hdir); - continue; - } - } - - // Sync and sign update dir - logger.info("Syncing '{}' update dir", name); - HashedDir updateHDir = new HashedDir(updateDir, null, true, true); - newUpdatesDirMap.put(name, updateHDir); - } - } - updatesDirMap = Collections.unmodifiableMap(newUpdatesDirMap); - if (server.config.cacheUpdates) { - try { - writeCache(cacheFile); - } catch (Throwable e) { - logger.error("Write updates cache failed", e); - } - } - server.modulesManager.invokeEvent(new LaunchServerUpdatesSyncEvent(server)); + server.config.updatesProvider.sync(dirs); } + @Deprecated public HashSet getUpdatesList() { - HashSet set = new HashSet<>(); - for (Map.Entry entry : updatesDirMap.entrySet()) - set.add(entry.getKey()); - return set; + return new HashSet<>(); } + @Deprecated public HashedDir getUpdate(String name) { - return updatesDirMap.get(name); + return server.config.updatesProvider.getUpdatesDir(name); } + @Deprecated public void addUpdate(String name, HashedDir dir) { - updatesDirMap.put(name, dir); + throw new UnsupportedOperationException(); } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ProfilesResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ProfilesResponse.java index 39058b76..6e5be16a 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ProfilesResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/ProfilesResponse.java @@ -13,6 +13,7 @@ import java.util.Set; public class ProfilesResponse extends SimpleResponse { + @Deprecated public static List getListVisibleProfiles(LaunchServer server, Client client) { List profileList; Set serverProfiles = server.getProfiles(); @@ -40,6 +41,6 @@ public void execute(ChannelHandlerContext ctx, Client client) { sendError("Access denied"); return; } - sendResult(new ProfilesRequestEvent(getListVisibleProfiles(server, client))); + sendResult(new ProfilesRequestEvent(server.config.profileProvider.getProfiles(client))); } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/LauncherResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/LauncherResponse.java index 05e6c958..1e2285f1 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/LauncherResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/update/LauncherResponse.java @@ -52,7 +52,7 @@ public void execute(ChannelHandlerContext ctx, Client client) { client.checkSign = true; sendResult(new LauncherRequestEvent(false, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); } else { - sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); + sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, null, 0)); } } else if (launcher_type == 2) //EXE { @@ -62,7 +62,7 @@ public void execute(ChannelHandlerContext ctx, Client client) { client.checkSign = true; sendResult(new LauncherRequestEvent(false, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); } else { - sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000)); + sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, null, 0)); } } else sendError("Request launcher type error"); } diff --git a/Launcher/build.gradle b/Launcher/build.gradle index a2253345..42ed13a7 100644 --- a/Launcher/build.gradle +++ b/Launcher/build.gradle @@ -1,7 +1,6 @@ apply plugin: 'com.github.johnrengelman.shadow' String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper" -String mainAgentName = "pro.gravit.launcher.runtime.LauncherAgent" repositories { maven { @@ -20,7 +19,6 @@ jar { archiveClassifier.set('clean') manifest.attributes("Main-Class": mainClassName, - "Premain-Class": mainAgentName, "Multi-Release": "true", "Automatic-Module-Name": "GravitLauncher") } diff --git a/Launcher/src/main/java/pro/gravit/launcher/runtime/LauncherEngine.java b/Launcher/src/main/java/pro/gravit/launcher/runtime/LauncherEngine.java index 7574ab79..a752b47b 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/runtime/LauncherEngine.java +++ b/Launcher/src/main/java/pro/gravit/launcher/runtime/LauncherEngine.java @@ -130,7 +130,6 @@ public static void main(String... args) throws Throwable { LogHelper.printLicense("Launcher"); LauncherEngine.checkClass(LauncherEngineWrapper.class); LauncherEngine.checkClass(LauncherEngine.class); - LauncherEngine.checkClass(LauncherAgent.class); LauncherEngine.checkClass(ClientLauncherEntryPoint.class); LauncherEngine.modulesManager = new RuntimeModuleManager(); LauncherEngine.modulesManager.loadModule(new RuntimeLauncherCoreModule()); diff --git a/Launcher/src/main/java/pro/gravit/launcher/runtime/client/ClientLauncherProcess.java b/Launcher/src/main/java/pro/gravit/launcher/runtime/client/ClientLauncherProcess.java index 22b1b843..dc1b5bcd 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/runtime/client/ClientLauncherProcess.java +++ b/Launcher/src/main/java/pro/gravit/launcher/runtime/client/ClientLauncherProcess.java @@ -142,9 +142,7 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException { } //ADD CLASSPATH processArgs.add(JVMHelper.jvmProperty("java.library.path", this.params.nativesDir)); - if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.AGENT) { - processArgs.add("-javaagent:".concat(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString())); - } else if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) { + if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) { Set ignorePath = new HashSet<>(); var moduleConf = params.profile.getModuleConf(); if(moduleConf != null) { @@ -159,6 +157,24 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException { processArgs.add("--add-modules"); processArgs.add(String.join(",", moduleConf.modules)); } + if(moduleConf.exports != null && !moduleConf.exports.isEmpty()) { + for(var e : moduleConf.exports.entrySet()) { + processArgs.add("--add-exports"); + processArgs.add(String.format("%s=%s", e.getKey(), e.getValue())); + } + } + if(moduleConf.opens != null && !moduleConf.opens.isEmpty()) { + for(var e : moduleConf.opens.entrySet()) { + processArgs.add("--add-opens"); + processArgs.add(String.format("%s=%s", e.getKey(), e.getValue())); + } + } + if(moduleConf.reads != null && !moduleConf.reads.isEmpty()) { + for(var e : moduleConf.reads.entrySet()) { + processArgs.add("--add-reads"); + processArgs.add(String.format("%s=%s", e.getKey(), e.getValue())); + } + } } systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(ignorePath, workDir, params.actions, params.profile) .map(Path::toString) diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/base/Downloader.java b/LauncherAPI/src/main/java/pro/gravit/launcher/base/Downloader.java index 1f8f27e9..980411d6 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/base/Downloader.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/base/Downloader.java @@ -141,7 +141,6 @@ public void cancel() { } } tasks.clear(); - executor.shutdownNow(); } public boolean isCanceled() { diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/base/profiles/ClientProfile.java b/LauncherAPI/src/main/java/pro/gravit/launcher/base/profiles/ClientProfile.java index ebfb7c87..802caba0 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/base/profiles/ClientProfile.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/base/profiles/ClientProfile.java @@ -414,7 +414,7 @@ public List getFlags() { } public enum ClassLoaderConfig { - AGENT, LAUNCHER, MODULE, SYSTEM_ARGS + LAUNCHER, MODULE, SYSTEM_ARGS } public enum CompatibilityFlags { diff --git a/LauncherClient/build.gradle b/LauncherClient/build.gradle index 9a59ce29..78ad4310 100644 --- a/LauncherClient/build.gradle +++ b/LauncherClient/build.gradle @@ -1,7 +1,6 @@ apply plugin: 'org.openjfx.javafxplugin' -String mainClassName = "pro.gravit.launcher.ClientLauncherWrapper" -String mainAgentName = "pro.gravit.launcher.LauncherAgent" +String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper" repositories { maven { @@ -14,7 +13,6 @@ jar { archiveClassifier.set('clean') manifest.attributes("Main-Class": mainClassName, - "Premain-Class": mainAgentName, "Multi-Release": "true") } diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java index d084f9d5..6aad0739 100644 --- a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java +++ b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java @@ -86,11 +86,9 @@ private static void realMain(String[] args) throws Throwable { modulesManager.invokeEvent(new PreConfigPhase()); LogHelper.debug("Reading ClientLauncher params"); ClientParams params = readParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort)); - if (params.profile.getClassLoaderConfig() != ClientProfile.ClassLoaderConfig.AGENT) { - ClientLauncherMethods.verifyNoAgent(); - } + ClientLauncherMethods.verifyNoAgent(); if(params.timestamp > System.currentTimeMillis() || params.timestamp + 30*1000 < System.currentTimeMillis() ) { - LogHelper.error("Timestamp failed. Exit"); + LogHelper.error("Timestamp failed: current %d | params %d | diff %d", System.currentTimeMillis(), params.timestamp, System.currentTimeMillis() - params.timestamp); ClientLauncherMethods.exitLauncher(-662); return; } @@ -160,7 +158,7 @@ private static void realMain(String[] args) throws Throwable { System.load(Paths.get(params.nativesDir).resolve(ClientService.findLibrary(e)).toAbsolutePath().toString()); } } - if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) { + if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER || classLoaderConfig == ClientProfile.ClassLoaderConfig.MODULE) { if(JVMHelper.JVM_VERSION <= 11) { launch = new LegacyLaunch(); } else { @@ -170,20 +168,12 @@ private static void realMain(String[] args) throws Throwable { System.setProperty("java.class.path", classpath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator))); modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, classLoaderControl, profile)); ClientService.baseURLs = classLoaderControl.getURLs(); - } else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.AGENT) { - launch = new BasicLaunch(LauncherAgent.inst); - classpathURLs.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toUri().toURL()); - classLoaderControl = launch.init(classpath, params.nativesDir, options); - for (URL url : classpathURLs) { - LauncherAgent.addJVMClassPath(Paths.get(url.toURI())); - } - ClientService.instrumentation = LauncherAgent.inst; - modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, null, profile)); - ClientService.baseURLs = classpathURLs.toArray(new URL[0]); } else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) { launch = new BasicLaunch(); classLoaderControl = launch.init(classpath, params.nativesDir, options); ClientService.baseURLs = classpathURLs.toArray(new URL[0]); + } else { + throw new UnsupportedOperationException(String.format("Unknown classLoaderConfig %s", classLoaderConfig)); } if(profile.hasFlag(ClientProfile.CompatibilityFlags.CLASS_CONTROL_API)) { ClientService.classLoaderControl = classLoaderControl; diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/client/LauncherAgent.java b/LauncherClient/src/main/java/pro/gravit/launcher/client/LauncherAgent.java deleted file mode 100644 index 0683af97..00000000 --- a/LauncherClient/src/main/java/pro/gravit/launcher/client/LauncherAgent.java +++ /dev/null @@ -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; - } -} diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java index a7511ad9..7be6eda4 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java @@ -123,6 +123,9 @@ protected Class findClass(String name) throws ClassNotFoundException { @Override public String findLibrary(String name) { + if(nativePath == null) { + return null; + } return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION); } diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java index d83e3c44..ca1e8bb7 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java @@ -262,6 +262,9 @@ protected Class findClass(String moduleName, String name) { @Override public String findLibrary(String name) { + if(nativePath == null) { + return null; + } return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION); } diff --git a/build.gradle b/build.gradle index a658cec2..1a1f5a77 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ id 'org.openjfx.javafxplugin' version '0.1.0' apply false } group = 'pro.gravit.launcher' -version = '5.7.0-SNAPSHOT' +version = '5.6.8' apply from: 'props.gradle' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136..a4b76b95 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e7646dea..79eb9d00 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a42..f5feea6d 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 25da30db..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## diff --git a/modules b/modules index c5087d40..0d8cef92 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit c5087d40c9dd5b065fccedeac21afb967a25e112 +Subproject commit 0d8cef927b1fda3097dc88c3adcffc4d0e33dd69 diff --git a/props.gradle b/props.gradle index af811c06..99b9aa98 100644 --- a/props.gradle +++ b/props.gradle @@ -1,18 +1,18 @@ project.ext { - verAsm = '9.7' - verNetty = '4.1.111.Final' - verOshiCore = '6.6.1' - verJunit = '5.10.2' + verAsm = '9.7.1' + verNetty = '4.1.114.Final' + verOshiCore = '6.6.5' + verJunit = '5.11.2' verJansi = '2.4.1' - verJline = '3.26.1' - verJwt = '0.12.5' + verJline = '3.26.3' + verJwt = '0.12.6' verGson = '2.11.0' verBcpkix = '1.78.1' - verSlf4j = '2.0.13' - verLog4j = '2.23.1' + verSlf4j = '2.0.16' + verLog4j = '2.24.1' verMySQLConn = '9.0.0' - verMariaDBConn = '3.4.0' - verPostgreSQLConn = '42.7.3' - verH2Conn = '2.2.224' - verProguard = '7.5.0' + verMariaDBConn = '3.4.1' + verPostgreSQLConn = '42.7.4' + verH2Conn = '2.3.232' + verProguard = '7.6.0' }