mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-01-24 16:19:23 +03:00
[FEATURE] ClientZoneInfo
This commit is contained in:
parent
9091c838e4
commit
837d27dd7b
9 changed files with 257 additions and 46 deletions
|
@ -20,6 +20,7 @@
|
|||
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
|
||||
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClassPath;
|
||||
import pro.gravit.launcher.profiles.optional.actions.OptionalActionClientArgs;
|
||||
import pro.gravit.launcher.profiles.optional.actions.OptionalLibraryAction;
|
||||
import pro.gravit.launcher.profiles.optional.triggers.OptionalTrigger;
|
||||
import pro.gravit.launcher.request.Request;
|
||||
import pro.gravit.launcher.request.RequestException;
|
||||
|
@ -119,7 +120,7 @@ public static void main(String[] args) throws Throwable {
|
|||
|
||||
// Verify ClientLauncher sign and classpath
|
||||
LogHelper.debug("Verifying ClientLauncher sign and classpath");
|
||||
List<URL> classpath = resolveClassPath(clientDir, params.actions, params.profile).map(IOHelper::toURL).collect(Collectors.toList());
|
||||
List<URL> classpath = resolveClassPath(clientDir, params.actions, params.zones, params.profile).map(IOHelper::toURL).collect(Collectors.toList());
|
||||
// Start client with WatchService monitoring
|
||||
boolean digest = !profile.isUpdateFastCheck();
|
||||
RequestService service;
|
||||
|
@ -204,8 +205,24 @@ public static void main(String[] args) throws Throwable {
|
|||
verifyHDir(clientDir, params.clientHDir, clientMatcher, digest);
|
||||
if (javaWatcher != null)
|
||||
verifyHDir(javaDir, params.javaHDir, null, digest);
|
||||
List<DirWatcher> watchers = null;
|
||||
if(params.zones != null) {
|
||||
watchers = new ArrayList<>(params.zones.size());
|
||||
for(ClientLauncherProcess.ClientParams.ClientZoneInfo info : params.zones) {
|
||||
if(info.dir == null)
|
||||
continue;
|
||||
DirWatcher watcher = new DirWatcher(Paths.get(info.path), info.dir, null, digest);
|
||||
watchers.add(watcher);
|
||||
CommonHelper.newThread(String.format("Zone '%s' Watcher", info.name), true, watcher).start();
|
||||
}
|
||||
}
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params));
|
||||
launch(profile, params);
|
||||
if(watchers != null) {
|
||||
for(DirWatcher watcher : watchers) {
|
||||
watcher.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,10 +307,6 @@ public static boolean checkJVMBitsAndVersion(int minVersion, int recommendVersio
|
|||
return ok;
|
||||
}
|
||||
|
||||
private static LinkedList<Path> resolveClassPathList(Path clientDir, String... classPath) throws IOException {
|
||||
return resolveClassPathStream(clientDir, classPath).collect(Collectors.toCollection(LinkedList::new));
|
||||
}
|
||||
|
||||
private static Stream<Path> resolveClassPathStream(Path clientDir, String... classPath) throws IOException {
|
||||
Stream.Builder<Path> builder = Stream.builder();
|
||||
for (String classPathEntry : classPath) {
|
||||
|
@ -307,11 +320,41 @@ private static Stream<Path> resolveClassPathStream(Path clientDir, String... cla
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
public static Stream<Path> resolveClassPath(Path clientDir, Set<OptionalAction> actions, ClientProfile profile) throws IOException {
|
||||
private static Stream<Path> resolveZonedClassPath(Path clientDir, List<ClientLauncherProcess.ClientParams.ClientZoneInfo> zones, ClientProfile.ClientProfileLibrary... libraries) {
|
||||
Stream.Builder<Path> builder = Stream.builder();
|
||||
for(ClientProfile.ClientProfileLibrary library : libraries) {
|
||||
if(library.type == ClientProfile.ClientProfileLibrary.LibraryType.CLASSPATH) {
|
||||
Path zone = null;
|
||||
if(library.zone == null || library.zone.isEmpty()) {
|
||||
zone = clientDir;
|
||||
} else {
|
||||
for(ClientLauncherProcess.ClientParams.ClientZoneInfo info : zones) {
|
||||
if(info.name.equals(library.zone)) {
|
||||
zone = Paths.get(info.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(zone == null) {
|
||||
LogHelper.warning("Library %s not enabled, because zone %s not found", library.name, library.zone);
|
||||
continue;
|
||||
}
|
||||
Path path = zone.resolve(library.path);
|
||||
builder.accept(path);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static Stream<Path> resolveClassPath(Path clientDir, Set<OptionalAction> actions, List<ClientLauncherProcess.ClientParams.ClientZoneInfo> zones, ClientProfile profile) throws IOException {
|
||||
Stream<Path> result = resolveClassPathStream(clientDir, profile.getClassPath());
|
||||
if(profile.getLibraries() != null) {
|
||||
result = Stream.concat(result, resolveZonedClassPath(clientDir, zones, profile.getLibraries().toArray(new ClientProfile.ClientProfileLibrary[0])));
|
||||
}
|
||||
for (OptionalAction a : actions) {
|
||||
if (a instanceof OptionalActionClassPath)
|
||||
result = Stream.concat(result, resolveClassPathStream(clientDir, ((OptionalActionClassPath) a).args));
|
||||
if(a instanceof OptionalLibraryAction)
|
||||
result = Stream.concat(result, resolveZonedClassPath(clientDir, zones, ((OptionalLibraryAction) a).libraries));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -20,10 +20,14 @@
|
|||
import pro.gravit.utils.Version;
|
||||
import pro.gravit.utils.helper.*;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
@ -99,6 +103,7 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, Path javaDir, Path r
|
|||
applyClientProfile();
|
||||
}
|
||||
|
||||
|
||||
public static String getPathSeparator() {
|
||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
||||
return ";";
|
||||
|
@ -154,7 +159,7 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
|
|||
if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.AGENT) {
|
||||
processArgs.add("-javaagent:".concat(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString()));
|
||||
} else if (params.profile.getClassLoaderConfig() == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) {
|
||||
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(workDir, params.actions, params.profile).map(Path::toString).collect(Collectors.toList()));
|
||||
systemClassPath.addAll(ClientLauncherEntryPoint.resolveClassPath(workDir, params.actions, params.zones, params.profile).map(Path::toString).collect(Collectors.toList()));
|
||||
}
|
||||
if (useLegacyJavaClassPathProperty) {
|
||||
processArgs.add("-Djava.class.path=".concat(String.join(getPathSeparator(), systemClassPath)));
|
||||
|
@ -212,30 +217,6 @@ private void applyJava9Params(List<String> processArgs) {
|
|||
}
|
||||
}
|
||||
|
||||
public void runWriteParams(SocketAddress address) throws IOException {
|
||||
try (ServerSocket serverSocket = new ServerSocket()) {
|
||||
serverSocket.bind(address);
|
||||
synchronized (waitWriteParams) {
|
||||
waitWriteParams[0] = true;
|
||||
waitWriteParams.notifyAll();
|
||||
}
|
||||
Socket socket = serverSocket.accept();
|
||||
try (HOutput output = new HOutput(socket.getOutputStream())) {
|
||||
byte[] serializedMainParams = IOHelper.encode(Launcher.gsonManager.gson.toJson(params));
|
||||
output.writeByteArray(serializedMainParams, 0);
|
||||
params.clientHDir.write(output);
|
||||
params.assetHDir.write(output);
|
||||
if (params.javaHDir == null || params.javaHDir == params.assetHDir) { //OLD RUNTIME USE params.assetHDir AS NULL IN java.javaHDir
|
||||
output.writeBoolean(false);
|
||||
} else {
|
||||
output.writeBoolean(true);
|
||||
params.javaHDir.write(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessBuilderParamsWrittedEvent(this));
|
||||
}
|
||||
|
||||
public Process getProcess() {
|
||||
return process;
|
||||
}
|
||||
|
@ -289,6 +270,8 @@ public static class ClientParams {
|
|||
|
||||
public transient HashedDir javaHDir;
|
||||
|
||||
public transient List<ClientZoneInfo> zones;
|
||||
|
||||
public void addClientArgs(Collection<String> args) {
|
||||
if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0)
|
||||
addModernClientArgs(args);
|
||||
|
@ -363,6 +346,58 @@ private void addModernClientArgs(Collection<String> args) {
|
|||
}
|
||||
}
|
||||
|
||||
public void write(DataOutputStream stream) throws IOException {
|
||||
stream.writeUTF(Launcher.gsonManager.gson.toJson(this));
|
||||
if(clientHDir == null) {
|
||||
stream.writeBoolean(false);
|
||||
} else {
|
||||
stream.writeBoolean(true);
|
||||
clientHDir.write(stream);
|
||||
}
|
||||
if(assetHDir == null) {
|
||||
stream.writeBoolean(false);
|
||||
} else {
|
||||
stream.writeBoolean(true);
|
||||
assetHDir.write(stream);
|
||||
}
|
||||
if(javaHDir == null) {
|
||||
stream.writeBoolean(false);
|
||||
} else {
|
||||
stream.writeBoolean(true);
|
||||
javaHDir.write(stream);
|
||||
}
|
||||
if(zones == null) {
|
||||
stream.writeBoolean(false);
|
||||
} else {
|
||||
stream.writeBoolean(true);
|
||||
stream.writeInt(zones.size());
|
||||
for(ClientZoneInfo zone : zones) {
|
||||
zone.write(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ClientParams read(DataInputStream input) throws IOException {
|
||||
ClientParams params = Launcher.gsonManager.gson.fromJson(input.readUTF(), ClientParams.class);
|
||||
if(input.readBoolean()) {
|
||||
params.clientHDir = new HashedDir(input);
|
||||
}
|
||||
if(input.readBoolean()) {
|
||||
params.assetHDir = new HashedDir(input);
|
||||
}
|
||||
if(input.readBoolean()) {
|
||||
params.javaHDir = new HashedDir(input);
|
||||
}
|
||||
if(input.readBoolean()) {
|
||||
int length = input.readInt();
|
||||
params.zones = new ArrayList<>(length);
|
||||
for(int i=0;i<length;++i) {
|
||||
params.zones.add(new ClientZoneInfo(input));
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
public static class ClientUserProperties {
|
||||
@LauncherNetworkAPI
|
||||
public String[] skinURL;
|
||||
|
@ -373,5 +408,41 @@ public static class ClientUserProperties {
|
|||
@LauncherNetworkAPI
|
||||
public String[] cloakDigest;
|
||||
}
|
||||
|
||||
public static class ClientZoneInfo {
|
||||
public String name;
|
||||
public String path;
|
||||
public HashedDir dir;
|
||||
|
||||
public ClientZoneInfo(String name, String path, HashedDir dir) {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
public ClientZoneInfo(String name, String path) {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public ClientZoneInfo(DataInputStream input) throws IOException {
|
||||
name = input.readUTF();
|
||||
path = input.readUTF();
|
||||
if(input.readBoolean()) {
|
||||
dir = new HashedDir(input);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(DataOutputStream stream) throws IOException {
|
||||
stream.writeUTF(name);
|
||||
stream.writeUTF(path);
|
||||
if(dir == null) {
|
||||
stream.writeBoolean(false);
|
||||
} else {
|
||||
stream.writeBoolean(true);
|
||||
dir.write(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,30 +87,64 @@ public static class ClientProfileLibrary {
|
|||
public final String zone;
|
||||
public final String name;
|
||||
public final String path;
|
||||
public final String url;
|
||||
public final LibraryType type;
|
||||
|
||||
public ClientProfileLibrary(String zone, String name, String path, String url, LibraryType type) {
|
||||
this.zone = zone;
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
this.url = url;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ClientProfileLibrary(String zone, String name, String path, LibraryType type) {
|
||||
this.zone = zone;
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
this.url = null;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ClientProfileLibrary(String zone, String name, String path) {
|
||||
this.zone = zone;
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
this.url = null;
|
||||
this.type = LibraryType.CLASSPATH;
|
||||
}
|
||||
|
||||
public ClientProfileLibrary(String name, String path) {
|
||||
this.zone = "libraries";
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
this.url = null;
|
||||
this.type = LibraryType.CLASSPATH;
|
||||
}
|
||||
|
||||
public ClientProfileLibrary(String name) {
|
||||
this.zone = "libraries";
|
||||
this.name = name;
|
||||
this.path = convertMavenNameToPath(name);
|
||||
this.url = null;
|
||||
this.type = LibraryType.CLASSPATH;
|
||||
}
|
||||
|
||||
public static String convertMavenNameToPath(String name) {
|
||||
String[] mavenIdSplit = name.split(":");
|
||||
if(mavenIdSplit.length < 3) {
|
||||
throw new IllegalArgumentException(String.format("%s not valid maven library name", name));
|
||||
}
|
||||
return String.format("%s/%s/%s/%s-%s.jar", mavenIdSplit[0].replaceAll("\\.", "/"),
|
||||
mavenIdSplit[1], mavenIdSplit[2], mavenIdSplit[1], mavenIdSplit[2]);
|
||||
}
|
||||
public enum LibraryType {
|
||||
CLASSPATH, MODULEPATH, NATIVE, RUNTIME
|
||||
}
|
||||
}
|
||||
|
||||
public List<ClientProfileLibrary> getLibraries() {
|
||||
return libraries;
|
||||
}
|
||||
|
||||
public ClientProfile() {
|
||||
|
|
|
@ -77,21 +77,6 @@ public ClientProfileBuilder library(ClientProfile.ClientProfileLibrary library)
|
|||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder library(String zone, String name, String path) {
|
||||
this.libraries.add(new ClientProfile.ClientProfileLibrary(zone, name, path));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder library(String name, String path) {
|
||||
this.libraries.add(new ClientProfile.ClientProfileLibrary(name, path));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder library(String name) {
|
||||
this.libraries.add(new ClientProfile.ClientProfileLibrary(name));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientProfileBuilder setUpdateOptional(Set<OptionalFile> updateOptional) {
|
||||
this.updateOptional = updateOptional;
|
||||
return this;
|
||||
|
|
|
@ -12,6 +12,7 @@ public static void registerProviders() {
|
|||
providers.register("clientArgs", OptionalActionClientArgs.class);
|
||||
providers.register("jvmArgs", OptionalActionJvmArgs.class);
|
||||
providers.register("classpath", OptionalActionClassPath.class);
|
||||
providers.register("library", OptionalLibraryAction.class);
|
||||
registerProviders = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package pro.gravit.launcher.profiles.optional.actions;
|
||||
|
||||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
|
||||
public class OptionalLibraryAction extends OptionalAction {
|
||||
public ClientProfile.ClientProfileLibrary[] libraries;
|
||||
|
||||
public OptionalLibraryAction() {
|
||||
}
|
||||
|
||||
public OptionalLibraryAction(ClientProfile.ClientProfileLibrary[] libraries) {
|
||||
this.libraries = libraries;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,10 @@
|
|||
import pro.gravit.utils.helper.IOHelper;
|
||||
import pro.gravit.utils.helper.VerifyHelper;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -48,6 +51,27 @@ public HashedDir(HInput input) throws IOException {
|
|||
}
|
||||
}
|
||||
|
||||
public HashedDir(DataInputStream input) throws IOException {
|
||||
int entriesCount = input.readInt();
|
||||
for (int i = 0; i < entriesCount; i++) {
|
||||
String name = input.readUTF();
|
||||
|
||||
// Read entry
|
||||
HashedEntry entry;
|
||||
int type = input.readByte();
|
||||
if(type == Type.FILE.getNumber()) {
|
||||
entry = new HashedFile(input);
|
||||
} else if(type == Type.DIR.getNumber()) {
|
||||
entry = new HashedDir(input);
|
||||
} else {
|
||||
throw new AssertionError("Unsupported hashed entry type: " + type);
|
||||
}
|
||||
|
||||
// Try add entry to map
|
||||
VerifyHelper.putIfAbsent(map, name, entry, String.format("Duplicate dir entry: '%s'", name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public HashedDir(Path dir, FileNameMatcher matcher, boolean allowSymlinks, boolean digest) throws IOException {
|
||||
IOHelper.walk(dir, new HashFileVisitor(dir, matcher, allowSymlinks, digest), true);
|
||||
|
@ -314,6 +338,18 @@ public void write(HOutput output) throws IOException {
|
|||
}
|
||||
}
|
||||
|
||||
public void write(DataOutputStream output) throws IOException {
|
||||
Set<Entry<String, HashedEntry>> entries = map.entrySet();
|
||||
output.writeInt(entries.size());
|
||||
for (Entry<String, HashedEntry> mapEntry : entries) {
|
||||
output.writeUTF(mapEntry.getKey());
|
||||
// Write hashed entry
|
||||
HashedEntry entry = mapEntry.getValue();
|
||||
output.writeByte(entry.getType().getNumber());
|
||||
entry.write(output);
|
||||
}
|
||||
}
|
||||
|
||||
public void walk(CharSequence separator, WalkCallback callback) throws IOException {
|
||||
String append = "";
|
||||
walk(append, separator, callback, true);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import pro.gravit.launcher.serialize.stream.EnumSerializer;
|
||||
import pro.gravit.launcher.serialize.stream.StreamObject;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class HashedEntry extends StreamObject {
|
||||
|
@ -16,6 +17,8 @@ public abstract class HashedEntry extends StreamObject {
|
|||
|
||||
public abstract long size();
|
||||
|
||||
public abstract void write(DataOutputStream stream) throws IOException;
|
||||
|
||||
public enum Type implements EnumSerializer.Itf {
|
||||
DIR(1), FILE(2);
|
||||
private static final EnumSerializer<Type> SERIALIZER = new EnumSerializer<>(Type.class);
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
import pro.gravit.utils.helper.SecurityHelper.DigestAlgorithm;
|
||||
import pro.gravit.utils.helper.VerifyHelper;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
|
@ -26,6 +28,16 @@ public HashedFile(HInput input) throws IOException {
|
|||
this(input.readVarLong(), input.readBoolean() ? input.readByteArray(-DIGEST_ALGO.bytes) : null);
|
||||
}
|
||||
|
||||
public HashedFile(DataInputStream input) throws IOException {
|
||||
this(input.readLong(), input.readBoolean() ? readDigest(input) : null);
|
||||
}
|
||||
|
||||
private static byte[] readDigest(DataInputStream input) throws IOException {
|
||||
byte[] bytes = new byte[input.readInt()];
|
||||
input.readFully(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
public HashedFile(long size, byte[] digest) {
|
||||
this.size = VerifyHelper.verifyLong(size, VerifyHelper.L_NOT_NEGATIVE, "Illegal size: " + size);
|
||||
|
@ -69,6 +81,18 @@ public long size() {
|
|||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(DataOutputStream stream) throws IOException {
|
||||
stream.writeLong(size);
|
||||
if(digest == null) {
|
||||
stream.writeBoolean(false);
|
||||
} else {
|
||||
stream.writeBoolean(true);
|
||||
stream.writeInt(digest.length);
|
||||
stream.write(digest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(HOutput output) throws IOException {
|
||||
output.writeVarLong(size);
|
||||
|
|
Loading…
Reference in a new issue