Merge branch 'release/5.5.0'

This commit is contained in:
Gravita 2023-10-31 01:52:54 +07:00
commit 1df283d659
153 changed files with 1901 additions and 1994 deletions

View file

@ -16,10 +16,10 @@ jobs:
path: ~/.gradle/caches
key: gravit-${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-launcher
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v1
with:
java-version: 17
java-version: 21
- name: Grant execute permission for gradlew
run: chmod +x gradlew

View file

@ -15,8 +15,8 @@
}
}
sourceCompatibility = '17'
targetCompatibility = '17'
sourceCompatibility = '21'
targetCompatibility = '21'
configurations {
compileOnlyA
@ -75,6 +75,7 @@
dependencies {
pack project(':LauncherAPI')
pack project(':LauncherModernCore')
bundle group: 'me.tongfei', name: 'progressbar', version: '0.9.2'
bundle group: 'com.github.Marcono1234', name: 'gson-record-type-adapter-factory', version: 'v0.2.0'
bundle group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
@ -85,7 +86,7 @@ pack project(':LauncherAPI')
bundle group: 'org.ow2.asm', name: 'asm-commons', version: rootProject['verAsm']
bundle group: 'io.netty', name: 'netty-all', version: rootProject['verNetty']
bundle group: 'org.slf4j', name: 'slf4j-api', version: rootProject['verSlf4j']
bundle group: 'mysql', name: 'mysql-connector-java', version: rootProject['verMySQLConn']
bundle group: 'com.mysql', name: 'mysql-connector-j', version: rootProject['verMySQLConn']
bundle group: 'org.postgresql', name: 'postgresql', version: rootProject['verPostgreSQLConn']
bundle group: 'com.guardsquare', name: 'proguard-base', version: rootProject['verProguard']
bundle group: 'org.apache.logging.log4j', name: 'log4j-core', version: rootProject['verLog4j']
@ -93,6 +94,7 @@ pack project(':LauncherAPI')
bundle group: 'io.jsonwebtoken', name: 'jjwt-api', version: rootProject['verJwt']
bundle group: 'io.jsonwebtoken', name: 'jjwt-impl', version: rootProject['verJwt']
bundle group: 'io.jsonwebtoken', name: 'jjwt-gson', version: rootProject['verJwt']
annotationProcessor(group: 'org.apache.logging.log4j', name: 'log4j-core', version: rootProject['verLog4j'])
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: rootProject['verJunit']
hikari 'io.micrometer:micrometer-core:1.8.4'

View file

@ -21,6 +21,10 @@ public <T> SimpleErrorHandler<T> makeEH(Class<T> clazz) {
return new SimpleErrorHandler<>(clazz);
}
public <T> SimpleErrorHandler<T> makeEH(Type clazz) {
return new SimpleErrorHandler<>(clazz);
}
public <T> HttpRequest get(String url, String token) {
try {
var requestBuilder = HttpRequest.newBuilder()
@ -59,6 +63,10 @@ public <T> HttpHelper.HttpOptional<T, SimpleError> send(HttpRequest request, Cla
return HttpHelper.send(httpClient, request, makeEH(clazz));
}
public <T> HttpHelper.HttpOptional<T, SimpleError> send(HttpRequest request, Type type) throws IOException {
return HttpHelper.send(httpClient, request, makeEH(type));
}
public static class SimpleErrorHandler<T> implements HttpHelper.HttpJsonErrorHandler<T, SimpleError> {
private final Type type;

View file

@ -16,6 +16,7 @@
import pro.gravit.launchserver.binary.LauncherBinary;
import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
import pro.gravit.launchserver.helper.SignHelper;
import pro.gravit.launchserver.launchermodules.LauncherModuleLoader;
import pro.gravit.launchserver.manangers.*;
import pro.gravit.launchserver.manangers.hook.AuthHookManager;
@ -36,13 +37,15 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.KeyStore;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@ -185,6 +188,10 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
}
launcherModuleLoader.init();
nettyServerSocketHandler = new NettyServerSocketHandler(this);
if(config.sign.checkCertificateExpired) {
checkCertificateExpired();
service.scheduleAtFixedRate(this::checkCertificateExpired, 24, 24, TimeUnit.HOURS);
}
// post init modules
modulesManager.invokeEvent(new LaunchServerPostInitPhase(this));
}
@ -234,7 +241,6 @@ public void invoke(String... args) throws Exception {
}
switch (args[0]) {
case "full" -> reload(ReloadType.FULL);
case "no_auth" -> reload(ReloadType.NO_AUTH);
case "no_components" -> reload(ReloadType.NO_COMPONENTS);
default -> reload(ReloadType.NO_AUTH);
}
@ -269,6 +275,25 @@ public void invoke(String... args) throws Exception {
return commands;
}
public void checkCertificateExpired() {
if(!config.sign.enabled) {
return;
}
try {
KeyStore keyStore = SignHelper.getStore(Paths.get(config.sign.keyStore), config.sign.keyStorePass, config.sign.keyStoreType);
Instant date = SignHelper.getCertificateExpired(keyStore, config.sign.keyAlias);
if(date == null) {
logger.debug("The certificate will expire at unlimited");
} else if(date.minus(Duration.ofDays(30)).isBefore(Instant.now())) {
logger.warn("The certificate will expire at {}", date.toString());
} else {
logger.debug("The certificate will expire at {}", date.toString());
}
} catch (Throwable e) {
logger.error("Can't get certificate expire date", e);
}
}
private LauncherBinary binary() {
LaunchServerLauncherExeInit event = new LaunchServerLauncherExeInit(this, null);
modulesManager.invokeEvent(event);

View file

@ -11,6 +11,7 @@
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.mix.MixProvider;
import pro.gravit.launchserver.auth.password.PasswordVerifier;
import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.texture.TextureProvider;
@ -212,6 +213,7 @@ public static void registerAll() {
GetAvailabilityAuthRequest.registerProviders();
OptionalAction.registerProviders();
OptionalTrigger.registerProviders();
MixProvider.registerProviders();
}
private static void printExperimentalBranch() {

View file

@ -11,7 +11,6 @@
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
@SuppressWarnings("rawtypes")
public class InjectClassAcceptor implements MainBuildTask.ASMTransformer {

View file

@ -149,10 +149,7 @@ public static int opcodeEmulation(AbstractInsnNode e) {
break;
case INVOKEVIRTUAL:
case INVOKESPECIAL:
case INVOKEINTERFACE:
stackSize += doMethodEmulation(((MethodInsnNode) e).desc);
break;
case INVOKESTATIC:
case INVOKEINTERFACE, INVOKESTATIC:
stackSize += doMethodEmulation(((MethodInsnNode) e).desc);
break;
case INVOKEDYNAMIC:

View file

@ -4,6 +4,7 @@
import org.apache.logging.log4j.Logger;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.mix.MixProvider;
import pro.gravit.launchserver.auth.texture.TextureProvider;
import java.io.IOException;
@ -16,6 +17,7 @@ public final class AuthProviderPair {
public boolean isDefault = true;
public AuthCoreProvider core;
public TextureProvider textureProvider;
public Map<String, MixProvider> mixes;
public Map<String, String> links;
public transient String name;
public transient Set<String> features;
@ -36,12 +38,14 @@ public static Set<String> getFeatures(Class<?> clazz) {
return list;
}
public Set<String> getFeatures() {
return features;
}
public static void getFeatures(Class<?> clazz, Set<String> list) {
Features features = clazz.getAnnotation(Features.class);
if (features != null) {
for (Feature feature : features.value()) {
list.add(feature.value());
}
Feature[] features = clazz.getAnnotationsByType(Feature.class);
for (Feature feature : features) {
list.add(feature.value());
}
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && superClass != Object.class) {
@ -55,8 +59,15 @@ public static void getFeatures(Class<?> clazz, Set<String> list) {
public final <T> T isSupport(Class<T> clazz) {
if (core == null) return null;
T result = null;
if (result == null) result = core.isSupport(clazz);
T result = core.isSupport(clazz);
if (result == null && mixes != null) {
for(var m : mixes.values()) {
result = m.isSupport(clazz);
if(result != null) {
break;
}
}
}
return result;
}
@ -66,6 +77,12 @@ public final void init(LaunchServer srv, String name) {
core.init(srv);
features = new HashSet<>();
getFeatures(core.getClass(), features);
if(mixes != null) {
for(var m : mixes.values()) {
m.init(srv, core);
getFeatures(m.getClass(), features);
}
}
}
public final void link(LaunchServer srv) {
@ -87,5 +104,10 @@ public final void close() throws IOException {
if (textureProvider != null) {
textureProvider.close();
}
if(mixes != null) {
for(var m : mixes.values()) {
m.close();
}
}
}
}

View file

@ -14,6 +14,7 @@
import pro.gravit.launchserver.auth.password.PasswordVerifier;
import pro.gravit.launchserver.helper.LegacySessionHelper;
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.SecurityHelper;
@ -162,6 +163,25 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
}
}
@Override
public User checkServer(Client client, String username, String serverID) throws IOException {
SQLUser user = (SQLUser) getUserByUsername(username);
if (user == null) {
return null;
}
if (user.getUsername().equals(username) && user.getServerId().equals(serverID)) {
return user;
}
return null;
}
@Override
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) throws IOException {
SQLUser user = (SQLUser) client.getUser();
if (user == null) return false;
return user.getUsername().equals(username) && user.getAccessToken().equals(accessToken) && updateServerID(user, serverID);
}
@Override
public void init(LaunchServer server) {
this.server = server;
@ -225,7 +245,6 @@ protected void updateAuth(User user, String accessToken) throws IOException {
}
}
@Override
protected boolean updateServerID(User user, String serverID) throws IOException {
try (Connection c = getSQLConfig().getConnection()) {
SQLUser SQLUser = (SQLUser) user;
@ -328,12 +347,10 @@ public UUID getUUID() {
return uuid;
}
@Override
public String getServerId() {
return serverId;
}
@Override
public String getAccessToken() {
return accessToken;
}
@ -372,6 +389,11 @@ public User getUser() {
return user;
}
@Override
public String getMinecraftAccessToken() {
return user.getAccessToken();
}
@Override
public long getExpireIn() {
return 0;

View file

@ -45,7 +45,6 @@ public static void registerProviders() {
providers.register("mysql", MySQLCoreProvider.class);
providers.register("postgresql", PostgresSQLCoreProvider.class);
providers.register("memory", MemoryAuthCoreProvider.class);
providers.register("http", HttpAuthCoreProvider.class);
providers.register("merge", MergeAuthCoreProvider.class);
registredProviders = true;
}
@ -75,11 +74,6 @@ public AuthManager.AuthReport authorize(User user, AuthResponse.AuthContext cont
public abstract void init(LaunchServer server);
// Auth Handler methods
protected boolean updateServerID(User user, String serverID) throws IOException {
throw new UnsupportedOperationException();
}
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
return List.of(new AuthPasswordDetails());
}
@ -292,22 +286,9 @@ public void invoke(String... args) throws Exception {
return map;
}
public User checkServer(Client client, String username, String serverID) throws IOException {
User user = getUserByUsername(username);
if (user == null) {
return null;
}
if (user.getUsername().equals(username) && user.getServerId().equals(serverID)) {
return user;
}
return null;
}
public abstract User checkServer(Client client, String username, String serverID) throws IOException;
public boolean joinServer(Client client, String username, String accessToken, String serverID) throws IOException {
User user = client.getUser();
if (user == null) return false;
return user.getUsername().equals(username) && user.getAccessToken().equals(accessToken) && updateServerID(user, serverID);
}
public abstract boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) throws IOException;
@SuppressWarnings("unchecked")
public <T> T isSupport(Class<T> clazz) {

View file

@ -1,646 +0,0 @@
package pro.gravit.launchserver.auth.core;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.events.request.GetAvailabilityAuthRequestEvent;
import pro.gravit.launcher.profiles.Texture;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.secure.HardwareReportRequest;
import pro.gravit.launchserver.HttpRequester;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportRemoteClientAccess;
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportHardware;
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportProperties;
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
import pro.gravit.launchserver.helper.HttpHelper;
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.helper.CommonHelper;
import java.io.IOException;
import java.util.*;
public class HttpAuthCoreProvider extends AuthCoreProvider implements AuthSupportHardware, AuthSupportRemoteClientAccess {
private transient final Logger logger = LogManager.getLogger();
public String bearerToken;
public String getUserByUsernameUrl;
public String getUserByLoginUrl;
public String getUserByUUIDUrl;
public String getUserByTokenUrl;
public String getAuthDetailsUrl;
public String refreshTokenUrl;
public String authorizeUrl;
public String joinServerUrl;
public String checkServerUrl;
public String updateServerIdUrl;
//below fields can be empty if advanced protect handler disabled
public String getHardwareInfoByPublicKeyUrl;
public String getHardwareInfoByDataUrl;
public String getHardwareInfoByIdUrl;
public String createHardwareInfoUrl;
public String connectUserAndHardwareUrl;
public String addPublicKeyToHardwareInfoUrl;
public String getUsersByHardwareInfoUrl;
public String banHardwareUrl;
public String unbanHardwareUrl;
public String apiUrl;
public List<String> apiFeatures;
private transient HttpRequester requester;
@Override
public User getUserByUsername(String username) {
try {
return requester.send(requester.get(CommonHelper.replace(getUserByUsernameUrl, "username", username), null), HttpUser.class).getOrThrow();
} catch (IOException e) {
logger.error(e);
return null;
}
}
@Override
public User getUserByLogin(String login) {
if (getUserByLoginUrl != null) {
try {
return requester.send(requester.get(CommonHelper.replace(getUserByLoginUrl, "login", login), null), HttpUser.class).getOrThrow();
} catch (IOException e) {
logger.error(e);
return null;
}
}
return super.getUserByLogin(login);
}
@Override
public User getUserByUUID(UUID uuid) {
try {
return requester.send(requester.get(CommonHelper.replace(getUserByUUIDUrl, "uuid", uuid.toString()), null), HttpUser.class).getOrThrow();
} catch (IOException e) {
logger.error(e);
return null;
}
}
@Override
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
if (getAuthDetailsUrl == null) {
return super.getDetails(client);
}
try {
var result = requester.send(requester.get(getAuthDetailsUrl, bearerToken), GetAuthDetailsResponse.class).getOrThrow();
return result.details;
} catch (IOException e) {
logger.error(e);
return super.getDetails(client);
}
}
@Override
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
if (getUserByTokenUrl == null) {
return null;
}
try {
var result = requester.send(requester.get(getUserByTokenUrl, accessToken), HttpUserSession.class);
if (!result.isSuccessful()) {
var error = result.error().error;
if (error.equals(AuthRequestEvent.OAUTH_TOKEN_EXPIRE)) {
throw new OAuthAccessTokenExpired();
}
return null;
}
return result.getOrThrow();
} catch (IOException e) {
logger.error(e);
return null;
}
}
@Override
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
if (refreshTokenUrl == null) {
return null;
}
try {
return requester.send(requester.post(refreshTokenUrl, new RefreshTokenRequest(refreshToken, context),
null), HttpAuthReport.class).getOrThrow().toAuthReport();
} catch (IOException e) {
logger.error(e);
return null;
}
}
@Override
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
var result = requester.send(requester.post(authorizeUrl, new AuthorizeRequest(login, context, password, minecraftAccess),
bearerToken), HttpAuthReport.class);
if (!result.isSuccessful()) {
var error = result.error().error;
if (error != null) {
throw new AuthException(error);
}
}
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);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Iterable<User> getUsersByHardwareInfo(UserHardware hardware) {
if (getUsersByHardwareInfoUrl == null) {
return null;
}
try {
return (List<User>) (List) requester.send(requester
.post(getUsersByHardwareInfoUrl, new HardwareRequest((HttpUserHardware) hardware), bearerToken), GetHardwareListResponse.class).getOrThrow().list;
} 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);
}
}
@Override
public String getClientApiUrl() {
return apiUrl;
}
@Override
public List<String> getClientApiFeatures() {
return apiFeatures;
}
@Override
protected boolean updateServerID(User user, String serverID) throws IOException {
var result = requester.send(requester.post(updateServerIdUrl, new UpdateServerIdRequest(user.getUsername(), user.getUUID(), serverID),
null), Void.class);
return result.isSuccessful();
}
@Override
public User checkServer(Client client, String username, String serverID) throws IOException {
return requester.send(requester.post(checkServerUrl, new CheckServerRequest(username, serverID), bearerToken), HttpUser.class).getOrThrow();
}
@Override
public boolean joinServer(Client client, String username, String accessToken, String serverID) throws IOException {
var result = requester.send(requester.post(joinServerUrl, new JoinServerRequest(username, accessToken, serverID), bearerToken), Void.class);
return result.isSuccessful();
}
@Override
public void init(LaunchServer server) {
requester = new HttpRequester();
if (getUserByUsernameUrl == null) {
throw new IllegalArgumentException("'getUserByUsernameUrl' can't be null");
}
if (getUserByUUIDUrl == null) {
throw new IllegalArgumentException("'getUserByUUIDUrl' can't be null");
}
if (authorizeUrl == null) {
throw new IllegalArgumentException("'authorizeUrl' can't be null");
}
if (checkServerUrl == null && joinServerUrl == null && updateServerIdUrl == null) {
throw new IllegalArgumentException("Please set 'checkServerUrl' and 'joinServerUrl' or 'updateServerIdUrl'");
}
}
@Override
public void close() {
}
public record HttpAuthReport(String minecraftAccessToken, String oauthAccessToken,
String oauthRefreshToken, long oauthExpire,
HttpUserSession session) {
public AuthManager.AuthReport toAuthReport() {
return new AuthManager.AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire, session);
}
}
public static class UpdateServerIdRequest {
public String username;
public UUID uuid;
public String serverId;
public UpdateServerIdRequest(String username, UUID uuid, String serverId) {
this.username = username;
this.uuid = uuid;
this.serverId = serverId;
}
}
public static class CheckServerRequest {
public String username;
public String serverId;
public CheckServerRequest(String username, String serverId) {
this.username = username;
this.serverId = serverId;
}
}
public static class GetAuthDetailsResponse {
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> details;
}
public static class GetHardwareListResponse {
public List<HttpUser> list;
}
public static class JoinServerRequest {
public String username;
public String accessToken;
public String serverId;
public JoinServerRequest(String username, String accessToken, String serverId) {
this.username = username;
this.accessToken = accessToken;
this.serverId = serverId;
}
}
public static class AuthorizeRequest {
public String login;
public AuthResponse.AuthContext context;
public AuthRequest.AuthPasswordInterface password;
public boolean minecraftAccess;
public AuthorizeRequest() {
}
public AuthorizeRequest(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) {
this.login = login;
this.context = context;
this.password = password;
this.minecraftAccess = minecraftAccess;
}
}
public static class RefreshTokenRequest {
public String refreshToken;
public AuthResponse.AuthContext context;
public RefreshTokenRequest(String refreshToken, AuthResponse.AuthContext context) {
this.refreshToken = refreshToken;
this.context = context;
}
}
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 static class HttpUserSession implements UserSession {
private String id;
private HttpUser user;
private long expireIn;
public HttpUserSession() {
}
public HttpUserSession(String id, HttpUser user, long expireIn) {
this.id = id;
this.user = user;
this.expireIn = expireIn;
}
@Override
public String getID() {
return id;
}
@Override
public User getUser() {
return user;
}
@Override
public long getExpireIn() {
return expireIn;
}
@Override
public String toString() {
return "HttpUserSession{" +
"id='" + id + '\'' +
", user=" + user +
", expireIn=" + expireIn +
'}';
}
}
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 +
'}';
}
}
public class HttpUser implements User, UserSupportTextures, UserSupportProperties, UserSupportHardware {
private String username;
private UUID uuid;
private String serverId;
private String accessToken;
private ClientPermissions permissions;
private Map<String, Texture> assets;
private Map<String, String> properties;
private long hwidId;
private transient HttpUserHardware hardware;
public HttpUser() {
}
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, long hwidId) {
this.username = username;
this.uuid = uuid;
this.serverId = serverId;
this.accessToken = accessToken;
this.permissions = permissions;
this.hwidId = hwidId;
}
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, long hwidId) {
this.username = username;
this.uuid = uuid;
this.serverId = serverId;
this.accessToken = accessToken;
this.permissions = permissions;
this.hwidId = hwidId;
}
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, Map<String, String> properties, long hwidId) {
this.username = username;
this.uuid = uuid;
this.serverId = serverId;
this.accessToken = accessToken;
this.permissions = permissions;
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, long hwidId) {
this.username = username;
this.uuid = uuid;
this.serverId = serverId;
this.accessToken = accessToken;
this.permissions = permissions;
this.assets = assets;
this.properties = properties;
this.hwidId = hwidId;
}
@Override
public String getUsername() {
return username;
}
@Override
public UUID getUUID() {
return uuid;
}
@Override
public String getServerId() {
return serverId;
}
@Override
public String getAccessToken() {
return accessToken;
}
@Override
public ClientPermissions getPermissions() {
return permissions;
}
@Override
public Texture getSkinTexture() {
return assets.get("SKIN");
}
@Override
public Texture getCloakTexture() {
return assets.get("CAPE");
}
public Map<String, Texture> getAssets() {
if (assets == null) {
return new HashMap<>();
}
return assets;
}
@Override
public Map<String, String> getProperties() {
if (properties == null) {
return new HashMap<>();
}
return properties;
}
@Override
public String toString() {
return "HttpUser{" +
"username='" + username + '\'' +
", uuid=" + uuid +
", serverId='" + serverId + '\'' +
", accessToken='" + accessToken + '\'' +
", permissions=" + permissions +
", assets=" + getAssets() +
", properties=" + properties +
", hwidId=" + hwidId +
'}';
}
@Override
public UserHardware getHardware() {
if (hardware != null) return hardware;
HttpAuthCoreProvider.HttpUserHardware result = (HttpUserHardware) getHardwareInfoById(String.valueOf(hwidId));
hardware = result;
return result;
}
}
}

