mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-22 16:41:46 +03:00
Merge branch 'release/5.3.0'
This commit is contained in:
commit
07be86f695
67 changed files with 1056 additions and 473 deletions
|
@ -7,9 +7,13 @@
|
|||
import pro.gravit.launcher.events.request.GetAvailabilityAuthRequestEvent;
|
||||
import pro.gravit.launcher.profiles.Texture;
|
||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||
import pro.gravit.launcher.request.secure.HardwareReportRequest;
|
||||
import pro.gravit.launchserver.HttpRequester;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.auth.AuthException;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportHardware;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportProperties;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
||||
import pro.gravit.launchserver.helper.HttpHelper;
|
||||
|
@ -19,12 +23,9 @@
|
|||
import pro.gravit.utils.helper.CommonHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
public class HttpAuthCoreProvider extends AuthCoreProvider {
|
||||
public class HttpAuthCoreProvider extends AuthCoreProvider implements AuthSupportHardware {
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
private transient HttpRequester requester;
|
||||
public String bearerToken;
|
||||
|
@ -32,12 +33,23 @@ public class HttpAuthCoreProvider extends AuthCoreProvider {
|
|||
public String getUserByLoginUrl;
|
||||
public String getUserByUUIDUrl;
|
||||
public String getUserByTokenUrl;
|
||||
public String getAuthDetails;
|
||||
public String getAuthDetailsUrl;
|
||||
public String refreshTokenUrl;
|
||||
public String authorizeUrl;
|
||||
public String joinServerUrl;
|
||||
public String checkServerUrl;
|
||||
public String updateServerIdUrl;
|
||||
//below fields can be empty if advanced protect handler disabled
|
||||
public String getHardwareInfoByPublicKeyUrl;
|
||||
public String getHardwareInfoByDataUrl;
|
||||
public String getHardwareInfoByIdUrl;
|
||||
public String createHardwareInfoUrl;
|
||||
public String connectUserAndHardwareUrl;
|
||||
public String addPublicKeyToHardwareInfoUrl;
|
||||
public String getUsersByHardwareInfoUrl;
|
||||
public String banHardwareUrl;
|
||||
public String unbanHardwareUrl;
|
||||
|
||||
@Override
|
||||
public User getUserByUsername(String username) {
|
||||
try {
|
||||
|
@ -73,11 +85,11 @@ public User getUserByUUID(UUID uuid) {
|
|||
|
||||
@Override
|
||||
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
||||
if(getAuthDetails == null) {
|
||||
if(getAuthDetailsUrl == null) {
|
||||
return super.getDetails(client);
|
||||
}
|
||||
try {
|
||||
var result = requester.send(requester.get(getAuthDetails, bearerToken), GetAuthDetailsResponse.class).getOrThrow();
|
||||
var result = requester.send(requester.get(getAuthDetailsUrl, bearerToken), GetAuthDetailsResponse.class).getOrThrow();
|
||||
return result.details;
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
|
@ -113,7 +125,7 @@ public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthRespon
|
|||
}
|
||||
try {
|
||||
return requester.send(requester.post(refreshTokenUrl, new RefreshTokenRequest(refreshToken, context),
|
||||
null), AuthManager.AuthReport.class).getOrThrow();
|
||||
null), HttpAuthReport.class).getOrThrow().toAuthReport();
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
return null;
|
||||
|
@ -133,6 +145,128 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
|
|||
return result.getOrThrow().toAuthReport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
|
||||
if(getHardwareInfoByPublicKeyUrl == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return requester.send(requester.post(getHardwareInfoByPublicKeyUrl, new HardwareRequest(publicKey),
|
||||
bearerToken), HttpUserHardware.class).getOrThrow();
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info) {
|
||||
if(getHardwareInfoByDataUrl == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
HardwareRequest request = new HardwareRequest(new HttpUserHardware(info));
|
||||
HttpHelper.HttpOptional<HttpUserHardware, HttpRequester.SimpleError> hardware =
|
||||
requester.send(requester.post(getHardwareInfoByDataUrl, request,
|
||||
bearerToken), HttpUserHardware.class);
|
||||
//should return null if not found
|
||||
return hardware.isSuccessful() ? hardware.getOrThrow() : null;
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware getHardwareInfoById(String id) {
|
||||
if(getHardwareInfoByIdUrl == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return requester.send(requester.post(getHardwareInfoByIdUrl, new HardwareRequest(new HttpUserHardware(Long.parseLong(id))),
|
||||
bearerToken), HttpUserHardware.class).getOrThrow();
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
logger.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo info, byte[] publicKey) {
|
||||
if(createHardwareInfoUrl == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return requester.send(requester.post(createHardwareInfoUrl, new HardwareRequest(new HttpUserHardware(info,
|
||||
publicKey, false)), bearerToken), HttpUserHardware.class).getOrThrow();
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectUserAndHardware(UserSession userSession, UserHardware hardware) {
|
||||
if(connectUserAndHardwareUrl == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
requester.send(requester.post(connectUserAndHardwareUrl, new HardwareRequest((HttpUserHardware) hardware, (HttpUserSession) userSession), bearerToken), Void.class);
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey) {
|
||||
if(addPublicKeyToHardwareInfoUrl == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
requester.send(requester.post(addPublicKeyToHardwareInfoUrl, new HardwareRequest((HttpUserHardware)hardware, publicKey), bearerToken), Void.class);
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<User> getUsersByHardwareInfo(UserHardware hardware) {
|
||||
if(getUsersByHardwareInfoUrl == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return requester.send(requester
|
||||
.post(getUsersByHardwareInfoUrl, new HardwareRequest((HttpUserHardware) hardware), bearerToken), List.class).getOrThrow();
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void banHardware(UserHardware hardware) {
|
||||
if(banHardwareUrl == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
requester.send(requester.post(banHardwareUrl, new HardwareRequest((HttpUserHardware) hardware), bearerToken), Void.class);
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbanHardware(UserHardware hardware) {
|
||||
if(unbanHardwareUrl == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
requester.send(requester.post(unbanHardwareUrl, new HardwareRequest((HttpUserHardware) hardware), bearerToken), Void.class);
|
||||
} catch (IOException e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
public record HttpAuthReport(String minecraftAccessToken, String oauthAccessToken,
|
||||
String oauthRefreshToken, long oauthExpire,
|
||||
HttpUserSession session) {
|
||||
|
@ -246,7 +380,27 @@ public RefreshTokenRequest(String refreshToken, AuthResponse.AuthContext context
|
|||
}
|
||||
}
|
||||
|
||||
public static class HttpUser implements User, UserSupportTextures, UserSupportProperties {
|
||||
public record HardwareRequest(HttpUserHardware userHardware, byte[] key, HttpUserSession userSession) {
|
||||
|
||||
public HardwareRequest(HttpUserHardware userHardware) {
|
||||
this(userHardware, null, null);
|
||||
}
|
||||
|
||||
public HardwareRequest(HttpUserHardware userHardware, byte[] key) {
|
||||
this(userHardware, key, null);
|
||||
}
|
||||
|
||||
public HardwareRequest(HttpUserHardware userHardware, HttpUserSession userSession) {
|
||||
this(userHardware, null, userSession);
|
||||
}
|
||||
|
||||
public HardwareRequest(byte[] key) {
|
||||
this(null, key, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class HttpUser implements User, UserSupportTextures, UserSupportProperties, UserSupportHardware {
|
||||
private String username;
|
||||
private UUID uuid;
|
||||
private String serverId;
|
||||
|
@ -258,19 +412,22 @@ public static class HttpUser implements User, UserSupportTextures, UserSupportPr
|
|||
private Texture cloak;
|
||||
private Map<String, Texture> assets;
|
||||
private Map<String, String> properties;
|
||||
private long hwidId;
|
||||
private transient HttpUserHardware hardware;
|
||||
|
||||
public HttpUser() {
|
||||
}
|
||||
|
||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions) {
|
||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, long hwidId) {
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.serverId = serverId;
|
||||
this.accessToken = accessToken;
|
||||
this.permissions = permissions;
|
||||
this.hwidId = hwidId;
|
||||
}
|
||||
|
||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak) {
|
||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, long hwidId) {
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.serverId = serverId;
|
||||
|
@ -278,9 +435,10 @@ public HttpUser(String username, UUID uuid, String serverId, String accessToken,
|
|||
this.permissions = permissions;
|
||||
this.skin = skin;
|
||||
this.cloak = cloak;
|
||||
this.hwidId = hwidId;
|
||||
}
|
||||
|
||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, Map<String, String> properties) {
|
||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, Map<String, String> properties, long hwidId) {
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.serverId = serverId;
|
||||
|
@ -289,9 +447,10 @@ public HttpUser(String username, UUID uuid, String serverId, String accessToken,
|
|||
this.skin = skin;
|
||||
this.cloak = cloak;
|
||||
this.properties = properties;
|
||||
this.hwidId = hwidId;
|
||||
}
|
||||
|
||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Map<String, Texture> assets, Map<String, String> properties) {
|
||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Map<String, Texture> assets, Map<String, String> properties, long hwidId) {
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
this.serverId = serverId;
|
||||
|
@ -299,6 +458,7 @@ public HttpUser(String username, UUID uuid, String serverId, String accessToken,
|
|||
this.permissions = permissions;
|
||||
this.assets = assets;
|
||||
this.properties = properties;
|
||||
this.hwidId = hwidId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -374,8 +534,17 @@ public String toString() {
|
|||
", permissions=" + permissions +
|
||||
", assets=" + getAssets() +
|
||||
", properties=" + properties +
|
||||
", hwidId=" + hwidId +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserHardware getHardware() {
|
||||
if (hardware != null) return hardware;
|
||||
HttpAuthCoreProvider.HttpUserHardware result = (HttpUserHardware) getHardwareInfoById(String.valueOf(hwidId));
|
||||
hardware = result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class HttpUserSession implements UserSession {
|
||||
|
@ -416,4 +585,65 @@ public String toString() {
|
|||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
public static class HttpUserHardware implements UserHardware {
|
||||
private final HardwareReportRequest.HardwareInfo hardwareInfo;
|
||||
private final long id;
|
||||
private byte[] publicKey;
|
||||
private boolean banned;
|
||||
|
||||
public HttpUserHardware(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, long id, boolean banned) {
|
||||
this.hardwareInfo = hardwareInfo;
|
||||
this.publicKey = publicKey;
|
||||
this.id = id;
|
||||
this.banned = banned;
|
||||
}
|
||||
|
||||
public HttpUserHardware(HardwareReportRequest.HardwareInfo hardwareInfo) {
|
||||
this.hardwareInfo = hardwareInfo;
|
||||
this.id = Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
public HttpUserHardware(HardwareReportRequest.HardwareInfo hardwareInfo, byte[] publicKey, boolean banned) {
|
||||
this.hardwareInfo = hardwareInfo;
|
||||
this.publicKey = publicKey;
|
||||
this.banned = banned;
|
||||
this.id = Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
public HttpUserHardware(long id) {
|
||||
this.id = id;
|
||||
this.hardwareInfo = null;
|
||||
}
|
||||
|
||||
@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 "HttpUserHardware{" +
|
||||
"hardwareInfo=" + hardwareInfo +
|
||||
", publicKey=" + (publicKey == null ? null : new String(Base64.getEncoder().encode(publicKey))) +
|
||||
", id=" + id +
|
||||
", banned=" + banned +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package pro.gravit.launchserver.auth.core.interfaces.session;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
public interface UserSessionSupportKeys {
|
||||
ClientProfileKeys getClientProfileKeys();
|
||||
record ClientProfileKeys(PublicKey publicKey, PrivateKey privateKey, byte[] signature /* V2 */, long expiresAt, long refreshedAfter) {
|
||||
|
||||
}
|
||||
}
|
|
@ -122,7 +122,7 @@ public String createHardwareToken(String username, UserHardware hardware) {
|
|||
return Jwts.builder()
|
||||
.setIssuer("LaunchServer")
|
||||
.setSubject(username)
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 8))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 1000 * server.config.netty.security.hardwareTokenExpire))
|
||||
.claim("hardware", hardware.getId())
|
||||
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
|
||||
.compact();
|
||||
|
@ -132,7 +132,7 @@ public String createPublicKeyToken(String username, byte[] publicKey) {
|
|||
return Jwts.builder()
|
||||
.setIssuer("LaunchServer")
|
||||
.setSubject(username)
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 8))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 1000 * server.config.netty.security.publicKeyTokenExpire))
|
||||
.claim("publicKey", Base64.getEncoder().encodeToString(publicKey))
|
||||
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
|
||||
.compact();
|
||||
|
|
|
@ -124,9 +124,8 @@ protected void initProps() {
|
|||
server.runtime.clientCheckSecret.concat(".").concat(launcherSalt));
|
||||
properties.put("runtimeconfig.secureCheckHash", Base64.getEncoder().encodeToString(launcherSecureHash));
|
||||
properties.put("runtimeconfig.secureCheckSalt", launcherSalt);
|
||||
//LogHelper.debug("[checkSecure] %s: %s", launcherSalt, Arrays.toString(launcherSecureHash));
|
||||
if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken();
|
||||
properties.put("runtimeconfig.oemUnlockKey", server.runtime.oemUnlockKey);
|
||||
if (server.runtime.unlockSecret == null) server.runtime.unlockSecret = SecurityHelper.randomStringToken();
|
||||
properties.put("runtimeconfig.unlockSecret", server.runtime.unlockSecret);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
import java.nio.file.Path;
|
||||
|
||||
public class Launch4JTask implements LauncherBuildTask, BuildExeMainTask {
|
||||
public static final String DOWNLOAD_URL = "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"; // Oracle
|
||||
public static final String DOWNLOAD_URL = "https://bell-sw.com/pages/downloads/?version=java-8-lts&os=Windows&package=jre-full"; // BellSoft
|
||||
private static final String VERSION = Version.getVersion().getVersionString();
|
||||
private static final int BUILD = Version.getVersion().build;
|
||||
private final Path faviconFile;
|
||||
|
|
|
@ -1,25 +1,40 @@
|
|||
package pro.gravit.launchserver.command.hash;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.AsyncDownloader;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launchserver.HttpRequester;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.command.Command;
|
||||
import pro.gravit.utils.Downloader;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import proguard.OutputWriter;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class DownloadAssetCommand extends Command {
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
|
||||
private static final String MINECRAFT_VERSIONS_URL = "https://launchermeta.mojang.com/mc/game/version_manifest.json";
|
||||
|
||||
private static final String RESOURCES_DOWNLOAD_URL = "https://resources.download.minecraft.net/";
|
||||
|
||||
public DownloadAssetCommand(LaunchServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[version] [dir]";
|
||||
return "[version] [dir] (mojang/mirror)";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,20 +47,94 @@ public void invoke(String... args) throws Exception {
|
|||
verifyArgs(args, 2);
|
||||
//Version version = Version.byName(args[0]);
|
||||
String versionName = args[0];
|
||||
String type = args.length > 2 ? args[2] : "mojang";
|
||||
String dirName = IOHelper.verifyFileName(args[1]);
|
||||
Path assetDir = server.updatesDir.resolve(dirName);
|
||||
|
||||
// Create asset dir
|
||||
logger.info("Creating asset dir: '{}'", dirName);
|
||||
Files.createDirectory(assetDir);
|
||||
if(Files.notExists(assetDir)) {
|
||||
logger.info("Creating asset dir: '{}'", dirName);
|
||||
Files.createDirectory(assetDir);
|
||||
}
|
||||
|
||||
// Download required asset
|
||||
logger.info("Downloading asset, it may take some time");
|
||||
//HttpDownloader.downloadZip(server.mirrorManager.getDefaultMirror().getAssetsURL(version.name), assetDir);
|
||||
server.mirrorManager.downloadZip(assetDir, "assets/%s.zip", versionName);
|
||||
if(type.equals("mojang")) {
|
||||
HttpRequester requester = new HttpRequester();
|
||||
logger.info("Fetch versions from {}", MINECRAFT_VERSIONS_URL);
|
||||
var versions = requester.send(requester.get(MINECRAFT_VERSIONS_URL, null), MinecraftVersions.class).getOrThrow();
|
||||
String profileUrl = null;
|
||||
for(var e : versions.versions) {
|
||||
if(e.id.equals(versionName)) {
|
||||
profileUrl = e.url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(profileUrl == null) {
|
||||
logger.error("Version {} not found", versionName);
|
||||
return;
|
||||
}
|
||||
logger.info("Fetch profile {} from {}", versionName, profileUrl);
|
||||
var profileInfo = requester.send(requester.get(profileUrl, null), MiniVersion.class).getOrThrow();
|
||||
String assetsIndexUrl = profileInfo.assetIndex.url;
|
||||
String assetIndex = profileInfo.assetIndex.id;
|
||||
Path indexPath = assetDir.resolve("indexes").resolve(assetIndex+".json");
|
||||
logger.info("Fetch asset index {} from {}", assetIndex, assetsIndexUrl);
|
||||
JsonObject assets = requester.send(requester.get(assetsIndexUrl, null), JsonObject.class).getOrThrow();
|
||||
JsonObject objects = assets.get("objects").getAsJsonObject();
|
||||
try(Writer writer = IOHelper.newWriter(indexPath)) {
|
||||
logger.info("Save {}", indexPath);
|
||||
Launcher.gsonManager.configGson.toJson(assets, writer);
|
||||
}
|
||||
if(!assetIndex.equals(versionName)) {
|
||||
Path targetPath = assetDir.resolve("indexes").resolve(versionName+".json");
|
||||
logger.info("Copy {} into {}", indexPath, targetPath);
|
||||
Files.copy(indexPath, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
List<AsyncDownloader.SizedFile> toDownload = new ArrayList<>(128);
|
||||
for(var e : objects.entrySet()) {
|
||||
var value = e.getValue().getAsJsonObject();
|
||||
var hash = value.get("hash").getAsString();
|
||||
hash = hash.substring(0, 2) + "/" + hash;
|
||||
var size = value.get("size").getAsLong();
|
||||
var path = "objects/" + hash;
|
||||
var target = assetDir.resolve(path);
|
||||
if(Files.exists(target)) {
|
||||
long fileSize = Files.size(target);
|
||||
if(fileSize != size) {
|
||||
logger.warn("File {} corrupted. Size {}, expected {}", target, size, fileSize);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
toDownload.add(new AsyncDownloader.SizedFile(hash, path, size));
|
||||
}
|
||||
logger.info("Download {} files", toDownload.size());
|
||||
Downloader downloader = downloadWithProgressBar(dirName, toDownload, RESOURCES_DOWNLOAD_URL, assetDir);
|
||||
downloader.getFuture().get();
|
||||
} else {
|
||||
// Download required asset
|
||||
logger.info("Downloading asset, it may take some time");
|
||||
//HttpDownloader.downloadZip(server.mirrorManager.getDefaultMirror().getAssetsURL(version.name), assetDir);
|
||||
server.mirrorManager.downloadZip(assetDir, "assets/%s.zip", versionName);
|
||||
}
|
||||
|
||||
// Finished
|
||||
server.syncUpdatesDir(Collections.singleton(dirName));
|
||||
logger.info("Asset successfully downloaded: '{}'", dirName);
|
||||
}
|
||||
|
||||
public record MiniVersionInfo(String id, String url) {
|
||||
|
||||
}
|
||||
|
||||
public record MinecraftVersions(List<MiniVersionInfo> versions) {
|
||||
|
||||
}
|
||||
|
||||
public record MinecraftAssetIndexInfo(String id, String url) {
|
||||
|
||||
}
|
||||
|
||||
public record MiniVersion(MinecraftAssetIndexInfo assetIndex) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public void invoke(String... args) throws IOException, CommandException {
|
|||
if (version.compareTo(ClientProfile.Version.MC164) <= 0) {
|
||||
logger.warn("Minecraft 1.6.4 and below not supported. Use at your own risk");
|
||||
}
|
||||
MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(clientDir, version);
|
||||
MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(clientDir, version, Files.exists(server.updatesDir.resolve("assets")));
|
||||
for (MakeProfileHelper.MakeProfileOption option : options) {
|
||||
logger.debug("Detected option {}", option.getClass().getSimpleName());
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class MakeProfileCommand extends Command {
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
|
@ -32,7 +33,7 @@ public String getUsageDescription() {
|
|||
public void invoke(String... args) throws Exception {
|
||||
verifyArgs(args, 3);
|
||||
ClientProfile.Version version = ClientProfile.Version.byName(args[1]);
|
||||
MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(server.updatesDir.resolve(args[2]), version);
|
||||
MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(server.updatesDir.resolve(args[2]), version, Files.exists(server.updatesDir.resolve("assets")));
|
||||
for (MakeProfileHelper.MakeProfileOption option : options) {
|
||||
logger.info("Detected option {}", option);
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ public Path process(Path inputFile) throws IOException {
|
|||
if (component.enabled) {
|
||||
Configuration proguard_cfg = new Configuration();
|
||||
if (!checkJMods(IOHelper.JVM_DIR.resolve("jmods"))) {
|
||||
logger.error("Java path: {} is not JDK! Please install JDK", IOHelper.JVM_DIR);
|
||||
throw new RuntimeException(String.format("Java path: %s is not JDK! Please install JDK", IOHelper.JVM_DIR));
|
||||
}
|
||||
Path jfxPath = tryFindOpenJFXPath(IOHelper.JVM_DIR);
|
||||
if (checkFXJMods(IOHelper.JVM_DIR.resolve("jmods"))) {
|
||||
|
@ -143,8 +143,7 @@ public Path process(Path inputFile) throws IOException {
|
|||
} else if (jfxPath != null && checkFXJMods(jfxPath)) {
|
||||
logger.debug("JMods resolved in {}", jfxPath.toString());
|
||||
} else {
|
||||
logger.error("JavaFX jmods not found. May be install OpenJFX?");
|
||||
jfxPath = null;
|
||||
throw new RuntimeException("JavaFX jmods not found. May be install OpenJFX?");
|
||||
}
|
||||
ConfigurationParser parser = new ConfigurationParser(proguardConf.buildConfig(inputFile, outputJar, jfxPath == null ? new Path[0] : new Path[]{jfxPath}),
|
||||
proguardConf.proguard.toFile(), System.getProperties());
|
||||
|
|
|
@ -45,9 +45,9 @@ public final class LaunchServerConfig {
|
|||
|
||||
public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
|
||||
LaunchServerConfig newConfig = new LaunchServerConfig();
|
||||
newConfig.mirrors = new String[]{"https://mirror.gravit.pro/5.2.x/", "https://gravit-launcher-mirror.storage.googleapis.com/"};
|
||||
newConfig.mirrors = new String[]{"https://mirror.gravit.pro/5.3.x/", "https://gravit-launcher-mirror.storage.googleapis.com/"};
|
||||
newConfig.launch4j = new LaunchServerConfig.ExeConf();
|
||||
newConfig.launch4j.enabled = true;
|
||||
newConfig.launch4j.enabled = false;
|
||||
newConfig.launch4j.copyright = "© GravitLauncher Team";
|
||||
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
|
||||
newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch));
|
||||
|
@ -168,9 +168,9 @@ public void verify() {
|
|||
boolean updateMirror = Boolean.getBoolean("launchserver.config.disableUpdateMirror");
|
||||
if(!updateMirror) {
|
||||
for(int i=0;i < mirrors.length;++i) {
|
||||
if("https://mirror.gravit.pro/".equals(mirrors[i])) {
|
||||
logger.warn("Replace mirror 'https://mirror.gravit.pro/' to 'https://mirror.gravit.pro/5.2.x/'. If you really need to use original url, use '-Dlaunchserver.config.disableUpdateMirror=true'");
|
||||
mirrors[i] = "https://mirror.gravit.pro/5.2.x/";
|
||||
if("https://mirror.gravit.pro/5.2.x/".equals(mirrors[i])) {
|
||||
logger.warn("Replace mirror 'https://mirror.gravit.pro/5.2.x/' to 'https://mirror.gravit.pro/5.3.x/'. If you really need to use original url, use '-Dlaunchserver.config.disableUpdateMirror=true'");
|
||||
mirrors[i] = "https://mirror.gravit.pro/5.3.x/";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -278,8 +278,6 @@ public static class LauncherConf {
|
|||
|
||||
public static class NettyConfig {
|
||||
public boolean fileServerEnabled;
|
||||
@Deprecated
|
||||
public boolean sendExceptionEnabled;
|
||||
public boolean ipForwarding;
|
||||
public boolean disableWebApiInterface;
|
||||
public boolean showHiddenFiles;
|
||||
|
@ -289,6 +287,8 @@ public static class NettyConfig {
|
|||
public String address;
|
||||
public Map<String, LaunchServerConfig.NettyUpdatesBind> bindings = new HashMap<>();
|
||||
public NettyPerformanceConfig performance;
|
||||
|
||||
public NettySecurityConfig security = new NettySecurityConfig();
|
||||
public NettyBindAddress[] binds;
|
||||
public LogLevel logLevel = LogLevel.DEBUG;
|
||||
}
|
||||
|
@ -298,7 +298,6 @@ public static class NettyPerformanceConfig {
|
|||
public int bossThread;
|
||||
public int workerThread;
|
||||
public int schedulerThread;
|
||||
public long sessionLifetimeMs = 24 * 60 * 60 * 1000;
|
||||
public int maxWebSocketRequestBytes = 1024 * 1024;
|
||||
}
|
||||
|
||||
|
@ -311,4 +310,11 @@ public NettyBindAddress(String address, int port) {
|
|||
this.port = port;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NettySecurityConfig {
|
||||
public long hardwareTokenExpire = 60 * 60 * 8;
|
||||
public long publicKeyTokenExpire = 60 * 60 * 8;
|
||||
|
||||
public long launcherTokenExpire = 60 * 60 * 8;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ public class LaunchServerRuntimeConfig {
|
|||
private transient final Logger logger = LogManager.getLogger();
|
||||
public String passwordEncryptKey;
|
||||
public String runtimeEncryptKey;
|
||||
public String oemUnlockKey;
|
||||
public String unlockSecret;
|
||||
public String registerApiKey;
|
||||
public String clientCheckSecret;
|
||||
|
||||
|
|
|
@ -18,7 +18,11 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
|
|||
ClientProfileBuilder builder = new ClientProfileBuilder();
|
||||
builder.setVersion(version.name);
|
||||
builder.setDir(title);
|
||||
builder.setAssetDir("asset" + version.name);
|
||||
if(findOption(options, MakeProfileOptionGlobalAssets.class).isPresent()) {
|
||||
builder.setAssetDir("assets");
|
||||
} else {
|
||||
builder.setAssetDir("asset" + version.name);
|
||||
}
|
||||
builder.setAssetIndex(version.name);
|
||||
builder.setInfo("Информация о сервере");
|
||||
builder.setTitle(title);
|
||||
|
@ -42,11 +46,16 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
|
|||
Set<OptionalFile> optionals = new HashSet<>();
|
||||
jvmArgs.add("-XX:+DisableAttachMechanism");
|
||||
// Official Mojang launcher java arguments
|
||||
jvmArgs.add("-XX:+UseG1GC");
|
||||
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
|
||||
jvmArgs.add("-XX:G1NewSizePercent=20");
|
||||
jvmArgs.add("-XX:MaxGCPauseMillis=50");
|
||||
jvmArgs.add("-XX:G1HeapRegionSize=32M");
|
||||
if(version.compareTo(ClientProfile.Version.MC112) <= 0) {
|
||||
jvmArgs.add("-XX:+UseConcMarkSweepGC");
|
||||
jvmArgs.add("-XX:+CMSIncrementalMode");
|
||||
} else if(version.compareTo(ClientProfile.Version.MC118) <= 0) { // 1.13 - 1.16.5
|
||||
jvmArgs.add("-XX:+UseG1GC");
|
||||
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
|
||||
} else { // 1.18+
|
||||
jvmArgs.add("-XX:+UseShenandoahGC");
|
||||
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
|
||||
}
|
||||
// -----------
|
||||
Optional<MakeProfileOptionForge> forge = findOption(options, MakeProfileOptionForge.class);
|
||||
Optional<MakeProfileOptionFabric> fabric = findOption(options, MakeProfileOptionFabric.class);
|
||||
|
@ -192,7 +201,7 @@ private static String getLog4jVersion(Path dir) throws IOException {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static MakeProfileOption[] getMakeProfileOptionsFromDir(Path dir, ClientProfile.Version version) throws IOException {
|
||||
public static MakeProfileOption[] getMakeProfileOptionsFromDir(Path dir, ClientProfile.Version version, boolean globalAssets) throws IOException {
|
||||
List<MakeProfileOption> options = new ArrayList<>(2);
|
||||
if (Files.exists(dir.resolve("forge.jar"))) {
|
||||
options.add(new MakeProfileOptionForge());
|
||||
|
@ -227,6 +236,9 @@ public static MakeProfileOption[] getMakeProfileOptionsFromDir(Path dir, ClientP
|
|||
if (Files.exists(dir.resolve("libraries/forge/launchwrapper-1.12-launcherfixed.jar.jar")) || Files.exists(dir.resolve("libraries/net/minecraft/launchwrapper"))) {
|
||||
options.add(new MakeProfileOptionLaunchWrapper());
|
||||
}
|
||||
if(globalAssets) {
|
||||
options.add(new MakeProfileOptionGlobalAssets());
|
||||
}
|
||||
return options.toArray(new MakeProfileOption[0]);
|
||||
}
|
||||
|
||||
|
@ -302,6 +314,10 @@ public static class MakeProfileOptionLaunchWrapper implements MakeProfileOption
|
|||
|
||||
}
|
||||
|
||||
public static class MakeProfileOptionGlobalAssets implements MakeProfileOption {
|
||||
|
||||
}
|
||||
|
||||
public static class MakeProfileOptionFabric implements MakeProfileOption {
|
||||
public String jimfsPath;
|
||||
public String guavaPath;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.core.User;
|
||||
import pro.gravit.launchserver.auth.core.UserSession;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportKeys;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportProperties;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
||||
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
||||
|
@ -26,7 +27,13 @@
|
|||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.*;
|
||||
|
||||
public class AuthManager {
|
||||
|
@ -180,6 +187,10 @@ public void internalAuth(Client client, AuthResponse.ConnectTypes authType, Auth
|
|||
client.uuid = uuid;
|
||||
}
|
||||
|
||||
public UserSessionSupportKeys.ClientProfileKeys createClientProfileKeys(UUID playerUUID) {
|
||||
throw new UnsupportedOperationException("Minecraft 1.19.1 signature"); // TODO
|
||||
}
|
||||
|
||||
public CheckServerReport checkServer(Client client, String username, String serverID) throws IOException {
|
||||
if (client.auth == null) return null;
|
||||
User user = client.auth.core.checkServer(client, username, serverID);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
import pro.gravit.launchserver.socket.response.WebSocketServerResponse;
|
||||
import pro.gravit.launchserver.socket.response.auth.*;
|
||||
import pro.gravit.launchserver.socket.response.management.FeaturesResponse;
|
||||
import pro.gravit.launchserver.socket.response.management.GetPublicKeyResponse;
|
||||
import pro.gravit.launchserver.socket.response.management.ServerStatusResponse;
|
||||
import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername;
|
||||
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
|
||||
|
@ -88,6 +89,8 @@ public static void registerResponses() {
|
|||
providers.register("refreshToken", RefreshTokenResponse.class);
|
||||
providers.register("restore", RestoreResponse.class);
|
||||
providers.register("additionalData", AdditionalDataResponse.class);
|
||||
providers.register("clientProfileKey", FetchClientProfileKeyResponse.class);
|
||||
providers.register("getPublicKey", GetPublicKeyResponse.class);
|
||||
}
|
||||
|
||||
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package pro.gravit.launchserver.socket.response.auth;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import pro.gravit.launcher.events.request.FetchClientProfileKeyRequestEvent;
|
||||
import pro.gravit.launchserver.auth.core.User;
|
||||
import pro.gravit.launchserver.auth.core.UserSession;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportKeys;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.SimpleResponse;
|
||||
|
||||
public class FetchClientProfileKeyResponse extends SimpleResponse {
|
||||
@Override
|
||||
public String getType() {
|
||||
return "clientProfileKey";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
if (!client.isAuth || client.type != AuthResponse.ConnectTypes.CLIENT) {
|
||||
sendError("Permissions denied");
|
||||
return;
|
||||
}
|
||||
UserSession session = client.sessionObject;
|
||||
UserSessionSupportKeys.ClientProfileKeys keys;
|
||||
if(session instanceof UserSessionSupportKeys support) {
|
||||
keys = support.getClientProfileKeys();
|
||||
} else {
|
||||
keys = server.authManager.createClientProfileKeys(client.uuid);
|
||||
}
|
||||
sendResult(new FetchClientProfileKeyRequestEvent(keys.publicKey(), keys.privateKey(), keys.signature(), keys.expiresAt(), keys.refreshedAfter()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package pro.gravit.launchserver.socket.response.management;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import pro.gravit.launcher.events.request.GetPublicKeyRequestEvent;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.launchserver.socket.response.SimpleResponse;
|
||||
|
||||
public class GetPublicKeyResponse extends SimpleResponse {
|
||||
@Override
|
||||
public String getType() {
|
||||
return "getPublicKey";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
sendResult(new GetPublicKeyRequestEvent(server.keyAgreementManager.rsaPublicKey, server.keyAgreementManager.ecdsaPublicKey));
|
||||
}
|
||||
}
|
|
@ -71,7 +71,7 @@ public String createLauncherExtendedToken() {
|
|||
return Jwts.builder()
|
||||
.setIssuer("LaunchServer")
|
||||
.claim("checkSign", true)
|
||||
.setExpiration(Date.from(LocalDateTime.now().plusHours(8).toInstant(ZoneOffset.UTC)))
|
||||
.setExpiration(Date.from(LocalDateTime.now().plusSeconds(server.config.netty.security.launcherTokenExpire).toInstant(ZoneOffset.UTC)))
|
||||
.signWith(server.keyAgreementManager.ecdsaPrivateKey, SignatureAlgorithm.ES256)
|
||||
.compact();
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ task javadocJar(type: Jar) {
|
|||
shadowJar {
|
||||
duplicatesStrategy = 'EXCLUDE'
|
||||
archiveClassifier.set(null)
|
||||
relocate 'org.objectweb.asm', 'pro.gravit.repackage.org.objectweb.asm'
|
||||
relocate 'io.netty', 'pro.gravit.repackage.io.netty'
|
||||
configurations = [project.configurations.pack]
|
||||
exclude 'module-info.class'
|
||||
|
@ -52,7 +51,6 @@ task javadocJar(type: Jar) {
|
|||
pack project(':LauncherAPI')
|
||||
bundle group: 'com.github.oshi', name: 'oshi-core', version: rootProject['verOshiCore']
|
||||
pack group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty']
|
||||
pack group: 'org.ow2.asm', name: 'asm-tree', version: rootProject['verAsm']
|
||||
}
|
||||
|
||||
task genRuntimeJS(type: Zip) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package pro.gravit.launcher;
|
||||
|
||||
import pro.gravit.launcher.patches.FMLPatcher;
|
||||
import pro.gravit.launcher.utils.NativeJVMHalt;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
|
@ -30,7 +29,6 @@ public static void premain(String agentArgument, Instrumentation instrumentation
|
|||
checkAgentStacktrace();
|
||||
inst = instrumentation;
|
||||
NativeJVMHalt.initFunc();
|
||||
FMLPatcher.apply();
|
||||
isAgentStarted = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
import pro.gravit.launcher.client.events.ClientExitPhase;
|
||||
import pro.gravit.launcher.client.events.ClientPreGuiPhase;
|
||||
import pro.gravit.launcher.console.GetPublicKeyCommand;
|
||||
import pro.gravit.launcher.console.ModulesCommand;
|
||||
import pro.gravit.launcher.console.SignDataCommand;
|
||||
import pro.gravit.launcher.events.request.*;
|
||||
import pro.gravit.launcher.guard.LauncherGuardInterface;
|
||||
import pro.gravit.launcher.guard.LauncherGuardManager;
|
||||
import pro.gravit.launcher.guard.LauncherGuard;
|
||||
import pro.gravit.launcher.guard.LauncherNoGuard;
|
||||
import pro.gravit.launcher.guard.LauncherWrapperGuard;
|
||||
import pro.gravit.launcher.gui.NoRuntimeProvider;
|
||||
|
@ -28,7 +28,6 @@
|
|||
import pro.gravit.launcher.request.secure.GetSecureLevelInfoRequest;
|
||||
import pro.gravit.launcher.request.secure.SecurityReportRequest;
|
||||
import pro.gravit.launcher.request.update.LauncherRequest;
|
||||
import pro.gravit.launcher.request.websockets.ClientWebSocketService;
|
||||
import pro.gravit.launcher.request.websockets.OfflineRequestService;
|
||||
import pro.gravit.launcher.request.websockets.StdWebSocketService;
|
||||
import pro.gravit.launcher.utils.NativeJVMHalt;
|
||||
|
@ -50,7 +49,7 @@
|
|||
|
||||
public class LauncherEngine {
|
||||
public static ClientLauncherProcess.ClientParams clientParams;
|
||||
public static LauncherGuardInterface guard;
|
||||
public static LauncherGuard guard;
|
||||
public static ClientModuleManager modulesManager;
|
||||
public final boolean clientInstance;
|
||||
// Instance
|
||||
|
@ -85,8 +84,14 @@ public static void checkClass(Class<?> clazz) throws SecurityException {
|
|||
}
|
||||
}
|
||||
|
||||
public static void exitLauncher(int code) {
|
||||
modulesManager.invokeEvent(new ClientExitPhase(code));
|
||||
public static void beforeExit(int code) {
|
||||
try {
|
||||
modulesManager.invokeEvent(new ClientExitPhase(code));
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void forceExit(int code) {
|
||||
try {
|
||||
System.exit(code);
|
||||
} catch (Throwable e) //Forge Security Manager?
|
||||
|
@ -95,6 +100,11 @@ public static void exitLauncher(int code) {
|
|||
}
|
||||
}
|
||||
|
||||
public static void exitLauncher(int code) {
|
||||
beforeExit(code);
|
||||
forceExit(code);
|
||||
}
|
||||
|
||||
public static void main(String... args) throws Throwable {
|
||||
JVMHelper.checkStackTrace(LauncherEngine.class);
|
||||
JVMHelper.verifySystemProperties(Launcher.class, true);
|
||||
|
@ -143,7 +153,7 @@ public static void verifyNoAgent() {
|
|||
throw new SecurityException("JavaAgent found");
|
||||
}
|
||||
|
||||
public static LauncherGuardInterface tryGetStdGuard() {
|
||||
public static LauncherGuard tryGetStdGuard() {
|
||||
switch (Launcher.getConfig().guardType) {
|
||||
case "no":
|
||||
return new LauncherNoGuard();
|
||||
|
@ -264,7 +274,6 @@ public void start(String... args) throws Throwable {
|
|||
registerCommands();
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientEngineInitPhase(this));
|
||||
runtimeProvider.preLoad();
|
||||
LauncherGuardManager.initGuard(clientInstance);
|
||||
LogHelper.debug("Dir: %s", DirBridge.dir);
|
||||
runtimeProvider.run(args);
|
||||
}
|
||||
|
@ -272,5 +281,6 @@ public void start(String... args) throws Throwable {
|
|||
private void registerCommands() {
|
||||
ConsoleManager.handler.registerCommand("getpublickey", new GetPublicKeyCommand(this));
|
||||
ConsoleManager.handler.registerCommand("signdata", new SignDataCommand(this));
|
||||
ConsoleManager.handler.registerCommand("modules", new ModulesCommand());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package pro.gravit.launcher.api;
|
||||
|
||||
import pro.gravit.launcher.utils.ApiBridgeService;
|
||||
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.net.URL;
|
||||
|
||||
|
@ -12,4 +14,8 @@ public class ClientService {
|
|||
public static ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
public static String findLibrary(String name) {
|
||||
return ApiBridgeService.findLibrary(classLoader, name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
import pro.gravit.launcher.*;
|
||||
import pro.gravit.launcher.api.AuthService;
|
||||
import pro.gravit.launcher.api.ClientService;
|
||||
import pro.gravit.launcher.api.KeyService;
|
||||
import pro.gravit.launcher.client.events.client.*;
|
||||
import pro.gravit.launcher.events.request.ProfileByUUIDRequestEvent;
|
||||
import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent;
|
||||
import pro.gravit.launcher.guard.LauncherGuardManager;
|
||||
import pro.gravit.launcher.hasher.FileNameMatcher;
|
||||
import pro.gravit.launcher.hasher.HashedDir;
|
||||
import pro.gravit.launcher.hasher.HashedEntry;
|
||||
|
@ -15,7 +15,6 @@
|
|||
import pro.gravit.launcher.modules.LauncherModulesManager;
|
||||
import pro.gravit.launcher.modules.events.OfflineModeEvent;
|
||||
import pro.gravit.launcher.modules.events.PreConfigPhase;
|
||||
import pro.gravit.launcher.patches.FMLPatcher;
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
|
||||
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClassPath;
|
||||
|
@ -26,7 +25,6 @@
|
|||
import pro.gravit.launcher.request.RequestService;
|
||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
|
||||
import pro.gravit.launcher.request.uuid.BatchProfileByUsernameRequest;
|
||||
import pro.gravit.launcher.request.uuid.ProfileByUUIDRequest;
|
||||
import pro.gravit.launcher.request.uuid.ProfileByUsernameRequest;
|
||||
import pro.gravit.launcher.request.websockets.OfflineRequestService;
|
||||
|
@ -87,7 +85,6 @@ public static void main(String[] args) throws Throwable {
|
|||
ConsoleManager.initConsole();
|
||||
LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase());
|
||||
engine.readKeys();
|
||||
LauncherGuardManager.initGuard(true);
|
||||
LogHelper.debug("Reading ClientLauncher params");
|
||||
ClientLauncherProcess.ClientParams params = readParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort));
|
||||
if (params.profile.getClassLoaderConfig() != ClientProfile.ClassLoaderConfig.AGENT) {
|
||||
|
@ -118,10 +115,11 @@ public static void main(String[] args) throws Throwable {
|
|||
|
||||
// Verify ClientLauncher sign and classpath
|
||||
LogHelper.debug("Verifying ClientLauncher sign and classpath");
|
||||
List<Path> classpath = resolveClassPath(clientDir, params.actions, params.profile).collect(Collectors.toList());
|
||||
List<Path> classpath = resolveClassPath(clientDir, params.actions, params.profile)
|
||||
.filter(x -> !profile.getModulePath().contains(clientDir.relativize(x).toString()))
|
||||
.collect(Collectors.toList());
|
||||
List<URL> classpathURLs = classpath.stream().map(IOHelper::toURL).collect(Collectors.toList());
|
||||
// Start client with WatchService monitoring
|
||||
boolean digest = !profile.isUpdateFastCheck();
|
||||
RequestService service;
|
||||
if(params.offlineMode) {
|
||||
service = initOffline(LauncherEngine.modulesManager, params);
|
||||
|
@ -143,13 +141,14 @@ public static void main(String[] args) throws Throwable {
|
|||
}
|
||||
};
|
||||
}
|
||||
LogHelper.debug("Natives dir %s", params.nativesDir);
|
||||
ClientProfile.ClassLoaderConfig classLoaderConfig = profile.getClassLoaderConfig();
|
||||
if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) {
|
||||
ClientClassLoader classLoader = new ClientClassLoader(classpathURLs.toArray(new URL[0]), ClassLoader.getSystemClassLoader());
|
||||
System.setProperty("java.class.path", classpath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator)));
|
||||
ClientLauncherEntryPoint.classLoader = classLoader;
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
classLoader.nativePath = clientDir.resolve("natives").toString();
|
||||
classLoader.nativePath = params.nativesDir;
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(engine, classLoader, profile));
|
||||
ClientService.classLoader = classLoader;
|
||||
ClientService.nativePath = classLoader.nativePath;
|
||||
|
@ -162,7 +161,7 @@ public static void main(String[] args) throws Throwable {
|
|||
LauncherAgent.addJVMClassPath(Paths.get(url.toURI()));
|
||||
}
|
||||
ClientService.instrumentation = LauncherAgent.inst;
|
||||
ClientService.nativePath = clientDir.resolve("natives").toString();
|
||||
ClientService.nativePath = params.nativesDir;
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(engine, classLoader, profile));
|
||||
ClientService.classLoader = classLoader;
|
||||
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
|
||||
|
@ -170,9 +169,11 @@ public static void main(String[] args) throws Throwable {
|
|||
ClientLauncherEntryPoint.classLoader = ClassLoader.getSystemClassLoader();
|
||||
ClientService.classLoader = ClassLoader.getSystemClassLoader();
|
||||
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
|
||||
ClientService.nativePath = params.nativesDir;
|
||||
}
|
||||
AuthService.username = params.playerProfile.username;
|
||||
AuthService.uuid = params.playerProfile.uuid;
|
||||
KeyService.serverRsaPublicKey = Launcher.getConfig().rsaPublicKey;
|
||||
if (params.profile.getRuntimeInClientConfig() != ClientProfile.RuntimeInClientConfig.NONE) {
|
||||
CommonHelper.newThread("Client Launcher Thread", true, () -> {
|
||||
try {
|
||||
|
@ -187,9 +188,9 @@ public static void main(String[] args) throws Throwable {
|
|||
FileNameMatcher assetMatcher = profile.getAssetUpdateMatcher();
|
||||
FileNameMatcher clientMatcher = profile.getClientUpdateMatcher();
|
||||
Path javaDir = Paths.get(System.getProperty("java.home"));
|
||||
try (DirWatcher assetWatcher = new DirWatcher(assetDir, params.assetHDir, assetMatcher, digest);
|
||||
DirWatcher clientWatcher = new DirWatcher(clientDir, params.clientHDir, clientMatcher, digest);
|
||||
DirWatcher javaWatcher = params.javaHDir == null ? null : new DirWatcher(javaDir, params.javaHDir, null, digest)) {
|
||||
try (DirWatcher assetWatcher = new DirWatcher(assetDir, params.assetHDir, assetMatcher, true);
|
||||
DirWatcher clientWatcher = new DirWatcher(clientDir, params.clientHDir, clientMatcher, true);
|
||||
DirWatcher javaWatcher = params.javaHDir == null ? null : new DirWatcher(javaDir, params.javaHDir, null, true)) {
|
||||
// Verify current state of all dirs
|
||||
//verifyHDir(IOHelper.JVM_DIR, jvmHDir.object, null, digest);
|
||||
//for (OptionalFile s : Launcher.profile.getOptional()) {
|
||||
|
@ -201,10 +202,10 @@ public static void main(String[] args) throws Throwable {
|
|||
CommonHelper.newThread("Client Directory Watcher", true, clientWatcher).start();
|
||||
if (javaWatcher != null)
|
||||
CommonHelper.newThread("Java Directory Watcher", true, javaWatcher).start();
|
||||
verifyHDir(assetDir, params.assetHDir, assetMatcher, digest);
|
||||
verifyHDir(clientDir, params.clientHDir, clientMatcher, digest);
|
||||
verifyHDir(assetDir, params.assetHDir, assetMatcher, false, false);
|
||||
verifyHDir(clientDir, params.clientHDir, clientMatcher, false, true);
|
||||
if (javaWatcher != null)
|
||||
verifyHDir(javaDir, params.javaHDir, null, digest);
|
||||
verifyHDir(javaDir, params.javaHDir, null, false, true);
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params));
|
||||
launch(profile, params);
|
||||
}
|
||||
|
@ -243,36 +244,34 @@ public static void applyClientOfflineProcessors(OfflineRequestService service, C
|
|||
});
|
||||
}
|
||||
|
||||
public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
|
||||
public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest, boolean checkExtra) throws IOException {
|
||||
//if (matcher != null)
|
||||
// matcher = matcher.verifyOnly();
|
||||
|
||||
// Hash directory and compare (ignore update-only matcher entries, it will break offline-mode)
|
||||
HashedDir currentHDir = new HashedDir(dir, matcher, true, digest);
|
||||
HashedDir.Diff diff = hdir.diff(currentHDir, matcher);
|
||||
if (!diff.isSame()) {
|
||||
if (LogHelper.isDebugEnabled()) {
|
||||
diff.extra.walk(File.separator, (e, k, v) -> {
|
||||
if (v.getType().equals(HashedEntry.Type.FILE)) {
|
||||
LogHelper.error("Extra file %s", e);
|
||||
} else LogHelper.error("Extra %s", e);
|
||||
return HashedDir.WalkAction.CONTINUE;
|
||||
});
|
||||
diff.mismatch.walk(File.separator, (e, k, v) -> {
|
||||
if (v.getType().equals(HashedEntry.Type.FILE)) {
|
||||
LogHelper.error("Mismatch file %s", e);
|
||||
} else LogHelper.error("Mismatch %s", e);
|
||||
return HashedDir.WalkAction.CONTINUE;
|
||||
});
|
||||
}
|
||||
if (!diff.mismatch.isEmpty() || (checkExtra && !diff.extra.isEmpty())) {
|
||||
diff.extra.walk(File.separator, (e, k, v) -> {
|
||||
if (v.getType().equals(HashedEntry.Type.FILE)) {
|
||||
LogHelper.error("Extra file %s", e);
|
||||
} else LogHelper.error("Extra %s", e);
|
||||
return HashedDir.WalkAction.CONTINUE;
|
||||
});
|
||||
diff.mismatch.walk(File.separator, (e, k, v) -> {
|
||||
if (v.getType().equals(HashedEntry.Type.FILE)) {
|
||||
LogHelper.error("Mismatch file %s", e);
|
||||
} else LogHelper.error("Mismatch %s", e);
|
||||
return HashedDir.WalkAction.CONTINUE;
|
||||
});
|
||||
throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir)));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkJVMBitsAndVersion(int minVersion, int recommendVersion, int maxVersion, boolean showMessage) {
|
||||
boolean ok = true;
|
||||
if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) {
|
||||
String error = String.format("У Вас установлена Java %d, но Ваша система определена как %d. Установите Java правильной разрядности", JVMHelper.JVM_BITS, JVMHelper.OS_BITS);
|
||||
if (JVMHelper.JVM_BITS == 64 && JVMHelper.ARCH_TYPE == JVMHelper.ARCH.X86) {
|
||||
String error = "У Вас установлена Java x64, но Ваша система определена как x32. Установите Java правильной разрядности";
|
||||
LogHelper.error(error);
|
||||
if (showMessage)
|
||||
JOptionPane.showMessageDialog(null, error);
|
||||
|
@ -282,7 +281,7 @@ public static boolean checkJVMBitsAndVersion(int minVersion, int recommendVersio
|
|||
LogHelper.info(jvmVersion);
|
||||
int version = JVMHelper.getVersion();
|
||||
if (version < minVersion || version > maxVersion) {
|
||||
String error = String.format("У Вас установлена Java %s. Для правильной работы необходима Java %d", JVMHelper.RUNTIME_MXBEAN.getVmVersion(), recommendVersion);
|
||||
String error = String.format("У Вас установлена Java %d, но этот клиент требует Java %d", JVMHelper.getVersion(), recommendVersion);
|
||||
LogHelper.error(error);
|
||||
if (showMessage)
|
||||
JOptionPane.showMessageDialog(null, error);
|
||||
|
@ -347,7 +346,6 @@ private static void launch(ClientProfile profile, ClientLauncherProcess.ClientPa
|
|||
LogHelper.dev("ClassLoader URL: %s", u.toString());
|
||||
}
|
||||
}
|
||||
FMLPatcher.apply();
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessPreInvokeMainClassEvent(params, profile, args));
|
||||
// Invoke main method
|
||||
try {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package pro.gravit.launcher.client;
|
||||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherConfig;
|
||||
import pro.gravit.launcher.LauncherEngine;
|
||||
import pro.gravit.launcher.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.client.events.client.ClientProcessBuilderCreateEvent;
|
||||
|
@ -25,6 +26,7 @@
|
|||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
@ -42,42 +44,45 @@ public class ClientLauncherProcess {
|
|||
private final transient Boolean[] waitWriteParams = new Boolean[]{false};
|
||||
public Path executeFile;
|
||||
public Path workDir;
|
||||
public Path javaDir;
|
||||
public int bits;
|
||||
public JavaHelper.JavaVersion javaVersion;
|
||||
public boolean useLegacyJavaClassPathProperty;
|
||||
public boolean isStarted;
|
||||
public JavaHelper.JavaVersion javaVersion;
|
||||
private transient Process process;
|
||||
|
||||
public ClientLauncherProcess(Path executeFile, Path workDir, Path javaDir, String mainClass) {
|
||||
public ClientLauncherProcess(Path executeFile, Path workDir, JavaHelper.JavaVersion javaVersion, String mainClass) {
|
||||
this.executeFile = executeFile;
|
||||
this.workDir = workDir;
|
||||
this.javaDir = javaDir;
|
||||
this.javaVersion = javaVersion;
|
||||
this.mainClass = mainClass;
|
||||
}
|
||||
|
||||
public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir,
|
||||
public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersion javaVersion,
|
||||
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
|
||||
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
|
||||
this(clientDir, assetDir, javaDir, clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
|
||||
this(clientDir, assetDir, javaVersion, clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
|
||||
}
|
||||
|
||||
public ClientLauncherProcess(Path clientDir, Path assetDir,
|
||||
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
|
||||
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
|
||||
this(clientDir, assetDir, Paths.get(System.getProperty("java.home")), clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
|
||||
this(clientDir, assetDir, JavaHelper.JavaVersion.getCurrentJavaVersion(), clientDir.resolve("resourcepacks"), profile, playerProfile, null, accessToken, clientHDir, assetHDir, jvmHDir);
|
||||
}
|
||||
|
||||
public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path resourcePackDir,
|
||||
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) {
|
||||
this.javaVersion = javaVersion;
|
||||
this.workDir = clientDir.toAbsolutePath();
|
||||
this.javaDir = javaDir;
|
||||
this.executeFile = IOHelper.resolveJavaBin(this.javaDir);
|
||||
this.executeFile = IOHelper.resolveJavaBin(this.javaVersion.jvmDir);
|
||||
this.mainClass = ClientLauncherEntryPoint.class.getName();
|
||||
this.params.clientDir = this.workDir.toString();
|
||||
this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString();
|
||||
this.params.assetDir = assetDir.toAbsolutePath().toString();
|
||||
Path nativesPath = workDir.resolve("natives").resolve(JVMHelper.OS_TYPE.name).resolve(javaVersion.arch.name);
|
||||
if(!Files.isDirectory(nativesPath)) {
|
||||
nativesPath = workDir.resolve("natives");
|
||||
}
|
||||
this.params.nativesDir = nativesPath.toString();
|
||||
this.params.profile = profile;
|
||||
this.params.playerProfile = playerProfile;
|
||||
this.params.accessToken = accessToken;
|
||||
|
@ -87,16 +92,6 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path r
|
|||
if (view != null) {
|
||||
this.params.actions = view.getEnabledActions();
|
||||
}
|
||||
try {
|
||||
javaVersion = JavaHelper.JavaVersion.getByPath(javaDir);
|
||||
} catch (IOException e) {
|
||||
LogHelper.error(e);
|
||||
javaVersion = null;
|
||||
}
|
||||
if (javaVersion == null) {
|
||||
javaVersion = JavaHelper.JavaVersion.getCurrentJavaVersion();
|
||||
}
|
||||
this.bits = JVMHelper.JVM_BITS;
|
||||
applyClientProfile();
|
||||
}
|
||||
|
||||
|
@ -115,7 +110,7 @@ private void applyClientProfile() {
|
|||
this.jvmArgs.addAll(((OptionalActionJvmArgs) a).args);
|
||||
}
|
||||
}
|
||||
this.systemEnv.put("JAVA_HOME", javaDir.toString());
|
||||
this.systemEnv.put("JAVA_HOME", javaVersion.jvmDir.toString());
|
||||
Collections.addAll(this.systemClassPath, this.params.profile.getAlternativeClassPath());
|
||||
if (params.ram > 0) {
|
||||
this.jvmArgs.add("-Xmx" + params.ram + 'M');
|
||||
|
@ -154,10 +149,19 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
|
|||
applyJava9Params(processArgs);
|
||||
}
|
||||
//ADD CLASSPATH
|
||||
processArgs.add(JVMHelper.jvmProperty("java.library.path", this.params.nativesDir));
|
||||
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.AGENT) {
|
||||
processArgs.add("-javaagent:".concat(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString()));
|
||||
} else if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
|
||||
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(workDir, params.actions, params.profile).map(Path::toString).collect(Collectors.toList()));
|
||||
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(workDir, params.actions, params.profile)
|
||||
.filter(x -> !params.profile.getModulePath().contains(workDir.relativize(x).toString()))
|
||||
.map(Path::toString)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
if(Launcher.getConfig().environment != LauncherConfig.LauncherEnvironment.PROD) {
|
||||
processArgs.add(JVMHelper.jvmProperty(LogHelper.DEV_PROPERTY, String.valueOf(LogHelper.isDevEnabled())));
|
||||
processArgs.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, String.valueOf(LogHelper.isDebugEnabled())));
|
||||
processArgs.add(JVMHelper.jvmProperty(LogHelper.STACKTRACE_PROPERTY, String.valueOf(LogHelper.isStacktraceEnabled())));
|
||||
}
|
||||
if (useLegacyJavaClassPathProperty) {
|
||||
processArgs.add("-Djava.class.path=".concat(String.join(getPathSeparator(), systemClassPath)));
|
||||
|
@ -176,7 +180,7 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
|
|||
LogHelper.debug("Commandline: %s", Arrays.toString(processArgs.toArray()));
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
|
||||
EnvHelper.addEnv(processBuilder);
|
||||
processBuilder.environment().put("JAVA_HOME", javaDir.toAbsolutePath().toString());
|
||||
processBuilder.environment().put("JAVA_HOME", javaVersion.jvmDir.toAbsolutePath().toString());
|
||||
processBuilder.environment().putAll(systemEnv);
|
||||
processBuilder.directory(workDir.toFile());
|
||||
processBuilder.inheritIO();
|
||||
|
@ -256,6 +260,8 @@ public static class ClientParams {
|
|||
|
||||
public String resourcePackDir;
|
||||
|
||||
public String nativesDir;
|
||||
|
||||
// Client params
|
||||
|
||||
public PlayerProfile playerProfile;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
import pro.gravit.launcher.modules.impl.SimpleModuleManager;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class ClientModuleManager extends SimpleModuleManager {
|
||||
public ClientModuleManager() {
|
||||
|
@ -28,6 +30,10 @@ public LauncherModule loadModule(LauncherModule module) {
|
|||
return super.loadModule(module);
|
||||
}
|
||||
|
||||
public List<LauncherModule> getModules() {
|
||||
return Collections.unmodifiableList(modules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean verifyClassCheckResult(LauncherTrustManager.CheckClassResult result) {
|
||||
return result.type == LauncherTrustManager.CheckClassResultType.SUCCESS;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package pro.gravit.launcher.console;
|
||||
|
||||
import pro.gravit.launcher.LauncherEngine;
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
import pro.gravit.launcher.managers.ConsoleManager;
|
||||
import pro.gravit.launcher.modules.LauncherModule;
|
||||
import pro.gravit.launcher.modules.LauncherModuleInfo;
|
||||
import pro.gravit.utils.command.Command;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ModulesCommand extends Command {
|
||||
@Override
|
||||
public String getArgsDescription() {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsageDescription() {
|
||||
return "show modules";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(String... args) throws Exception {
|
||||
for(LauncherModule module : LauncherEngine.modulesManager.getModules()) {
|
||||
LauncherModuleInfo info = module.getModuleInfo();
|
||||
LauncherTrustManager.CheckClassResult checkStatus = module.getCheckResult();
|
||||
if(!ConsoleManager.isConsoleUnlock) {
|
||||
LogHelper.info("[MODULE] %s v: %s", info.name, info.version.getVersionString());
|
||||
} else {
|
||||
LogHelper.info("[MODULE] %s v: %s p: %d deps: %s sig: %s", info.name, info.version.getVersionString(), info.priority, Arrays.toString(info.dependencies), checkStatus == null ? "null" : checkStatus.type);
|
||||
printCheckStatusInfo(checkStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printCheckStatusInfo(LauncherTrustManager.CheckClassResult checkStatus) {
|
||||
if (checkStatus != null && checkStatus.endCertificate != null) {
|
||||
X509Certificate cert = checkStatus.endCertificate;
|
||||
LogHelper.info("[MODULE CERT] Module signer: %s", cert.getSubjectX500Principal().getName());
|
||||
}
|
||||
if (checkStatus != null && checkStatus.rootCertificate != null) {
|
||||
X509Certificate cert = checkStatus.rootCertificate;
|
||||
LogHelper.info("[MODULE CERT] Module signer CA: %s", cert.getSubjectX500Principal().getName());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ public class DebugMain {
|
|||
public static final AtomicBoolean IS_DEBUG = new AtomicBoolean(false);
|
||||
public static String webSocketURL = System.getProperty("launcherdebug.websocket", "ws://localhost:9274/api");
|
||||
public static String projectName = System.getProperty("launcherdebug.projectname", "Minecraft");
|
||||
public static String unlockKey = System.getProperty("launcherdebug.unlockkey", "0000");
|
||||
public static String unlockSecret = System.getProperty("launcherdebug.unlocksecret", "");
|
||||
public static boolean offlineMode = Boolean.getBoolean("launcherdebug.offlinemode");
|
||||
public static String[] moduleClasses = System.getProperty("launcherdebug.modules", "").split(",");
|
||||
public static String[] moduleFiles = System.getProperty("launcherdebug.modulefiles", "").split(",");
|
||||
|
@ -39,7 +39,7 @@ public static void main(String[] args) throws Throwable {
|
|||
LogHelper.info("Launcher start in DEBUG mode (Only for developers)");
|
||||
LogHelper.debug("Initialization LauncherConfig");
|
||||
LauncherConfig config = new LauncherConfig(webSocketURL, new HashMap<>(), projectName, environment, new DebugLauncherTrustManager(DebugLauncherTrustManager.TrustDebugMode.TRUST_ALL));
|
||||
config.oemUnlockKey = unlockKey;
|
||||
config.unlockSecret = unlockSecret;
|
||||
Launcher.setConfig(config);
|
||||
Launcher.applyLauncherEnv(environment);
|
||||
LauncherEngine.modulesManager = new ClientModuleManager();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import pro.gravit.launcher.client.ClientLauncherProcess;
|
||||
|
||||
public interface LauncherGuardInterface {
|
||||
public interface LauncherGuard {
|
||||
String getName();
|
||||
|
||||
void applyGuardParams(ClientLauncherProcess process);
|
|
@ -1,8 +0,0 @@
|
|||
package pro.gravit.launcher.guard;
|
||||
|
||||
public class LauncherGuardManager {
|
||||
public static LauncherGuardInterface guard;
|
||||
|
||||
public static void initGuard(boolean clientInstance) {
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import pro.gravit.launcher.client.ClientLauncherProcess;
|
||||
|
||||
public class LauncherNoGuard implements LauncherGuardInterface {
|
||||
public class LauncherNoGuard implements LauncherGuard {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "noGuard";
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LauncherWrapperGuard implements LauncherGuardInterface {
|
||||
public class LauncherWrapperGuard implements LauncherGuard {
|
||||
|
||||
public LauncherWrapperGuard() {
|
||||
try {
|
||||
|
|
|
@ -49,7 +49,7 @@ public static void registerCommands() {
|
|||
}
|
||||
|
||||
public static boolean checkUnlockKey(String key) {
|
||||
return key.equals(Launcher.getConfig().oemUnlockKey);
|
||||
return key.equals(Launcher.getConfig().unlockSecret);
|
||||
}
|
||||
|
||||
public static boolean unlock() {
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
package pro.gravit.launcher.patches;
|
||||
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.Vector;
|
||||
|
||||
public class FMLPatcher extends ClassLoader implements Opcodes {
|
||||
public static final MethodType EXITMH = MethodType.methodType(void.class, int.class);
|
||||
public static final String[] PACKAGES = new String[]{"cpw.mods.fml.", "net.minecraftforge.fml.", "cpw.mods."};
|
||||
public static final Vector<MethodHandle> MHS = new Vector<>();
|
||||
public static volatile FMLPatcher INSTANCE = null;
|
||||
|
||||
public FMLPatcher(final ClassLoader cl) {
|
||||
super(cl);
|
||||
}
|
||||
|
||||
public static void apply() {
|
||||
INSTANCE = new FMLPatcher(null); // Never cause ClassFormatError (fuck forge 1.14!!!)
|
||||
for (String s : PACKAGES) {
|
||||
String rMethod = randomStr(16);
|
||||
try {
|
||||
MHS.add(MethodHandles.publicLookup().findStatic(INSTANCE.def(s + randomStr(16), rMethod), rMethod,
|
||||
EXITMH));
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
// Simple ignore - other Forge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void exit(final int code) {
|
||||
for (MethodHandle mh : MHS)
|
||||
try {
|
||||
mh.invoke(code);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] gen(final String name, final String exName) { // "cpw/mods/fml/SafeExitJVMLegacy", "exit"
|
||||
|
||||
final ClassWriter classWriter = new ClassWriter(0);
|
||||
MethodVisitor methodVisitor;
|
||||
|
||||
classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, name, null, "java/lang/Object", null);
|
||||
|
||||
{
|
||||
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
methodVisitor.visitCode();
|
||||
methodVisitor.visitVarInsn(ALOAD, 0);
|
||||
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
methodVisitor.visitInsn(RETURN);
|
||||
methodVisitor.visitMaxs(1, 1);
|
||||
methodVisitor.visitEnd();
|
||||
}
|
||||
{
|
||||
methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, exName, "(I)V", null, null);
|
||||
methodVisitor.visitCode();
|
||||
final Label label0 = new Label();
|
||||
final Label label1 = new Label();
|
||||
final Label label2 = new Label();
|
||||
methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable");
|
||||
methodVisitor.visitLabel(label0);
|
||||
methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/Runtime", "getRuntime", "()Ljava/lang/Runtime;",
|
||||
false);
|
||||
methodVisitor.visitVarInsn(ILOAD, 0);
|
||||
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Runtime", "halt", "(I)V", false);
|
||||
methodVisitor.visitLabel(label1);
|
||||
final Label label3 = new Label();
|
||||
methodVisitor.visitJumpInsn(GOTO, label3);
|
||||
methodVisitor.visitLabel(label2);
|
||||
methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Throwable"});
|
||||
methodVisitor.visitVarInsn(ASTORE, 1);
|
||||
methodVisitor.visitVarInsn(ILOAD, 0);
|
||||
methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/System", "exit", "(I)V", false);
|
||||
methodVisitor.visitLabel(label3);
|
||||
methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
|
||||
methodVisitor.visitInsn(RETURN);
|
||||
methodVisitor.visitMaxs(2, 2);
|
||||
methodVisitor.visitEnd();
|
||||
}
|
||||
classWriter.visitEnd();
|
||||
return classWriter.toByteArray();
|
||||
}
|
||||
|
||||
public static String randomStr(final int lenght) {
|
||||
String alphabet = "abcdefghijklmnopqrstuvwxyz";
|
||||
alphabet += alphabet.toUpperCase(Locale.US);
|
||||
final StringBuilder sb = new StringBuilder(lenght);
|
||||
final Random random = SecurityHelper.newRandom();
|
||||
for (int i = 0; i < lenght; i++)
|
||||
sb.append(alphabet.charAt(random.nextInt(26)));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public Class<?> def(final String name, final String exName) {
|
||||
return super.defineClass(name, ByteBuffer.wrap(gen(name.replace('.', '/'), exName)), null);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherTrustManager;
|
||||
import pro.gravit.launcher.client.ClientClassLoader;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
|
@ -15,4 +16,12 @@ public static void checkCertificatesSuccess(X509Certificate[] certs) throws Exce
|
|||
LauncherTrustManager trustManager = Launcher.getConfig().trustManager;
|
||||
trustManager.checkCertificatesSuccess(certs, trustManager::stdCertificateChecker);
|
||||
}
|
||||
|
||||
public static String findLibrary(ClassLoader classLoader, String library) {
|
||||
if(classLoader instanceof ClientClassLoader) {
|
||||
ClientClassLoader clientClassLoader = (ClientClassLoader) classLoader;
|
||||
return clientClassLoader.findLibrary(library);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package pro.gravit.launcher.utils;
|
||||
|
||||
import pro.gravit.launcher.LauncherEngine;
|
||||
import pro.gravit.launcher.hasher.FileNameMatcher;
|
||||
import pro.gravit.launcher.hasher.HashedDir;
|
||||
import pro.gravit.launcher.hasher.HashedEntry;
|
||||
|
@ -46,7 +47,7 @@ public DirWatcher(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean dig
|
|||
|
||||
private static void handleError(Throwable e) {
|
||||
LogHelper.error(e);
|
||||
NativeJVMHalt.haltA(-123);
|
||||
LauncherEngine.exitLauncher(-123);
|
||||
}
|
||||
|
||||
private static Deque<String> toPath(Iterable<Path> path) {
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
package pro.gravit.launcher.utils;
|
||||
|
||||
import pro.gravit.launcher.AsyncDownloader;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherEngine;
|
||||
import pro.gravit.launcher.LauncherInject;
|
||||
import pro.gravit.launcher.events.request.LauncherRequestEvent;
|
||||
import pro.gravit.launcher.request.update.LauncherRequest;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class LauncherUpdater {
|
||||
@LauncherInject("launcher.certificatePinning")
|
||||
private static boolean isCertificatePinning;
|
||||
public static void nothing() {
|
||||
|
||||
}
|
||||
private static Path getLauncherPath() {
|
||||
Path pathToCore = IOHelper.getCodeSource(IOHelper.class);
|
||||
Path pathToApi = IOHelper.getCodeSource(LauncherRequest.class);
|
||||
Path pathToSelf = IOHelper.getCodeSource(LauncherUpdater.class);
|
||||
if(pathToCore.equals(pathToApi) && pathToCore.equals(pathToSelf)) {
|
||||
return pathToCore;
|
||||
} else {
|
||||
throw new SecurityException("Found split-jar launcher");
|
||||
}
|
||||
}
|
||||
|
||||
public static Path prepareUpdate(URL url) throws Exception {
|
||||
Path pathToLauncher = getLauncherPath();
|
||||
Path tempFile = Files.createTempFile("launcher-update-", ".jar");
|
||||
URLConnection connection = url.openConnection();
|
||||
if (isCertificatePinning) {
|
||||
HttpsURLConnection connection1 = (HttpsURLConnection) connection;
|
||||
try {
|
||||
connection1.setSSLSocketFactory(AsyncDownloader.makeSSLSocketFactory());
|
||||
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | KeyManagementException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
try (InputStream in = connection.getInputStream()) {
|
||||
IOHelper.transfer(in, tempFile);
|
||||
}
|
||||
if (Arrays.equals(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, tempFile),
|
||||
SecurityHelper.digest(SecurityHelper.DigestAlgorithm.MD5, pathToLauncher)))
|
||||
throw new IOException("Invalid update (launcher needs update, but link has old launcher), check LaunchServer config...");
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
public static void restart() {
|
||||
List<String> args = new ArrayList<>(8);
|
||||
args.add(IOHelper.resolveJavaBin(null).toString());
|
||||
args.add("-jar");
|
||||
args.add(IOHelper.getCodeSource(LauncherUpdater.class).toString());
|
||||
ProcessBuilder builder = new ProcessBuilder(args.toArray(new String[0]));
|
||||
builder.inheritIO();
|
||||
try {
|
||||
builder.start();
|
||||
} catch (IOException e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
LauncherEngine.forceExit(0);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package pro.gravit.launcher.utils;
|
||||
|
||||
import pro.gravit.launcher.patches.FMLPatcher;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
public final class NativeJVMHalt {
|
||||
public final int haltCode;
|
||||
|
@ -15,51 +15,24 @@ public NativeJVMHalt(int haltCode) {
|
|||
System.out.println("JVM exit code " + haltCode);
|
||||
}
|
||||
|
||||
public static void initFunc() {
|
||||
|
||||
}
|
||||
|
||||
public static void haltA(int code) {
|
||||
Throwable[] th = new Throwable[3];
|
||||
NativeJVMHalt halt = new NativeJVMHalt(code);
|
||||
try {
|
||||
JVMHelper.RUNTIME.exit(code);
|
||||
} catch (Throwable exitExc) {
|
||||
th[0] = exitExc;
|
||||
try {
|
||||
new WindowShutdown();
|
||||
} catch (Throwable windowExc) {
|
||||
th[1] = windowExc;
|
||||
LogHelper.dev("Try invoke Shutdown.exit");
|
||||
Class<?> clazz = Class.forName("java.lang.Shutdown", true, ClassLoader.getSystemClassLoader());
|
||||
Method exitMethod = clazz.getDeclaredMethod("exit", int.class);
|
||||
exitMethod.setAccessible(true);
|
||||
exitMethod.invoke(null, code);
|
||||
} catch (Throwable e) {
|
||||
th[1] = e;
|
||||
if(LogHelper.isDevEnabled()) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
FMLPatcher.exit(code);
|
||||
} catch (Throwable fmlExc) {
|
||||
th[2] = fmlExc;
|
||||
}
|
||||
for (Throwable t : th) {
|
||||
if (t != null) LogHelper.error(t);
|
||||
}
|
||||
boolean a = halt.aaabBooleanC_D();
|
||||
System.out.println(a);
|
||||
halt.aaabbb38C_D();
|
||||
|
||||
}
|
||||
|
||||
public static boolean initFunc() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public native void aaabbb38C_D();
|
||||
|
||||
@SuppressWarnings("null")
|
||||
private boolean aaabBooleanC_D() {
|
||||
return (boolean) (Boolean) null;
|
||||
}
|
||||
|
||||
public static class WindowShutdown extends JFrame {
|
||||
private static final long serialVersionUID = 6321323663070818367L;
|
||||
|
||||
public WindowShutdown() {
|
||||
super();
|
||||
super.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
super.processWindowEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,8 @@ public final class LauncherConfig extends StreamObject {
|
|||
public final String address;
|
||||
@LauncherInject("runtimeconfig.secretKeyClient")
|
||||
public String secretKeyClient;
|
||||
@LauncherInject("runtimeconfig.oemUnlockKey")
|
||||
public String oemUnlockKey;
|
||||
@LauncherInject("runtimeconfig.unlockSecret")
|
||||
public String unlockSecret;
|
||||
@LauncherInject("launchercore.env")
|
||||
public LauncherEnvironment environment;
|
||||
|
||||
|
@ -63,7 +63,6 @@ public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException
|
|||
projectName = null;
|
||||
clientPort = -1;
|
||||
secretKeyClient = null;
|
||||
oemUnlockKey = null;
|
||||
try {
|
||||
trustManager = new LauncherTrustManager(secureConfigCertificates);
|
||||
} catch (CertificateException e) {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package pro.gravit.launcher.api;
|
||||
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
public class KeyService {
|
||||
public static RSAPublicKey serverRsaPublicKey;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package pro.gravit.launcher.events.request;
|
||||
|
||||
import pro.gravit.launcher.events.RequestEvent;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
public class FetchClientProfileKeyRequestEvent extends RequestEvent {
|
||||
public byte[] publicKey;
|
||||
public byte[] privateKey;
|
||||
public byte[] signature /* V2 */;
|
||||
public long expiresAt;
|
||||
public long refreshedAfter;
|
||||
|
||||
public FetchClientProfileKeyRequestEvent(byte[] publicKey, byte[] privateKey, byte[] signature, long expiresAt, long refreshedAfter) {
|
||||
this.publicKey = publicKey;
|
||||
this.privateKey = privateKey;
|
||||
this.signature = signature;
|
||||
this.expiresAt = expiresAt;
|
||||
this.refreshedAfter = refreshedAfter;
|
||||
}
|
||||
|
||||
public FetchClientProfileKeyRequestEvent(PublicKey publicKey, PrivateKey privateKey, byte[] signature, long expiresAt, long refreshedAfter) {
|
||||
this.publicKey = publicKey.getEncoded();
|
||||
this.privateKey = privateKey.getEncoded();
|
||||
this.signature = signature;
|
||||
this.expiresAt = expiresAt;
|
||||
this.refreshedAfter = refreshedAfter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "clientProfileKey";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package pro.gravit.launcher.events.request;
|
||||
|
||||
import pro.gravit.launcher.events.RequestEvent;
|
||||
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
public class GetPublicKeyRequestEvent extends RequestEvent {
|
||||
public byte[] rsaPublicKey;
|
||||
public byte[] ecdsaPublicKey;
|
||||
|
||||
public GetPublicKeyRequestEvent(byte[] rsaPublicKey, byte[] ecdsaPublicKey) {
|
||||
this.rsaPublicKey = rsaPublicKey;
|
||||
this.ecdsaPublicKey = ecdsaPublicKey;
|
||||
}
|
||||
|
||||
public GetPublicKeyRequestEvent(RSAPublicKey rsaPublicKey, ECPublicKey ecdsaPublicKey) {
|
||||
this.rsaPublicKey = rsaPublicKey.getEncoded();
|
||||
this.ecdsaPublicKey = ecdsaPublicKey.getEncoded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "getPublicKey";
|
||||
}
|
||||
}
|
|
@ -80,8 +80,6 @@ public final class ClientProfile implements Comparable<ClientProfile> {
|
|||
@LauncherNetworkAPI
|
||||
private ProfileDefaultSettings settings = new ProfileDefaultSettings();
|
||||
@LauncherNetworkAPI
|
||||
private boolean updateFastCheck;
|
||||
@LauncherNetworkAPI
|
||||
private boolean limited;
|
||||
// Client launcher
|
||||
@LauncherNetworkAPI
|
||||
|
@ -107,7 +105,7 @@ public ClientProfile() {
|
|||
runtimeInClientConfig = RuntimeInClientConfig.NONE;
|
||||
}
|
||||
|
||||
public ClientProfile(List<String> update, List<String> updateExclusions, List<String> updateShared, List<String> updateVerify, Set<OptionalFile> updateOptional, List<String> jvmArgs, List<String> classPath, List<String> modulePath, List<String> modules, List<String> altClassPath, List<String> clientArgs, List<String> compatClasses, Map<String, String> properties, List<ServerProfile> servers, SecurityManagerConfig securityManagerConfig, ClassLoaderConfig classLoaderConfig, SignedClientConfig signedClientConfig, RuntimeInClientConfig runtimeInClientConfig, String version, String assetIndex, String dir, String assetDir, int recommendJavaVersion, int minJavaVersion, int maxJavaVersion, boolean warnMissJavaVersion, ProfileDefaultSettings settings, int sortIndex, UUID uuid, String title, String info, boolean updateFastCheck, String mainClass) {
|
||||
public ClientProfile(List<String> update, List<String> updateExclusions, List<String> updateShared, List<String> updateVerify, Set<OptionalFile> updateOptional, List<String> jvmArgs, List<String> classPath, List<String> modulePath, List<String> modules, List<String> altClassPath, List<String> clientArgs, List<String> compatClasses, Map<String, String> properties, List<ServerProfile> servers, SecurityManagerConfig securityManagerConfig, ClassLoaderConfig classLoaderConfig, SignedClientConfig signedClientConfig, RuntimeInClientConfig runtimeInClientConfig, String version, String assetIndex, String dir, String assetDir, int recommendJavaVersion, int minJavaVersion, int maxJavaVersion, boolean warnMissJavaVersion, ProfileDefaultSettings settings, int sortIndex, UUID uuid, String title, String info, String mainClass) {
|
||||
this.update = update;
|
||||
this.updateExclusions = updateExclusions;
|
||||
this.updateShared = updateShared;
|
||||
|
@ -139,7 +137,6 @@ public ClientProfile(List<String> update, List<String> updateExclusions, List<St
|
|||
this.uuid = uuid;
|
||||
this.title = title;
|
||||
this.info = info;
|
||||
this.updateFastCheck = updateFastCheck;
|
||||
this.mainClass = mainClass;
|
||||
}
|
||||
|
||||
|
@ -316,8 +313,9 @@ public void setVersion(Version version) {
|
|||
this.version = version.name;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean isUpdateFastCheck() {
|
||||
return updateFastCheck;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -484,7 +482,9 @@ public enum Version {
|
|||
MC118("1.18", 757),
|
||||
MC1181("1.18.1", 757),
|
||||
MC1182("1.18.2", 758),
|
||||
MC119("1.19", 759);
|
||||
MC119("1.19", 759),
|
||||
MC1191("1.19.1", 760),
|
||||
MC1192("1.19.2", 760);
|
||||
private static final Map<String, Version> VERSIONS;
|
||||
|
||||
static {
|
||||
|
|
|
@ -36,7 +36,6 @@ public class ClientProfileBuilder {
|
|||
private UUID uuid;
|
||||
private String title;
|
||||
private String info;
|
||||
private boolean updateFastCheck = true;
|
||||
private String mainClass;
|
||||
|
||||
public ClientProfileBuilder setUpdate(List<String> update) {
|
||||
|
@ -194,17 +193,12 @@ public ClientProfileBuilder setInfo(String info) {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setUpdateFastCheck(boolean updateFastCheck) {
|
||||
this.updateFastCheck = updateFastCheck;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setMainClass(String mainClass) {
|
||||
this.mainClass = mainClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfile createClientProfile() {
|
||||
return new ClientProfile(update, updateExclusions, updateShared, updateVerify, updateOptional, jvmArgs, classPath, modulePath, modules, altClassPath, clientArgs, compatClasses, properties, servers, securityManagerConfig, classLoaderConfig, signedClientConfig, runtimeInClientConfig, version, assetIndex, dir, assetDir, recommendJavaVersion, minJavaVersion, maxJavaVersion, warnMissJavaVersion, settings, sortIndex, uuid, title, info, updateFastCheck, mainClass);
|
||||
return new ClientProfile(update, updateExclusions, updateShared, updateVerify, updateOptional, jvmArgs, classPath, modulePath, modules, altClassPath, clientArgs, compatClasses, properties, servers, securityManagerConfig, classLoaderConfig, signedClientConfig, runtimeInClientConfig, version, assetIndex, dir, assetDir, recommendJavaVersion, minJavaVersion, maxJavaVersion, warnMissJavaVersion, settings, sortIndex, uuid, title, info, mainClass);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package pro.gravit.launcher.profiles.optional.triggers;
|
||||
|
||||
import pro.gravit.launcher.profiles.optional.OptionalFile;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
|
||||
public class ArchTrigger extends OptionalTrigger {
|
||||
public JVMHelper.ARCH arch;
|
||||
@Override
|
||||
protected boolean isTriggered(OptionalFile optional, OptionalTriggerContext context) {
|
||||
return context.getJavaVersion().arch == arch;
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ public static void registerProviders() {
|
|||
if (!isRegisteredProviders) {
|
||||
providers.register("java", JavaTrigger.class);
|
||||
providers.register("os", OSTrigger.class);
|
||||
providers.register("arch", ArchTrigger.class);
|
||||
isRegisteredProviders = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package pro.gravit.launcher.request.auth;
|
||||
|
||||
import pro.gravit.launcher.events.request.FetchClientProfileKeyRequestEvent;
|
||||
import pro.gravit.launcher.request.Request;
|
||||
|
||||
public class FetchClientProfileKeyRequest extends Request<FetchClientProfileKeyRequestEvent> {
|
||||
public FetchClientProfileKeyRequest() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "clientProfileKey";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package pro.gravit.launcher.request.auth;
|
||||
|
||||
import pro.gravit.launcher.events.request.GetPublicKeyRequestEvent;
|
||||
import pro.gravit.launcher.request.Request;
|
||||
|
||||
public class GetPublicKeyRequest extends Request<GetPublicKeyRequestEvent> {
|
||||
public GetPublicKeyRequest() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "getPublicKey";
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
public final class LauncherRequest extends Request<LauncherRequestEvent> implements WebSocketRequest {
|
||||
public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class);
|
||||
public static final Path C_BINARY_PATH = BINARY_PATH.getParent().resolve(IOHelper.getFileName(BINARY_PATH) + ".tmp");
|
||||
public static final boolean EXE_BINARY = IOHelper.hasExtension(BINARY_PATH, "exe");
|
||||
@LauncherNetworkAPI
|
||||
public final String secureHash;
|
||||
|
@ -46,53 +45,9 @@ public LauncherRequest() {
|
|||
secureSalt = Launcher.getConfig().secureCheckSalt;
|
||||
}
|
||||
|
||||
public static void update(LauncherRequestEvent result) throws IOException {
|
||||
List<String> args = new ArrayList<>(8);
|
||||
args.add(IOHelper.resolveJavaBin(null).toString());
|
||||
if (LogHelper.isDebugEnabled())
|
||||
args.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, Boolean.toString(LogHelper.isDebugEnabled())));
|
||||
args.add("-jar");
|
||||
args.add(BINARY_PATH.toString());
|
||||
ProcessBuilder builder = new ProcessBuilder(args.toArray(new String[0]));
|
||||
builder.inheritIO();
|
||||
|
||||
// Rewrite and start new instance
|
||||
if (result.binary != null)
|
||||
IOHelper.write(BINARY_PATH, result.binary);
|
||||
else {
|
||||
/*URLConnection connection = IOHelper.newConnection(new URL(result.url));
|
||||
connection.setDoOutput(true);
|
||||
connection.connect();
|
||||
try (OutputStream stream = connection.getOutputStream()) {
|
||||
IOHelper.transfer(BINARY_PATH, stream);
|
||||
}*/
|
||||
try {
|
||||
Files.deleteIfExists(C_BINARY_PATH);
|
||||
URL url = new URL(result.url);
|
||||
URLConnection connection = url.openConnection();
|
||||
try (InputStream in = connection.getInputStream()) {
|
||||
IOHelper.transfer(in, C_BINARY_PATH);
|
||||
}
|
||||
try (InputStream in = IOHelper.newInput(C_BINARY_PATH)) {
|
||||
IOHelper.transfer(in, BINARY_PATH);
|
||||
}
|
||||
Files.deleteIfExists(C_BINARY_PATH);
|
||||
} catch (Throwable e) {
|
||||
LogHelper.error(e);
|
||||
}
|
||||
}
|
||||
builder.start();
|
||||
|
||||
// Kill current instance
|
||||
JVMHelper.RUNTIME.exit(255);
|
||||
throw new AssertionError("Why Launcher wasn't restarted?!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LauncherRequestEvent requestDo(RequestService service) throws Exception {
|
||||
LauncherRequestEvent result = super.request(service);
|
||||
if (result.needUpdate) update(result);
|
||||
return result;
|
||||
return super.request(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -108,6 +108,8 @@ public void registerResults() {
|
|||
results.register("refreshToken", RefreshTokenRequestEvent.class);
|
||||
results.register("restore", RestoreRequestEvent.class);
|
||||
results.register("additionalData", AdditionalDataRequestEvent.class);
|
||||
results.register("clientProfileKey", FetchClientProfileKeyRequestEvent.class);
|
||||
results.register("getPublicKey", GetPublicKeyRequestEvent.class);
|
||||
resultsRegistered = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,10 +60,10 @@ protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Except
|
|||
final WebSocketFrame frame = (WebSocketFrame) msg;
|
||||
if (frame instanceof TextWebSocketFrame) {
|
||||
final TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
|
||||
clientJSONPoint.onMessage(textFrame.text());
|
||||
if (LogHelper.isDevEnabled()) {
|
||||
LogHelper.dev("Message: %s", textFrame.text());
|
||||
}
|
||||
clientJSONPoint.onMessage(textFrame.text());
|
||||
// uncomment to print request
|
||||
// logger.info(textFrame.text());
|
||||
} else if ((frame instanceof PingWebSocketFrame)) {
|
||||
|
|
|
@ -2,21 +2,34 @@
|
|||
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class LauncherTrustManager {
|
||||
private final X509Certificate[] trustSigners;
|
||||
private final List<X509Certificate> trustCache = new ArrayList<>();
|
||||
|
||||
@LauncherInject("launcher.certificatePinning")
|
||||
private static boolean isCertificatePinning;
|
||||
|
||||
public LauncherTrustManager(X509Certificate[] trustSigners) {
|
||||
this.trustSigners = trustSigners;
|
||||
if (requireCustomTrustStore()) {
|
||||
injectCertificates();
|
||||
}
|
||||
}
|
||||
|
||||
public LauncherTrustManager(List<byte[]> encodedCertificate) throws CertificateException {
|
||||
|
@ -29,6 +42,95 @@ public LauncherTrustManager(List<byte[]> encodedCertificate) throws CertificateE
|
|||
return null;
|
||||
}
|
||||
}).toArray(X509Certificate[]::new);
|
||||
if (requireCustomTrustStore()) {
|
||||
injectCertificates();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean requireCustomTrustStore() {
|
||||
return trustSigners != null && trustSigners.length != 0 && isCertificatePinning;
|
||||
}
|
||||
|
||||
private void injectCertificates() {
|
||||
try {
|
||||
// Получение списка всех существующих и действительных сертификатов из стандартного KeyStore JVM
|
||||
final Map<String, Certificate> jdkTrustStore = getDefaultKeyStore();
|
||||
// Создание нового KeyStore с дополнительными сертификатами.
|
||||
final KeyStore mergedTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
mergedTrustStore.load(null, new char[0]);
|
||||
|
||||
// добавление дополнительных сертификатов в новый KeyStore
|
||||
Arrays.stream(trustSigners).forEach(cert -> setCertificateEntry(mergedTrustStore, "injected-certificate" + UUID.randomUUID(), cert));
|
||||
|
||||
// добавление стандартных сертификатов в новый KeyStore
|
||||
jdkTrustStore.keySet().forEach(key -> setCertificateEntry(mergedTrustStore, key, jdkTrustStore.get(key)));
|
||||
|
||||
// Инициализация контекста. В случае неудачи допустимо прерывание процесса, но сертификаты добавлены не будут
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(mergedTrustStore);
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
|
||||
|
||||
// Установка контекста по умолчанию
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
|
||||
LogHelper.info("Successfully injected certificates to truststore");
|
||||
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | IOException | CertificateException e) {
|
||||
LogHelper.error("Error while modify existing keystore");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение набора стандартных сертификатов, вшитых в текущую сессию JVM
|
||||
*/
|
||||
private static Map<String, Certificate> getDefaultKeyStore() {
|
||||
// init existing keystore
|
||||
final Map<String, Certificate> jdkTrustStore = new HashMap<>();
|
||||
try {
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
Path ksPath = Paths.get(System.getProperty("java.home"), "lib", "security", "cacerts");
|
||||
keyStore.load(Files.newInputStream(ksPath), "changeit".toCharArray());
|
||||
|
||||
// getting all JDK/JRE certificates
|
||||
extractAllCertsAndPutInMap(keyStore, jdkTrustStore);
|
||||
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) {
|
||||
LogHelper.warning("Error while loading existing keystore");
|
||||
}
|
||||
return jdkTrustStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Извлечение существующих сертификатов из стандартного KeyStore текущий сессии JVM. Процесс не должен прерываться в случае неудачи
|
||||
*/
|
||||
private static void extractAllCertsAndPutInMap(KeyStore keyStore, Map<String, Certificate> placeToExport) {
|
||||
try {
|
||||
Collections.list(keyStore.aliases()).forEach(key -> extractCertAndPutInMap(keyStore, key, placeToExport));
|
||||
} catch (KeyStoreException e) {
|
||||
LogHelper.error("Error during extraction certificates from default keystore");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавление сертификата с именем name в KeyStore. Не должно прерывать общий процесс инъекции сертификатов, в случае неудачи.
|
||||
*/
|
||||
private static void setCertificateEntry(KeyStore keyStore, String name, Certificate cert) {
|
||||
try {
|
||||
keyStore.setCertificateEntry(name, cert);
|
||||
} catch (KeyStoreException e) {
|
||||
LogHelper.warning("Something went wrong while adding certificate " + name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Извлечение существующего сертификата из стандартного KeyStore текущий сессии JVM. Процесс не должен прерываться в случае неудачи
|
||||
*/
|
||||
private static void extractCertAndPutInMap(KeyStore keyStoreFromExtract, String key, Map<String, Certificate> placeToExtract) {
|
||||
try {
|
||||
if (keyStoreFromExtract.containsAlias(key)) {
|
||||
placeToExtract.put(key, keyStoreFromExtract.getCertificate(key));
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
LogHelper.warning("Error while extracting certificate " + key);
|
||||
}
|
||||
}
|
||||
|
||||
public CheckClassResult checkCertificates(X509Certificate[] certs, CertificateChecker checker) {
|
||||
|
|
|
@ -15,7 +15,7 @@ public class GsonManager {
|
|||
public void initGson() {
|
||||
gsonBuilder = CommonHelper.newBuilder();
|
||||
configGsonBuilder = CommonHelper.newBuilder();
|
||||
configGsonBuilder.setPrettyPrinting();
|
||||
configGsonBuilder.setPrettyPrinting().disableHtmlEscaping();
|
||||
registerAdapters(gsonBuilder);
|
||||
registerAdapters(configGsonBuilder);
|
||||
preConfigGson(configGsonBuilder);
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
public final class Version implements Comparable<Version> {
|
||||
|
||||
public static final int MAJOR = 5;
|
||||
public static final int MINOR = 2;
|
||||
public static final int PATCH = 13;
|
||||
public static final int MINOR = 3;
|
||||
public static final int PATCH = 0;
|
||||
public static final int BUILD = 1;
|
||||
public static final Version.Type RELEASE = Type.STABLE;
|
||||
public final int major;
|
||||
|
|
|
@ -5,7 +5,7 @@ public final class CommandException extends Exception {
|
|||
|
||||
|
||||
public CommandException(String message) {
|
||||
super(message);
|
||||
super(message, null, false, false);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -16,27 +16,9 @@
|
|||
|
||||
public final class CommonHelper {
|
||||
|
||||
private static ScriptEngineFactory nashornFactory;
|
||||
|
||||
static {
|
||||
try {
|
||||
ScriptEngineManager scriptManager = new ScriptEngineManager();
|
||||
nashornFactory = getEngineFactories(scriptManager);
|
||||
} catch (Throwable e) {
|
||||
nashornFactory = null;
|
||||
}
|
||||
}
|
||||
|
||||
private CommonHelper() {
|
||||
}
|
||||
|
||||
private static ScriptEngineFactory getEngineFactories(ScriptEngineManager manager) {
|
||||
// Метод похож на костыль но таковым не является, ибо единоразовое получение фактории быстрее, чем её переполучение на ходу.
|
||||
for (ScriptEngineFactory fact : manager.getEngineFactories())
|
||||
if (fact.getNames().contains("nashorn") || fact.getNames().contains("Nashorn")) return fact;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String low(String s) {
|
||||
return s.toLowerCase(Locale.US);
|
||||
}
|
||||
|
@ -57,11 +39,9 @@ public static String multiReplace(Pattern[] pattern, String from, String replace
|
|||
return tmp != null ? tmp : from;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static ScriptEngine newScriptEngine() {
|
||||
if (nashornFactory == null) {
|
||||
throw new UnsupportedOperationException("ScriptEngine not supported");
|
||||
}
|
||||
return nashornFactory.getScriptEngine();
|
||||
throw new UnsupportedOperationException("ScriptEngine not supported");
|
||||
}
|
||||
|
||||
public static Thread newThread(String name, boolean daemon, Runnable runnable) {
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
package pro.gravit.utils.helper;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
public final class CryptoHelper {
|
||||
private CryptoHelper() {
|
||||
}
|
||||
|
||||
public static byte[] encode(byte[] txt, String pKey) {
|
||||
Objects.requireNonNull(txt);
|
||||
Objects.requireNonNull(pKey);
|
||||
byte[] key = SecurityHelper.fromHex(pKey);
|
||||
byte[] res = new byte[txt.length];
|
||||
for (int i = 0; i < txt.length; i++)
|
||||
res[i] = (byte) (txt[i] ^ key[i % key.length]);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static byte[] decode(byte[] pText, String pKey) {
|
||||
Objects.requireNonNull(pText);
|
||||
Objects.requireNonNull(pKey);
|
||||
byte[] res = new byte[pText.length];
|
||||
byte[] key = SecurityHelper.fromHex(pKey);
|
||||
for (int i = 0; i < pText.length; i++)
|
||||
res[i] = (byte) (pText[i] ^ key[i % key.length]);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static void encodeOrig(byte[] txt, String pKey) {
|
||||
Objects.requireNonNull(txt);
|
||||
Objects.requireNonNull(pKey);
|
||||
byte[] key = SecurityHelper.fromHex(pKey);
|
||||
for (int i = 0; i < txt.length; i++)
|
||||
txt[i] = (byte) (txt[i] ^ key[i % key.length]);
|
||||
}
|
||||
|
||||
public static void decodeOrig(byte[] pText, String pKey) {
|
||||
Objects.requireNonNull(pText);
|
||||
Objects.requireNonNull(pKey);
|
||||
byte[] key = SecurityHelper.fromHex(pKey);
|
||||
for (int i = 0; i < pText.length; i++)
|
||||
pText[i] = (byte) (pText[i] ^ key[i % key.length]);
|
||||
}
|
||||
|
||||
public static String randomToken(int depth) {
|
||||
VerifyHelper.verifyInt(depth, VerifyHelper.POSITIVE, "Depth must be positive");
|
||||
return SecurityHelper.toHex(SecurityHelper.randomBytes(SecurityHelper.TOKEN_LENGTH * depth));
|
||||
}
|
||||
|
||||
public static class StaticRandom implements LongSupplier {
|
||||
private volatile long rnd;
|
||||
|
||||
public StaticRandom(long rnd) {
|
||||
this.rnd = rnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAsLong() {
|
||||
this.rnd ^= (this.rnd << 21);
|
||||
this.rnd ^= (this.rnd >>> 35);
|
||||
this.rnd ^= (this.rnd << 4);
|
||||
return this.rnd;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,11 @@ public final class JVMHelper {
|
|||
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
||||
// System properties
|
||||
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
||||
|
||||
@Deprecated
|
||||
public static final int OS_BITS = getCorrectOSArch();
|
||||
|
||||
public static final ARCH ARCH_TYPE = getArch(System.getProperty("os.arch"));
|
||||
public static final int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
|
||||
public static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
|
||||
// Public static fields
|
||||
|
@ -42,6 +46,24 @@ public final class JVMHelper {
|
|||
private JVMHelper() {
|
||||
}
|
||||
|
||||
public enum ARCH {
|
||||
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
|
||||
|
||||
public final String name;
|
||||
|
||||
ARCH(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static ARCH getArch(String arch) {
|
||||
if(arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
|
||||
if(arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
|
||||
if(arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
|
||||
if(arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
|
||||
throw new InternalError(String.format("Unsupported arch '%s'", arch));
|
||||
}
|
||||
|
||||
public static int getVersion() {
|
||||
String version = System.getProperty("java.version");
|
||||
if (version.startsWith("1.")) {
|
||||
|
@ -132,6 +154,7 @@ public static void checkStackTrace(Class<?> mainClass) {
|
|||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private static int getCorrectOSArch() {
|
||||
// As always, mustdie must die
|
||||
if (OS_TYPE == OS.MUSTDIE)
|
||||
|
@ -147,6 +170,7 @@ public static String getEnvPropertyCaseSensitive(String name) {
|
|||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static boolean isJVMMatchesSystemArch() {
|
||||
return JVM_BITS == OS_BITS;
|
||||
}
|
||||
|
@ -178,10 +202,6 @@ public static void verifySystemProperties(Class<?> mainClass, boolean requireSys
|
|||
|
||||
// Verify system and java architecture
|
||||
LogHelper.debug("Verifying JVM architecture");
|
||||
if (!isJVMMatchesSystemArch()) {
|
||||
LogHelper.warning("Java and OS architecture mismatch");
|
||||
LogHelper.warning("It's recommended to download %d-bit JRE", OS_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
public enum OS {
|
||||
|
|
|
@ -185,14 +185,14 @@ public static class JavaVersion {
|
|||
public final Path jvmDir;
|
||||
public final int version;
|
||||
public final int build;
|
||||
public final int bitness;
|
||||
public final JVMHelper.ARCH arch;
|
||||
public boolean enabledJavaFX;
|
||||
|
||||
public JavaVersion(Path jvmDir, int version) {
|
||||
this.jvmDir = jvmDir;
|
||||
this.version = version;
|
||||
this.build = 0;
|
||||
this.bitness = JVMHelper.OS_BITS;
|
||||
this.arch = JVMHelper.ARCH_TYPE;
|
||||
this.enabledJavaFX = true;
|
||||
}
|
||||
|
||||
|
@ -200,20 +200,20 @@ public JavaVersion(Path jvmDir, int version, int build, boolean enabledJavaFX) {
|
|||
this.jvmDir = jvmDir;
|
||||
this.version = version;
|
||||
this.build = build;
|
||||
this.bitness = JVMHelper.OS_BITS;
|
||||
this.arch = JVMHelper.ARCH_TYPE;
|
||||
this.enabledJavaFX = enabledJavaFX;
|
||||
}
|
||||
|
||||
public JavaVersion(Path jvmDir, int version, int build, int bitness, boolean enabledJavaFX) {
|
||||
public JavaVersion(Path jvmDir, int version, int build, JVMHelper.ARCH arch, boolean enabledJavaFX) {
|
||||
this.jvmDir = jvmDir;
|
||||
this.version = version;
|
||||
this.build = build;
|
||||
this.bitness = bitness;
|
||||
this.arch = arch;
|
||||
this.enabledJavaFX = enabledJavaFX;
|
||||
}
|
||||
|
||||
public static JavaVersion getCurrentJavaVersion() {
|
||||
return new JavaVersion(Paths.get(System.getProperty("java.home")), JVMHelper.getVersion(), JVMHelper.JVM_BUILD, JVMHelper.JVM_BITS, isCurrentJavaSupportJavaFX());
|
||||
return new JavaVersion(Paths.get(System.getProperty("java.home")), JVMHelper.getVersion(), JVMHelper.JVM_BUILD, JVMHelper.ARCH_TYPE, isCurrentJavaSupportJavaFX());
|
||||
}
|
||||
|
||||
private static boolean isCurrentJavaSupportJavaFX() {
|
||||
|
@ -238,21 +238,20 @@ public static JavaVersion getByPath(Path jvmDir) throws IOException {
|
|||
}
|
||||
Path releaseFile = jvmDir.resolve("release");
|
||||
JavaVersionAndBuild versionAndBuild;
|
||||
int bitness = JVMHelper.OS_BITS;
|
||||
JVMHelper.ARCH arch = JVMHelper.ARCH_TYPE;
|
||||
if (IOHelper.isFile(releaseFile)) {
|
||||
Properties properties = new Properties();
|
||||
properties.load(IOHelper.newReader(releaseFile));
|
||||
versionAndBuild = getJavaVersion(properties.getProperty("JAVA_VERSION").replaceAll("\"", ""));
|
||||
String arch = properties.getProperty("JAVA_VERSION").replaceAll("\"", "");
|
||||
if (arch.contains("x86_64")) {
|
||||
bitness = 64;
|
||||
} else if (arch.contains("x86") || arch.contains("x32")) {
|
||||
bitness = 32;
|
||||
try {
|
||||
arch = JVMHelper.getArch(properties.getProperty("OS_ARCH").replaceAll("\"", ""));
|
||||
} catch (Throwable ignored) {
|
||||
arch = null;
|
||||
}
|
||||
} else {
|
||||
versionAndBuild = new JavaVersionAndBuild(isExistExtJavaLibrary(jvmDir, "jfxrt") ? 8 : 9, 0);
|
||||
}
|
||||
JavaVersion resultJavaVersion = new JavaVersion(jvmDir, versionAndBuild.version, versionAndBuild.build, bitness, false);
|
||||
JavaVersion resultJavaVersion = new JavaVersion(jvmDir, versionAndBuild.version, versionAndBuild.build, arch, false);
|
||||
if (versionAndBuild.version <= 8) {
|
||||
resultJavaVersion.enabledJavaFX = isExistExtJavaLibrary(jvmDir, "jfxrt");
|
||||
} else {
|
||||
|
|
|
@ -387,6 +387,16 @@ public static byte[] sign(byte[] bytes, ECPrivateKey privateKey) {
|
|||
}
|
||||
}
|
||||
|
||||
public static byte[] sign(byte[] bytes, RSAPrivateKey privateKey) {
|
||||
Signature signature = newRSASignSignature(privateKey);
|
||||
try {
|
||||
signature.update(bytes);
|
||||
return signature.sign();
|
||||
} catch (SignatureException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String toHex(byte[] bytes) {
|
||||
if (bytes == null) {
|
||||
|
|
|
@ -22,7 +22,12 @@ public final class JVMHelper {
|
|||
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
||||
// System properties
|
||||
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
||||
|
||||
@Deprecated
|
||||
public static final int OS_BITS = getCorrectOSArch();
|
||||
|
||||
public static final ARCH ARCH_TYPE = getArch(System.getProperty("os.arch"));
|
||||
|
||||
public static final int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
|
||||
// Public static fields
|
||||
public static final Runtime RUNTIME = Runtime.getRuntime();
|
||||
|
@ -41,6 +46,24 @@ public final class JVMHelper {
|
|||
private JVMHelper() {
|
||||
}
|
||||
|
||||
public enum ARCH {
|
||||
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
|
||||
|
||||
public final String name;
|
||||
|
||||
ARCH(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static ARCH getArch(String arch) {
|
||||
if(arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
|
||||
if(arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
|
||||
if(arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
|
||||
if(arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
|
||||
throw new InternalError(String.format("Unsupported arch '%s'", arch));
|
||||
}
|
||||
|
||||
public static int getVersion() {
|
||||
//System.out.println("[DEBUG] JVMHelper 11 version");
|
||||
return Runtime.version().feature();
|
||||
|
@ -108,6 +131,7 @@ public static void checkStackTrace(Class<?> mainClass) {
|
|||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private static int getCorrectOSArch() {
|
||||
// As always, mustdie must die
|
||||
if (OS_TYPE == OS.MUSTDIE)
|
||||
|
@ -123,6 +147,7 @@ public static String getEnvPropertyCaseSensitive(String name) {
|
|||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static boolean isJVMMatchesSystemArch() {
|
||||
return JVM_BITS == OS_BITS;
|
||||
}
|
||||
|
@ -154,10 +179,6 @@ public static void verifySystemProperties(Class<?> mainClass, boolean requireSys
|
|||
|
||||
// Verify system and java architecture
|
||||
LogHelper.debug("Verifying JVM architecture");
|
||||
if (!isJVMMatchesSystemArch()) {
|
||||
LogHelper.warning("Java and OS architecture mismatch");
|
||||
LogHelper.warning("It's recommended to download %d-bit JRE", OS_BITS);
|
||||
}
|
||||
}
|
||||
|
||||
public enum OS {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import pro.gravit.launcher.ClientPermissions;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launcher.LauncherConfig;
|
||||
import pro.gravit.launcher.api.KeyService;
|
||||
import pro.gravit.launcher.config.JsonConfigurable;
|
||||
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||
import pro.gravit.launcher.events.request.ProfilesRequestEvent;
|
||||
|
@ -24,6 +25,7 @@
|
|||
import pro.gravit.utils.PublicURLClassLoader;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.file.Path;
|
||||
|
@ -134,6 +136,9 @@ public void run(String... args) throws Throwable {
|
|||
restore();
|
||||
getProfiles();
|
||||
}
|
||||
if(config.encodedServerRsaPublicKey != null) {
|
||||
KeyService.serverRsaPublicKey = SecurityHelper.toPublicRSAKey(config.encodedServerRsaPublicKey);
|
||||
}
|
||||
String classname = (config.mainclass == null || config.mainclass.isEmpty()) ? args[0] : config.mainclass;
|
||||
if (classname.length() == 0) {
|
||||
LogHelper.error("MainClass not found. Please set MainClass for ServerWrapper.json or first commandline argument");
|
||||
|
@ -231,6 +236,10 @@ public static final class Config {
|
|||
public Map<String, String> extendedTokens;
|
||||
public LauncherConfig.LauncherEnvironment env;
|
||||
public ModuleConf moduleConf = new ModuleConf();
|
||||
|
||||
public byte[] encodedServerRsaPublicKey;
|
||||
|
||||
public byte[] encodedServerEcPublicKey;
|
||||
}
|
||||
|
||||
public static final class ModuleConf {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package pro.gravit.launcher.server.setup;
|
||||
|
||||
import pro.gravit.launcher.events.request.GetPublicKeyRequestEvent;
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.request.Request;
|
||||
import pro.gravit.launcher.request.auth.GetPublicKeyRequest;
|
||||
import pro.gravit.launcher.request.websockets.StdWebSocketService;
|
||||
import pro.gravit.launcher.server.ServerWrapper;
|
||||
import pro.gravit.utils.PublicURLClassLoader;
|
||||
|
@ -79,6 +81,9 @@ public void run() throws Exception {
|
|||
try {
|
||||
wrapper.restore();
|
||||
wrapper.getProfiles();
|
||||
GetPublicKeyRequestEvent publicKeyRequestEvent = new GetPublicKeyRequest().request();
|
||||
wrapper.config.encodedServerRsaPublicKey = publicKeyRequestEvent.rsaPublicKey;
|
||||
wrapper.config.encodedServerEcPublicKey = publicKeyRequestEvent.ecdsaPublicKey;
|
||||
break;
|
||||
} catch (Throwable e) {
|
||||
LogHelper.error(e);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
id 'org.openjfx.javafxplugin' version '0.0.10' apply false
|
||||
}
|
||||
group = 'pro.gravit.launcher'
|
||||
version = '5.2.13'
|
||||
version = '5.3.0'
|
||||
|
||||
apply from: 'props.gradle'
|
||||
|
||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
6
gradlew
vendored
6
gradlew
vendored
|
@ -205,6 +205,12 @@ set -- \
|
|||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
|
14
gradlew.bat
vendored
14
gradlew.bat
vendored
|
@ -14,7 +14,7 @@
|
|||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
|
@ -25,7 +25,7 @@
|
|||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
|
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
|||
Subproject commit d3722bf136b8c8c8da3dea879d5d2015e84b0f8c
|
||||
Subproject commit eb3145d3e75ff3557fe9f9fc87dd5ea0737ff3a2
|
Loading…
Reference in a new issue