diff --git a/LaunchServer/build.gradle b/LaunchServer/build.gradle index 4889e6a6..c6702389 100644 --- a/LaunchServer/build.gradle +++ b/LaunchServer/build.gradle @@ -44,9 +44,10 @@ pack project(':libLauncher') bundle 'org.fusesource.jansi:jansi:1.17.1' bundle 'commons-io:commons-io:2.6' bundle 'commons-codec:commons-codec:1.11' - bundle 'org.javassist:javassist:3.23.1-GA' + bundle 'org.javassist:javassist:3.24.1-GA' bundle 'io.netty:netty-all:4.1.32.Final' - + bundle 'org.kohsuke:github-api:1.95' + bundle 'org.slf4j:slf4j-simple:1.7.25' bundle 'org.slf4j:slf4j-api:1.7.25' diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/ru/gravit/launchserver/LaunchServer.java index ced4b54a..f1552b8d 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/LaunchServer.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/LaunchServer.java @@ -111,6 +111,7 @@ public static final class Config { public String startScript; + public boolean updatesNotify = true; // Defaultly to true public String getAddress() { return address; @@ -289,6 +290,8 @@ public static void main(String... args) throws Throwable { public volatile Map> updatesDirMap; + public final Updater updater; + public static Gson gson; public static GsonBuilder gsonBuilder; @@ -342,8 +345,8 @@ public LaunchServer(Path dir, String[] args) throws IOException, InvalidKeySpecE publicKey = (RSAPublicKey) pair.getPublic(); privateKey = (RSAPrivateKey) pair.getPrivate(); - // Write key pair files - LogHelper.info("Writing RSA keypair files"); + // Write key pair list + LogHelper.info("Writing RSA keypair list"); IOHelper.write(publicKeyFile, publicKey.getEncoded()); IOHelper.write(privateKeyFile, privateKey.getEncoded()); } @@ -439,6 +442,8 @@ public LaunchServer(Path dir, String[] args) throws IOException, InvalidKeySpecE // post init modules modulesManager.postInitModules(); + // start updater + this.updater = new Updater(this); } public static void initGson() { diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/Updater.java b/LaunchServer/src/main/java/ru/gravit/launchserver/Updater.java new file mode 100644 index 00000000..7136de39 --- /dev/null +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/Updater.java @@ -0,0 +1,95 @@ +package ru.gravit.launchserver; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.kohsuke.github.GHRelease; +import org.kohsuke.github.GHRepository; +import org.kohsuke.github.GitHub; + +import ru.gravit.launcher.Launcher; +import ru.gravit.utils.Version; +import ru.gravit.utils.Version.Type; +import ru.gravit.utils.helper.LogHelper; + +public class Updater extends TimerTask { + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss", Locale.US); + private static final long period = 1000*3600; + private static final Version VERSION = Launcher.getVersion(); + private final Timer taskPool; + private final GHRepository gravitLauncher; + private Version parent = VERSION; + + public Updater(LaunchServer srv) { + this.taskPool = new Timer("Updater thread", true); + + GHRepository gravitLauncherTmp = null; + try { + gravitLauncherTmp = GitHub.connectAnonymously().getOrganization("GravitLauncher").getRepository("Launcher"); + } catch (Throwable e) { + LogHelper.error(e); + } + this.gravitLauncher = gravitLauncherTmp; + run(); + if (srv.config.updatesNotify) taskPool.schedule(this, new Date(System.currentTimeMillis()+period), period); + } + + @Override + public void run() { + try { + GHRelease rel = gravitLauncher.getLatestRelease(); + Version relV = parseVer(rel.getTagName()); + if (relV == null) { + LogHelper.debug("Updater: parsing version error."); + return; + } + if (!parent.equals(relV)) parent = relV; + if (VERSION.major >= relV.major && VERSION.minor >= relV.minor + && VERSION.patch >= relV.patch && VERSION.build >= relV.build) return; + if (relV.release.equals(Type.STABLE) || relV.release.equals(Type.LTS)) { + LogHelper.warning("New %s release: %s", relV.getReleaseStatus(), relV.getVersionString()); + LogHelper.warning("You can download it: " + rel.getHtmlUrl().toString()); + LogHelper.warning("It`s published at: " + DATE_TIME_FORMATTER.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(rel.getPublished_at().getTime()), ZoneId.systemDefault()))); + } else { + LogHelper.debug("New %s release: %s", relV.getReleaseStatus(), relV.getVersionString()); + LogHelper.debug("You can download it: " + rel.getHtmlUrl()); + LogHelper.debug("It`s published at: " + DATE_TIME_FORMATTER.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(rel.getPublished_at().getTime()), ZoneId.systemDefault()))); + } + } catch (Throwable e) { + LogHelper.error(e); + } + } + + private static final Pattern startingVerPattern = Pattern.compile("\\d+\\.\\d+\\.\\d+"); + private static final Pattern pointPatternSpltitter = Pattern.compile("\\."); + + private static Version parseVer(String relS) { + Matcher verMatcher = startingVerPattern.matcher(relS); + if (!verMatcher.find()) return VERSION; + String[] ver = pointPatternSpltitter.split(relS.substring(verMatcher.start(), verMatcher.end())); + if (ver.length < 3) return VERSION; + return new Version(Integer.parseInt(ver[0]), Integer.parseInt(ver[1]), + Integer.parseInt(ver[2]), ver.length > 3 ? Integer.parseInt(ver[3]) : 0, findRelType(relS.substring(verMatcher.end()+1))); + } + + private static Type findRelType(String substring) { + if (substring.length() < 3 || substring.isEmpty()) return Type.UNKNOWN; + String tS = substring; + if (tS.startsWith("-")) tS = tS.substring(1); + final String wrk = tS.toLowerCase(Locale.ENGLISH); + final AtomicReference t = new AtomicReference(Type.UNKNOWN); + Type.unModTypes.forEach((s, type) -> { + if (wrk.startsWith(s)) t.set(type); + }); + return t.get(); + } +} diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/auth/permissions/JsonLongFilePermissionsHandler.java b/LaunchServer/src/main/java/ru/gravit/launchserver/auth/permissions/JsonLongFilePermissionsHandler.java new file mode 100644 index 00000000..cb6aa234 --- /dev/null +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/auth/permissions/JsonLongFilePermissionsHandler.java @@ -0,0 +1,65 @@ +package ru.gravit.launchserver.auth.permissions; + +import com.google.gson.reflect.TypeToken; +import ru.gravit.launcher.ClientPermissions; +import ru.gravit.launcher.Launcher; +import ru.gravit.launchserver.Reloadable; +import ru.gravit.utils.helper.IOHelper; +import ru.gravit.utils.helper.LogHelper; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.Type; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +public class JsonLongFilePermissionsHandler extends PermissionsHandler implements Reloadable { + public String filename = "permissions.json"; + public long defaultPerms = 0L; + public static Map map; + + @Override + public void reload() { + map.clear(); + Path path = Paths.get(filename); + Type type = new TypeToken>() { + }.getType(); + try (Reader reader = IOHelper.newReader(path)) { + map = Launcher.gson.fromJson(reader, type); + } catch (IOException e) { + LogHelper.error(e); + } + } + + public static class Enity { + public String username; + public ClientPermissions permissions; + } + + @Override + public ClientPermissions getPermissions(String username) { + return new ClientPermissions(map.getOrDefault(username, defaultPerms)); + } + + public JsonLongFilePermissionsHandler() { + Type type = new TypeToken>() { + }.getType(); + Path path = Paths.get(filename); + if (!IOHelper.exists(path)) { + map = new HashMap<>(); + try (Writer writer = IOHelper.newWriter(path)) { + Launcher.gson.toJson(map, writer); + } catch (IOException e) { + LogHelper.error(e); + } + } + try (Reader reader = IOHelper.newReader(path)) { + map = Launcher.gson.fromJson(reader, type); + } catch (IOException e) { + LogHelper.error(e); + } + } +} diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/auth/permissions/PermissionsHandler.java b/LaunchServer/src/main/java/ru/gravit/launchserver/auth/permissions/PermissionsHandler.java index db00382a..d3d379e9 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/auth/permissions/PermissionsHandler.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/auth/permissions/PermissionsHandler.java @@ -31,6 +31,7 @@ public static String getHandlerName(Class clazz) { public static void registerHandlers() { if (!registredHandl) { registerHandler("json", JsonFilePermissionsHandler.class); + registerHandler("json-long", JsonLongFilePermissionsHandler.class); registerHandler("config", ConfigPermissionsHandler.class); registerHandler("default", DefaultPermissionsHandler.class); registredHandl = true; diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/auth/provider/MySQLAuthProvider.java b/LaunchServer/src/main/java/ru/gravit/launchserver/auth/provider/MySQLAuthProvider.java index cb817ba5..3a3c5cfa 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/auth/provider/MySQLAuthProvider.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/auth/provider/MySQLAuthProvider.java @@ -1,6 +1,7 @@ package ru.gravit.launchserver.auth.provider; import ru.gravit.launcher.ClientPermissions; +import ru.gravit.launchserver.LaunchServer; import ru.gravit.launchserver.auth.AuthException; import ru.gravit.launchserver.auth.MySQLSourceConfig; import ru.gravit.utils.helper.CommonHelper; @@ -35,7 +36,7 @@ public AuthProviderResult auth(String login, String password, String ip) throws // Execute SQL query s.setQueryTimeout(MySQLSourceConfig.TIMEOUT); try (ResultSet set = s.executeQuery()) { - return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), usePermission ? new ClientPermissions(set.getLong(2)) : new ClientPermissions()) : authError(message); + return set.next() ? new AuthProviderResult(set.getString(1), SecurityHelper.randomStringToken(), usePermission ? new ClientPermissions(set.getLong(2)) : LaunchServer.server.config.permissionsHandler.getPermissions(set.getString(1))) : authError(message); } } diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/auth/provider/RequestAuthProvider.java b/LaunchServer/src/main/java/ru/gravit/launchserver/auth/provider/RequestAuthProvider.java index f11ed82e..83694199 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/auth/provider/RequestAuthProvider.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/auth/provider/RequestAuthProvider.java @@ -1,6 +1,7 @@ package ru.gravit.launchserver.auth.provider; import ru.gravit.launcher.ClientPermissions; +import ru.gravit.launchserver.LaunchServer; import ru.gravit.utils.helper.CommonHelper; import ru.gravit.utils.helper.IOHelper; import ru.gravit.utils.helper.LogHelper; @@ -31,7 +32,7 @@ public AuthProviderResult auth(String login, String password, String ip) throws // Match username Matcher matcher = pattern.matcher(currentResponse); return matcher.matches() && matcher.groupCount() >= 1 ? - new AuthProviderResult(matcher.group("username"), SecurityHelper.randomStringToken(), usePermission ? new ClientPermissions(Long.getLong(matcher.group("permission"))) : new ClientPermissions()) : + new AuthProviderResult(matcher.group("username"), SecurityHelper.randomStringToken(), usePermission ? new ClientPermissions(Long.getLong(matcher.group("permission"))) : LaunchServer.server.config.permissionsHandler.getPermissions(login)) : authError(currentResponse); } diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/binary/tasks/PrepareBuildTask.java b/LaunchServer/src/main/java/ru/gravit/launchserver/binary/tasks/PrepareBuildTask.java index 1e2ae3df..dc08fe0b 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/binary/tasks/PrepareBuildTask.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/binary/tasks/PrepareBuildTask.java @@ -42,7 +42,7 @@ public boolean allowDelete() { } public void tryUnpack() throws IOException { - LogHelper.info("Unpacking launcher native guard files and runtime"); + LogHelper.info("Unpacking launcher native guard list and runtime"); UnpackHelper.unpackZipNoCheck("guard.zip", server.launcherBinary.guardDir); UnpackHelper.unpackZipNoCheck("runtime.zip", server.launcherBinary.runtimeDir); } diff --git a/LaunchServer/src/main/resources/ru/gravit/launchserver/defaults/config.cfg b/LaunchServer/src/main/resources/ru/gravit/launchserver/defaults/config.cfg index 85f9a164..c0eaf34b 100644 --- a/LaunchServer/src/main/resources/ru/gravit/launchserver/defaults/config.cfg +++ b/LaunchServer/src/main/resources/ru/gravit/launchserver/defaults/config.cfg @@ -56,6 +56,7 @@ "isDownloadJava": false, "isWarningMissArchJava": true, "enabledProGuard": true, + "updatesNotify": true, "stripLineNumbers": true, "deleteTempFiles": true, "startScript": ".\\start.sh" diff --git a/Launcher/runtime/dialog/overlay/options/options.js b/Launcher/runtime/dialog/overlay/options/options.js index 282a63fa..336c99b8 100644 --- a/Launcher/runtime/dialog/overlay/options/options.js +++ b/Launcher/runtime/dialog/overlay/options/options.js @@ -43,15 +43,16 @@ var options = { for(var j = 0; j < listSize; j++) { var mark = input.readBoolean(); + var modType = OptionalFile.readType(input); var modFile = input.readString(0); if(mark) { - profile.markOptional(modFile); + profile.markOptional(modFile,modType); LogHelper.debug("Load options %s marked",modFile); } else { - profile.unmarkOptional(modFile); + profile.unmarkOptional(modFile,modType); LogHelper.debug("Load options %s unmarked",modFile); } } @@ -69,7 +70,8 @@ var options = { output.writeInt(profile.getSortIndex()); list.forEach(function(modFile,j,arr2) { output.writeBoolean(modFile.mark); - output.writeString(modFile.file, 0); + modFile.writeType(output); + output.writeString(modFile.name, 0); }); }); }, @@ -96,16 +98,19 @@ var options = { var profile = profilesList[serverHolder.old]; var list = profile.getOptional(); var checkBoxList = new java.util.ArrayList; - var dModsIds = []; - list.forEach(function(modFile) { - dModsIds.push(modFile.string); - - var modName = modFile.file, modDescription = "", subLevel = 1; - if(modFile.isAdminOnly && !loginData.permissions.canAdmin) + var modName = modFile.name, modDescription = "", subLevel = 1; + if(!modFile.isVisible) + { + LogHelper.debug("optionalMod %s hidden",modFile.name); return; - if(modFile.name != null)//Есть ли у модификации имя? - modName = modFile.name; + } + + if(modFile.permissions != 0 && ((loginData.permissions.toLong() & modFile.permissions) != 0)) + { + LogHelper.debug("optionalMod %s permissions deny",modFile.name); + return; + } if(modFile.info != null) //Есть ли описание? modDescription = modFile.info; if(modFile.subTreeLevel != null && modFile.subTreeLevel > 1)//Это суб-модификация? @@ -121,18 +126,17 @@ var options = { var isSelected = event.getSource().isSelected(); if(isSelected) { - profile.markOptional(modFile.file); - LogHelper.debug("Selected mod %s", modFile.file); + profile.markOptional(modFile); + LogHelper.debug("Selected mod %s", modFile.name); } else { - profile.unmarkOptional(modFile.file); - LogHelper.debug("Unselected mod %s", modFile.file); + profile.unmarkOptional(modFile); + LogHelper.debug("Unselected mod %s", modFile.name); } options.update(); }); checkBoxList.add(testMod); - if(modDescription != "") { //Добавляем описание? textDescr = new javafx.scene.text.Text(modDescription); if(subLevel > 1) { diff --git a/Launcher/runtime/engine/api.js b/Launcher/runtime/engine/api.js index f423687d..d7cbcae5 100644 --- a/Launcher/runtime/engine/api.js +++ b/Launcher/runtime/engine/api.js @@ -44,6 +44,7 @@ var StreamObjectAdapter = StreamObjectAdapterClass.static; var SignedBytesHolder = SignedBytesHolderClass.static; var SignedObjectHolder = SignedObjectHolderClass.static; var EnumSerializer = EnumSerializerClass.static; +var OptionalFile = OptionalFileClass.static; // Helper class API imports var CommonHelper = CommonHelperClass.static; diff --git a/Launcher/src/main/java/ru/gravit/launcher/client/ClientLauncher.java b/Launcher/src/main/java/ru/gravit/launcher/client/ClientLauncher.java index fa950ead..84fe9162 100644 --- a/Launcher/src/main/java/ru/gravit/launcher/client/ClientLauncher.java +++ b/Launcher/src/main/java/ru/gravit/launcher/client/ClientLauncher.java @@ -32,7 +32,6 @@ import java.net.URL; import java.nio.file.FileVisitResult; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFilePermission; @@ -68,12 +67,6 @@ public static final class Params extends StreamObject { @LauncherAPI public final PlayerProfile pp; @LauncherAPI - public final Set updateOptional; - @LauncherAPI - public final Set optionalClientArgs; - @LauncherAPI - public final Set optionalClassPath; - @LauncherAPI public final String accessToken; @LauncherAPI public final boolean autoEnter; @@ -93,18 +86,6 @@ public static final class Params extends StreamObject { public Params(byte[] launcherDigest, Path assetDir, Path clientDir, PlayerProfile pp, String accessToken, boolean autoEnter, boolean fullScreen, int ram, int width, int height) { this.launcherDigest = launcherDigest.clone(); - this.updateOptional = new HashSet<>(); - this.optionalClientArgs = new HashSet<>(); - this.optionalClassPath = new HashSet<>(); - for (ClientProfile.OptionalFile s : Launcher.profile.getOptional()) { - if (s.mark) updateOptional.add(s); - } - for (ClientProfile.OptionalArgs s : Launcher.profile.getOptionalClientArgs()) { - if (s.mark) optionalClientArgs.add(s); - } - for (ClientProfile.OptionalArgs s : Launcher.profile.getOptionalClassPath()) { - if (s.mark) optionalClassPath.add(s); - } // Client paths this.assetDir = assetDir; this.clientDir = clientDir; @@ -126,31 +107,6 @@ public Params(HInput input) throws Exception { // Client paths assetDir = IOHelper.toPath(input.readString(0)); clientDir = IOHelper.toPath(input.readString(0)); - updateOptional = new HashSet<>(); - optionalClientArgs = new HashSet<>(); - optionalClassPath = new HashSet<>(); - int len = input.readLength(128); - for (int i = 0; i < len; ++i) { - String file = input.readString(512); - boolean mark = input.readBoolean(); - updateOptional.add(new ClientProfile.OptionalFile(file, mark)); - } - len = input.readLength(256); - for (int i = 0; i < len; ++i) { - int len2 = input.readLength(16); - boolean mark = input.readBoolean(); - String[] optArgs = new String[len]; - for (int j = 0; j < len2; ++j) optArgs[j] = input.readString(512); - optionalClientArgs.add(new ClientProfile.OptionalArgs(optArgs, mark)); - } - len = input.readLength(256); - for (int i = 0; i < len; ++i) { - int len2 = input.readLength(16); - boolean mark = input.readBoolean(); - String[] optArgs = new String[len]; - for (int j = 0; j < len2; ++j) optArgs[j] = input.readString(512); - optionalClassPath.add(new ClientProfile.OptionalArgs(optArgs, mark)); - } // Client params pp = new PlayerProfile(input); byte[] encryptedAccessToken = input.readByteArray(SecurityHelper.CRYPTO_MAX_LENGTH); @@ -170,24 +126,6 @@ public void write(HOutput output) throws IOException { // Client paths output.writeString(assetDir.toString(), 0); output.writeString(clientDir.toString(), 0); - output.writeLength(updateOptional.size(), 128); - for (ClientProfile.OptionalFile s : updateOptional) { - output.writeString(s.file, 512); - output.writeBoolean(s.mark); - } - output.writeLength(optionalClientArgs.size(), 256); - for (ClientProfile.OptionalArgs s : optionalClientArgs) { - output.writeLength(s.args.length, 16); - output.writeBoolean(s.mark); - for (String f : s.args) output.writeString(f, 512); - } - output.writeLength(optionalClassPath.size(), 256); - for (ClientProfile.OptionalArgs s : optionalClassPath) { - output.writeLength(s.args.length, 16); - output.writeBoolean(s.mark); - for (String f : s.args) output.writeString(f, 512); - } - // Client params pp.write(output); try { output.writeByteArray(SecurityHelper.encrypt(Launcher.getConfig().secretKeyClient.getBytes(), accessToken.getBytes()), SecurityHelper.CRYPTO_MAX_LENGTH); @@ -284,9 +222,7 @@ private static void addClientArgs(Collection args, ClientProfile profile Collections.addAll(args, "--server", profile.getServerAddress()); Collections.addAll(args, "--port", Integer.toString(profile.getServerPort())); } - for (ClientProfile.OptionalArgs optionalArgs : params.optionalClientArgs) { - if (optionalArgs.mark) Collections.addAll(args, optionalArgs.args); - } + profile.pushOptionalClientArgs(args); // Add window size args if (params.fullScreen) Collections.addAll(args, "--fullscreen", Boolean.toString(true)); @@ -425,11 +361,7 @@ public static Process launch( // Add classpath and main class String pathLauncher = IOHelper.getCodeSource(ClientLauncher.class).toString(); Collections.addAll(args, profile.getJvmArgs()); - if (profile.getOptionalJVMArgs() != null) { - for (ClientProfile.OptionalArgs addArgs : profile.getOptionalJVMArgs()) { - if (addArgs.mark) Collections.addAll(args, addArgs.args); - } - } + profile.pushOptionalJvmArgs(args); Collections.addAll(args, "-Djava.library.path=".concat(params.clientDir.resolve(NATIVES_DIR).toString())); // Add Native Path Collections.addAll(args, "-javaagent:".concat(pathLauncher)); if (wrapper) @@ -512,14 +444,12 @@ public static void main(String... args) throws Throwable { for (Path classpathURL : classPath) { LauncherAgent.addJVMClassPath(classpathURL.toAbsolutePath().toString()); } - for (ClientProfile.OptionalArgs optionalArgs : params.optionalClassPath) { - if (!optionalArgs.mark) continue; - LinkedList optionalClassPath = resolveClassPathList(params.clientDir, optionalArgs.args); + profile.pushOptionalClassPath(cp -> { + LinkedList optionalClassPath = resolveClassPathList(params.clientDir, cp); for (Path classpathURL : optionalClassPath) { LauncherAgent.addJVMClassPath(classpathURL.toAbsolutePath().toString()); } - } - + }); URL[] classpathurls = resolveClassPath(params.clientDir, profile.getClassPath()); classLoader = new PublicURLClassLoader(classpathurls, ClassLoader.getSystemClassLoader()); Thread.currentThread().setContextClassLoader(classLoader); @@ -535,10 +465,11 @@ public static void main(String... args) throws Throwable { // Verify current state of all dirs //verifyHDir(IOHelper.JVM_DIR, jvmHDir.object, null, digest); HashedDir hdir = clientHDir.object; - for (ClientProfile.OptionalFile s : Launcher.profile.getOptional()) { - if (params.updateOptional.contains(s)) s.mark = true; - else hdir.removeR(s.file); - } + //for (OptionalFile s : Launcher.profile.getOptional()) { + // if (params.updateOptional.contains(s)) s.mark = true; + // else hdir.removeR(s.file); + //} + Launcher.profile.pushOptionalFile(hdir,false); verifyHDir(params.assetDir, assetHDir.object, assetMatcher, digest); verifyHDir(params.clientDir, hdir, clientMatcher, digest); Launcher.modulesManager.postInitModules(); @@ -574,10 +505,11 @@ public void launchLocal(SignedObjectHolder assetHDir, SignedObjectHol // Verify current state of all dirs //verifyHDir(IOHelper.JVM_DIR, jvmHDir.object, null, digest); HashedDir hdir = clientHDir.object; - for (ClientProfile.OptionalFile s : Launcher.profile.getOptional()) { - if (params.updateOptional.contains(s)) s.mark = true; - else hdir.removeR(s.file); - } + //for (OptionalFile s : Launcher.profile.getOptional()) { + // if (params.updateOptional.contains(s)) s.mark = true; + // else hdir.removeR(s.file); + //} + Launcher.profile.pushOptionalFile(hdir,false); verifyHDir(params.assetDir, assetHDir.object, assetMatcher, digest); verifyHDir(params.clientDir, hdir, clientMatcher, digest); Launcher.modulesManager.postInitModules(); diff --git a/Launcher/src/main/java/ru/gravit/launcher/client/LauncherSettings.java b/Launcher/src/main/java/ru/gravit/launcher/client/LauncherSettings.java index 9b2f4e54..1921204c 100644 --- a/Launcher/src/main/java/ru/gravit/launcher/client/LauncherSettings.java +++ b/Launcher/src/main/java/ru/gravit/launcher/client/LauncherSettings.java @@ -21,7 +21,7 @@ import java.util.Map; public class LauncherSettings { - public static int settingsMagic = 0xc0de8; + public static int settingsMagic = 0xc0de9; @LauncherAPI public Path file = DirBridge.dir.resolve("settings.bin"); @LauncherAPI diff --git a/Launcher/src/main/java/ru/gravit/launcher/guard/LauncherJavaGuard.java b/Launcher/src/main/java/ru/gravit/launcher/guard/LauncherJavaGuard.java index 06659f42..d48d406c 100644 --- a/Launcher/src/main/java/ru/gravit/launcher/guard/LauncherJavaGuard.java +++ b/Launcher/src/main/java/ru/gravit/launcher/guard/LauncherJavaGuard.java @@ -16,7 +16,7 @@ public String getName() { @Override public Path getJavaBinPath() { if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) - return ClientLauncher.getJavaBinPath(); + return IOHelper.resolveJavaBin(ClientLauncher.getJavaBinPath()); else return IOHelper.resolveJavaBin(Paths.get(System.getProperty("java.home"))); } diff --git a/Launcher/src/main/java/ru/gravit/launcher/guard/LauncherWrapperGuard.java b/Launcher/src/main/java/ru/gravit/launcher/guard/LauncherWrapperGuard.java index a244196d..42da5172 100644 --- a/Launcher/src/main/java/ru/gravit/launcher/guard/LauncherWrapperGuard.java +++ b/Launcher/src/main/java/ru/gravit/launcher/guard/LauncherWrapperGuard.java @@ -1,5 +1,6 @@ package ru.gravit.launcher.guard; +import ru.gravit.launcher.Launcher; import ru.gravit.launcher.client.DirBridge; import ru.gravit.utils.helper.IOHelper; import ru.gravit.utils.helper.JVMHelper; @@ -27,7 +28,10 @@ public Path getJavaBinPath() { @Override public void init(boolean clientInstance) { try { - UnpackHelper.unpack(IOHelper.getResourceURL(JVMHelper.JVM_BITS == 64 ? "wrapper64.exe" : "wrapper32.exe"),DirBridge.getGuardDir()); + String wrapperName = JVMHelper.JVM_BITS == 64 ? "wrapper64.exe" : "wrapper32.exe"; + String antiInjectName = JVMHelper.JVM_BITS == 64 ? "AntiInject64.dll" : "AntiInject32.dll"; + UnpackHelper.unpack(Launcher.getResourceURL(wrapperName, "guard"),DirBridge.getGuardDir().resolve(wrapperName)); + UnpackHelper.unpack(Launcher.getResourceURL(antiInjectName, "guard"),DirBridge.getGuardDir().resolve(antiInjectName)); } catch (IOException e) { throw new SecurityException(e); } diff --git a/Launcher/src/main/java/ru/gravit/launcher/gui/JSRuntimeProvider.java b/Launcher/src/main/java/ru/gravit/launcher/gui/JSRuntimeProvider.java index bf8ec25f..fc99a3c0 100644 --- a/Launcher/src/main/java/ru/gravit/launcher/gui/JSRuntimeProvider.java +++ b/Launcher/src/main/java/ru/gravit/launcher/gui/JSRuntimeProvider.java @@ -18,6 +18,7 @@ import ru.gravit.launcher.profiles.ClientProfile; import ru.gravit.launcher.profiles.PlayerProfile; import ru.gravit.launcher.profiles.Texture; +import ru.gravit.launcher.profiles.optional.OptionalFile; import ru.gravit.launcher.request.*; import ru.gravit.launcher.request.auth.AuthRequest; import ru.gravit.launcher.request.auth.CheckServerRequest; @@ -95,6 +96,7 @@ public static void addLauncherClassBindings(Map bindings) { bindings.put("SignedBytesHolderClass", SignedBytesHolder.class); bindings.put("SignedObjectHolderClass", SignedObjectHolder.class); bindings.put("EnumSerializerClass", EnumSerializer.class); + bindings.put("OptionalFileClass", OptionalFile.class); // Set helper class bindings bindings.put("CommonHelperClass", CommonHelper.class); diff --git a/LauncherAPI/src/main/java/ru/gravit/launcher/request/update/UpdateRequest.java b/LauncherAPI/src/main/java/ru/gravit/launcher/request/update/UpdateRequest.java index 57d72295..5d065966 100644 --- a/LauncherAPI/src/main/java/ru/gravit/launcher/request/update/UpdateRequest.java +++ b/LauncherAPI/src/main/java/ru/gravit/launcher/request/update/UpdateRequest.java @@ -224,7 +224,7 @@ private void deleteExtraDir(Path subDir, HashedDir subHDir, boolean flag) throws String name = mapEntry.getKey(); Path path = subDir.resolve(name); - // Delete files and dirs based on type + // Delete list and dirs based on type HashedEntry entry = mapEntry.getValue(); HashedEntry.Type entryType = entry.getType(); switch (entryType) { @@ -308,7 +308,7 @@ protected SignedObjectHolder requestDo(HInput input, HOutput output) // Get diff between local and remote dir SignedObjectHolder remoteHDirHolder = new SignedObjectHolder<>(input, config.publicKey, HashedDir::new); HashedDir hackHackedDir = remoteHDirHolder.object; - Launcher.profile.pushOptional(hackHackedDir, !Launcher.profile.isUpdateFastCheck()); + Launcher.profile.pushOptionalFile(hackHackedDir, !Launcher.profile.isUpdateFastCheck()); HashedDir.Diff diff = hackHackedDir.diff(localDir, matcher); totalSize = diff.mismatch.size(); boolean compress = input.readBoolean(); diff --git a/ServerWrapper/src/main/java/ru/gravit/launcher/server/ServerAgent.java b/ServerWrapper/src/main/java/ru/gravit/launcher/server/ServerAgent.java index 607cca82..fbcb6b23 100644 --- a/ServerWrapper/src/main/java/ru/gravit/launcher/server/ServerAgent.java +++ b/ServerWrapper/src/main/java/ru/gravit/launcher/server/ServerAgent.java @@ -10,7 +10,6 @@ import java.lang.invoke.MethodType; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; -import java.util.Collections; import java.util.jar.JarFile; public class ServerAgent { diff --git a/libLauncher/src/main/java/ru/gravit/launcher/ClientPermissions.java b/libLauncher/src/main/java/ru/gravit/launcher/ClientPermissions.java index 9ab91c20..c13828a6 100644 --- a/libLauncher/src/main/java/ru/gravit/launcher/ClientPermissions.java +++ b/libLauncher/src/main/java/ru/gravit/launcher/ClientPermissions.java @@ -8,23 +8,55 @@ public class ClientPermissions { public static final ClientPermissions DEFAULT = new ClientPermissions(); @LauncherAPI - public boolean canAdmin; + public boolean canAdmin = false; @LauncherAPI - public boolean canServer; + public boolean canServer = false; + @LauncherAPI + public boolean canUSR1 = false; + @LauncherAPI + public boolean canUSR2 = false; + @LauncherAPI + public boolean canUSR3 = false; + @LauncherAPI + public boolean canBot = false; public ClientPermissions(HInput input) throws IOException { canAdmin = input.readBoolean(); canServer = input.readBoolean(); + canUSR1 = input.readBoolean(); + canUSR2 = input.readBoolean(); + canUSR3 = input.readBoolean(); + canBot = input.readBoolean(); } public ClientPermissions() { canAdmin = false; canServer = false; + canUSR1 = false; + canUSR2 = false; + canUSR3 = false; + canBot = false; } public ClientPermissions(long data) { canAdmin = (data & (1)) != 0; canServer = (data & (1 << 1)) != 0; + canUSR1 = (data & (1 << 2)) != 0; + canUSR2 = (data & (1 << 3)) != 0; + canUSR3 = (data & (1 << 4)) != 0; + canBot = (data & (1 << 5)) != 0; + } + @LauncherAPI + public long toLong() + { + long result = 0; + result |= canAdmin ? 0 : 1; + result |= canServer ? 0 : (1 << 1); + result |= canUSR1 ? 0 : (1 << 2); + result |= canUSR2 ? 0 : (1 << 3); + result |= canUSR3 ? 0 : (1 << 4); + result |= canBot ? 0 : (1 << 5); + return result; } public static ClientPermissions getSuperuserAccount() { @@ -37,5 +69,9 @@ public static ClientPermissions getSuperuserAccount() { public void write(HOutput output) throws IOException { output.writeBoolean(canAdmin); output.writeBoolean(canServer); + output.writeBoolean(canUSR1); + output.writeBoolean(canUSR2); + output.writeBoolean(canUSR3); + output.writeBoolean(canBot); } } diff --git a/libLauncher/src/main/java/ru/gravit/launcher/Launcher.java b/libLauncher/src/main/java/ru/gravit/launcher/Launcher.java index 7a245e8f..a01a817c 100644 --- a/libLauncher/src/main/java/ru/gravit/launcher/Launcher.java +++ b/libLauncher/src/main/java/ru/gravit/launcher/Launcher.java @@ -59,8 +59,8 @@ public final class Launcher { private static final Pattern UUID_PATTERN = Pattern.compile("-", Pattern.LITERAL); public static final int MAJOR = 4; - public static final int MINOR = 2; - public static final int PATCH = 2; + public static final int MINOR = 3; + public static final int PATCH = 0; public static final int BUILD = 1; public static final Version.Type RELEASE = Version.Type.STABLE; public static GsonBuilder gsonBuilder; diff --git a/libLauncher/src/main/java/ru/gravit/launcher/profiles/ClientProfile.java b/libLauncher/src/main/java/ru/gravit/launcher/profiles/ClientProfile.java index b5d6476c..8de9245a 100644 --- a/libLauncher/src/main/java/ru/gravit/launcher/profiles/ClientProfile.java +++ b/libLauncher/src/main/java/ru/gravit/launcher/profiles/ClientProfile.java @@ -3,9 +3,12 @@ import ru.gravit.launcher.LauncherAPI; import ru.gravit.launcher.hasher.FileNameMatcher; import ru.gravit.launcher.hasher.HashedDir; +import ru.gravit.launcher.profiles.optional.OptionalFile; +import ru.gravit.launcher.profiles.optional.OptionalType; import ru.gravit.utils.helper.IOHelper; import ru.gravit.utils.helper.VerifyHelper; +import java.io.IOException; import java.net.InetSocketAddress; import java.util.*; @@ -79,66 +82,6 @@ public String toString() { @LauncherAPI private int serverPort; - public static class OptionalFile { - @LauncherAPI - public String file; - @LauncherAPI - public boolean mark; - @LauncherAPI - public String name; - @LauncherAPI - public String info; - @LauncherAPI - public String[] dependenciesFile; - @LauncherAPI - public String[] conflictFile; - @LauncherAPI - public transient OptionalFile[] dependencies; - @LauncherAPI - public transient OptionalFile[] conflict; - @LauncherAPI - public int subTreeLevel = 1; - @LauncherAPI - public boolean isAdminOnly = false; - @LauncherAPI - public transient Set dependenciesCount; - - public OptionalFile(String file, boolean mark) { - this.file = file; - this.mark = mark; - } - - public OptionalFile(String file) { - this.file = file; - this.mark = false; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - OptionalFile that = (OptionalFile) o; - return Objects.equals(file, that.file); - } - - @Override - public int hashCode() { - return Objects.hash(file); - } - } - - public static class OptionalArgs { - @LauncherAPI - public boolean mark; - @LauncherAPI - public String[] args; - - public OptionalArgs(String[] args, boolean mark) { - this.mark = mark; - this.args = args; - } - } - // Updater and client watch service @LauncherAPI private final List update = new ArrayList<>(); @@ -165,12 +108,6 @@ public OptionalArgs(String[] args, boolean mark) { private final List clientArgs = new ArrayList<>(); @LauncherAPI private final List whitelist = new ArrayList<>(); - @LauncherAPI - private final List optionalJVMArgs = new ArrayList<>(); - @LauncherAPI - private final List optionalClientArgs = new ArrayList<>(); - @LauncherAPI - private final List optionalClassPath = new ArrayList<>(); @Override public int compareTo(ClientProfile o) { @@ -192,21 +129,6 @@ public String[] getClassPath() { return classPath.toArray(new String[0]); } - @LauncherAPI - public List getOptionalJVMArgs() { - return optionalJVMArgs; - } - - @LauncherAPI - public List getOptionalClientArgs() { - return optionalClientArgs; - } - - @LauncherAPI - public List getOptionalClassPath() { - return optionalClassPath; - } - @LauncherAPI public String[] getClientArgs() { return clientArgs.toArray(new String[0]); @@ -269,22 +191,22 @@ public void updateOptionalGraph() { if (file.dependenciesFile != null) { file.dependencies = new OptionalFile[file.dependenciesFile.length]; for (int i = 0; i < file.dependenciesFile.length; ++i) { - file.dependencies[i] = getOptionalFile(file.dependenciesFile[i]); + file.dependencies[i] = getOptionalFile(file.dependenciesFile[i].name, file.dependenciesFile[i].type); } } if (file.conflictFile != null) { file.conflict = new OptionalFile[file.conflictFile.length]; for (int i = 0; i < file.conflictFile.length; ++i) { - file.conflict[i] = getOptionalFile(file.conflictFile[i]); + file.conflict[i] = getOptionalFile(file.conflictFile[i].name, file.conflictFile[i].type); } } } } @LauncherAPI - public OptionalFile getOptionalFile(String file) { + public OptionalFile getOptionalFile(String file, OptionalType type) { for (OptionalFile f : updateOptional) - if (f.file.equals(file)) return f; + if (f.type.equals(type) && f.name.equals(file)) return f; return null; } @@ -294,10 +216,11 @@ public Collection getShared() { } @LauncherAPI - public void markOptional(String opt) { - if (!updateOptional.contains(new OptionalFile(opt))) - throw new SecurityException(String.format("Optional mod %s not found in optionalList", opt)); - OptionalFile file = getOptionalFile(opt); + public void markOptional(String name, OptionalType type) { + OptionalFile file = getOptionalFile(name, type); + if (file == null) { + throw new SecurityException(String.format("Optional %s not found in optionalList", name)); + } markOptional(file); } @@ -321,10 +244,11 @@ public void markOptional(OptionalFile file) { } @LauncherAPI - public void unmarkOptional(String opt) { - if (!updateOptional.contains(new OptionalFile(opt))) - throw new SecurityException(String.format("Optional mod %s not found in optionalList", opt)); - OptionalFile file = getOptionalFile(opt); + public void unmarkOptional(String name, OptionalType type) { + OptionalFile file = getOptionalFile(name, type); + if (file == null) { + throw new SecurityException(String.format("Optional %s not found in optionalList", name)); + } unmarkOptional(file); } @@ -353,12 +277,45 @@ public void unmarkOptional(OptionalFile file) { } } - public void pushOptional(HashedDir dir, boolean digest) { + public void pushOptionalFile(HashedDir dir, boolean digest) { for (OptionalFile opt : updateOptional) { - if (!opt.mark) dir.removeR(opt.file); + if (opt.type.equals(OptionalType.FILE) && !opt.mark) { + for (String file : opt.list) + dir.removeR(file); + } } } + public void pushOptionalJvmArgs(Collection jvmArgs1) + { + for (OptionalFile opt : updateOptional) { + if (opt.type.equals(OptionalType.JVMARGS) && opt.mark) { + jvmArgs1.addAll(Arrays.asList(opt.list)); + } + } + } + public void pushOptionalClientArgs(Collection clientArgs1) + { + for (OptionalFile opt : updateOptional) { + if (opt.type.equals(OptionalType.CLIENTARGS) && opt.mark) { + clientArgs1.addAll(Arrays.asList(opt.list)); + } + } + } + public void pushOptionalClassPath(pushOptionalClassPathCallback callback) throws IOException + { + for (OptionalFile opt : updateOptional) { + if (opt.type.equals(OptionalType.CLASSPATH) && opt.mark) { + callback.run(opt.list); + } + } + } + @FunctionalInterface + public interface pushOptionalClassPathCallback + { + void run(String[] opt) throws IOException; + } + @LauncherAPI public int getServerPort() { return serverPort; diff --git a/libLauncher/src/main/java/ru/gravit/launcher/profiles/optional/OptionalDepend.java b/libLauncher/src/main/java/ru/gravit/launcher/profiles/optional/OptionalDepend.java new file mode 100644 index 00000000..c49d9ddb --- /dev/null +++ b/libLauncher/src/main/java/ru/gravit/launcher/profiles/optional/OptionalDepend.java @@ -0,0 +1,10 @@ +package ru.gravit.launcher.profiles.optional; + +import ru.gravit.launcher.LauncherAPI; + +public class OptionalDepend { + @LauncherAPI + public String name; + @LauncherAPI + public OptionalType type; +} diff --git a/libLauncher/src/main/java/ru/gravit/launcher/profiles/optional/OptionalFile.java b/libLauncher/src/main/java/ru/gravit/launcher/profiles/optional/OptionalFile.java new file mode 100644 index 00000000..878b6e3d --- /dev/null +++ b/libLauncher/src/main/java/ru/gravit/launcher/profiles/optional/OptionalFile.java @@ -0,0 +1,120 @@ +package ru.gravit.launcher.profiles.optional; + +import ru.gravit.launcher.LauncherAPI; +import ru.gravit.launcher.serialize.HInput; +import ru.gravit.launcher.serialize.HOutput; +import ru.gravit.utils.helper.LogHelper; + +import java.io.IOException; +import java.util.Objects; +import java.util.Set; + +public class OptionalFile { + @LauncherAPI + public String[] list; + @LauncherAPI + public OptionalType type; + @LauncherAPI + public boolean mark; + @LauncherAPI + public boolean visible = true; + @LauncherAPI + public String name; + @LauncherAPI + public String info; + @LauncherAPI + public OptionalDepend[] dependenciesFile; + @LauncherAPI + public OptionalDepend[] conflictFile; + @LauncherAPI + public transient OptionalFile[] dependencies; + @LauncherAPI + public transient OptionalFile[] conflict; + @LauncherAPI + public int subTreeLevel = 1; + @LauncherAPI + public long permissions = 0L; + @LauncherAPI + public transient Set dependenciesCount; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OptionalFile that = (OptionalFile) o; + return Objects.equals(name, that.name); + } + + public int hashCode() { + return Objects.hash(name); + } + @LauncherAPI + public OptionalType getType() { + return OptionalType.FILE; + } + @LauncherAPI + public String getName() { + return name; + } + @LauncherAPI + public boolean isVisible() { + return visible; + } + @LauncherAPI + public boolean isMark() { + return mark; + } + @LauncherAPI + public long getPermissions() { + return permissions; + } + @LauncherAPI + public void writeType(HOutput output) throws IOException + { + switch(type) + { + + case FILE: + output.writeInt(1); + break; + case CLASSPATH: + output.writeInt(2); + break; + case JVMARGS: + output.writeInt(3); + break; + case CLIENTARGS: + output.writeInt(4); + break; + default: + output.writeInt(5); + break; + } + } + @LauncherAPI + public static OptionalType readType(HInput input) throws IOException + { + int t = input.readInt(); + OptionalType type; + switch(t) + { + case 1: + type = OptionalType.FILE; + break; + case 2: + type = OptionalType.CLASSPATH; + break; + case 3: + type = OptionalType.JVMARGS; + break; + case 4: + type = OptionalType.CLIENTARGS; + break; + default: + LogHelper.error("readType failed. Read int %d",t); + type = OptionalType.FILE; + break; + } + return type; + } +} diff --git a/libLauncher/src/main/java/ru/gravit/launcher/profiles/optional/OptionalType.java b/libLauncher/src/main/java/ru/gravit/launcher/profiles/optional/OptionalType.java new file mode 100644 index 00000000..2bcacf60 --- /dev/null +++ b/libLauncher/src/main/java/ru/gravit/launcher/profiles/optional/OptionalType.java @@ -0,0 +1,16 @@ +package ru.gravit.launcher.profiles.optional; + +import ru.gravit.launcher.LauncherAPI; + +@LauncherAPI +public enum OptionalType +{ + @LauncherAPI + FILE, + @LauncherAPI + CLASSPATH, + @LauncherAPI + JVMARGS, + @LauncherAPI + CLIENTARGS +} diff --git a/libLauncher/src/main/java/ru/gravit/utils/Version.java b/libLauncher/src/main/java/ru/gravit/utils/Version.java index 5795a7a8..1f0a0577 100644 --- a/libLauncher/src/main/java/ru/gravit/utils/Version.java +++ b/libLauncher/src/main/java/ru/gravit/utils/Version.java @@ -2,6 +2,11 @@ import ru.gravit.launcher.LauncherAPI; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; import java.util.Objects; public final class Version { @@ -68,34 +73,8 @@ public int hashCode() { @LauncherAPI public String getReleaseStatus() { - String result; - switch (release) { - case LTS: - result = "lts"; - break; - case STABLE: - result = "stable"; - break; - case BETA: - result = "beta"; - break; - case ALPHA: - result = "alpha"; - break; - case DEV: - result = "dev"; - break; - case EXPERIMENTAL: - result = "experimental"; - break; - case UNKNOWN: - result = ""; - break; - default: - result = ""; - break; - } - return result; + if (release.equals(Type.UNKNOWN)) return ""; + return release.name().toLowerCase(Locale.ENGLISH); } @Override @@ -112,6 +91,13 @@ public enum Type { ALPHA, DEV, EXPERIMENTAL, - UNKNOWN + UNKNOWN; + + private static final Map types = new HashMap<>(); + public static final Map unModTypes = Collections.unmodifiableMap(types); + + static { + Arrays.asList(values()).stream().forEach(type -> types.put(type.name().substring(0, type.name().length() < 3 ? type.name().length() : 3), type)); + } } } diff --git a/modules b/modules index 38e11640..53106ee2 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit 38e11640ce4a4ed3e9745718db02a888770a22bb +Subproject commit 53106ee20700cb73ad65fd7dddf69b0d0b766f4c