mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-03-30 21:18:17 +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.events.request.GetAvailabilityAuthRequestEvent;
|
||||||
import pro.gravit.launcher.profiles.Texture;
|
import pro.gravit.launcher.profiles.Texture;
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
|
import pro.gravit.launcher.request.secure.HardwareReportRequest;
|
||||||
import pro.gravit.launchserver.HttpRequester;
|
import pro.gravit.launchserver.HttpRequester;
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
import pro.gravit.launchserver.auth.AuthException;
|
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.UserSupportProperties;
|
||||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
||||||
import pro.gravit.launchserver.helper.HttpHelper;
|
import pro.gravit.launchserver.helper.HttpHelper;
|
||||||
|
@ -19,12 +23,9 @@
|
||||||
import pro.gravit.utils.helper.CommonHelper;
|
import pro.gravit.utils.helper.CommonHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class HttpAuthCoreProvider extends AuthCoreProvider {
|
public class HttpAuthCoreProvider extends AuthCoreProvider implements AuthSupportHardware {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
private transient HttpRequester requester;
|
private transient HttpRequester requester;
|
||||||
public String bearerToken;
|
public String bearerToken;
|
||||||
|
@ -32,12 +33,23 @@ public class HttpAuthCoreProvider extends AuthCoreProvider {
|
||||||
public String getUserByLoginUrl;
|
public String getUserByLoginUrl;
|
||||||
public String getUserByUUIDUrl;
|
public String getUserByUUIDUrl;
|
||||||
public String getUserByTokenUrl;
|
public String getUserByTokenUrl;
|
||||||
public String getAuthDetails;
|
public String getAuthDetailsUrl;
|
||||||
public String refreshTokenUrl;
|
public String refreshTokenUrl;
|
||||||
public String authorizeUrl;
|
public String authorizeUrl;
|
||||||
public String joinServerUrl;
|
public String joinServerUrl;
|
||||||
public String checkServerUrl;
|
public String checkServerUrl;
|
||||||
public String updateServerIdUrl;
|
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
|
@Override
|
||||||
public User getUserByUsername(String username) {
|
public User getUserByUsername(String username) {
|
||||||
try {
|
try {
|
||||||
|
@ -73,11 +85,11 @@ public User getUserByUUID(UUID uuid) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
||||||
if(getAuthDetails == null) {
|
if(getAuthDetailsUrl == null) {
|
||||||
return super.getDetails(client);
|
return super.getDetails(client);
|
||||||
}
|
}
|
||||||
try {
|
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;
|
return result.details;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
|
@ -113,7 +125,7 @@ public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthRespon
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return requester.send(requester.post(refreshTokenUrl, new RefreshTokenRequest(refreshToken, context),
|
return requester.send(requester.post(refreshTokenUrl, new RefreshTokenRequest(refreshToken, context),
|
||||||
null), AuthManager.AuthReport.class).getOrThrow();
|
null), HttpAuthReport.class).getOrThrow().toAuthReport();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
return null;
|
return null;
|
||||||
|
@ -133,6 +145,128 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
|
||||||
return result.getOrThrow().toAuthReport();
|
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,
|
public record HttpAuthReport(String minecraftAccessToken, String oauthAccessToken,
|
||||||
String oauthRefreshToken, long oauthExpire,
|
String oauthRefreshToken, long oauthExpire,
|
||||||
HttpUserSession session) {
|
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 String username;
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
private String serverId;
|
private String serverId;
|
||||||
|
@ -258,19 +412,22 @@ public static class HttpUser implements User, UserSupportTextures, UserSupportPr
|
||||||
private Texture cloak;
|
private Texture cloak;
|
||||||
private Map<String, Texture> assets;
|
private Map<String, Texture> assets;
|
||||||
private Map<String, String> properties;
|
private Map<String, String> properties;
|
||||||
|
private long hwidId;
|
||||||
|
private transient HttpUserHardware hardware;
|
||||||
|
|
||||||
public HttpUser() {
|
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.username = username;
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.serverId = serverId;
|
this.serverId = serverId;
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
this.permissions = permissions;
|
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.username = username;
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.serverId = serverId;
|
this.serverId = serverId;
|
||||||
|
@ -278,9 +435,10 @@ public HttpUser(String username, UUID uuid, String serverId, String accessToken,
|
||||||
this.permissions = permissions;
|
this.permissions = permissions;
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
this.cloak = cloak;
|
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.username = username;
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.serverId = serverId;
|
this.serverId = serverId;
|
||||||
|
@ -289,9 +447,10 @@ public HttpUser(String username, UUID uuid, String serverId, String accessToken,
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
this.cloak = cloak;
|
this.cloak = cloak;
|
||||||
this.properties = properties;
|
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.username = username;
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.serverId = serverId;
|
this.serverId = serverId;
|
||||||
|
@ -299,6 +458,7 @@ public HttpUser(String username, UUID uuid, String serverId, String accessToken,
|
||||||
this.permissions = permissions;
|
this.permissions = permissions;
|
||||||
this.assets = assets;
|
this.assets = assets;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
|
this.hwidId = hwidId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -374,8 +534,17 @@ public String toString() {
|
||||||
", permissions=" + permissions +
|
", permissions=" + permissions +
|
||||||
", assets=" + getAssets() +
|
", assets=" + getAssets() +
|
||||||
", properties=" + properties +
|
", 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 {
|
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()
|
return Jwts.builder()
|
||||||
.setIssuer("LaunchServer")
|
.setIssuer("LaunchServer")
|
||||||
.setSubject(username)
|
.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())
|
.claim("hardware", hardware.getId())
|
||||||
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
|
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
|
||||||
.compact();
|
.compact();
|
||||||
|
@ -132,7 +132,7 @@ public String createPublicKeyToken(String username, byte[] publicKey) {
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
.setIssuer("LaunchServer")
|
.setIssuer("LaunchServer")
|
||||||
.setSubject(username)
|
.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))
|
.claim("publicKey", Base64.getEncoder().encodeToString(publicKey))
|
||||||
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
|
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
|
||||||
.compact();
|
.compact();
|
||||||
|
|
|
@ -124,9 +124,8 @@ protected void initProps() {
|
||||||
server.runtime.clientCheckSecret.concat(".").concat(launcherSalt));
|
server.runtime.clientCheckSecret.concat(".").concat(launcherSalt));
|
||||||
properties.put("runtimeconfig.secureCheckHash", Base64.getEncoder().encodeToString(launcherSecureHash));
|
properties.put("runtimeconfig.secureCheckHash", Base64.getEncoder().encodeToString(launcherSecureHash));
|
||||||
properties.put("runtimeconfig.secureCheckSalt", launcherSalt);
|
properties.put("runtimeconfig.secureCheckSalt", launcherSalt);
|
||||||
//LogHelper.debug("[checkSecure] %s: %s", launcherSalt, Arrays.toString(launcherSecureHash));
|
if (server.runtime.unlockSecret == null) server.runtime.unlockSecret = SecurityHelper.randomStringToken();
|
||||||
if (server.runtime.oemUnlockKey == null) server.runtime.oemUnlockKey = SecurityHelper.randomStringToken();
|
properties.put("runtimeconfig.unlockSecret", server.runtime.unlockSecret);
|
||||||
properties.put("runtimeconfig.oemUnlockKey", server.runtime.oemUnlockKey);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class Launch4JTask implements LauncherBuildTask, BuildExeMainTask {
|
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 String VERSION = Version.getVersion().getVersionString();
|
||||||
private static final int BUILD = Version.getVersion().build;
|
private static final int BUILD = Version.getVersion().build;
|
||||||
private final Path faviconFile;
|
private final Path faviconFile;
|
||||||
|
|
|
@ -1,25 +1,40 @@
|
||||||
package pro.gravit.launchserver.command.hash;
|
package pro.gravit.launchserver.command.hash;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
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.LaunchServer;
|
||||||
import pro.gravit.launchserver.command.Command;
|
import pro.gravit.launchserver.command.Command;
|
||||||
|
import pro.gravit.utils.Downloader;
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
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.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public final class DownloadAssetCommand extends Command {
|
public final class DownloadAssetCommand extends Command {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
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) {
|
public DownloadAssetCommand(LaunchServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getArgsDescription() {
|
public String getArgsDescription() {
|
||||||
return "[version] [dir]";
|
return "[version] [dir] (mojang/mirror)";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -32,20 +47,94 @@ public void invoke(String... args) throws Exception {
|
||||||
verifyArgs(args, 2);
|
verifyArgs(args, 2);
|
||||||
//Version version = Version.byName(args[0]);
|
//Version version = Version.byName(args[0]);
|
||||||
String versionName = args[0];
|
String versionName = args[0];
|
||||||
|
String type = args.length > 2 ? args[2] : "mojang";
|
||||||
String dirName = IOHelper.verifyFileName(args[1]);
|
String dirName = IOHelper.verifyFileName(args[1]);
|
||||||
Path assetDir = server.updatesDir.resolve(dirName);
|
Path assetDir = server.updatesDir.resolve(dirName);
|
||||||
|
|
||||||
// Create asset dir
|
// Create asset dir
|
||||||
logger.info("Creating asset dir: '{}'", dirName);
|
if(Files.notExists(assetDir)) {
|
||||||
Files.createDirectory(assetDir);
|
logger.info("Creating asset dir: '{}'", dirName);
|
||||||
|
Files.createDirectory(assetDir);
|
||||||
|
}
|
||||||
|
|
||||||
// Download required asset
|
if(type.equals("mojang")) {
|
||||||
logger.info("Downloading asset, it may take some time");
|
HttpRequester requester = new HttpRequester();
|
||||||
//HttpDownloader.downloadZip(server.mirrorManager.getDefaultMirror().getAssetsURL(version.name), assetDir);
|
logger.info("Fetch versions from {}", MINECRAFT_VERSIONS_URL);
|
||||||
server.mirrorManager.downloadZip(assetDir, "assets/%s.zip", versionName);
|
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
|
// Finished
|
||||||
server.syncUpdatesDir(Collections.singleton(dirName));
|
server.syncUpdatesDir(Collections.singleton(dirName));
|
||||||
logger.info("Asset successfully downloaded: '{}'", 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) {
|
if (version.compareTo(ClientProfile.Version.MC164) <= 0) {
|
||||||
logger.warn("Minecraft 1.6.4 and below not supported. Use at your own risk");
|
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) {
|
for (MakeProfileHelper.MakeProfileOption option : options) {
|
||||||
logger.debug("Detected option {}", option.getClass().getSimpleName());
|
logger.debug("Detected option {}", option.getClass().getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
|
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
|
||||||
public class MakeProfileCommand extends Command {
|
public class MakeProfileCommand extends Command {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
|
@ -32,7 +33,7 @@ public String getUsageDescription() {
|
||||||
public void invoke(String... args) throws Exception {
|
public void invoke(String... args) throws Exception {
|
||||||
verifyArgs(args, 3);
|
verifyArgs(args, 3);
|
||||||
ClientProfile.Version version = ClientProfile.Version.byName(args[1]);
|
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) {
|
for (MakeProfileHelper.MakeProfileOption option : options) {
|
||||||
logger.info("Detected option {}", option);
|
logger.info("Detected option {}", option);
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ public Path process(Path inputFile) throws IOException {
|
||||||
if (component.enabled) {
|
if (component.enabled) {
|
||||||
Configuration proguard_cfg = new Configuration();
|
Configuration proguard_cfg = new Configuration();
|
||||||
if (!checkJMods(IOHelper.JVM_DIR.resolve("jmods"))) {
|
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);
|
Path jfxPath = tryFindOpenJFXPath(IOHelper.JVM_DIR);
|
||||||
if (checkFXJMods(IOHelper.JVM_DIR.resolve("jmods"))) {
|
if (checkFXJMods(IOHelper.JVM_DIR.resolve("jmods"))) {
|
||||||
|
@ -143,8 +143,7 @@ public Path process(Path inputFile) throws IOException {
|
||||||
} else if (jfxPath != null && checkFXJMods(jfxPath)) {
|
} else if (jfxPath != null && checkFXJMods(jfxPath)) {
|
||||||
logger.debug("JMods resolved in {}", jfxPath.toString());
|
logger.debug("JMods resolved in {}", jfxPath.toString());
|
||||||
} else {
|
} else {
|
||||||
logger.error("JavaFX jmods not found. May be install OpenJFX?");
|
throw new RuntimeException("JavaFX jmods not found. May be install OpenJFX?");
|
||||||
jfxPath = null;
|
|
||||||
}
|
}
|
||||||
ConfigurationParser parser = new ConfigurationParser(proguardConf.buildConfig(inputFile, outputJar, jfxPath == null ? new Path[0] : new Path[]{jfxPath}),
|
ConfigurationParser parser = new ConfigurationParser(proguardConf.buildConfig(inputFile, outputJar, jfxPath == null ? new Path[0] : new Path[]{jfxPath}),
|
||||||
proguardConf.proguard.toFile(), System.getProperties());
|
proguardConf.proguard.toFile(), System.getProperties());
|
||||||
|
|
|
@ -45,9 +45,9 @@ public final class LaunchServerConfig {
|
||||||
|
|
||||||
public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
|
public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
|
||||||
LaunchServerConfig newConfig = new LaunchServerConfig();
|
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 = new LaunchServerConfig.ExeConf();
|
||||||
newConfig.launch4j.enabled = true;
|
newConfig.launch4j.enabled = false;
|
||||||
newConfig.launch4j.copyright = "© GravitLauncher Team";
|
newConfig.launch4j.copyright = "© GravitLauncher Team";
|
||||||
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
|
newConfig.launch4j.fileDesc = "GravitLauncher ".concat(Version.getVersion().getVersionString());
|
||||||
newConfig.launch4j.fileVer = Version.getVersion().getVersionString().concat(".").concat(String.valueOf(Version.getVersion().patch));
|
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");
|
boolean updateMirror = Boolean.getBoolean("launchserver.config.disableUpdateMirror");
|
||||||
if(!updateMirror) {
|
if(!updateMirror) {
|
||||||
for(int i=0;i < mirrors.length;++i) {
|
for(int i=0;i < mirrors.length;++i) {
|
||||||
if("https://mirror.gravit.pro/".equals(mirrors[i])) {
|
if("https://mirror.gravit.pro/5.2.x/".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'");
|
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.2.x/";
|
mirrors[i] = "https://mirror.gravit.pro/5.3.x/";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,8 +278,6 @@ public static class LauncherConf {
|
||||||
|
|
||||||
public static class NettyConfig {
|
public static class NettyConfig {
|
||||||
public boolean fileServerEnabled;
|
public boolean fileServerEnabled;
|
||||||
@Deprecated
|
|
||||||
public boolean sendExceptionEnabled;
|
|
||||||
public boolean ipForwarding;
|
public boolean ipForwarding;
|
||||||
public boolean disableWebApiInterface;
|
public boolean disableWebApiInterface;
|
||||||
public boolean showHiddenFiles;
|
public boolean showHiddenFiles;
|
||||||
|
@ -289,6 +287,8 @@ public static class NettyConfig {
|
||||||
public String address;
|
public String address;
|
||||||
public Map<String, LaunchServerConfig.NettyUpdatesBind> bindings = new HashMap<>();
|
public Map<String, LaunchServerConfig.NettyUpdatesBind> bindings = new HashMap<>();
|
||||||
public NettyPerformanceConfig performance;
|
public NettyPerformanceConfig performance;
|
||||||
|
|
||||||
|
public NettySecurityConfig security = new NettySecurityConfig();
|
||||||
public NettyBindAddress[] binds;
|
public NettyBindAddress[] binds;
|
||||||
public LogLevel logLevel = LogLevel.DEBUG;
|
public LogLevel logLevel = LogLevel.DEBUG;
|
||||||
}
|
}
|
||||||
|
@ -298,7 +298,6 @@ public static class NettyPerformanceConfig {
|
||||||
public int bossThread;
|
public int bossThread;
|
||||||
public int workerThread;
|
public int workerThread;
|
||||||
public int schedulerThread;
|
public int schedulerThread;
|
||||||
public long sessionLifetimeMs = 24 * 60 * 60 * 1000;
|
|
||||||
public int maxWebSocketRequestBytes = 1024 * 1024;
|
public int maxWebSocketRequestBytes = 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,4 +310,11 @@ public NettyBindAddress(String address, int port) {
|
||||||
this.port = 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();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
public String passwordEncryptKey;
|
public String passwordEncryptKey;
|
||||||
public String runtimeEncryptKey;
|
public String runtimeEncryptKey;
|
||||||
public String oemUnlockKey;
|
public String unlockSecret;
|
||||||
public String registerApiKey;
|
public String registerApiKey;
|
||||||
public String clientCheckSecret;
|
public String clientCheckSecret;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,11 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
|
||||||
ClientProfileBuilder builder = new ClientProfileBuilder();
|
ClientProfileBuilder builder = new ClientProfileBuilder();
|
||||||
builder.setVersion(version.name);
|
builder.setVersion(version.name);
|
||||||
builder.setDir(title);
|
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.setAssetIndex(version.name);
|
||||||
builder.setInfo("Информация о сервере");
|
builder.setInfo("Информация о сервере");
|
||||||
builder.setTitle(title);
|
builder.setTitle(title);
|
||||||
|
@ -42,11 +46,16 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
|
||||||
Set<OptionalFile> optionals = new HashSet<>();
|
Set<OptionalFile> optionals = new HashSet<>();
|
||||||
jvmArgs.add("-XX:+DisableAttachMechanism");
|
jvmArgs.add("-XX:+DisableAttachMechanism");
|
||||||
// Official Mojang launcher java arguments
|
// Official Mojang launcher java arguments
|
||||||
jvmArgs.add("-XX:+UseG1GC");
|
if(version.compareTo(ClientProfile.Version.MC112) <= 0) {
|
||||||
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
|
jvmArgs.add("-XX:+UseConcMarkSweepGC");
|
||||||
jvmArgs.add("-XX:G1NewSizePercent=20");
|
jvmArgs.add("-XX:+CMSIncrementalMode");
|
||||||
jvmArgs.add("-XX:MaxGCPauseMillis=50");
|
} else if(version.compareTo(ClientProfile.Version.MC118) <= 0) { // 1.13 - 1.16.5
|
||||||
jvmArgs.add("-XX:G1HeapRegionSize=32M");
|
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<MakeProfileOptionForge> forge = findOption(options, MakeProfileOptionForge.class);
|
||||||
Optional<MakeProfileOptionFabric> fabric = findOption(options, MakeProfileOptionFabric.class);
|
Optional<MakeProfileOptionFabric> fabric = findOption(options, MakeProfileOptionFabric.class);
|
||||||
|
@ -192,7 +201,7 @@ private static String getLog4jVersion(Path dir) throws IOException {
|
||||||
return null;
|
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);
|
List<MakeProfileOption> options = new ArrayList<>(2);
|
||||||
if (Files.exists(dir.resolve("forge.jar"))) {
|
if (Files.exists(dir.resolve("forge.jar"))) {
|
||||||
options.add(new MakeProfileOptionForge());
|
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"))) {
|
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());
|
options.add(new MakeProfileOptionLaunchWrapper());
|
||||||
}
|
}
|
||||||
|
if(globalAssets) {
|
||||||
|
options.add(new MakeProfileOptionGlobalAssets());
|
||||||
|
}
|
||||||
return options.toArray(new MakeProfileOption[0]);
|
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 static class MakeProfileOptionFabric implements MakeProfileOption {
|
||||||
public String jimfsPath;
|
public String jimfsPath;
|
||||||
public String guavaPath;
|
public String guavaPath;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||||
import pro.gravit.launchserver.auth.core.User;
|
import pro.gravit.launchserver.auth.core.User;
|
||||||
import pro.gravit.launchserver.auth.core.UserSession;
|
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.UserSupportProperties;
|
||||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
||||||
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
||||||
|
@ -26,7 +27,13 @@
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class AuthManager {
|
public class AuthManager {
|
||||||
|
@ -180,6 +187,10 @@ public void internalAuth(Client client, AuthResponse.ConnectTypes authType, Auth
|
||||||
client.uuid = uuid;
|
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 {
|
public CheckServerReport checkServer(Client client, String username, String serverID) throws IOException {
|
||||||
if (client.auth == null) return null;
|
if (client.auth == null) return null;
|
||||||
User user = client.auth.core.checkServer(client, username, serverID);
|
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.WebSocketServerResponse;
|
||||||
import pro.gravit.launchserver.socket.response.auth.*;
|
import pro.gravit.launchserver.socket.response.auth.*;
|
||||||
import pro.gravit.launchserver.socket.response.management.FeaturesResponse;
|
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.management.ServerStatusResponse;
|
||||||
import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername;
|
import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername;
|
||||||
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
|
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
|
||||||
|
@ -88,6 +89,8 @@ public static void registerResponses() {
|
||||||
providers.register("refreshToken", RefreshTokenResponse.class);
|
providers.register("refreshToken", RefreshTokenResponse.class);
|
||||||
providers.register("restore", RestoreResponse.class);
|
providers.register("restore", RestoreResponse.class);
|
||||||
providers.register("additionalData", AdditionalDataResponse.class);
|
providers.register("additionalData", AdditionalDataResponse.class);
|
||||||
|
providers.register("clientProfileKey", FetchClientProfileKeyResponse.class);
|
||||||
|
providers.register("getPublicKey", GetPublicKeyResponse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {
|
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()
|
return Jwts.builder()
|
||||||
.setIssuer("LaunchServer")
|
.setIssuer("LaunchServer")
|
||||||
.claim("checkSign", true)
|
.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)
|
.signWith(server.keyAgreementManager.ecdsaPrivateKey, SignatureAlgorithm.ES256)
|
||||||
.compact();
|
.compact();
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,6 @@ task javadocJar(type: Jar) {
|
||||||
shadowJar {
|
shadowJar {
|
||||||
duplicatesStrategy = 'EXCLUDE'
|
duplicatesStrategy = 'EXCLUDE'
|
||||||
archiveClassifier.set(null)
|
archiveClassifier.set(null)
|
||||||
relocate 'org.objectweb.asm', 'pro.gravit.repackage.org.objectweb.asm'
|
|
||||||
relocate 'io.netty', 'pro.gravit.repackage.io.netty'
|
relocate 'io.netty', 'pro.gravit.repackage.io.netty'
|
||||||
configurations = [project.configurations.pack]
|
configurations = [project.configurations.pack]
|
||||||
exclude 'module-info.class'
|
exclude 'module-info.class'
|
||||||
|
@ -52,7 +51,6 @@ task javadocJar(type: Jar) {
|
||||||
pack project(':LauncherAPI')
|
pack project(':LauncherAPI')
|
||||||
bundle group: 'com.github.oshi', name: 'oshi-core', version: rootProject['verOshiCore']
|
bundle group: 'com.github.oshi', name: 'oshi-core', version: rootProject['verOshiCore']
|
||||||
pack group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty']
|
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) {
|
task genRuntimeJS(type: Zip) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package pro.gravit.launcher;
|
package pro.gravit.launcher;
|
||||||
|
|
||||||
import pro.gravit.launcher.patches.FMLPatcher;
|
|
||||||
import pro.gravit.launcher.utils.NativeJVMHalt;
|
import pro.gravit.launcher.utils.NativeJVMHalt;
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
|
|
||||||
|
@ -30,7 +29,6 @@ public static void premain(String agentArgument, Instrumentation instrumentation
|
||||||
checkAgentStacktrace();
|
checkAgentStacktrace();
|
||||||
inst = instrumentation;
|
inst = instrumentation;
|
||||||
NativeJVMHalt.initFunc();
|
NativeJVMHalt.initFunc();
|
||||||
FMLPatcher.apply();
|
|
||||||
isAgentStarted = true;
|
isAgentStarted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
import pro.gravit.launcher.client.events.ClientExitPhase;
|
import pro.gravit.launcher.client.events.ClientExitPhase;
|
||||||
import pro.gravit.launcher.client.events.ClientPreGuiPhase;
|
import pro.gravit.launcher.client.events.ClientPreGuiPhase;
|
||||||
import pro.gravit.launcher.console.GetPublicKeyCommand;
|
import pro.gravit.launcher.console.GetPublicKeyCommand;
|
||||||
|
import pro.gravit.launcher.console.ModulesCommand;
|
||||||
import pro.gravit.launcher.console.SignDataCommand;
|
import pro.gravit.launcher.console.SignDataCommand;
|
||||||
import pro.gravit.launcher.events.request.*;
|
import pro.gravit.launcher.events.request.*;
|
||||||
import pro.gravit.launcher.guard.LauncherGuardInterface;
|
import pro.gravit.launcher.guard.LauncherGuard;
|
||||||
import pro.gravit.launcher.guard.LauncherGuardManager;
|
|
||||||
import pro.gravit.launcher.guard.LauncherNoGuard;
|
import pro.gravit.launcher.guard.LauncherNoGuard;
|
||||||
import pro.gravit.launcher.guard.LauncherWrapperGuard;
|
import pro.gravit.launcher.guard.LauncherWrapperGuard;
|
||||||
import pro.gravit.launcher.gui.NoRuntimeProvider;
|
import pro.gravit.launcher.gui.NoRuntimeProvider;
|
||||||
|
@ -28,7 +28,6 @@
|
||||||
import pro.gravit.launcher.request.secure.GetSecureLevelInfoRequest;
|
import pro.gravit.launcher.request.secure.GetSecureLevelInfoRequest;
|
||||||
import pro.gravit.launcher.request.secure.SecurityReportRequest;
|
import pro.gravit.launcher.request.secure.SecurityReportRequest;
|
||||||
import pro.gravit.launcher.request.update.LauncherRequest;
|
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.OfflineRequestService;
|
||||||
import pro.gravit.launcher.request.websockets.StdWebSocketService;
|
import pro.gravit.launcher.request.websockets.StdWebSocketService;
|
||||||
import pro.gravit.launcher.utils.NativeJVMHalt;
|
import pro.gravit.launcher.utils.NativeJVMHalt;
|
||||||
|
@ -50,7 +49,7 @@
|
||||||
|
|
||||||
public class LauncherEngine {
|
public class LauncherEngine {
|
||||||
public static ClientLauncherProcess.ClientParams clientParams;
|
public static ClientLauncherProcess.ClientParams clientParams;
|
||||||
public static LauncherGuardInterface guard;
|
public static LauncherGuard guard;
|
||||||
public static ClientModuleManager modulesManager;
|
public static ClientModuleManager modulesManager;
|
||||||
public final boolean clientInstance;
|
public final boolean clientInstance;
|
||||||
// Instance
|
// Instance
|
||||||
|
@ -85,8 +84,14 @@ public static void checkClass(Class<?> clazz) throws SecurityException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void exitLauncher(int code) {
|
public static void beforeExit(int code) {
|
||||||
modulesManager.invokeEvent(new ClientExitPhase(code));
|
try {
|
||||||
|
modulesManager.invokeEvent(new ClientExitPhase(code));
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void forceExit(int code) {
|
||||||
try {
|
try {
|
||||||
System.exit(code);
|
System.exit(code);
|
||||||
} catch (Throwable e) //Forge Security Manager?
|
} 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 {
|
public static void main(String... args) throws Throwable {
|
||||||
JVMHelper.checkStackTrace(LauncherEngine.class);
|
JVMHelper.checkStackTrace(LauncherEngine.class);
|
||||||
JVMHelper.verifySystemProperties(Launcher.class, true);
|
JVMHelper.verifySystemProperties(Launcher.class, true);
|
||||||
|
@ -143,7 +153,7 @@ public static void verifyNoAgent() {
|
||||||
throw new SecurityException("JavaAgent found");
|
throw new SecurityException("JavaAgent found");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LauncherGuardInterface tryGetStdGuard() {
|
public static LauncherGuard tryGetStdGuard() {
|
||||||
switch (Launcher.getConfig().guardType) {
|
switch (Launcher.getConfig().guardType) {
|
||||||
case "no":
|
case "no":
|
||||||
return new LauncherNoGuard();
|
return new LauncherNoGuard();
|
||||||
|
@ -264,7 +274,6 @@ public void start(String... args) throws Throwable {
|
||||||
registerCommands();
|
registerCommands();
|
||||||
LauncherEngine.modulesManager.invokeEvent(new ClientEngineInitPhase(this));
|
LauncherEngine.modulesManager.invokeEvent(new ClientEngineInitPhase(this));
|
||||||
runtimeProvider.preLoad();
|
runtimeProvider.preLoad();
|
||||||
LauncherGuardManager.initGuard(clientInstance);
|
|
||||||
LogHelper.debug("Dir: %s", DirBridge.dir);
|
LogHelper.debug("Dir: %s", DirBridge.dir);
|
||||||
runtimeProvider.run(args);
|
runtimeProvider.run(args);
|
||||||
}
|
}
|
||||||
|
@ -272,5 +281,6 @@ public void start(String... args) throws Throwable {
|
||||||
private void registerCommands() {
|
private void registerCommands() {
|
||||||
ConsoleManager.handler.registerCommand("getpublickey", new GetPublicKeyCommand(this));
|
ConsoleManager.handler.registerCommand("getpublickey", new GetPublicKeyCommand(this));
|
||||||
ConsoleManager.handler.registerCommand("signdata", new SignDataCommand(this));
|
ConsoleManager.handler.registerCommand("signdata", new SignDataCommand(this));
|
||||||
|
ConsoleManager.handler.registerCommand("modules", new ModulesCommand());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package pro.gravit.launcher.api;
|
package pro.gravit.launcher.api;
|
||||||
|
|
||||||
|
import pro.gravit.launcher.utils.ApiBridgeService;
|
||||||
|
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
|
@ -12,4 +14,8 @@ public class ClientService {
|
||||||
public static ClassLoader getClassLoader() {
|
public static ClassLoader getClassLoader() {
|
||||||
return classLoader;
|
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.*;
|
||||||
import pro.gravit.launcher.api.AuthService;
|
import pro.gravit.launcher.api.AuthService;
|
||||||
import pro.gravit.launcher.api.ClientService;
|
import pro.gravit.launcher.api.ClientService;
|
||||||
|
import pro.gravit.launcher.api.KeyService;
|
||||||
import pro.gravit.launcher.client.events.client.*;
|
import pro.gravit.launcher.client.events.client.*;
|
||||||
import pro.gravit.launcher.events.request.ProfileByUUIDRequestEvent;
|
import pro.gravit.launcher.events.request.ProfileByUUIDRequestEvent;
|
||||||
import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent;
|
import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent;
|
||||||
import pro.gravit.launcher.guard.LauncherGuardManager;
|
|
||||||
import pro.gravit.launcher.hasher.FileNameMatcher;
|
import pro.gravit.launcher.hasher.FileNameMatcher;
|
||||||
import pro.gravit.launcher.hasher.HashedDir;
|
import pro.gravit.launcher.hasher.HashedDir;
|
||||||
import pro.gravit.launcher.hasher.HashedEntry;
|
import pro.gravit.launcher.hasher.HashedEntry;
|
||||||
|
@ -15,7 +15,6 @@
|
||||||
import pro.gravit.launcher.modules.LauncherModulesManager;
|
import pro.gravit.launcher.modules.LauncherModulesManager;
|
||||||
import pro.gravit.launcher.modules.events.OfflineModeEvent;
|
import pro.gravit.launcher.modules.events.OfflineModeEvent;
|
||||||
import pro.gravit.launcher.modules.events.PreConfigPhase;
|
import pro.gravit.launcher.modules.events.PreConfigPhase;
|
||||||
import pro.gravit.launcher.patches.FMLPatcher;
|
|
||||||
import pro.gravit.launcher.profiles.ClientProfile;
|
import pro.gravit.launcher.profiles.ClientProfile;
|
||||||
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
|
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
|
||||||
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClassPath;
|
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClassPath;
|
||||||
|
@ -26,7 +25,6 @@
|
||||||
import pro.gravit.launcher.request.RequestService;
|
import pro.gravit.launcher.request.RequestService;
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
|
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.ProfileByUUIDRequest;
|
||||||
import pro.gravit.launcher.request.uuid.ProfileByUsernameRequest;
|
import pro.gravit.launcher.request.uuid.ProfileByUsernameRequest;
|
||||||
import pro.gravit.launcher.request.websockets.OfflineRequestService;
|
import pro.gravit.launcher.request.websockets.OfflineRequestService;
|
||||||
|
@ -87,7 +85,6 @@ public static void main(String[] args) throws Throwable {
|
||||||
ConsoleManager.initConsole();
|
ConsoleManager.initConsole();
|
||||||
LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase());
|
LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase());
|
||||||
engine.readKeys();
|
engine.readKeys();
|
||||||
LauncherGuardManager.initGuard(true);
|
|
||||||
LogHelper.debug("Reading ClientLauncher params");
|
LogHelper.debug("Reading ClientLauncher params");
|
||||||
ClientLauncherProcess.ClientParams params = readParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort));
|
ClientLauncherProcess.ClientParams params = readParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort));
|
||||||
if (params.profile.getClassLoaderConfig() != ClientProfile.ClassLoaderConfig.AGENT) {
|
if (params.profile.getClassLoaderConfig() != ClientProfile.ClassLoaderConfig.AGENT) {
|
||||||
|
@ -118,10 +115,11 @@ public static void main(String[] args) throws Throwable {
|
||||||
|
|
||||||
// Verify ClientLauncher sign and classpath
|
// Verify ClientLauncher sign and classpath
|
||||||
LogHelper.debug("Verifying 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());
|
List<URL> classpathURLs = classpath.stream().map(IOHelper::toURL).collect(Collectors.toList());
|
||||||
// Start client with WatchService monitoring
|
// Start client with WatchService monitoring
|
||||||
boolean digest = !profile.isUpdateFastCheck();
|
|
||||||
RequestService service;
|
RequestService service;
|
||||||
if(params.offlineMode) {
|
if(params.offlineMode) {
|
||||||
service = initOffline(LauncherEngine.modulesManager, params);
|
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();
|
ClientProfile.ClassLoaderConfig classLoaderConfig = profile.getClassLoaderConfig();
|
||||||
if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) {
|
if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) {
|
||||||
ClientClassLoader classLoader = new ClientClassLoader(classpathURLs.toArray(new URL[0]), ClassLoader.getSystemClassLoader());
|
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)));
|
System.setProperty("java.class.path", classpath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator)));
|
||||||
ClientLauncherEntryPoint.classLoader = classLoader;
|
ClientLauncherEntryPoint.classLoader = classLoader;
|
||||||
Thread.currentThread().setContextClassLoader(classLoader);
|
Thread.currentThread().setContextClassLoader(classLoader);
|
||||||
classLoader.nativePath = clientDir.resolve("natives").toString();
|
classLoader.nativePath = params.nativesDir;
|
||||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(engine, classLoader, profile));
|
LauncherEngine.modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(engine, classLoader, profile));
|
||||||
ClientService.classLoader = classLoader;
|
ClientService.classLoader = classLoader;
|
||||||
ClientService.nativePath = classLoader.nativePath;
|
ClientService.nativePath = classLoader.nativePath;
|
||||||
|
@ -162,7 +161,7 @@ public static void main(String[] args) throws Throwable {
|
||||||
LauncherAgent.addJVMClassPath(Paths.get(url.toURI()));
|
LauncherAgent.addJVMClassPath(Paths.get(url.toURI()));
|
||||||
}
|
}
|
||||||
ClientService.instrumentation = LauncherAgent.inst;
|
ClientService.instrumentation = LauncherAgent.inst;
|
||||||
ClientService.nativePath = clientDir.resolve("natives").toString();
|
ClientService.nativePath = params.nativesDir;
|
||||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(engine, classLoader, profile));
|
LauncherEngine.modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(engine, classLoader, profile));
|
||||||
ClientService.classLoader = classLoader;
|
ClientService.classLoader = classLoader;
|
||||||
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
|
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
|
||||||
|
@ -170,9 +169,11 @@ public static void main(String[] args) throws Throwable {
|
||||||
ClientLauncherEntryPoint.classLoader = ClassLoader.getSystemClassLoader();
|
ClientLauncherEntryPoint.classLoader = ClassLoader.getSystemClassLoader();
|
||||||
ClientService.classLoader = ClassLoader.getSystemClassLoader();
|
ClientService.classLoader = ClassLoader.getSystemClassLoader();
|
||||||
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
|
ClientService.baseURLs = classpathURLs.toArray(new URL[0]);
|
||||||
|
ClientService.nativePath = params.nativesDir;
|
||||||
}
|
}
|
||||||
AuthService.username = params.playerProfile.username;
|
AuthService.username = params.playerProfile.username;
|
||||||
AuthService.uuid = params.playerProfile.uuid;
|
AuthService.uuid = params.playerProfile.uuid;
|
||||||
|
KeyService.serverRsaPublicKey = Launcher.getConfig().rsaPublicKey;
|
||||||
if (params.profile.getRuntimeInClientConfig() != ClientProfile.RuntimeInClientConfig.NONE) {
|
if (params.profile.getRuntimeInClientConfig() != ClientProfile.RuntimeInClientConfig.NONE) {
|
||||||
CommonHelper.newThread("Client Launcher Thread", true, () -> {
|
CommonHelper.newThread("Client Launcher Thread", true, () -> {
|
||||||
try {
|
try {
|
||||||
|
@ -187,9 +188,9 @@ public static void main(String[] args) throws Throwable {
|
||||||
FileNameMatcher assetMatcher = profile.getAssetUpdateMatcher();
|
FileNameMatcher assetMatcher = profile.getAssetUpdateMatcher();
|
||||||
FileNameMatcher clientMatcher = profile.getClientUpdateMatcher();
|
FileNameMatcher clientMatcher = profile.getClientUpdateMatcher();
|
||||||
Path javaDir = Paths.get(System.getProperty("java.home"));
|
Path javaDir = Paths.get(System.getProperty("java.home"));
|
||||||
try (DirWatcher assetWatcher = new DirWatcher(assetDir, params.assetHDir, assetMatcher, digest);
|
try (DirWatcher assetWatcher = new DirWatcher(assetDir, params.assetHDir, assetMatcher, true);
|
||||||
DirWatcher clientWatcher = new DirWatcher(clientDir, params.clientHDir, clientMatcher, digest);
|
DirWatcher clientWatcher = new DirWatcher(clientDir, params.clientHDir, clientMatcher, true);
|
||||||
DirWatcher javaWatcher = params.javaHDir == null ? null : new DirWatcher(javaDir, params.javaHDir, null, digest)) {
|
DirWatcher javaWatcher = params.javaHDir == null ? null : new DirWatcher(javaDir, params.javaHDir, null, true)) {
|
||||||
// Verify current state of all dirs
|
// Verify current state of all dirs
|
||||||
//verifyHDir(IOHelper.JVM_DIR, jvmHDir.object, null, digest);
|
//verifyHDir(IOHelper.JVM_DIR, jvmHDir.object, null, digest);
|
||||||
//for (OptionalFile s : Launcher.profile.getOptional()) {
|
//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();
|
CommonHelper.newThread("Client Directory Watcher", true, clientWatcher).start();
|
||||||
if (javaWatcher != null)
|
if (javaWatcher != null)
|
||||||
CommonHelper.newThread("Java Directory Watcher", true, javaWatcher).start();
|
CommonHelper.newThread("Java Directory Watcher", true, javaWatcher).start();
|
||||||
verifyHDir(assetDir, params.assetHDir, assetMatcher, digest);
|
verifyHDir(assetDir, params.assetHDir, assetMatcher, false, false);
|
||||||
verifyHDir(clientDir, params.clientHDir, clientMatcher, digest);
|
verifyHDir(clientDir, params.clientHDir, clientMatcher, false, true);
|
||||||
if (javaWatcher != null)
|
if (javaWatcher != null)
|
||||||
verifyHDir(javaDir, params.javaHDir, null, digest);
|
verifyHDir(javaDir, params.javaHDir, null, false, true);
|
||||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params));
|
LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params));
|
||||||
launch(profile, 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)
|
//if (matcher != null)
|
||||||
// matcher = matcher.verifyOnly();
|
// matcher = matcher.verifyOnly();
|
||||||
|
|
||||||
// Hash directory and compare (ignore update-only matcher entries, it will break offline-mode)
|
// Hash directory and compare (ignore update-only matcher entries, it will break offline-mode)
|
||||||
HashedDir currentHDir = new HashedDir(dir, matcher, true, digest);
|
HashedDir currentHDir = new HashedDir(dir, matcher, true, digest);
|
||||||
HashedDir.Diff diff = hdir.diff(currentHDir, matcher);
|
HashedDir.Diff diff = hdir.diff(currentHDir, matcher);
|
||||||
if (!diff.isSame()) {
|
if (!diff.mismatch.isEmpty() || (checkExtra && !diff.extra.isEmpty())) {
|
||||||
if (LogHelper.isDebugEnabled()) {
|
diff.extra.walk(File.separator, (e, k, v) -> {
|
||||||
diff.extra.walk(File.separator, (e, k, v) -> {
|
if (v.getType().equals(HashedEntry.Type.FILE)) {
|
||||||
if (v.getType().equals(HashedEntry.Type.FILE)) {
|
LogHelper.error("Extra file %s", e);
|
||||||
LogHelper.error("Extra file %s", e);
|
} else LogHelper.error("Extra %s", e);
|
||||||
} else LogHelper.error("Extra %s", e);
|
return HashedDir.WalkAction.CONTINUE;
|
||||||
return HashedDir.WalkAction.CONTINUE;
|
});
|
||||||
});
|
diff.mismatch.walk(File.separator, (e, k, v) -> {
|
||||||
diff.mismatch.walk(File.separator, (e, k, v) -> {
|
if (v.getType().equals(HashedEntry.Type.FILE)) {
|
||||||
if (v.getType().equals(HashedEntry.Type.FILE)) {
|
LogHelper.error("Mismatch file %s", e);
|
||||||
LogHelper.error("Mismatch file %s", e);
|
} else LogHelper.error("Mismatch %s", e);
|
||||||
} else LogHelper.error("Mismatch %s", e);
|
return HashedDir.WalkAction.CONTINUE;
|
||||||
return HashedDir.WalkAction.CONTINUE;
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir)));
|
throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean checkJVMBitsAndVersion(int minVersion, int recommendVersion, int maxVersion, boolean showMessage) {
|
public static boolean checkJVMBitsAndVersion(int minVersion, int recommendVersion, int maxVersion, boolean showMessage) {
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) {
|
if (JVMHelper.JVM_BITS == 64 && JVMHelper.ARCH_TYPE == JVMHelper.ARCH.X86) {
|
||||||
String error = String.format("У Вас установлена Java %d, но Ваша система определена как %d. Установите Java правильной разрядности", JVMHelper.JVM_BITS, JVMHelper.OS_BITS);
|
String error = "У Вас установлена Java x64, но Ваша система определена как x32. Установите Java правильной разрядности";
|
||||||
LogHelper.error(error);
|
LogHelper.error(error);
|
||||||
if (showMessage)
|
if (showMessage)
|
||||||
JOptionPane.showMessageDialog(null, error);
|
JOptionPane.showMessageDialog(null, error);
|
||||||
|
@ -282,7 +281,7 @@ public static boolean checkJVMBitsAndVersion(int minVersion, int recommendVersio
|
||||||
LogHelper.info(jvmVersion);
|
LogHelper.info(jvmVersion);
|
||||||
int version = JVMHelper.getVersion();
|
int version = JVMHelper.getVersion();
|
||||||
if (version < minVersion || version > maxVersion) {
|
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);
|
LogHelper.error(error);
|
||||||
if (showMessage)
|
if (showMessage)
|
||||||
JOptionPane.showMessageDialog(null, error);
|
JOptionPane.showMessageDialog(null, error);
|
||||||
|
@ -347,7 +346,6 @@ private static void launch(ClientProfile profile, ClientLauncherProcess.ClientPa
|
||||||
LogHelper.dev("ClassLoader URL: %s", u.toString());
|
LogHelper.dev("ClassLoader URL: %s", u.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FMLPatcher.apply();
|
|
||||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessPreInvokeMainClassEvent(params, profile, args));
|
LauncherEngine.modulesManager.invokeEvent(new ClientProcessPreInvokeMainClassEvent(params, profile, args));
|
||||||
// Invoke main method
|
// Invoke main method
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package pro.gravit.launcher.client;
|
package pro.gravit.launcher.client;
|
||||||
|
|
||||||
import pro.gravit.launcher.Launcher;
|
import pro.gravit.launcher.Launcher;
|
||||||
|
import pro.gravit.launcher.LauncherConfig;
|
||||||
import pro.gravit.launcher.LauncherEngine;
|
import pro.gravit.launcher.LauncherEngine;
|
||||||
import pro.gravit.launcher.LauncherNetworkAPI;
|
import pro.gravit.launcher.LauncherNetworkAPI;
|
||||||
import pro.gravit.launcher.client.events.client.ClientProcessBuilderCreateEvent;
|
import pro.gravit.launcher.client.events.client.ClientProcessBuilderCreateEvent;
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -42,42 +44,45 @@ public class ClientLauncherProcess {
|
||||||
private final transient Boolean[] waitWriteParams = new Boolean[]{false};
|
private final transient Boolean[] waitWriteParams = new Boolean[]{false};
|
||||||
public Path executeFile;
|
public Path executeFile;
|
||||||
public Path workDir;
|
public Path workDir;
|
||||||
public Path javaDir;
|
public JavaHelper.JavaVersion javaVersion;
|
||||||
public int bits;
|
|
||||||
public boolean useLegacyJavaClassPathProperty;
|
public boolean useLegacyJavaClassPathProperty;
|
||||||
public boolean isStarted;
|
public boolean isStarted;
|
||||||
public JavaHelper.JavaVersion javaVersion;
|
|
||||||
private transient Process process;
|
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.executeFile = executeFile;
|
||||||
this.workDir = workDir;
|
this.workDir = workDir;
|
||||||
this.javaDir = javaDir;
|
this.javaVersion = javaVersion;
|
||||||
this.mainClass = mainClass;
|
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,
|
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
|
||||||
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
|
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,
|
public ClientLauncherProcess(Path clientDir, Path assetDir,
|
||||||
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
|
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
|
||||||
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
|
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,
|
ClientProfile profile, PlayerProfile playerProfile, OptionalView view, String accessToken,
|
||||||
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
|
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
|
||||||
|
this.javaVersion = javaVersion;
|
||||||
this.workDir = clientDir.toAbsolutePath();
|
this.workDir = clientDir.toAbsolutePath();
|
||||||
this.javaDir = javaDir;
|
this.executeFile = IOHelper.resolveJavaBin(this.javaVersion.jvmDir);
|
||||||
this.executeFile = IOHelper.resolveJavaBin(this.javaDir);
|
|
||||||
this.mainClass = ClientLauncherEntryPoint.class.getName();
|
this.mainClass = ClientLauncherEntryPoint.class.getName();
|
||||||
this.params.clientDir = this.workDir.toString();
|
this.params.clientDir = this.workDir.toString();
|
||||||
this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString();
|
this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString();
|
||||||
this.params.assetDir = assetDir.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.profile = profile;
|
||||||
this.params.playerProfile = playerProfile;
|
this.params.playerProfile = playerProfile;
|
||||||
this.params.accessToken = accessToken;
|
this.params.accessToken = accessToken;
|
||||||
|
@ -87,16 +92,6 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path r
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
this.params.actions = view.getEnabledActions();
|
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();
|
applyClientProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +110,7 @@ private void applyClientProfile() {
|
||||||
this.jvmArgs.addAll(((OptionalActionJvmArgs) a).args);
|
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());
|
Collections.addAll(this.systemClassPath, this.params.profile.getAlternativeClassPath());
|
||||||
if (params.ram > 0) {
|
if (params.ram > 0) {
|
||||||
this.jvmArgs.add("-Xmx" + params.ram + 'M');
|
this.jvmArgs.add("-Xmx" + params.ram + 'M');
|
||||||
|
@ -154,10 +149,19 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
|
||||||
applyJava9Params(processArgs);
|
applyJava9Params(processArgs);
|
||||||
}
|
}
|
||||||
//ADD CLASSPATH
|
//ADD CLASSPATH
|
||||||
|
processArgs.add(JVMHelper.jvmProperty("java.library.path", this.params.nativesDir));
|
||||||
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.AGENT) {
|
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.AGENT) {
|
||||||
processArgs.add("-javaagent:".concat(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString()));
|
processArgs.add("-javaagent:".concat(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString()));
|
||||||
} else if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
|
} 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) {
|
if (useLegacyJavaClassPathProperty) {
|
||||||
processArgs.add("-Djava.class.path=".concat(String.join(getPathSeparator(), systemClassPath)));
|
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()));
|
LogHelper.debug("Commandline: %s", Arrays.toString(processArgs.toArray()));
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
|
ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
|
||||||
EnvHelper.addEnv(processBuilder);
|
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.environment().putAll(systemEnv);
|
||||||
processBuilder.directory(workDir.toFile());
|
processBuilder.directory(workDir.toFile());
|
||||||
processBuilder.inheritIO();
|
processBuilder.inheritIO();
|
||||||
|
@ -256,6 +260,8 @@ public static class ClientParams {
|
||||||
|
|
||||||
public String resourcePackDir;
|
public String resourcePackDir;
|
||||||
|
|
||||||
|
public String nativesDir;
|
||||||
|
|
||||||
// Client params
|
// Client params
|
||||||
|
|
||||||
public PlayerProfile playerProfile;
|
public PlayerProfile playerProfile;
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
import pro.gravit.launcher.modules.impl.SimpleModuleManager;
|
import pro.gravit.launcher.modules.impl.SimpleModuleManager;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public final class ClientModuleManager extends SimpleModuleManager {
|
public final class ClientModuleManager extends SimpleModuleManager {
|
||||||
public ClientModuleManager() {
|
public ClientModuleManager() {
|
||||||
|
@ -28,6 +30,10 @@ public LauncherModule loadModule(LauncherModule module) {
|
||||||
return super.loadModule(module);
|
return super.loadModule(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<LauncherModule> getModules() {
|
||||||
|
return Collections.unmodifiableList(modules);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean verifyClassCheckResult(LauncherTrustManager.CheckClassResult result) {
|
public final boolean verifyClassCheckResult(LauncherTrustManager.CheckClassResult result) {
|
||||||
return result.type == LauncherTrustManager.CheckClassResultType.SUCCESS;
|
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 final AtomicBoolean IS_DEBUG = new AtomicBoolean(false);
|
||||||
public static String webSocketURL = System.getProperty("launcherdebug.websocket", "ws://localhost:9274/api");
|
public static String webSocketURL = System.getProperty("launcherdebug.websocket", "ws://localhost:9274/api");
|
||||||
public static String projectName = System.getProperty("launcherdebug.projectname", "Minecraft");
|
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 boolean offlineMode = Boolean.getBoolean("launcherdebug.offlinemode");
|
||||||
public static String[] moduleClasses = System.getProperty("launcherdebug.modules", "").split(",");
|
public static String[] moduleClasses = System.getProperty("launcherdebug.modules", "").split(",");
|
||||||
public static String[] moduleFiles = System.getProperty("launcherdebug.modulefiles", "").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.info("Launcher start in DEBUG mode (Only for developers)");
|
||||||
LogHelper.debug("Initialization LauncherConfig");
|
LogHelper.debug("Initialization LauncherConfig");
|
||||||
LauncherConfig config = new LauncherConfig(webSocketURL, new HashMap<>(), projectName, environment, new DebugLauncherTrustManager(DebugLauncherTrustManager.TrustDebugMode.TRUST_ALL));
|
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.setConfig(config);
|
||||||
Launcher.applyLauncherEnv(environment);
|
Launcher.applyLauncherEnv(environment);
|
||||||
LauncherEngine.modulesManager = new ClientModuleManager();
|
LauncherEngine.modulesManager = new ClientModuleManager();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import pro.gravit.launcher.client.ClientLauncherProcess;
|
import pro.gravit.launcher.client.ClientLauncherProcess;
|
||||||
|
|
||||||
public interface LauncherGuardInterface {
|
public interface LauncherGuard {
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
void applyGuardParams(ClientLauncherProcess process);
|
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;
|
import pro.gravit.launcher.client.ClientLauncherProcess;
|
||||||
|
|
||||||
public class LauncherNoGuard implements LauncherGuardInterface {
|
public class LauncherNoGuard implements LauncherGuard {
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "noGuard";
|
return "noGuard";
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class LauncherWrapperGuard implements LauncherGuardInterface {
|
public class LauncherWrapperGuard implements LauncherGuard {
|
||||||
|
|
||||||
public LauncherWrapperGuard() {
|
public LauncherWrapperGuard() {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -49,7 +49,7 @@ public static void registerCommands() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean checkUnlockKey(String key) {
|
public static boolean checkUnlockKey(String key) {
|
||||||
return key.equals(Launcher.getConfig().oemUnlockKey);
|
return key.equals(Launcher.getConfig().unlockSecret);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean unlock() {
|
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.Launcher;
|
||||||
import pro.gravit.launcher.LauncherTrustManager;
|
import pro.gravit.launcher.LauncherTrustManager;
|
||||||
|
import pro.gravit.launcher.client.ClientClassLoader;
|
||||||
|
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
@ -15,4 +16,12 @@ public static void checkCertificatesSuccess(X509Certificate[] certs) throws Exce
|
||||||
LauncherTrustManager trustManager = Launcher.getConfig().trustManager;
|
LauncherTrustManager trustManager = Launcher.getConfig().trustManager;
|
||||||
trustManager.checkCertificatesSuccess(certs, trustManager::stdCertificateChecker);
|
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;
|
package pro.gravit.launcher.utils;
|
||||||
|
|
||||||
|
import pro.gravit.launcher.LauncherEngine;
|
||||||
import pro.gravit.launcher.hasher.FileNameMatcher;
|
import pro.gravit.launcher.hasher.FileNameMatcher;
|
||||||
import pro.gravit.launcher.hasher.HashedDir;
|
import pro.gravit.launcher.hasher.HashedDir;
|
||||||
import pro.gravit.launcher.hasher.HashedEntry;
|
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) {
|
private static void handleError(Throwable e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
NativeJVMHalt.haltA(-123);
|
LauncherEngine.exitLauncher(-123);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Deque<String> toPath(Iterable<Path> path) {
|
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;
|
package pro.gravit.launcher.utils;
|
||||||
|
|
||||||
import pro.gravit.launcher.patches.FMLPatcher;
|
|
||||||
import pro.gravit.utils.helper.JVMHelper;
|
import pro.gravit.utils.helper.JVMHelper;
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
|
|
||||||
import javax.swing.*;
|
import java.lang.reflect.Method;
|
||||||
import java.awt.event.WindowEvent;
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
|
||||||
public final class NativeJVMHalt {
|
public final class NativeJVMHalt {
|
||||||
public final int haltCode;
|
public final int haltCode;
|
||||||
|
@ -15,51 +15,24 @@ public NativeJVMHalt(int haltCode) {
|
||||||
System.out.println("JVM exit code " + haltCode);
|
System.out.println("JVM exit code " + haltCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void initFunc() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static void haltA(int code) {
|
public static void haltA(int code) {
|
||||||
Throwable[] th = new Throwable[3];
|
Throwable[] th = new Throwable[3];
|
||||||
NativeJVMHalt halt = new NativeJVMHalt(code);
|
NativeJVMHalt halt = new NativeJVMHalt(code);
|
||||||
try {
|
try {
|
||||||
JVMHelper.RUNTIME.exit(code);
|
LogHelper.dev("Try invoke Shutdown.exit");
|
||||||
} catch (Throwable exitExc) {
|
Class<?> clazz = Class.forName("java.lang.Shutdown", true, ClassLoader.getSystemClassLoader());
|
||||||
th[0] = exitExc;
|
Method exitMethod = clazz.getDeclaredMethod("exit", int.class);
|
||||||
try {
|
exitMethod.setAccessible(true);
|
||||||
new WindowShutdown();
|
exitMethod.invoke(null, code);
|
||||||
} catch (Throwable windowExc) {
|
} catch (Throwable e) {
|
||||||
th[1] = windowExc;
|
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;
|
public final String address;
|
||||||
@LauncherInject("runtimeconfig.secretKeyClient")
|
@LauncherInject("runtimeconfig.secretKeyClient")
|
||||||
public String secretKeyClient;
|
public String secretKeyClient;
|
||||||
@LauncherInject("runtimeconfig.oemUnlockKey")
|
@LauncherInject("runtimeconfig.unlockSecret")
|
||||||
public String oemUnlockKey;
|
public String unlockSecret;
|
||||||
@LauncherInject("launchercore.env")
|
@LauncherInject("launchercore.env")
|
||||||
public LauncherEnvironment environment;
|
public LauncherEnvironment environment;
|
||||||
|
|
||||||
|
@ -63,7 +63,6 @@ public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException
|
||||||
projectName = null;
|
projectName = null;
|
||||||
clientPort = -1;
|
clientPort = -1;
|
||||||
secretKeyClient = null;
|
secretKeyClient = null;
|
||||||
oemUnlockKey = null;
|
|
||||||
try {
|
try {
|
||||||
trustManager = new LauncherTrustManager(secureConfigCertificates);
|
trustManager = new LauncherTrustManager(secureConfigCertificates);
|
||||||
} catch (CertificateException e) {
|
} 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
|
@LauncherNetworkAPI
|
||||||
private ProfileDefaultSettings settings = new ProfileDefaultSettings();
|
private ProfileDefaultSettings settings = new ProfileDefaultSettings();
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
private boolean updateFastCheck;
|
|
||||||
@LauncherNetworkAPI
|
|
||||||
private boolean limited;
|
private boolean limited;
|
||||||
// Client launcher
|
// Client launcher
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
|
@ -107,7 +105,7 @@ public ClientProfile() {
|
||||||
runtimeInClientConfig = RuntimeInClientConfig.NONE;
|
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.update = update;
|
||||||
this.updateExclusions = updateExclusions;
|
this.updateExclusions = updateExclusions;
|
||||||
this.updateShared = updateShared;
|
this.updateShared = updateShared;
|
||||||
|
@ -139,7 +137,6 @@ public ClientProfile(List<String> update, List<String> updateExclusions, List<St
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.info = info;
|
this.info = info;
|
||||||
this.updateFastCheck = updateFastCheck;
|
|
||||||
this.mainClass = mainClass;
|
this.mainClass = mainClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,8 +313,9 @@ public void setVersion(Version version) {
|
||||||
this.version = version.name;
|
this.version = version.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public boolean isUpdateFastCheck() {
|
public boolean isUpdateFastCheck() {
|
||||||
return updateFastCheck;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -484,7 +482,9 @@ public enum Version {
|
||||||
MC118("1.18", 757),
|
MC118("1.18", 757),
|
||||||
MC1181("1.18.1", 757),
|
MC1181("1.18.1", 757),
|
||||||
MC1182("1.18.2", 758),
|
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;
|
private static final Map<String, Version> VERSIONS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
|
@ -36,7 +36,6 @@ public class ClientProfileBuilder {
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
private String title;
|
private String title;
|
||||||
private String info;
|
private String info;
|
||||||
private boolean updateFastCheck = true;
|
|
||||||
private String mainClass;
|
private String mainClass;
|
||||||
|
|
||||||
public ClientProfileBuilder setUpdate(List<String> update) {
|
public ClientProfileBuilder setUpdate(List<String> update) {
|
||||||
|
@ -194,17 +193,12 @@ public ClientProfileBuilder setInfo(String info) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientProfileBuilder setUpdateFastCheck(boolean updateFastCheck) {
|
|
||||||
this.updateFastCheck = updateFastCheck;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientProfileBuilder setMainClass(String mainClass) {
|
public ClientProfileBuilder setMainClass(String mainClass) {
|
||||||
this.mainClass = mainClass;
|
this.mainClass = mainClass;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientProfile createClientProfile() {
|
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) {
|
if (!isRegisteredProviders) {
|
||||||
providers.register("java", JavaTrigger.class);
|
providers.register("java", JavaTrigger.class);
|
||||||
providers.register("os", OSTrigger.class);
|
providers.register("os", OSTrigger.class);
|
||||||
|
providers.register("arch", ArchTrigger.class);
|
||||||
isRegisteredProviders = true;
|
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 final class LauncherRequest extends Request<LauncherRequestEvent> implements WebSocketRequest {
|
||||||
public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class);
|
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");
|
public static final boolean EXE_BINARY = IOHelper.hasExtension(BINARY_PATH, "exe");
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
public final String secureHash;
|
public final String secureHash;
|
||||||
|
@ -46,53 +45,9 @@ public LauncherRequest() {
|
||||||
secureSalt = Launcher.getConfig().secureCheckSalt;
|
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
|
@Override
|
||||||
public LauncherRequestEvent requestDo(RequestService service) throws Exception {
|
public LauncherRequestEvent requestDo(RequestService service) throws Exception {
|
||||||
LauncherRequestEvent result = super.request(service);
|
return super.request(service);
|
||||||
if (result.needUpdate) update(result);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -108,6 +108,8 @@ public void registerResults() {
|
||||||
results.register("refreshToken", RefreshTokenRequestEvent.class);
|
results.register("refreshToken", RefreshTokenRequestEvent.class);
|
||||||
results.register("restore", RestoreRequestEvent.class);
|
results.register("restore", RestoreRequestEvent.class);
|
||||||
results.register("additionalData", AdditionalDataRequestEvent.class);
|
results.register("additionalData", AdditionalDataRequestEvent.class);
|
||||||
|
results.register("clientProfileKey", FetchClientProfileKeyRequestEvent.class);
|
||||||
|
results.register("getPublicKey", GetPublicKeyRequestEvent.class);
|
||||||
resultsRegistered = true;
|
resultsRegistered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,10 +60,10 @@ protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Except
|
||||||
final WebSocketFrame frame = (WebSocketFrame) msg;
|
final WebSocketFrame frame = (WebSocketFrame) msg;
|
||||||
if (frame instanceof TextWebSocketFrame) {
|
if (frame instanceof TextWebSocketFrame) {
|
||||||
final TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
|
final TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
|
||||||
clientJSONPoint.onMessage(textFrame.text());
|
|
||||||
if (LogHelper.isDevEnabled()) {
|
if (LogHelper.isDevEnabled()) {
|
||||||
LogHelper.dev("Message: %s", textFrame.text());
|
LogHelper.dev("Message: %s", textFrame.text());
|
||||||
}
|
}
|
||||||
|
clientJSONPoint.onMessage(textFrame.text());
|
||||||
// uncomment to print request
|
// uncomment to print request
|
||||||
// logger.info(textFrame.text());
|
// logger.info(textFrame.text());
|
||||||
} else if ((frame instanceof PingWebSocketFrame)) {
|
} else if ((frame instanceof PingWebSocketFrame)) {
|
||||||
|
|
|
@ -2,21 +2,34 @@
|
||||||
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
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.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
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.security.cert.*;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class LauncherTrustManager {
|
public class LauncherTrustManager {
|
||||||
private final X509Certificate[] trustSigners;
|
private final X509Certificate[] trustSigners;
|
||||||
private final List<X509Certificate> trustCache = new ArrayList<>();
|
private final List<X509Certificate> trustCache = new ArrayList<>();
|
||||||
|
|
||||||
|
@LauncherInject("launcher.certificatePinning")
|
||||||
|
private static boolean isCertificatePinning;
|
||||||
|
|
||||||
public LauncherTrustManager(X509Certificate[] trustSigners) {
|
public LauncherTrustManager(X509Certificate[] trustSigners) {
|
||||||
this.trustSigners = trustSigners;
|
this.trustSigners = trustSigners;
|
||||||
|
if (requireCustomTrustStore()) {
|
||||||
|
injectCertificates();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LauncherTrustManager(List<byte[]> encodedCertificate) throws CertificateException {
|
public LauncherTrustManager(List<byte[]> encodedCertificate) throws CertificateException {
|
||||||
|
@ -29,6 +42,95 @@ public LauncherTrustManager(List<byte[]> encodedCertificate) throws CertificateE
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}).toArray(X509Certificate[]::new);
|
}).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) {
|
public CheckClassResult checkCertificates(X509Certificate[] certs, CertificateChecker checker) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class GsonManager {
|
||||||
public void initGson() {
|
public void initGson() {
|
||||||
gsonBuilder = CommonHelper.newBuilder();
|
gsonBuilder = CommonHelper.newBuilder();
|
||||||
configGsonBuilder = CommonHelper.newBuilder();
|
configGsonBuilder = CommonHelper.newBuilder();
|
||||||
configGsonBuilder.setPrettyPrinting();
|
configGsonBuilder.setPrettyPrinting().disableHtmlEscaping();
|
||||||
registerAdapters(gsonBuilder);
|
registerAdapters(gsonBuilder);
|
||||||
registerAdapters(configGsonBuilder);
|
registerAdapters(configGsonBuilder);
|
||||||
preConfigGson(configGsonBuilder);
|
preConfigGson(configGsonBuilder);
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
public final class Version implements Comparable<Version> {
|
public final class Version implements Comparable<Version> {
|
||||||
|
|
||||||
public static final int MAJOR = 5;
|
public static final int MAJOR = 5;
|
||||||
public static final int MINOR = 2;
|
public static final int MINOR = 3;
|
||||||
public static final int PATCH = 13;
|
public static final int PATCH = 0;
|
||||||
public static final int BUILD = 1;
|
public static final int BUILD = 1;
|
||||||
public static final Version.Type RELEASE = Type.STABLE;
|
public static final Version.Type RELEASE = Type.STABLE;
|
||||||
public final int major;
|
public final int major;
|
||||||
|
|
|
@ -5,7 +5,7 @@ public final class CommandException extends Exception {
|
||||||
|
|
||||||
|
|
||||||
public CommandException(String message) {
|
public CommandException(String message) {
|
||||||
super(message);
|
super(message, null, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,27 +16,9 @@
|
||||||
|
|
||||||
public final class CommonHelper {
|
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 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) {
|
public static String low(String s) {
|
||||||
return s.toLowerCase(Locale.US);
|
return s.toLowerCase(Locale.US);
|
||||||
}
|
}
|
||||||
|
@ -57,11 +39,9 @@ public static String multiReplace(Pattern[] pattern, String from, String replace
|
||||||
return tmp != null ? tmp : from;
|
return tmp != null ? tmp : from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static ScriptEngine newScriptEngine() {
|
public static ScriptEngine newScriptEngine() {
|
||||||
if (nashornFactory == null) {
|
throw new UnsupportedOperationException("ScriptEngine not supported");
|
||||||
throw new UnsupportedOperationException("ScriptEngine not supported");
|
|
||||||
}
|
|
||||||
return nashornFactory.getScriptEngine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Thread newThread(String name, boolean daemon, Runnable runnable) {
|
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());
|
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
||||||
// System properties
|
// System properties
|
||||||
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static final int OS_BITS = getCorrectOSArch();
|
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 int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
|
||||||
public static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
|
public static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
|
||||||
// Public static fields
|
// Public static fields
|
||||||
|
@ -42,6 +46,24 @@ public final class JVMHelper {
|
||||||
private 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() {
|
public static int getVersion() {
|
||||||
String version = System.getProperty("java.version");
|
String version = System.getProperty("java.version");
|
||||||
if (version.startsWith("1.")) {
|
if (version.startsWith("1.")) {
|
||||||
|
@ -132,6 +154,7 @@ public static void checkStackTrace(Class<?> mainClass) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
private static int getCorrectOSArch() {
|
private static int getCorrectOSArch() {
|
||||||
// As always, mustdie must die
|
// As always, mustdie must die
|
||||||
if (OS_TYPE == OS.MUSTDIE)
|
if (OS_TYPE == OS.MUSTDIE)
|
||||||
|
@ -147,6 +170,7 @@ public static String getEnvPropertyCaseSensitive(String name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static boolean isJVMMatchesSystemArch() {
|
public static boolean isJVMMatchesSystemArch() {
|
||||||
return JVM_BITS == OS_BITS;
|
return JVM_BITS == OS_BITS;
|
||||||
}
|
}
|
||||||
|
@ -178,10 +202,6 @@ public static void verifySystemProperties(Class<?> mainClass, boolean requireSys
|
||||||
|
|
||||||
// Verify system and java architecture
|
// Verify system and java architecture
|
||||||
LogHelper.debug("Verifying JVM 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 {
|
public enum OS {
|
||||||
|
|
|
@ -185,14 +185,14 @@ public static class JavaVersion {
|
||||||
public final Path jvmDir;
|
public final Path jvmDir;
|
||||||
public final int version;
|
public final int version;
|
||||||
public final int build;
|
public final int build;
|
||||||
public final int bitness;
|
public final JVMHelper.ARCH arch;
|
||||||
public boolean enabledJavaFX;
|
public boolean enabledJavaFX;
|
||||||
|
|
||||||
public JavaVersion(Path jvmDir, int version) {
|
public JavaVersion(Path jvmDir, int version) {
|
||||||
this.jvmDir = jvmDir;
|
this.jvmDir = jvmDir;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.build = 0;
|
this.build = 0;
|
||||||
this.bitness = JVMHelper.OS_BITS;
|
this.arch = JVMHelper.ARCH_TYPE;
|
||||||
this.enabledJavaFX = true;
|
this.enabledJavaFX = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,20 +200,20 @@ public JavaVersion(Path jvmDir, int version, int build, boolean enabledJavaFX) {
|
||||||
this.jvmDir = jvmDir;
|
this.jvmDir = jvmDir;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.build = build;
|
this.build = build;
|
||||||
this.bitness = JVMHelper.OS_BITS;
|
this.arch = JVMHelper.ARCH_TYPE;
|
||||||
this.enabledJavaFX = enabledJavaFX;
|
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.jvmDir = jvmDir;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.build = build;
|
this.build = build;
|
||||||
this.bitness = bitness;
|
this.arch = arch;
|
||||||
this.enabledJavaFX = enabledJavaFX;
|
this.enabledJavaFX = enabledJavaFX;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JavaVersion getCurrentJavaVersion() {
|
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() {
|
private static boolean isCurrentJavaSupportJavaFX() {
|
||||||
|
@ -238,21 +238,20 @@ public static JavaVersion getByPath(Path jvmDir) throws IOException {
|
||||||
}
|
}
|
||||||
Path releaseFile = jvmDir.resolve("release");
|
Path releaseFile = jvmDir.resolve("release");
|
||||||
JavaVersionAndBuild versionAndBuild;
|
JavaVersionAndBuild versionAndBuild;
|
||||||
int bitness = JVMHelper.OS_BITS;
|
JVMHelper.ARCH arch = JVMHelper.ARCH_TYPE;
|
||||||
if (IOHelper.isFile(releaseFile)) {
|
if (IOHelper.isFile(releaseFile)) {
|
||||||
Properties properties = new Properties();
|
Properties properties = new Properties();
|
||||||
properties.load(IOHelper.newReader(releaseFile));
|
properties.load(IOHelper.newReader(releaseFile));
|
||||||
versionAndBuild = getJavaVersion(properties.getProperty("JAVA_VERSION").replaceAll("\"", ""));
|
versionAndBuild = getJavaVersion(properties.getProperty("JAVA_VERSION").replaceAll("\"", ""));
|
||||||
String arch = properties.getProperty("JAVA_VERSION").replaceAll("\"", "");
|
try {
|
||||||
if (arch.contains("x86_64")) {
|
arch = JVMHelper.getArch(properties.getProperty("OS_ARCH").replaceAll("\"", ""));
|
||||||
bitness = 64;
|
} catch (Throwable ignored) {
|
||||||
} else if (arch.contains("x86") || arch.contains("x32")) {
|
arch = null;
|
||||||
bitness = 32;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
versionAndBuild = new JavaVersionAndBuild(isExistExtJavaLibrary(jvmDir, "jfxrt") ? 8 : 9, 0);
|
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) {
|
if (versionAndBuild.version <= 8) {
|
||||||
resultJavaVersion.enabledJavaFX = isExistExtJavaLibrary(jvmDir, "jfxrt");
|
resultJavaVersion.enabledJavaFX = isExistExtJavaLibrary(jvmDir, "jfxrt");
|
||||||
} else {
|
} 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) {
|
public static String toHex(byte[] bytes) {
|
||||||
if (bytes == null) {
|
if (bytes == null) {
|
||||||
|
|
|
@ -22,7 +22,12 @@ public final class JVMHelper {
|
||||||
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
||||||
// System properties
|
// System properties
|
||||||
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static final int OS_BITS = getCorrectOSArch();
|
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 int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
|
||||||
// Public static fields
|
// Public static fields
|
||||||
public static final Runtime RUNTIME = Runtime.getRuntime();
|
public static final Runtime RUNTIME = Runtime.getRuntime();
|
||||||
|
@ -41,6 +46,24 @@ public final class JVMHelper {
|
||||||
private 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() {
|
public static int getVersion() {
|
||||||
//System.out.println("[DEBUG] JVMHelper 11 version");
|
//System.out.println("[DEBUG] JVMHelper 11 version");
|
||||||
return Runtime.version().feature();
|
return Runtime.version().feature();
|
||||||
|
@ -108,6 +131,7 @@ public static void checkStackTrace(Class<?> mainClass) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
private static int getCorrectOSArch() {
|
private static int getCorrectOSArch() {
|
||||||
// As always, mustdie must die
|
// As always, mustdie must die
|
||||||
if (OS_TYPE == OS.MUSTDIE)
|
if (OS_TYPE == OS.MUSTDIE)
|
||||||
|
@ -123,6 +147,7 @@ public static String getEnvPropertyCaseSensitive(String name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static boolean isJVMMatchesSystemArch() {
|
public static boolean isJVMMatchesSystemArch() {
|
||||||
return JVM_BITS == OS_BITS;
|
return JVM_BITS == OS_BITS;
|
||||||
}
|
}
|
||||||
|
@ -154,10 +179,6 @@ public static void verifySystemProperties(Class<?> mainClass, boolean requireSys
|
||||||
|
|
||||||
// Verify system and java architecture
|
// Verify system and java architecture
|
||||||
LogHelper.debug("Verifying JVM 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 {
|
public enum OS {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import pro.gravit.launcher.ClientPermissions;
|
import pro.gravit.launcher.ClientPermissions;
|
||||||
import pro.gravit.launcher.Launcher;
|
import pro.gravit.launcher.Launcher;
|
||||||
import pro.gravit.launcher.LauncherConfig;
|
import pro.gravit.launcher.LauncherConfig;
|
||||||
|
import pro.gravit.launcher.api.KeyService;
|
||||||
import pro.gravit.launcher.config.JsonConfigurable;
|
import pro.gravit.launcher.config.JsonConfigurable;
|
||||||
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||||
import pro.gravit.launcher.events.request.ProfilesRequestEvent;
|
import pro.gravit.launcher.events.request.ProfilesRequestEvent;
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
import pro.gravit.utils.PublicURLClassLoader;
|
import pro.gravit.utils.PublicURLClassLoader;
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -134,6 +136,9 @@ public void run(String... args) throws Throwable {
|
||||||
restore();
|
restore();
|
||||||
getProfiles();
|
getProfiles();
|
||||||
}
|
}
|
||||||
|
if(config.encodedServerRsaPublicKey != null) {
|
||||||
|
KeyService.serverRsaPublicKey = SecurityHelper.toPublicRSAKey(config.encodedServerRsaPublicKey);
|
||||||
|
}
|
||||||
String classname = (config.mainclass == null || config.mainclass.isEmpty()) ? args[0] : config.mainclass;
|
String classname = (config.mainclass == null || config.mainclass.isEmpty()) ? args[0] : config.mainclass;
|
||||||
if (classname.length() == 0) {
|
if (classname.length() == 0) {
|
||||||
LogHelper.error("MainClass not found. Please set MainClass for ServerWrapper.json or first commandline argument");
|
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 Map<String, String> extendedTokens;
|
||||||
public LauncherConfig.LauncherEnvironment env;
|
public LauncherConfig.LauncherEnvironment env;
|
||||||
public ModuleConf moduleConf = new ModuleConf();
|
public ModuleConf moduleConf = new ModuleConf();
|
||||||
|
|
||||||
|
public byte[] encodedServerRsaPublicKey;
|
||||||
|
|
||||||
|
public byte[] encodedServerEcPublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class ModuleConf {
|
public static final class ModuleConf {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package pro.gravit.launcher.server.setup;
|
package pro.gravit.launcher.server.setup;
|
||||||
|
|
||||||
|
import pro.gravit.launcher.events.request.GetPublicKeyRequestEvent;
|
||||||
import pro.gravit.launcher.profiles.ClientProfile;
|
import pro.gravit.launcher.profiles.ClientProfile;
|
||||||
import pro.gravit.launcher.request.Request;
|
import pro.gravit.launcher.request.Request;
|
||||||
|
import pro.gravit.launcher.request.auth.GetPublicKeyRequest;
|
||||||
import pro.gravit.launcher.request.websockets.StdWebSocketService;
|
import pro.gravit.launcher.request.websockets.StdWebSocketService;
|
||||||
import pro.gravit.launcher.server.ServerWrapper;
|
import pro.gravit.launcher.server.ServerWrapper;
|
||||||
import pro.gravit.utils.PublicURLClassLoader;
|
import pro.gravit.utils.PublicURLClassLoader;
|
||||||
|
@ -79,6 +81,9 @@ public void run() throws Exception {
|
||||||
try {
|
try {
|
||||||
wrapper.restore();
|
wrapper.restore();
|
||||||
wrapper.getProfiles();
|
wrapper.getProfiles();
|
||||||
|
GetPublicKeyRequestEvent publicKeyRequestEvent = new GetPublicKeyRequest().request();
|
||||||
|
wrapper.config.encodedServerRsaPublicKey = publicKeyRequestEvent.rsaPublicKey;
|
||||||
|
wrapper.config.encodedServerEcPublicKey = publicKeyRequestEvent.ecdsaPublicKey;
|
||||||
break;
|
break;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
id 'org.openjfx.javafxplugin' version '0.0.10' apply false
|
id 'org.openjfx.javafxplugin' version '0.0.10' apply false
|
||||||
}
|
}
|
||||||
group = 'pro.gravit.launcher'
|
group = 'pro.gravit.launcher'
|
||||||
version = '5.2.13'
|
version = '5.3.0'
|
||||||
|
|
||||||
apply from: 'props.gradle'
|
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
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
6
gradlew
vendored
6
gradlew
vendored
|
@ -205,6 +205,12 @@ set -- \
|
||||||
org.gradle.wrapper.GradleWrapperMain \
|
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.
|
# Use "xargs" to parse quoted args.
|
||||||
#
|
#
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
# 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 limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
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
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
||||||
Subproject commit d3722bf136b8c8c8da3dea879d5d2015e84b0f8c
|
Subproject commit eb3145d3e75ff3557fe9f9fc87dd5ea0737ff3a2
|
Loading…
Reference in a new issue