View file

@ -94,13 +94,6 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
}
}
@Override
protected boolean updateServerID(User user, String serverID) {
MemoryUser memoryUser = (MemoryUser) user;
memoryUser.serverId = serverID;
return true;
}
@Override
public User checkServer(Client client, String username, String serverID) {
synchronized (memory) {
@ -116,7 +109,7 @@ public User checkServer(Client client, String username, String serverID) {
}
@Override
public boolean joinServer(Client client, String username, String accessToken, String serverID) {
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) {
return true;
}
@ -158,16 +151,6 @@ public UUID getUUID() {
return uuid;
}
@Override
public String getServerId() {
return serverId;
}
@Override
public String getAccessToken() {
return accessToken;
}
@Override
public ClientPermissions getPermissions() {
return permissions;
@ -208,6 +191,11 @@ public User getUser() {
return user;
}
@Override
public String getMinecraftAccessToken() {
return "IGNORED";
}
@Override
public long getExpireIn() {
return expireIn;

View file

@ -67,7 +67,7 @@ public User checkServer(Client client, String username, String serverID) throws
}
@Override
public boolean joinServer(Client client, String username, String accessToken, String serverID) {
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) {
return false; // Authorization not supported
}

View file

@ -4,6 +4,7 @@
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import java.io.IOException;
@ -46,7 +47,12 @@ public void init(LaunchServer server) {
}
@Override
protected boolean updateServerID(User user, String serverID) {
public User checkServer(Client client, String username, String serverID) throws IOException {
return null;
}
@Override
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) throws IOException {
return false;
}

View file

@ -9,10 +9,6 @@ public interface User {
UUID getUUID();
String getServerId();
String getAccessToken();
ClientPermissions getPermissions();
default boolean isBanned() {

View file

@ -5,5 +5,7 @@ public interface UserSession {
User getUser();
String getMinecraftAccessToken();
long getExpireIn();
}

View file

@ -0,0 +1,22 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launcher.events.request.AssetUploadInfoRequestEvent;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.events.request.GetAssetUploadUrlRequestEvent;
import pro.gravit.launchserver.auth.Feature;
import pro.gravit.launchserver.auth.core.User;
import java.util.Set;
@Feature(GetAssetUploadUrlRequestEvent.FEATURE_NAME)
public interface AuthSupportAssetUpload {
String getAssetUploadUrl(String name, User user);
default AuthRequestEvent.OAuthRequestEvent getAssetUploadToken(String name, User user) {
return null;
}
default AssetUploadInfoRequestEvent getAssetUploadInfo(User user) {
return new AssetUploadInfoRequestEvent(Set.of("SKIN", "CAPE"), AssetUploadInfoRequestEvent.SlimSupportConf.USER);
}
}

View file

@ -1,12 +0,0 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launchserver.auth.Feature;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.UserSession;
import java.util.List;
@Feature("sessions")
public interface AuthSupportGetSessionsFromUser extends AuthSupport {
List<UserSession> getSessionsByUser(User user);
}

View file

@ -1,20 +0,0 @@
package pro.gravit.launchserver.auth.core.interfaces.provider;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportBanInfo;
import java.time.LocalDateTime;
public interface AuthSupportUserBan extends AuthSupport {
UserSupportBanInfo.UserBanInfo banUser(User user, String reason, String moderator, LocalDateTime startTime, LocalDateTime endTime);
default UserSupportBanInfo.UserBanInfo banUser(User user) {
return banUser(user, null, null, LocalDateTime.now(), null);
}
void unbanUser(User user);
default UserSupportBanInfo fetchUserBanInfo(User user) {
return (UserSupportBanInfo) user;
}
}

View file

@ -1,27 +0,0 @@
package pro.gravit.launchserver.auth.core.interfaces.user;
import java.time.LocalDateTime;
public interface UserSupportBanInfo {
UserBanInfo getBanInfo();
interface UserBanInfo {
String getId();
default String getReason() {
return null;
}
default String getModerator() {
return null;
}
default LocalDateTime getStartDate() {
return null;
}
default LocalDateTime getEndDate() {
return null;
}
}
}

View file

@ -0,0 +1,31 @@
package pro.gravit.launchserver.auth.mix;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.utils.ProviderMap;
public abstract class MixProvider implements AutoCloseable{
public static final ProviderMap<MixProvider> providers = new ProviderMap<>("MixProvider");
private static final Logger logger = LogManager.getLogger();
private static boolean registredProviders = false;
public static void registerProviders() {
if (!registredProviders) {
providers.register("uploadAsset", UploadAssetMixProvider.class);
registredProviders = true;
}
}
public abstract void init(LaunchServer server, AuthCoreProvider core);
@SuppressWarnings("unchecked")
public <T> T isSupport(Class<T> clazz) {
if (clazz.isAssignableFrom(getClass())) return (T) this;
return null;
}
@Override
public abstract void close();
}

View file

@ -0,0 +1,34 @@
package pro.gravit.launchserver.auth.mix;
import pro.gravit.launcher.events.request.AssetUploadInfoRequestEvent;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportAssetUpload;
import java.util.Map;
public class UploadAssetMixProvider extends MixProvider implements AuthSupportAssetUpload {
public Map<String, String> urls;
public AssetUploadInfoRequestEvent.SlimSupportConf slimSupportConf;
@Override
public String getAssetUploadUrl(String name, User user) {
return urls.get(name);
}
@Override
public AssetUploadInfoRequestEvent getAssetUploadInfo(User user) {
return new AssetUploadInfoRequestEvent(urls.keySet(), slimSupportConf);
}
@Override
public void init(LaunchServer server, AuthCoreProvider core) {
}
@Override
public void close() {
}
}

View file

@ -20,6 +20,7 @@
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -41,7 +42,7 @@ public boolean allowGetSecureLevelInfo(Client client) {
@Override
public void onHardwareReport(HardwareReportResponse response, Client client) {
if (!enableHardwareFeature) {
response.sendResult(new HardwareReportRequestEvent(null));
response.sendResult(new HardwareReportRequestEvent());
return;
}
if (!client.isAuth || client.trustLevel == null || client.trustLevel.publicKey == null) {
@ -63,7 +64,7 @@ public void onHardwareReport(HardwareReportResponse response, Client client) {
throw new SecurityException("Your hardware banned");
}
client.trustLevel.hardwareInfo = hardware.getHardwareInfo();
response.sendResult(new HardwareReportRequestEvent(createHardwareToken(client.username, hardware)));
response.sendResult(new HardwareReportRequestEvent(createHardwareToken(client.username, hardware), SECONDS.toMillis(server.config.netty.security.hardwareTokenExpire)));
} else {
logger.error("AuthCoreProvider not supported hardware");
response.sendError("AuthCoreProvider not supported hardware");
@ -78,22 +79,22 @@ public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) {
if (authSupportHardware != null) {
UserHardware hardware = authSupportHardware.getHardwareInfoByPublicKey(client.trustLevel.publicKey);
if (hardware == null) //HWID not found?
return new VerifySecureLevelKeyRequestEvent(true, false, createPublicKeyToken(client.username, client.trustLevel.publicKey));
return new VerifySecureLevelKeyRequestEvent(true, false, createPublicKeyToken(client.username, client.trustLevel.publicKey), SECONDS.toMillis(server.config.netty.security.publicKeyTokenExpire));
if (hardware.isBanned()) {
throw new SecurityException("Your hardware banned");
}
client.trustLevel.hardwareInfo = hardware.getHardwareInfo();
authSupportHardware.connectUserAndHardware(client.sessionObject, hardware);
return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey), createHardwareToken(client.username, hardware));
return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey), SECONDS.toMillis(server.config.netty.security.publicKeyTokenExpire));
} else {
logger.warn("AuthCoreProvider not supported hardware. HardwareInfo not checked!");
}
}
return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey));
return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey), SECONDS.toMillis(server.config.netty.security.publicKeyTokenExpire));
}
@Override
public boolean onJoinServer(String serverID, String username, Client client) {
public boolean onJoinServer(String serverID, String username, UUID uuid, Client client) {
return !enableHardwareFeature || (client.trustLevel != null && client.trustLevel.hardwareInfo != null);
}

View file

@ -1,5 +1,6 @@
package pro.gravit.launchserver.auth.protect;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
public class NoProtectHandler extends ProtectHandler {
@ -9,4 +10,8 @@ public boolean allowGetAccessToken(AuthResponse.AuthContext context) {
return true;
}
@Override
public boolean allowJoinServer(Client client) {
return true;
}
}

View file

@ -1,6 +1,7 @@
package pro.gravit.launchserver.auth.protect;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.utils.ProviderMap;
@ -19,6 +20,9 @@ public static void registerHandlers() {
}
public abstract boolean allowGetAccessToken(AuthResponse.AuthContext context);
public boolean allowJoinServer(Client client) {
return client.isAuth && client.type == AuthResponse.ConnectTypes.CLIENT;
}
public void init(LaunchServer server) {

View file

@ -2,8 +2,10 @@
import pro.gravit.launchserver.socket.Client;
import java.util.UUID;
public interface JoinServerProtectHandler {
default boolean onJoinServer(String serverID, String username, Client client) {
default boolean onJoinServer(String serverID, String username, UUID uuid, Client client) {
return true;
}
}

View file

@ -3,22 +3,23 @@
import com.google.gson.reflect.TypeToken;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.HTTPRequest;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.profiles.Texture;
import pro.gravit.launchserver.HttpRequester;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class JsonTextureProvider extends TextureProvider {
private static final Type MAP_TYPE = new TypeToken<Map<String, Texture>>() {
private static final Type MAP_TYPE = new TypeToken<Map<String, JsonTexture>>() {
}.getType();
private transient final Logger logger = LogManager.getLogger();
private transient final HttpRequester requester = new HttpRequester();
public String url;
public String bearerToken;
@Override
public void close() {
@ -40,24 +41,28 @@ public Texture getSkinTexture(UUID uuid, String username, String client) {
@Override
public Map<String, Texture> getAssets(UUID uuid, String username, String client) {
try {
var result = HTTPRequest.jsonRequest(null, "GET", new URL(RequestTextureProvider.getTextureURL(url, uuid, username, client)));
Map<String, Texture> map = Launcher.gsonManager.gson.fromJson(result, MAP_TYPE);
if (map == null) {
return new HashMap<>();
}
if (map.get("skin") != null) { // Legacy script
map.put("SKIN", map.get("skin"));
map.remove("skin");
}
if (map.get("cloak") != null) {
map.put("CAPE", map.get("cloak"));
map.remove("cloak");
}
return map;
Map<String, JsonTexture> map = requester.<Map<String, JsonTexture>>send(requester.get(RequestTextureProvider.getTextureURL(url, uuid, username, client), bearerToken), MAP_TYPE).getOrThrow();
return JsonTexture.convertMap(map);
} catch (IOException e) {
logger.error("JsonTextureProvider", e);
return new HashMap<>();
}
}
public record JsonTexture(String url, String digest, Map<String, String> metadata) {
public Texture toTexture() {
return new Texture(url, digest == null ? null : SecurityHelper.fromHex(digest), metadata);
}
public static Map<String, Texture> convertMap(Map<String, JsonTexture> map) {
if (map == null) {
return new HashMap<>();
}
Map<String, Texture> res = new HashMap<>();
for(var e : map.entrySet()) {
res.put(e.getKey(), e.getValue().toTexture());
}
return res;
}
}
}

View file

@ -18,7 +18,6 @@
public class BinaryPipeline {
public final List<LauncherBuildTask> tasks = new ArrayList<>();
public final AtomicLong count = new AtomicLong(0);
public final Path buildDir;
public final String nameFormat;
private transient final Logger logger = LogManager.getLogger();
@ -72,11 +71,20 @@ public <T extends LauncherBuildTask> Optional<T> getTaskByClass(Class<T> taskCla
return tasks.stream().filter(taskClass::isInstance).map(taskClass::cast).findFirst();
}
public Optional<LauncherBuildTask> getTaskBefore(Predicate<LauncherBuildTask> pred) {
LauncherBuildTask last = null;
for(var e : tasks) {
if(pred.test(e)) {
return Optional.ofNullable(last);
}
last = e;
}
return Optional.empty();
}
public void build(Path target, boolean deleteTempFiles) throws IOException {
logger.info("Building launcher binary file");
count.set(0); // set jar number
Path thisPath = null;
boolean isNeedDelete = false;
long time_start = System.currentTimeMillis();
long time_this = time_start;
for (LauncherBuildTask task : tasks) {
@ -86,19 +94,17 @@ public void build(Path target, boolean deleteTempFiles) throws IOException {
long time_task_end = System.currentTimeMillis();
long time_task = time_task_end - time_this;
time_this = time_task_end;
if (isNeedDelete && deleteTempFiles && oldPath != thisPath) Files.deleteIfExists(oldPath);
isNeedDelete = task.allowDelete();
logger.info("Task {} processed from {} millis", task.getName(), time_task);
}
long time_end = System.currentTimeMillis();
if (isNeedDelete && deleteTempFiles) IOHelper.move(thisPath, target);
if (deleteTempFiles) IOHelper.move(thisPath, target);
else IOHelper.copy(thisPath, target);
IOHelper.deleteDir(buildDir, false);
logger.info("Build successful from {} millis", time_end - time_start);
}
public String nextName(String taskName) {
return nameFormat.formatted(taskName, count.getAndIncrement());
return nameFormat.formatted(taskName);
}
public Path nextPath(String taskName) {

View file

@ -45,6 +45,7 @@ public class BuildContext {
public final MainBuildTask task;
public final HashSet<String> fileList;
public final HashSet<String> clientModules;
public final HashSet<String> legacyClientModules;
public BuildContext(ZipOutputStream output, List<JarFile> readerClassPath, MainBuildTask task) {
this.output = output;
@ -52,6 +53,7 @@ public BuildContext(ZipOutputStream output, List<JarFile> readerClassPath, MainB
this.task = task;
fileList = new HashSet<>(1024);
clientModules = new HashSet<>();
legacyClientModules = new HashSet<>();
}
public void pushFile(String filename, InputStream inputStream) throws IOException {

View file

@ -7,7 +7,7 @@ public final class EXEL4JLauncherBinary extends LauncherBinary {
public EXEL4JLauncherBinary(LaunchServer server) {
super(server, LauncherBinary.resolve(server, ".exe"), "Launcher-%s-%d.exe");
super(server, LauncherBinary.resolve(server, ".exe"), "Launcher-%s.exe");
}
@Override

View file

@ -9,7 +9,7 @@
public class EXELauncherBinary extends LauncherBinary {
public EXELauncherBinary(LaunchServer server) {
super(server, LauncherBinary.resolve(server, ".exe"), "Launcher-%s-%d.exe");
super(server, LauncherBinary.resolve(server, ".exe"), "Launcher-%s.exe");
}
@Override

View file

@ -23,7 +23,7 @@ public final class JARLauncherBinary extends LauncherBinary {
public final Map<String, Path> files;
public JARLauncherBinary(LaunchServer server) throws IOException {
super(server, resolve(server, ".jar"), "Launcher-%s-%d.jar");
super(server, resolve(server, ".jar"), "Launcher-%s.jar");
count = new AtomicLong(0);
runtimeDir = server.dir.resolve(Launcher.RUNTIME_DIR);
buildDir = server.dir.resolve("build");

View file

@ -69,7 +69,6 @@ public SignerJar(ZipOutputStream out, Supplier<CMSSignedDataGenerator> gen, Stri
*
* @param filename name of the file to add (use forward slash as a path separator)
* @param contents contents of the file
* @throws IOException
* @throws NullPointerException if any of the arguments is {@code null}
*/
public void addFileContents(String filename, byte[] contents) throws IOException {
@ -82,7 +81,6 @@ public void addFileContents(String filename, byte[] contents) throws IOException
*
* @param filename name of the file to add (use forward slash as a path separator)
* @param contents contents of the file
* @throws IOException
* @throws NullPointerException if any of the arguments is {@code null}
*/
public void addFileContents(String filename, InputStream contents) throws IOException {
@ -95,7 +93,6 @@ public void addFileContents(String filename, InputStream contents) throws IOExce
*
* @param entry name of the file to add (use forward slash as a path separator)
* @param contents contents of the file
* @throws IOException
* @throws NullPointerException if any of the arguments is {@code null}
*/
public void addFileContents(ZipEntry entry, byte[] contents) throws IOException {
@ -108,7 +105,6 @@ public void addFileContents(ZipEntry entry, byte[] contents) throws IOException
*
* @param entry name of the file to add (use forward slash as a path separator)
* @param contents contents of the file
* @throws IOException
* @throws NullPointerException if any of the arguments is {@code null}
*/
public void addFileContents(ZipEntry entry, InputStream contents) throws IOException {
@ -134,7 +130,6 @@ public void addManifestAttribute(String name, String value) {
* Closes the JAR file by writing the manifest and signature data to it and finishing the ZIP entries. It closes the
* underlying stream.
*
* @throws IOException
* @throws RuntimeException if the signing goes wrong
*/
@Override
@ -148,7 +143,6 @@ public void close() throws IOException {
* Finishes the JAR file by writing the manifest and signature data to it and finishing the ZIP entries. It leaves the
* underlying stream open.
*
* @throws IOException
* @throws RuntimeException if the signing goes wrong
*/
public void finish() throws IOException {
@ -205,7 +199,6 @@ private byte[] signSigFile(byte[] sigContents) throws Exception {
* Writes the manifest to the JAR. It also calculates the digests that are required to be placed in the the signature
* file.
*
* @throws IOException
*/
private void writeManifest() throws IOException {
zos.putNextEntry(IOHelper.newZipEntry(MANIFEST_FN));
@ -268,7 +261,6 @@ private byte[] writeSigFile() throws IOException {
/**
* Signs the .SIG file and writes the signature (.RSA file) to the JAR.
*
* @throws IOException
* @throws RuntimeException if the signing failed
*/
private void writeSignature(byte[] sigFile) throws IOException {

View file

@ -74,9 +74,4 @@ public Path process(Path inputFile) throws IOException {
return out;
}
@Override
public boolean allowDelete() {
return true;
}
}

View file

@ -68,11 +68,6 @@ private boolean filter(String name) {
return exclusions.stream().anyMatch(name::startsWith);
}
@Override
public boolean allowDelete() {
return true;
}
public List<Path> getJars() {
return jars;
}

View file

@ -81,9 +81,4 @@ public Path process(Path inputFile) throws IOException {
}
return inputFile;
}
@Override
public boolean allowDelete() {
return false;
}
}

View file

@ -43,9 +43,4 @@ public Path process(Path inputFile) throws IOException {
}
return output;
}
@Override
public boolean allowDelete() {
return true;
}
}

View file

@ -7,6 +7,4 @@ public interface LauncherBuildTask {
String getName();
Path process(Path inputFile) throws IOException;
boolean allowDelete();
}

View file

@ -51,11 +51,12 @@ public String getName() {
@Override
public Path process(Path inputJar) throws IOException {
Path outputJar = server.launcherBinary.nextPath("main");
Path outputJar = server.launcherBinary.nextPath(this);
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(outputJar))) {
BuildContext context = new BuildContext(output, reader.getCp(), this);
initProps();
preBuildHook.hook(context);
properties.put("launcher.legacymodules", context.legacyClientModules.stream().map(e -> Type.getObjectType(e.replace('.', '/'))).collect(Collectors.toList()));
properties.put("launcher.modules", context.clientModules.stream().map(e -> Type.getObjectType(e.replace('.', '/'))).collect(Collectors.toList()));
postInitProps();
reader.getCp().add(new JarFile(inputJar.toFile()));
@ -160,11 +161,6 @@ public byte[] transformClass(byte[] bytes, String classname, BuildContext contex
return result;
}
@Override
public boolean allowDelete() {
return true;
}
@FunctionalInterface
public interface Transformer {
byte[] transform(byte[] input, String classname, BuildContext context);

View file

@ -53,11 +53,6 @@ public Path process(Path inputFile) throws IOException {
return result;
}
@Override
public boolean allowDelete() {
return false;
}
public void tryUnpack() throws IOException {
logger.info("Unpacking launcher native guard list and runtime");
UnpackHelper.unpackZipNoCheck("runtime.zip", server.launcherBinary.runtimeDir);

View file

@ -104,9 +104,4 @@ private void autoSign(Path inputFile, Path signedFile) throws IOException {
}
}
}
@Override
public boolean allowDelete() {
return true;
}
}

View file

@ -59,11 +59,6 @@ public Path process(Path inputFile) throws IOException {
return output;
}
@Override
public boolean allowDelete() {
return true;
}
private Path setConfig() {
Path path = server.launcherEXEBinary.nextPath(getName());
Config config = new Config();

View file

@ -3,11 +3,10 @@
import me.tongfei.progressbar.ProgressBar;
import me.tongfei.progressbar.ProgressBarBuilder;
import me.tongfei.progressbar.ProgressBarStyle;
import pro.gravit.launcher.AsyncDownloader;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.modern.Downloader;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.Downloader;
import pro.gravit.utils.command.CommandException;
import java.io.IOException;
@ -45,9 +44,9 @@ protected boolean showApplyDialog(String text) throws IOException {
return response.equals("y");
}
protected Downloader downloadWithProgressBar(String taskName, List<AsyncDownloader.SizedFile> list, String baseUrl, Path targetDir) throws Exception {
protected Downloader downloadWithProgressBar(String taskName, List<Downloader.SizedFile> list, String baseUrl, Path targetDir) throws Exception {
long total = 0;
for (AsyncDownloader.SizedFile file : list) {
for (Downloader.SizedFile file : list) {
total += file.size;
}
long totalFiles = list.size();

View file

@ -3,12 +3,11 @@
import com.google.gson.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.AsyncDownloader;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.modern.Downloader;
import pro.gravit.launchserver.HttpRequester;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.command.Command;
import pro.gravit.utils.Downloader;
import pro.gravit.utils.helper.IOHelper;
import java.io.Writer;
@ -85,7 +84,7 @@ public void invoke(String... args) throws Exception {
logger.info("Copy {} into {}", indexPath, targetPath);
Files.copy(indexPath, targetPath, StandardCopyOption.REPLACE_EXISTING);
}
List<AsyncDownloader.SizedFile> toDownload = new ArrayList<>(128);
List<Downloader.SizedFile> toDownload = new ArrayList<>(128);
for (var e : objects.entrySet()) {
var value = e.getValue().getAsJsonObject();
var hash = value.get("hash").getAsString();
@ -101,7 +100,7 @@ public void invoke(String... args) throws Exception {
continue;
}
}
toDownload.add(new AsyncDownloader.SizedFile(hash, path, size));
toDownload.add(new Downloader.SizedFile(hash, path, size));
}
logger.info("Download {} files", toDownload.size());
Downloader downloader = downloadWithProgressBar(dirName, toDownload, RESOURCES_DOWNLOAD_URL, assetDir);

View file

@ -14,7 +14,6 @@
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.UUID;

View file

@ -10,7 +10,6 @@
import pro.gravit.utils.helper.IOHelper;
import java.io.Writer;
import java.nio.file.Files;
public class MakeProfileCommand extends Command {
private transient final Logger logger = LogManager.getLogger();

View file

@ -27,7 +27,6 @@
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
public class SecurityCheckCommand extends Command {
private static final Logger logger = LogManager.getLogger();
@ -113,7 +112,7 @@ public void invoke(String... args) {
List<X509Certificate> certChain = Arrays.stream(certChainPlain).map(e -> (X509Certificate) e).toList();
X509Certificate cert = certChain.get(0);
cert.checkValidity();
if (certChain.size() <= 1) {
if (certChain.size() == 1) {
printCheckResult("sign", "certificate chain contains <2 element(recommend 2 and more)", false);
bad = true;
}

View file

@ -15,9 +15,7 @@
import proguard.ConfigurationParser;
import proguard.ProGuard;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -26,6 +24,9 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class ProGuardComponent extends Component implements AutoCloseable, Reconfigurable {
private static final Logger logger = LogManager.getLogger();
@ -36,6 +37,7 @@ public class ProGuardComponent extends Component implements AutoCloseable, Recon
public transient ProguardConf proguardConf;
private transient LaunchServer launchServer;
private transient ProGuardBuildTask buildTask;
private transient ProGuardMultiReleaseFixer fixerTask;
public static boolean checkFXJMods(Path path) {
if (!IOHelper.exists(path.resolve("javafx.base.jmod")))
@ -75,7 +77,9 @@ public void init(LaunchServer launchServer) {
this.launchServer = launchServer;
proguardConf = new ProguardConf(launchServer, this);
this.buildTask = new ProGuardBuildTask(launchServer, proguardConf, this);
this.fixerTask = new ProGuardMultiReleaseFixer(launchServer, this, "ProGuard.".concat(componentName));
launchServer.launcherBinary.addAfter((v) -> v.getName().startsWith(modeAfter), buildTask);
launchServer.launcherBinary.addAfter((v) -> v.getName().equals("ProGuard.".concat(componentName)), fixerTask);
}
@Override
@ -111,6 +115,62 @@ public void invoke(String... args) throws Exception {
return null;
}
public static class ProGuardMultiReleaseFixer implements LauncherBuildTask {
private final LaunchServer server;
private final ProGuardComponent component;
private final String proguardTaskName;
public ProGuardMultiReleaseFixer(LaunchServer server, ProGuardComponent component, String proguardTaskName) {
this.server = server;
this.component = component;
this.proguardTaskName = proguardTaskName;
}
@Override
public String getName() {
return "ProGuardMultiReleaseFixer.".concat(component.componentName);
}
@Override
public Path process(Path inputFile) throws IOException {
if (!component.enabled) {
return inputFile;
}
LauncherBuildTask task = server.launcherBinary.getTaskBefore((x) -> proguardTaskName.equals(x.getName())).get();
Path lastPath = server.launcherBinary.nextPath(task);
if(Files.notExists(lastPath)) {
logger.error("{} not exist. Multi-Release JAR fix not applied!", lastPath);
return inputFile;
}
Path outputPath = server.launcherBinary.nextPath(this);
try(ZipOutputStream output = new ZipOutputStream(new FileOutputStream(outputPath.toFile()))) {
try(ZipInputStream input = new ZipInputStream(new FileInputStream(inputFile.toFile()))) {
ZipEntry entry = input.getNextEntry();
while(entry != null) {
ZipEntry newEntry = new ZipEntry(entry.getName());
output.putNextEntry(newEntry);
input.transferTo(output);
entry = input.getNextEntry();
}
}
try(ZipInputStream input = new ZipInputStream(new FileInputStream(lastPath.toFile()))) {
ZipEntry entry = input.getNextEntry();
while(entry != null) {
if(!entry.getName().startsWith("META-INF/versions")) {
entry = input.getNextEntry();
continue;
}
ZipEntry newEntry = new ZipEntry(entry.getName());
output.putNextEntry(newEntry);
input.transferTo(output);
entry = input.getNextEntry();
}
}
}
return outputPath;
}
}
public static class ProGuardBuildTask implements LauncherBuildTask {
private final LaunchServer server;
private final ProGuardComponent component;
@ -157,11 +217,6 @@ public Path process(Path inputFile) throws IOException {
IOHelper.copy(inputFile, outputJar);
return outputJar;
}
@Override
public boolean allowDelete() {
return true;
}
}
public static class ProguardConf {

View file

@ -27,7 +27,7 @@
import static java.util.concurrent.TimeUnit.SECONDS;
public final class LaunchServerConfig {
private final static List<String> oldMirrorList = List.of("https://mirror.gravit.pro/5.2.x/", "https://mirror.gravit.pro/5.3.x/", "https://mirror.gravitlauncher.com/5.2.x/", "https://mirror.gravitlauncher.com/5.3.x/");
private final static List<String> oldMirrorList = List.of("https://mirror.gravit.pro/5.2.x/", "https://mirror.gravit.pro/5.3.x/", "https://mirror.gravitlauncher.com/5.2.x/", "https://mirror.gravitlauncher.com/5.3.x/", "https://mirror.gravitlauncher.com/5.4.x/");
private transient final Logger logger = LogManager.getLogger();
public String projectName;
public String[] mirrors;
@ -49,7 +49,7 @@ public final class LaunchServerConfig {
public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
LaunchServerConfig newConfig = new LaunchServerConfig();
newConfig.mirrors = new String[]{"https://mirror.gravitlauncher.com/5.4.x/", "https://gravit-launcher-mirror.storage.googleapis.com/"};
newConfig.mirrors = new String[]{"https://mirror.gravitlauncher.com/5.5.x/", "https://gravit-launcher-mirror.storage.googleapis.com/"};
newConfig.launch4j = new LaunchServerConfig.ExeConf();
newConfig.launch4j.enabled = false;
newConfig.launch4j.copyright = "© GravitLauncher Team";
@ -61,7 +61,7 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
newConfig.launch4j.txtProductVersion = "%s, build %d";
newConfig.launch4j.productName = "GravitLauncher";
newConfig.launch4j.productVer = newConfig.launch4j.fileVer;
newConfig.launch4j.maxVersion = "1.8.999";
newConfig.launch4j.maxVersion = "99.0.0";
newConfig.env = LauncherConfig.LauncherEnvironment.STD;
newConfig.startScript = JVMHelper.OS_TYPE.equals(JVMHelper.OS.MUSTDIE) ? "." + File.separator + "start.bat" : "." + File.separator + "start.sh";
newConfig.auth = new HashMap<>();
@ -171,8 +171,8 @@ public void verify() {
if (!updateMirror) {
for (int i = 0; i < mirrors.length; ++i) {
if (mirrors[i] != null && oldMirrorList.contains(mirrors[i])) {
logger.warn("Replace mirror '{}' to 'https://mirror.gravitlauncher.com/5.4.x/'. If you really need to use original url, use '-Dlaunchserver.config.disableUpdateMirror=true'", mirrors[i]);
mirrors[i] = "https://mirror.gravitlauncher.com/5.4.x/";
logger.warn("Replace mirror '{}' to 'https://mirror.gravitlauncher.com/5.5.x/'. If you really need to use original url, use '-Dlaunchserver.config.disableUpdateMirror=true'", mirrors[i]);
mirrors[i] = "https://mirror.gravitlauncher.com/5.5.x/";
}
}
}
@ -234,7 +234,7 @@ public static class ExeConf {
public boolean enabled;
public boolean setMaxVersion;
public String maxVersion;
public String minVersion = "1.8.0";
public String minVersion = "17.0.0";
public String supportURL = null;
public String downloadUrl = Launch4JTask.DOWNLOAD_URL;
public String productName;
@ -259,6 +259,7 @@ public static class JarSignerConf {
public String metaInfKeyName = "SIGNUMO.RSA";
public String metaInfSfName = "SIGNUMO.SF";
public String signAlgo = "SHA256WITHRSA";
public boolean checkCertificateExpired = true;
}
public static class NettyUpdatesBind {

View file

@ -49,9 +49,7 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
} else if (version.compareTo(ClientProfileVersions.MINECRAFT_1_18) <= 0) { // 1.13 - 1.16.5
jvmArgs.add("-XX:+UseG1GC");
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
} else { // 1.18+
//jvmArgs.add("-XX:+UseShenandoahGC");
//jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
} else {
}
// -----------
Optional<MakeProfileOptionForge> forge = findOption(options, MakeProfileOptionForge.class);

View file

@ -21,8 +21,10 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
public class SignHelper {
@ -46,6 +48,24 @@ public static KeyStore getStore(Path file, String storepass, String algo) throws
}
}
public static Instant getCertificateExpired(KeyStore keyStore, String keyAlias) throws KeyStoreException {
List<Certificate> certChain = new ArrayList<>(Arrays.asList(keyStore.getCertificateChain(keyAlias)));
Date date = null;
for(var e : certChain) {
if(e instanceof X509Certificate x509Certificate) {
if(x509Certificate.getNotAfter() == null) {
continue;
}
if(date == null || date.after(x509Certificate.getNotAfter())) {
date = x509Certificate.getNotAfter();
}
}
}
return date == null ? null : date.toInstant();
}
/**
* Creates the beast that can actually sign the data (for JKS, for other make it).
*/

View file

@ -4,7 +4,6 @@
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherTrustManager;
import pro.gravit.launcher.modules.LauncherModule;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.asm.InjectClassAcceptor;
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
@ -50,7 +49,11 @@ public void init() {
mainTask.preBuildHook.registerHook((buildContext) -> {
for (ModuleEntity e : launcherModules) {
if (e.propertyMap != null) buildContext.task.properties.putAll(e.propertyMap);
buildContext.clientModules.add(e.moduleMainClass);
if(e.modernModule) {
buildContext.clientModules.add(e.moduleMainClass);
} else {
buildContext.legacyClientModules.add(e.moduleMainClass);
}
buildContext.readerClassPath.add(new JarFile(e.path.toFile()));
}
});
@ -94,6 +97,7 @@ public static class ModuleEntity {
public String moduleMainClass;
public String moduleConfigClass;
public String moduleConfigName;
public boolean modernModule;
public Map<String, Object> propertyMap;
}
@ -104,7 +108,6 @@ private ModulesVisitor() {
}
@Override
@SuppressWarnings("unchecked")
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toFile().getName().endsWith(".jar"))
try (JarFile f = new JarFile(file.toFile())) {
@ -120,6 +123,8 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
entity.path = file;
entity.moduleMainClass = mainClass;
entity.moduleConfigClass = attributes.getValue("Module-Config-Class");
String requiredModernJava = attributes.getValue("Required-Modern-Java");
entity.modernModule = Boolean.parseBoolean(requiredModernJava);
if (entity.moduleConfigClass != null) {
entity.moduleConfigName = attributes.getValue("Module-Config-Name");
if (entity.moduleConfigName == null) {

View file

@ -115,7 +115,7 @@ public AuthReport auth(AuthResponse.AuthContext context, AuthRequest.AuthPasswor
context.client.sessionObject = session;
internalAuth(context.client, context.authType, context.pair, user.getUsername(), user.getUUID(), user.getPermissions(), true);
if (context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) {
return AuthReport.ofMinecraftAccessToken(user.getAccessToken(), session);
return AuthReport.ofMinecraftAccessToken(session.getMinecraftAccessToken(), session);
}
return AuthReport.ofMinecraftAccessToken(null, session);
}
@ -166,9 +166,9 @@ public CheckServerReport checkServer(Client client, String username, String serv
else return CheckServerReport.ofUser(user, getPlayerProfile(client.auth, user));
}
public boolean joinServer(Client client, String username, String accessToken, String serverID) throws IOException {
public boolean joinServer(Client client, String username, UUID uuid, String accessToken, String serverID) throws IOException {
if (client.auth == null) return false;
return client.auth.core.joinServer(client, username, accessToken, serverID);
return client.auth.core.joinServer(client, username, uuid, accessToken, serverID);
}
public PlayerProfile getPlayerProfile(Client client) {

View file

@ -13,6 +13,7 @@
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.mix.MixProvider;
import pro.gravit.launchserver.auth.password.PasswordVerifier;
import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.texture.TextureProvider;
@ -48,6 +49,7 @@ public void registerAdapters(GsonBuilder builder) {
builder.registerTypeAdapter(GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails.class, new UniversalJsonAdapter<>(GetAvailabilityAuthRequest.providers));
builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers));
builder.registerTypeAdapter(OptionalTrigger.class, new UniversalJsonAdapter<>(OptionalTrigger.providers));
builder.registerTypeAdapter(MixProvider.class, new UniversalJsonAdapter<>(MixProvider.providers));
modulesManager.invokeEvent(new PreGsonPhase(builder));
//ClientWebSocketService.appendTypeAdapters(builder);
}

View file

@ -3,20 +3,27 @@
import com.google.gson.JsonElement;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.HTTPRequest;
import pro.gravit.utils.HttpDownloader;
import pro.gravit.launcher.Launcher;
import pro.gravit.utils.helper.IOHelper;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class MirrorManager {
protected final ArrayList<Mirror> list = new ArrayList<>();
private transient final Logger logger = LogManager.getLogger();
private transient final HttpClient client = HttpClient.newBuilder().build();
private Mirror defaultMirror;
public void addMirror(String mirror) {
@ -58,7 +65,7 @@ public boolean downloadZip(Mirror mirror, Path path, String mask, Object... args
URL url = mirror.getURL(mask, args);
logger.debug("Try download {}", url.toString());
try {
HttpDownloader.downloadZip(url, path);
downloadZip(url, path);
} catch (IOException e) {
logger.error("Download {} failed({}: {})", url.toString(), e.getClass().getName(), e.getMessage());
return false;
@ -82,8 +89,12 @@ public JsonElement jsonRequest(Mirror mirror, JsonElement request, String method
if (!mirror.enabled) return null;
URL url = mirror.getURL(mask, args);
try {
return HTTPRequest.jsonRequest(request, method, url);
} catch (IOException e) {
var response = client.send(HttpRequest.newBuilder()
.method(method, request == null ? HttpRequest.BodyPublishers.noBody() : HttpRequest.BodyPublishers.ofString(Launcher.gsonManager.gson.toJson(request)))
.uri(url.toURI())
.build(), HttpResponse.BodyHandlers.ofString());
return Launcher.gsonManager.gson.fromJson(response.body(), JsonElement.class);
} catch (IOException | URISyntaxException | InterruptedException e) {
logger.error("JsonRequest {} failed({}: {})", url.toString(), e.getClass().getName(), e.getMessage());
return null;
}
@ -101,6 +112,22 @@ public JsonElement jsonRequest(JsonElement request, String method, String mask,
throw new IOException("Error jsonRequest. All mirrors return error");
}
private void downloadZip(URL url, Path dir) throws IOException {
try (ZipInputStream input = IOHelper.newZipInput(url)) {
Files.createDirectory(dir);
for (ZipEntry entry = input.getNextEntry(); entry != null; entry = input.getNextEntry()) {
if (entry.isDirectory()) {
Files.createDirectory(dir.resolve(IOHelper.toPath(entry.getName())));
continue;
}
// Unpack entry
String name = entry.getName();
logger.debug("Downloading file: '{}'", name);
IOHelper.transfer(input, dir.resolve(IOHelper.toPath(name)));
}
}
}
public static class Mirror {
final String baseUrl;
boolean enabled;

View file

@ -18,6 +18,8 @@
import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.WebSocketServerResponse;
import pro.gravit.launchserver.socket.response.auth.*;
import pro.gravit.launchserver.socket.response.cabinet.AssetUploadInfoResponse;
import pro.gravit.launchserver.socket.response.cabinet.GetAssetUploadInfoResponse;
import pro.gravit.launchserver.socket.response.management.FeaturesResponse;
import pro.gravit.launchserver.socket.response.management.GetPublicKeyResponse;
import pro.gravit.launchserver.socket.response.profile.BatchProfileByUsername;
@ -65,7 +67,6 @@ public static void registerResponses() {
providers.register("updateList", UpdateListResponse.class);
providers.register("setProfile", SetProfileResponse.class);
providers.register("update", UpdateResponse.class);
providers.register("restoreSession", RestoreSessionResponse.class);
providers.register("batchProfileByUsername", BatchProfileByUsername.class);
providers.register("profileByUsername", ProfileByUsername.class);
providers.register("profileByUUID", ProfileByUUIDResponse.class);
@ -82,6 +83,8 @@ public static void registerResponses() {
providers.register("additionalData", AdditionalDataResponse.class);
providers.register("clientProfileKey", FetchClientProfileKeyResponse.class);
providers.register("getPublicKey", GetPublicKeyResponse.class);
providers.register("getAssetUploadUrl", GetAssetUploadInfoResponse.class);
providers.register("assetUploadInfo", AssetUploadInfoResponse.class);
}
public static String getIPFromContext(ChannelHandlerContext ctx) {

View file

@ -20,14 +20,8 @@ public String getType() {
public void execute(ChannelHandlerContext ctx, Client client) {
List<GetAvailabilityAuthRequestEvent.AuthAvailability> list = new ArrayList<>();
for (AuthProviderPair pair : server.config.auth.values()) {
var rca = pair.isSupport(AuthSupportRemoteClientAccess.class);
if (rca != null) {
list.add(new GetAvailabilityAuthRequestEvent.AuthAvailability(pair.name, pair.displayName,
pair.visible, pair.core.getDetails(client), rca.getClientApiUrl(), rca.getClientApiFeatures()));
} else {
list.add(new GetAvailabilityAuthRequestEvent.AuthAvailability(pair.name, pair.displayName,
pair.visible, pair.core.getDetails(client)));
}
list.add(new GetAvailabilityAuthRequestEvent.AuthAvailability(pair.core.getDetails(client), pair.name, pair.displayName,
pair.visible, pair.getFeatures()));
}
sendResult(new GetAvailabilityAuthRequestEvent(list));
}

View file

@ -10,11 +10,14 @@
import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.utils.HookException;
import java.util.UUID;
public class JoinServerResponse extends SimpleResponse {
private transient final Logger logger = LogManager.getLogger();
public String serverID;
public String accessToken;
public String username;
public UUID uuid;
@Override
public String getType() {
@ -23,11 +26,11 @@ public String getType() {
@Override
public void execute(ChannelHandlerContext ctx, Client client) {
if (!client.isAuth || client.type != AuthResponse.ConnectTypes.CLIENT) {
if (!server.config.protectHandler.allowJoinServer(client)) {
sendError("Permissions denied");
return;
}
if (username == null || accessToken == null || serverID == null) {
if ((username == null && uuid == null) || accessToken == null || serverID == null) {
sendError("Invalid request");
return;
}
@ -35,13 +38,13 @@ public void execute(ChannelHandlerContext ctx, Client client) {
try {
server.authHookManager.joinServerHook.hook(this, client);
if (server.config.protectHandler instanceof JoinServerProtectHandler joinServerProtectHandler) {
success = joinServerProtectHandler.onJoinServer(serverID, username, client);
success = joinServerProtectHandler.onJoinServer(serverID, username, uuid, client);
if (!success) {
sendResult(new JoinServerRequestEvent(false));
return;
}
}
success = server.authManager.joinServer(client, username, accessToken, serverID);
success = server.authManager.joinServer(client, username, uuid, accessToken, serverID);
if (success) {
logger.debug("joinServer: {} accessToken: {} serverID: {}", username, accessToken, serverID);
}

View file

@ -1,24 +0,0 @@
package pro.gravit.launchserver.socket.response.auth;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import java.util.UUID;
public class RestoreSessionResponse extends SimpleResponse {
@LauncherNetworkAPI
public UUID session;
public boolean needUserInfo;
@Override
public String getType() {
return "restoreSession";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) {
sendError("Legacy session system removed");
}
}

View file

@ -0,0 +1,27 @@
package pro.gravit.launchserver.socket.response.cabinet;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportAssetUpload;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class AssetUploadInfoResponse extends SimpleResponse {
@Override
public String getType() {
return "assetUploadInfo";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if(!client.isAuth || client.auth == null || client.getUser() == null) {
sendError("Access denied");
return;
}
var support = client.auth.isSupport(AuthSupportAssetUpload.class);
if(support == null) {
sendError("Not supported");
return;
}
sendResult(support.getAssetUploadInfo(client.getUser()));
}
}

View file

@ -0,0 +1,29 @@
package pro.gravit.launchserver.socket.response.cabinet;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.GetAssetUploadUrlRequestEvent;
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportAssetUpload;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class GetAssetUploadInfoResponse extends SimpleResponse {
public String name;
@Override
public String getType() {
return "getAssetUploadUrl";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if(!client.isAuth || client.auth == null || client.getUser() == null) {
sendError("Access denied");
return;
}
var support = client.auth.isSupport(AuthSupportAssetUpload.class);
if(support == null) {
sendError("Not supported");
return;
}
sendResult(new GetAssetUploadUrlRequestEvent(support.getAssetUploadUrl(name, client.getUser()), support.getAssetUploadToken(name, client.getUser())));
}
}

View file

@ -50,9 +50,9 @@ public void execute(ChannelHandlerContext ctx, Client client) {
service.sendObjectAndClose(ctx, new LauncherRequestEvent(true, server.config.netty.launcherURL));
if (Arrays.equals(bytes, hash) && checkSecure(secureHash, secureSalt)) {
client.checkSign = true;
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherURL, createLauncherExtendedToken()));
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
} else {
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, createLauncherExtendedToken()));
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
}
} else if (launcher_type == 2) //EXE
{
@ -60,9 +60,9 @@ public void execute(ChannelHandlerContext ctx, Client client) {
if (hash == null) sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL));
if (Arrays.equals(bytes, hash) && checkSecure(secureHash, secureSalt)) {
client.checkSign = true;
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherEXEURL, createLauncherExtendedToken()));
sendResult(new LauncherRequestEvent(false, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
} else {
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, createLauncherExtendedToken()));
sendResultAndClose(new LauncherRequestEvent(true, server.config.netty.launcherEXEURL, createLauncherExtendedToken(), server.config.netty.security.launcherTokenExpire*1000));
}
} else sendError("Request launcher type error");
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" packages="pro.gravit.launchserver.config.log4j">
<Configuration status="INFO">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout

View file

@ -6,7 +6,6 @@
-dontshrink
-dontoptimize
-ignorewarnings
-target 8
-forceprocessing
-repackageclasses 'pro.gravit.launcher'
@ -17,7 +16,7 @@
-keeppackagenames com.mojang.**,net.minecraftforge.fml.**,cpw.mods.fml.**,com.google.gson.**,pro.gravit.repackage.**,org.fusesource.**, pro.gravit.launcher.api.**, pro.gravit.utils.**, pro.gravit.launcher.request.**, pro.gravit.launcher.events.**, pro.gravit.launcher.profiles.**
-keep class com.mojang.**,net.minecraftforge.fml.**,cpw.mods.fml.**,com.google.gson.**,pro.gravit.repackage.**,org.fusesource.**, pro.gravit.launcher.api.**, pro.gravit.utils.**, pro.gravit.launcher.request.**, pro.gravit.launcher.events.**, pro.gravit.launcher.profiles.** {
-keep class com.mojang.**,net.minecraftforge.fml.**,cpw.mods.fml.**,com.google.gson.**,pro.gravit.repackage.**,org.fusesource.**, pro.gravit.launcher.api.**, pro.gravit.utils.**, pro.gravit.launcher.request.**, pro.gravit.launcher.events.**, pro.gravit.launcher.profiles.**, pro.gravit.launcher.LauncherEngineWrapper {
*;
}

View file

@ -0,0 +1,25 @@
package pro.gravit.launchserver;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.Feature;
import java.util.List;
public class FeatureCollectionTest {
public static class TestClass1 implements TextInterface1 {
}
@Feature("test")
public interface TextInterface1 {
}
@Test
public void simpleTest() {
var set = AuthProviderPair.getFeatures(TestClass1.class);
Assertions.assertTrue(set.contains("test"));
}
}

View file

@ -13,8 +13,8 @@
version = "12"
modules = ['javafx.controls', 'javafx.fxml']
}
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
sourceCompatibility = '17'
targetCompatibility = '17'
configurations {
bundle
@ -49,6 +49,9 @@
dependencies {
pack project(':LauncherAPI')
pack project(':LauncherModernCore')
pack project(':LauncherClient')
pack project(':LauncherStart')
bundle group: 'com.github.oshi', name: 'oshi-core', version: rootProject['verOshiCore']
pack group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty']
}

View file

@ -7,10 +7,8 @@
import pro.gravit.launcher.console.GetPublicKeyCommand;
import pro.gravit.launcher.console.ModulesCommand;
import pro.gravit.launcher.console.SignDataCommand;
import pro.gravit.launcher.events.request.*;
import pro.gravit.launcher.gui.NoRuntimeProvider;
import pro.gravit.launcher.gui.RuntimeProvider;
import pro.gravit.launcher.managers.ClientGsonManager;
import pro.gravit.launcher.managers.ConsoleManager;
import pro.gravit.launcher.modules.events.OfflineModeEvent;
import pro.gravit.launcher.modules.events.PreConfigPhase;
@ -20,11 +18,6 @@
import pro.gravit.launcher.request.RequestException;
import pro.gravit.launcher.request.RequestService;
import pro.gravit.launcher.request.auth.*;
import pro.gravit.launcher.request.auth.details.AuthLoginOnlyDetails;
import pro.gravit.launcher.request.management.FeaturesRequest;
import pro.gravit.launcher.request.secure.GetSecureLevelInfoRequest;
import pro.gravit.launcher.request.secure.SecurityReportRequest;
import pro.gravit.launcher.request.update.LauncherRequest;
import pro.gravit.launcher.request.websockets.OfflineRequestService;
import pro.gravit.launcher.request.websockets.StdWebSocketService;
import pro.gravit.launcher.utils.NativeJVMHalt;
@ -32,31 +25,31 @@
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
public class LauncherEngine {
public static ClientLauncherProcess.ClientParams clientParams;
public static ClientModuleManager modulesManager;
public static ClientParams clientParams;
public static RuntimeModuleManager modulesManager;
public final boolean clientInstance;
// Instance
private final AtomicBoolean started = new AtomicBoolean(false);
public RuntimeProvider runtimeProvider;
public ECPublicKey publicKey;
public ECPrivateKey privateKey;
public Class<? extends RuntimeProvider> basicRuntimeProvider;
private LauncherEngine(boolean clientInstance) {
private LauncherEngine(boolean clientInstance, Class<? extends RuntimeProvider> basicRuntimeProvider) {
this.clientInstance = clientInstance;
this.basicRuntimeProvider = basicRuntimeProvider;
}
//JVMHelper.getCertificates
@ -101,19 +94,32 @@ public static void exitLauncher(int code) {
forceExit(code);
}
public static boolean contains(String[] array, String value) {
for(String s : array) {
if(s.equals(value)) {
return true;
}
}
return false;
}
public static void main(String... args) throws Throwable {
JVMHelper.checkStackTrace(LauncherEngine.class);
JVMHelper.checkStackTrace(LauncherEngineWrapper.class);
JVMHelper.verifySystemProperties(Launcher.class, true);
EnvHelper.checkDangerousParams();
//if(!LauncherAgent.isStarted()) throw new SecurityException("JavaAgent not set");
verifyNoAgent();
if(contains(args, "--log-output") && Launcher.getConfig().environment != LauncherConfig.LauncherEnvironment.PROD) {
LogHelper.addOutput(Paths.get("Launcher.log"));
}
LogHelper.printVersion("Launcher");
LogHelper.printLicense("Launcher");
LauncherEngine.checkClass(LauncherEngineWrapper.class);
LauncherEngine.checkClass(LauncherEngine.class);
LauncherEngine.checkClass(LauncherAgent.class);
LauncherEngine.checkClass(ClientLauncherEntryPoint.class);
LauncherEngine.modulesManager = new ClientModuleManager();
LauncherEngine.modulesManager.loadModule(new ClientLauncherCoreModule());
LauncherEngine.modulesManager = new RuntimeModuleManager();
LauncherEngine.modulesManager.loadModule(new RuntimeLauncherCoreModule());
LauncherConfig.initModules(LauncherEngine.modulesManager);
LauncherEngine.modulesManager.initModules(null);
// Start Launcher
@ -123,7 +129,7 @@ public static void main(String... args) throws Throwable {
Launcher.getConfig(); // init config
long startTime = System.currentTimeMillis();
try {
new LauncherEngine(false).start(args);
newInstance(false).start(args);
} catch (Exception e) {
LogHelper.error(e);
return;
@ -133,12 +139,12 @@ public static void main(String... args) throws Throwable {
LauncherEngine.exitLauncher(0);
}
public static void initGson(ClientModuleManager modulesManager) {
public static void initGson(RuntimeModuleManager modulesManager) {
AuthRequest.registerProviders();
GetAvailabilityAuthRequest.registerProviders();
OptionalAction.registerProviders();
OptionalTrigger.registerProviders();
Launcher.gsonManager = new ClientGsonManager(modulesManager);
Launcher.gsonManager = new RuntimeGsonManager(modulesManager);
Launcher.gsonManager.initGson();
}
@ -149,39 +155,18 @@ public static void verifyNoAgent() {
public static RequestService initOffline() {
OfflineRequestService service = new OfflineRequestService();
applyBasicOfflineProcessors(service);
ClientLauncherMethods.applyBasicOfflineProcessors(service);
OfflineModeEvent event = new OfflineModeEvent(service);
modulesManager.invokeEvent(event);
return event.service;
}
public static void applyBasicOfflineProcessors(OfflineRequestService service) {
service.registerRequestProcessor(LauncherRequest.class, (r) -> new LauncherRequestEvent(false, (String) null));
service.registerRequestProcessor(CheckServerRequest.class, (r) -> {
throw new RequestException("CheckServer disabled in offline mode");
});
service.registerRequestProcessor(GetAvailabilityAuthRequest.class, (r) -> {
List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> details = new ArrayList<>();
details.add(new AuthLoginOnlyDetails());
GetAvailabilityAuthRequestEvent.AuthAvailability authAvailability = new GetAvailabilityAuthRequestEvent.AuthAvailability("offline", "Offline Mode", true, details);
List<GetAvailabilityAuthRequestEvent.AuthAvailability> list = new ArrayList<>(1);
list.add(authAvailability);
return new GetAvailabilityAuthRequestEvent(list);
});
service.registerRequestProcessor(JoinServerRequest.class, (r) -> new JoinServerRequestEvent(false));
service.registerRequestProcessor(ExitRequest.class, (r) -> new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT));
service.registerRequestProcessor(SetProfileRequest.class, (r) -> new SetProfileRequestEvent(null));
service.registerRequestProcessor(FeaturesRequest.class, (r) -> new FeaturesRequestEvent());
service.registerRequestProcessor(GetSecureLevelInfoRequest.class, (r) -> new GetSecureLevelInfoRequestEvent(null, false));
service.registerRequestProcessor(SecurityReportRequest.class, (r) -> new SecurityReportRequestEvent(SecurityReportRequestEvent.ReportAction.NONE));
}
public static LauncherEngine clientInstance() {
return new LauncherEngine(true);
}
public static LauncherEngine newInstance(boolean clientInstance) {
return new LauncherEngine(clientInstance);
return new LauncherEngine(clientInstance, NoRuntimeProvider.class);
}
public static LauncherEngine newInstance(boolean clientInstance, Class<? extends RuntimeProvider> basicRuntimeProvider) {
return new LauncherEngine(clientInstance, basicRuntimeProvider);
}
public ECPublicKey getClientPublicKey() {
@ -219,7 +204,7 @@ public void start(String... args) throws Throwable {
ClientPreGuiPhase event = new ClientPreGuiPhase(null);
LauncherEngine.modulesManager.invokeEvent(event);
runtimeProvider = event.runtimeProvider;
if (runtimeProvider == null) runtimeProvider = new NoRuntimeProvider();
if (runtimeProvider == null) runtimeProvider = basicRuntimeProvider.getConstructor().newInstance();
runtimeProvider.init(clientInstance);
//runtimeProvider.preLoad();
if (!Request.isAvailable()) {
@ -248,8 +233,9 @@ public void start(String... args) throws Throwable {
}
};
}
service.registerEventHandler(new BasicLauncherEventHandler());
}
Request.startAutoRefresh();
Request.getRequestService().registerEventHandler(new BasicLauncherEventHandler());
Objects.requireNonNull(args, "args");
if (started.getAndSet(true))
throw new IllegalStateException("Launcher has been already started");

View file

@ -0,0 +1,8 @@
package pro.gravit.launcher;
@LauncherNetworkAPI
public class LauncherEngineWrapper {
public static void main(String[] args) throws Throwable {
LauncherEngine.main(args);
}
}

View file

@ -10,33 +10,8 @@
import java.util.Map;
public class NewLauncherSettings {
@LauncherNetworkAPI
public final transient List<HashedStoreEntry> lastHDirs = new ArrayList<>(16);
@LauncherNetworkAPI
public Map<String, UserSettings> userSettings = new HashMap<>();
@LauncherNetworkAPI
public List<String> features = new ArrayList<>();
@LauncherNetworkAPI
public String consoleUnlockKey;
public void putHDir(String name, Path path, HashedDir dir) {
String fullPath = path.toAbsolutePath().toString();
lastHDirs.removeIf((e) -> e.fullPath.equals(fullPath) && e.name.equals(name));
HashedStoreEntry e = new HashedStoreEntry(dir, name, fullPath);
e.needSave = true;
lastHDirs.add(e);
}
public static class HashedStoreEntry {
public final HashedDir hdir;
public final String name;
public final String fullPath;
public transient boolean needSave = false;
public HashedStoreEntry(HashedDir hdir, String name, String fullPath) {
this.hdir = hdir;
this.name = name;
this.fullPath = fullPath;
}
}
}

View file

@ -7,18 +7,14 @@
import pro.gravit.launcher.client.events.client.ClientProcessBuilderLaunchedEvent;
import pro.gravit.launcher.client.events.client.ClientProcessBuilderParamsWrittedEvent;
import pro.gravit.launcher.client.events.client.ClientProcessBuilderPreLaunchEvent;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.hasher.HashedDir;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.profiles.ClientProfileVersions;
import pro.gravit.launcher.profiles.PlayerProfile;
import pro.gravit.launcher.profiles.optional.OptionalView;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClientArgs;
import pro.gravit.launcher.profiles.optional.actions.OptionalActionJvmArgs;
import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.serialize.HOutput;
import pro.gravit.utils.Version;
import pro.gravit.utils.helper.*;
import java.io.File;
@ -238,120 +234,4 @@ public Process getProcess() {
return process;
}
public static class ClientParams {
public String assetDir;
public String clientDir;
public String resourcePackDir;
public String nativesDir;
// Client params
public PlayerProfile playerProfile;
public ClientProfile profile;
public String accessToken;
//==Minecraft params==
public boolean autoEnter;
public boolean fullScreen;
public int ram;
public int width;
public int height;
public Set<OptionalAction> actions = new HashSet<>();
//========
public UUID session;
public AuthRequestEvent.OAuthRequestEvent oauth;
public String authId;
public long oauthExpiredTime;
public Map<String, String> extendedTokens;
public boolean offlineMode;
public transient HashedDir assetHDir;
public transient HashedDir clientHDir;
public transient HashedDir javaHDir;
public void addClientArgs(Collection<String> args) {
if (profile.getVersion().compareTo(ClientProfileVersions.MINECRAFT_1_6_4) >= 0)
addModernClientArgs(args);
else
addClientLegacyArgs(args);
}
public void addClientLegacyArgs(Collection<String> args) {
args.add(playerProfile.username);
args.add(accessToken);
// Add args for tweaker
Collections.addAll(args, "--version", profile.getVersion().toString());
Collections.addAll(args, "--gameDir", clientDir);
Collections.addAll(args, "--assetsDir", assetDir);
}
private void addModernClientArgs(Collection<String> args) {
// Add version-dependent args
ClientProfile.Version version = profile.getVersion();
Collections.addAll(args, "--username", playerProfile.username);
if (version.compareTo(ClientProfileVersions.MINECRAFT_1_7_2) >= 0) {
Collections.addAll(args, "--uuid", Launcher.toHash(playerProfile.uuid));
Collections.addAll(args, "--accessToken", accessToken);
// Add 1.7.10+ args (user properties, asset index)
if (version.compareTo(ClientProfileVersions.MINECRAFT_1_7_10) >= 0) {
// Add user properties
Collections.addAll(args, "--userType", "mojang");
Collections.addAll(args, "--userProperties", "{}");
// Add asset index
Collections.addAll(args, "--assetIndex", profile.getAssetIndex());
}
} else
Collections.addAll(args, "--session", accessToken);
// Add version and dirs args
Collections.addAll(args, "--version", profile.getVersion().toString());
Collections.addAll(args, "--gameDir", clientDir);
Collections.addAll(args, "--assetsDir", assetDir);
Collections.addAll(args, "--resourcePackDir", resourcePackDir);
if (version.compareTo(ClientProfileVersions.MINECRAFT_1_9_4) >= 0)
Collections.addAll(args, "--versionType", "Launcher v" + Version.getVersion().getVersionString());
// Add server args
if (autoEnter) {
Collections.addAll(args, "--server", profile.getServerAddress());
Collections.addAll(args, "--port", Integer.toString(profile.getServerPort()));
}
for (OptionalAction a : actions) {
if (a instanceof OptionalActionClientArgs) {
args.addAll(((OptionalActionClientArgs) a).args);
}
}
// Add window size args
if (fullScreen)
Collections.addAll(args, "--fullscreen", Boolean.toString(true));
if (width > 0 && height > 0) {
Collections.addAll(args, "--width", Integer.toString(width));
Collections.addAll(args, "--height", Integer.toString(height));
}
}
}
}

View file

@ -0,0 +1,23 @@
package pro.gravit.launcher.client;
import com.google.gson.GsonBuilder;
import pro.gravit.launcher.managers.GsonManager;
import pro.gravit.launcher.modules.events.PreGsonPhase;
import pro.gravit.launcher.request.websockets.ClientWebSocketService;
import pro.gravit.utils.UniversalJsonAdapter;
public class RuntimeGsonManager extends GsonManager {
private final RuntimeModuleManager moduleManager;
public RuntimeGsonManager(RuntimeModuleManager moduleManager) {
this.moduleManager = moduleManager;
}
@Override
public void registerAdapters(GsonBuilder builder) {
super.registerAdapters(builder);
builder.registerTypeAdapter(UserSettings.class, new UniversalJsonAdapter<>(UserSettings.providers));
ClientWebSocketService.appendTypeAdapters(builder);
moduleManager.invokeEvent(new PreGsonPhase(builder));
}
}

View file

@ -0,0 +1,17 @@
package pro.gravit.launcher.client;
import pro.gravit.launcher.modules.LauncherInitContext;
import pro.gravit.launcher.modules.LauncherModule;
import pro.gravit.launcher.modules.LauncherModuleInfo;
import pro.gravit.utils.Version;
public class RuntimeLauncherCoreModule extends LauncherModule {
public RuntimeLauncherCoreModule() {
super(new LauncherModuleInfo("ClientLauncherCore", Version.getVersion()));
}
@Override
public void init(LauncherInitContext initContext) {
}
}

View file

@ -1,15 +0,0 @@
package pro.gravit.launcher.client.events.client;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.client.ClientLauncherProcess;
import pro.gravit.launcher.modules.events.InitPhase;
public class ClientProcessInitPhase extends InitPhase {
public final LauncherEngine clientInstance;
public final ClientLauncherProcess.ClientParams params;
public ClientProcessInitPhase(LauncherEngine clientInstance, ClientLauncherProcess.ClientParams params) {
this.clientInstance = clientInstance;
this.params = params;
}
}

View file

@ -1,15 +0,0 @@
package pro.gravit.launcher.client.events.client;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.client.ClientLauncherProcess;
import pro.gravit.launcher.modules.LauncherModule;
public class ClientProcessLaunchEvent extends LauncherModule.Event {
public final LauncherEngine clientInstance;
public final ClientLauncherProcess.ClientParams params;
public ClientProcessLaunchEvent(LauncherEngine clientInstance, ClientLauncherProcess.ClientParams params) {
this.clientInstance = clientInstance;
this.params = params;
}
}

View file

@ -1,15 +0,0 @@
package pro.gravit.launcher.client.events.client;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.client.ClientLauncherProcess;
import pro.gravit.launcher.modules.events.PostInitPhase;
public class ClientProcessReadyEvent extends PostInitPhase {
public final LauncherEngine clientInstance;
public final ClientLauncherProcess.ClientParams params;
public ClientProcessReadyEvent(LauncherEngine clientInstance, ClientLauncherProcess.ClientParams params) {
this.clientInstance = clientInstance;
this.params = params;
}
}

View file

@ -0,0 +1,114 @@
package pro.gravit.launcher.debug;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.api.AuthService;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.events.request.ProfilesRequestEvent;
import pro.gravit.launcher.gui.RuntimeProvider;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.update.ProfilesRequest;
import pro.gravit.utils.helper.LogHelper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;
public class ClientRuntimeProvider implements RuntimeProvider {
@Override
public void run(String[] args) {
ArrayList<String> newArgs = new ArrayList<>(Arrays.asList(args));
try {
String username = System.getProperty("launcher.runtime.username", null);
String uuid = System.getProperty("launcher.runtime.uuid", null);
String login = System.getProperty("launcher.runtime.login", username);
String password = System.getProperty("launcher.runtime.password", "Player");
String authId = System.getProperty("launcher.runtime.auth.authid", "std");
String accessToken = System.getProperty("launcher.runtime.auth.accesstoken", null);
String refreshToken = System.getProperty("launcher.runtime.auth.refreshtoken", null);
String minecraftAccessToken = System.getProperty("launcher.runtime.auth.minecraftaccesstoken", "DEBUG");
long expire = Long.parseLong(System.getProperty("launcher.runtime.auth.expire", "0"));
String profileUUID = System.getProperty("launcher.runtime.profileuuid", null);
String mainClass = System.getProperty("launcher.runtime.mainclass", null);
ClientPermissions permissions = new ClientPermissions();
if(mainClass == null) {
throw new NullPointerException("Add `-Dlauncher.runtime.mainclass=YOUR_MAIN_CLASS` to jvmArgs");
}
if(uuid == null) {
if(accessToken != null) {
Request.setOAuth(authId, new AuthRequestEvent.OAuthRequestEvent(accessToken, refreshToken, expire));
Request.RequestRestoreReport report = Request.restore(true, false);
permissions = report.userInfo.permissions;
username = report.userInfo.playerProfile.username;
uuid = report.userInfo.playerProfile.uuid.toString();
if(report.userInfo.accessToken != null) {
minecraftAccessToken = report.userInfo.accessToken;
}
} else {
AuthRequest request = new AuthRequest(login, password, authId, AuthRequest.ConnectTypes.API);
AuthRequestEvent event = request.request();
Request.setOAuth(authId, event.oauth);
if(event.accessToken != null) {
minecraftAccessToken = event.accessToken;
}
username = event.playerProfile.username;
uuid = event.playerProfile.uuid.toString();
}
}
if(profileUUID != null) {
UUID profileUuid = UUID.fromString(profileUUID);
ProfilesRequest profiles = new ProfilesRequest();
ProfilesRequestEvent event = profiles.request();
for(ClientProfile profile : event.profiles) {
if(profile.getUUID().equals(profileUuid)) {
AuthService.profile = profile;
}
}
}
if(username == null) {
username = "Player";
}
if(uuid == null) {
uuid = "a7899336-e61c-4e51-b480-0c815b18aed8";
}
replaceOrCreateArgument(newArgs, "--username", username);
replaceOrCreateArgument(newArgs, "--uuid", uuid);
replaceOrCreateArgument(newArgs, "--accessToken", minecraftAccessToken);
AuthService.uuid = UUID.fromString(uuid);
AuthService.username = username;
AuthService.permissions = permissions;
Class<?> mainClazz = Class.forName(mainClass);
mainClazz.getMethod("main", String[].class).invoke(null, (Object) newArgs.toArray(new String[0]));
} catch (Throwable e) {
LogHelper.error(e);
LauncherEngine.exitLauncher(-15);
}
}
public void replaceOrCreateArgument(ArrayList<String> args, String name, String value) {
int index = args.indexOf(name);
if(index < 0) {
args.add(name);
if(value != null) {
args.add(value);
}
return;
}
if(value != null) {
int valueIndex = index+1;
args.set(valueIndex, value);
}
}
@Override
public void preLoad() {
}
@Override
public void init(boolean clientInstance) {
}
}

View file

@ -1,10 +1,11 @@
package pro.gravit.launcher.debug;
import pro.gravit.launcher.ClientLauncherMethods;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherConfig;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.client.ClientLauncherCoreModule;
import pro.gravit.launcher.client.ClientModuleManager;
import pro.gravit.launcher.client.RuntimeLauncherCoreModule;
import pro.gravit.launcher.client.RuntimeModuleManager;
import pro.gravit.launcher.managers.ConsoleManager;
import pro.gravit.launcher.modules.LauncherModule;
import pro.gravit.launcher.modules.events.OfflineModeEvent;
@ -28,6 +29,7 @@ public class DebugMain {
public static String projectName = System.getProperty("launcherdebug.projectname", "Minecraft");
public static String unlockSecret = System.getProperty("launcherdebug.unlocksecret", "");
public static boolean offlineMode = Boolean.getBoolean("launcherdebug.offlinemode");
public static boolean disableAutoRefresh = Boolean.getBoolean("launcherdebug.disableautorefresh");
public static String[] moduleClasses = System.getProperty("launcherdebug.modules", "").split(",");
public static String[] moduleFiles = System.getProperty("launcherdebug.modulefiles", "").split(",");
public static LauncherConfig.LauncherEnvironment environment = LauncherConfig.LauncherEnvironment.valueOf(System.getProperty("launcherdebug.env", "STD"));
@ -42,8 +44,8 @@ public static void main(String[] args) throws Throwable {
config.unlockSecret = unlockSecret;
Launcher.setConfig(config);
Launcher.applyLauncherEnv(environment);
LauncherEngine.modulesManager = new ClientModuleManager();
LauncherEngine.modulesManager.loadModule(new ClientLauncherCoreModule());
LauncherEngine.modulesManager = new RuntimeModuleManager();
LauncherEngine.modulesManager.loadModule(new RuntimeLauncherCoreModule());
for (String moduleClassName : moduleClasses) {
if (moduleClassName.isEmpty()) continue;
LauncherEngine.modulesManager.loadModule(newModule(moduleClassName));
@ -59,7 +61,7 @@ public static void main(String[] args) throws Throwable {
RequestService service;
if (offlineMode) {
OfflineRequestService offlineRequestService = new OfflineRequestService();
LauncherEngine.applyBasicOfflineProcessors(offlineRequestService);
ClientLauncherMethods.applyBasicOfflineProcessors(offlineRequestService);
OfflineModeEvent event = new OfflineModeEvent(offlineRequestService);
LauncherEngine.modulesManager.invokeEvent(event);
service = event.service;
@ -67,9 +69,13 @@ public static void main(String[] args) throws Throwable {
service = StdWebSocketService.initWebSockets(webSocketURL).get();
}
Request.setRequestService(service);
if(!disableAutoRefresh) {
Request.startAutoRefresh();
}
LogHelper.debug("Initialization LauncherEngine");
LauncherEngine instance = LauncherEngine.newInstance(false);
LauncherEngine instance = LauncherEngine.newInstance(false, ClientRuntimeProvider.class);
instance.start(args);
LauncherEngine.exitLauncher(0);
}
@SuppressWarnings("unchecked")

View file

@ -5,7 +5,7 @@
public class NoRuntimeProvider implements RuntimeProvider {
@Override
public void run(String[] args) {
JOptionPane.showMessageDialog(null, "GUI часть лаунчера не найдена.\nС 5.1.0 вам необходимо самостоятельно установить модуль, отвечающий за GUI. Рантайм на JS более не поддерживается");
JOptionPane.showMessageDialog(null, "Модуль графического интерфейса лаунчера отсутствует");
}
@Override

View file

@ -44,48 +44,4 @@ public void setConfig(NewLauncherSettings config) {
public NewLauncherSettings getDefaultConfig() {
return new NewLauncherSettings();
}
public void loadHDirStore(Path storePath) throws IOException {
Files.createDirectories(storePath);
IOHelper.walk(storePath, new StoreFileVisitor(), false);
}
public void saveHDirStore(Path storeProjectPath) throws IOException {
Files.createDirectories(storeProjectPath);
for (NewLauncherSettings.HashedStoreEntry e : settings.lastHDirs) {
if (!e.needSave) continue;
Path file = storeProjectPath.resolve(e.name.concat(".bin"));
if (!Files.exists(file)) Files.createFile(file);
try (HOutput output = new HOutput(IOHelper.newOutput(file))) {
output.writeString(e.name, 128);
output.writeString(e.fullPath, 1024);
e.hdir.write(output);
}
}
}
public void loadHDirStore() throws IOException {
loadHDirStore(DirBridge.dirStore);
}
public void saveHDirStore() throws IOException {
saveHDirStore(DirBridge.dirProjectStore);
}
public static class StoreFileVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
try (HInput input = new HInput(IOHelper.newInput(file))) {
String dirName = input.readString(128);
String fullPath = input.readString(1024);
HashedDir dir = new HashedDir(input);
settings.lastHDirs.add(new NewLauncherSettings.HashedStoreEntry(dir, dirName, fullPath));
} catch (IOException e) {
LogHelper.error("Skip file %s exception: %s", file.toAbsolutePath().toString(), e.getMessage());
}
return super.visitFile(file, attrs);
}
}
}

View file

@ -1,6 +1,5 @@
package pro.gravit.launcher.utils;
import pro.gravit.launcher.AsyncDownloader;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.launcher.LauncherInject;
import pro.gravit.launcher.request.update.LauncherRequest;
@ -23,6 +22,8 @@
import java.util.Arrays;
import java.util.List;
import static pro.gravit.launcher.modern.Downloader.makeSSLSocketFactory;
public class LauncherUpdater {
@LauncherInject("launcher.certificatePinning")
private static boolean isCertificatePinning;
@ -49,7 +50,7 @@ public static Path prepareUpdate(URL url) throws Exception {
if (isCertificatePinning) {
HttpsURLConnection connection1 = (HttpsURLConnection) connection;
try {
connection1.setSSLSocketFactory(AsyncDownloader.makeSSLSocketFactory());
connection1.setSSLSocketFactory(makeSSLSocketFactory());
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | KeyManagementException e) {
throw new IOException(e);
}

View file

@ -6,13 +6,11 @@
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.JVMHelper;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
import java.io.IOException;
import java.net.URL;
import java.nio.file.NoSuchFileException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@ -22,23 +20,9 @@ public final class Launcher {
// Authlib constants
public static final String SKIN_URL_PROPERTY = "skinURL";
public static final String SKIN_DIGEST_PROPERTY = "skinDigest";
public static final String SKIN_METADATA_PROPERTY = "skinMetadata";
public static final String CLOAK_URL_PROPERTY = "cloakURL";
public static final String CLOAK_DIGEST_PROPERTY = "cloakDigest";
public static final String CLOAK_METADATA_PROPERTY = "cloakMetadata";
// Used to determine from clientside is launched from launcher
public static final AtomicBoolean LAUNCHED = new AtomicBoolean(false);
public static final int PROTOCOL_MAGIC_LEGACY = 0x724724_00 + 24;
public static final int PROTOCOL_MAGIC = 0xA205B064; // e = 2.718281828
public static final String RUNTIME_DIR = "runtime";
// Constants

View file

@ -5,6 +5,7 @@
import pro.gravit.launcher.serialize.HInput;
import pro.gravit.launcher.serialize.HOutput;
import pro.gravit.launcher.serialize.stream.StreamObject;
import pro.gravit.utils.helper.JVMHelper;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.SecurityHelper;
import pro.gravit.utils.helper.VerifyHelper;
@ -21,7 +22,7 @@
public final class LauncherConfig extends StreamObject {
@LauncherInject("launchercore.certificates")
private static final List<byte[]> secureConfigCertificates = null;
@LauncherInject("launcher.modules")
@LauncherInject("launcher.legacymodules")
private static final List<Class<?>> modulesClasses = null;
private static final MethodType VOID_TYPE = MethodType.methodType(void.class);
@LauncherInject("launcher.projectName")
@ -51,6 +52,11 @@ public final class LauncherConfig extends StreamObject {
@LauncherInject("runtimeconfig.buildNumber")
public long buildNumber;
private static class ModernModulesClass {
@LauncherInject("launcher.modules")
private static final List<Class<?>> modulesClasses = null;
}
@LauncherInjectionConstructor
public LauncherConfig(HInput input) throws IOException, InvalidKeySpecException {
@ -114,6 +120,9 @@ public LauncherConfig(String address, Map<String, byte[]> runtime, String projec
}
public static void initModules(LauncherModulesManager modulesManager) {
if(JVMHelper.JVM_VERSION >= 17) {
modulesClasses.addAll(ModernModulesClass.modulesClasses);
}
for (Class<?> clazz : modulesClasses)
try {
modulesManager.loadModule((LauncherModule) MethodHandles.publicLookup().findConstructor(clazz, VOID_TYPE).invokeWithArguments(Collections.emptyList()));

View file

@ -41,7 +41,13 @@ default String toJsonString() {
default void loadConfig(Gson gson, Path configPath) throws IOException {
if (generateConfigIfNotExists(configPath)) return;
try (BufferedReader reader = IOHelper.newReader(configPath)) {
setConfig(gson.fromJson(reader, getType()));
T value = gson.fromJson(reader, getType());
if(value == null) {
LogHelper.warning("Config %s is null", configPath);
resetConfig(configPath);
return;
}
setConfig(value);
} catch (Exception e) {
LogHelper.error(e);
resetConfig(configPath);

View file

@ -4,4 +4,6 @@ public interface ExtendedTokenRequestEvent {
String getExtendedTokenName();
String getExtendedToken();
long getExtendedTokenExpire();
}

View file

@ -0,0 +1,24 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
import java.util.Set;
public class AssetUploadInfoRequestEvent extends RequestEvent {
public Set<String> available;
public SlimSupportConf slimSupportConf;
public AssetUploadInfoRequestEvent(Set<String> available, SlimSupportConf slimSupportConf) {
this.available = available;
this.slimSupportConf = slimSupportConf;
}
@Override
public String getType() {
return "assetUploadInfo";
}
public enum SlimSupportConf {
UNSUPPORTED, USER, SERVER
}
}

View file

@ -0,0 +1,22 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
public class GetAssetUploadUrlRequestEvent extends RequestEvent {
public static final String FEATURE_NAME = "assetupload";
public String url;
public AuthRequestEvent.OAuthRequestEvent token;
public GetAssetUploadUrlRequestEvent() {
}
public GetAssetUploadUrlRequestEvent(String url, AuthRequestEvent.OAuthRequestEvent token) {
this.url = url;
this.token = token;
}
@Override
public String getType() {
return "getAssetUploadUrl";
}
}

View file

@ -5,6 +5,7 @@
import pro.gravit.utils.TypeSerializeInterface;
import java.util.List;
import java.util.Set;
public class GetAvailabilityAuthRequestEvent extends RequestEvent {
@LauncherNetworkAPI
@ -49,24 +50,14 @@ public static class AuthAvailability {
@LauncherNetworkAPI
public boolean visible;
@LauncherNetworkAPI
public String apiUrl;
@LauncherNetworkAPI
public List<String> apiFeatures;
public Set<String> features;
public AuthAvailability(String name, String displayName, boolean visible, List<AuthAvailabilityDetails> details) {
this.name = name;
this.displayName = displayName;
this.visible = visible;
this.details = details;
}
public AuthAvailability(String name, String displayName, boolean visible, List<AuthAvailabilityDetails> details, String apiUrl, List<String> apiFeatures) {
this.visible = visible;
public AuthAvailability(List<AuthAvailabilityDetails> details, String name, String displayName, boolean visible, Set<String> features) {
this.details = details;
this.name = name;
this.displayName = displayName;
this.apiUrl = apiUrl;
this.apiFeatures = apiFeatures;
this.visible = visible;
this.features = features;
}
}
}

View file

@ -1,19 +1,37 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.ExtendedTokenRequestEvent;
import pro.gravit.launcher.events.RequestEvent;
public class HardwareReportRequestEvent extends RequestEvent {
public class HardwareReportRequestEvent extends RequestEvent implements ExtendedTokenRequestEvent {
public String extendedToken;
public long expire;
public HardwareReportRequestEvent() {
}
public HardwareReportRequestEvent(String extendedToken) {
public HardwareReportRequestEvent(String extendedToken, long expire) {
this.extendedToken = extendedToken;
this.expire = expire;
}
@Override
public String getType() {
return "hardwareReport";
}
@Override
public String getExtendedTokenName() {
return "hardware";
}
@Override
public String getExtendedToken() {
return extendedToken;
}
@Override
public long getExtendedTokenExpire() {
return expire;
}
}

View file

@ -1,12 +1,13 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.events.ExtendedTokenRequestEvent;
import pro.gravit.launcher.events.RequestEvent;
import java.util.UUID;
public class LauncherRequestEvent extends RequestEvent {
public class LauncherRequestEvent extends RequestEvent implements ExtendedTokenRequestEvent {
public static final String LAUNCHER_EXTENDED_TOKEN_NAME = "launcher";
@SuppressWarnings("unused")
private static final UUID uuid = UUID.fromString("d54cc12a-4f59-4f23-9b10-f527fdd2e38f");
@ -19,6 +20,7 @@ public class LauncherRequestEvent extends RequestEvent {
@LauncherNetworkAPI
public boolean needUpdate;
public String launcherExtendedToken;
public long launcherExtendedTokenExpire;
public LauncherRequestEvent(boolean needUpdate, String url) {
this.needUpdate = needUpdate;
@ -30,10 +32,11 @@ public LauncherRequestEvent(boolean b, byte[] digest) {
this.digest = digest;
}
public LauncherRequestEvent(boolean needUpdate, String url, String launcherExtendedToken) {
public LauncherRequestEvent(boolean needUpdate, String url, String launcherExtendedToken, long expire) {
this.url = url;
this.needUpdate = needUpdate;
this.launcherExtendedToken = launcherExtendedToken;
this.launcherExtendedTokenExpire = expire;
}
public LauncherRequestEvent(byte[] binary, byte[] digest) { //Legacy support constructor
@ -45,4 +48,19 @@ public LauncherRequestEvent(byte[] binary, byte[] digest) { //Legacy support con
public String getType() {
return "launcher";
}
@Override
public String getExtendedTokenName() {
return "launcher";
}
@Override
public String getExtendedToken() {
return launcherExtendedToken;
}
@Override
public long getExtendedTokenExpire() {
return launcherExtendedTokenExpire;
}
}

View file

@ -31,6 +31,7 @@ public enum ReportAction {
LOGOUT,
TOKEN_EXPIRED,
EXIT,
@Deprecated
CRASH,
OTHER
}

View file

@ -1,12 +1,13 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.ExtendedTokenRequestEvent;
import pro.gravit.launcher.events.RequestEvent;
public class VerifySecureLevelKeyRequestEvent extends RequestEvent {
public class VerifySecureLevelKeyRequestEvent extends RequestEvent implements ExtendedTokenRequestEvent {
public boolean needHardwareInfo;
public boolean onlyStatisticInfo;
public String extendedToken;
public String hardwareExtendedToken;
public long expire;
public VerifySecureLevelKeyRequestEvent() {
}
@ -15,21 +16,30 @@ public VerifySecureLevelKeyRequestEvent(boolean needHardwareInfo) {
this.needHardwareInfo = needHardwareInfo;
}
public VerifySecureLevelKeyRequestEvent(boolean needHardwareInfo, boolean onlyStatisticInfo, String extendedToken) {
public VerifySecureLevelKeyRequestEvent(boolean needHardwareInfo, boolean onlyStatisticInfo, String extendedToken, long expire) {
this.needHardwareInfo = needHardwareInfo;
this.onlyStatisticInfo = onlyStatisticInfo;
this.extendedToken = extendedToken;
}
public VerifySecureLevelKeyRequestEvent(boolean needHardwareInfo, boolean onlyStatisticInfo, String extendedToken, String hardwareExtendedToken) {
this.needHardwareInfo = needHardwareInfo;
this.onlyStatisticInfo = onlyStatisticInfo;
this.extendedToken = extendedToken;
this.hardwareExtendedToken = hardwareExtendedToken;
this.expire = expire;
}
@Override
public String getType() {
return "verifySecureLevelKey";
}
@Override
public String getExtendedTokenName() {
return "publicKey";
}
@Override
public String getExtendedToken() {
return extendedToken;
}
@Override
public long getExtendedTokenExpire() {
return expire;
}
}

View file

@ -16,4 +16,5 @@ private ClientProfileVersions() {
public static final ClientProfile.Version MINECRAFT_1_18 = ClientProfile.Version.of("1.18");
public static final ClientProfile.Version MINECRAFT_1_19 = ClientProfile.Version.of("1.19");
public static final ClientProfile.Version MINECRAFT_1_20 = ClientProfile.Version.of("1.20");
public static final ClientProfile.Version MINECRAFT_1_20_2 = ClientProfile.Version.of("1.20.2");
}

View file

@ -38,6 +38,9 @@ public class OptionalFile {
@LauncherNetworkAPI
public boolean limited;
@LauncherNetworkAPI
public String category;
@Override
public boolean equals(Object o) {
if (this == o) return true;

View file

@ -2,6 +2,7 @@
import pro.gravit.launcher.LauncherNetworkAPI;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.events.request.CurrentUserRequestEvent;
import pro.gravit.launcher.events.request.RefreshTokenRequestEvent;
import pro.gravit.launcher.events.request.RestoreRequestEvent;
import pro.gravit.launcher.request.auth.RefreshTokenRequest;
@ -11,32 +12,55 @@
import pro.gravit.utils.helper.LogHelper;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
public abstract class Request<R extends WebSocketEvent> implements WebSocketRequest {
private static final List<ExtendedTokenCallback> extendedTokenCallbacks = new ArrayList<>(4);
private static final List<BiConsumer<String, AuthRequestEvent.OAuthRequestEvent>> oauthChangeHandlers = new ArrayList<>(4);
@Deprecated
public static StdWebSocketService service;
private static RequestService requestService;
private static AuthRequestEvent.OAuthRequestEvent oauth;
private static Map<String, String> extendedTokens;
private static String authId;
private static long tokenExpiredTime;
private static volatile RequestService requestService;
private static volatile AuthRequestEvent.OAuthRequestEvent oauth;
private static volatile Map<String, ExtendedToken> extendedTokens;
private static volatile String authId;
private static volatile long tokenExpiredTime;
private static volatile ScheduledExecutorService executorService;
private static volatile boolean autoRefreshRunning;
@LauncherNetworkAPI
public final UUID requestUUID = UUID.randomUUID();
private transient final AtomicBoolean started = new AtomicBoolean(false);
public static synchronized void startAutoRefresh() {
if(!autoRefreshRunning) {
if(executorService == null) {
executorService = Executors.newSingleThreadScheduledExecutor((t) -> {
Thread thread = new Thread(t);
thread.setName("AutoRefresh thread");
thread.setDaemon(true);
return thread;
});
}
executorService.scheduleAtFixedRate(() -> {
try {
restore(false, true);
} catch (Exception e) {
LogHelper.error(e);
}
}, 5, 5, TimeUnit.SECONDS);
autoRefreshRunning = true;
}
}
public static RequestService getRequestService() {
return requestService;
}
public static void setRequestService(RequestService service) {
requestService = service;
if (service instanceof StdWebSocketService) {
Request.service = (StdWebSocketService) service;
}
}
public static boolean isAvailable() {
@ -64,7 +88,7 @@ public static String getAuthId() {
return authId;
}
public static Map<String, String> getExtendedTokens() {
public static Map<String, ExtendedToken> getExtendedTokens() {
if (extendedTokens != null) {
return Collections.unmodifiableMap(extendedTokens);
} else {
@ -72,22 +96,34 @@ public static Map<String, String> getExtendedTokens() {
}
}
public static Map<String, String> getStringExtendedTokens() {
if(extendedTokens != null) {
Map<String, String> map = new HashMap<>();
for(Map.Entry<String, ExtendedToken> e : extendedTokens.entrySet()) {
map.put(e.getKey(), e.getValue().token);
}
return map;
} else {
return null;
}
}
public static void clearExtendedTokens() {
if (extendedTokens != null) {
extendedTokens.clear();
}
}
public static void addExtendedToken(String name, String token) {
public static void addExtendedToken(String name, ExtendedToken token) {
if (extendedTokens == null) {
extendedTokens = new HashMap<>();
extendedTokens = new ConcurrentHashMap<>();
}
extendedTokens.put(name, token);
}
public static void addAllExtendedToken(Map<String, String> map) {
public static void addAllExtendedToken(Map<String, ExtendedToken> map) {
if (extendedTokens == null) {
extendedTokens = new HashMap<>();
extendedTokens = new ConcurrentHashMap<>();
}
extendedTokens.putAll(map);
}
@ -117,11 +153,32 @@ public static String getRefreshToken() {
}
public static void reconnect() throws Exception {
service.open();
getRequestService().open();
restore();
}
public static RequestRestoreReport restore() throws Exception {
return restore(false, false);
}
private synchronized static Map<String, String> getExpiredExtendedTokens() {
if(extendedTokens == null) {
return new HashMap<>();
}
Set<String> set = new HashSet<>();
for(Map.Entry<String, ExtendedToken> e : extendedTokens.entrySet()) {
if(e.getValue().expire != 0 && e.getValue().expire < System.currentTimeMillis()) {
set.add(e.getKey());
}
}
if(set.isEmpty()) {
return new HashMap<>();
}
return makeNewTokens(set);
}
public static synchronized RequestRestoreReport restore(boolean needUserInfo, boolean refreshOnly) throws Exception {
boolean refreshed = false;
RestoreRequest request;
if (oauth != null) {
@ -131,26 +188,18 @@ public static RequestRestoreReport restore() throws Exception {
setOAuth(authId, event.oauth);
refreshed = true;
}
request = new RestoreRequest(authId, oauth.accessToken, extendedTokens, false);
request = new RestoreRequest(authId, oauth.accessToken, refreshOnly ? getExpiredExtendedTokens() : getStringExtendedTokens(), needUserInfo);
} else {
request = new RestoreRequest(authId, null, extendedTokens, false);
request = new RestoreRequest(authId, null, refreshOnly ? getExpiredExtendedTokens() : getStringExtendedTokens(), false);
}
if(refreshOnly && (request.extended == null || request.extended.isEmpty())) {
return new RequestRestoreReport(refreshed, null, null);
}
RestoreRequestEvent event = request.request();
List<String> invalidTokens = null;
if (event.invalidTokens != null && event.invalidTokens.size() > 0) {
boolean needRequest = false;
Map<String, String> tokens = new HashMap<>();
for (ExtendedTokenCallback cb : extendedTokenCallbacks) {
for (String tokenName : event.invalidTokens) {
String newToken = cb.tryGetNewToken(tokenName);
if (newToken != null) {
needRequest = true;
tokens.put(tokenName, newToken);
addExtendedToken(tokenName, newToken);
}
}
}
if (needRequest) {
Map<String, String> tokens = makeNewTokens(event.invalidTokens);
if (!tokens.isEmpty()) {
request = new RestoreRequest(authId, null, tokens, false);
event = request.request();
if (event.invalidTokens != null && event.invalidTokens.size() > 0) {
@ -159,7 +208,21 @@ public static RequestRestoreReport restore() throws Exception {
}
invalidTokens = event.invalidTokens;
}
return new RequestRestoreReport(false, refreshed, invalidTokens);
return new RequestRestoreReport(refreshed, invalidTokens, event.userInfo);
}
private synchronized static Map<String, String> makeNewTokens(Collection<String> keys) {
Map<String, String> tokens = new HashMap<>();
for (ExtendedTokenCallback cb : extendedTokenCallbacks) {
for (String tokenName : keys) {
ExtendedToken newToken = cb.tryGetNewToken(tokenName);
if (newToken != null) {
tokens.put(tokenName, newToken.token);
addExtendedToken(tokenName, newToken);
}
}
}
return tokens;
}
public static void requestError(String message) throws RequestException {
@ -209,18 +272,29 @@ protected R requestDo(RequestService service) throws Exception {
}
public interface ExtendedTokenCallback {
String tryGetNewToken(String name);
ExtendedToken tryGetNewToken(String name);
}
public static class RequestRestoreReport {
public final boolean legacySession;
public final boolean refreshed;
public final List<String> invalidExtendedTokens;
public final CurrentUserRequestEvent.UserInfo userInfo;
public RequestRestoreReport(boolean legacySession, boolean refreshed, List<String> invalidExtendedTokens) {
this.legacySession = legacySession;
public RequestRestoreReport(boolean refreshed, List<String> invalidExtendedTokens, CurrentUserRequestEvent.UserInfo userInfo) {
this.refreshed = refreshed;
this.invalidExtendedTokens = invalidExtendedTokens;
this.userInfo = userInfo;
}
}
public static class ExtendedToken {
public final String token;
public final long expire;
public ExtendedToken(String token, long expire) {
this.token = token;
long time = System.currentTimeMillis();
this.expire = expire < time/2 ? time+expire : expire;
}
}

View file

@ -6,6 +6,7 @@
public interface RequestService {
<T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request) throws IOException;
void open() throws Exception;
void registerEventHandler(EventHandler handler);

View file

@ -6,12 +6,16 @@
import pro.gravit.launcher.request.websockets.WebSocketRequest;
import pro.gravit.utils.helper.VerifyHelper;
import java.util.UUID;
public final class JoinServerRequest extends Request<JoinServerRequestEvent> implements WebSocketRequest {
// Instance
@LauncherNetworkAPI
public final String username;
@LauncherNetworkAPI
public final UUID uuid;
@LauncherNetworkAPI
public final String accessToken;
@LauncherNetworkAPI
public final String serverID;
@ -19,10 +23,18 @@ public final class JoinServerRequest extends Request<JoinServerRequestEvent> imp
public JoinServerRequest(String username, String accessToken, String serverID) {
this.username = username;
this.uuid = null;
this.accessToken = accessToken;
this.serverID = VerifyHelper.verifyServerID(serverID);
}
public JoinServerRequest(UUID uuid, String accessToken, String serverID) {
this.username = null;
this.uuid = uuid;
this.accessToken = accessToken;
this.serverID = serverID;
}
@Override
public String getType() {
return "joinServer";

View file

@ -0,0 +1,11 @@
package pro.gravit.launcher.request.cabinet;
import pro.gravit.launcher.events.request.AssetUploadInfoRequestEvent;
import pro.gravit.launcher.request.Request;
public class AssetUploadInfoRequest extends Request<AssetUploadInfoRequestEvent> {
@Override
public String getType() {
return "assetUploadInfo";
}
}

View file

@ -0,0 +1,20 @@
package pro.gravit.launcher.request.cabinet;
import pro.gravit.launcher.events.request.GetAssetUploadUrlRequestEvent;
import pro.gravit.launcher.request.Request;
public class GetAssetUploadUrl extends Request<GetAssetUploadUrlRequestEvent> {
public String name;
public GetAssetUploadUrl() {
}
public GetAssetUploadUrl(String name) {
this.name = name;
}
@Override
public String getType() {
return "getAssetUploadUrl";
}
}

View file

@ -111,6 +111,8 @@ public void registerResults() {
results.register("additionalData", AdditionalDataRequestEvent.class);
results.register("clientProfileKey", FetchClientProfileKeyRequestEvent.class);
results.register("getPublicKey", GetPublicKeyRequestEvent.class);
results.register("getAssetUploadUrl", GetAssetUploadUrlRequestEvent.class);
results.register("assetUploadInfo", AssetUploadInfoRequestEvent.class);
resultsRegistered = true;
}
}

View file

@ -44,6 +44,11 @@ public <T extends WebSocketEvent> CompletableFuture<T> request(Request<T> reques
return future;
}
@Override
public void open() {
}
@Override
public void registerEventHandler(EventHandler handler) {
eventHandlers.add(handler);

Some files were not shown because too many files have changed in this diff Show more