mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-06-28 12:08:09 +03:00
[FEATURE][EXPERIMENTAL] Merge experimental/5.7.x
This commit is contained in:
commit
6110a29f5c
73 changed files with 2161 additions and 68 deletions
|
@ -152,12 +152,8 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
|
|||
}
|
||||
}
|
||||
}
|
||||
builder.setMinJavaVersion(17);
|
||||
builder.setRecommendJavaVersion(17);
|
||||
if(version.compareTo(ClientProfileVersions.MINECRAFT_1_20_3) >= 0) {
|
||||
builder.setMinJavaVersion(21);
|
||||
builder.setRecommendJavaVersion(21);
|
||||
}
|
||||
builder.setMinJavaVersion(21);
|
||||
builder.setRecommendJavaVersion(21);
|
||||
jvmArgs.add("-Dfml.ignorePatchDiscrepancies=true");
|
||||
jvmArgs.add("-Dfml.ignoreInvalidMinecraftCertificates=true");
|
||||
builder.setJvmArgs(jvmArgs);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"features": [],
|
||||
"features": ["new-api"],
|
||||
"info": []
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'com.gradleup.shadow'
|
||||
|
||||
String mainClassName = "pro.gravit.launcher.start.ClientLauncherWrapper"
|
||||
|
||||
|
@ -7,8 +7,8 @@
|
|||
url "https://repo.spring.io/plugins-release/"
|
||||
}
|
||||
}
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
sourceCompatibility = '21'
|
||||
targetCompatibility = '21'
|
||||
|
||||
configurations {
|
||||
bundle
|
||||
|
|
|
@ -2,7 +2,15 @@
|
|||
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.base.LauncherConfig;
|
||||
import pro.gravit.launcher.base.request.*;
|
||||
import pro.gravit.launcher.client.*;
|
||||
import pro.gravit.launcher.core.api.LauncherAPI;
|
||||
import pro.gravit.launcher.core.api.LauncherAPIHolder;
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.UserFeatureAPI;
|
||||
import pro.gravit.launcher.core.backend.LauncherBackendAPIHolder;
|
||||
import pro.gravit.launcher.runtime.backend.LauncherBackendImpl;
|
||||
import pro.gravit.launcher.runtime.client.*;
|
||||
import pro.gravit.launcher.runtime.client.events.ClientEngineInitPhase;
|
||||
import pro.gravit.launcher.client.events.ClientExitPhase;
|
||||
|
@ -19,9 +27,6 @@
|
|||
import pro.gravit.launcher.base.modules.events.PreConfigPhase;
|
||||
import pro.gravit.launcher.base.profiles.optional.actions.OptionalAction;
|
||||
import pro.gravit.launcher.base.profiles.optional.triggers.OptionalTrigger;
|
||||
import pro.gravit.launcher.base.request.Request;
|
||||
import pro.gravit.launcher.base.request.RequestException;
|
||||
import pro.gravit.launcher.base.request.RequestService;
|
||||
import pro.gravit.launcher.base.request.auth.*;
|
||||
import pro.gravit.launcher.base.request.websockets.OfflineRequestService;
|
||||
import pro.gravit.launcher.base.request.websockets.StdWebSocketService;
|
||||
|
@ -38,6 +43,7 @@
|
|||
import java.security.interfaces.ECPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
|
@ -243,6 +249,17 @@ public void start(String... args) throws Throwable {
|
|||
}
|
||||
Request.startAutoRefresh();
|
||||
Request.getRequestService().registerEventHandler(new BasicLauncherEventHandler());
|
||||
// Init New API
|
||||
LauncherAPIHolder.setCoreAPI(new RequestCoreFeatureAPIImpl(Request.getRequestService()));
|
||||
LauncherAPIHolder.setCreateApiFactory((authId) -> {
|
||||
var impl = new RequestFeatureAPIImpl(Request.getRequestService(), authId);
|
||||
return new LauncherAPI(Map.of(
|
||||
AuthFeatureAPI.class, impl,
|
||||
UserFeatureAPI.class, impl,
|
||||
ProfileFeatureAPI.class, impl));
|
||||
});
|
||||
LauncherBackendAPIHolder.setApi(new LauncherBackendImpl());
|
||||
//
|
||||
Objects.requireNonNull(args, "args");
|
||||
if (started.getAndSet(true))
|
||||
throw new IllegalStateException("Launcher has been already started");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package pro.gravit.launcher.runtime;
|
||||
|
||||
import pro.gravit.launcher.runtime.client.UserSettings;
|
||||
import pro.gravit.launcher.core.backend.UserSettings;
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package pro.gravit.launcher.runtime.backend;
|
||||
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.backend.UserSettings;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BackendSettings extends UserSettings {
|
||||
@LauncherNetworkAPI
|
||||
public AuthorizationData auth;
|
||||
@LauncherNetworkAPI
|
||||
public Map<UUID, ProfileSettingsImpl> settings = new HashMap<>();
|
||||
public static class AuthorizationData {
|
||||
@LauncherNetworkAPI
|
||||
public String accessToken;
|
||||
@LauncherNetworkAPI
|
||||
public String refreshToken;
|
||||
@LauncherNetworkAPI
|
||||
public long expireIn;
|
||||
|
||||
public AuthFeatureAPI.AuthToken toToken() {
|
||||
return new AuthFeatureAPI.AuthToken() {
|
||||
@Override
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getExpire() {
|
||||
return expireIn;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
package pro.gravit.launcher.runtime.backend;
|
||||
|
||||
import pro.gravit.launcher.base.Downloader;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.base.profiles.optional.OptionalView;
|
||||
import pro.gravit.launcher.base.profiles.optional.actions.OptionalAction;
|
||||
import pro.gravit.launcher.base.profiles.optional.actions.OptionalActionFile;
|
||||
import pro.gravit.launcher.core.api.LauncherAPIHolder;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.backend.LauncherBackendAPI;
|
||||
import pro.gravit.launcher.core.hasher.FileNameMatcher;
|
||||
import pro.gravit.launcher.core.hasher.HashedDir;
|
||||
import pro.gravit.launcher.core.hasher.HashedEntry;
|
||||
import pro.gravit.launcher.core.hasher.HashedFile;
|
||||
import pro.gravit.launcher.runtime.client.DirBridge;
|
||||
import pro.gravit.launcher.runtime.utils.AssetIndexHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ClientDownloadImpl {
|
||||
private LauncherBackendImpl backend;
|
||||
|
||||
ClientDownloadImpl(LauncherBackendImpl backend) {
|
||||
this.backend = backend;
|
||||
}
|
||||
|
||||
CompletableFuture<LauncherBackendAPI.ReadyProfile> downloadProfile(ClientProfile profile, ProfileSettingsImpl settings, LauncherBackendAPI.DownloadCallback callback) {
|
||||
AtomicReference<DownloadedDir> clientRef = new AtomicReference<>();
|
||||
AtomicReference<DownloadedDir> assetRef = new AtomicReference<>();
|
||||
AtomicReference<DownloadedDir> javaRef = new AtomicReference<>();
|
||||
return LauncherAPIHolder.profile().changeCurrentProfile(profile)
|
||||
.thenCompose(vv -> downloadDir(profile.getDir(), profile.getClientUpdateMatcher(), settings.view, callback)).thenCompose((clientDir -> {
|
||||
clientRef.set(clientDir);
|
||||
return downloadAsset(profile.getAssetDir(), profile.getAssetUpdateMatcher(), profile.getAssetIndex(), callback);
|
||||
})).thenCompose(assetDir -> {
|
||||
assetRef.set(assetDir);
|
||||
return CompletableFuture.completedFuture((DownloadedDir)null); // TODO Custom Java
|
||||
}).thenCompose(javaDir -> {
|
||||
javaRef.set(javaDir);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}).thenApply(v -> {
|
||||
return new ReadyProfileImpl(backend, profile, settings, clientRef.get(), assetRef.get(), javaRef.get());
|
||||
});
|
||||
}
|
||||
|
||||
CompletableFuture<DownloadedDir> downloadAsset(String dirName, FileNameMatcher matcher, String assetIndexString, LauncherBackendAPI.DownloadCallback callback) {
|
||||
Path targetDir = DirBridge.dirUpdates.resolve(dirName);
|
||||
Path assetIndexPath = targetDir.resolve("indexes").resolve(assetIndexString+".json");
|
||||
return LauncherAPIHolder.profile().fetchUpdateInfo(dirName).thenComposeAsync((response) -> {
|
||||
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_ASSET_VERIFY);
|
||||
return verifyAssetIndex(assetIndexString, response, assetIndexPath, targetDir);
|
||||
}, backend.executorService)
|
||||
.thenApply(assetData -> {
|
||||
HashedDir dir = assetData.updateInfo.getHashedDir();
|
||||
AssetIndexHelper.modifyHashedDir(assetData.index, dir);
|
||||
return new VirtualUpdateInfo(dir, assetData.updateInfo.getUrl());
|
||||
})
|
||||
.thenCompose(response -> downloadDir(targetDir, response, matcher, callback, e -> e));
|
||||
}
|
||||
|
||||
private CompletableFuture<AssetData> verifyAssetIndex(String assetIndexString, ProfileFeatureAPI.UpdateInfo response, Path assetIndexPath, Path targetDir) {
|
||||
var assetIndexRelPath = String.format("indexes/%s.json", assetIndexString);
|
||||
var assetIndexHash = response.getHashedDir().findRecursive(assetIndexRelPath);
|
||||
if(!(assetIndexHash.entry instanceof HashedFile assetIndexHashFile)) {
|
||||
return CompletableFuture.failedFuture(new FileNotFoundException(String.format("Asset Index %s not found in the server response", assetIndexString)));
|
||||
}
|
||||
try {
|
||||
if(Files.exists(assetIndexPath) && assetIndexHashFile.isSame(assetIndexPath, true)) {
|
||||
var assetIndex = AssetIndexHelper.parse(assetIndexPath);
|
||||
return CompletableFuture.completedFuture(new AssetData(response, assetIndex));
|
||||
} else {
|
||||
var downloader = Downloader.newDownloader(backend.executorService);
|
||||
var list = new LinkedList<Downloader.SizedFile>();
|
||||
list.add(new Downloader.SizedFile(assetIndexRelPath, assetIndexRelPath, assetIndexHashFile.size));
|
||||
return downloader.downloadFiles(list, response.getUrl(), targetDir, null, backend.executorService, 1).thenComposeAsync(v -> {
|
||||
try {
|
||||
var assetIndex = AssetIndexHelper.parse(assetIndexPath);
|
||||
return CompletableFuture.completedFuture(new AssetData(response, assetIndex));
|
||||
} catch (IOException e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
}, backend.executorService);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
}
|
||||
|
||||
CompletableFuture<DownloadedDir> downloadDir(String dirName, FileNameMatcher matcher, LauncherBackendAPI.DownloadCallback callback) {
|
||||
Path targetDir = DirBridge.dirUpdates.resolve(dirName);
|
||||
return LauncherAPIHolder.profile().fetchUpdateInfo(dirName)
|
||||
.thenCompose(response -> downloadDir(targetDir, response, matcher, callback, e -> e));
|
||||
}
|
||||
|
||||
CompletableFuture<DownloadedDir> downloadDir(String dirName, FileNameMatcher matcher, OptionalView view, LauncherBackendAPI.DownloadCallback callback) {
|
||||
Path targetDir = DirBridge.dirUpdates.resolve(dirName);
|
||||
return LauncherAPIHolder.profile().fetchUpdateInfo(dirName)
|
||||
.thenCompose(response -> {
|
||||
var hashedDir = response.getHashedDir();
|
||||
var remap = applyOptionalMods(view, hashedDir);
|
||||
return downloadDir(targetDir, new VirtualUpdateInfo(hashedDir, response.getUrl()), matcher, callback, makePathRemapperFunction(remap));
|
||||
});
|
||||
}
|
||||
|
||||
CompletableFuture<DownloadedDir> downloadDir(Path targetDir, ProfileFeatureAPI.UpdateInfo updateInfo, FileNameMatcher matcher, LauncherBackendAPI.DownloadCallback callback, Function<String, String> remap) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_HASHING);
|
||||
HashedDir realFiles = new HashedDir(targetDir, matcher, false, true);
|
||||
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_DIFF);
|
||||
return updateInfo.getHashedDir().diff(realFiles, matcher);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, backend.executorService).thenComposeAsync((diff) -> {
|
||||
return downloadFiles(targetDir, updateInfo, callback, diff, remap);
|
||||
}, backend.executorService).thenApply(v -> new DownloadedDir(updateInfo.getHashedDir(), targetDir));
|
||||
}
|
||||
|
||||
private CompletableFuture<HashedDir.Diff> downloadFiles(Path targetDir, ProfileFeatureAPI.UpdateInfo updateInfo, LauncherBackendAPI.DownloadCallback callback, HashedDir.Diff diff, Function<String, String> remap) {
|
||||
Downloader downloader = Downloader.newDownloader(backend.executorService);
|
||||
try {
|
||||
var files = collectFilesAndCreateDirectories(targetDir, diff.mismatch, remap);
|
||||
long total = 0;
|
||||
for(var e : files) {
|
||||
total += e.size;
|
||||
}
|
||||
callback.onTotalDownload(total);
|
||||
callback.onCanCancel(downloader::cancel);
|
||||
return downloader.downloadFiles(files, updateInfo.getUrl(), targetDir, new Downloader.DownloadCallback() {
|
||||
@Override
|
||||
public void apply(long fullDiff) {
|
||||
callback.onCurrentDownloaded(fullDiff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(Path path) {
|
||||
|
||||
}
|
||||
}, backend.executorService, 4).thenComposeAsync(v -> {
|
||||
callback.onCanCancel(null);
|
||||
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_DELETE_EXTRA);
|
||||
try {
|
||||
deleteExtraDir(targetDir, diff.extra, diff.extra.flag);
|
||||
} catch (IOException ex) {
|
||||
return CompletableFuture.failedFuture(ex);
|
||||
}
|
||||
callback.onStage(LauncherBackendAPI.DownloadCallback.STAGE_DONE_PART);
|
||||
return CompletableFuture.completedFuture(diff);
|
||||
}, backend.executorService);
|
||||
} catch (Exception e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Downloader.SizedFile> collectFilesAndCreateDirectories(Path dir, HashedDir mismatch, Function<String, String> pathRemapper) throws IOException {
|
||||
List<Downloader.SizedFile> files = new ArrayList<>();
|
||||
mismatch.walk(File.separator, (path, name, entry) -> {
|
||||
if(entry.getType() == HashedEntry.Type.DIR) {
|
||||
var dirPath = dir.resolve(path);
|
||||
try {
|
||||
if(!Files.exists(dirPath)) {
|
||||
Files.createDirectory(dirPath);
|
||||
} else if (!Files.isDirectory(dirPath)) {
|
||||
throw new IOException(String.format("%s is not a directory", path));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return HashedDir.WalkAction.CONTINUE;
|
||||
}
|
||||
String pathFixed = path.replace(File.separatorChar, '/');
|
||||
files.add(new Downloader.SizedFile(pathFixed, pathRemapper.apply(pathFixed), entry.size()));
|
||||
return HashedDir.WalkAction.CONTINUE;
|
||||
});
|
||||
return files;
|
||||
}
|
||||
|
||||
private void deleteExtraDir(Path subDir, HashedDir subHDir, boolean deleteDir) throws IOException {
|
||||
for (Map.Entry<String, HashedEntry> mapEntry : subHDir.map().entrySet()) {
|
||||
String name = mapEntry.getKey();
|
||||
Path path = subDir.resolve(name);
|
||||
|
||||
// Delete list and dirs based on type
|
||||
HashedEntry entry = mapEntry.getValue();
|
||||
HashedEntry.Type entryType = entry.getType();
|
||||
switch (entryType) {
|
||||
case FILE -> Files.delete(path);
|
||||
case DIR -> deleteExtraDir(path, (HashedDir) entry, deleteDir || entry.flag);
|
||||
default -> throw new AssertionError("Unsupported hashed entry type: " + entryType.name());
|
||||
}
|
||||
}
|
||||
|
||||
// Delete!
|
||||
if (deleteDir) {
|
||||
Files.delete(subDir);
|
||||
}
|
||||
}
|
||||
|
||||
private Function<String, String> makePathRemapperFunction(LinkedList<PathRemapperData> map) {
|
||||
return (path) -> {
|
||||
for(var e : map) {
|
||||
if(path.startsWith(e.key)) {
|
||||
return e.value;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
};
|
||||
}
|
||||
|
||||
private LinkedList<PathRemapperData> applyOptionalMods(OptionalView view, HashedDir hdir) {
|
||||
for (OptionalAction action : view.getDisabledActions()) {
|
||||
if (action instanceof OptionalActionFile optionalActionFile) {
|
||||
optionalActionFile.disableInHashedDir(hdir);
|
||||
}
|
||||
}
|
||||
LinkedList<PathRemapperData> pathRemapper = new LinkedList<>();
|
||||
Set<OptionalActionFile> fileActions = view.getActionsByClass(OptionalActionFile.class);
|
||||
for (OptionalActionFile file : fileActions) {
|
||||
file.injectToHashedDir(hdir);
|
||||
file.files.forEach((k, v) -> {
|
||||
if (v == null || v.isEmpty()) return;
|
||||
pathRemapper.add(new PathRemapperData(v, k)); //reverse (!)
|
||||
LogHelper.dev("Remap prepare %s to %s", v, k);
|
||||
});
|
||||
}
|
||||
pathRemapper.sort(Comparator.comparingInt(c -> -c.key.length())); // Support deep remap
|
||||
return pathRemapper;
|
||||
}
|
||||
|
||||
private record PathRemapperData(String key, String value) {
|
||||
}
|
||||
|
||||
record AssetData(ProfileFeatureAPI.UpdateInfo updateInfo, AssetIndexHelper.AssetIndex index) {
|
||||
|
||||
}
|
||||
|
||||
record DownloadedDir(HashedDir dir, Path path) {
|
||||
|
||||
}
|
||||
|
||||
record VirtualUpdateInfo(HashedDir dir, String url) implements ProfileFeatureAPI.UpdateInfo {
|
||||
|
||||
@Override
|
||||
public HashedDir getHashedDir() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,304 @@
|
|||
package pro.gravit.launcher.runtime.backend;
|
||||
|
||||
import pro.gravit.launcher.base.ClientPermissions;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.core.api.LauncherAPIHolder;
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.CoreFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethod;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
import pro.gravit.launcher.core.api.model.SelfUser;
|
||||
import pro.gravit.launcher.core.api.model.Texture;
|
||||
import pro.gravit.launcher.core.api.model.UserPermissions;
|
||||
import pro.gravit.launcher.core.backend.LauncherBackendAPI;
|
||||
import pro.gravit.launcher.core.backend.UserSettings;
|
||||
import pro.gravit.launcher.core.backend.exceptions.LauncherBackendException;
|
||||
import pro.gravit.launcher.core.backend.extensions.Extension;
|
||||
import pro.gravit.launcher.runtime.NewLauncherSettings;
|
||||
import pro.gravit.launcher.runtime.client.DirBridge;
|
||||
import pro.gravit.launcher.runtime.client.ServerPinger;
|
||||
import pro.gravit.launcher.runtime.debug.DebugMain;
|
||||
import pro.gravit.launcher.runtime.managers.SettingsManager;
|
||||
import pro.gravit.launcher.runtime.utils.LauncherUpdater;
|
||||
import pro.gravit.utils.helper.JavaHelper;
|
||||
import pro.gravit.utils.helper.LogHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class LauncherBackendImpl implements LauncherBackendAPI {
|
||||
private final ClientDownloadImpl clientDownloadImpl = new ClientDownloadImpl(this);
|
||||
private volatile MainCallback callback;
|
||||
ExecutorService executorService;
|
||||
private volatile AuthMethod authMethod;
|
||||
// Settings
|
||||
private SettingsManager settingsManager;
|
||||
private NewLauncherSettings allSettings;
|
||||
private BackendSettings backendSettings;
|
||||
// Data
|
||||
private volatile List<ProfileFeatureAPI.ClientProfile> profiles;
|
||||
private volatile UserPermissions permissions;
|
||||
private volatile SelfUser selfUser;
|
||||
private volatile List<Java> availableJavas;
|
||||
private volatile CompletableFuture<List<Java>> availableJavasFuture;
|
||||
private final Map<UUID, CompletableFuture<ServerPingInfo>> pingFutures = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void setCallback(MainCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
private void doInit() throws Exception {
|
||||
executorService = Executors.newScheduledThreadPool(2, (r) -> {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
registerUserSettings("backend", BackendSettings.class);
|
||||
settingsManager = new SettingsManager();
|
||||
settingsManager.generateConfigIfNotExists();
|
||||
settingsManager.loadConfig();
|
||||
allSettings = settingsManager.getConfig();
|
||||
backendSettings = (BackendSettings) getUserSettings("backend", (k) -> new BackendSettings());
|
||||
permissions = new ClientPermissions();
|
||||
DirBridge.dirUpdates = DirBridge.defaultUpdatesDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LauncherInitData> init() {
|
||||
try {
|
||||
doInit();
|
||||
} catch (Throwable e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
CompletableFuture<CoreFeatureAPI.LauncherUpdateInfo> feature;
|
||||
if(isTestMode()) {
|
||||
feature = CompletableFuture.completedFuture(new CoreFeatureAPI.LauncherUpdateInfo(null, "Unknown", false, false));
|
||||
} else {
|
||||
feature = LauncherAPIHolder.core().checkUpdates();
|
||||
}
|
||||
return feature.thenCombineAsync(LauncherAPIHolder.core().getAuthMethods(), (updatesInfo, authMethods) -> {
|
||||
if(updatesInfo.required()) {
|
||||
try {
|
||||
LauncherUpdater.prepareUpdate(URI.create(updatesInfo.url()).toURL());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
callback.onShutdown();
|
||||
LauncherUpdater.restart();
|
||||
}
|
||||
return new LauncherInitData(authMethods);
|
||||
}, executorService);
|
||||
}
|
||||
|
||||
public AuthFeatureAPI.AuthToken getAuthToken() {
|
||||
return backendSettings.auth.toToken();
|
||||
}
|
||||
|
||||
public AuthMethod getAuthMethod() {
|
||||
return authMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectAuthMethod(AuthMethod method) {
|
||||
this.authMethod = method;
|
||||
LauncherAPIHolder.changeAuthId(method.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SelfUser> tryAuthorize() {
|
||||
if(this.authMethod == null) {
|
||||
return CompletableFuture.failedFuture(new LauncherBackendException("This method call not allowed before select authMethod"));
|
||||
}
|
||||
if(backendSettings.auth == null) {
|
||||
return CompletableFuture.failedFuture(new LauncherBackendException("Auth data not found"));
|
||||
}
|
||||
if(backendSettings.auth.expireIn > 0 && LocalDateTime.ofInstant(Instant.ofEpochMilli(backendSettings.auth.expireIn), ZoneOffset.UTC).isBefore(LocalDateTime.now(ZoneOffset.UTC))) {
|
||||
return LauncherAPIHolder.auth().refreshToken(backendSettings.auth.refreshToken).thenCompose((response) -> {
|
||||
setAuthToken(response);
|
||||
return LauncherAPIHolder.auth().restore(backendSettings.auth.accessToken, true);
|
||||
}).thenApply((user) -> {
|
||||
onAuthorize(user);
|
||||
return user;
|
||||
});
|
||||
}
|
||||
return LauncherAPIHolder.auth().restore(backendSettings.auth.accessToken, true).thenApply((user) -> {
|
||||
onAuthorize(user);
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
private void setAuthToken(AuthFeatureAPI.AuthToken authToken) {
|
||||
backendSettings.auth = new BackendSettings.AuthorizationData();
|
||||
backendSettings.auth.accessToken = authToken.getAccessToken();
|
||||
backendSettings.auth.refreshToken = authToken.getRefreshToken();
|
||||
if(authToken.getExpire() <= 0) {
|
||||
backendSettings.auth.expireIn = 0;
|
||||
}
|
||||
backendSettings.auth.expireIn = LocalDateTime.now(ZoneOffset.UTC)
|
||||
.plus(authToken.getExpire(), ChronoUnit.MILLIS).
|
||||
toEpochSecond(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
private void onAuthorize(SelfUser selfUser) {
|
||||
this.selfUser = selfUser;
|
||||
permissions = selfUser.getPermissions();
|
||||
callback.onAuthorize(selfUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SelfUser> authorize(String login, AuthMethodPassword password) {
|
||||
if(this.authMethod == null) {
|
||||
return CompletableFuture.failedFuture(new LauncherBackendException("This method call not allowed before select authMethod"));
|
||||
}
|
||||
return LauncherAPIHolder.auth().auth(login, password).thenApply((response) -> {
|
||||
setAuthToken(response.authToken());
|
||||
onAuthorize(response.user());
|
||||
return response.user();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<ProfileFeatureAPI.ClientProfile>> fetchProfiles() {
|
||||
return LauncherAPIHolder.profile().getProfiles().thenApply((profiles) -> {
|
||||
this.profiles = profiles;
|
||||
callback.onProfiles(profiles);
|
||||
return profiles;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientProfileSettings makeClientProfileSettings(ProfileFeatureAPI.ClientProfile profile) {
|
||||
var settings = backendSettings.settings.get(profile.getUUID());
|
||||
if(settings == null) {
|
||||
settings = new ProfileSettingsImpl((ClientProfile) profile);
|
||||
settings.backend = this;
|
||||
settings.updateEnabledMods();
|
||||
} else {
|
||||
settings = settings.copy();
|
||||
//settings.initAfterGson((ClientProfile) profile, this);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveClientProfileSettings(ClientProfileSettings settings) {
|
||||
var impl = (ProfileSettingsImpl) settings;
|
||||
impl.updateEnabledMods();
|
||||
backendSettings.settings.put(impl.profile.getUUID(), impl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ReadyProfile> downloadProfile(ProfileFeatureAPI.ClientProfile profile, ClientProfileSettings settings, DownloadCallback callback) {
|
||||
return clientDownloadImpl.downloadProfile((ClientProfile) profile, (ProfileSettingsImpl) settings, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<byte[]> fetchTexture(Texture texture) {
|
||||
return CompletableFuture.failedFuture(new UnsupportedOperationException());
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public CompletableFuture<List<Java>> getAvailableJava() {
|
||||
if(availableJavas == null) {
|
||||
if(availableJavasFuture == null) {
|
||||
availableJavasFuture = CompletableFuture.supplyAsync(() -> {
|
||||
return (List) JavaHelper.findJava(); // TODO: Custom Java
|
||||
}, executorService).thenApply(e -> {
|
||||
availableJavas = e;
|
||||
return e;
|
||||
});
|
||||
}
|
||||
return availableJavasFuture;
|
||||
}
|
||||
return CompletableFuture.completedFuture(availableJavas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ServerPingInfo> pingServer(ProfileFeatureAPI.ClientProfile profile) {
|
||||
return pingFutures.computeIfAbsent(profile.getUUID(), (k) -> {
|
||||
CompletableFuture<ServerPingInfo> future = new CompletableFuture<>();
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
ServerPinger pinger = new ServerPinger((ClientProfile) profile);
|
||||
future.complete(pinger.ping());
|
||||
} catch (Throwable e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
return future;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerUserSettings(String name, Class<? extends UserSettings> clazz) {
|
||||
UserSettings.providers.register(name, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserSettings getUserSettings(String name, Function<String, UserSettings> ifNotExist) {
|
||||
return allSettings.userSettings.computeIfAbsent(name, ifNotExist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPermissions getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return permissions.hasPerm(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return selfUser == null ? "Player" : getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelfUser getSelfUser() {
|
||||
return selfUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTestMode() {
|
||||
try {
|
||||
return DebugMain.IS_DEBUG.get();
|
||||
} catch (Throwable ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Extension> T getExtension(Class<T> clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if(executorService != null) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
if(settingsManager != null) {
|
||||
try {
|
||||
settingsManager.saveConfig();
|
||||
} catch (IOException e) {
|
||||
LogHelper.error("Config not saved", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
package pro.gravit.launcher.runtime.backend;
|
||||
|
||||
import oshi.SystemInfo;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.base.profiles.optional.OptionalFile;
|
||||
import pro.gravit.launcher.base.profiles.optional.OptionalView;
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.backend.LauncherBackendAPI;
|
||||
import pro.gravit.launcher.runtime.utils.SystemMemory;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
import pro.gravit.utils.helper.JavaHelper;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class ProfileSettingsImpl implements LauncherBackendAPI.ClientProfileSettings {
|
||||
transient ClientProfile profile;
|
||||
transient LauncherBackendImpl backend;
|
||||
@LauncherNetworkAPI
|
||||
private Map<MemoryClass, Long> ram;
|
||||
@LauncherNetworkAPI
|
||||
private Set<Flag> flags;
|
||||
@LauncherNetworkAPI
|
||||
private Set<String> enabled;
|
||||
@LauncherNetworkAPI
|
||||
private String saveJavaPath;
|
||||
transient OptionalView view;
|
||||
transient JavaHelper.JavaVersion selectedJava;
|
||||
|
||||
public ProfileSettingsImpl() {
|
||||
}
|
||||
|
||||
public ProfileSettingsImpl(ClientProfile profile) {
|
||||
this.profile = profile;
|
||||
var def = profile.getSettings();
|
||||
this.ram = new HashMap<>();
|
||||
this.ram.put(MemoryClass.TOTAL, ((long)def.ram) << 20);
|
||||
this.flags = new HashSet<>();
|
||||
if(def.autoEnter) {
|
||||
this.flags.add(Flag.AUTO_ENTER);
|
||||
}
|
||||
if(def.fullScreen) {
|
||||
this.flags.add(Flag.FULLSCREEN);
|
||||
}
|
||||
this.view = new OptionalView(profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getReservedMemoryBytes(MemoryClass memoryClass) {
|
||||
return ram.getOrDefault(memoryClass, 0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxMemoryBytes(MemoryClass memoryClass) {
|
||||
try {
|
||||
return SystemMemory.getPhysicalMemorySize();
|
||||
} catch (Throwable e) {
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
return systemInfo.getHardware().getMemory().getTotal();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReservedMemoryBytes(MemoryClass memoryClass, long value) {
|
||||
this.ram.put(memoryClass, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Flag> getFlags() {
|
||||
return Collections.unmodifiableSet(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Flag> getAvailableFlags() {
|
||||
Set<Flag> set = new HashSet<>();
|
||||
set.add(Flag.AUTO_ENTER);
|
||||
set.add(Flag.FULLSCREEN);
|
||||
if(JVMHelper.OS_TYPE == JVMHelper.OS.LINUX) {
|
||||
set.add(Flag.LINUX_WAYLAND_SUPPORT);
|
||||
}
|
||||
if(backend.hasPermission("launcher.debug.skipfilemonitor")) {
|
||||
set.add(Flag.DEBUG_SKIP_FILE_MONITOR);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFlag(Flag flag) {
|
||||
return flags.contains(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFlag(Flag flag) {
|
||||
flags.add(flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFlag(Flag flag) {
|
||||
flags.remove(flag);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public Set<ProfileFeatureAPI.OptionalMod> getEnabledOptionals() {
|
||||
return (Set) view.enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableOptional(ProfileFeatureAPI.OptionalMod mod, ChangedOptionalStatusCallback callback) {
|
||||
view.enable((OptionalFile) mod, true, callback::onChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableOptional(ProfileFeatureAPI.OptionalMod mod, ChangedOptionalStatusCallback callback) {
|
||||
view.disable((OptionalFile) mod, callback::onChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaHelper.JavaVersion getSelectedJava() {
|
||||
return selectedJava;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaHelper.JavaVersion getRecommendedJava() {
|
||||
JavaHelper.JavaVersion result = null;
|
||||
try {
|
||||
for(var java : backend.getAvailableJava().get()) {
|
||||
if(isRecommended(java)) {
|
||||
return (JavaHelper.JavaVersion) java;
|
||||
}
|
||||
if(isCompatible(java)) {
|
||||
if(result == null) {
|
||||
result = (JavaHelper.JavaVersion) java;
|
||||
continue;
|
||||
}
|
||||
if(result.getMajorVersion() < java.getMajorVersion()) {
|
||||
result = (JavaHelper.JavaVersion) java;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedJava(LauncherBackendAPI.Java java) {
|
||||
selectedJava = (JavaHelper.JavaVersion) java;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecommended(LauncherBackendAPI.Java java) {
|
||||
return java.getMajorVersion() == profile.getRecommendJavaVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompatible(LauncherBackendAPI.Java java) {
|
||||
return java.getMajorVersion() >= profile.getMinJavaVersion() && java.getMajorVersion() <= profile.getMaxJavaVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileSettingsImpl copy() {
|
||||
ProfileSettingsImpl cloned = new ProfileSettingsImpl();
|
||||
cloned.backend = backend;
|
||||
cloned.profile = profile;
|
||||
cloned.ram = new HashMap<>(ram);
|
||||
cloned.flags = new HashSet<>(flags);
|
||||
cloned.enabled = new HashSet<>(enabled);
|
||||
if(view != null) {
|
||||
cloned.view = new OptionalView(profile, view);
|
||||
}
|
||||
cloned.selectedJava = selectedJava;
|
||||
cloned.saveJavaPath = saveJavaPath;
|
||||
return cloned;
|
||||
}
|
||||
|
||||
public void updateEnabledMods() {
|
||||
enabled = new HashSet<>();
|
||||
for(var e : view.enabled) {
|
||||
enabled.add(e.name);
|
||||
}
|
||||
if(selectedJava != null) {
|
||||
saveJavaPath = selectedJava.getPath().toAbsolutePath().toString();
|
||||
}
|
||||
}
|
||||
|
||||
public void initAfterGson(ClientProfile profile, LauncherBackendImpl backend) {
|
||||
this.backend = backend;
|
||||
this.profile = profile;
|
||||
this.view = new OptionalView(profile);
|
||||
for(var e : enabled) {
|
||||
var opt = profile.getOptionalFile(e);
|
||||
if(opt == null) {
|
||||
continue;
|
||||
}
|
||||
enableOptional(opt, (var1, var2) -> {});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package pro.gravit.launcher.runtime.backend;
|
||||
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.base.events.request.AuthRequestEvent;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfileBuilder;
|
||||
import pro.gravit.launcher.base.profiles.PlayerProfile;
|
||||
import pro.gravit.launcher.core.api.LauncherAPIHolder;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.backend.LauncherBackendAPI;
|
||||
import pro.gravit.launcher.runtime.client.ClientLauncherProcess;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.JVMHelper;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ReadyProfileImpl implements LauncherBackendAPI.ReadyProfile {
|
||||
private LauncherBackendImpl backend;
|
||||
private ClientProfile profile;
|
||||
private ProfileSettingsImpl settings;
|
||||
private ClientDownloadImpl.DownloadedDir clientDir;
|
||||
private ClientDownloadImpl.DownloadedDir assetDir;
|
||||
private ClientDownloadImpl.DownloadedDir javaDir;
|
||||
private volatile Thread writeParamsThread;
|
||||
private volatile Thread runThread;
|
||||
private volatile ClientLauncherProcess process;
|
||||
private volatile Process nativeProcess;
|
||||
private volatile LauncherBackendAPI.RunCallback callback;
|
||||
|
||||
public ReadyProfileImpl(LauncherBackendImpl backend, ClientProfile profile, ProfileSettingsImpl settings, ClientDownloadImpl.DownloadedDir clientDir, ClientDownloadImpl.DownloadedDir assetDir, ClientDownloadImpl.DownloadedDir javaDir) {
|
||||
this.backend = backend;
|
||||
this.profile = profile;
|
||||
this.settings = settings;
|
||||
this.clientDir = clientDir;
|
||||
this.assetDir = assetDir;
|
||||
this.javaDir = javaDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileFeatureAPI.ClientProfile getClientProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LauncherBackendAPI.ClientProfileSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(LauncherBackendAPI.RunCallback callback) {
|
||||
if(isAlive()) {
|
||||
terminate();
|
||||
}
|
||||
this.callback = callback;
|
||||
if(backend.hasPermission("launcher.debug.skipfilemonitor") && settings.hasFlag(LauncherBackendAPI.ClientProfileSettings.Flag.DEBUG_SKIP_FILE_MONITOR)) {
|
||||
var builder = new ClientProfileBuilder(profile);
|
||||
builder.setUpdate(new ArrayList<>());
|
||||
builder.setUpdateVerify(new ArrayList<>());
|
||||
builder.setUpdateExclusions(new ArrayList<>());
|
||||
profile = builder.createClientProfile();
|
||||
}
|
||||
var java = settings.getSelectedJava();
|
||||
if(java == null) {
|
||||
java = settings.getRecommendedJava();
|
||||
}
|
||||
process = new ClientLauncherProcess(clientDir.path(), assetDir.path(), java, clientDir.path().resolve("resourcepacks"),
|
||||
profile, new PlayerProfile(backend.getSelfUser()), settings.view, backend.getSelfUser().getAccessToken(),
|
||||
clientDir.dir(), assetDir.dir(), javaDir == null ? null : javaDir.dir(),
|
||||
new AuthRequestEvent.OAuthRequestEvent(backend.getAuthToken()), backend.getAuthMethod().getName());
|
||||
process.params.ram = (int) (settings.getReservedMemoryBytes(LauncherBackendAPI.ClientProfileSettings.MemoryClass.TOTAL) >> 20);
|
||||
if (process.params.ram > 0) {
|
||||
process.jvmArgs.add("-Xms" + process.params.ram + 'M');
|
||||
process.jvmArgs.add("-Xmx" + process.params.ram + 'M');
|
||||
}
|
||||
process.params.fullScreen = settings.hasFlag(LauncherBackendAPI.ClientProfileSettings.Flag.FULLSCREEN);
|
||||
process.params.autoEnter = settings.hasFlag(LauncherBackendAPI.ClientProfileSettings.Flag.AUTO_ENTER);
|
||||
if(JVMHelper.OS_TYPE == JVMHelper.OS.LINUX) {
|
||||
process.params.lwjglGlfwWayland = settings.hasFlag(LauncherBackendAPI.ClientProfileSettings.Flag.LINUX_WAYLAND_SUPPORT);
|
||||
}
|
||||
writeParamsThread = new Thread(this::writeParams);
|
||||
writeParamsThread.setDaemon(true);
|
||||
writeParamsThread.start();
|
||||
runThread = new Thread(this::readThread);
|
||||
runThread.setDaemon(true);
|
||||
runThread.start();
|
||||
}
|
||||
|
||||
private void readThread() {
|
||||
try {
|
||||
process.start(true);
|
||||
nativeProcess = process.getProcess();
|
||||
callback.onCanTerminate(this::terminate);
|
||||
InputStream stream = nativeProcess.getInputStream();
|
||||
byte[] buf = IOHelper.newBuffer();
|
||||
try {
|
||||
for (int length = stream.read(buf); length >= 0; length = stream.read(buf)) {
|
||||
callback.onNormalOutput(buf, 0, length);
|
||||
}
|
||||
} catch (EOFException ignored) {
|
||||
}
|
||||
if (nativeProcess.isAlive()) {
|
||||
int code = nativeProcess.waitFor();
|
||||
callback.onFinished(code);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if(e instanceof InterruptedException) {
|
||||
return;
|
||||
}
|
||||
terminate();
|
||||
}
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
if(nativeProcess == null) {
|
||||
return;
|
||||
}
|
||||
nativeProcess.destroyForcibly();
|
||||
}
|
||||
|
||||
public boolean isAlive() {
|
||||
return nativeProcess != null && nativeProcess.isAlive();
|
||||
}
|
||||
|
||||
private void writeParams() {
|
||||
try {
|
||||
process.runWriteParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort));
|
||||
} catch (Throwable e) {
|
||||
terminate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.base.LauncherConfig;
|
||||
import pro.gravit.launcher.base.events.request.AuthRequestEvent;
|
||||
import pro.gravit.launcher.client.ClientLauncherEntryPoint;
|
||||
import pro.gravit.launcher.client.ClientParams;
|
||||
import pro.gravit.launcher.runtime.LauncherEngine;
|
||||
|
@ -69,6 +70,12 @@ public ClientLauncherProcess(Path clientDir, Path assetDir,
|
|||
public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersion javaVersion, Path resourcePackDir,
|
||||
ClientProfile profile, PlayerProfile playerProfile, OptionalView view, String accessToken,
|
||||
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
|
||||
this(clientDir, assetDir, javaVersion, resourcePackDir, profile, playerProfile, view, accessToken, clientHDir, assetHDir, jvmHDir, null, null);
|
||||
}
|
||||
|
||||
public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersion javaVersion, Path resourcePackDir,
|
||||
ClientProfile profile, PlayerProfile playerProfile, OptionalView view, String accessToken,
|
||||
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir, AuthRequestEvent.OAuthRequestEvent oAuthRequestEvent, String authId) {
|
||||
this.javaVersion = javaVersion;
|
||||
this.workDir = clientDir.toAbsolutePath();
|
||||
this.executeFile = IOHelper.resolveJavaBin(this.javaVersion.jvmDir);
|
||||
|
@ -77,6 +84,8 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersi
|
|||
this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString();
|
||||
this.params.assetDir = assetDir.toAbsolutePath().toString();
|
||||
this.params.timestamp = System.currentTimeMillis();
|
||||
this.params.oauth = oAuthRequestEvent;
|
||||
this.params.authId = authId;
|
||||
Path nativesPath;
|
||||
if(profile.hasFlag(ClientProfile.CompatibilityFlags.LEGACY_NATIVES_DIR)) {
|
||||
nativesPath = workDir.resolve("natives");
|
||||
|
@ -119,10 +128,8 @@ private void applyClientProfile() {
|
|||
if (params.ram > 0) {
|
||||
this.jvmArgs.add("-Xmx" + params.ram + 'M');
|
||||
}
|
||||
this.params.oauth = Request.getOAuth();
|
||||
if (this.params.oauth == null) {
|
||||
throw new UnsupportedOperationException("Legacy session not supported");
|
||||
} else {
|
||||
this.params.oauth = Request.getOAuth();
|
||||
this.params.authId = Request.getAuthId();
|
||||
this.params.oauthExpiredTime = Request.getTokenExpiredTime();
|
||||
this.params.extendedTokens = Request.getExtendedTokens();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package pro.gravit.launcher.runtime.client;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import pro.gravit.launcher.core.backend.UserSettings;
|
||||
import pro.gravit.launcher.start.RuntimeModuleManager;
|
||||
import pro.gravit.launcher.core.managers.GsonManager;
|
||||
import pro.gravit.launcher.base.modules.events.PreGsonPhase;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import com.google.gson.JsonParser;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfileVersions;
|
||||
import pro.gravit.launcher.core.backend.LauncherBackendAPI;
|
||||
import pro.gravit.launcher.core.serialize.HInput;
|
||||
import pro.gravit.launcher.core.serialize.HOutput;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
@ -18,6 +19,7 @@
|
|||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -208,7 +210,7 @@ public Result ping() throws IOException {
|
|||
}
|
||||
}
|
||||
|
||||
public static final class Result {
|
||||
public static final class Result implements LauncherBackendAPI.ServerPingInfo {
|
||||
|
||||
public final int onlinePlayers;
|
||||
|
||||
|
@ -228,5 +230,20 @@ public Result(int onlinePlayers, int maxPlayers, String raw) {
|
|||
public boolean isOverfilled() {
|
||||
return onlinePlayers >= maxPlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxOnline() {
|
||||
return maxPlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOnline() {
|
||||
return onlinePlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPlayerNames() {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package pro.gravit.launcher.runtime.utils;
|
||||
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.core.hasher.HashedDir;
|
||||
import pro.gravit.launcher.core.hasher.HashedEntry;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class AssetIndexHelper {
|
||||
|
||||
public static AssetIndex parse(Path path) throws IOException {
|
||||
try (Reader reader = IOHelper.newReader(path)) {
|
||||
return Launcher.gsonManager.gson.fromJson(reader, AssetIndex.class);
|
||||
}
|
||||
}
|
||||
|
||||
public static void modifyHashedDir(AssetIndex index, HashedDir original) {
|
||||
Set<String> hashes = new HashSet<>();
|
||||
for (AssetIndexObject obj : index.objects.values()) {
|
||||
hashes.add(obj.hash);
|
||||
}
|
||||
HashedDir objects = (HashedDir) original.getEntry("objects");
|
||||
List<String> toDeleteDirs = new ArrayList<>(16);
|
||||
for (Map.Entry<String, HashedEntry> entry : objects.map().entrySet()) {
|
||||
if (entry.getValue().getType() != HashedEntry.Type.DIR) {
|
||||
continue;
|
||||
}
|
||||
HashedDir dir = (HashedDir) entry.getValue();
|
||||
List<String> toDelete = new ArrayList<>(16);
|
||||
for (String hash : dir.map().keySet()) {
|
||||
if (!hashes.contains(hash)) {
|
||||
toDelete.add(hash);
|
||||
}
|
||||
}
|
||||
for (String s : toDelete) {
|
||||
dir.remove(s);
|
||||
}
|
||||
if (dir.map().isEmpty()) {
|
||||
toDeleteDirs.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
for (String s : toDeleteDirs) {
|
||||
objects.remove(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static class AssetIndex {
|
||||
@LauncherNetworkAPI
|
||||
public boolean virtual;
|
||||
@LauncherNetworkAPI
|
||||
public Map<String, AssetIndexObject> objects;
|
||||
}
|
||||
|
||||
public static class AssetIndexObject {
|
||||
@LauncherNetworkAPI
|
||||
public String hash;
|
||||
@LauncherNetworkAPI
|
||||
public long size;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package pro.gravit.launcher.runtime.utils;
|
||||
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
|
||||
public class SystemMemory {
|
||||
private static final OperatingSystemMXBean systemMXBean = (OperatingSystemMXBean) java.lang.management.ManagementFactory.getOperatingSystemMXBean();
|
||||
public static long getPhysicalMemorySize() {
|
||||
return systemMXBean.getTotalMemorySize();
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
sourceCompatibility = '21'
|
||||
targetCompatibility = '21'
|
||||
|
||||
dependencies {
|
||||
api project(':LauncherCore')
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package pro.gravit.launcher.base;
|
||||
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.core.api.model.UserPermissions;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ClientPermissions {
|
||||
public class ClientPermissions implements UserPermissions {
|
||||
public static final ClientPermissions DEFAULT = new ClientPermissions();
|
||||
@LauncherNetworkAPI
|
||||
private List<String> roles;
|
||||
|
@ -28,6 +29,7 @@ public static ClientPermissions getSuperuserAccount() {
|
|||
return perm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRole(String role) {
|
||||
return roles != null && roles.contains(role);
|
||||
}
|
||||
|
@ -46,6 +48,7 @@ public synchronized void compile() {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPerm(String action) {
|
||||
if (available == null) {
|
||||
compile();
|
||||
|
|
|
@ -234,7 +234,7 @@ protected DownloadTask sendAsync(SizedFile file, URI baseUri, Path targetDir, Do
|
|||
return task.get();
|
||||
}
|
||||
|
||||
protected HttpRequest makeHttpRequest(URI baseUri, String filePath) throws URISyntaxException {
|
||||
public static URI makeURI(URI baseUri, String filePath) throws URISyntaxException {
|
||||
URI uri;
|
||||
if(baseUri != null) {
|
||||
String scheme = baseUri.getScheme();
|
||||
|
@ -247,6 +247,11 @@ protected HttpRequest makeHttpRequest(URI baseUri, String filePath) throws URISy
|
|||
} else {
|
||||
uri = new URI(filePath);
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
protected HttpRequest makeHttpRequest(URI baseUri, String filePath) throws URISyntaxException {
|
||||
var uri = makeURI(baseUri, filePath);
|
||||
return HttpRequest.newBuilder()
|
||||
.GET()
|
||||
.uri(uri)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package pro.gravit.launcher.base.events.request;
|
||||
|
||||
import pro.gravit.launcher.base.ClientPermissions;
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.base.events.RequestEvent;
|
||||
import pro.gravit.launcher.base.profiles.PlayerProfile;
|
||||
|
@ -67,7 +68,15 @@ public String getType() {
|
|||
return "auth";
|
||||
}
|
||||
|
||||
public static class OAuthRequestEvent {
|
||||
public CurrentUserRequestEvent.UserInfo makeUserInfo() {
|
||||
var userInfo = new CurrentUserRequestEvent.UserInfo();
|
||||
userInfo.accessToken = accessToken;
|
||||
userInfo.permissions = permissions;
|
||||
userInfo.playerProfile = playerProfile;
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
public static class OAuthRequestEvent implements AuthFeatureAPI.AuthToken {
|
||||
public final String accessToken;
|
||||
public final String refreshToken;
|
||||
public final long expire;
|
||||
|
@ -77,5 +86,26 @@ public OAuthRequestEvent(String accessToken, String refreshToken, long expire) {
|
|||
this.refreshToken = refreshToken;
|
||||
this.expire = expire;
|
||||
}
|
||||
|
||||
public OAuthRequestEvent(AuthFeatureAPI.AuthToken token) {
|
||||
this.accessToken = token.getAccessToken();
|
||||
this.refreshToken = token.getRefreshToken();
|
||||
this.expire = token.getExpire();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRefreshToken() {
|
||||
return refreshToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getExpire() {
|
||||
return expire;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
import pro.gravit.launcher.base.ClientPermissions;
|
||||
import pro.gravit.launcher.base.events.RequestEvent;
|
||||
import pro.gravit.launcher.base.profiles.PlayerProfile;
|
||||
import pro.gravit.launcher.core.api.model.SelfUser;
|
||||
import pro.gravit.launcher.core.api.model.Texture;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CurrentUserRequestEvent extends RequestEvent {
|
||||
public final UserInfo userInfo;
|
||||
|
@ -16,7 +21,7 @@ public String getType() {
|
|||
return "currentUser";
|
||||
}
|
||||
|
||||
public static class UserInfo {
|
||||
public static class UserInfo implements SelfUser {
|
||||
public ClientPermissions permissions;
|
||||
public String accessToken;
|
||||
public PlayerProfile playerProfile;
|
||||
|
@ -29,5 +34,35 @@ public UserInfo(ClientPermissions permissions, String accessToken, PlayerProfile
|
|||
this.accessToken = accessToken;
|
||||
this.playerProfile = playerProfile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientPermissions getPermissions() {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return playerProfile.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return playerProfile.getUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Texture> getAssets() {
|
||||
return playerProfile.getAssets();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProperties() {
|
||||
return playerProfile.getProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
package pro.gravit.launcher.base.events.request;
|
||||
|
||||
import pro.gravit.launcher.base.request.auth.details.AuthLoginOnlyDetails;
|
||||
import pro.gravit.launcher.base.request.auth.details.AuthWebViewDetails;
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.base.events.RequestEvent;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethod;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodDetails;
|
||||
import pro.gravit.launcher.core.api.method.details.AuthPasswordDetails;
|
||||
import pro.gravit.launcher.core.api.method.details.AuthWebDetails;
|
||||
import pro.gravit.utils.TypeSerializeInterface;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -37,10 +44,11 @@ public enum ServerFeature {
|
|||
}
|
||||
}
|
||||
|
||||
public interface AuthAvailabilityDetails extends TypeSerializeInterface {
|
||||
public interface AuthAvailabilityDetails extends AuthMethodDetails, TypeSerializeInterface {
|
||||
AuthMethodDetails toAuthMethodDetails();
|
||||
}
|
||||
|
||||
public static class AuthAvailability {
|
||||
public static class AuthAvailability implements AuthMethod {
|
||||
public final List<AuthAvailabilityDetails> details;
|
||||
@LauncherNetworkAPI
|
||||
public String name;
|
||||
|
@ -59,5 +67,34 @@ public AuthAvailability(List<AuthAvailabilityDetails> details, String name, Stri
|
|||
this.visible = visible;
|
||||
this.features = features;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuthMethodDetails> getDetails() {
|
||||
List<AuthMethodDetails> convert = new ArrayList<>();
|
||||
for(var e : details) {
|
||||
convert.add(e.toAuthMethodDetails());
|
||||
}
|
||||
return convert;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getFeatures() {
|
||||
return features;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import pro.gravit.launcher.base.profiles.optional.OptionalDepend;
|
||||
import pro.gravit.launcher.base.profiles.optional.OptionalFile;
|
||||
import pro.gravit.launcher.base.profiles.optional.triggers.OptionalTrigger;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.VerifyHelper;
|
||||
import pro.gravit.utils.launch.LaunchOptions;
|
||||
|
@ -13,8 +14,9 @@
|
|||
import java.lang.reflect.Type;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class ClientProfile implements Comparable<ClientProfile> {
|
||||
public final class ClientProfile implements Comparable<ClientProfile>, ProfileFeatureAPI.ClientProfile {
|
||||
private static final FileNameMatcher ASSET_MATCHER = new FileNameMatcher(
|
||||
new String[0], new String[]{"indexes", "objects"}, new String[0]);
|
||||
@LauncherNetworkAPI
|
||||
|
@ -284,10 +286,25 @@ public String toString() {
|
|||
return String.format("%s (%s)", title, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProfileFeatureAPI.OptionalMod> getOptionalMods() {
|
||||
return updateOptional.stream().collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
public boolean hasFlag(CompatibilityFlags flag) {
|
||||
return flags.contains(flag);
|
||||
}
|
||||
|
@ -361,6 +378,11 @@ public Map<String, String> getProperties() {
|
|||
return Collections.unmodifiableMap(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerInfo getServer() {
|
||||
return getDefaultServerProfile();
|
||||
}
|
||||
|
||||
public List<String> getCompatClasses() {
|
||||
return Collections.unmodifiableList(compatClasses);
|
||||
}
|
||||
|
@ -498,7 +520,7 @@ public JsonElement serialize(Version src, Type typeOfSrc, JsonSerializationConte
|
|||
}
|
||||
}
|
||||
|
||||
public static class ServerProfile {
|
||||
public static class ServerProfile implements ServerInfo {
|
||||
public String name;
|
||||
public String serverAddress;
|
||||
public int serverPort;
|
||||
|
@ -525,6 +547,16 @@ public ServerProfile(String name, String serverAddress, int serverPort, boolean
|
|||
public InetSocketAddress toSocketAddress() {
|
||||
return InetSocketAddress.createUnresolved(serverAddress, serverPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAddress() {
|
||||
return serverAddress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return serverPort;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProfileDefaultSettings {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package pro.gravit.launcher.base.profiles;
|
||||
|
||||
import pro.gravit.launcher.core.api.model.User;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -7,7 +8,7 @@
|
|||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class PlayerProfile {
|
||||
public final class PlayerProfile implements User {
|
||||
|
||||
public final UUID uuid;
|
||||
public final String username;
|
||||
|
@ -41,6 +42,14 @@ public PlayerProfile(UUID uuid, String username, Map<String, Texture> assets, Ma
|
|||
this.properties = properties;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public PlayerProfile(User user) {
|
||||
this.uuid = user.getUUID();
|
||||
this.username = user.getUsername();
|
||||
this.assets = new HashMap<>((Map) user.getAssets());
|
||||
this.properties = user.getProperties();
|
||||
}
|
||||
|
||||
public static PlayerProfile newOfflineProfile(String username) {
|
||||
return new PlayerProfile(offlineUUID(username), username, new HashMap<>(), new HashMap<>());
|
||||
}
|
||||
|
@ -49,4 +58,24 @@ public static UUID offlineUUID(String username) {
|
|||
return UUID.nameUUIDFromBytes(IOHelper.encodeASCII("OfflinePlayer:" + username));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@Override
|
||||
public Map<String, pro.gravit.launcher.core.api.model.Texture> getAssets() {
|
||||
return (Map) assets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
public final class Texture extends StreamObject {
|
||||
public final class Texture extends StreamObject implements pro.gravit.launcher.core.api.model.Texture {
|
||||
private static final SecurityHelper.DigestAlgorithm DIGEST_ALGO = SecurityHelper.DigestAlgorithm.SHA256;
|
||||
|
||||
// Instance
|
||||
|
@ -85,4 +85,19 @@ public String toString() {
|
|||
", metadata=" + metadata +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
return SecurityHelper.toHex(digest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.base.profiles.optional.actions.OptionalAction;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class OptionalFile {
|
||||
public class OptionalFile implements ProfileFeatureAPI.OptionalMod {
|
||||
@LauncherNetworkAPI
|
||||
public List<OptionalAction> actions;
|
||||
@LauncherNetworkAPI
|
||||
|
@ -57,10 +59,25 @@ public String getName() {
|
|||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ProfileFeatureAPI.OptionalMod> getDependencies() {
|
||||
return Set.of(dependencies);
|
||||
}
|
||||
|
||||
public boolean isMark() {
|
||||
return mark;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package pro.gravit.launcher.base.request;
|
||||
|
||||
import pro.gravit.launcher.base.request.auth.GetAvailabilityAuthRequest;
|
||||
import pro.gravit.launcher.base.request.update.LauncherRequest;
|
||||
import pro.gravit.launcher.core.api.features.CoreFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethod;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class RequestCoreFeatureAPIImpl implements CoreFeatureAPI {
|
||||
private final RequestService request;
|
||||
|
||||
public RequestCoreFeatureAPIImpl(RequestService request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public CompletableFuture<List<AuthMethod>> getAuthMethods() {
|
||||
return request.request(new GetAvailabilityAuthRequest()).thenApply(response -> (List) response.list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<LauncherUpdateInfo> checkUpdates() {
|
||||
return request.request(new LauncherRequest()).thenApply(response -> new LauncherUpdateInfo(response.url,
|
||||
"Unknown", response.needUpdate, response.needUpdate));
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class RequestException extends IOException {
|
||||
public final class RequestException extends RuntimeException {
|
||||
|
||||
|
||||
public RequestException(String message) {
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
package pro.gravit.launcher.base.request;
|
||||
|
||||
import pro.gravit.launcher.base.Launcher;
|
||||
import pro.gravit.launcher.base.profiles.ClientProfile;
|
||||
import pro.gravit.launcher.base.request.auth.*;
|
||||
import pro.gravit.launcher.base.request.auth.password.*;
|
||||
import pro.gravit.launcher.base.request.update.ProfilesRequest;
|
||||
import pro.gravit.launcher.base.request.update.UpdateRequest;
|
||||
import pro.gravit.launcher.base.request.uuid.ProfileByUUIDRequest;
|
||||
import pro.gravit.launcher.base.request.uuid.ProfileByUsernameRequest;
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.UserFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
import pro.gravit.launcher.core.api.method.password.AuthChainPassword;
|
||||
import pro.gravit.launcher.core.api.method.password.AuthOAuthPassword;
|
||||
import pro.gravit.launcher.core.api.method.password.AuthPlainPassword;
|
||||
import pro.gravit.launcher.core.api.method.password.AuthTotpPassword;
|
||||
import pro.gravit.launcher.core.api.model.SelfUser;
|
||||
import pro.gravit.launcher.core.api.model.User;
|
||||
import pro.gravit.launcher.core.hasher.HashedDir;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class RequestFeatureAPIImpl implements AuthFeatureAPI, UserFeatureAPI, ProfileFeatureAPI {
|
||||
private final RequestService request;
|
||||
private final String authId;
|
||||
|
||||
public RequestFeatureAPIImpl(RequestService request, String authId) {
|
||||
this.request = request;
|
||||
this.authId = authId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SelfUser> getCurrentUser() {
|
||||
return request.request(new CurrentUserRequest()).thenApply(response -> response.userInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthResponse> auth(String login, AuthMethodPassword password) {
|
||||
AuthRequest.ConnectTypes connectType = AuthRequest.ConnectTypes.API;
|
||||
if(Request.getExtendedTokens() != null && Request.getExtendedTokens().get("launcher") != null) {
|
||||
connectType = AuthRequest.ConnectTypes.CLIENT;
|
||||
}
|
||||
return request.request(new AuthRequest(login, convertAuthPasswordAll(password), authId, false, connectType))
|
||||
.thenApply(response -> new AuthResponse(response.makeUserInfo(), response.oauth));
|
||||
}
|
||||
|
||||
private AuthRequest.AuthPasswordInterface convertAuthPasswordAll(AuthMethodPassword password) {
|
||||
AuthRequest.AuthPasswordInterface requestPassword;
|
||||
if(password instanceof AuthChainPassword chain) {
|
||||
if(chain.list().size() == 1) {
|
||||
requestPassword = convertAuthPassword(chain.list().get(0));
|
||||
} else if(chain.list().size() == 2) {
|
||||
requestPassword = new Auth2FAPassword(convertAuthPassword(chain.list().get(0)),
|
||||
convertAuthPassword(chain.list().get(1)));
|
||||
} else {
|
||||
var multi = new AuthMultiPassword();
|
||||
for(var e : chain.list()) {
|
||||
multi.list.add(convertAuthPassword(e));
|
||||
}
|
||||
requestPassword = multi;
|
||||
}
|
||||
} else {
|
||||
requestPassword = convertAuthPassword(password);
|
||||
}
|
||||
return requestPassword;
|
||||
}
|
||||
|
||||
private AuthRequest.AuthPasswordInterface convertAuthPassword(AuthMethodPassword password) {
|
||||
if(password instanceof AuthPlainPassword plain) {
|
||||
String encryptKey = Launcher.getConfig().passwordEncryptKey;
|
||||
if(encryptKey != null) {
|
||||
try {
|
||||
return new AuthAESPassword(SecurityHelper.encrypt(encryptKey, plain.value()));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
return new pro.gravit.launcher.base.request.auth.password.AuthPlainPassword(plain.value());
|
||||
}
|
||||
} else if(password instanceof AuthTotpPassword totp) {
|
||||
return new AuthTOTPPassword(totp.value());
|
||||
} else if(password instanceof AuthOAuthPassword oauth) {
|
||||
return new AuthCodePassword(oauth.redirectUrl());
|
||||
} else if(password instanceof AuthRequest.AuthPasswordInterface custom) {
|
||||
return custom;
|
||||
} else if(password == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<User> getUserByUsername(String username) {
|
||||
return request.request(new ProfileByUsernameRequest(username)).thenApply(response -> response.playerProfile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<User> getUserByUUID(UUID uuid) {
|
||||
return request.request(new ProfileByUUIDRequest(uuid)).thenApply(response -> response.playerProfile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> joinServer(String username, String accessToken, String serverID) {
|
||||
return request.request(new JoinServerRequest(username, accessToken, serverID)).thenCompose(response -> {
|
||||
if(response.allow) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
return CompletableFuture.failedFuture(new RequestException("Not allowed"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> joinServer(UUID uuid, String accessToken, String serverID) {
|
||||
return request.request(new JoinServerRequest(uuid, accessToken, serverID)).thenCompose(response -> {
|
||||
if(response.allow) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
return CompletableFuture.failedFuture(new RequestException("Not allowed"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CheckServerResponse> checkServer(String username, String serverID, boolean extended) {
|
||||
return request.request(new CheckServerRequest(username, serverID, extended, extended))
|
||||
.thenApply(response -> new CheckServerResponse(response.playerProfile, response.hardwareId,
|
||||
response.sessionId, response.sessionProperties));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<AuthToken> refreshToken(String refreshToken) {
|
||||
return request.request(new RefreshTokenRequest(authId, refreshToken)).thenApply(response -> response.oauth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SelfUser> restore(String accessToken, boolean fetchUser) {
|
||||
Map<String, String> extended = new HashMap<>();
|
||||
if(Request.getExtendedTokens() != null) { // TODO: Control extended token
|
||||
for(var e : Request.getExtendedTokens().entrySet()) {
|
||||
extended.put(e.getKey(), e.getValue().token);
|
||||
}
|
||||
}
|
||||
return request.request(new RestoreRequest(authId, accessToken, extended, fetchUser)).thenApply(e -> {
|
||||
// TODO: invalidToken process
|
||||
return e.userInfo;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> exit() {
|
||||
return request.request(new ExitRequest()).thenApply(response -> null);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Override
|
||||
public CompletableFuture<List<ProfileFeatureAPI.ClientProfile>> getProfiles() {
|
||||
return request.request(new ProfilesRequest()).thenApply(response -> (List) response.profiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> changeCurrentProfile(ClientProfile profile) {
|
||||
return request.request(new SetProfileRequest((pro.gravit.launcher.base.profiles.ClientProfile) profile)).thenApply(response -> null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<UpdateInfo> fetchUpdateInfo(String dirName) {
|
||||
return request.request(new UpdateRequest(dirName)).thenApply(response -> new UpdateInfoData(response.hdir, response.url));
|
||||
}
|
||||
|
||||
public record UpdateInfoData(HashedDir hdir, String url) implements ProfileFeatureAPI.UpdateInfo {
|
||||
@Override
|
||||
public HashedDir getHashedDir() {
|
||||
return hdir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,25 +5,21 @@
|
|||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public interface RequestService {
|
||||
<T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request) throws IOException;
|
||||
<T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request);
|
||||
void connect() throws Exception;
|
||||
|
||||
void registerEventHandler(EventHandler handler);
|
||||
|
||||
void unregisterEventHandler(EventHandler handler);
|
||||
|
||||
default <T extends WebSocketEvent> T requestSync(Request<T> request) throws IOException {
|
||||
default <T extends WebSocketEvent> T requestSync(Request<T> request) {
|
||||
try {
|
||||
return request(request).get();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RequestException("Request interrupted");
|
||||
} catch (ExecutionException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof IOException)
|
||||
throw (IOException) e.getCause();
|
||||
else {
|
||||
throw new RequestException(cause);
|
||||
}
|
||||
throw new RequestException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import pro.gravit.launcher.base.request.Request;
|
||||
import pro.gravit.launcher.base.request.auth.password.*;
|
||||
import pro.gravit.launcher.base.request.websockets.WebSocketRequest;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
import pro.gravit.utils.ProviderMap;
|
||||
|
||||
public final class AuthRequest extends Request<AuthRequestEvent> implements WebSocketRequest {
|
||||
|
@ -64,7 +65,7 @@ public enum ConnectTypes {
|
|||
API
|
||||
}
|
||||
|
||||
public interface AuthPasswordInterface {
|
||||
public interface AuthPasswordInterface extends AuthMethodPassword {
|
||||
boolean check();
|
||||
|
||||
default boolean isAllowSave() {
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
package pro.gravit.launcher.base.request.auth.details;
|
||||
|
||||
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodDetails;
|
||||
|
||||
public class AuthLoginOnlyDetails implements GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails {
|
||||
@Override
|
||||
public String getType() {
|
||||
return "loginonly";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthMethodDetails toAuthMethodDetails() {
|
||||
return new pro.gravit.launcher.core.api.method.details.AuthLoginOnlyDetails();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package pro.gravit.launcher.base.request.auth.details;
|
||||
|
||||
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodDetails;
|
||||
|
||||
public class AuthPasswordDetails implements GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails {
|
||||
@Override
|
||||
|
@ -9,4 +10,8 @@ public String getType() {
|
|||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AuthMethodDetails toAuthMethodDetails() {
|
||||
return new pro.gravit.launcher.core.api.method.details.AuthPasswordDetails();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package pro.gravit.launcher.base.request.auth.details;
|
||||
|
||||
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodDetails;
|
||||
|
||||
public class AuthTotpDetails implements GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails {
|
||||
public final String alg;
|
||||
|
@ -20,4 +21,9 @@ public AuthTotpDetails(String alg) {
|
|||
public String getType() {
|
||||
return "totp";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthMethodDetails toAuthMethodDetails() {
|
||||
return new pro.gravit.launcher.core.api.method.details.AuthTotpDetails(maxKeyLength);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package pro.gravit.launcher.base.request.auth.details;
|
||||
|
||||
import pro.gravit.launcher.base.events.request.GetAvailabilityAuthRequestEvent;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodDetails;
|
||||
import pro.gravit.launcher.core.api.method.details.AuthWebDetails;
|
||||
|
||||
public class AuthWebViewDetails implements GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails {
|
||||
public final String url;
|
||||
|
@ -26,4 +28,9 @@ public AuthWebViewDetails(String url, String redirectUrl) {
|
|||
public String getType() {
|
||||
return "webview";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthMethodDetails toAuthMethodDetails() {
|
||||
return new AuthWebDetails(url, redirectUrl, canBrowser);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,14 @@ public class Auth2FAPassword implements AuthRequest.AuthPasswordInterface {
|
|||
public AuthRequest.AuthPasswordInterface firstPassword;
|
||||
public AuthRequest.AuthPasswordInterface secondPassword;
|
||||
|
||||
public Auth2FAPassword() {
|
||||
}
|
||||
|
||||
public Auth2FAPassword(AuthRequest.AuthPasswordInterface firstPassword, AuthRequest.AuthPasswordInterface secondPassword) {
|
||||
this.firstPassword = firstPassword;
|
||||
this.secondPassword = secondPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check() {
|
||||
return firstPassword != null && firstPassword.check() && secondPassword != null && secondPassword.check();
|
||||
|
|
|
@ -5,6 +5,13 @@
|
|||
public class AuthTOTPPassword implements AuthRequest.AuthPasswordInterface {
|
||||
public String totp;
|
||||
|
||||
public AuthTOTPPassword() {
|
||||
}
|
||||
|
||||
public AuthTOTPPassword(String totp) {
|
||||
this.totp = totp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check() {
|
||||
return true;
|
||||
|
|
|
@ -97,10 +97,14 @@ public <T extends WebSocketEvent> void eventHandle(T webSocketEvent) {
|
|||
processEventHandlers(webSocketEvent);
|
||||
}
|
||||
|
||||
public <T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request) throws IOException {
|
||||
public <T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request) {
|
||||
CompletableFuture<T> result = new CompletableFuture<>();
|
||||
futureMap.put(request.requestUUID, result);
|
||||
sendObject(request, WebSocketRequest.class);
|
||||
try {
|
||||
sendObject(request, WebSocketRequest.class);
|
||||
} catch (IOException e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -114,18 +118,14 @@ public void unregisterEventHandler(RequestService.EventHandler handler) {
|
|||
eventHandlers.remove(handler);
|
||||
}
|
||||
|
||||
public <T extends WebSocketEvent> T requestSync(Request<T> request) throws IOException {
|
||||
public <T extends WebSocketEvent> T requestSync(Request<T> request) {
|
||||
try {
|
||||
return request(request).get();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RequestException("Request interrupted");
|
||||
} catch (ExecutionException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof IOException)
|
||||
throw (IOException) e.getCause();
|
||||
else {
|
||||
throw new RequestException(cause);
|
||||
}
|
||||
throw new RequestException(cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
url "https://repo.spring.io/plugins-release/"
|
||||
}
|
||||
}
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
sourceCompatibility = '21'
|
||||
targetCompatibility = '21'
|
||||
|
||||
jar {
|
||||
archiveClassifier.set('clean')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
sourceCompatibility = '21'
|
||||
targetCompatibility = '21'
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'org.fusesource.jansi', name: 'jansi', version: rootProject['verJansi']
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package pro.gravit.launcher.core.api;
|
||||
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.FeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.UserFeatureAPI;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class LauncherAPI {
|
||||
private final Map<Class<? extends FeatureAPI>, FeatureAPI> map;
|
||||
|
||||
public LauncherAPI(Map<Class<? extends FeatureAPI>, FeatureAPI> map) {
|
||||
this.map = new HashMap<>(map);
|
||||
}
|
||||
|
||||
public AuthFeatureAPI auth() {
|
||||
return get(AuthFeatureAPI.class);
|
||||
}
|
||||
|
||||
public UserFeatureAPI user() {
|
||||
return get(UserFeatureAPI.class);
|
||||
}
|
||||
|
||||
public ProfileFeatureAPI profile() {
|
||||
return get(ProfileFeatureAPI.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public<T extends FeatureAPI> T get(Class<T> clazz) {
|
||||
return (T) map.get(clazz);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package pro.gravit.launcher.core.api;
|
||||
|
||||
import pro.gravit.launcher.core.api.features.AuthFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.CoreFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.features.UserFeatureAPI;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class LauncherAPIHolder {
|
||||
private static volatile CoreFeatureAPI coreAPI;
|
||||
private static volatile LauncherAPI api;
|
||||
private static volatile Function<String, LauncherAPI> createApiFactory;
|
||||
private static final Map<String, LauncherAPI> map = new ConcurrentHashMap<>();
|
||||
|
||||
public static void setCoreAPI(CoreFeatureAPI coreAPI) {
|
||||
LauncherAPIHolder.coreAPI = coreAPI;
|
||||
}
|
||||
|
||||
public static void setApi(LauncherAPI api) {
|
||||
LauncherAPIHolder.api = api;
|
||||
}
|
||||
|
||||
public static void setCreateApiFactory(Function<String, LauncherAPI> createApiFactory) {
|
||||
LauncherAPIHolder.createApiFactory = createApiFactory;
|
||||
}
|
||||
|
||||
public static void changeAuthId(String authId) {
|
||||
LauncherAPIHolder.api = map.computeIfAbsent(authId, createApiFactory);
|
||||
}
|
||||
|
||||
public static LauncherAPI get() {
|
||||
return api;
|
||||
}
|
||||
|
||||
public static LauncherAPI get(String authId) {
|
||||
return map.computeIfAbsent(authId, createApiFactory);
|
||||
}
|
||||
|
||||
public static CoreFeatureAPI core() {
|
||||
return coreAPI;
|
||||
}
|
||||
|
||||
public static AuthFeatureAPI auth() {
|
||||
if(api == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
return api.auth();
|
||||
}
|
||||
|
||||
public static UserFeatureAPI user() {
|
||||
if(api == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
return api.user();
|
||||
}
|
||||
|
||||
public static ProfileFeatureAPI profile() {
|
||||
if(api == null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
return api.profile();
|
||||
}
|
||||
|
||||
public static void set(LauncherAPI api) {
|
||||
LauncherAPIHolder.api = api;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package pro.gravit.launcher.core.api.features;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethod;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
import pro.gravit.launcher.core.api.model.SelfUser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface AuthFeatureAPI extends FeatureAPI {
|
||||
CompletableFuture<SelfUser> getCurrentUser();
|
||||
CompletableFuture<AuthResponse> auth(String login, AuthMethodPassword password);
|
||||
CompletableFuture<AuthToken> refreshToken(String refreshToken);
|
||||
CompletableFuture<SelfUser> restore(String accessToken, boolean fetchUser);
|
||||
CompletableFuture<Void> exit();
|
||||
|
||||
record AuthResponse(SelfUser user, AuthToken authToken) {}
|
||||
|
||||
interface AuthToken {
|
||||
String getAccessToken();
|
||||
String getRefreshToken();
|
||||
long getExpire();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package pro.gravit.launcher.core.api.features;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethod;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface CoreFeatureAPI {
|
||||
CompletableFuture<List<AuthMethod>> getAuthMethods();
|
||||
CompletableFuture<LauncherUpdateInfo> checkUpdates();
|
||||
|
||||
record LauncherUpdateInfo(String url, String version, boolean available, boolean required) {}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package pro.gravit.launcher.core.api.features;
|
||||
|
||||
public interface FeatureAPI {
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package pro.gravit.launcher.core.api.features;
|
||||
|
||||
import pro.gravit.launcher.core.hasher.HashedDir;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface ProfileFeatureAPI extends FeatureAPI {
|
||||
CompletableFuture<List<ClientProfile>> getProfiles();
|
||||
CompletableFuture<Void> changeCurrentProfile(ClientProfile profile);
|
||||
CompletableFuture<UpdateInfo> fetchUpdateInfo(String dirName);
|
||||
|
||||
interface UpdateInfo {
|
||||
HashedDir getHashedDir();
|
||||
String getUrl();
|
||||
}
|
||||
|
||||
interface ClientProfile {
|
||||
String getName();
|
||||
UUID getUUID();
|
||||
String getDescription();
|
||||
List<OptionalMod> getOptionalMods();
|
||||
String getProperty(String name);
|
||||
Map<String, String> getProperties();
|
||||
ServerInfo getServer();
|
||||
|
||||
interface ServerInfo {
|
||||
String getAddress();
|
||||
int getPort();
|
||||
}
|
||||
}
|
||||
|
||||
interface OptionalMod {
|
||||
String getName();
|
||||
String getDescription();
|
||||
String getCategory();
|
||||
boolean isVisible();
|
||||
Set<OptionalMod> getDependencies();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package pro.gravit.launcher.core.api.features;
|
||||
|
||||
import pro.gravit.launcher.core.api.model.Texture;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface TextureUploadFeatureAPI {
|
||||
CompletableFuture<TextureUploadInfo> fetchInfo();
|
||||
CompletableFuture<Texture> upload(String name, byte[] bytes, UploadSettings settings);
|
||||
|
||||
interface TextureUploadInfo {
|
||||
Set<String> getAvailable();
|
||||
boolean isRequireManualSlimSkinSelect();
|
||||
}
|
||||
|
||||
record UploadSettings(boolean slim) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package pro.gravit.launcher.core.api.features;
|
||||
|
||||
import pro.gravit.launcher.core.api.model.User;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public interface UserFeatureAPI extends FeatureAPI {
|
||||
CompletableFuture<User> getUserByUsername(String username);
|
||||
CompletableFuture<User> getUserByUUID(UUID uuid);
|
||||
CompletableFuture<Void> joinServer(String username, String accessToken, String serverID);
|
||||
CompletableFuture<Void> joinServer(UUID uuid, String accessToken, String serverID);
|
||||
CompletableFuture<CheckServerResponse> checkServer(String username, String serverID, boolean extended);
|
||||
default CompletableFuture<List<User>> getUsersByUsernames(List<String> usernames) {
|
||||
List<CompletableFuture<User>> list = new ArrayList<>();
|
||||
for(var username : usernames) {
|
||||
list.add(getUserByUsername(username));
|
||||
}
|
||||
return CompletableFuture.allOf(list.toArray(CompletableFuture[]::new)).thenApply(x -> {
|
||||
List<User> r = new ArrayList<>();
|
||||
for(var e : list) {
|
||||
try {
|
||||
r.add(e.get());
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
r.add(null);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
});
|
||||
}
|
||||
|
||||
record CheckServerResponse(User user, String hardwareId, String sessionId, Map<String, String> sessionProperties) {}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package pro.gravit.launcher.core.api.method;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface AuthMethod {
|
||||
List<AuthMethodDetails> getDetails();
|
||||
String getName();
|
||||
String getDisplayName();
|
||||
boolean isVisible();
|
||||
Set<String> getFeatures();
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package pro.gravit.launcher.core.api.method;
|
||||
|
||||
public interface AuthMethodDetails {
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package pro.gravit.launcher.core.api.method;
|
||||
|
||||
public interface AuthMethodPassword {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package pro.gravit.launcher.core.api.method.details;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodDetails;
|
||||
|
||||
public class AuthLoginOnlyDetails implements AuthMethodDetails {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package pro.gravit.launcher.core.api.method.details;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodDetails;
|
||||
|
||||
public class AuthPasswordDetails implements AuthMethodDetails {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package pro.gravit.launcher.core.api.method.details;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodDetails;
|
||||
|
||||
public record AuthTotpDetails(int length) implements AuthMethodDetails {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package pro.gravit.launcher.core.api.method.details;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodDetails;
|
||||
|
||||
public record AuthWebDetails(String url, String redirectUrl, boolean externalBrowserSupport) implements AuthMethodDetails {
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package pro.gravit.launcher.core.api.method.password;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record AuthChainPassword(List<AuthMethodPassword> list) implements AuthMethodPassword {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package pro.gravit.launcher.core.api.method.password;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
|
||||
public record AuthOAuthPassword(String redirectUrl) implements AuthMethodPassword {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package pro.gravit.launcher.core.api.method.password;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
|
||||
public record AuthPlainPassword(String value) implements AuthMethodPassword {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package pro.gravit.launcher.core.api.method.password;
|
||||
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
|
||||
public record AuthTotpPassword(String value) implements AuthMethodPassword {
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package pro.gravit.launcher.core.api.model;
|
||||
|
||||
public interface SelfUser extends User {
|
||||
String getAccessToken();
|
||||
UserPermissions getPermissions();
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package pro.gravit.launcher.core.api.model;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface Texture {
|
||||
String getUrl();
|
||||
String getHash();
|
||||
Map<String, String> getMetadata();
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package pro.gravit.launcher.core.api.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface User {
|
||||
String getUsername();
|
||||
UUID getUUID();
|
||||
Map<String, Texture> getAssets();
|
||||
Map<String, String> getProperties();
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package pro.gravit.launcher.core.api.model;
|
||||
|
||||
public interface UserPermissions {
|
||||
boolean hasRole(String role);
|
||||
boolean hasPerm(String action);
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
package pro.gravit.launcher.core.backend;
|
||||
|
||||
import pro.gravit.launcher.core.LauncherNetworkAPI;
|
||||
import pro.gravit.launcher.core.api.features.ProfileFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethod;
|
||||
import pro.gravit.launcher.core.api.method.AuthMethodPassword;
|
||||
import pro.gravit.launcher.core.api.model.SelfUser;
|
||||
import pro.gravit.launcher.core.api.model.Texture;
|
||||
import pro.gravit.launcher.core.api.model.UserPermissions;
|
||||
import pro.gravit.launcher.core.backend.extensions.Extension;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface LauncherBackendAPI {
|
||||
void setCallback(MainCallback callback);
|
||||
CompletableFuture<LauncherInitData> init();
|
||||
void selectAuthMethod(AuthMethod method);
|
||||
CompletableFuture<SelfUser> tryAuthorize();
|
||||
CompletableFuture<SelfUser> authorize(String login, AuthMethodPassword password);
|
||||
CompletableFuture<List<ProfileFeatureAPI.ClientProfile>> fetchProfiles();
|
||||
ClientProfileSettings makeClientProfileSettings(ProfileFeatureAPI.ClientProfile profile);
|
||||
void saveClientProfileSettings(ClientProfileSettings settings);
|
||||
CompletableFuture<ReadyProfile> downloadProfile(ProfileFeatureAPI.ClientProfile profile, ClientProfileSettings settings, DownloadCallback callback);
|
||||
// Tools
|
||||
CompletableFuture<byte[]> fetchTexture(Texture texture);
|
||||
CompletableFuture<List<Java>> getAvailableJava();
|
||||
CompletableFuture<ServerPingInfo> pingServer(ProfileFeatureAPI.ClientProfile profile);
|
||||
// Settings
|
||||
void registerUserSettings(String name, Class<? extends UserSettings> clazz);
|
||||
UserSettings getUserSettings(String name, Function<String, UserSettings> ifNotExist);
|
||||
// Status
|
||||
UserPermissions getPermissions();
|
||||
boolean hasPermission(String permission);
|
||||
String getUsername();
|
||||
SelfUser getSelfUser();
|
||||
boolean isTestMode();
|
||||
// Extensions
|
||||
<T extends Extension> T getExtension(Class<T> clazz);
|
||||
void shutdown();
|
||||
|
||||
record LauncherInitData(List<AuthMethod> methods) {}
|
||||
|
||||
interface ReadyProfile {
|
||||
ProfileFeatureAPI.ClientProfile getClientProfile();
|
||||
ClientProfileSettings getSettings();
|
||||
void run(RunCallback callback) throws Exception;
|
||||
}
|
||||
|
||||
interface ClientProfileSettings {
|
||||
long getReservedMemoryBytes(MemoryClass memoryClass);
|
||||
long getMaxMemoryBytes(MemoryClass memoryClass);
|
||||
void setReservedMemoryBytes(MemoryClass memoryClass, long value);
|
||||
Set<Flag> getFlags();
|
||||
Set<Flag> getAvailableFlags();
|
||||
boolean hasFlag(Flag flag);
|
||||
void addFlag(Flag flag);
|
||||
void removeFlag(Flag flag);
|
||||
Set<ProfileFeatureAPI.OptionalMod> getEnabledOptionals();
|
||||
void enableOptional(ProfileFeatureAPI.OptionalMod mod, ChangedOptionalStatusCallback callback);
|
||||
void disableOptional(ProfileFeatureAPI.OptionalMod mod, ChangedOptionalStatusCallback callback);
|
||||
Java getSelectedJava();
|
||||
Java getRecommendedJava();
|
||||
void setSelectedJava(Java java);
|
||||
boolean isRecommended(Java java);
|
||||
boolean isCompatible(Java java);
|
||||
ClientProfileSettings copy();
|
||||
|
||||
enum Flag {
|
||||
@LauncherNetworkAPI
|
||||
AUTO_ENTER,
|
||||
@LauncherNetworkAPI
|
||||
FULLSCREEN,
|
||||
@LauncherNetworkAPI
|
||||
LINUX_WAYLAND_SUPPORT,
|
||||
@LauncherNetworkAPI
|
||||
DEBUG_SKIP_FILE_MONITOR
|
||||
}
|
||||
|
||||
enum MemoryClass {
|
||||
TOTAL
|
||||
}
|
||||
|
||||
interface ChangedOptionalStatusCallback {
|
||||
void onChanged(ProfileFeatureAPI.OptionalMod mod, boolean enabled);
|
||||
}
|
||||
}
|
||||
|
||||
// Callbacks
|
||||
|
||||
class MainCallback {
|
||||
// On any request
|
||||
public void onChangeStatus(String status) {
|
||||
|
||||
}
|
||||
|
||||
public void onProfiles(List<ProfileFeatureAPI.ClientProfile> profiles) {
|
||||
|
||||
}
|
||||
|
||||
public void onAuthorize(SelfUser selfUser) {
|
||||
|
||||
}
|
||||
|
||||
public void onNotify(String header, String description) {
|
||||
|
||||
}
|
||||
|
||||
public void onShutdown() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class RunCallback {
|
||||
public void onStarted() {
|
||||
|
||||
}
|
||||
|
||||
public void onCanTerminate(Runnable terminate) {
|
||||
|
||||
}
|
||||
|
||||
public void onFinished(int code) {
|
||||
|
||||
}
|
||||
|
||||
public void onNormalOutput(byte[] buf, int offset, int size) {
|
||||
|
||||
}
|
||||
|
||||
public void onErrorOutput(byte[] buf, int offset, int size) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadCallback {
|
||||
public static final String STAGE_ASSET_VERIFY = "assetVerify";
|
||||
public static final String STAGE_HASHING = "hashing";
|
||||
public static final String STAGE_DIFF = "diff";
|
||||
public static final String STAGE_DOWNLOAD = "download";
|
||||
public static final String STAGE_DELETE_EXTRA = "deleteExtra";
|
||||
public static final String STAGE_DONE_PART = "done.part";
|
||||
public static final String STAGE_DONE = "done";
|
||||
|
||||
public void onStage(String stage) {
|
||||
|
||||
}
|
||||
|
||||
public void onCanCancel(Runnable cancel) {
|
||||
|
||||
}
|
||||
|
||||
public void onTotalDownload(long total) {
|
||||
|
||||
}
|
||||
|
||||
public void onCurrentDownloaded(long current) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
interface Java {
|
||||
int getMajorVersion();
|
||||
Path getPath();
|
||||
}
|
||||
|
||||
interface ServerPingInfo {
|
||||
int getMaxOnline();
|
||||
int getOnline();
|
||||
List<String> getPlayerNames();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package pro.gravit.launcher.core.backend;
|
||||
|
||||
public class LauncherBackendAPIHolder {
|
||||
private static volatile LauncherBackendAPI api;
|
||||
|
||||
public static LauncherBackendAPI getApi() {
|
||||
return api;
|
||||
}
|
||||
|
||||
public static void setApi(LauncherBackendAPI api) {
|
||||
LauncherBackendAPIHolder.api = api;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package pro.gravit.launcher.runtime.client;
|
||||
package pro.gravit.launcher.core.backend;
|
||||
|
||||
import pro.gravit.utils.ProviderMap;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package pro.gravit.launcher.core.backend.exceptions;
|
||||
|
||||
public class LauncherBackendException extends RuntimeException {
|
||||
public LauncherBackendException() {
|
||||
}
|
||||
|
||||
public LauncherBackendException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public LauncherBackendException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package pro.gravit.launcher.core.backend.extensions;
|
||||
|
||||
public interface Extension {
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package pro.gravit.launcher.core.backend.extensions;
|
||||
|
||||
import pro.gravit.launcher.core.api.features.TextureUploadFeatureAPI;
|
||||
import pro.gravit.launcher.core.api.model.Texture;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface TextureUploadExtension extends Extension {
|
||||
CompletableFuture<TextureUploadFeatureAPI.TextureUploadInfo> fetchTextureUploadInfo();
|
||||
CompletableFuture<Texture> uploadTexture(String name, byte[] bytes, TextureUploadFeatureAPI.UploadSettings settings);
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package pro.gravit.utils.helper;
|
||||
|
||||
import pro.gravit.launcher.core.backend.LauncherBackendAPI;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
@ -186,7 +188,7 @@ public JavaVersionAndBuild() {
|
|||
}
|
||||
}
|
||||
|
||||
public static class JavaVersion {
|
||||
public static class JavaVersion implements LauncherBackendAPI.Java {
|
||||
public final Path jvmDir;
|
||||
public final int version;
|
||||
public final int build;
|
||||
|
@ -301,5 +303,15 @@ public static boolean isExistExtJavaLibrary(Path jvmDir, String name) {
|
|||
Path jdkPathLin = jvmDir.resolve("jre").resolve("lib").resolve(name.concat(".jar"));
|
||||
return IOHelper.isFile(jrePath) || IOHelper.isFile(jdkPath) || IOHelper.isFile(jdkPathLin) || IOHelper.isFile(jrePathLin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMajorVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath() {
|
||||
return jvmDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
url "https://repo.spring.io/plugins-release/"
|
||||
}
|
||||
}
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
sourceCompatibility = '21'
|
||||
targetCompatibility = '21'
|
||||
|
||||
jar {
|
||||
archiveClassifier.set('clean')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'com.gradleup.shadow'
|
||||
|
||||
String mainClassName = "pro.gravit.launcher.server.ServerWrapper"
|
||||
String mainAgentName = "pro.gravit.launcher.server.ServerAgent"
|
||||
|
@ -14,8 +14,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
sourceCompatibility = '17'
|
||||
targetCompatibility = '17'
|
||||
sourceCompatibility = '21'
|
||||
targetCompatibility = '21'
|
||||
|
||||
jar {
|
||||
archiveClassifier.set('clean')
|
||||
|
|
13
build.gradle
13
build.gradle
|
@ -1,5 +1,5 @@
|
|||
plugins {
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
|
||||
id 'com.gradleup.shadow' version '9.0.0-beta15' apply false
|
||||
id 'maven-publish'
|
||||
id 'signing'
|
||||
id 'org.openjfx.javafxplugin' version '0.1.0' apply false
|
||||
|
@ -30,13 +30,6 @@
|
|||
maven {
|
||||
url "https://jitpack.io/"
|
||||
}
|
||||
maven {
|
||||
url 'https://maven.gravit-support.ru/repository/jitpack'
|
||||
credentials {
|
||||
username = 'gravitlauncher'
|
||||
password = 'gravitlauncher'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
|
@ -71,6 +64,10 @@
|
|||
repositories {
|
||||
maven {
|
||||
url = version.endsWith('SNAPSHOT') ? getProperty('mavenSnapshotRepository') : getProperty('mavenReleaseRepository')
|
||||
credentials {
|
||||
username getProperty('mavenUsername')
|
||||
password getProperty('mavenPassword')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
|||
Subproject commit 6f699fae50f98cec19279092d9d5fac1ac451914
|
||||
Subproject commit acab14e403bb12649883017b14f2cf66cb1f4e68
|
Loading…
Reference in a new issue