mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-23 00:51:01 +03:00
IDEA Refractoring
This commit is contained in:
parent
78766d0c5c
commit
8580cd4403
145 changed files with 1992 additions and 1860 deletions
|
@ -166,6 +166,7 @@ public void verify() {
|
||||||
VerifyHelper.verify(getAddress(), VerifyHelper.NOT_EMPTY, "LaunchServer address can't be empty");
|
VerifyHelper.verify(getAddress(), VerifyHelper.NOT_EMPTY, "LaunchServer address can't be empty");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ExeConf extends ConfigObject {
|
public static class ExeConf extends ConfigObject {
|
||||||
public final boolean enabled;
|
public final boolean enabled;
|
||||||
public String productName;
|
public String productName;
|
||||||
|
@ -201,6 +202,7 @@ private ExeConf(BlockConfigEntry block) {
|
||||||
: String.format("%s, build %d", Launcher.getVersion().getVersionString(), Launcher.getVersion().build);
|
: String.format("%s, build %d", Launcher.getVersion().getVersionString(), Launcher.getVersion().build);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
|
private final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
|
||||||
private final Collection<SignedObjectHolder<ClientProfile>> result;
|
private final Collection<SignedObjectHolder<ClientProfile>> result;
|
||||||
|
|
||||||
|
@ -224,6 +226,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
|
||||||
return super.visitFile(file, attrs);
|
return super.visitFile(file, attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SignConf extends ConfigObject {
|
public static class SignConf extends ConfigObject {
|
||||||
public final boolean enabled;
|
public final boolean enabled;
|
||||||
public String algo;
|
public String algo;
|
||||||
|
@ -233,22 +236,24 @@ public static class SignConf extends ConfigObject {
|
||||||
public boolean hasPass;
|
public boolean hasPass;
|
||||||
public String pass;
|
public String pass;
|
||||||
public String keyalias;
|
public String keyalias;
|
||||||
|
|
||||||
private SignConf(BlockConfigEntry block, Path coredir) {
|
private SignConf(BlockConfigEntry block, Path coredir) {
|
||||||
super(block);
|
super(block);
|
||||||
enabled = block.getEntryValue("enabled", BooleanConfigEntry.class);
|
enabled = block.getEntryValue("enabled", BooleanConfigEntry.class);
|
||||||
storepass = null;
|
storepass = null;
|
||||||
pass = null;
|
pass = null;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
algo = block.hasEntry("storeType") ? block.getEntryValue("storeType", StringConfigEntry.class) : "JKS";
|
algo = block.hasEntry("storeType") ? block.getEntryValue("storeType", StringConfigEntry.class) : "JKS";
|
||||||
key = coredir.resolve(block.getEntryValue("keyFile", StringConfigEntry.class));
|
key = coredir.resolve(block.getEntryValue("keyFile", StringConfigEntry.class));
|
||||||
hasStorePass = block.hasEntry("keyStorePass");
|
hasStorePass = block.hasEntry("keyStorePass");
|
||||||
if (hasStorePass) storepass = block.getEntryValue("keyStorePass", StringConfigEntry.class);
|
if (hasStorePass) storepass = block.getEntryValue("keyStorePass", StringConfigEntry.class);
|
||||||
keyalias = block.getEntryValue("keyAlias", StringConfigEntry.class);
|
keyalias = block.getEntryValue("keyAlias", StringConfigEntry.class);
|
||||||
hasPass = block.hasEntry("keyPass");
|
hasPass = block.hasEntry("keyPass");
|
||||||
if (hasPass) pass = block.getEntryValue("keyPass", StringConfigEntry.class);
|
if (hasPass) pass = block.getEntryValue("keyPass", StringConfigEntry.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String... args) throws Throwable {
|
public static void main(String... args) throws Throwable {
|
||||||
JVMHelper.verifySystemProperties(LaunchServer.class, true);
|
JVMHelper.verifySystemProperties(LaunchServer.class, true);
|
||||||
LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log"));
|
LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log"));
|
||||||
|
@ -267,6 +272,7 @@ public static void main(String... args) throws Throwable {
|
||||||
Instant end = Instant.now();
|
Instant end = Instant.now();
|
||||||
LogHelper.debug("LaunchServer started in %dms", Duration.between(start, end).toMillis());
|
LogHelper.debug("LaunchServer started in %dms", Duration.between(start, end).toMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constant paths
|
// Constant paths
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final Path dir;
|
public final Path dir;
|
||||||
|
@ -317,7 +323,7 @@ public static void main(String... args) throws Throwable {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final ServerSocketHandler serverSocketHandler;
|
public final ServerSocketHandler serverSocketHandler;
|
||||||
|
|
||||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||||
|
|
||||||
// Updates and profiles
|
// Updates and profiles
|
||||||
private volatile List<SignedObjectHolder<ClientProfile>> profilesList;
|
private volatile List<SignedObjectHolder<ClientProfile>> profilesList;
|
||||||
|
@ -346,9 +352,9 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
|
||||||
// Set command handler
|
// Set command handler
|
||||||
CommandHandler localCommandHandler;
|
CommandHandler localCommandHandler;
|
||||||
if (portable)
|
if (portable)
|
||||||
localCommandHandler = new StdCommandHandler(this, false);
|
localCommandHandler = new StdCommandHandler(this, false);
|
||||||
else
|
else
|
||||||
try {
|
try {
|
||||||
Class.forName("jline.Terminal");
|
Class.forName("jline.Terminal");
|
||||||
|
|
||||||
// JLine2 available
|
// JLine2 available
|
||||||
|
@ -366,7 +372,7 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
|
||||||
publicKey = SecurityHelper.toPublicRSAKey(IOHelper.read(publicKeyFile));
|
publicKey = SecurityHelper.toPublicRSAKey(IOHelper.read(publicKeyFile));
|
||||||
privateKey = SecurityHelper.toPrivateRSAKey(IOHelper.read(privateKeyFile));
|
privateKey = SecurityHelper.toPrivateRSAKey(IOHelper.read(privateKeyFile));
|
||||||
if (!publicKey.getModulus().equals(privateKey.getModulus()))
|
if (!publicKey.getModulus().equals(privateKey.getModulus()))
|
||||||
throw new IOException("Private and public key modulus mismatch");
|
throw new IOException("Private and public key modulus mismatch");
|
||||||
} else {
|
} else {
|
||||||
LogHelper.info("Generating RSA keypair");
|
LogHelper.info("Generating RSA keypair");
|
||||||
KeyPair pair = SecurityHelper.genRSAKeyPair();
|
KeyPair pair = SecurityHelper.genRSAKeyPair();
|
||||||
|
@ -415,12 +421,12 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
|
||||||
|
|
||||||
// Sync updates dir
|
// Sync updates dir
|
||||||
if (!IOHelper.isDir(updatesDir))
|
if (!IOHelper.isDir(updatesDir))
|
||||||
Files.createDirectory(updatesDir);
|
Files.createDirectory(updatesDir);
|
||||||
syncUpdatesDir(null);
|
syncUpdatesDir(null);
|
||||||
|
|
||||||
// Sync profiles dir
|
// Sync profiles dir
|
||||||
if (!IOHelper.isDir(profilesDir))
|
if (!IOHelper.isDir(profilesDir))
|
||||||
Files.createDirectory(profilesDir);
|
Files.createDirectory(profilesDir);
|
||||||
syncProfilesDir();
|
syncProfilesDir();
|
||||||
|
|
||||||
|
|
||||||
|
@ -432,8 +438,8 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
|
||||||
}
|
}
|
||||||
|
|
||||||
private LauncherBinary binary() {
|
private LauncherBinary binary() {
|
||||||
if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this);
|
if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this);
|
||||||
return new EXELauncherBinary(this);
|
return new EXELauncherBinary(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
|
@ -462,11 +468,7 @@ public void close() {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
}
|
}
|
||||||
try {
|
config.hwidHandler.close();
|
||||||
config.hwidHandler.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
}
|
|
||||||
modulesManager.close();
|
modulesManager.close();
|
||||||
// Print last message before death :(
|
// Print last message before death :(
|
||||||
LogHelper.info("LaunchServer stopped");
|
LogHelper.info("LaunchServer stopped");
|
||||||
|
@ -474,7 +476,7 @@ public void close() {
|
||||||
|
|
||||||
private void generateConfigIfNotExists() throws IOException {
|
private void generateConfigIfNotExists() throws IOException {
|
||||||
if (IOHelper.isFile(configFile))
|
if (IOHelper.isFile(configFile))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Create new config
|
// Create new config
|
||||||
LogHelper.info("Creating LaunchServer config");
|
LogHelper.info("Creating LaunchServer config");
|
||||||
|
@ -524,7 +526,7 @@ public void rebindServerSocket() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (started.getAndSet(true))
|
if (started.getAndSet(true))
|
||||||
throw new IllegalStateException("LaunchServer has been already started");
|
throw new IllegalStateException("LaunchServer has been already started");
|
||||||
|
|
||||||
// Add shutdown hook, then start LaunchServer
|
// Add shutdown hook, then start LaunchServer
|
||||||
if (!portable) {
|
if (!portable) {
|
||||||
|
@ -567,7 +569,7 @@ public void syncUpdatesDir(Collection<String> dirs) throws IOException {
|
||||||
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(updatesDir)) {
|
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(updatesDir)) {
|
||||||
for (Path updateDir : dirStream) {
|
for (Path updateDir : dirStream) {
|
||||||
if (Files.isHidden(updateDir))
|
if (Files.isHidden(updateDir))
|
||||||
continue; // Skip hidden
|
continue; // Skip hidden
|
||||||
|
|
||||||
// Resolve name and verify is dir
|
// Resolve name and verify is dir
|
||||||
String name = IOHelper.getFileName(updateDir);
|
String name = IOHelper.getFileName(updateDir);
|
||||||
|
|
|
@ -10,78 +10,76 @@
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
import ru.gravit.utils.helper.IOHelper;
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
import ru.gravit.utils.helper.LogHelper;
|
||||||
import ru.gravit.utils.helper.SecurityHelper;
|
import ru.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
public class ProguardConf {
|
public class ProguardConf {
|
||||||
private static final String charsFirst = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
|
private static final String charsFirst = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
|
||||||
private static final String chars = "1aAbBcC2dDeEfF3gGhHiI4jJkKl5mMnNoO6pPqQrR7sStT8uUvV9wWxX0yYzZ";
|
private static final String chars = "1aAbBcC2dDeEfF3gGhHiI4jJkKl5mMnNoO6pPqQrR7sStT8uUvV9wWxX0yYzZ";
|
||||||
private static String generateString(SecureRandom rand, int il) {
|
|
||||||
StringBuilder sb = new StringBuilder(il);
|
|
||||||
sb.append(charsFirst.charAt(rand.nextInt(charsFirst.length())));
|
|
||||||
for (int i = 0; i < il - 1; i++) sb.append(chars.charAt(rand.nextInt(chars.length())));
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
private final LaunchServer srv;
|
|
||||||
public final Path proguard;
|
|
||||||
public final Path config;
|
|
||||||
public final Path mappings;
|
|
||||||
public final Path words;
|
|
||||||
public final ArrayList<String> confStrs;
|
|
||||||
|
|
||||||
public ProguardConf(LaunchServer srv) {
|
private static String generateString(SecureRandom rand, int il) {
|
||||||
this.srv = srv;
|
StringBuilder sb = new StringBuilder(il);
|
||||||
proguard = this.srv.dir.resolve("proguard");
|
sb.append(charsFirst.charAt(rand.nextInt(charsFirst.length())));
|
||||||
config = proguard.resolve("proguard.config");
|
for (int i = 0; i < il - 1; i++) sb.append(chars.charAt(rand.nextInt(chars.length())));
|
||||||
mappings = proguard.resolve("mappings.pro");
|
return sb.toString();
|
||||||
words = proguard.resolve("random.pro");
|
}
|
||||||
confStrs = new ArrayList<>();
|
|
||||||
prepare(false);
|
|
||||||
if (this.srv.config.genMappings) confStrs.add("-printmapping \'" + mappings.toFile().getName() + "\'");
|
|
||||||
confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'");
|
|
||||||
confStrs.add("-injar \'" + Paths.get(".").toAbsolutePath() + IOHelper.PLATFORM_SEPARATOR + srv.config.binaryName + ".jar\'");
|
|
||||||
confStrs.add("-outjar \'" + Paths.get(".").toAbsolutePath() + IOHelper.PLATFORM_SEPARATOR + srv.config.binaryName + "-obf.jar\'");
|
|
||||||
confStrs.add("-classobfuscationdictionary \'" + words.toFile().getName() + "\'");
|
|
||||||
confStrs.add(readConf());
|
|
||||||
|
|
||||||
}
|
public final Path proguard;
|
||||||
|
public final Path config;
|
||||||
|
public final Path mappings;
|
||||||
|
public final Path words;
|
||||||
|
public final ArrayList<String> confStrs;
|
||||||
|
|
||||||
private void genConfig(boolean force) throws IOException {
|
public ProguardConf(LaunchServer srv) {
|
||||||
if (IOHelper.exists(config) && !force) return;
|
LaunchServer srv1 = srv;
|
||||||
Files.deleteIfExists(config);
|
proguard = srv1.dir.resolve("proguard");
|
||||||
config.toFile().createNewFile();
|
config = proguard.resolve("proguard.config");
|
||||||
try (OutputStream out = IOHelper.newOutput(config); InputStream in = IOHelper.newInput(IOHelper.getResourceURL("ru/gravit/launchserver/defaults/proguard.cfg"))) {
|
mappings = proguard.resolve("mappings.pro");
|
||||||
IOHelper.transfer(in, out);
|
words = proguard.resolve("random.pro");
|
||||||
}
|
confStrs = new ArrayList<>();
|
||||||
}
|
prepare(false);
|
||||||
|
if (srv1.config.genMappings) confStrs.add("-printmapping \'" + mappings.toFile().getName() + "\'");
|
||||||
|
confStrs.add("-obfuscationdictionary \'" + words.toFile().getName() + "\'");
|
||||||
|
confStrs.add("-injar \'" + Paths.get(".").toAbsolutePath() + IOHelper.PLATFORM_SEPARATOR + srv.config.binaryName + ".jar\'");
|
||||||
|
confStrs.add("-outjar \'" + Paths.get(".").toAbsolutePath() + IOHelper.PLATFORM_SEPARATOR + srv.config.binaryName + "-obf.jar\'");
|
||||||
|
confStrs.add("-classobfuscationdictionary \'" + words.toFile().getName() + "\'");
|
||||||
|
confStrs.add(readConf());
|
||||||
|
|
||||||
public void genWords(boolean force) throws IOException {
|
}
|
||||||
if (IOHelper.exists(words) && !force) return;
|
|
||||||
Files.deleteIfExists(words);
|
|
||||||
words.toFile().createNewFile();
|
|
||||||
SecureRandom rand = SecurityHelper.newRandom();
|
|
||||||
rand.setSeed(SecureRandom.getSeed(32));
|
|
||||||
try (PrintWriter out = new PrintWriter(new OutputStreamWriter(IOHelper.newOutput(words), IOHelper.UNICODE_CHARSET))) {
|
|
||||||
for (int i = 0; i < Short.MAX_VALUE; i++) out.println(generateString(rand, 24));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void prepare(boolean force) {
|
private void genConfig(boolean force) throws IOException {
|
||||||
try {
|
if (IOHelper.exists(config) && !force) return;
|
||||||
IOHelper.createParentDirs(config);
|
Files.deleteIfExists(config);
|
||||||
genWords(force);
|
config.toFile().createNewFile();
|
||||||
genConfig(force);
|
try (OutputStream out = IOHelper.newOutput(config); InputStream in = IOHelper.newInput(IOHelper.getResourceURL("ru/gravit/launchserver/defaults/proguard.cfg"))) {
|
||||||
} catch (IOException e) {
|
IOHelper.transfer(in, out);
|
||||||
LogHelper.error(e);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private String readConf() {
|
public void genWords(boolean force) throws IOException {
|
||||||
return "@".concat(config.toFile().getName());
|
if (IOHelper.exists(words) && !force) return;
|
||||||
}
|
Files.deleteIfExists(words);
|
||||||
|
words.toFile().createNewFile();
|
||||||
|
SecureRandom rand = SecurityHelper.newRandom();
|
||||||
|
rand.setSeed(SecureRandom.getSeed(32));
|
||||||
|
try (PrintWriter out = new PrintWriter(new OutputStreamWriter(IOHelper.newOutput(words), IOHelper.UNICODE_CHARSET))) {
|
||||||
|
for (int i = 0; i < Short.MAX_VALUE; i++) out.println(generateString(rand, 24));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void prepare(boolean force) {
|
||||||
|
try {
|
||||||
|
IOHelper.createParentDirs(config);
|
||||||
|
genWords(force);
|
||||||
|
genConfig(force);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogHelper.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readConf() {
|
||||||
|
return "@".concat(config.toFile().getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,24 +14,26 @@
|
||||||
|
|
||||||
public class StarterAgent {
|
public class StarterAgent {
|
||||||
|
|
||||||
public static final class StarterVisitor extends SimpleFileVisitor<Path> {
|
public static final class StarterVisitor extends SimpleFileVisitor<Path> {
|
||||||
private Instrumentation inst;
|
private Instrumentation inst;
|
||||||
|
|
||||||
public StarterVisitor(Instrumentation inst) {
|
public StarterVisitor(Instrumentation inst) {
|
||||||
this.inst = inst;
|
this.inst = inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
if (file.toFile().getName().endsWith(".jar")) inst.appendToSystemClassLoaderSearch(new JarFile(file.toFile()));
|
if (file.toFile().getName().endsWith(".jar"))
|
||||||
return super.visitFile(file, attrs);
|
inst.appendToSystemClassLoaderSearch(new JarFile(file.toFile()));
|
||||||
}
|
return super.visitFile(file, attrs);
|
||||||
}
|
}
|
||||||
public static void premain(String agentArgument, Instrumentation inst) {
|
}
|
||||||
try {
|
|
||||||
Files.walkFileTree(Paths.get("libraries"), Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new StarterVisitor(inst));
|
public static void premain(String agentArgument, Instrumentation inst) {
|
||||||
} catch (IOException e) {
|
try {
|
||||||
e.printStackTrace(System.err);
|
Files.walkFileTree(Paths.get("libraries"), Collections.singleton(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new StarterVisitor(inst));
|
||||||
}
|
} catch (IOException e) {
|
||||||
}
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,16 +20,17 @@ public AuthEntry(int i, long l) {
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj)
|
||||||
return true;
|
return true;
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
return false;
|
return false;
|
||||||
if (!(obj instanceof AuthEntry))
|
if (!(obj instanceof AuthEntry))
|
||||||
return false;
|
return false;
|
||||||
AuthEntry other = (AuthEntry) obj;
|
AuthEntry other = (AuthEntry) obj;
|
||||||
if (ts != other.ts)
|
if (ts != other.ts)
|
||||||
return false;
|
return false;
|
||||||
return value == other.value;
|
return value == other.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
|
@ -44,6 +45,7 @@ public String toString() {
|
||||||
return String.format("AuthEntry {value=%s, ts=%s}", value, ts);
|
return String.format("AuthEntry {value=%s, ts=%s}", value, ts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public static final long TIMEOUT = 10 * 60 * 1000; //10 минут
|
public static final long TIMEOUT = 10 * 60 * 1000; //10 минут
|
||||||
public final int rateLimit;
|
public final int rateLimit;
|
||||||
|
@ -78,7 +80,7 @@ public boolean isLimit(String ip) {
|
||||||
rate.ts = currenttime;
|
rate.ts = currenttime;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
map.put(ip, new AuthEntry(1, System.currentTimeMillis()));
|
map.put(ip, new AuthEntry(1, System.currentTimeMillis()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ public MySQLSourceConfig(String poolName, BlockConfigEntry block) {
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
if (hikari)
|
if (hikari)
|
||||||
((HikariDataSource) source).close();
|
((HikariDataSource) source).close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
|
|
|
@ -45,7 +45,7 @@ public static void registerHandlers() {
|
||||||
registerHandler("binaryFile", BinaryFileAuthHandler::new);
|
registerHandler("binaryFile", BinaryFileAuthHandler::new);
|
||||||
registerHandler("textFile", TextFileAuthHandler::new);
|
registerHandler("textFile", TextFileAuthHandler::new);
|
||||||
registerHandler("mysql", MySQLAuthHandler::new);
|
registerHandler("mysql", MySQLAuthHandler::new);
|
||||||
registerHandler("json",JsonAuthHandler::new);
|
registerHandler("json", JsonAuthHandler::new);
|
||||||
registredHandl = true;
|
registredHandl = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ public Entry(UUID uuid, String username, String accessToken, String serverID) {
|
||||||
this.serverID = serverID == null ? null : VerifyHelper.verifyServerID(serverID);
|
this.serverID = serverID == null ? null : VerifyHelper.verifyServerID(serverID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<UUID, Entry> entryCache = new HashMap<>(1024);
|
private final Map<UUID, Entry> entryCache = new HashMap<>(1024);
|
||||||
|
|
||||||
private final Map<String, UUID> usernamesCache = new HashMap<>(1024);
|
private final Map<String, UUID> usernamesCache = new HashMap<>(1024);
|
||||||
|
@ -39,14 +40,15 @@ public Entry(UUID uuid, String username, String accessToken, String serverID) {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
protected CachedAuthHandler(BlockConfigEntry block) {
|
protected CachedAuthHandler(BlockConfigEntry block) {
|
||||||
super(block);
|
super(block);
|
||||||
if(block.hasEntry("garbage")) if(block.getEntryValue("garbage", BooleanConfigEntry.class)) GarbageManager.registerNeedGC(this);
|
if (block.hasEntry("garbage"))
|
||||||
|
if (block.getEntryValue("garbage", BooleanConfigEntry.class)) GarbageManager.registerNeedGC(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
protected void addEntry(Entry entry) {
|
protected void addEntry(Entry entry) {
|
||||||
Entry previous = entryCache.put(entry.uuid, entry);
|
Entry previous = entryCache.put(entry.uuid, entry);
|
||||||
if (previous != null)
|
if (previous != null)
|
||||||
usernamesCache.remove(CommonHelper.low(previous.username));
|
usernamesCache.remove(CommonHelper.low(previous.username));
|
||||||
usernamesCache.put(CommonHelper.low(entry.username), entry.uuid);
|
usernamesCache.put(CommonHelper.low(entry.username), entry.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +56,7 @@ protected void addEntry(Entry entry) {
|
||||||
public final synchronized UUID auth(AuthProviderResult result) throws IOException {
|
public final synchronized UUID auth(AuthProviderResult result) throws IOException {
|
||||||
Entry entry = getEntry(result.username);
|
Entry entry = getEntry(result.username);
|
||||||
if (entry == null || !updateAuth(entry.uuid, entry.username, result.accessToken))
|
if (entry == null || !updateAuth(entry.uuid, entry.username, result.accessToken))
|
||||||
return authError(String.format("UUID is null for username '%s'", result.username));
|
return authError(String.format("UUID is null for username '%s'", result.username));
|
||||||
|
|
||||||
// Update cached access token (and username case)
|
// Update cached access token (and username case)
|
||||||
entry.username = result.username;
|
entry.username = result.username;
|
||||||
|
@ -79,12 +81,12 @@ public synchronized UUID checkServer(String username, String serverID) throws IO
|
||||||
private Entry getEntry(String username) throws IOException {
|
private Entry getEntry(String username) throws IOException {
|
||||||
UUID uuid = usernamesCache.get(CommonHelper.low(username));
|
UUID uuid = usernamesCache.get(CommonHelper.low(username));
|
||||||
if (uuid != null)
|
if (uuid != null)
|
||||||
return getEntry(uuid);
|
return getEntry(uuid);
|
||||||
|
|
||||||
// Fetch entry by username
|
// Fetch entry by username
|
||||||
Entry entry = fetchEntry(username);
|
Entry entry = fetchEntry(username);
|
||||||
if (entry != null)
|
if (entry != null)
|
||||||
addEntry(entry);
|
addEntry(entry);
|
||||||
|
|
||||||
// Return what we got
|
// Return what we got
|
||||||
return entry;
|
return entry;
|
||||||
|
@ -95,7 +97,7 @@ private Entry getEntry(UUID uuid) throws IOException {
|
||||||
if (entry == null) {
|
if (entry == null) {
|
||||||
entry = fetchEntry(uuid);
|
entry = fetchEntry(uuid);
|
||||||
if (entry != null)
|
if (entry != null)
|
||||||
addEntry(entry);
|
addEntry(entry);
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
@ -105,16 +107,17 @@ public synchronized boolean joinServer(String username, String accessToken, Stri
|
||||||
Entry entry = getEntry(username);
|
Entry entry = getEntry(username);
|
||||||
if (entry == null || !username.equals(entry.username) || !accessToken.equals(entry.accessToken) ||
|
if (entry == null || !username.equals(entry.username) || !accessToken.equals(entry.accessToken) ||
|
||||||
!updateServerID(entry.uuid, serverID))
|
!updateServerID(entry.uuid, serverID))
|
||||||
return false; // Account doesn't exist or invalid access token
|
return false; // Account doesn't exist or invalid access token
|
||||||
|
|
||||||
// Update cached server ID
|
// Update cached server ID
|
||||||
entry.serverID = serverID;
|
entry.serverID = serverID;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public synchronized void garbageCollection()
|
|
||||||
{
|
public synchronized void garbageCollection() {
|
||||||
entryCache.clear();
|
entryCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
protected abstract boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException;
|
protected abstract boolean updateAuth(UUID uuid, String username, String accessToken) throws IOException;
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ public Entry(HInput input) throws IOException {
|
||||||
if (input.readBoolean()) {
|
if (input.readBoolean()) {
|
||||||
accessToken = SecurityHelper.verifyToken(input.readASCII(-SecurityHelper.TOKEN_STRING_LENGTH));
|
accessToken = SecurityHelper.verifyToken(input.readASCII(-SecurityHelper.TOKEN_STRING_LENGTH));
|
||||||
if (input.readBoolean())
|
if (input.readBoolean())
|
||||||
serverID = VerifyHelper.verifyServerID(input.readASCII(41));
|
serverID = VerifyHelper.verifyServerID(input.readASCII(41));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ public Entry(String username) {
|
||||||
public Entry(String username, String accessToken, String serverID) {
|
public Entry(String username, String accessToken, String serverID) {
|
||||||
this(username);
|
this(username);
|
||||||
if (accessToken == null && serverID != null)
|
if (accessToken == null && serverID != null)
|
||||||
throw new IllegalArgumentException("Can't set access token while server ID is null");
|
throw new IllegalArgumentException("Can't set access token while server ID is null");
|
||||||
|
|
||||||
// Set and verify access token
|
// Set and verify access token
|
||||||
this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken);
|
this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken);
|
||||||
|
@ -84,7 +84,7 @@ public String getUsername() {
|
||||||
|
|
||||||
private boolean joinServer(String username, String accessToken, String serverID) {
|
private boolean joinServer(String username, String accessToken, String serverID) {
|
||||||
if (!username.equals(this.username) || !accessToken.equals(this.accessToken))
|
if (!username.equals(this.username) || !accessToken.equals(this.accessToken))
|
||||||
return false; // Username or access token mismatch
|
return false; // Username or access token mismatch
|
||||||
|
|
||||||
// Update server ID
|
// Update server ID
|
||||||
this.serverID = serverID;
|
this.serverID = serverID;
|
||||||
|
@ -99,10 +99,11 @@ public void write(HOutput output) throws IOException {
|
||||||
output.writeASCII(accessToken, -SecurityHelper.TOKEN_STRING_LENGTH);
|
output.writeASCII(accessToken, -SecurityHelper.TOKEN_STRING_LENGTH);
|
||||||
output.writeBoolean(serverID != null);
|
output.writeBoolean(serverID != null);
|
||||||
if (serverID != null)
|
if (serverID != null)
|
||||||
output.writeASCII(serverID, 41);
|
output.writeASCII(serverID, 41);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final Path file;
|
public final Path file;
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
|
@ -143,7 +144,7 @@ protected final void addAuth(UUID uuid, Entry entry) {
|
||||||
try {
|
try {
|
||||||
Entry previous = entryMap.put(uuid, entry);
|
Entry previous = entryMap.put(uuid, entry);
|
||||||
if (previous != null)
|
if (previous != null)
|
||||||
usernamesMap.remove(CommonHelper.low(previous.username));
|
usernamesMap.remove(CommonHelper.low(previous.username));
|
||||||
usernamesMap.put(CommonHelper.low(entry.username), uuid);
|
usernamesMap.put(CommonHelper.low(entry.username), uuid);
|
||||||
} finally {
|
} finally {
|
||||||
lock.writeLock().unlock();
|
lock.writeLock().unlock();
|
||||||
|
@ -210,15 +211,15 @@ private UUID genUUIDFor(String username) {
|
||||||
if (offlineUUIDs) {
|
if (offlineUUIDs) {
|
||||||
UUID md5UUID = PlayerProfile.offlineUUID(username);
|
UUID md5UUID = PlayerProfile.offlineUUID(username);
|
||||||
if (!entryMap.containsKey(md5UUID))
|
if (!entryMap.containsKey(md5UUID))
|
||||||
return md5UUID;
|
return md5UUID;
|
||||||
LogHelper.warning("Offline UUID collision, using random: '%s'", username);
|
LogHelper.warning("Offline UUID collision, using random: '%s'", username);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick random UUID
|
// Pick random UUID
|
||||||
UUID uuid;
|
UUID uuid;
|
||||||
do
|
do
|
||||||
uuid = new UUID(random.nextLong(), random.nextLong());
|
uuid = new UUID(random.nextLong(), random.nextLong());
|
||||||
while (entryMap.containsKey(uuid));
|
while (entryMap.containsKey(uuid));
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,19 +72,20 @@ public UUID checkServer(String username, String serverID) throws IOException {
|
||||||
JsonObject result = jsonRequestChecked(request, urlCheckServer);
|
JsonObject result = jsonRequestChecked(request, urlCheckServer);
|
||||||
String value;
|
String value;
|
||||||
if ((value = result.getString(uuidKeyName, null)) != null)
|
if ((value = result.getString(uuidKeyName, null)) != null)
|
||||||
return UUID.fromString(value);
|
return UUID.fromString(value);
|
||||||
throw new IOException("Service error");
|
throw new IOException("Service error");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean joinServer(String username, String accessToken, String serverID) throws IOException {
|
public boolean joinServer(String username, String accessToken, String serverID) throws IOException {
|
||||||
JsonObject request = Json.object().add(userKeyName, username).add(serverIDKeyName, serverID).add(accessTokenKeyName, accessToken);
|
JsonObject request = Json.object().add(userKeyName, username).add(serverIDKeyName, serverID).add(accessTokenKeyName, accessToken);
|
||||||
HTTPRequest.jsonRequest(request, urlJoinServer);
|
HTTPRequest.jsonRequest(request, urlJoinServer);
|
||||||
return request.getString(responseOKKeyName,null).equals("OK");
|
return request.getString(responseOKKeyName, null).equals("OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -107,8 +108,8 @@ public String uuidToUsername(UUID uuid) throws IOException {
|
||||||
throw new IOException("Service error");
|
throw new IOException("Service error");
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonObject jsonRequestChecked(JsonObject object,URL url) throws IOException {
|
public JsonObject jsonRequestChecked(JsonObject object, URL url) throws IOException {
|
||||||
JsonValue result = HTTPRequest.jsonRequest(object,url);
|
JsonValue result = HTTPRequest.jsonRequest(object, url);
|
||||||
if (!result.isObject())
|
if (!result.isObject())
|
||||||
authError("Authentication server response is malformed");
|
authError("Authentication server response is malformed");
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ private static String toUsername(UUID uuid) {
|
||||||
// Find username end
|
// Find username end
|
||||||
int length = 0;
|
int length = 0;
|
||||||
while (length < bytes.length && bytes[length] != 0)
|
while (length < bytes.length && bytes[length] != 0)
|
||||||
length++;
|
length++;
|
||||||
|
|
||||||
// Decode and verify
|
// Decode and verify
|
||||||
return VerifyHelper.verifyUsername(new String(bytes, 0, length, IOHelper.ASCII_CHARSET));
|
return VerifyHelper.verifyUsername(new String(bytes, 0, length, IOHelper.ASCII_CHARSET));
|
||||||
|
|
|
@ -30,7 +30,7 @@ public UUID checkServer(String username, String serverID) throws IOException {
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
AuthHandler handler = this.handler;
|
AuthHandler handler = this.handler;
|
||||||
if (handler != null)
|
if (handler != null)
|
||||||
handler.close();
|
handler.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthHandler getHandler() {
|
private AuthHandler getHandler() {
|
||||||
|
|
|
@ -72,17 +72,17 @@ protected void writeAuthFileTmp() throws IOException {
|
||||||
authMap.put("username", cc(auth.getUsername()));
|
authMap.put("username", cc(auth.getUsername()));
|
||||||
String accessToken = auth.getAccessToken();
|
String accessToken = auth.getAccessToken();
|
||||||
if (accessToken != null)
|
if (accessToken != null)
|
||||||
authMap.put("accessToken", cc(accessToken));
|
authMap.put("accessToken", cc(accessToken));
|
||||||
String serverID = auth.getServerID();
|
String serverID = auth.getServerID();
|
||||||
if (serverID != null)
|
if (serverID != null)
|
||||||
authMap.put("serverID", cc(serverID));
|
authMap.put("serverID", cc(serverID));
|
||||||
|
|
||||||
// Create and add auth block
|
// Create and add auth block
|
||||||
BlockConfigEntry authBlock = new BlockConfigEntry(authMap, true, 5);
|
BlockConfigEntry authBlock = new BlockConfigEntry(authMap, true, 5);
|
||||||
if (next)
|
if (next)
|
||||||
authBlock.setComment(0, "\n"); // Pre-name
|
authBlock.setComment(0, "\n"); // Pre-name
|
||||||
else
|
else
|
||||||
next = true;
|
next = true;
|
||||||
authBlock.setComment(2, " "); // Pre-value
|
authBlock.setComment(2, " "); // Pre-value
|
||||||
authBlock.setComment(4, "\n"); // Post-comment
|
authBlock.setComment(4, "\n"); // Post-comment
|
||||||
map.put(uuid.toString(), authBlock);
|
map.put(uuid.toString(), authBlock);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package ru.gravit.launchserver.auth.hwid;
|
package ru.gravit.launchserver.auth.hwid;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,11 @@ public class HWID {
|
||||||
public static HWID fromData(HInput in) throws IOException {
|
public static HWID fromData(HInput in) throws IOException {
|
||||||
return gen(in.readLong(), in.readLong(), in.readLong());
|
return gen(in.readLong(), in.readLong(), in.readLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HWID gen(long hwid_hdd, long hwid_bios, long hwid_cpu) {
|
public static HWID gen(long hwid_hdd, long hwid_bios, long hwid_cpu) {
|
||||||
return new HWID(hwid_hdd, hwid_bios, hwid_cpu);
|
return new HWID(hwid_hdd, hwid_bios, hwid_cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long hwid_bios;
|
private long hwid_bios;
|
||||||
|
|
||||||
private long hwid_hdd;
|
private long hwid_hdd;
|
||||||
|
@ -27,16 +29,16 @@ private HWID(long hwid_hdd, long hwid_bios, long hwid_cpu) {
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj)
|
||||||
return true;
|
return true;
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
return false;
|
return false;
|
||||||
if (!(obj instanceof HWID))
|
if (!(obj instanceof HWID))
|
||||||
return false;
|
return false;
|
||||||
HWID other = (HWID) obj;
|
HWID other = (HWID) obj;
|
||||||
if (hwid_bios != other.hwid_bios)
|
if (hwid_bios != other.hwid_bios)
|
||||||
return false;
|
return false;
|
||||||
if (hwid_cpu != other.hwid_cpu)
|
if (hwid_cpu != other.hwid_cpu)
|
||||||
return false;
|
return false;
|
||||||
return hwid_hdd == other.hwid_hdd;
|
return hwid_hdd == other.hwid_hdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
public class HWIDException extends Exception {
|
public class HWIDException extends Exception {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private static final long serialVersionUID = -5307315891121889972L;
|
private static final long serialVersionUID = -5307315891121889972L;
|
||||||
|
|
||||||
public HWIDException() {
|
public HWIDException() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public HWIDException(String s) {
|
public HWIDException(String s) {
|
||||||
|
|
|
@ -29,17 +29,20 @@ public static void registerHandler(String name, Adapter<HWIDHandler> adapter) {
|
||||||
VerifyHelper.putIfAbsent(HW_HANDLERS, name, Objects.requireNonNull(adapter, "adapter"),
|
VerifyHelper.putIfAbsent(HW_HANDLERS, name, Objects.requireNonNull(adapter, "adapter"),
|
||||||
String.format("HWID handler has been already registered: '%s'", name));
|
String.format("HWID handler has been already registered: '%s'", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerHandlers() {
|
public static void registerHandlers() {
|
||||||
if (!registredHandl) {
|
if (!registredHandl) {
|
||||||
registerHandler("accept", AcceptHWIDHandler::new);
|
registerHandler("accept", AcceptHWIDHandler::new);
|
||||||
registerHandler("mysql",MysqlHWIDHandler::new);
|
registerHandler("mysql", MysqlHWIDHandler::new);
|
||||||
registerHandler("json",JsonHWIDHandler::new);
|
registerHandler("json", JsonHWIDHandler::new);
|
||||||
registredHandl = true;
|
registredHandl = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HWIDHandler(BlockConfigEntry block) {
|
protected HWIDHandler(BlockConfigEntry block) {
|
||||||
super(block);
|
super(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void ban(List<HWID> hwid) throws HWIDException;
|
public abstract void ban(List<HWID> hwid) throws HWIDException;
|
||||||
|
|
||||||
public void check(HWID hwid, String username) throws HWIDException {
|
public void check(HWID hwid, String username) throws HWIDException {
|
||||||
|
@ -50,7 +53,7 @@ public void check(HWID hwid, String username) throws HWIDException {
|
||||||
public abstract void check0(HWID hwid, String username) throws HWIDException;
|
public abstract void check0(HWID hwid, String username) throws HWIDException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract void close() throws IOException;
|
public abstract void close();
|
||||||
|
|
||||||
public abstract List<HWID> getHwid(String username) throws HWIDException;
|
public abstract List<HWID> getHwid(String username) throws HWIDException;
|
||||||
|
|
||||||
|
|
|
@ -58,16 +58,17 @@ public final class JsonHWIDHandler extends HWIDHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void ban(List<HWID> l_hwid) throws HWIDException {
|
public void ban(List<HWID> l_hwid) throws HWIDException {
|
||||||
for(HWID hwid : l_hwid) {
|
for (HWID hwid : l_hwid) {
|
||||||
JsonObject request = Json.object().add(hddKeyName, hwid.getHwid_hdd()).add(cpuKeyName, hwid.getHwid_cpu()).add(biosKeyName, hwid.getHwid_bios());
|
JsonObject request = Json.object().add(hddKeyName, hwid.getHwid_hdd()).add(cpuKeyName, hwid.getHwid_cpu()).add(biosKeyName, hwid.getHwid_bios());
|
||||||
try {
|
try {
|
||||||
request(request,urlBan);
|
request(request, urlBan);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
throw new HWIDException("HWID service error");
|
throw new HWIDException("HWID service error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonObject request(JsonObject request, URL url) throws HWIDException, IOException {
|
public JsonObject request(JsonObject request, URL url) throws HWIDException, IOException {
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
connection.setDoInput(true);
|
connection.setDoInput(true);
|
||||||
|
@ -95,20 +96,21 @@ public JsonObject request(JsonObject request, URL url) throws HWIDException, IOE
|
||||||
throw new HWIDException("HWID server response is malformed");
|
throw new HWIDException("HWID server response is malformed");
|
||||||
|
|
||||||
JsonObject response = content.asObject();
|
JsonObject response = content.asObject();
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void check0(HWID hwid, String username) throws HWIDException {
|
public void check0(HWID hwid, String username) throws HWIDException {
|
||||||
JsonObject request = Json.object().add(loginKeyName,username).add(hddKeyName,hwid.getHwid_hdd()).add(cpuKeyName,hwid.getHwid_cpu()).add(biosKeyName,hwid.getHwid_bios());
|
JsonObject request = Json.object().add(loginKeyName, username).add(hddKeyName, hwid.getHwid_hdd()).add(cpuKeyName, hwid.getHwid_cpu()).add(biosKeyName, hwid.getHwid_bios());
|
||||||
JsonObject response;
|
JsonObject response;
|
||||||
try {
|
try {
|
||||||
response = request(request,url);
|
response = request(request, url);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
throw new HWIDException("HWID service error");
|
throw new HWIDException("HWID service error");
|
||||||
}
|
}
|
||||||
boolean isBanned = response.getBoolean(isBannedKeyName,false);
|
boolean isBanned = response.getBoolean(isBannedKeyName, false);
|
||||||
if(isBanned) throw new HWIDException("You will BANNED!");
|
if (isBanned) throw new HWIDException("You will BANNED!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -118,24 +120,23 @@ public void close() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<HWID> getHwid(String username) throws HWIDException {
|
public List<HWID> getHwid(String username) throws HWIDException {
|
||||||
JsonObject request = Json.object().add(loginKeyName,username);
|
JsonObject request = Json.object().add(loginKeyName, username);
|
||||||
JsonObject responce;
|
JsonObject responce;
|
||||||
try {
|
try {
|
||||||
responce = request(request,urlGet);
|
responce = request(request, urlGet);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
throw new HWIDException("HWID service error");
|
throw new HWIDException("HWID service error");
|
||||||
}
|
}
|
||||||
JsonArray array = responce.get("hwids").asArray();
|
JsonArray array = responce.get("hwids").asArray();
|
||||||
ArrayList<HWID> hwids = new ArrayList<>();
|
ArrayList<HWID> hwids = new ArrayList<>();
|
||||||
for(JsonValue i : array)
|
for (JsonValue i : array) {
|
||||||
{
|
long hdd, cpu, bios;
|
||||||
long hdd,cpu,bios;
|
|
||||||
JsonObject object = i.asObject();
|
JsonObject object = i.asObject();
|
||||||
hdd = object.getLong(hddKeyName,0);
|
hdd = object.getLong(hddKeyName, 0);
|
||||||
cpu = object.getLong(cpuKeyName,0);
|
cpu = object.getLong(cpuKeyName, 0);
|
||||||
bios = object.getLong(biosKeyName,0);
|
bios = object.getLong(biosKeyName, 0);
|
||||||
HWID hwid = HWID.gen(hdd,cpu,bios);
|
HWID hwid = HWID.gen(hdd, cpu, bios);
|
||||||
hwids.add(hwid);
|
hwids.add(hwid);
|
||||||
}
|
}
|
||||||
return hwids;
|
return hwids;
|
||||||
|
@ -143,10 +144,10 @@ public List<HWID> getHwid(String username) throws HWIDException {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unban(List<HWID> l_hwid) throws HWIDException {
|
public void unban(List<HWID> l_hwid) throws HWIDException {
|
||||||
for(HWID hwid : l_hwid) {
|
for (HWID hwid : l_hwid) {
|
||||||
JsonObject request = Json.object().add(hddKeyName, hwid.getHwid_hdd()).add(cpuKeyName, hwid.getHwid_cpu()).add(biosKeyName, hwid.getHwid_bios());
|
JsonObject request = Json.object().add(hddKeyName, hwid.getHwid_hdd()).add(cpuKeyName, hwid.getHwid_cpu()).add(biosKeyName, hwid.getHwid_bios());
|
||||||
try {
|
try {
|
||||||
request(request,urlUnBan);
|
request(request, urlUnBan);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
throw new HWIDException("HWID service error");
|
throw new HWIDException("HWID service error");
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class MysqlHWIDHandler extends HWIDHandler {
|
||||||
private final String banMessage;
|
private final String banMessage;
|
||||||
private final String isBannedName;
|
private final String isBannedName;
|
||||||
private final String loginName;
|
private final String loginName;
|
||||||
private final String hddName,cpuName,biosName;
|
private final String hddName, cpuName, biosName;
|
||||||
private final String[] queryParams;
|
private final String[] queryParams;
|
||||||
private final String queryUpd;
|
private final String queryUpd;
|
||||||
private final String[] queryParamsUpd;
|
private final String[] queryParamsUpd;
|
||||||
|
@ -72,7 +72,7 @@ public void check0(HWID hwid, String username) throws HWIDException {
|
||||||
Connection c = mySQLHolder.getConnection();
|
Connection c = mySQLHolder.getConnection();
|
||||||
|
|
||||||
PreparedStatement s = c.prepareStatement(query);
|
PreparedStatement s = c.prepareStatement(query);
|
||||||
String[] replaceParams = {"hwid_hdd", String.valueOf(hwid.getHwid_hdd()), "hwid_cpu", String.valueOf(hwid.getHwid_cpu()), "hwid_bios", String.valueOf(hwid.getHwid_bios()),"login",username};
|
String[] replaceParams = {"hwid_hdd", String.valueOf(hwid.getHwid_hdd()), "hwid_cpu", String.valueOf(hwid.getHwid_cpu()), "hwid_bios", String.valueOf(hwid.getHwid_bios()), "login", username};
|
||||||
for (int i = 0; i < queryParams.length; i++) {
|
for (int i = 0; i < queryParams.length; i++) {
|
||||||
s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams));
|
s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams));
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ public void check0(HWID hwid, String username) throws HWIDException {
|
||||||
try (ResultSet set = s.executeQuery()) {
|
try (ResultSet set = s.executeQuery()) {
|
||||||
boolean isOne = false;
|
boolean isOne = false;
|
||||||
boolean needWrite = true;
|
boolean needWrite = true;
|
||||||
while(set.next()) {
|
while (set.next()) {
|
||||||
isOne = true;
|
isOne = true;
|
||||||
boolean isBanned = set.getBoolean(isBannedName);
|
boolean isBanned = set.getBoolean(isBannedName);
|
||||||
if (isBanned) throw new HWIDException(banMessage);
|
if (isBanned) throw new HWIDException(banMessage);
|
||||||
|
@ -95,8 +95,7 @@ public void check0(HWID hwid, String username) throws HWIDException {
|
||||||
writeHWID(hwid, username, c);
|
writeHWID(hwid, username, c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(needWrite)
|
if (needWrite) {
|
||||||
{
|
|
||||||
writeHWID(hwid, username, c);
|
writeHWID(hwid, username, c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -105,9 +104,9 @@ public void check0(HWID hwid, String username) throws HWIDException {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void writeHWID(HWID hwid, String username, Connection c)
|
|
||||||
{
|
public void writeHWID(HWID hwid, String username, Connection c) {
|
||||||
LogHelper.debug("Write HWID %s from username %s",hwid.toString(),username);
|
LogHelper.debug("Write HWID %s from username %s", hwid.toString(), username);
|
||||||
try (PreparedStatement a = c.prepareStatement(queryUpd)) {
|
try (PreparedStatement a = c.prepareStatement(queryUpd)) {
|
||||||
//IF
|
//IF
|
||||||
String[] replaceParamsUpd = {"hwid_hdd", String.valueOf(hwid.getHwid_hdd()), "hwid_cpu", String.valueOf(hwid.getHwid_cpu()), "hwid_bios", String.valueOf(hwid.getHwid_bios()), "login", username};
|
String[] replaceParamsUpd = {"hwid_hdd", String.valueOf(hwid.getHwid_hdd()), "hwid_cpu", String.valueOf(hwid.getHwid_cpu()), "hwid_bios", String.valueOf(hwid.getHwid_bios()), "login", username};
|
||||||
|
@ -120,9 +119,9 @@ public void writeHWID(HWID hwid, String username, Connection c)
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void setIsBanned(HWID hwid,boolean isBanned)
|
|
||||||
{
|
public void setIsBanned(HWID hwid, boolean isBanned) {
|
||||||
LogHelper.debug("%s Request HWID: %s",isBanned ? "Ban" : "UnBan",hwid.toString());
|
LogHelper.debug("%s Request HWID: %s", isBanned ? "Ban" : "UnBan", hwid.toString());
|
||||||
Connection c = null;
|
Connection c = null;
|
||||||
try {
|
try {
|
||||||
c = mySQLHolder.getConnection();
|
c = mySQLHolder.getConnection();
|
||||||
|
@ -141,36 +140,35 @@ public void setIsBanned(HWID hwid,boolean isBanned)
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public void ban(List<HWID> list) throws HWIDException {
|
|
||||||
|
|
||||||
for(HWID hwid : list)
|
@Override
|
||||||
{
|
public void ban(List<HWID> list) {
|
||||||
setIsBanned(hwid,true);
|
|
||||||
|
for (HWID hwid : list) {
|
||||||
|
setIsBanned(hwid, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unban(List<HWID> list) throws HWIDException {
|
public void unban(List<HWID> list) {
|
||||||
for(HWID hwid : list)
|
for (HWID hwid : list) {
|
||||||
{
|
setIsBanned(hwid, false);
|
||||||
setIsBanned(hwid,false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<HWID> getHwid(String username) throws HWIDException {
|
public List<HWID> getHwid(String username) {
|
||||||
try {
|
try {
|
||||||
LogHelper.debug("Try find HWID from username %s",username);
|
LogHelper.debug("Try find HWID from username %s", username);
|
||||||
Connection c = mySQLHolder.getConnection();
|
Connection c = mySQLHolder.getConnection();
|
||||||
PreparedStatement s = c.prepareStatement(querySelect);
|
PreparedStatement s = c.prepareStatement(querySelect);
|
||||||
String[] replaceParams = {"login", username};
|
String[] replaceParams = {"login", username};
|
||||||
for (int i = 0; i < queryParamsSelect.length; i++) {
|
for (int i = 0; i < queryParamsSelect.length; i++) {
|
||||||
s.setString(i + 1, CommonHelper.replace(queryParamsSelect[i], replaceParams));
|
s.setString(i + 1, CommonHelper.replace(queryParamsSelect[i], replaceParams));
|
||||||
}
|
}
|
||||||
long hdd,cpu,bios;
|
long hdd, cpu, bios;
|
||||||
try (ResultSet set = s.executeQuery()) {
|
try (ResultSet set = s.executeQuery()) {
|
||||||
if(!set.next()) {
|
if (!set.next()) {
|
||||||
LogHelper.error(new HWIDException("HWID not found"));
|
LogHelper.error(new HWIDException("HWID not found"));
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
@ -179,11 +177,12 @@ public List<HWID> getHwid(String username) throws HWIDException {
|
||||||
bios = set.getLong(biosName);
|
bios = set.getLong(biosName);
|
||||||
}
|
}
|
||||||
ArrayList<HWID> list = new ArrayList<>();
|
ArrayList<HWID> list = new ArrayList<>();
|
||||||
HWID hwid = HWID.gen(hdd,bios,cpu);
|
HWID hwid = HWID.gen(hdd, bios, cpu);
|
||||||
if(hdd == 0 && cpu == 0 && bios == 0) {LogHelper.warning("Null HWID");}
|
if (hdd == 0 && cpu == 0 && bios == 0) {
|
||||||
else {
|
LogHelper.warning("Null HWID");
|
||||||
|
} else {
|
||||||
list.add(hwid);
|
list.add(hwid);
|
||||||
LogHelper.debug("Username: %s HWID: %s",username,hwid.toString());
|
LogHelper.debug("Username: %s HWID: %s", username, hwid.toString());
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
|
|
@ -20,16 +20,16 @@ protected DigestAuthProvider(BlockConfigEntry block) {
|
||||||
protected final void verifyDigest(String validDigest, String password) throws AuthException {
|
protected final void verifyDigest(String validDigest, String password) throws AuthException {
|
||||||
boolean valid;
|
boolean valid;
|
||||||
if (digest == DigestAlgorithm.PLAIN)
|
if (digest == DigestAlgorithm.PLAIN)
|
||||||
valid = password.equals(validDigest);
|
valid = password.equals(validDigest);
|
||||||
else if (validDigest == null)
|
else if (validDigest == null)
|
||||||
valid = false;
|
valid = false;
|
||||||
else {
|
else {
|
||||||
byte[] actualDigest = SecurityHelper.digest(digest, password);
|
byte[] actualDigest = SecurityHelper.digest(digest, password);
|
||||||
valid = SecurityHelper.toHex(actualDigest).equals(validDigest);
|
valid = SecurityHelper.toHex(actualDigest).equals(validDigest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify is valid
|
// Verify is valid
|
||||||
if (!valid)
|
if (!valid)
|
||||||
authError("Incorrect username or password");
|
authError("Incorrect username or password");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ public AuthProviderResult auth(String login, String password, String ip) throws
|
||||||
// Verify digest and return true username
|
// Verify digest and return true username
|
||||||
verifyDigest(entry == null ? null : entry.password, password);
|
verifyDigest(entry == null ? null : entry.password, password);
|
||||||
if (entry == null || entry.ip != null && !entry.ip.equals(ip))
|
if (entry == null || entry.ip != null && !entry.ip.equals(ip))
|
||||||
authError("Authentication from this IP is not allowed");
|
authError("Authentication from this IP is not allowed");
|
||||||
|
|
||||||
// We're done
|
// We're done
|
||||||
return new AuthProviderResult(entry.username, SecurityHelper.randomStringToken());
|
return new AuthProviderResult(entry.username, SecurityHelper.randomStringToken());
|
||||||
|
@ -80,7 +80,7 @@ public void close() {
|
||||||
private void updateCache() throws IOException {
|
private void updateCache() throws IOException {
|
||||||
FileTime lastModified = IOHelper.readAttributes(file).lastModifiedTime();
|
FileTime lastModified = IOHelper.readAttributes(file).lastModifiedTime();
|
||||||
if (lastModified.equals(cacheLastModified))
|
if (lastModified.equals(cacheLastModified))
|
||||||
return; // Not modified, so cache is up-to-date
|
return; // Not modified, so cache is up-to-date
|
||||||
|
|
||||||
// Read file
|
// Read file
|
||||||
LogHelper.info("Recaching auth provider file: '%s'", file);
|
LogHelper.info("Recaching auth provider file: '%s'", file);
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
package ru.gravit.launchserver.auth.provider;
|
package ru.gravit.launchserver.auth.provider;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
import com.eclipsesource.json.Json;
|
import com.eclipsesource.json.Json;
|
||||||
import com.eclipsesource.json.JsonObject;
|
import com.eclipsesource.json.JsonObject;
|
||||||
|
@ -49,19 +44,19 @@ public final class JsonAuthProvider extends AuthProvider {
|
||||||
@Override
|
@Override
|
||||||
public AuthProviderResult auth(String login, String password, String ip) throws IOException {
|
public AuthProviderResult auth(String login, String password, String ip) throws IOException {
|
||||||
JsonObject request = Json.object().add(userKeyName, login).add(passKeyName, password).add(ipKeyName, ip);
|
JsonObject request = Json.object().add(userKeyName, login).add(passKeyName, password).add(ipKeyName, ip);
|
||||||
JsonValue content = HTTPRequest.jsonRequest(request,url);
|
JsonValue content = HTTPRequest.jsonRequest(request, url);
|
||||||
if (!content.isObject())
|
if (!content.isObject())
|
||||||
return authError("Authentication server response is malformed");
|
return authError("Authentication server response is malformed");
|
||||||
|
|
||||||
JsonObject response = content.asObject();
|
JsonObject response = content.asObject();
|
||||||
String value;
|
String value;
|
||||||
|
|
||||||
if ((value = response.getString(responseUserKeyName, null)) != null)
|
if ((value = response.getString(responseUserKeyName, null)) != null)
|
||||||
return new AuthProviderResult(value, SecurityHelper.randomStringToken());
|
return new AuthProviderResult(value, SecurityHelper.randomStringToken());
|
||||||
else if ((value = response.getString(responseErrorKeyName, null)) != null)
|
else if ((value = response.getString(responseErrorKeyName, null)) != null)
|
||||||
return authError(value);
|
return authError(value);
|
||||||
else
|
else
|
||||||
return authError("Authentication server response is malformed");
|
return authError("Authentication server response is malformed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -66,10 +66,10 @@ public AuthProviderResult auth(String login, String password, String ip) throws
|
||||||
// Verify there's no error
|
// Verify there's no error
|
||||||
JsonObject response = makeJSONRequest(URL, request);
|
JsonObject response = makeJSONRequest(URL, request);
|
||||||
if (response == null)
|
if (response == null)
|
||||||
authError("Empty mojang response");
|
authError("Empty mojang response");
|
||||||
JsonValue errorMessage = response.get("errorMessage");
|
JsonValue errorMessage = response.get("errorMessage");
|
||||||
if (errorMessage != null)
|
if (errorMessage != null)
|
||||||
authError(errorMessage.asString());
|
authError(errorMessage.asString());
|
||||||
|
|
||||||
// Parse JSON data
|
// Parse JSON data
|
||||||
JsonObject selectedProfile = response.get("selectedProfile").asObject();
|
JsonObject selectedProfile = response.get("selectedProfile").asObject();
|
||||||
|
|
|
@ -36,7 +36,7 @@ public AuthProviderResult auth(String login, String password, String ip) throws
|
||||||
PreparedStatement s = c.prepareStatement(query);
|
PreparedStatement s = c.prepareStatement(query);
|
||||||
String[] replaceParams = {"login", login, "password", password, "ip", ip};
|
String[] replaceParams = {"login", login, "password", password, "ip", ip};
|
||||||
for (int i = 0; i < queryParams.length; i++)
|
for (int i = 0; i < queryParams.length; i++)
|
||||||
s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams));
|
s.setString(i + 1, CommonHelper.replace(queryParams[i], replaceParams));
|
||||||
|
|
||||||
// Execute SQL query
|
// Execute SQL query
|
||||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||||
|
|
|
@ -23,7 +23,7 @@ public AuthProviderResult auth(String login, String password, String ip) throws
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
AuthProvider provider = this.provider;
|
AuthProvider provider = this.provider;
|
||||||
if (provider != null)
|
if (provider != null)
|
||||||
provider.close();
|
provider.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthProvider getProvider() {
|
private AuthProvider getProvider() {
|
||||||
|
|
|
@ -17,28 +17,31 @@ public BuildContext(ZipOutputStream output, JAConfigurator config) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
public void pushFile(String filename,InputStream inputStream) throws IOException {
|
|
||||||
|
public void pushFile(String filename, InputStream inputStream) throws IOException {
|
||||||
ZipEntry zip = IOHelper.newZipEntry(filename);
|
ZipEntry zip = IOHelper.newZipEntry(filename);
|
||||||
output.putNextEntry(zip);
|
output.putNextEntry(zip);
|
||||||
IOHelper.transfer(inputStream,output);
|
IOHelper.transfer(inputStream, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pushJarFile(JarInputStream input) throws IOException {
|
public void pushJarFile(JarInputStream input) throws IOException {
|
||||||
ZipEntry e = input.getNextEntry();
|
ZipEntry e = input.getNextEntry();
|
||||||
while (e != null) {
|
while (e != null) {
|
||||||
output.putNextEntry(e);
|
output.putNextEntry(e);
|
||||||
IOHelper.transfer(input,output);
|
IOHelper.transfer(input, output);
|
||||||
e = input.getNextEntry();
|
e = input.getNextEntry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pushJarFile(JarInputStream input, Set<String> blacklist) throws IOException {
|
public void pushJarFile(JarInputStream input, Set<String> blacklist) throws IOException {
|
||||||
ZipEntry e = input.getNextEntry();
|
ZipEntry e = input.getNextEntry();
|
||||||
while (e != null) {
|
while (e != null) {
|
||||||
if(blacklist.contains(e.getName())){
|
if (blacklist.contains(e.getName())) {
|
||||||
e = input.getNextEntry();
|
e = input.getNextEntry();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
output.putNextEntry(e);
|
output.putNextEntry(e);
|
||||||
IOHelper.transfer(input,output);
|
IOHelper.transfer(input, output);
|
||||||
e = input.getNextEntry();
|
e = input.getNextEntry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,106 +17,107 @@
|
||||||
import net.sf.launch4j.config.VersionInfo;
|
import net.sf.launch4j.config.VersionInfo;
|
||||||
|
|
||||||
public final class EXEL4JLauncherBinary extends LauncherBinary {
|
public final class EXEL4JLauncherBinary extends LauncherBinary {
|
||||||
private final static class Launch4JLog extends Log {
|
private final static class Launch4JLog extends Log {
|
||||||
private static final Launch4JLog INSTANCE = new Launch4JLog();
|
private static final Launch4JLog INSTANCE = new Launch4JLog();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void append(String s) {
|
public void append(String s) {
|
||||||
LogHelper.subInfo(s);
|
LogHelper.subInfo(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL constants
|
// URL constants
|
||||||
private static final String DOWNLOAD_URL = "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"; // Oracle
|
private static final String DOWNLOAD_URL = "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"; // Oracle
|
||||||
// JRE
|
// JRE
|
||||||
// 8
|
// 8
|
||||||
|
|
||||||
// File constants
|
// File constants
|
||||||
private final Path faviconFile;
|
private final Path faviconFile;
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public EXEL4JLauncherBinary(LaunchServer server) {
|
public EXEL4JLauncherBinary(LaunchServer server) {
|
||||||
super(server, server.dir.resolve(server.config.binaryName + ".exe"));
|
super(server, server.dir.resolve(server.config.binaryName + ".exe"));
|
||||||
faviconFile = server.dir.resolve("favicon.ico");
|
faviconFile = server.dir.resolve("favicon.ico");
|
||||||
setConfig();
|
setConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build() throws IOException {
|
public void build() throws IOException {
|
||||||
LogHelper.info("Building launcher EXE binary file (Using Launch4J)");
|
LogHelper.info("Building launcher EXE binary file (Using Launch4J)");
|
||||||
|
|
||||||
// Set favicon path
|
// Set favicon path
|
||||||
Config config = ConfigPersister.getInstance().getConfig();
|
Config config = ConfigPersister.getInstance().getConfig();
|
||||||
if (IOHelper.isFile(faviconFile))
|
if (IOHelper.isFile(faviconFile))
|
||||||
config.setIcon(faviconFile.toFile());
|
config.setIcon(faviconFile.toFile());
|
||||||
else {
|
else {
|
||||||
config.setIcon(null);
|
config.setIcon(null);
|
||||||
LogHelper.warning("Missing favicon.ico file");
|
LogHelper.warning("Missing favicon.ico file");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start building
|
// Start building
|
||||||
Builder builder = new Builder(Launch4JLog.INSTANCE);
|
Builder builder = new Builder(Launch4JLog.INSTANCE);
|
||||||
try {
|
try {
|
||||||
builder.build();
|
builder.build();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setConfig() {
|
private void setConfig() {
|
||||||
Config config = new Config();
|
Config config = new Config();
|
||||||
// Set string options
|
// Set string options
|
||||||
config.setChdir(".");
|
config.setChdir(".");
|
||||||
config.setErrTitle("JVM Error");
|
config.setErrTitle("JVM Error");
|
||||||
config.setDownloadUrl(DOWNLOAD_URL);
|
config.setDownloadUrl(DOWNLOAD_URL);
|
||||||
|
|
||||||
// Set boolean options
|
// Set boolean options
|
||||||
config.setPriorityIndex(0);
|
config.setPriorityIndex(0);
|
||||||
config.setHeaderType(Config.GUI_HEADER);
|
config.setHeaderType(Config.GUI_HEADER);
|
||||||
config.setStayAlive(false);
|
config.setStayAlive(false);
|
||||||
config.setRestartOnCrash(false);
|
config.setRestartOnCrash(false);
|
||||||
|
|
||||||
// Prepare JRE
|
// Prepare JRE
|
||||||
Jre jre = new Jre();
|
Jre jre = new Jre();
|
||||||
jre.setMinVersion("1.8.0");
|
jre.setMinVersion("1.8.0");
|
||||||
jre.setRuntimeBits(Jre.RUNTIME_BITS_64_AND_32);
|
jre.setRuntimeBits(Jre.RUNTIME_BITS_64_AND_32);
|
||||||
jre.setJdkPreference(Jre.JDK_PREFERENCE_PREFER_JRE);
|
jre.setJdkPreference(Jre.JDK_PREFERENCE_PREFER_JRE);
|
||||||
config.setJre(jre);
|
config.setJre(jre);
|
||||||
|
|
||||||
// Prepare version info (product)
|
// Prepare version info (product)
|
||||||
VersionInfo info = new VersionInfo();
|
VersionInfo info = new VersionInfo();
|
||||||
info.setProductName(server.config.launch4j.productName);
|
info.setProductName(server.config.launch4j.productName);
|
||||||
info.setProductVersion(formatVars(server.config.launch4j.productVer));
|
info.setProductVersion(formatVars(server.config.launch4j.productVer));
|
||||||
info.setFileDescription(server.config.launch4j.fileDesc);
|
info.setFileDescription(server.config.launch4j.fileDesc);
|
||||||
info.setFileVersion(formatVars(server.config.launch4j.fileVer));
|
info.setFileVersion(formatVars(server.config.launch4j.fileVer));
|
||||||
info.setCopyright(server.config.launch4j.copyright);
|
info.setCopyright(server.config.launch4j.copyright);
|
||||||
info.setTrademarks(server.config.launch4j.trademarks);
|
info.setTrademarks(server.config.launch4j.trademarks);
|
||||||
info.setInternalName(formatVars(server.config.launch4j.internalName));
|
info.setInternalName(formatVars(server.config.launch4j.internalName));
|
||||||
// Prepare version info (file)
|
// Prepare version info (file)
|
||||||
info.setTxtFileVersion(formatVars(server.config.launch4j.txtFileVersion));
|
info.setTxtFileVersion(formatVars(server.config.launch4j.txtFileVersion));
|
||||||
info.setTxtProductVersion(formatVars(server.config.launch4j.txtProductVersion));
|
info.setTxtProductVersion(formatVars(server.config.launch4j.txtProductVersion));
|
||||||
// Prepare version info (misc)
|
// Prepare version info (misc)
|
||||||
info.setOriginalFilename(binaryFile.getFileName().toString());
|
info.setOriginalFilename(binaryFile.getFileName().toString());
|
||||||
info.setLanguage(LanguageID.RUSSIAN);
|
info.setLanguage(LanguageID.RUSSIAN);
|
||||||
config.setVersionInfo(info);
|
config.setVersionInfo(info);
|
||||||
|
|
||||||
// Set JAR wrapping options
|
// Set JAR wrapping options
|
||||||
config.setDontWrapJar(false);
|
config.setDontWrapJar(false);
|
||||||
config.setJar(server.launcherBinary.syncBinaryFile.toFile());
|
config.setJar(server.launcherBinary.syncBinaryFile.toFile());
|
||||||
config.setOutfile(binaryFile.toFile());
|
config.setOutfile(binaryFile.toFile());
|
||||||
|
|
||||||
// Return prepared config
|
// Return prepared config
|
||||||
ConfigPersister.getInstance().setAntConfig(config, null);
|
ConfigPersister.getInstance().setAntConfig(config, null);
|
||||||
}
|
}
|
||||||
private static String VERSION = Launcher.getVersion().getVersionString();
|
|
||||||
private static int BUILD = Launcher.getVersion().build;
|
private static String VERSION = Launcher.getVersion().getVersionString();
|
||||||
public static String formatVars(String mask)
|
private static int BUILD = Launcher.getVersion().build;
|
||||||
{
|
|
||||||
return String.format(mask, VERSION, BUILD);
|
public static String formatVars(String mask) {
|
||||||
}
|
return String.format(mask, VERSION, BUILD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,16 +9,16 @@
|
||||||
|
|
||||||
public class EXELauncherBinary extends LauncherBinary {
|
public class EXELauncherBinary extends LauncherBinary {
|
||||||
|
|
||||||
public EXELauncherBinary(LaunchServer server) {
|
public EXELauncherBinary(LaunchServer server) {
|
||||||
super(server, server.dir.resolve(server.config.binaryName + ".exe"));
|
super(server, server.dir.resolve(server.config.binaryName + ".exe"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build() throws IOException {
|
public void build() throws IOException {
|
||||||
if (IOHelper.isFile(binaryFile)) {
|
if (IOHelper.isFile(binaryFile)) {
|
||||||
LogHelper.subWarning("Deleting obsolete launcher EXE binary file");
|
LogHelper.subWarning("Deleting obsolete launcher EXE binary file");
|
||||||
Files.delete(binaryFile);
|
Files.delete(binaryFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ public class JAConfigurator implements AutoCloseable {
|
||||||
StringBuilder body;
|
StringBuilder body;
|
||||||
StringBuilder moduleBody;
|
StringBuilder moduleBody;
|
||||||
int autoincrement;
|
int autoincrement;
|
||||||
|
|
||||||
public JAConfigurator(Class<?> configclass) throws NotFoundException {
|
public JAConfigurator(Class<?> configclass) throws NotFoundException {
|
||||||
classname = configclass.getName();
|
classname = configclass.getName();
|
||||||
ctClass = pool.get(classname);
|
ctClass = pool.get(classname);
|
||||||
|
@ -22,8 +23,8 @@ public JAConfigurator(Class<?> configclass) throws NotFoundException {
|
||||||
moduleBody = new StringBuilder("{ isInitModules = true; ");
|
moduleBody = new StringBuilder("{ isInitModules = true; ");
|
||||||
autoincrement = 0;
|
autoincrement = 0;
|
||||||
}
|
}
|
||||||
public void addModuleClass(String fullName)
|
|
||||||
{
|
public void addModuleClass(String fullName) {
|
||||||
moduleBody.append("ru.gravit.launcher.modules.Module mod");
|
moduleBody.append("ru.gravit.launcher.modules.Module mod");
|
||||||
moduleBody.append(autoincrement);
|
moduleBody.append(autoincrement);
|
||||||
moduleBody.append(" = new ");
|
moduleBody.append(" = new ");
|
||||||
|
@ -34,10 +35,12 @@ public void addModuleClass(String fullName)
|
||||||
moduleBody.append(" , true );");
|
moduleBody.append(" , true );");
|
||||||
autoincrement++;
|
autoincrement++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
ctClass.defrost();
|
ctClass.defrost();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getBytecode() throws IOException, CannotCompileException {
|
public byte[] getBytecode() throws IOException, CannotCompileException {
|
||||||
body.append("}");
|
body.append("}");
|
||||||
moduleBody.append("}");
|
moduleBody.append("}");
|
||||||
|
@ -45,31 +48,30 @@ public byte[] getBytecode() throws IOException, CannotCompileException {
|
||||||
initModuleMethod.insertAfter(moduleBody.toString());
|
initModuleMethod.insertAfter(moduleBody.toString());
|
||||||
return ctClass.toBytecode();
|
return ctClass.toBytecode();
|
||||||
}
|
}
|
||||||
public String getZipEntryPath()
|
|
||||||
{
|
public String getZipEntryPath() {
|
||||||
return classname.replace('.','/').concat(".class");
|
return classname.replace('.', '/').concat(".class");
|
||||||
}
|
}
|
||||||
public void setAddress(String address)
|
|
||||||
{
|
public void setAddress(String address) {
|
||||||
body.append("this.address = \"");
|
body.append("this.address = \"");
|
||||||
body.append(address);
|
body.append(address);
|
||||||
body.append("\";");
|
body.append("\";");
|
||||||
}
|
}
|
||||||
public void setProjectName(String name)
|
|
||||||
{
|
public void setProjectName(String name) {
|
||||||
body.append("this.projectname = \"");
|
body.append("this.projectname = \"");
|
||||||
body.append(name);
|
body.append(name);
|
||||||
body.append("\";");
|
body.append("\";");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPort(int port)
|
public void setPort(int port) {
|
||||||
{
|
|
||||||
body.append("this.port = ");
|
body.append("this.port = ");
|
||||||
body.append(port);
|
body.append(port);
|
||||||
body.append(";");
|
body.append(";");
|
||||||
}
|
}
|
||||||
public ClassPool getPool()
|
|
||||||
{
|
public ClassPool getPool() {
|
||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,193 +36,193 @@
|
||||||
import proguard.ProGuard;
|
import proguard.ProGuard;
|
||||||
|
|
||||||
public final class JARLauncherBinary extends LauncherBinary {
|
public final class JARLauncherBinary extends LauncherBinary {
|
||||||
private final class RuntimeDirVisitor extends SimpleFileVisitor<Path> {
|
private final class RuntimeDirVisitor extends SimpleFileVisitor<Path> {
|
||||||
private final ZipOutputStream output;
|
private final ZipOutputStream output;
|
||||||
private final Map<String, byte[]> runtime;
|
private final Map<String, byte[]> runtime;
|
||||||
|
|
||||||
private RuntimeDirVisitor(ZipOutputStream output, Map<String, byte[]> runtime) {
|
private RuntimeDirVisitor(ZipOutputStream output, Map<String, byte[]> runtime) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
this.runtime = runtime;
|
this.runtime = runtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||||
String dirName = IOHelper.toString(runtimeDir.relativize(dir));
|
String dirName = IOHelper.toString(runtimeDir.relativize(dir));
|
||||||
output.putNextEntry(newEntry(dirName + '/'));
|
output.putNextEntry(newEntry(dirName + '/'));
|
||||||
return super.preVisitDirectory(dir, attrs);
|
return super.preVisitDirectory(dir, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
String fileName = IOHelper.toString(runtimeDir.relativize(file));
|
String fileName = IOHelper.toString(runtimeDir.relativize(file));
|
||||||
runtime.put(fileName, SecurityHelper.digest(DigestAlgorithm.MD5, file));
|
runtime.put(fileName, SecurityHelper.digest(DigestAlgorithm.MD5, file));
|
||||||
|
|
||||||
// Create zip entry and transfer contents
|
// Create zip entry and transfer contents
|
||||||
output.putNextEntry(newEntry(fileName));
|
output.putNextEntry(newEntry(fileName));
|
||||||
IOHelper.transfer(file, output);
|
IOHelper.transfer(file, output);
|
||||||
|
|
||||||
// Return result
|
// Return result
|
||||||
return super.visitFile(file, attrs);
|
return super.visitFile(file, attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ZipEntry newEntry(String fileName) {
|
private static ZipEntry newEntry(String fileName) {
|
||||||
return newZipEntry(Launcher.RUNTIME_DIR + IOHelper.CROSS_SEPARATOR + fileName);
|
return newZipEntry(Launcher.RUNTIME_DIR + IOHelper.CROSS_SEPARATOR + fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final Path runtimeDir;
|
public final Path runtimeDir;
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final Path initScriptFile;
|
public final Path initScriptFile;
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final Path obfJar;
|
public final Path obfJar;
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public JARLauncherBinary(LaunchServer server) throws IOException {
|
public JARLauncherBinary(LaunchServer server) throws IOException {
|
||||||
super(server, server.dir.resolve(server.config.binaryName + ".jar"),
|
super(server, server.dir.resolve(server.config.binaryName + ".jar"),
|
||||||
server.dir.resolve(server.config.binaryName + (server.config.sign.enabled ? "-sign.jar" : "-obf.jar")));
|
server.dir.resolve(server.config.binaryName + (server.config.sign.enabled ? "-sign.jar" : "-obf.jar")));
|
||||||
runtimeDir = server.dir.resolve(Launcher.RUNTIME_DIR);
|
runtimeDir = server.dir.resolve(Launcher.RUNTIME_DIR);
|
||||||
initScriptFile = runtimeDir.resolve(Launcher.INIT_SCRIPT_FILE);
|
initScriptFile = runtimeDir.resolve(Launcher.INIT_SCRIPT_FILE);
|
||||||
obfJar = server.config.sign.enabled ? server.dir.resolve(server.config.binaryName + "-obf.jar")
|
obfJar = server.config.sign.enabled ? server.dir.resolve(server.config.binaryName + "-obf.jar")
|
||||||
: syncBinaryFile;
|
: syncBinaryFile;
|
||||||
tryUnpackRuntime();
|
tryUnpackRuntime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build() throws IOException {
|
public void build() throws IOException {
|
||||||
tryUnpackRuntime();
|
tryUnpackRuntime();
|
||||||
|
|
||||||
// Build launcher binary
|
// Build launcher binary
|
||||||
LogHelper.info("Building launcher binary file");
|
LogHelper.info("Building launcher binary file");
|
||||||
stdBuild();
|
stdBuild();
|
||||||
|
|
||||||
// ProGuard
|
// ProGuard
|
||||||
Configuration proguard_cfg = new Configuration();
|
Configuration proguard_cfg = new Configuration();
|
||||||
ConfigurationParser parser = new ConfigurationParser(
|
ConfigurationParser parser = new ConfigurationParser(
|
||||||
server.proguardConf.confStrs.toArray(new String[0]),
|
server.proguardConf.confStrs.toArray(new String[0]),
|
||||||
server.proguardConf.proguard.toFile(), System.getProperties());
|
server.proguardConf.proguard.toFile(), System.getProperties());
|
||||||
try {
|
try {
|
||||||
parser.parse(proguard_cfg);
|
parser.parse(proguard_cfg);
|
||||||
ProGuard proGuard = new ProGuard(proguard_cfg);
|
ProGuard proGuard = new ProGuard(proguard_cfg);
|
||||||
proGuard.execute();
|
proGuard.execute();
|
||||||
} catch (ParseException e1) {
|
} catch (ParseException e1) {
|
||||||
e1.printStackTrace();
|
e1.printStackTrace();
|
||||||
}
|
}
|
||||||
if (server.config.sign.enabled)
|
if (server.config.sign.enabled)
|
||||||
signBuild();
|
signBuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void signBuild() throws IOException {
|
private void signBuild() throws IOException {
|
||||||
try (SignerJar output = new SignerJar(IOHelper.newOutput(syncBinaryFile),
|
try (SignerJar output = new SignerJar(IOHelper.newOutput(syncBinaryFile),
|
||||||
SignerJar.getStore(server.config.sign.key, server.config.sign.storepass, server.config.sign.algo),
|
SignerJar.getStore(server.config.sign.key, server.config.sign.storepass, server.config.sign.algo),
|
||||||
server.config.sign.keyalias, server.config.sign.pass);
|
server.config.sign.keyalias, server.config.sign.pass);
|
||||||
ZipInputStream input = new ZipInputStream(IOHelper.newInput(obfJar))) {
|
ZipInputStream input = new ZipInputStream(IOHelper.newInput(obfJar))) {
|
||||||
ZipEntry e = input.getNextEntry();
|
ZipEntry e = input.getNextEntry();
|
||||||
while (e != null) {
|
while (e != null) {
|
||||||
output.addFileContents(e, input);
|
output.addFileContents(e, input);
|
||||||
e = input.getNextEntry();
|
e = input.getNextEntry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stdBuild() throws IOException {
|
private void stdBuild() throws IOException {
|
||||||
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(binaryFile));
|
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(binaryFile));
|
||||||
JAConfigurator jaConfigurator = new JAConfigurator(AutogenConfig.class)) {
|
JAConfigurator jaConfigurator = new JAConfigurator(AutogenConfig.class)) {
|
||||||
BuildContext context = new BuildContext(output, jaConfigurator);
|
BuildContext context = new BuildContext(output, jaConfigurator);
|
||||||
server.buildHookManager.preHook(context);
|
server.buildHookManager.preHook(context);
|
||||||
jaConfigurator.setAddress(server.config.getAddress());
|
jaConfigurator.setAddress(server.config.getAddress());
|
||||||
jaConfigurator.setPort(server.config.port);
|
jaConfigurator.setPort(server.config.port);
|
||||||
jaConfigurator.setProjectName(server.config.projectName);
|
jaConfigurator.setProjectName(server.config.projectName);
|
||||||
server.buildHookManager.registerAllClientModuleClass(jaConfigurator);
|
server.buildHookManager.registerAllClientModuleClass(jaConfigurator);
|
||||||
try (ZipInputStream input = new ZipInputStream(
|
try (ZipInputStream input = new ZipInputStream(
|
||||||
IOHelper.newInput(IOHelper.getResourceURL("Launcher.jar")))) {
|
IOHelper.newInput(IOHelper.getResourceURL("Launcher.jar")))) {
|
||||||
ZipEntry e = input.getNextEntry();
|
ZipEntry e = input.getNextEntry();
|
||||||
while (e != null) {
|
while (e != null) {
|
||||||
String filename = e.getName();
|
String filename = e.getName();
|
||||||
if (server.buildHookManager.isContainsBlacklist(filename)) {
|
if (server.buildHookManager.isContainsBlacklist(filename)) {
|
||||||
e = input.getNextEntry();
|
e = input.getNextEntry();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
output.putNextEntry(e);
|
output.putNextEntry(e);
|
||||||
} catch (ZipException ex) {
|
} catch (ZipException ex) {
|
||||||
LogHelper.error(ex);
|
LogHelper.error(ex);
|
||||||
e = input.getNextEntry();
|
e = input.getNextEntry();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (filename.endsWith(".class")) {
|
if (filename.endsWith(".class")) {
|
||||||
CharSequence classname = filename.replace('/', '.').subSequence(0,
|
CharSequence classname = filename.replace('/', '.').subSequence(0,
|
||||||
filename.length() - ".class".length());
|
filename.length() - ".class".length());
|
||||||
byte[] bytes;
|
byte[] bytes;
|
||||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048)) {
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048)) {
|
||||||
IOHelper.transfer(input, outputStream);
|
IOHelper.transfer(input, outputStream);
|
||||||
bytes = outputStream.toByteArray();
|
bytes = outputStream.toByteArray();
|
||||||
}
|
}
|
||||||
bytes = server.buildHookManager.classTransform(bytes, classname);
|
bytes = server.buildHookManager.classTransform(bytes, classname);
|
||||||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
|
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
|
||||||
IOHelper.transfer(inputStream, output);
|
IOHelper.transfer(inputStream, output);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
IOHelper.transfer(input, output);
|
IOHelper.transfer(input, output);
|
||||||
// }
|
// }
|
||||||
e = input.getNextEntry();
|
e = input.getNextEntry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// write additional classes
|
// write additional classes
|
||||||
for (Entry<String, byte[]> ent : server.buildHookManager.getIncludeClass().entrySet()) {
|
for (Entry<String, byte[]> ent : server.buildHookManager.getIncludeClass().entrySet()) {
|
||||||
output.putNextEntry(newZipEntry(ent.getKey().replace('.', '/').concat(".class")));
|
output.putNextEntry(newZipEntry(ent.getKey().replace('.', '/').concat(".class")));
|
||||||
output.write(server.buildHookManager.classTransform(ent.getValue(), ent.getKey()));
|
output.write(server.buildHookManager.classTransform(ent.getValue(), ent.getKey()));
|
||||||
}
|
}
|
||||||
// map for runtime
|
// map for runtime
|
||||||
Map<String, byte[]> runtime = new HashMap<>(256);
|
Map<String, byte[]> runtime = new HashMap<>(256);
|
||||||
if (server.buildHookManager.buildRuntime()) {
|
if (server.buildHookManager.buildRuntime()) {
|
||||||
// Verify has init script file
|
// Verify has init script file
|
||||||
if (!IOHelper.isFile(initScriptFile))
|
if (!IOHelper.isFile(initScriptFile))
|
||||||
throw new IOException(String.format("Missing init script file ('%s')", Launcher.INIT_SCRIPT_FILE));
|
throw new IOException(String.format("Missing init script file ('%s')", Launcher.INIT_SCRIPT_FILE));
|
||||||
// Write launcher runtime dir
|
// Write launcher runtime dir
|
||||||
IOHelper.walk(runtimeDir, new RuntimeDirVisitor(output, runtime), false);
|
IOHelper.walk(runtimeDir, new RuntimeDirVisitor(output, runtime), false);
|
||||||
}
|
}
|
||||||
// Create launcher config file
|
// Create launcher config file
|
||||||
byte[] launcherConfigBytes;
|
byte[] launcherConfigBytes;
|
||||||
try (ByteArrayOutputStream configArray = IOHelper.newByteArrayOutput()) {
|
try (ByteArrayOutputStream configArray = IOHelper.newByteArrayOutput()) {
|
||||||
try (HOutput configOutput = new HOutput(configArray)) {
|
try (HOutput configOutput = new HOutput(configArray)) {
|
||||||
new LauncherConfig(server.config.getAddress(), server.config.port, server.publicKey, runtime)
|
new LauncherConfig(server.config.getAddress(), server.config.port, server.publicKey, runtime)
|
||||||
.write(configOutput);
|
.write(configOutput);
|
||||||
}
|
}
|
||||||
launcherConfigBytes = configArray.toByteArray();
|
launcherConfigBytes = configArray.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write launcher config file
|
// Write launcher config file
|
||||||
output.putNextEntry(newZipEntry(Launcher.CONFIG_FILE));
|
output.putNextEntry(newZipEntry(Launcher.CONFIG_FILE));
|
||||||
output.write(launcherConfigBytes);
|
output.write(launcherConfigBytes);
|
||||||
ZipEntry e = newZipEntry(jaConfigurator.getZipEntryPath());
|
ZipEntry e = newZipEntry(jaConfigurator.getZipEntryPath());
|
||||||
output.putNextEntry(e);
|
output.putNextEntry(e);
|
||||||
output.write(jaConfigurator.getBytecode());
|
output.write(jaConfigurator.getBytecode());
|
||||||
server.buildHookManager.postHook(context);
|
server.buildHookManager.postHook(context);
|
||||||
} catch (CannotCompileException | NotFoundException e) {
|
} catch (CannotCompileException | NotFoundException e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public void tryUnpackRuntime() throws IOException {
|
public void tryUnpackRuntime() throws IOException {
|
||||||
// Verify is runtime dir unpacked
|
// Verify is runtime dir unpacked
|
||||||
if (IOHelper.isDir(runtimeDir))
|
if (IOHelper.isDir(runtimeDir))
|
||||||
return; // Already unpacked
|
return; // Already unpacked
|
||||||
|
|
||||||
// Unpack launcher runtime files
|
// Unpack launcher runtime files
|
||||||
Files.createDirectory(runtimeDir);
|
Files.createDirectory(runtimeDir);
|
||||||
LogHelper.info("Unpacking launcher runtime files");
|
LogHelper.info("Unpacking launcher runtime files");
|
||||||
try (ZipInputStream input = IOHelper.newZipInput(IOHelper.getResourceURL("runtime.zip"))) {
|
try (ZipInputStream input = IOHelper.newZipInput(IOHelper.getResourceURL("runtime.zip"))) {
|
||||||
for (ZipEntry entry = input.getNextEntry(); entry != null; entry = input.getNextEntry()) {
|
for (ZipEntry entry = input.getNextEntry(); entry != null; entry = input.getNextEntry()) {
|
||||||
if (entry.isDirectory())
|
if (entry.isDirectory())
|
||||||
continue; // Skip dirs
|
continue; // Skip dirs
|
||||||
|
|
||||||
// Unpack runtime file
|
// Unpack runtime file
|
||||||
IOHelper.transfer(input, runtimeDir.resolve(IOHelper.toPath(entry.getName())));
|
IOHelper.transfer(input, runtimeDir.resolve(IOHelper.toPath(entry.getName())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ protected LauncherBinary(LaunchServer server, Path binaryFile) {
|
||||||
this.binaryFile = binaryFile;
|
this.binaryFile = binaryFile;
|
||||||
syncBinaryFile = binaryFile;
|
syncBinaryFile = binaryFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
protected LauncherBinary(LaunchServer server, Path binaryFile, Path syncBinaryFile) {
|
protected LauncherBinary(LaunchServer server, Path binaryFile, Path syncBinaryFile) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
|
|
|
@ -59,365 +59,384 @@
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public class SignerJar implements AutoCloseable {
|
public class SignerJar implements AutoCloseable {
|
||||||
/** Helper output stream that also sends the data to the given {@link com.google.common.hash.Hasher}. */
|
/**
|
||||||
private static class HashingOutputStream extends OutputStream {
|
* Helper output stream that also sends the data to the given {@link com.google.common.hash.Hasher}.
|
||||||
private final OutputStream out;
|
*/
|
||||||
private final MessageDigest hasher;
|
private static class HashingOutputStream extends OutputStream {
|
||||||
|
private final OutputStream out;
|
||||||
|
private final MessageDigest hasher;
|
||||||
|
|
||||||
public HashingOutputStream(OutputStream out, MessageDigest hasher) {
|
public HashingOutputStream(OutputStream out, MessageDigest hasher) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.hasher = hasher;
|
this.hasher = hasher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] b) throws IOException {
|
public void write(byte[] b) throws IOException {
|
||||||
out.write(b);
|
out.write(b);
|
||||||
hasher.update(b);
|
hasher.update(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] b, int off, int len) throws IOException {
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
out.write(b, off, len);
|
out.write(b, off, len);
|
||||||
hasher.update(b, off, len);
|
hasher.update(b, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int b) throws IOException {
|
public void write(int b) throws IOException {
|
||||||
out.write(b);
|
out.write(b);
|
||||||
hasher.update((byte) b);
|
hasher.update((byte) b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static final String MANIFEST_FN = "META-INF/MANIFEST.MF";
|
|
||||||
private static final String SIG_FN = "META-INF/SIGNUMO.SF";
|
|
||||||
|
|
||||||
private static final String SIG_RSA_FN = "META-INF/SIGNUMO.RSA";
|
private static final String MANIFEST_FN = "META-INF/MANIFEST.MF";
|
||||||
|
private static final String SIG_FN = "META-INF/SIGNUMO.SF";
|
||||||
|
|
||||||
private static final String hashFunctionName = "SHA-256";
|
private static final String SIG_RSA_FN = "META-INF/SIGNUMO.RSA";
|
||||||
|
|
||||||
public static final KeyStore getStore(Path file, String storepass, String algo) throws IOException {
|
private static final String hashFunctionName = "SHA-256";
|
||||||
try {
|
|
||||||
KeyStore st = KeyStore.getInstance(algo);
|
|
||||||
st.load(IOHelper.newInput(file), storepass != null ? storepass.toCharArray() : null);
|
|
||||||
return st;
|
|
||||||
} catch (NoSuchAlgorithmException | CertificateException| KeyStoreException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private final static MessageDigest hasher() {
|
|
||||||
try {
|
|
||||||
return MessageDigest.getInstance(hashFunctionName);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private final ZipOutputStream zos;
|
|
||||||
|
|
||||||
private final KeyStore keyStore;
|
public static KeyStore getStore(Path file, String storepass, String algo) throws IOException {
|
||||||
|
try {
|
||||||
|
KeyStore st = KeyStore.getInstance(algo);
|
||||||
|
st.load(IOHelper.newInput(file), storepass != null ? storepass.toCharArray() : null);
|
||||||
|
return st;
|
||||||
|
} catch (NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final String keyAlias;
|
private static MessageDigest hasher() {
|
||||||
|
try {
|
||||||
|
return MessageDigest.getInstance(hashFunctionName);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final String password;
|
private final ZipOutputStream zos;
|
||||||
private final Map<String, String> manifestAttributes;
|
|
||||||
private String manifestHash;
|
|
||||||
private String manifestMainHash;
|
|
||||||
|
|
||||||
private final Map<String, String> fileDigests;
|
private final KeyStore keyStore;
|
||||||
|
|
||||||
private final Map<String, String> sectionDigests;
|
private final String keyAlias;
|
||||||
|
|
||||||
/**
|
private final String password;
|
||||||
* Constructor.
|
private final Map<String, String> manifestAttributes;
|
||||||
*
|
private String manifestHash;
|
||||||
* @param out the output stream to write JAR data to
|
private String manifestMainHash;
|
||||||
* @param keyStore the key store to load given key from
|
|
||||||
* @param keyAlias the name of the key in the store, this key is used to sign the JAR
|
|
||||||
* @param keyPassword the password to access the key
|
|
||||||
*/
|
|
||||||
public SignerJar(OutputStream out, KeyStore keyStore, String keyAlias, String keyPassword) {
|
|
||||||
zos = new ZipOutputStream(out);
|
|
||||||
this.keyStore = keyStore;
|
|
||||||
this.keyAlias = keyAlias;
|
|
||||||
password = keyPassword;
|
|
||||||
|
|
||||||
manifestAttributes = new LinkedHashMap<>();
|
private final Map<String, String> fileDigests;
|
||||||
fileDigests = new LinkedHashMap<>();
|
|
||||||
sectionDigests = new LinkedHashMap<>();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Adds a file to the JAR. The file is immediately added to the zipped output stream. This method cannot be called once
|
|
||||||
* the stream is closed.
|
|
||||||
*
|
|
||||||
* @param filename name of the file to add (use forward slash as a path separator)
|
|
||||||
* @param contents contents of the file
|
|
||||||
* @throws java.io.IOException
|
|
||||||
* @throws NullPointerException if any of the arguments is {@code null}
|
|
||||||
*/
|
|
||||||
public void addFileContents(String filename, byte[] contents) throws IOException {
|
|
||||||
zos.putNextEntry(new ZipEntry(filename));
|
|
||||||
zos.write(contents);
|
|
||||||
zos.closeEntry();
|
|
||||||
|
|
||||||
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(contents));
|
private final Map<String, String> sectionDigests;
|
||||||
fileDigests.put(filename, hashCode64);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a file to the JAR. The file is immediately added to the zipped output stream. This method cannot be called once
|
* Constructor.
|
||||||
* the stream is closed.
|
*
|
||||||
*
|
* @param out the output stream to write JAR data to
|
||||||
* @param filename name of the file to add (use forward slash as a path separator)
|
* @param keyStore the key store to load given key from
|
||||||
* @param contents contents of the file
|
* @param keyAlias the name of the key in the store, this key is used to sign the JAR
|
||||||
* @throws java.io.IOException
|
* @param keyPassword the password to access the key
|
||||||
* @throws NullPointerException if any of the arguments is {@code null}
|
*/
|
||||||
*/
|
public SignerJar(OutputStream out, KeyStore keyStore, String keyAlias, String keyPassword) {
|
||||||
public void addFileContents(String filename, InputStream contents) throws IOException {
|
zos = new ZipOutputStream(out);
|
||||||
zos.putNextEntry(new ZipEntry(filename));
|
this.keyStore = keyStore;
|
||||||
byte[] arr = IOHelper.toByteArray(contents);
|
this.keyAlias = keyAlias;
|
||||||
zos.write(arr);
|
password = keyPassword;
|
||||||
zos.closeEntry();
|
|
||||||
|
|
||||||
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(arr));
|
manifestAttributes = new LinkedHashMap<>();
|
||||||
fileDigests.put(filename, hashCode64);
|
fileDigests = new LinkedHashMap<>();
|
||||||
}
|
sectionDigests = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a file to the JAR. The file is immediately added to the zipped output stream. This method cannot be called once
|
* Adds a file to the JAR. The file is immediately added to the zipped output stream. This method cannot be called once
|
||||||
* the stream is closed.
|
* the stream is closed.
|
||||||
*
|
*
|
||||||
* @param entry name of the file to add (use forward slash as a path separator)
|
* @param filename name of the file to add (use forward slash as a path separator)
|
||||||
* @param contents contents of the file
|
* @param contents contents of the file
|
||||||
* @throws java.io.IOException
|
* @throws java.io.IOException
|
||||||
* @throws NullPointerException if any of the arguments is {@code null}
|
* @throws NullPointerException if any of the arguments is {@code null}
|
||||||
*/
|
*/
|
||||||
public void addFileContents(ZipEntry entry, byte[] contents) throws IOException {
|
public void addFileContents(String filename, byte[] contents) throws IOException {
|
||||||
zos.putNextEntry(entry);
|
zos.putNextEntry(new ZipEntry(filename));
|
||||||
zos.write(contents);
|
zos.write(contents);
|
||||||
zos.closeEntry();
|
zos.closeEntry();
|
||||||
|
|
||||||
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(contents));
|
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(contents));
|
||||||
fileDigests.put(entry.getName(), hashCode64);
|
fileDigests.put(filename, hashCode64);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a file to the JAR. The file is immediately added to the zipped output stream. This method cannot be called once
|
* Adds a file to the JAR. The file is immediately added to the zipped output stream. This method cannot be called once
|
||||||
* the stream is closed.
|
* the stream is closed.
|
||||||
*
|
*
|
||||||
* @param entry name of the file to add (use forward slash as a path separator)
|
* @param filename name of the file to add (use forward slash as a path separator)
|
||||||
* @param contents contents of the file
|
* @param contents contents of the file
|
||||||
* @throws java.io.IOException
|
* @throws java.io.IOException
|
||||||
* @throws NullPointerException if any of the arguments is {@code null}
|
* @throws NullPointerException if any of the arguments is {@code null}
|
||||||
*/
|
*/
|
||||||
public void addFileContents(ZipEntry entry, InputStream contents) throws IOException {
|
public void addFileContents(String filename, InputStream contents) throws IOException {
|
||||||
zos.putNextEntry(entry);
|
zos.putNextEntry(new ZipEntry(filename));
|
||||||
byte[] arr = IOHelper.toByteArray(contents);
|
byte[] arr = IOHelper.toByteArray(contents);
|
||||||
zos.write(arr);
|
zos.write(arr);
|
||||||
zos.closeEntry();
|
zos.closeEntry();
|
||||||
|
|
||||||
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(arr));
|
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(arr));
|
||||||
fileDigests.put(entry.getName(), hashCode64);
|
fileDigests.put(filename, hashCode64);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a header to the manifest of the JAR.
|
* Adds a file to the JAR. The file is immediately added to the zipped output stream. This method cannot be called once
|
||||||
*
|
* the stream is closed.
|
||||||
* @param name name of the attribute, it is placed into the main section of the manifest file, it cannot be longer
|
*
|
||||||
* than {@value #MANIFEST_ATTR_MAX_LEN} bytes (in utf-8 encoding)
|
* @param entry name of the file to add (use forward slash as a path separator)
|
||||||
* @param value value of the attribute
|
* @param contents contents of the file
|
||||||
*/
|
* @throws java.io.IOException
|
||||||
public void addManifestAttribute(String name, String value) {
|
* @throws NullPointerException if any of the arguments is {@code null}
|
||||||
manifestAttributes.put(name, value);
|
*/
|
||||||
}
|
public void addFileContents(ZipEntry entry, byte[] contents) throws IOException {
|
||||||
|
zos.putNextEntry(entry);
|
||||||
|
zos.write(contents);
|
||||||
|
zos.closeEntry();
|
||||||
|
|
||||||
|
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(contents));
|
||||||
|
fileDigests.put(entry.getName(), hashCode64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a file to the JAR. The file is immediately added to the zipped output stream. This method cannot be called once
|
||||||
|
* the stream is closed.
|
||||||
|
*
|
||||||
|
* @param entry name of the file to add (use forward slash as a path separator)
|
||||||
|
* @param contents contents of the file
|
||||||
|
* @throws java.io.IOException
|
||||||
|
* @throws NullPointerException if any of the arguments is {@code null}
|
||||||
|
*/
|
||||||
|
public void addFileContents(ZipEntry entry, InputStream contents) throws IOException {
|
||||||
|
zos.putNextEntry(entry);
|
||||||
|
byte[] arr = IOHelper.toByteArray(contents);
|
||||||
|
zos.write(arr);
|
||||||
|
zos.closeEntry();
|
||||||
|
|
||||||
|
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(arr));
|
||||||
|
fileDigests.put(entry.getName(), hashCode64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a header to the manifest of the JAR.
|
||||||
|
*
|
||||||
|
* @param name name of the attribute, it is placed into the main section of the manifest file, it cannot be longer
|
||||||
|
* than {@value #MANIFEST_ATTR_MAX_LEN} bytes (in utf-8 encoding)
|
||||||
|
* @param value value of the attribute
|
||||||
|
*/
|
||||||
|
public void addManifestAttribute(String name, String value) {
|
||||||
|
manifestAttributes.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the JAR file by writing the manifest and signature data to it and finishing the ZIP entries. It closes the
|
* Closes the JAR file by writing the manifest and signature data to it and finishing the ZIP entries. It closes the
|
||||||
* underlying stream.
|
* underlying stream.
|
||||||
*
|
*
|
||||||
* @throws java.io.IOException
|
* @throws java.io.IOException
|
||||||
* @throws RuntimeException if the signing goes wrong
|
* @throws RuntimeException if the signing goes wrong
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
finish();
|
finish();
|
||||||
zos.close();
|
zos.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates the beast that can actually sign the data. */
|
/**
|
||||||
private CMSSignedDataGenerator createSignedDataGenerator() throws Exception {
|
* Creates the beast that can actually sign the data.
|
||||||
Security.addProvider(new BouncyCastleProvider());
|
*/
|
||||||
|
private CMSSignedDataGenerator createSignedDataGenerator() throws Exception {
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
|
||||||
List<Certificate> certChain = new ArrayList<>(Arrays.asList(keyStore.getCertificateChain(keyAlias)));
|
List<Certificate> certChain = new ArrayList<>(Arrays.asList(keyStore.getCertificateChain(keyAlias)));
|
||||||
Store certStore = new JcaCertStore(certChain);
|
Store certStore = new JcaCertStore(certChain);
|
||||||
Certificate cert = keyStore.getCertificate(keyAlias);
|
Certificate cert = keyStore.getCertificate(keyAlias);
|
||||||
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, password != null ? password.toCharArray() : null);
|
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, password != null ? password.toCharArray() : null);
|
||||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256WITHRSA").setProvider("BC").build(privateKey);
|
ContentSigner signer = new JcaContentSignerBuilder("SHA256WITHRSA").setProvider("BC").build(privateKey);
|
||||||
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
|
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
|
||||||
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
|
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
|
||||||
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
|
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
|
||||||
generator.addSignerInfoGenerator(sig);
|
generator.addSignerInfoGenerator(sig);
|
||||||
generator.addCertificates(certStore);
|
generator.addCertificates(certStore);
|
||||||
return generator;
|
return generator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finishes the JAR file by writing the manifest and signature data to it and finishing the ZIP entries. It leaves the
|
* Finishes the JAR file by writing the manifest and signature data to it and finishing the ZIP entries. It leaves the
|
||||||
* underlying stream open.
|
* underlying stream open.
|
||||||
*
|
*
|
||||||
* @throws java.io.IOException
|
* @throws java.io.IOException
|
||||||
* @throws RuntimeException if the signing goes wrong
|
* @throws RuntimeException if the signing goes wrong
|
||||||
*/
|
*/
|
||||||
public void finish() throws IOException {
|
public void finish() throws IOException {
|
||||||
writeManifest();
|
writeManifest();
|
||||||
byte sig[] = writeSigFile();
|
byte sig[] = writeSigFile();
|
||||||
writeSignature(sig);
|
writeSignature(sig);
|
||||||
zos.finish();
|
zos.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZipOutputStream getZos() {
|
public ZipOutputStream getZos() {
|
||||||
return zos;
|
return zos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Helper for {@link #writeManifest()} that creates the digest of one entry. */
|
/**
|
||||||
private String hashEntrySection(String name, Attributes attributes) throws IOException {
|
* Helper for {@link #writeManifest()} that creates the digest of one entry.
|
||||||
Manifest manifest = new Manifest();
|
*/
|
||||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
private String hashEntrySection(String name, Attributes attributes) throws IOException {
|
||||||
ByteArrayOutputStream o = new ByteArrayOutputStream();
|
Manifest manifest = new Manifest();
|
||||||
manifest.write(o);
|
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||||
int emptyLen = o.toByteArray().length;
|
ByteArrayOutputStream o = new ByteArrayOutputStream();
|
||||||
|
manifest.write(o);
|
||||||
|
int emptyLen = o.toByteArray().length;
|
||||||
|
|
||||||
manifest.getEntries().put(name, attributes);
|
manifest.getEntries().put(name, attributes);
|
||||||
|
|
||||||
manifest.write(o);
|
manifest.write(o);
|
||||||
byte[] ob = o.toByteArray();
|
byte[] ob = o.toByteArray();
|
||||||
ob = Arrays.copyOfRange(ob, emptyLen, ob.length);
|
ob = Arrays.copyOfRange(ob, emptyLen, ob.length);
|
||||||
return Base64.getEncoder().encodeToString(hasher().digest(ob));
|
return Base64.getEncoder().encodeToString(hasher().digest(ob));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Helper for {@link #writeManifest()} that creates the digest of the main section. */
|
/**
|
||||||
private String hashMainSection(Attributes attributes) throws IOException {
|
* Helper for {@link #writeManifest()} that creates the digest of the main section.
|
||||||
Manifest manifest = new Manifest();
|
*/
|
||||||
manifest.getMainAttributes().putAll(attributes);
|
private String hashMainSection(Attributes attributes) throws IOException {
|
||||||
MessageDigest hasher = hasher();
|
Manifest manifest = new Manifest();
|
||||||
SignerJar.HashingOutputStream o = new SignerJar.HashingOutputStream(new OutputStream() {
|
manifest.getMainAttributes().putAll(attributes);
|
||||||
@Override
|
MessageDigest hasher = hasher();
|
||||||
public String toString() {
|
SignerJar.HashingOutputStream o = new SignerJar.HashingOutputStream(new OutputStream() {
|
||||||
return "NullOutputStream";
|
@Override
|
||||||
}
|
public String toString() {
|
||||||
/** Discards the specified byte array. */
|
return "NullOutputStream";
|
||||||
@Override public void write(byte[] b) {
|
}
|
||||||
}
|
|
||||||
/** Discards the specified byte array. */
|
|
||||||
@Override public void write(byte[] b, int off, int len) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Discards the specified byte. */
|
/** Discards the specified byte array. */
|
||||||
@Override public void write(int b) {
|
@Override
|
||||||
}
|
public void write(byte[] b) {
|
||||||
}, hasher);
|
}
|
||||||
manifest.write(o);
|
|
||||||
return Base64.getEncoder().encodeToString(hasher.digest());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the CMS signed data. */
|
/** Discards the specified byte array. */
|
||||||
private byte[] signSigFile(byte[] sigContents) throws Exception {
|
@Override
|
||||||
CMSSignedDataGenerator gen = createSignedDataGenerator();
|
public void write(byte[] b, int off, int len) {
|
||||||
CMSTypedData cmsData = new CMSProcessableByteArray(sigContents);
|
}
|
||||||
CMSSignedData signedData = gen.generate(cmsData, true);
|
|
||||||
return signedData.getEncoded();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/** Discards the specified byte. */
|
||||||
* Writes the manifest to the JAR. It also calculates the digests that are required to be placed in the the signature
|
@Override
|
||||||
* file.
|
public void write(int b) {
|
||||||
*
|
}
|
||||||
* @throws java.io.IOException
|
}, hasher);
|
||||||
*/
|
manifest.write(o);
|
||||||
private void writeManifest() throws IOException {
|
return Base64.getEncoder().encodeToString(hasher.digest());
|
||||||
zos.putNextEntry(new ZipEntry(MANIFEST_FN));
|
}
|
||||||
Manifest man = new Manifest();
|
|
||||||
|
|
||||||
// main section
|
/**
|
||||||
Attributes mainAttributes = man.getMainAttributes();
|
* Returns the CMS signed data.
|
||||||
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
*/
|
||||||
|
private byte[] signSigFile(byte[] sigContents) throws Exception {
|
||||||
|
CMSSignedDataGenerator gen = createSignedDataGenerator();
|
||||||
|
CMSTypedData cmsData = new CMSProcessableByteArray(sigContents);
|
||||||
|
CMSSignedData signedData = gen.generate(cmsData, true);
|
||||||
|
return signedData.getEncoded();
|
||||||
|
}
|
||||||
|
|
||||||
for (Map.Entry<String, String> entry : manifestAttributes.entrySet())
|
/**
|
||||||
mainAttributes.put(new Attributes.Name(entry.getKey()), entry.getValue());
|
* Writes the manifest to the JAR. It also calculates the digests that are required to be placed in the the signature
|
||||||
|
* file.
|
||||||
|
*
|
||||||
|
* @throws java.io.IOException
|
||||||
|
*/
|
||||||
|
private void writeManifest() throws IOException {
|
||||||
|
zos.putNextEntry(new ZipEntry(MANIFEST_FN));
|
||||||
|
Manifest man = new Manifest();
|
||||||
|
|
||||||
// individual files sections
|
// main section
|
||||||
Attributes.Name digestAttr = new Attributes.Name(hashFunctionName + "-Digest");
|
Attributes mainAttributes = man.getMainAttributes();
|
||||||
for (Map.Entry<String, String> entry : fileDigests.entrySet()) {
|
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||||
Attributes attributes = new Attributes();
|
|
||||||
man.getEntries().put(entry.getKey(), attributes);
|
|
||||||
attributes.put(digestAttr, entry.getValue());
|
|
||||||
sectionDigests.put(entry.getKey(), hashEntrySection(entry.getKey(), attributes));
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageDigest hasher = hasher();
|
for (Map.Entry<String, String> entry : manifestAttributes.entrySet())
|
||||||
OutputStream out = new SignerJar.HashingOutputStream(zos, hasher);
|
mainAttributes.put(new Attributes.Name(entry.getKey()), entry.getValue());
|
||||||
man.write(out);
|
|
||||||
zos.closeEntry();
|
|
||||||
|
|
||||||
manifestHash = Base64.getEncoder().encodeToString(hasher.digest());
|
// individual files sections
|
||||||
manifestMainHash = hashMainSection(man.getMainAttributes());
|
Attributes.Name digestAttr = new Attributes.Name(hashFunctionName + "-Digest");
|
||||||
}
|
for (Map.Entry<String, String> entry : fileDigests.entrySet()) {
|
||||||
|
Attributes attributes = new Attributes();
|
||||||
|
man.getEntries().put(entry.getKey(), attributes);
|
||||||
|
attributes.put(digestAttr, entry.getValue());
|
||||||
|
sectionDigests.put(entry.getKey(), hashEntrySection(entry.getKey(), attributes));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
MessageDigest hasher = hasher();
|
||||||
* Writes the .SIG file to the JAR.
|
OutputStream out = new SignerJar.HashingOutputStream(zos, hasher);
|
||||||
*
|
man.write(out);
|
||||||
* @return the contents of the file as bytes
|
zos.closeEntry();
|
||||||
*/
|
|
||||||
private byte[] writeSigFile() throws IOException {
|
|
||||||
zos.putNextEntry(new ZipEntry(SIG_FN));
|
|
||||||
Manifest man = new Manifest();
|
|
||||||
// main section
|
|
||||||
Attributes mainAttributes = man.getMainAttributes();
|
|
||||||
mainAttributes.put(Attributes.Name.SIGNATURE_VERSION, "1.0");
|
|
||||||
mainAttributes.put(new Attributes.Name(hashFunctionName + "-Digest-Manifest"), manifestHash);
|
|
||||||
mainAttributes.put(new Attributes.Name(hashFunctionName + "-Digest-Manifest-Main-Attributes"), manifestMainHash);
|
|
||||||
|
|
||||||
// individual files sections
|
manifestHash = Base64.getEncoder().encodeToString(hasher.digest());
|
||||||
Attributes.Name digestAttr = new Attributes.Name(hashFunctionName + "-Digest");
|
manifestMainHash = hashMainSection(man.getMainAttributes());
|
||||||
for (Map.Entry<String, String> entry : sectionDigests.entrySet()) {
|
}
|
||||||
Attributes attributes = new Attributes();
|
|
||||||
man.getEntries().put(entry.getKey(), attributes);
|
|
||||||
attributes.put(digestAttr, entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
man.write(zos);
|
/**
|
||||||
zos.closeEntry();
|
* Writes the .SIG file to the JAR.
|
||||||
|
*
|
||||||
|
* @return the contents of the file as bytes
|
||||||
|
*/
|
||||||
|
private byte[] writeSigFile() throws IOException {
|
||||||
|
zos.putNextEntry(new ZipEntry(SIG_FN));
|
||||||
|
Manifest man = new Manifest();
|
||||||
|
// main section
|
||||||
|
Attributes mainAttributes = man.getMainAttributes();
|
||||||
|
mainAttributes.put(Attributes.Name.SIGNATURE_VERSION, "1.0");
|
||||||
|
mainAttributes.put(new Attributes.Name(hashFunctionName + "-Digest-Manifest"), manifestHash);
|
||||||
|
mainAttributes.put(new Attributes.Name(hashFunctionName + "-Digest-Manifest-Main-Attributes"), manifestMainHash);
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
// individual files sections
|
||||||
man.write(baos);
|
Attributes.Name digestAttr = new Attributes.Name(hashFunctionName + "-Digest");
|
||||||
return baos.toByteArray();
|
for (Map.Entry<String, String> entry : sectionDigests.entrySet()) {
|
||||||
}
|
Attributes attributes = new Attributes();
|
||||||
|
man.getEntries().put(entry.getKey(), attributes);
|
||||||
|
attributes.put(digestAttr, entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
man.write(zos);
|
||||||
* Signs the .SIG file and writes the signature (.RSA file) to the JAR.
|
zos.closeEntry();
|
||||||
*
|
|
||||||
* @throws java.io.IOException
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
* @throws RuntimeException if the signing failed
|
man.write(baos);
|
||||||
*/
|
return baos.toByteArray();
|
||||||
private void writeSignature(byte[] sigFile) throws IOException {
|
}
|
||||||
zos.putNextEntry(new ZipEntry(SIG_RSA_FN));
|
|
||||||
try {
|
/**
|
||||||
byte[] signature = signSigFile(sigFile);
|
* Signs the .SIG file and writes the signature (.RSA file) to the JAR.
|
||||||
zos.write(signature);
|
*
|
||||||
} catch (IOException e) {
|
* @throws java.io.IOException
|
||||||
throw e;
|
* @throws RuntimeException if the signing failed
|
||||||
} catch (Exception e) {
|
*/
|
||||||
throw new RuntimeException("Signing failed.", e);
|
private void writeSignature(byte[] sigFile) throws IOException {
|
||||||
}
|
zos.putNextEntry(new ZipEntry(SIG_RSA_FN));
|
||||||
zos.closeEntry();
|
try {
|
||||||
}
|
byte[] signature = signSigFile(sigFile);
|
||||||
|
zos.write(signature);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Signing failed.", e);
|
||||||
|
}
|
||||||
|
zos.closeEntry();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -45,6 +45,6 @@ protected Command(LaunchServer server) {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
protected final void verifyArgs(String[] args, int min) throws CommandException {
|
protected final void verifyArgs(String[] args, int min) throws CommandException {
|
||||||
if (args.length < min)
|
if (args.length < min)
|
||||||
throw new CommandException("Command usage: " + getArgsDescription());
|
throw new CommandException("Command usage: " + getArgsDescription());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ public String getUsageDescription() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(String... args) throws Exception {
|
public void invoke(String... args) throws Exception {
|
||||||
verifyArgs(args,1);
|
verifyArgs(args, 1);
|
||||||
List<HWID> target = server.config.hwidHandler.getHwid(args[0]);
|
List<HWID> target = server.config.hwidHandler.getHwid(args[0]);
|
||||||
server.config.hwidHandler.ban(target);
|
server.config.hwidHandler.ban(target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public void invoke(String... args) throws CommandException, IOException {
|
||||||
// Get UUID by username
|
// Get UUID by username
|
||||||
String username = server.config.authHandler.uuidToUsername(uuid);
|
String username = server.config.authHandler.uuidToUsername(uuid);
|
||||||
if (username == null)
|
if (username == null)
|
||||||
throw new CommandException("Unknown UUID: " + uuid);
|
throw new CommandException("Unknown UUID: " + uuid);
|
||||||
|
|
||||||
// Print username
|
// Print username
|
||||||
LogHelper.subInfo("Username of player %s: '%s'", uuid, username);
|
LogHelper.subInfo("Username of player %s: '%s'", uuid, username);
|
||||||
|
|
|
@ -23,7 +23,7 @@ public String getUsageDescription() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(String... args) throws Exception {
|
public void invoke(String... args) throws Exception {
|
||||||
verifyArgs(args,1);
|
verifyArgs(args, 1);
|
||||||
List<HWID> target = server.config.hwidHandler.getHwid(args[0]);
|
List<HWID> target = server.config.hwidHandler.getHwid(args[0]);
|
||||||
server.config.hwidHandler.unban(target);
|
server.config.hwidHandler.unban(target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public void invoke(String... args) throws CommandException, IOException {
|
||||||
// Get UUID by username
|
// Get UUID by username
|
||||||
UUID uuid = server.config.authHandler.usernameToUUID(username);
|
UUID uuid = server.config.authHandler.usernameToUUID(username);
|
||||||
if (uuid == null)
|
if (uuid == null)
|
||||||
throw new CommandException(String.format("Unknown username: '%s'", username));
|
throw new CommandException(String.format("Unknown username: '%s'", username));
|
||||||
|
|
||||||
// Print UUID
|
// Print UUID
|
||||||
LogHelper.subInfo("UUID of player '%s': %s", username, uuid);
|
LogHelper.subInfo("UUID of player '%s': %s", username, uuid);
|
||||||
|
|
|
@ -26,7 +26,7 @@ public void invoke(String... args) {
|
||||||
newValue = Boolean.parseBoolean(args[0]);
|
newValue = Boolean.parseBoolean(args[0]);
|
||||||
LogHelper.setDebugEnabled(newValue);
|
LogHelper.setDebugEnabled(newValue);
|
||||||
} else
|
} else
|
||||||
newValue = LogHelper.isDebugEnabled();
|
newValue = LogHelper.isDebugEnabled();
|
||||||
LogHelper.subInfo("Debug enabled: " + newValue);
|
LogHelper.subInfo("Debug enabled: " + newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,6 @@ private void printCommand(String name) throws CommandException {
|
||||||
|
|
||||||
private void printCommands() {
|
private void printCommands() {
|
||||||
for (Entry<String, Command> entry : server.commandHandler.commandsMap().entrySet())
|
for (Entry<String, Command> entry : server.commandHandler.commandsMap().entrySet())
|
||||||
printCommand(entry.getKey(), entry.getValue());
|
printCommand(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ public void invoke(String... args) {
|
||||||
newValue = Boolean.parseBoolean(args[0]);
|
newValue = Boolean.parseBoolean(args[0]);
|
||||||
server.serverSocketHandler.logConnections = newValue;
|
server.serverSocketHandler.logConnections = newValue;
|
||||||
} else
|
} else
|
||||||
newValue = server.serverSocketHandler.logConnections;
|
newValue = server.serverSocketHandler.logConnections;
|
||||||
LogHelper.subInfo("Log connections: " + newValue);
|
LogHelper.subInfo("Log connections: " + newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,22 +4,22 @@
|
||||||
import ru.gravit.launchserver.command.Command;
|
import ru.gravit.launchserver.command.Command;
|
||||||
|
|
||||||
public class ProguardCleanCommand extends Command {
|
public class ProguardCleanCommand extends Command {
|
||||||
public ProguardCleanCommand(LaunchServer server) {
|
public ProguardCleanCommand(LaunchServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getArgsDescription() {
|
public String getArgsDescription() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsageDescription() {
|
public String getUsageDescription() {
|
||||||
return "Resets proguard config";
|
return "Resets proguard config";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(String... args) {
|
public void invoke(String... args) {
|
||||||
server.proguardConf.prepare(true);
|
server.proguardConf.prepare(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,23 +7,23 @@
|
||||||
|
|
||||||
public class RegenProguardDictCommand extends Command {
|
public class RegenProguardDictCommand extends Command {
|
||||||
|
|
||||||
public RegenProguardDictCommand(LaunchServer server) {
|
public RegenProguardDictCommand(LaunchServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getArgsDescription() {
|
public String getArgsDescription() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsageDescription() {
|
public String getUsageDescription() {
|
||||||
return "Regenerates proguard dictonary";
|
return "Regenerates proguard dictonary";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(String... args) throws IOException {
|
public void invoke(String... args) throws IOException {
|
||||||
server.proguardConf.genWords(true);
|
server.proguardConf.genWords(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,23 +8,23 @@
|
||||||
|
|
||||||
public class RemoveMappingsProguardCommand extends Command {
|
public class RemoveMappingsProguardCommand extends Command {
|
||||||
|
|
||||||
public RemoveMappingsProguardCommand(LaunchServer server) {
|
public RemoveMappingsProguardCommand(LaunchServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getArgsDescription() {
|
public String getArgsDescription() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsageDescription() {
|
public String getUsageDescription() {
|
||||||
return "Removes proguard mappings (if you want to gen new mappings).";
|
return "Removes proguard mappings (if you want to gen new mappings).";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(String... args) throws IOException {
|
public void invoke(String... args) throws IOException {
|
||||||
Files.deleteIfExists(server.proguardConf.mappings);
|
Files.deleteIfExists(server.proguardConf.mappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,14 @@
|
||||||
import ru.gravit.launchserver.LaunchServer;
|
import ru.gravit.launchserver.LaunchServer;
|
||||||
import ru.gravit.launchserver.command.Command;
|
import ru.gravit.launchserver.command.Command;
|
||||||
import ru.gravit.launchserver.socket.NettyServerSocketHandler;
|
import ru.gravit.launchserver.socket.NettyServerSocketHandler;
|
||||||
import ru.gravit.utils.HttpDownloader;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
public class TestCommand extends Command {
|
public class TestCommand extends Command {
|
||||||
public TestCommand(LaunchServer server) {
|
public TestCommand(LaunchServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
NettyServerSocketHandler handler;
|
NettyServerSocketHandler handler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getArgsDescription() {
|
public String getArgsDescription() {
|
||||||
return null;
|
return null;
|
||||||
|
@ -25,11 +23,10 @@ public String getUsageDescription() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(String... args) throws Exception {
|
public void invoke(String... args) throws Exception {
|
||||||
verifyArgs(args,1);
|
verifyArgs(args, 1);
|
||||||
if(handler == null)
|
if (handler == null)
|
||||||
handler = new NettyServerSocketHandler(server);
|
handler = new NettyServerSocketHandler(server);
|
||||||
if(args[0].equals("start"))
|
if (args[0].equals("start")) {
|
||||||
{
|
|
||||||
handler.run();
|
handler.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,11 +48,11 @@ private static String[] parse(CharSequence line) throws CommandException {
|
||||||
// Maybe we should read next argument?
|
// Maybe we should read next argument?
|
||||||
if (end || !quoted && Character.isWhitespace(ch)) {
|
if (end || !quoted && Character.isWhitespace(ch)) {
|
||||||
if (end && quoted)
|
if (end && quoted)
|
||||||
throw new CommandException("Quotes wasn't closed");
|
throw new CommandException("Quotes wasn't closed");
|
||||||
|
|
||||||
// Empty args are ignored (except if was quoted)
|
// Empty args are ignored (except if was quoted)
|
||||||
if (wasQuoted || builder.length() > 0)
|
if (wasQuoted || builder.length() > 0)
|
||||||
result.add(builder.toString());
|
result.add(builder.toString());
|
||||||
|
|
||||||
// Reset string builder
|
// Reset string builder
|
||||||
wasQuoted = false;
|
wasQuoted = false;
|
||||||
|
@ -68,7 +68,7 @@ private static String[] parse(CharSequence line) throws CommandException {
|
||||||
break;
|
break;
|
||||||
case '\\': // All escapes, including spaces etc
|
case '\\': // All escapes, including spaces etc
|
||||||
if (i + 1 >= line.length())
|
if (i + 1 >= line.length())
|
||||||
throw new CommandException("Escape character is not specified");
|
throw new CommandException("Escape character is not specified");
|
||||||
char next = line.charAt(i + 1);
|
char next = line.charAt(i + 1);
|
||||||
builder.append(next);
|
builder.append(next);
|
||||||
i++;
|
i++;
|
||||||
|
@ -101,7 +101,7 @@ protected CommandHandler(LaunchServer server) {
|
||||||
registerCommand("logConnections", new LogConnectionsCommand(server));
|
registerCommand("logConnections", new LogConnectionsCommand(server));
|
||||||
registerCommand("loadModule", new LoadModuleCommand(server));
|
registerCommand("loadModule", new LoadModuleCommand(server));
|
||||||
registerCommand("modules", new ModulesCommand(server));
|
registerCommand("modules", new ModulesCommand(server));
|
||||||
registerCommand("test",new TestCommand(server));
|
registerCommand("test", new TestCommand(server));
|
||||||
|
|
||||||
// Register sync commands
|
// Register sync commands
|
||||||
registerCommand("indexAsset", new IndexAssetCommand(server));
|
registerCommand("indexAsset", new IndexAssetCommand(server));
|
||||||
|
@ -151,7 +151,7 @@ public final void eval(String line, boolean bell) {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final void eval(String[] args, boolean bell) {
|
public final void eval(String[] args, boolean bell) {
|
||||||
if (args.length == 0)
|
if (args.length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Measure start time and invoke command
|
// Measure start time and invoke command
|
||||||
Instant startTime = Instant.now();
|
Instant startTime = Instant.now();
|
||||||
|
@ -164,7 +164,7 @@ public final void eval(String[] args, boolean bell) {
|
||||||
// Bell if invocation took > 1s
|
// Bell if invocation took > 1s
|
||||||
Instant endTime = Instant.now();
|
Instant endTime = Instant.now();
|
||||||
if (bell && Duration.between(startTime, endTime).getSeconds() >= 5)
|
if (bell && Duration.between(startTime, endTime).getSeconds() >= 5)
|
||||||
try {
|
try {
|
||||||
bell();
|
bell();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
|
@ -175,7 +175,7 @@ public final void eval(String[] args, boolean bell) {
|
||||||
public final Command lookup(String name) throws CommandException {
|
public final Command lookup(String name) throws CommandException {
|
||||||
Command command = commands.get(name);
|
Command command = commands.get(name);
|
||||||
if (command == null)
|
if (command == null)
|
||||||
throw new CommandException(String.format("Unknown command: '%s'", name));
|
throw new CommandException(String.format("Unknown command: '%s'", name));
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ public final Command lookup(String name) throws CommandException {
|
||||||
|
|
||||||
private void readLoop() throws IOException {
|
private void readLoop() throws IOException {
|
||||||
for (String line = readLine(); line != null; line = readLine())
|
for (String line = readLine(); line != null; line = readLine())
|
||||||
eval(line, true);
|
eval(line, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
|
|
|
@ -48,6 +48,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
|
||||||
return super.visitFile(file, attrs);
|
return super.visitFile(file, attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String INDEXES_DIR = "indexes";
|
public static final String INDEXES_DIR = "indexes";
|
||||||
public static final String OBJECTS_DIR = "objects";
|
public static final String OBJECTS_DIR = "objects";
|
||||||
|
|
||||||
|
@ -86,7 +87,7 @@ public void invoke(String... args) throws Exception {
|
||||||
Path inputAssetDir = server.updatesDir.resolve(inputAssetDirName);
|
Path inputAssetDir = server.updatesDir.resolve(inputAssetDirName);
|
||||||
Path outputAssetDir = server.updatesDir.resolve(outputAssetDirName);
|
Path outputAssetDir = server.updatesDir.resolve(outputAssetDirName);
|
||||||
if (outputAssetDir.equals(inputAssetDir))
|
if (outputAssetDir.equals(inputAssetDir))
|
||||||
throw new CommandException("Unindexed and indexed asset dirs can't be same");
|
throw new CommandException("Unindexed and indexed asset dirs can't be same");
|
||||||
|
|
||||||
// Create new asset dir
|
// Create new asset dir
|
||||||
LogHelper.subInfo("Creating indexed asset dir: '%s'", outputAssetDirName);
|
LogHelper.subInfo("Creating indexed asset dir: '%s'", outputAssetDirName);
|
||||||
|
|
|
@ -39,7 +39,7 @@ public void invoke(String... args) throws Exception {
|
||||||
Path inputAssetDir = server.updatesDir.resolve(inputAssetDirName);
|
Path inputAssetDir = server.updatesDir.resolve(inputAssetDirName);
|
||||||
Path outputAssetDir = server.updatesDir.resolve(outputAssetDirName);
|
Path outputAssetDir = server.updatesDir.resolve(outputAssetDirName);
|
||||||
if (outputAssetDir.equals(inputAssetDir))
|
if (outputAssetDir.equals(inputAssetDir))
|
||||||
throw new CommandException("Indexed and unindexed asset dirs can't be same");
|
throw new CommandException("Indexed and unindexed asset dirs can't be same");
|
||||||
|
|
||||||
// Create new asset dir
|
// Create new asset dir
|
||||||
LogHelper.subInfo("Creating unindexed asset dir: '%s'", outputAssetDirName);
|
LogHelper.subInfo("Creating unindexed asset dir: '%s'", outputAssetDirName);
|
||||||
|
|
|
@ -19,7 +19,7 @@ public String getUsageDescription() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(String... args) throws Exception {
|
public void invoke(String... args) {
|
||||||
server.modulesManager.printModules();
|
server.modulesManager.printModules();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ public final class LaunchServerPluginBridge implements Runnable, AutoCloseable {
|
||||||
* Err text.
|
* Err text.
|
||||||
*/
|
*/
|
||||||
public static final String nonInitText = "Лаунчсервер не был полностью загружен";
|
public static final String nonInitText = "Лаунчсервер не был полностью загружен";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
//SecurityHelper.verifyCertificates(LaunchServer.class);
|
//SecurityHelper.verifyCertificates(LaunchServer.class);
|
||||||
JVMHelper.verifySystemProperties(LaunchServer.class, false);
|
JVMHelper.verifySystemProperties(LaunchServer.class, false);
|
||||||
|
|
|
@ -19,9 +19,9 @@ public boolean onCommand(CommandSender sender, Command command, String alias, St
|
||||||
// Eval command
|
// Eval command
|
||||||
LaunchServerPluginBridge bridge = plugin.bridge;
|
LaunchServerPluginBridge bridge = plugin.bridge;
|
||||||
if (bridge == null)
|
if (bridge == null)
|
||||||
sender.sendMessage(ChatColor.RED + LaunchServerPluginBridge.nonInitText);
|
sender.sendMessage(ChatColor.RED + LaunchServerPluginBridge.nonInitText);
|
||||||
else
|
else
|
||||||
bridge.eval(args);
|
bridge.eval(args);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@ public void execute(CommandSender sender, String[] args) {
|
||||||
// Eval command
|
// Eval command
|
||||||
LaunchServerPluginBridge bridge = plugin.bridge;
|
LaunchServerPluginBridge bridge = plugin.bridge;
|
||||||
if (bridge == null)
|
if (bridge == null)
|
||||||
sender.sendMessage(NOT_INITIALIZED_MESSAGE);
|
sender.sendMessage(NOT_INITIALIZED_MESSAGE);
|
||||||
else
|
else
|
||||||
bridge.eval(args);
|
bridge.eval(args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,97 +12,102 @@
|
||||||
|
|
||||||
public class BuildHookManager {
|
public class BuildHookManager {
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface PostBuildHook
|
public interface PostBuildHook {
|
||||||
{
|
|
||||||
void build(BuildContext context);
|
|
||||||
}
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface PreBuildHook
|
|
||||||
{
|
|
||||||
void build(BuildContext context);
|
void build(BuildContext context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface Transformer
|
public interface PreBuildHook {
|
||||||
{
|
void build(BuildContext context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Transformer {
|
||||||
byte[] transform(byte[] input, CharSequence classname);
|
byte[] transform(byte[] input, CharSequence classname);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean BUILDRUNTIME;
|
private boolean BUILDRUNTIME;
|
||||||
private final Set<PostBuildHook> POST_HOOKS;
|
private final Set<PostBuildHook> POST_HOOKS;
|
||||||
private final Set<PreBuildHook> PRE_HOOKS;
|
private final Set<PreBuildHook> PRE_HOOKS;
|
||||||
private final Set<Transformer> CLASS_TRANSFORMER;
|
private final Set<Transformer> CLASS_TRANSFORMER;
|
||||||
private final Set<String> CLASS_BLACKLIST;
|
private final Set<String> CLASS_BLACKLIST;
|
||||||
private final Set<String> MODULE_CLASS;
|
private final Set<String> MODULE_CLASS;
|
||||||
private final Map<String, byte[]> INCLUDE_CLASS;
|
private final Map<String, byte[]> INCLUDE_CLASS;
|
||||||
|
|
||||||
public BuildHookManager() {
|
public BuildHookManager() {
|
||||||
POST_HOOKS = new HashSet<>(4);
|
POST_HOOKS = new HashSet<>(4);
|
||||||
PRE_HOOKS = new HashSet<>(4);
|
PRE_HOOKS = new HashSet<>(4);
|
||||||
CLASS_BLACKLIST = new HashSet<>(4);
|
CLASS_BLACKLIST = new HashSet<>(4);
|
||||||
MODULE_CLASS = new HashSet<>(4);
|
MODULE_CLASS = new HashSet<>(4);
|
||||||
INCLUDE_CLASS = new HashMap<>(4);
|
INCLUDE_CLASS = new HashMap<>(4);
|
||||||
CLASS_TRANSFORMER = new HashSet<>(4);
|
CLASS_TRANSFORMER = new HashSet<>(4);
|
||||||
BUILDRUNTIME = true;
|
BUILDRUNTIME = true;
|
||||||
autoRegisterIgnoredClass(AutogenConfig.class.getName());
|
autoRegisterIgnoredClass(AutogenConfig.class.getName());
|
||||||
registerIgnoredClass("META-INF/DEPENDENCIES");
|
registerIgnoredClass("META-INF/DEPENDENCIES");
|
||||||
registerIgnoredClass("META-INF/LICENSE");
|
registerIgnoredClass("META-INF/LICENSE");
|
||||||
registerIgnoredClass("META-INF/NOTICE");
|
registerIgnoredClass("META-INF/NOTICE");
|
||||||
registerClientModuleClass(TestClientModule.class.getName());
|
registerClientModuleClass(TestClientModule.class.getName());
|
||||||
}
|
|
||||||
public void autoRegisterIgnoredClass(String clazz)
|
|
||||||
{
|
|
||||||
CLASS_BLACKLIST.add(clazz.replace('.','/').concat(".class"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void autoRegisterIgnoredClass(String clazz) {
|
||||||
|
CLASS_BLACKLIST.add(clazz.replace('.', '/').concat(".class"));
|
||||||
|
}
|
||||||
|
|
||||||
public boolean buildRuntime() {
|
public boolean buildRuntime() {
|
||||||
return BUILDRUNTIME;
|
return BUILDRUNTIME;
|
||||||
}
|
}
|
||||||
public byte[] classTransform(byte[] clazz, CharSequence classname)
|
|
||||||
{
|
public byte[] classTransform(byte[] clazz, CharSequence classname) {
|
||||||
byte[] result = clazz;
|
byte[] result = clazz;
|
||||||
for(Transformer transformer : CLASS_TRANSFORMER) result = transformer.transform(result,classname);
|
for (Transformer transformer : CLASS_TRANSFORMER) result = transformer.transform(result, classname);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerIncludeClass(String classname, byte[] classdata) {
|
public void registerIncludeClass(String classname, byte[] classdata) {
|
||||||
INCLUDE_CLASS.put(classname, classdata);
|
INCLUDE_CLASS.put(classname, classdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, byte[]> getIncludeClass() {
|
public Map<String, byte[]> getIncludeClass() {
|
||||||
return INCLUDE_CLASS;
|
return INCLUDE_CLASS;
|
||||||
}
|
}
|
||||||
public boolean isContainsBlacklist(String clazz)
|
|
||||||
{
|
public boolean isContainsBlacklist(String clazz) {
|
||||||
return CLASS_BLACKLIST.contains(clazz);
|
return CLASS_BLACKLIST.contains(clazz);
|
||||||
}
|
}
|
||||||
public void postHook(BuildContext context)
|
|
||||||
{
|
public void postHook(BuildContext context) {
|
||||||
for(PostBuildHook hook : POST_HOOKS) hook.build(context);
|
for (PostBuildHook hook : POST_HOOKS) hook.build(context);
|
||||||
}
|
}
|
||||||
public void preHook(BuildContext context)
|
|
||||||
{
|
public void preHook(BuildContext context) {
|
||||||
for(PreBuildHook hook : PRE_HOOKS) hook.build(context);
|
for (PreBuildHook hook : PRE_HOOKS) hook.build(context);
|
||||||
}
|
}
|
||||||
public void registerAllClientModuleClass(JAConfigurator cfg)
|
|
||||||
{
|
public void registerAllClientModuleClass(JAConfigurator cfg) {
|
||||||
for(String clazz : MODULE_CLASS) cfg.addModuleClass(clazz);
|
for (String clazz : MODULE_CLASS) cfg.addModuleClass(clazz);
|
||||||
}
|
}
|
||||||
public void registerClassTransformer(Transformer transformer)
|
|
||||||
{
|
public void registerClassTransformer(Transformer transformer) {
|
||||||
CLASS_TRANSFORMER.add(transformer);
|
CLASS_TRANSFORMER.add(transformer);
|
||||||
}
|
}
|
||||||
public void registerClientModuleClass(String clazz)
|
|
||||||
{
|
public void registerClientModuleClass(String clazz) {
|
||||||
MODULE_CLASS.add(clazz);
|
MODULE_CLASS.add(clazz);
|
||||||
}
|
}
|
||||||
public void registerIgnoredClass(String clazz)
|
|
||||||
{
|
public void registerIgnoredClass(String clazz) {
|
||||||
CLASS_BLACKLIST.add(clazz);
|
CLASS_BLACKLIST.add(clazz);
|
||||||
}
|
}
|
||||||
public void registerPostHook(PostBuildHook hook)
|
|
||||||
{
|
public void registerPostHook(PostBuildHook hook) {
|
||||||
POST_HOOKS.add(hook);
|
POST_HOOKS.add(hook);
|
||||||
}
|
}
|
||||||
public void registerPreHook(PreBuildHook hook)
|
|
||||||
{
|
public void registerPreHook(PreBuildHook hook) {
|
||||||
PRE_HOOKS.add(hook);
|
PRE_HOOKS.add(hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBuildRuntime(boolean runtime) {
|
public void setBuildRuntime(boolean runtime) {
|
||||||
BUILDRUNTIME = runtime;
|
BUILDRUNTIME = runtime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,13 @@
|
||||||
import ru.gravit.launchserver.modules.LaunchServerModuleContext;
|
import ru.gravit.launchserver.modules.LaunchServerModuleContext;
|
||||||
|
|
||||||
public class ModulesManager extends SimpleModuleManager {
|
public class ModulesManager extends SimpleModuleManager {
|
||||||
public ModulesManager(LaunchServer lsrv) {
|
public ModulesManager(LaunchServer lsrv) {
|
||||||
modules = new ArrayList<>(1);
|
modules = new ArrayList<>(1);
|
||||||
classloader = new PublicURLClassLoader(new URL[0], ClassLoader.getSystemClassLoader());
|
classloader = new PublicURLClassLoader(new URL[0], ClassLoader.getSystemClassLoader());
|
||||||
context = new LaunchServerModuleContext(lsrv, classloader);
|
context = new LaunchServerModuleContext(lsrv, classloader);
|
||||||
}
|
}
|
||||||
private void registerCoreModule() {
|
|
||||||
load(new CoreModule());
|
private void registerCoreModule() {
|
||||||
}
|
load(new CoreModule());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ public boolean addClient(Client client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public void garbageCollection() {
|
public void garbageCollection() {
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
clientSet.removeIf(c -> c.timestamp + SESSION_TIMEOUT < time);
|
clientSet.removeIf(c -> c.timestamp + SESSION_TIMEOUT < time);
|
||||||
|
@ -28,14 +28,14 @@ public void garbageCollection() {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public Client getClient(long session) {
|
public Client getClient(long session) {
|
||||||
for (Client c : clientSet)
|
for (Client c : clientSet)
|
||||||
if (c.session == session) return c;
|
if (c.session == session) return c;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public Client getOrNewClient(long session) {
|
public Client getOrNewClient(long session) {
|
||||||
for (Client c : clientSet)
|
for (Client c : clientSet)
|
||||||
if (c.session == session) return c;
|
if (c.session == session) return c;
|
||||||
Client newClient = new Client(session);
|
Client newClient = new Client(session);
|
||||||
clientSet.add(newClient);
|
clientSet.add(newClient);
|
||||||
return newClient;
|
return newClient;
|
||||||
|
@ -44,7 +44,7 @@ public Client getOrNewClient(long session) {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public void updateClient(long session) {
|
public void updateClient(long session) {
|
||||||
for (Client c : clientSet)
|
for (Client c : clientSet)
|
||||||
if (c.session == session) {
|
if (c.session == session) {
|
||||||
c.up();
|
c.up();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,39 +6,39 @@
|
||||||
import ru.gravit.launcher.modules.ModuleContext;
|
import ru.gravit.launcher.modules.ModuleContext;
|
||||||
|
|
||||||
public class CoreModule implements Module {
|
public class CoreModule implements Module {
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "LaunchServer";
|
return "LaunchServer";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Version getVersion() {
|
public Version getVersion() {
|
||||||
return Launcher.getVersion();
|
return Launcher.getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPriority() {
|
public int getPriority() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(ModuleContext context) {
|
public void init(ModuleContext context) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postInit(ModuleContext context) {
|
public void postInit(ModuleContext context) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preInit(ModuleContext context) {
|
public void preInit(ModuleContext context) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,12 @@
|
||||||
public class LaunchServerModuleContext implements ModuleContext {
|
public class LaunchServerModuleContext implements ModuleContext {
|
||||||
public final LaunchServer launchServer;
|
public final LaunchServer launchServer;
|
||||||
public final PublicURLClassLoader classloader;
|
public final PublicURLClassLoader classloader;
|
||||||
public LaunchServerModuleContext(LaunchServer server, PublicURLClassLoader classloader)
|
|
||||||
{
|
public LaunchServerModuleContext(LaunchServer server, PublicURLClassLoader classloader) {
|
||||||
launchServer = server;
|
launchServer = server;
|
||||||
this.classloader = classloader;
|
this.classloader = classloader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType() {
|
public Type getType() {
|
||||||
return Type.LAUNCHSERVER;
|
return Type.LAUNCHSERVER;
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
|
|
||||||
public class SimpleModule implements Module {
|
public class SimpleModule implements Module {
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
// on stop
|
// on stop
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -17,7 +17,7 @@ public String getName() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Version getVersion() {
|
public Version getVersion() {
|
||||||
return new Version(1,0,0,0, Version.Type.UNKNOWN);
|
return new Version(1, 0, 0, 0, Version.Type.UNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,13 +28,17 @@ public interface Factory<R> {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
Response newResponse(LaunchServer server, long id, HInput input, HOutput output, String ip);
|
Response newResponse(LaunchServer server, long id, HInput input, HOutput output, String ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<Integer, Factory<?>> RESPONSES = new ConcurrentHashMap<>(8);
|
private static final Map<Integer, Factory<?>> RESPONSES = new ConcurrentHashMap<>(8);
|
||||||
|
|
||||||
public static Response getResponse(int type, LaunchServer server, long session, HInput input, HOutput output, String ip) {
|
public static Response getResponse(int type, LaunchServer server, long session, HInput input, HOutput output, String ip) {
|
||||||
return RESPONSES.get(type).newResponse(server, session, input, output, ip);
|
return RESPONSES.get(type).newResponse(server, session, input, output, ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerResponse(int type, Factory<?> factory) {
|
public static void registerResponse(int type, Factory<?> factory) {
|
||||||
RESPONSES.put(type, factory);
|
RESPONSES.put(type, factory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerResponses() {
|
public static void registerResponses() {
|
||||||
registerResponse(RequestType.PING.getNumber(), PingResponse::new);
|
registerResponse(RequestType.PING.getNumber(), PingResponse::new);
|
||||||
registerResponse(RequestType.AUTH.getNumber(), AuthResponse::new);
|
registerResponse(RequestType.AUTH.getNumber(), AuthResponse::new);
|
||||||
|
@ -50,6 +54,7 @@ public static void registerResponses() {
|
||||||
registerResponse(RequestType.UPDATE.getNumber(), UpdateResponse::new);
|
registerResponse(RequestType.UPDATE.getNumber(), UpdateResponse::new);
|
||||||
registerResponse(RequestType.PROFILES.getNumber(), ProfilesResponse::new);
|
registerResponse(RequestType.PROFILES.getNumber(), ProfilesResponse::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public static void requestError(String message) throws RequestException {
|
public static void requestError(String message) throws RequestException {
|
||||||
throw new RequestException(message);
|
throw new RequestException(message);
|
||||||
|
|
|
@ -68,10 +68,10 @@ public void reply() throws Exception {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Collection<SignedObjectHolder<ClientProfile>> profiles = server.getProfiles();
|
Collection<SignedObjectHolder<ClientProfile>> profiles = server.getProfiles();
|
||||||
for(SignedObjectHolder<ClientProfile> p : profiles)
|
for (SignedObjectHolder<ClientProfile> p : profiles)
|
||||||
if(p.object.getTitle().equals(client))
|
if (p.object.getTitle().equals(client))
|
||||||
if(!p.object.isWhitelistContains(login))
|
if (!p.object.isWhitelistContains(login))
|
||||||
throw new AuthException(server.config.whitelistRejectString);
|
throw new AuthException(server.config.whitelistRejectString);
|
||||||
server.config.hwidHandler.check(HWID.gen(hwid_hdd, hwid_bios, hwid_cpu), result.username);
|
server.config.hwidHandler.check(HWID.gen(hwid_hdd, hwid_bios, hwid_cpu), result.username);
|
||||||
} catch (AuthException | HWIDException e) {
|
} catch (AuthException | HWIDException e) {
|
||||||
requestError(e.getMessage());
|
requestError(e.getMessage());
|
||||||
|
|
|
@ -43,6 +43,6 @@ public void reply() throws IOException {
|
||||||
// Write profile and UUID
|
// Write profile and UUID
|
||||||
output.writeBoolean(uuid != null);
|
output.writeBoolean(uuid != null);
|
||||||
if (uuid != null)
|
if (uuid != null)
|
||||||
ProfileByUUIDResponse.getProfile(server, uuid, username, client).write(output);
|
ProfileByUUIDResponse.getProfile(server, uuid, username, client).write(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,6 @@ public void reply() throws IOException {
|
||||||
|
|
||||||
// Respond with profiles array
|
// Respond with profiles array
|
||||||
for (int i = 0; i < usernames.length; i++)
|
for (int i = 0; i < usernames.length; i++)
|
||||||
ProfileByUsernameResponse.writeProfile(server, output, usernames[i], clients[i]);
|
ProfileByUsernameResponse.writeProfile(server, output, usernames[i], clients[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,6 @@ public void reply() throws IOException {
|
||||||
Collection<SignedObjectHolder<ClientProfile>> profiles = server.getProfiles();
|
Collection<SignedObjectHolder<ClientProfile>> profiles = server.getProfiles();
|
||||||
output.writeLength(profiles.size(), 0);
|
output.writeLength(profiles.size(), 0);
|
||||||
for (SignedObjectHolder<ClientProfile> profile : profiles)
|
for (SignedObjectHolder<ClientProfile> profile : profiles)
|
||||||
profile.write(output);
|
profile.write(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ public void reply() throws IOException {
|
||||||
Collection<SignedObjectHolder<ClientProfile>> profiles = server.getProfiles();
|
Collection<SignedObjectHolder<ClientProfile>> profiles = server.getProfiles();
|
||||||
output.writeLength(profiles.size(), 0);
|
output.writeLength(profiles.size(), 0);
|
||||||
for (SignedObjectHolder<ClientProfile> profile : profiles) {
|
for (SignedObjectHolder<ClientProfile> profile : profiles) {
|
||||||
LogHelper.debug("Writted profile: %s",profile.object.getTitle());
|
LogHelper.debug("Writted profile: %s", profile.object.getTitle());
|
||||||
profile.write(output);
|
profile.write(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,6 @@ public void reply() throws Exception {
|
||||||
// Write all update dirs names
|
// Write all update dirs names
|
||||||
output.writeLength(updateDirs.size(), 0);
|
output.writeLength(updateDirs.size(), 0);
|
||||||
for (Entry<String, SignedObjectHolder<HashedDir>> entry : updateDirs)
|
for (Entry<String, SignedObjectHolder<HashedDir>> entry : updateDirs)
|
||||||
output.writeString(entry.getKey(), 255);
|
output.writeString(entry.getKey(), 255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ public void reply() throws IOException {
|
||||||
// Read actions slice
|
// Read actions slice
|
||||||
int length = input.readLength(actionsSlice.length);
|
int length = input.readLength(actionsSlice.length);
|
||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
actionsSlice[i] = new UpdateAction(input);
|
actionsSlice[i] = new UpdateAction(input);
|
||||||
|
|
||||||
// Perform actions
|
// Perform actions
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
|
@ -69,7 +69,7 @@ public void reply() throws IOException {
|
||||||
// Get hashed dir (for validation)
|
// Get hashed dir (for validation)
|
||||||
HashedEntry hSubdir = dirStack.getLast().getEntry(action.name);
|
HashedEntry hSubdir = dirStack.getLast().getEntry(action.name);
|
||||||
if (hSubdir == null || hSubdir.getType() != Type.DIR)
|
if (hSubdir == null || hSubdir.getType() != Type.DIR)
|
||||||
throw new IOException("Unknown hashed dir: " + action.name);
|
throw new IOException("Unknown hashed dir: " + action.name);
|
||||||
dirStack.add((HashedDir) hSubdir);
|
dirStack.add((HashedDir) hSubdir);
|
||||||
|
|
||||||
// Resolve dir
|
// Resolve dir
|
||||||
|
@ -81,7 +81,7 @@ public void reply() throws IOException {
|
||||||
// Get hashed file (for validation)
|
// Get hashed file (for validation)
|
||||||
HashedEntry hFile = dirStack.getLast().getEntry(action.name);
|
HashedEntry hFile = dirStack.getLast().getEntry(action.name);
|
||||||
if (hFile == null || hFile.getType() != Type.FILE)
|
if (hFile == null || hFile.getType() != Type.FILE)
|
||||||
throw new IOException("Unknown hashed file: " + action.name);
|
throw new IOException("Unknown hashed file: " + action.name);
|
||||||
|
|
||||||
// Resolve and write file
|
// Resolve and write file
|
||||||
Path file = dir.resolve(action.name);
|
Path file = dir.resolve(action.name);
|
||||||
|
@ -101,7 +101,7 @@ public void reply() throws IOException {
|
||||||
// Remove from hashed dir stack
|
// Remove from hashed dir stack
|
||||||
dirStack.removeLast();
|
dirStack.removeLast();
|
||||||
if (dirStack.isEmpty())
|
if (dirStack.isEmpty())
|
||||||
throw new IOException("Empty hDir stack");
|
throw new IOException("Empty hDir stack");
|
||||||
|
|
||||||
// Get parent
|
// Get parent
|
||||||
dir = dir.getParent();
|
dir = dir.getParent();
|
||||||
|
@ -119,6 +119,6 @@ public void reply() throws IOException {
|
||||||
|
|
||||||
// So we've updated :)
|
// So we've updated :)
|
||||||
if (fileOutput instanceof DeflaterOutputStream)
|
if (fileOutput instanceof DeflaterOutputStream)
|
||||||
((DeflaterOutputStream) fileOutput).finish();
|
((DeflaterOutputStream) fileOutput).finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ public class Client {
|
||||||
public long session;
|
public long session;
|
||||||
|
|
||||||
public long timestamp;
|
public long timestamp;
|
||||||
|
|
||||||
public Client(long session) {
|
public Client(long session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
timestamp = System.currentTimeMillis();
|
timestamp = System.currentTimeMillis();
|
||||||
|
|
|
@ -15,9 +15,6 @@
|
||||||
import io.netty.handler.logging.LogLevel;
|
import io.netty.handler.logging.LogLevel;
|
||||||
import io.netty.handler.logging.LoggingHandler;
|
import io.netty.handler.logging.LoggingHandler;
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
import ru.gravit.launcher.LauncherAPI;
|
||||||
import ru.gravit.launcher.hasher.HashedEntry;
|
|
||||||
import ru.gravit.launcher.serialize.HInput;
|
|
||||||
import ru.gravit.launcher.serialize.HOutput;
|
|
||||||
import ru.gravit.launcher.ssl.LauncherKeyStore;
|
import ru.gravit.launcher.ssl.LauncherKeyStore;
|
||||||
import ru.gravit.launcher.ssl.LauncherTrustManager;
|
import ru.gravit.launcher.ssl.LauncherTrustManager;
|
||||||
import ru.gravit.launchserver.LaunchServer;
|
import ru.gravit.launchserver.LaunchServer;
|
||||||
|
@ -55,8 +52,6 @@ public final class NettyServerSocketHandler implements Runnable, AutoCloseable {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public volatile boolean logConnections = Boolean.getBoolean("launcher.logConnections");
|
public volatile boolean logConnections = Boolean.getBoolean("launcher.logConnections");
|
||||||
|
|
||||||
// Instance
|
|
||||||
private final LaunchServer server;
|
|
||||||
private final AtomicReference<ServerSocket> serverSocket = new AtomicReference<>();
|
private final AtomicReference<ServerSocket> serverSocket = new AtomicReference<>();
|
||||||
private final ExecutorService threadPool = Executors.newCachedThreadPool(THREAD_FACTORY);
|
private final ExecutorService threadPool = Executors.newCachedThreadPool(THREAD_FACTORY);
|
||||||
|
|
||||||
|
@ -64,12 +59,11 @@ public final class NettyServerSocketHandler implements Runnable, AutoCloseable {
|
||||||
private final Map<String, Response.Factory> customResponses = new ConcurrentHashMap<>(2);
|
private final Map<String, Response.Factory> customResponses = new ConcurrentHashMap<>(2);
|
||||||
private final AtomicLong idCounter = new AtomicLong(0L);
|
private final AtomicLong idCounter = new AtomicLong(0L);
|
||||||
private Set<Socket> sockets;
|
private Set<Socket> sockets;
|
||||||
private Selector selector;
|
|
||||||
private ServerSocketChannel serverChannel;
|
|
||||||
private volatile Listener listener;
|
private volatile Listener listener;
|
||||||
|
|
||||||
public NettyServerSocketHandler(LaunchServer server) {
|
public NettyServerSocketHandler(LaunchServer server) {
|
||||||
this.server = server;
|
// Instance
|
||||||
|
LaunchServer server1 = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -86,10 +80,10 @@ public void close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SSLContext SSLContextInit() throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, KeyManagementException, IOException, CertificateException {
|
public SSLContext SSLContextInit() throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, KeyManagementException, IOException, CertificateException {
|
||||||
TrustManager[] trustAllCerts = new TrustManager[] {
|
TrustManager[] trustAllCerts = new TrustManager[]{
|
||||||
new LauncherTrustManager()
|
new LauncherTrustManager()
|
||||||
};
|
};
|
||||||
KeyStore ks = LauncherKeyStore.getKeyStore("keystore","PSP1000");
|
KeyStore ks = LauncherKeyStore.getKeyStore("keystore", "PSP1000");
|
||||||
|
|
||||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
|
||||||
.getDefaultAlgorithm());
|
.getDefaultAlgorithm());
|
||||||
|
@ -120,8 +114,8 @@ public void run() {
|
||||||
//System.setProperty( "javax.net.ssl.keyStore","keystore");
|
//System.setProperty( "javax.net.ssl.keyStore","keystore");
|
||||||
//System.setProperty( "javax.net.ssl.keyStorePassword","PSP1000");
|
//System.setProperty( "javax.net.ssl.keyStorePassword","PSP1000");
|
||||||
try {
|
try {
|
||||||
selector = Selector.open();
|
Selector selector = Selector.open();
|
||||||
serverChannel = ServerSocketChannel.open();
|
ServerSocketChannel serverChannel = ServerSocketChannel.open();
|
||||||
serverChannel.configureBlocking(false);
|
serverChannel.configureBlocking(false);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -138,7 +132,7 @@ public void run() {
|
||||||
.handler(new LoggingHandler(LogLevel.DEBUG))
|
.handler(new LoggingHandler(LogLevel.DEBUG))
|
||||||
.childHandler(new ChannelInitializer<NioSocketChannel>() {
|
.childHandler(new ChannelInitializer<NioSocketChannel>() {
|
||||||
@Override
|
@Override
|
||||||
public void initChannel(NioSocketChannel ch) throws Exception {
|
public void initChannel(NioSocketChannel ch) {
|
||||||
ChannelPipeline pipeline = ch.pipeline();
|
ChannelPipeline pipeline = ch.pipeline();
|
||||||
//p.addLast(new LoggingHandler(LogLevel.INFO));
|
//p.addLast(new LoggingHandler(LogLevel.INFO));
|
||||||
System.out.println("P!");
|
System.out.println("P!");
|
||||||
|
@ -198,7 +192,7 @@ public void initChannel(NioSocketChannel ch) throws Exception {
|
||||||
public void registerCustomResponse(String name, Response.Factory factory) {
|
public void registerCustomResponse(String name, Response.Factory factory) {
|
||||||
VerifyHelper.verifyIDName(name);
|
VerifyHelper.verifyIDName(name);
|
||||||
VerifyHelper.putIfAbsent(customResponses, name, Objects.requireNonNull(factory, "factory"),
|
VerifyHelper.putIfAbsent(customResponses, name, Objects.requireNonNull(factory, "factory"),
|
||||||
String.format("Custom response has been already registered: '%s'", name));
|
String.format("Custom response has been already registered: '%s'", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
|
|
|
@ -26,6 +26,7 @@ public Handshake(int type, long session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final LaunchServer server;
|
private final LaunchServer server;
|
||||||
private final Socket socket;
|
private final Socket socket;
|
||||||
|
|
||||||
|
@ -45,18 +46,16 @@ private Handshake readHandshake(HInput input, HOutput output) throws IOException
|
||||||
// Verify magic number
|
// Verify magic number
|
||||||
int magicNumber = input.readInt();
|
int magicNumber = input.readInt();
|
||||||
if (magicNumber != Launcher.PROTOCOL_MAGIC)
|
if (magicNumber != Launcher.PROTOCOL_MAGIC)
|
||||||
if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY - 1) { // Previous launcher protocol
|
if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY - 1) { // Previous launcher protocol
|
||||||
session = 0;
|
session = 0;
|
||||||
legacy = true;
|
legacy = true;
|
||||||
}
|
} else if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY - 2) { // Previous launcher protocol
|
||||||
else if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY - 2) { // Previous launcher protocol
|
|
||||||
session = 0;
|
session = 0;
|
||||||
legacy = true;
|
legacy = true;
|
||||||
}
|
} else if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY) {
|
||||||
else if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY){
|
|
||||||
|
|
||||||
} else
|
} else
|
||||||
throw new IOException("Invalid Handshake");
|
throw new IOException("Invalid Handshake");
|
||||||
// Verify key modulus
|
// Verify key modulus
|
||||||
BigInteger keyModulus = input.readBigInteger(SecurityHelper.RSA_KEY_LENGTH + 1);
|
BigInteger keyModulus = input.readBigInteger(SecurityHelper.RSA_KEY_LENGTH + 1);
|
||||||
if (!legacy) {
|
if (!legacy) {
|
||||||
|
@ -68,7 +67,7 @@ else if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY){
|
||||||
throw new IOException(String.format("#%d Key modulus mismatch", session));
|
throw new IOException(String.format("#%d Key modulus mismatch", session));
|
||||||
}
|
}
|
||||||
// Read request type
|
// Read request type
|
||||||
Integer type = input.readVarInt();
|
int type = input.readVarInt();
|
||||||
if (!server.serverSocketHandler.onHandshake(session, type)) {
|
if (!server.serverSocketHandler.onHandshake(session, type)) {
|
||||||
output.writeBoolean(false);
|
output.writeBoolean(false);
|
||||||
return null;
|
return null;
|
||||||
|
@ -82,7 +81,7 @@ else if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY){
|
||||||
|
|
||||||
private void respond(Integer type, HInput input, HOutput output, long session, String ip) throws Exception {
|
private void respond(Integer type, HInput input, HOutput output, long session, String ip) throws Exception {
|
||||||
if (server.serverSocketHandler.logConnections)
|
if (server.serverSocketHandler.logConnections)
|
||||||
LogHelper.info("Connection #%d from %s", session, ip);
|
LogHelper.info("Connection #%d from %s", session, ip);
|
||||||
|
|
||||||
// Choose response based on type
|
// Choose response based on type
|
||||||
Response response = Response.getResponse(type, server, session, input, output, ip);
|
Response response = Response.getResponse(type, server, session, input, output, ip);
|
||||||
|
@ -95,7 +94,7 @@ private void respond(Integer type, HInput input, HOutput output, long session, S
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!server.serverSocketHandler.logConnections)
|
if (!server.serverSocketHandler.logConnections)
|
||||||
LogHelper.debug("Connection from %s", IOHelper.getIP(socket.getRemoteSocketAddress()));
|
LogHelper.debug("Connection from %s", IOHelper.getIP(socket.getRemoteSocketAddress()));
|
||||||
|
|
||||||
// Process connection
|
// Process connection
|
||||||
boolean cancelled = false;
|
boolean cancelled = false;
|
||||||
|
@ -121,7 +120,7 @@ public void run() {
|
||||||
} finally {
|
} finally {
|
||||||
IOHelper.close(socket);
|
IOHelper.close(socket);
|
||||||
if (!cancelled)
|
if (!cancelled)
|
||||||
server.serverSocketHandler.onDisconnect(savedError);
|
server.serverSocketHandler.onDisconnect(savedError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ public interface Listener {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
boolean onHandshake(long session, Integer type);
|
boolean onHandshake(long session, Integer type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final ThreadFactory THREAD_FACTORY = r -> CommonHelper.newThread("Network Thread", true, r);
|
private static final ThreadFactory THREAD_FACTORY = r -> CommonHelper.newThread("Network Thread", true, r);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
|
@ -68,7 +69,7 @@ public void close() {
|
||||||
|
|
||||||
/*package*/ void onDisconnect(Exception e) {
|
/*package*/ void onDisconnect(Exception e) {
|
||||||
if (listener != null)
|
if (listener != null)
|
||||||
listener.onDisconnect(e);
|
listener.onDisconnect(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ boolean onHandshake(long session, Integer type) {
|
/*package*/ boolean onHandshake(long session, Integer type) {
|
||||||
|
@ -80,7 +81,7 @@ public void run() {
|
||||||
LogHelper.info("Starting server socket thread");
|
LogHelper.info("Starting server socket thread");
|
||||||
try (ServerSocket serverSocket = new ServerSocket()) {
|
try (ServerSocket serverSocket = new ServerSocket()) {
|
||||||
if (!this.serverSocket.compareAndSet(null, serverSocket))
|
if (!this.serverSocket.compareAndSet(null, serverSocket))
|
||||||
throw new IllegalStateException("Previous socket wasn't closed");
|
throw new IllegalStateException("Previous socket wasn't closed");
|
||||||
|
|
||||||
// Set socket params
|
// Set socket params
|
||||||
serverSocket.setReuseAddress(true);
|
serverSocket.setReuseAddress(true);
|
||||||
|
@ -96,7 +97,7 @@ public void run() {
|
||||||
// Invoke pre-connect listener
|
// Invoke pre-connect listener
|
||||||
long id = idCounter.incrementAndGet();
|
long id = idCounter.incrementAndGet();
|
||||||
if (listener != null && !listener.onConnect(socket.getInetAddress()))
|
if (listener != null && !listener.onConnect(socket.getInetAddress()))
|
||||||
continue; // Listener didn't accepted this connection
|
continue; // Listener didn't accepted this connection
|
||||||
|
|
||||||
// Reply in separate thread
|
// Reply in separate thread
|
||||||
threadPool.execute(new ResponseThread(server, id, socket, sessionManager));
|
threadPool.execute(new ResponseThread(server, id, socket, sessionManager));
|
||||||
|
@ -104,7 +105,7 @@ public void run() {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Ignore error after close/rebind
|
// Ignore error after close/rebind
|
||||||
if (serverSocket.get() != null)
|
if (serverSocket.get() != null)
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,13 @@
|
||||||
|
|
||||||
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
|
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
|
||||||
static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
|
static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelActive(ChannelHandlerContext ctx) {
|
public void channelActive(ChannelHandlerContext ctx) {
|
||||||
LogHelper.debug("New client %s", IOHelper.getIP(ctx.channel().remoteAddress()));
|
LogHelper.debug("New client %s", IOHelper.getIP(ctx.channel().remoteAddress()));
|
||||||
channels.add(ctx.channel());
|
channels.add(ctx.channel());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
|
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
|
||||||
// ping and pong frames already handled
|
// ping and pong frames already handled
|
||||||
|
@ -26,6 +28,6 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) thr
|
||||||
long handshake = input.readLong();
|
long handshake = input.readLong();
|
||||||
long connection_flags = input.readLong();
|
long connection_flags = input.readLong();
|
||||||
long type = input.readInt();
|
long type = input.readInt();
|
||||||
LogHelper.debug("MessageHead: handshake %dl, flags %dl, type %d", handshake,connection_flags,type);
|
LogHelper.debug("MessageHead: handshake %dl, flags %dl, type %d", handshake, connection_flags, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ public WebSocketIndexPageHandler(String websocketPath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
|
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) {
|
||||||
// Handle a bad request.
|
// Handle a bad request.
|
||||||
if (!req.decoderResult().isSuccess()) {
|
if (!req.decoderResult().isSuccess()) {
|
||||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
|
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
|
||||||
|
|
|
@ -20,7 +20,7 @@ public NullTextureProvider(BlockConfigEntry block) {
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
TextureProvider provider = this.provider;
|
TextureProvider provider = this.provider;
|
||||||
if (provider != null)
|
if (provider != null)
|
||||||
provider.close();
|
provider.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -24,6 +24,7 @@ private static Texture getTexture(String url, boolean cloak) throws IOException
|
||||||
return null; // Simply not found
|
return null; // Simply not found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getTextureURL(String url, UUID uuid, String username, String client) {
|
private static String getTextureURL(String url, UUID uuid, String username, String client) {
|
||||||
return CommonHelper.replace(url, "username", IOHelper.urlEncode(username),
|
return CommonHelper.replace(url, "username", IOHelper.urlEncode(username),
|
||||||
"uuid", IOHelper.urlEncode(uuid.toString()), "hash", IOHelper.urlEncode(Launcher.toHash(uuid)),
|
"uuid", IOHelper.urlEncode(uuid.toString()), "hash", IOHelper.urlEncode(Launcher.toHash(uuid)),
|
||||||
|
|
|
@ -10,16 +10,18 @@ public enum Type {
|
||||||
CAPE,
|
CAPE,
|
||||||
ELYTRA
|
ELYTRA
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Set<Type> PROFILE_TEXTURE_TYPES = Collections.unmodifiableSet(EnumSet.allOf(Type.class));
|
public static final Set<Type> PROFILE_TEXTURE_TYPES = Collections.unmodifiableSet(EnumSet.allOf(Type.class));
|
||||||
|
|
||||||
public static final int PROFILE_TEXTURE_COUNT = PROFILE_TEXTURE_TYPES.size();
|
public static final int PROFILE_TEXTURE_COUNT = PROFILE_TEXTURE_TYPES.size();
|
||||||
|
|
||||||
private static String baseName(String url) {
|
private static String baseName(String url) {
|
||||||
String name = url.substring(url.lastIndexOf('/') + 1);
|
String name = url.substring(url.lastIndexOf('/') + 1);
|
||||||
|
|
||||||
// Remove index
|
// Remove index
|
||||||
int extensionIndex = name.lastIndexOf('.');
|
int extensionIndex = name.lastIndexOf('.');
|
||||||
if (extensionIndex >= 0)
|
if (extensionIndex >= 0)
|
||||||
name = name.substring(0, extensionIndex);
|
name = name.substring(0, extensionIndex);
|
||||||
|
|
||||||
// We're done
|
// We're done
|
||||||
return name;
|
return name;
|
||||||
|
|
|
@ -28,7 +28,7 @@ public static CompatProfile checkServer(String username, String serverID) throws
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static boolean joinServer(String username, String accessToken, String serverID) throws Exception {
|
public static boolean joinServer(String username, String accessToken, String serverID) throws Exception {
|
||||||
if (!ClientLauncher.isLaunched())
|
if (!ClientLauncher.isLaunched())
|
||||||
throw new IllegalStateException("Bad Login (Cheater)");
|
throw new IllegalStateException("Bad Login (Cheater)");
|
||||||
|
|
||||||
// Join server
|
// Join server
|
||||||
LogHelper.debug("LegacyBridge.joinServer, Username: '%s', Access token: %s, Server ID: %s", username, accessToken, serverID);
|
LogHelper.debug("LegacyBridge.joinServer, Username: '%s', Access token: %s, Server ID: %s", username, accessToken, serverID);
|
||||||
|
@ -52,7 +52,7 @@ public static CompatProfile[] profilesByUsername(String... usernames) throws Exc
|
||||||
// Convert profiles
|
// Convert profiles
|
||||||
CompatProfile[] resultProfiles = new CompatProfile[profiles.length];
|
CompatProfile[] resultProfiles = new CompatProfile[profiles.length];
|
||||||
for (int i = 0; i < profiles.length; i++)
|
for (int i = 0; i < profiles.length; i++)
|
||||||
resultProfiles[i] = CompatProfile.fromPlayerProfile(profiles[i]);
|
resultProfiles[i] = CompatProfile.fromPlayerProfile(profiles[i]);
|
||||||
|
|
||||||
// We're dones
|
// We're dones
|
||||||
return resultProfiles;
|
return resultProfiles;
|
||||||
|
|
|
@ -24,6 +24,7 @@ public static CompatProfile fromPlayerProfile(PlayerProfile profile) {
|
||||||
profile.cloak == null ? null : SecurityHelper.toHex(profile.cloak.digest)
|
profile.cloak == null ? null : SecurityHelper.toHex(profile.cloak.digest)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance
|
// Instance
|
||||||
public final UUID uuid;
|
public final UUID uuid;
|
||||||
public final String uuidHash, username;
|
public final String uuidHash, username;
|
||||||
|
@ -44,13 +45,13 @@ public CompatProfile(UUID uuid, String username, String skinURL, String skinDige
|
||||||
public int countProperties() {
|
public int countProperties() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
if (skinURL != null)
|
if (skinURL != null)
|
||||||
count++;
|
count++;
|
||||||
if (skinDigest != null)
|
if (skinDigest != null)
|
||||||
count++;
|
count++;
|
||||||
if (cloakURL != null)
|
if (cloakURL != null)
|
||||||
count++;
|
count++;
|
||||||
if (cloakDigest != null)
|
if (cloakDigest != null)
|
||||||
count++;
|
count++;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ public static String getSkinURL(String username) {
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static String joinServer(String username, String accessToken, String serverID) {
|
public static String joinServer(String username, String accessToken, String serverID) {
|
||||||
if (!ClientLauncher.isLaunched())
|
if (!ClientLauncher.isLaunched())
|
||||||
return "Bad Login (Cheater)";
|
return "Bad Login (Cheater)";
|
||||||
|
|
||||||
// Join server
|
// Join server
|
||||||
LogHelper.debug("LegacyBridge.joinServer, Username: '%s', Access token: %s, Server ID: %s", username, accessToken, serverID);
|
LogHelper.debug("LegacyBridge.joinServer, Username: '%s', Access token: %s, Server ID: %s", username, accessToken, serverID);
|
||||||
|
|
|
@ -36,7 +36,7 @@ public final class YggdrasilMinecraftSessionService extends BaseMinecraftSession
|
||||||
public static void fillTextureProperties(GameProfile profile, PlayerProfile pp) {
|
public static void fillTextureProperties(GameProfile profile, PlayerProfile pp) {
|
||||||
LogHelper.debug("fillTextureProperties, Username: '%s'", profile.getName());
|
LogHelper.debug("fillTextureProperties, Username: '%s'", profile.getName());
|
||||||
if (NO_TEXTURES)
|
if (NO_TEXTURES)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Fill textures map
|
// Fill textures map
|
||||||
PropertyMap properties = profile.getProperties();
|
PropertyMap properties = profile.getProperties();
|
||||||
|
@ -66,14 +66,14 @@ private static void getTexturesMojang(Map<Type, MinecraftProfileTexture> texture
|
||||||
// Fetch textures from textures JSON
|
// Fetch textures from textures JSON
|
||||||
for (Type type : MinecraftProfileTexture.PROFILE_TEXTURE_TYPES) {
|
for (Type type : MinecraftProfileTexture.PROFILE_TEXTURE_TYPES) {
|
||||||
if (textures.containsKey(type))
|
if (textures.containsKey(type))
|
||||||
continue; // Overriden by launcher
|
continue; // Overriden by launcher
|
||||||
|
|
||||||
// Get texture from JSON
|
// Get texture from JSON
|
||||||
JsonElement textureJSON = texturesJSON.get(type.name());
|
JsonElement textureJSON = texturesJSON.get(type.name());
|
||||||
if (textureJSON != null && textureJSON.isJsonObject()) {
|
if (textureJSON != null && textureJSON.isJsonObject()) {
|
||||||
JsonElement urlValue = textureJSON.getAsJsonObject().get("url");
|
JsonElement urlValue = textureJSON.getAsJsonObject().get("url");
|
||||||
if (urlValue.isJsonPrimitive())
|
if (urlValue.isJsonPrimitive())
|
||||||
textures.put(type, new MinecraftProfileTexture(urlValue.getAsString()));
|
textures.put(type, new MinecraftProfileTexture(urlValue.getAsString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ public GameProfile fillProfileProperties(GameProfile profile, boolean requireSec
|
||||||
UUID uuid = profile.getUUID();
|
UUID uuid = profile.getUUID();
|
||||||
LogHelper.debug("fillProfileProperties, UUID: %s", uuid);
|
LogHelper.debug("fillProfileProperties, UUID: %s", uuid);
|
||||||
if (uuid == null)
|
if (uuid == null)
|
||||||
return profile;
|
return profile;
|
||||||
|
|
||||||
// Make profile request
|
// Make profile request
|
||||||
PlayerProfile pp;
|
PlayerProfile pp;
|
||||||
|
@ -129,19 +129,19 @@ public Map<Type, MinecraftProfileTexture> getTextures(GameProfile profile, boole
|
||||||
Property skinURL = Iterables.getFirst(profile.getProperties().get(ClientLauncher.SKIN_URL_PROPERTY), null);
|
Property skinURL = Iterables.getFirst(profile.getProperties().get(ClientLauncher.SKIN_URL_PROPERTY), null);
|
||||||
Property skinDigest = Iterables.getFirst(profile.getProperties().get(ClientLauncher.SKIN_DIGEST_PROPERTY), null);
|
Property skinDigest = Iterables.getFirst(profile.getProperties().get(ClientLauncher.SKIN_DIGEST_PROPERTY), null);
|
||||||
if (skinURL != null && skinDigest != null)
|
if (skinURL != null && skinDigest != null)
|
||||||
textures.put(Type.SKIN, new MinecraftProfileTexture(skinURL.getValue(), skinDigest.getValue()));
|
textures.put(Type.SKIN, new MinecraftProfileTexture(skinURL.getValue(), skinDigest.getValue()));
|
||||||
|
|
||||||
// Add cloak URL to textures map
|
// Add cloak URL to textures map
|
||||||
Property cloakURL = Iterables.getFirst(profile.getProperties().get(ClientLauncher.CLOAK_URL_PROPERTY), null);
|
Property cloakURL = Iterables.getFirst(profile.getProperties().get(ClientLauncher.CLOAK_URL_PROPERTY), null);
|
||||||
Property cloakDigest = Iterables.getFirst(profile.getProperties().get(ClientLauncher.CLOAK_DIGEST_PROPERTY), null);
|
Property cloakDigest = Iterables.getFirst(profile.getProperties().get(ClientLauncher.CLOAK_DIGEST_PROPERTY), null);
|
||||||
if (cloakURL != null && cloakDigest != null)
|
if (cloakURL != null && cloakDigest != null)
|
||||||
textures.put(Type.CAPE, new MinecraftProfileTexture(cloakURL.getValue(), cloakDigest.getValue()));
|
textures.put(Type.CAPE, new MinecraftProfileTexture(cloakURL.getValue(), cloakDigest.getValue()));
|
||||||
|
|
||||||
// Try to find missing textures in textures payload (now always true because launcher is not passing elytra skins)
|
// Try to find missing textures in textures payload (now always true because launcher is not passing elytra skins)
|
||||||
if (textures.size() != MinecraftProfileTexture.PROFILE_TEXTURE_COUNT) {
|
if (textures.size() != MinecraftProfileTexture.PROFILE_TEXTURE_COUNT) {
|
||||||
Property texturesMojang = Iterables.getFirst(profile.getProperties().get("textures"), null);
|
Property texturesMojang = Iterables.getFirst(profile.getProperties().get("textures"), null);
|
||||||
if (texturesMojang != null)
|
if (texturesMojang != null)
|
||||||
getTexturesMojang(textures, texturesMojang.getValue(), profile);
|
getTexturesMojang(textures, texturesMojang.getValue(), profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,13 +171,15 @@ public GameProfile hasJoinedServer(GameProfile profile, String serverID) throws
|
||||||
public GameProfile hasJoinedServer(GameProfile profile, String serverID, InetAddress address) throws AuthenticationUnavailableException {
|
public GameProfile hasJoinedServer(GameProfile profile, String serverID, InetAddress address) throws AuthenticationUnavailableException {
|
||||||
return hasJoinedServer(profile, serverID);
|
return hasJoinedServer(profile, serverID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public YggdrasilAuthenticationService getAuthenticationService() {
|
public YggdrasilAuthenticationService getAuthenticationService() {
|
||||||
return (YggdrasilAuthenticationService)super.getAuthenticationService();
|
return (YggdrasilAuthenticationService) super.getAuthenticationService();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void joinServer(GameProfile profile, String accessToken, String serverID) throws AuthenticationException {
|
public void joinServer(GameProfile profile, String accessToken, String serverID) throws AuthenticationException {
|
||||||
if (!ClientLauncher.isLaunched())
|
if (!ClientLauncher.isLaunched())
|
||||||
throw new AuthenticationException("Bad Login (Cheater)");
|
throw new AuthenticationException("Bad Login (Cheater)");
|
||||||
|
|
||||||
// Join server
|
// Join server
|
||||||
String username = profile.getName();
|
String username = profile.getName();
|
||||||
|
@ -193,6 +195,6 @@ public void joinServer(GameProfile profile, String accessToken, String serverID)
|
||||||
|
|
||||||
// Verify is success
|
// Verify is success
|
||||||
if (!success)
|
if (!success)
|
||||||
throw new AuthenticationException("Bad Login (Clientside)");
|
throw new AuthenticationException("Bad Login (Clientside)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
import ru.zaxar163.GuardBind;
|
import ru.zaxar163.GuardBind;
|
||||||
|
|
||||||
public class AvanguardStarter {
|
public class AvanguardStarter {
|
||||||
static class SecurityThread implements Runnable {
|
static class SecurityThread implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (!Thread.interrupted()) {
|
while (!Thread.interrupted()) {
|
||||||
|
@ -40,8 +40,9 @@ public void run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static final String NAME = Launcher.getConfig().projectname;
|
|
||||||
public static String avn32 = null, avn64 = null;
|
public static final String NAME = Launcher.getConfig().projectname;
|
||||||
|
public static String avn32 = null, avn64 = null;
|
||||||
public static Path wrap32 = null, wrap64 = null;
|
public static Path wrap32 = null, wrap64 = null;
|
||||||
|
|
||||||
private static Path handle(Path mustdiedll, String resource) {
|
private static Path handle(Path mustdiedll, String resource) {
|
||||||
|
@ -51,13 +52,13 @@ private static Path handle(Path mustdiedll, String resource) {
|
||||||
in.close();
|
in.close();
|
||||||
if (IOHelper.exists(mustdiedll)) {
|
if (IOHelper.exists(mustdiedll)) {
|
||||||
if (!matches(mustdiedll, orig))
|
if (!matches(mustdiedll, orig))
|
||||||
transfer(orig, mustdiedll);
|
transfer(orig, mustdiedll);
|
||||||
} else
|
} else
|
||||||
transfer(orig, mustdiedll);
|
transfer(orig, mustdiedll);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (e instanceof RuntimeException)
|
if (e instanceof RuntimeException)
|
||||||
throw (RuntimeException) e;
|
throw (RuntimeException) e;
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
return mustdiedll;
|
return mustdiedll;
|
||||||
}
|
}
|
||||||
|
@ -107,8 +108,8 @@ public static void start(Path path1) throws IOException {
|
||||||
handle(path.resolve("Avanguard64.dll"), "Avanguard64.dll"),
|
handle(path.resolve("Avanguard64.dll"), "Avanguard64.dll"),
|
||||||
handle(path.resolve(NAME + "32.exe"), "wrapper32.exe"),
|
handle(path.resolve(NAME + "32.exe"), "wrapper32.exe"),
|
||||||
handle(path.resolve(NAME + "64.exe"), "wrapper64.exe"));
|
handle(path.resolve(NAME + "64.exe"), "wrapper64.exe"));
|
||||||
HashedDir guard = new HashedDir(path,null,true,false);
|
HashedDir guard = new HashedDir(path, null, true, false);
|
||||||
try(DirWatcher dirWatcher = new DirWatcher(path, guard, null, false)){
|
try (DirWatcher dirWatcher = new DirWatcher(path, guard, null, false)) {
|
||||||
CommonHelper.newThread("Guard Directory Watcher", true, dirWatcher).start();
|
CommonHelper.newThread("Guard Directory Watcher", true, dirWatcher).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,6 +161,7 @@ public static void main(String... args) throws Throwable {
|
||||||
Instant end = Instant.now();
|
Instant end = Instant.now();
|
||||||
LogHelper.debug("Launcher started in %dms", Duration.between(start, end).toMillis());
|
LogHelper.debug("Launcher started in %dms", Duration.between(start, end).toMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance
|
// Instance
|
||||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
private final AtomicBoolean started = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
@ -175,7 +176,7 @@ public Object loadScript(String path) throws IOException, ScriptException {
|
||||||
URL url = Launcher.getResourceURL(path);
|
URL url = Launcher.getResourceURL(path);
|
||||||
LogHelper.debug("Loading script: '%s'", url);
|
LogHelper.debug("Loading script: '%s'", url);
|
||||||
try (BufferedReader reader = IOHelper.newReader(url)) {
|
try (BufferedReader reader = IOHelper.newReader(url)) {
|
||||||
return engine.eval(reader,engine.getBindings(ScriptContext.ENGINE_SCOPE));
|
return engine.eval(reader, engine.getBindings(ScriptContext.ENGINE_SCOPE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +196,7 @@ public void start(String... args) throws Throwable {
|
||||||
Launcher.modulesManager.preInitModules();
|
Launcher.modulesManager.preInitModules();
|
||||||
Objects.requireNonNull(args, "args");
|
Objects.requireNonNull(args, "args");
|
||||||
if (started.getAndSet(true))
|
if (started.getAndSet(true))
|
||||||
throw new IllegalStateException("Launcher has been already started");
|
throw new IllegalStateException("Launcher has been already started");
|
||||||
Launcher.modulesManager.initModules();
|
Launcher.modulesManager.initModules();
|
||||||
// Load init.js script
|
// Load init.js script
|
||||||
loadScript(Launcher.API_SCRIPT_FILE);
|
loadScript(Launcher.API_SCRIPT_FILE);
|
||||||
|
|
|
@ -62,10 +62,11 @@ private ClassPathFileVisitor(Collection<Path> result) {
|
||||||
@Override
|
@Override
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
|
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
|
||||||
result.add(file);
|
result.add(file);
|
||||||
return super.visitFile(file, attrs);
|
return super.visitFile(file, attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Params extends StreamObject {
|
public static final class Params extends StreamObject {
|
||||||
// Client paths
|
// Client paths
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
|
@ -147,6 +148,7 @@ public void write(HOutput output) throws IOException {
|
||||||
output.writeVarInt(height);
|
output.writeVarInt(height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] EMPTY_ARRAY = new String[0];
|
private static final String[] EMPTY_ARRAY = new String[0];
|
||||||
private static final String MAGICAL_INTEL_OPTION = "-XX:HeapDumpPath=ThisTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump";
|
private static final String MAGICAL_INTEL_OPTION = "-XX:HeapDumpPath=ThisTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump";
|
||||||
private static final boolean isUsingWrapper = true;
|
private static final boolean isUsingWrapper = true;
|
||||||
|
@ -215,7 +217,7 @@ private static void addClientArgs(Collection<String> args, ClientProfile profile
|
||||||
Collections.addAll(args, "--assetIndex", profile.getAssetIndex());
|
Collections.addAll(args, "--assetIndex", profile.getAssetIndex());
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
Collections.addAll(args, "--session", params.accessToken);
|
Collections.addAll(args, "--session", params.accessToken);
|
||||||
|
|
||||||
// Add version and dirs args
|
// Add version and dirs args
|
||||||
Collections.addAll(args, "--version", profile.getVersion().name);
|
Collections.addAll(args, "--version", profile.getVersion().name);
|
||||||
|
@ -223,7 +225,7 @@ private static void addClientArgs(Collection<String> args, ClientProfile profile
|
||||||
Collections.addAll(args, "--assetsDir", params.assetDir.toString());
|
Collections.addAll(args, "--assetsDir", params.assetDir.toString());
|
||||||
Collections.addAll(args, "--resourcePackDir", params.clientDir.resolve(RESOURCEPACKS_DIR).toString());
|
Collections.addAll(args, "--resourcePackDir", params.clientDir.resolve(RESOURCEPACKS_DIR).toString());
|
||||||
if (version.compareTo(ClientProfile.Version.MC194) >= 0)
|
if (version.compareTo(ClientProfile.Version.MC194) >= 0)
|
||||||
Collections.addAll(args, "--versionType", "Launcher v" + Launcher.getVersion().getVersionString());
|
Collections.addAll(args, "--versionType", "Launcher v" + Launcher.getVersion().getVersionString());
|
||||||
|
|
||||||
// Add server args
|
// Add server args
|
||||||
if (params.autoEnter) {
|
if (params.autoEnter) {
|
||||||
|
@ -233,7 +235,7 @@ private static void addClientArgs(Collection<String> args, ClientProfile profile
|
||||||
|
|
||||||
// Add window size args
|
// Add window size args
|
||||||
if (params.fullScreen)
|
if (params.fullScreen)
|
||||||
Collections.addAll(args, "--fullscreen", Boolean.toString(true));
|
Collections.addAll(args, "--fullscreen", Boolean.toString(true));
|
||||||
if (params.width > 0 && params.height > 0) {
|
if (params.width > 0 && params.height > 0) {
|
||||||
Collections.addAll(args, "--width", Integer.toString(params.width));
|
Collections.addAll(args, "--width", Integer.toString(params.width));
|
||||||
Collections.addAll(args, "--height", Integer.toString(params.height));
|
Collections.addAll(args, "--height", Integer.toString(params.height));
|
||||||
|
@ -249,6 +251,7 @@ private static void addClientLegacyArgs(Collection<String> args, ClientProfile p
|
||||||
Collections.addAll(args, "--gameDir", params.clientDir.toString());
|
Collections.addAll(args, "--gameDir", params.clientDir.toString());
|
||||||
Collections.addAll(args, "--assetsDir", params.assetDir.toString());
|
Collections.addAll(args, "--assetsDir", params.assetDir.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public static void checkJVMBitsAndVersion() {
|
public static void checkJVMBitsAndVersion() {
|
||||||
if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) {
|
if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) {
|
||||||
|
@ -264,6 +267,7 @@ public static void checkJVMBitsAndVersion() {
|
||||||
JOptionPane.showMessageDialog(null, error);
|
JOptionPane.showMessageDialog(null, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public static boolean isLaunched() {
|
public static boolean isLaunched() {
|
||||||
return LAUNCHED.get();
|
return LAUNCHED.get();
|
||||||
|
@ -281,9 +285,9 @@ private static void launch(ClientProfile profile, Params params) throws Throwabl
|
||||||
// Add client args
|
// Add client args
|
||||||
Collection<String> args = new LinkedList<>();
|
Collection<String> args = new LinkedList<>();
|
||||||
if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0)
|
if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0)
|
||||||
addClientArgs(args, profile, params);
|
addClientArgs(args, profile, params);
|
||||||
else
|
else
|
||||||
addClientLegacyArgs(args, profile, params);
|
addClientLegacyArgs(args, profile, params);
|
||||||
Collections.addAll(args, profile.getClientArgs());
|
Collections.addAll(args, profile.getClientArgs());
|
||||||
LogHelper.debug("Args: " + args);
|
LogHelper.debug("Args: " + args);
|
||||||
// Resolve main class and method
|
// Resolve main class and method
|
||||||
|
@ -304,7 +308,7 @@ public static Process launch(
|
||||||
// Write params file (instead of CLI; Mustdie32 API can't handle command line > 32767 chars)
|
// Write params file (instead of CLI; Mustdie32 API can't handle command line > 32767 chars)
|
||||||
LogHelper.debug("Writing ClientLauncher params");
|
LogHelper.debug("Writing ClientLauncher params");
|
||||||
Path paramsFile = Files.createTempFile("ClientLauncherParams", ".bin");
|
Path paramsFile = Files.createTempFile("ClientLauncherParams", ".bin");
|
||||||
CommonHelper.newThread("Client params writter",false,() ->
|
CommonHelper.newThread("Client params writter", false, () ->
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
try (ServerSocket socket = new ServerSocket()) {
|
try (ServerSocket socket = new ServerSocket()) {
|
||||||
|
@ -333,13 +337,14 @@ public static Process launch(
|
||||||
// Resolve java bin and set permissions
|
// Resolve java bin and set permissions
|
||||||
LogHelper.debug("Resolving JVM binary");
|
LogHelper.debug("Resolving JVM binary");
|
||||||
//Path javaBin = IOHelper.resolveJavaBin(jvmDir);
|
//Path javaBin = IOHelper.resolveJavaBin(jvmDir);
|
||||||
checkJVMBitsAndVersion();
|
checkJVMBitsAndVersion();
|
||||||
// Fill CLI arguments
|
// Fill CLI arguments
|
||||||
List<String> args = new LinkedList<>();
|
List<String> args = new LinkedList<>();
|
||||||
boolean wrapper = isUsingWrapper();
|
boolean wrapper = isUsingWrapper();
|
||||||
Path javaBin;
|
Path javaBin;
|
||||||
if (wrapper) javaBin = JVMHelper.JVM_BITS == 64 ? AvanguardStarter.wrap64: AvanguardStarter.wrap32;
|
if (wrapper) javaBin = JVMHelper.JVM_BITS == 64 ? AvanguardStarter.wrap64 : AvanguardStarter.wrap32;
|
||||||
else javaBin = Paths.get(System.getProperty("java.home") + IOHelper.PLATFORM_SEPARATOR + "bin" + IOHelper.PLATFORM_SEPARATOR + "java");
|
else
|
||||||
|
javaBin = Paths.get(System.getProperty("java.home") + IOHelper.PLATFORM_SEPARATOR + "bin" + IOHelper.PLATFORM_SEPARATOR + "java");
|
||||||
args.add(javaBin.toString());
|
args.add(javaBin.toString());
|
||||||
args.add(MAGICAL_INTEL_OPTION);
|
args.add(MAGICAL_INTEL_OPTION);
|
||||||
if (params.ram > 0 && params.ram <= JVMHelper.RAM) {
|
if (params.ram > 0 && params.ram <= JVMHelper.RAM) {
|
||||||
|
@ -348,7 +353,7 @@ public static Process launch(
|
||||||
}
|
}
|
||||||
args.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, Boolean.toString(LogHelper.isDebugEnabled())));
|
args.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, Boolean.toString(LogHelper.isDebugEnabled())));
|
||||||
if (LauncherConfig.ADDRESS_OVERRIDE != null)
|
if (LauncherConfig.ADDRESS_OVERRIDE != null)
|
||||||
args.add(JVMHelper.jvmProperty(LauncherConfig.ADDRESS_OVERRIDE_PROPERTY, LauncherConfig.ADDRESS_OVERRIDE));
|
args.add(JVMHelper.jvmProperty(LauncherConfig.ADDRESS_OVERRIDE_PROPERTY, LauncherConfig.ADDRESS_OVERRIDE));
|
||||||
if (JVMHelper.OS_TYPE == OS.MUSTDIE) {
|
if (JVMHelper.OS_TYPE == OS.MUSTDIE) {
|
||||||
if (JVMHelper.OS_VERSION.startsWith("10.")) {
|
if (JVMHelper.OS_VERSION.startsWith("10.")) {
|
||||||
LogHelper.debug("MustDie 10 fix is applied");
|
LogHelper.debug("MustDie 10 fix is applied");
|
||||||
|
@ -363,10 +368,10 @@ public static Process launch(
|
||||||
StringBuilder classPathString = new StringBuilder(pathLauncher);
|
StringBuilder classPathString = new StringBuilder(pathLauncher);
|
||||||
LinkedList<Path> classPath = resolveClassPathList(params.clientDir, profile.object.getClassPath());
|
LinkedList<Path> classPath = resolveClassPathList(params.clientDir, profile.object.getClassPath());
|
||||||
for (Path path : classPath)
|
for (Path path : classPath)
|
||||||
classPathString.append(File.pathSeparatorChar).append(path.toString());
|
classPathString.append(File.pathSeparatorChar).append(path.toString());
|
||||||
Collections.addAll(args, profile.object.getJvmArgs());
|
Collections.addAll(args, profile.object.getJvmArgs());
|
||||||
Collections.addAll(args, "-Djava.library.path=".concat(params.clientDir.resolve(NATIVES_DIR).toString())); // Add Native Path
|
Collections.addAll(args, "-Djava.library.path=".concat(params.clientDir.resolve(NATIVES_DIR).toString())); // Add Native Path
|
||||||
Collections.addAll(args,"-javaagent:".concat(pathLauncher));
|
Collections.addAll(args, "-javaagent:".concat(pathLauncher));
|
||||||
//Collections.addAll(args, "-classpath", classPathString.toString());
|
//Collections.addAll(args, "-classpath", classPathString.toString());
|
||||||
//if(wrapper)
|
//if(wrapper)
|
||||||
//Collections.addAll(args, "-Djava.class.path=".concat(classPathString.toString())); // Add Class Path
|
//Collections.addAll(args, "-Djava.class.path=".concat(classPathString.toString())); // Add Class Path
|
||||||
|
@ -379,8 +384,8 @@ public static Process launch(
|
||||||
// Build client process
|
// Build client process
|
||||||
LogHelper.debug("Launching client instance");
|
LogHelper.debug("Launching client instance");
|
||||||
ProcessBuilder builder = new ProcessBuilder(args);
|
ProcessBuilder builder = new ProcessBuilder(args);
|
||||||
if(wrapper)
|
if (wrapper)
|
||||||
builder.environment().put("JAVA_HOME", System.getProperty("java.home"));
|
builder.environment().put("JAVA_HOME", System.getProperty("java.home"));
|
||||||
//else
|
//else
|
||||||
//builder.environment().put("CLASSPATH", classPathString.toString());
|
//builder.environment().put("CLASSPATH", classPathString.toString());
|
||||||
EnvHelper.addEnv(builder);
|
EnvHelper.addEnv(builder);
|
||||||
|
@ -427,8 +432,7 @@ public static void main(String... args) throws Throwable {
|
||||||
clientHDir = new SignedObjectHolder<>(input, publicKey, HashedDir::new);
|
clientHDir = new SignedObjectHolder<>(input, publicKey, HashedDir::new);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ex)
|
} catch (IOException ex) {
|
||||||
{
|
|
||||||
LogHelper.error(ex);
|
LogHelper.error(ex);
|
||||||
try (HInput input = new HInput(IOHelper.newInput(paramsFile))) {
|
try (HInput input = new HInput(IOHelper.newInput(paramsFile))) {
|
||||||
params = new Params(input);
|
params = new Params(input);
|
||||||
|
@ -509,12 +513,12 @@ public static void setProfile(ClientProfile profile) {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
|
public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
|
||||||
if (matcher != null)
|
if (matcher != null)
|
||||||
matcher = matcher.verifyOnly();
|
matcher = matcher.verifyOnly();
|
||||||
|
|
||||||
// Hash directory and compare (ignore update-only matcher entries, it will break offline-mode)
|
// Hash directory and compare (ignore update-only matcher entries, it will break offline-mode)
|
||||||
HashedDir currentHDir = new HashedDir(dir, matcher, false, digest);
|
HashedDir currentHDir = new HashedDir(dir, matcher, false, digest);
|
||||||
if (!hdir.diff(currentHDir, matcher).isSame())
|
if (!hdir.diff(currentHDir, matcher).isSame())
|
||||||
throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir)));
|
throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientLauncher() {
|
private ClientLauncher() {
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
|
|
||||||
public class ClientModuleContext implements ModuleContext {
|
public class ClientModuleContext implements ModuleContext {
|
||||||
public final LauncherEngine engine;
|
public final LauncherEngine engine;
|
||||||
ClientModuleContext(LauncherEngine engine)
|
|
||||||
{
|
ClientModuleContext(LauncherEngine engine) {
|
||||||
this.engine = engine;
|
this.engine = engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType() {
|
public Type getType() {
|
||||||
return Type.CLIENT;
|
return Type.CLIENT;
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
import ru.gravit.launcher.modules.SimpleModuleManager;
|
import ru.gravit.launcher.modules.SimpleModuleManager;
|
||||||
|
|
||||||
public class ClientModuleManager extends SimpleModuleManager {
|
public class ClientModuleManager extends SimpleModuleManager {
|
||||||
public ClientModuleManager(LauncherEngine engine)
|
public ClientModuleManager(LauncherEngine engine) {
|
||||||
{
|
|
||||||
context = new ClientModuleContext(engine);
|
context = new ClientModuleContext(engine);
|
||||||
modules = new ArrayList<>();
|
modules = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
@SuppressWarnings("AbstractClassNeverImplemented")
|
@SuppressWarnings("AbstractClassNeverImplemented")
|
||||||
public abstract class JSApplication extends Application {
|
public abstract class JSApplication extends Application {
|
||||||
private static final AtomicReference<JSApplication> INSTANCE = new AtomicReference<>();
|
private static final AtomicReference<JSApplication> INSTANCE = new AtomicReference<>();
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public static JSApplication getInstance() {
|
public static JSApplication getInstance() {
|
||||||
return INSTANCE.get();
|
return INSTANCE.get();
|
||||||
|
|
|
@ -43,12 +43,14 @@ public boolean isOverfilled() {
|
||||||
return onlinePlayers >= maxPlayers;
|
return onlinePlayers >= maxPlayers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
private static final String LEGACY_PING_HOST_MAGIC = "§1";
|
private static final String LEGACY_PING_HOST_MAGIC = "§1";
|
||||||
private static final String LEGACY_PING_HOST_CHANNEL = "MC|PingHost";
|
private static final String LEGACY_PING_HOST_CHANNEL = "MC|PingHost";
|
||||||
private static final Pattern LEGACY_PING_HOST_DELIMETER = Pattern.compile("\0", Pattern.LITERAL);
|
private static final Pattern LEGACY_PING_HOST_DELIMETER = Pattern.compile("\0", Pattern.LITERAL);
|
||||||
|
|
||||||
private static final int PACKET_LENGTH = 65535;
|
private static final int PACKET_LENGTH = 65535;
|
||||||
|
|
||||||
private static String readUTF16String(HInput input) throws IOException {
|
private static String readUTF16String(HInput input) throws IOException {
|
||||||
int length = input.readUnsignedShort() << 1;
|
int length = input.readUnsignedShort() << 1;
|
||||||
byte[] encoded = input.readByteArray(-length);
|
byte[] encoded = input.readByteArray(-length);
|
||||||
|
@ -59,6 +61,7 @@ private static void writeUTF16String(HOutput output, String s) throws IOExceptio
|
||||||
output.writeShort((short) s.length());
|
output.writeShort((short) s.length());
|
||||||
output.stream.write(s.getBytes(StandardCharsets.UTF_16BE));
|
output.stream.write(s.getBytes(StandardCharsets.UTF_16BE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance
|
// Instance
|
||||||
private final InetSocketAddress address;
|
private final InetSocketAddress address;
|
||||||
private final ClientProfile.Version version;
|
private final ClientProfile.Version version;
|
||||||
|
@ -114,25 +117,25 @@ private Result legacyPing(HInput input, HOutput output, boolean mc16) throws IOE
|
||||||
// Raed kick (response) packet
|
// Raed kick (response) packet
|
||||||
int kickPacketID = input.readUnsignedByte();
|
int kickPacketID = input.readUnsignedByte();
|
||||||
if (kickPacketID != 0xFF)
|
if (kickPacketID != 0xFF)
|
||||||
throw new IOException("Illegal kick packet ID: " + kickPacketID);
|
throw new IOException("Illegal kick packet ID: " + kickPacketID);
|
||||||
|
|
||||||
// Read and parse response
|
// Read and parse response
|
||||||
String response = readUTF16String(input);
|
String response = readUTF16String(input);
|
||||||
LogHelper.debug("Ping response (legacy): '%s'", response);
|
LogHelper.debug("Ping response (legacy): '%s'", response);
|
||||||
String[] splitted = LEGACY_PING_HOST_DELIMETER.split(response);
|
String[] splitted = LEGACY_PING_HOST_DELIMETER.split(response);
|
||||||
if (splitted.length != 6)
|
if (splitted.length != 6)
|
||||||
throw new IOException("Tokens count mismatch");
|
throw new IOException("Tokens count mismatch");
|
||||||
|
|
||||||
// Verify all parts
|
// Verify all parts
|
||||||
String magic = splitted[0];
|
String magic = splitted[0];
|
||||||
if (!magic.equals(LEGACY_PING_HOST_MAGIC))
|
if (!magic.equals(LEGACY_PING_HOST_MAGIC))
|
||||||
throw new IOException("Magic string mismatch: " + magic);
|
throw new IOException("Magic string mismatch: " + magic);
|
||||||
int protocol = Integer.parseInt(splitted[1]);
|
int protocol = Integer.parseInt(splitted[1]);
|
||||||
if (protocol != version.protocol)
|
if (protocol != version.protocol)
|
||||||
throw new IOException("Protocol mismatch: " + protocol);
|
throw new IOException("Protocol mismatch: " + protocol);
|
||||||
String clientVersion = splitted[2];
|
String clientVersion = splitted[2];
|
||||||
if (!clientVersion.equals(version.name))
|
if (!clientVersion.equals(version.name))
|
||||||
throw new IOException(String.format("Version mismatch: '%s'", clientVersion));
|
throw new IOException(String.format("Version mismatch: '%s'", clientVersion));
|
||||||
int onlinePlayers = VerifyHelper.verifyInt(Integer.parseInt(splitted[4]),
|
int onlinePlayers = VerifyHelper.verifyInt(Integer.parseInt(splitted[4]),
|
||||||
VerifyHelper.NOT_NEGATIVE, "onlinePlayers can't be < 0");
|
VerifyHelper.NOT_NEGATIVE, "onlinePlayers can't be < 0");
|
||||||
int maxPlayers = VerifyHelper.verifyInt(Integer.parseInt(splitted[5]),
|
int maxPlayers = VerifyHelper.verifyInt(Integer.parseInt(splitted[5]),
|
||||||
|
@ -167,7 +170,7 @@ private Result modernPing(HInput input, HOutput output) throws IOException {
|
||||||
// ab is a dirty fix for some servers (noticed KCauldron 1.7.10)
|
// ab is a dirty fix for some servers (noticed KCauldron 1.7.10)
|
||||||
int ab = 0;
|
int ab = 0;
|
||||||
while (ab <= 0)
|
while (ab <= 0)
|
||||||
ab = IOHelper.verifyLength(input.readVarInt(), PACKET_LENGTH);
|
ab = IOHelper.verifyLength(input.readVarInt(), PACKET_LENGTH);
|
||||||
|
|
||||||
// Read outer status response packet ID
|
// Read outer status response packet ID
|
||||||
String response;
|
String response;
|
||||||
|
@ -175,7 +178,7 @@ private Result modernPing(HInput input, HOutput output) throws IOException {
|
||||||
try (HInput packetInput = new HInput(statusPacket)) {
|
try (HInput packetInput = new HInput(statusPacket)) {
|
||||||
int statusPacketID = packetInput.readVarInt();
|
int statusPacketID = packetInput.readVarInt();
|
||||||
if (statusPacketID != 0x0)
|
if (statusPacketID != 0x0)
|
||||||
throw new IOException("Illegal status packet ID: " + statusPacketID);
|
throw new IOException("Illegal status packet ID: " + statusPacketID);
|
||||||
response = packetInput.readString(PACKET_LENGTH);
|
response = packetInput.readString(PACKET_LENGTH);
|
||||||
LogHelper.debug("Ping response (modern): '%s'", response);
|
LogHelper.debug("Ping response (modern): '%s'", response);
|
||||||
}
|
}
|
||||||
|
@ -209,9 +212,9 @@ public Result ping() throws IOException {
|
||||||
// Verify is result available
|
// Verify is result available
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
if (cacheException instanceof IOException)
|
if (cacheException instanceof IOException)
|
||||||
throw (IOException) cacheException;
|
throw (IOException) cacheException;
|
||||||
if (cacheException instanceof IllegalArgumentException)
|
if (cacheException instanceof IllegalArgumentException)
|
||||||
throw (IllegalArgumentException) cacheException;
|
throw (IllegalArgumentException) cacheException;
|
||||||
cacheException = new IOException("Unavailable");
|
cacheException = new IOException("Unavailable");
|
||||||
throw (IOException) cacheException;
|
throw (IOException) cacheException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,22 +18,22 @@
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
abstract class ProgressCircleIndicator extends ProgressIndicator {
|
abstract class ProgressCircleIndicator extends ProgressIndicator {
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public ProgressCircleIndicator() {
|
public ProgressCircleIndicator() {
|
||||||
this.getStylesheets().add(ProgressCircleIndicator.class.getResource("/runtime/launcher/overlay/update/circleprogress.css").toExternalForm());
|
this.getStylesheets().add(ProgressCircleIndicator.class.getResource("/runtime/launcher/overlay/update/circleprogress.css").toExternalForm());
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final void setInnerCircleRadius(int value) {
|
public final void setInnerCircleRadius(int value) {
|
||||||
innerCircleRadiusProperty().set(value);
|
innerCircleRadiusProperty().set(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final DoubleProperty innerCircleRadiusProperty() {
|
public final DoubleProperty innerCircleRadiusProperty() {
|
||||||
return innerCircleRadius;
|
return innerCircleRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final double getInnerCircleRadius() {
|
public final double getInnerCircleRadius() {
|
||||||
return innerCircleRadiusProperty().get();
|
return innerCircleRadiusProperty().get();
|
||||||
}
|
}
|
||||||
|
@ -68,13 +68,14 @@ public boolean isSettable(ProgressCircleIndicator n) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public StyleableProperty<Number> getStyleableProperty(ProgressCircleIndicator n) {
|
public StyleableProperty<Number> getStyleableProperty(ProgressCircleIndicator n) {
|
||||||
return (StyleableProperty<Number>) n.innerCircleRadiusProperty();
|
return (StyleableProperty<Number>) n.innerCircleRadiusProperty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
|
public static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Control.getClassCssMetaData());
|
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Control.getClassCssMetaData());
|
||||||
styleables.add(INNER_CIRCLE_RADIUS);
|
styleables.add(INNER_CIRCLE_RADIUS);
|
||||||
|
@ -83,9 +84,6 @@ public StyleableProperty<Number> getStyleableProperty(ProgressCircleIndicator n)
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
/**
|
|
||||||
* @return The CssMetaData associated with this class, which may include the CssMetaData of its super classes.
|
|
||||||
*/
|
|
||||||
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
|
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
|
||||||
return StyleableProperties.STYLEABLES;
|
return StyleableProperties.STYLEABLES;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,13 +75,14 @@ public boolean isSettable(RingProgressIndicator n) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public StyleableProperty<Number> getStyleableProperty(RingProgressIndicator n) {
|
public StyleableProperty<Number> getStyleableProperty(RingProgressIndicator n) {
|
||||||
return (StyleableProperty<Number>) n.ringWidth;
|
return (StyleableProperty<Number>) n.ringWidth;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
|
public static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Control.getClassCssMetaData());
|
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Control.getClassCssMetaData());
|
||||||
styleables.addAll(getClassCssMetaData());
|
styleables.addAll(getClassCssMetaData());
|
||||||
|
@ -93,6 +94,6 @@ public StyleableProperty<Number> getStyleableProperty(RingProgressIndicator n) {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
|
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
|
||||||
return StyleableProperties.STYLEABLES;
|
return StyleableProperties.STYLEABLES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
* Skin of the ring progress indicator where an arc grows and by the progress value up to 100% where the arc becomes a ring.
|
* Skin of the ring progress indicator where an arc grows and by the progress value up to 100% where the arc becomes a ring.
|
||||||
*
|
*
|
||||||
* @author Andrea Vacondio
|
* @author Andrea Vacondio
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public class RingProgressIndicatorSkin implements Skin<RingProgressIndicator> {
|
public class RingProgressIndicatorSkin implements Skin<RingProgressIndicator> {
|
||||||
|
@ -29,6 +28,7 @@ public class RingProgressIndicatorSkin implements Skin<RingProgressIndicator> {
|
||||||
private final StackPane container = new StackPane();
|
private final StackPane container = new StackPane();
|
||||||
private final Arc fillerArc = new Arc();
|
private final Arc fillerArc = new Arc();
|
||||||
private final RotateTransition transition = new RotateTransition(Duration.millis(2000), fillerArc);
|
private final RotateTransition transition = new RotateTransition(Duration.millis(2000), fillerArc);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public RingProgressIndicatorSkin(final RingProgressIndicator indicator) {
|
public RingProgressIndicatorSkin(final RingProgressIndicator indicator) {
|
||||||
this.indicator = indicator;
|
this.indicator = indicator;
|
||||||
|
@ -108,16 +108,19 @@ private void initIndeterminate(boolean newVal) {
|
||||||
transition.stop();
|
transition.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
public RingProgressIndicator getSkinnable() {
|
public RingProgressIndicator getSkinnable() {
|
||||||
return indicator;
|
return indicator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
public Node getNode() {
|
public Node getNode() {
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
import com.sun.javafx.collections.NonIterableChange;
|
import com.sun.javafx.collections.NonIterableChange;
|
||||||
import com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList;
|
import com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList;
|
||||||
|
|
||||||
import javafx.beans.InvalidationListener;
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
|
@ -15,222 +14,240 @@
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
import ru.gravit.launcher.LauncherAPI;
|
||||||
|
|
||||||
abstract class CheckBitSetModelBase<T> implements IndexedCheckModel<T> {
|
abstract class CheckBitSetModelBase<T> implements IndexedCheckModel<T> {
|
||||||
private final Map<T, BooleanProperty> itemBooleanMap;
|
private final Map<T, BooleanProperty> itemBooleanMap;
|
||||||
|
|
||||||
private final BitSet checkedIndices;
|
private final BitSet checkedIndices;
|
||||||
private final ReadOnlyUnbackedObservableList<Integer> checkedIndicesList;
|
private final ReadOnlyUnbackedObservableList<Integer> checkedIndicesList;
|
||||||
private final ReadOnlyUnbackedObservableList<T> checkedItemsList;
|
private final ReadOnlyUnbackedObservableList<T> checkedItemsList;
|
||||||
|
|
||||||
CheckBitSetModelBase(final Map<T, BooleanProperty> itemBooleanMap) {
|
CheckBitSetModelBase(final Map<T, BooleanProperty> itemBooleanMap) {
|
||||||
this.itemBooleanMap = itemBooleanMap;
|
this.itemBooleanMap = itemBooleanMap;
|
||||||
|
|
||||||
this.checkedIndices = new BitSet();
|
this.checkedIndices = new BitSet();
|
||||||
|
|
||||||
this.checkedIndicesList = new ReadOnlyUnbackedObservableList<Integer>() {
|
this.checkedIndicesList = new ReadOnlyUnbackedObservableList<Integer>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(Object o) {
|
public boolean contains(Object o) {
|
||||||
if (o instanceof Number) {
|
if (o instanceof Number) {
|
||||||
Number n = (Number) o;
|
Number n = (Number) o;
|
||||||
int index = n.intValue();
|
int index = n.intValue();
|
||||||
|
|
||||||
return index >= 0 && index < checkedIndices.length() && checkedIndices.get(index);
|
return index >= 0 && index < checkedIndices.length() && checkedIndices.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Integer get(int index) {
|
public Integer get(int index) {
|
||||||
if (index < 0 || index >= getItemCount())
|
if (index < 0 || index >= getItemCount())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (int pos = 0, val = checkedIndices.nextSetBit(0); val >= 0
|
for (int pos = 0, val = checkedIndices.nextSetBit(0); val >= 0
|
||||||
|| pos == index; pos++, val = checkedIndices.nextSetBit(val + 1))
|
|| pos == index; pos++, val = checkedIndices.nextSetBit(val + 1))
|
||||||
if (pos == index)
|
if (pos == index)
|
||||||
return val;
|
return val;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return checkedIndices.cardinality();
|
return checkedIndices.cardinality();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.checkedItemsList = new ReadOnlyUnbackedObservableList<T>() {
|
this.checkedItemsList = new ReadOnlyUnbackedObservableList<T>() {
|
||||||
@Override
|
@Override
|
||||||
public T get(int i) {
|
public T get(int i) {
|
||||||
int pos = checkedIndicesList.get(i);
|
int pos = checkedIndicesList.get(i);
|
||||||
if (pos < 0 || pos >= getItemCount())
|
if (pos < 0 || pos >= getItemCount())
|
||||||
return null;
|
return null;
|
||||||
return getItem(pos);
|
return getItem(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return checkedIndices.cardinality();
|
return checkedIndices.cardinality();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
final MappingChange.Map<Integer, T> map = this::getItem;
|
final MappingChange.Map<Integer, T> map = this::getItem;
|
||||||
|
|
||||||
checkedIndicesList.addListener((ListChangeListener<Integer>) c -> {
|
checkedIndicesList.addListener((ListChangeListener<Integer>) c -> {
|
||||||
boolean hasRealChangeOccurred = false;
|
boolean hasRealChangeOccurred = false;
|
||||||
while (c.next() && !hasRealChangeOccurred)
|
while (c.next() && !hasRealChangeOccurred)
|
||||||
hasRealChangeOccurred = c.wasAdded() || c.wasRemoved();
|
hasRealChangeOccurred = c.wasAdded() || c.wasRemoved();
|
||||||
|
|
||||||
if (hasRealChangeOccurred) {
|
if (hasRealChangeOccurred) {
|
||||||
c.reset();
|
c.reset();
|
||||||
checkedItemsList.callObservers(new MappingChange<>(c, map, checkedItemsList));
|
checkedItemsList.callObservers(new MappingChange<>(c, map, checkedItemsList));
|
||||||
}
|
}
|
||||||
c.reset();
|
c.reset();
|
||||||
});
|
});
|
||||||
getCheckedItems().addListener((ListChangeListener<T>) c -> {
|
getCheckedItems().addListener((ListChangeListener<T>) c -> {
|
||||||
while (c.next()) {
|
while (c.next()) {
|
||||||
if (c.wasAdded())
|
if (c.wasAdded())
|
||||||
for (T item : c.getAddedSubList()) {
|
for (T item : c.getAddedSubList()) {
|
||||||
BooleanProperty p = getItemBooleanProperty(item);
|
BooleanProperty p = getItemBooleanProperty(item);
|
||||||
if (p != null)
|
if (p != null)
|
||||||
p.set(true);
|
p.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.wasRemoved())
|
if (c.wasRemoved())
|
||||||
for (T item : c.getRemoved()) {
|
for (T item : c.getRemoved()) {
|
||||||
BooleanProperty p = getItemBooleanProperty(item);
|
BooleanProperty p = getItemBooleanProperty(item);
|
||||||
if (p != null)
|
if (p != null)
|
||||||
p.set(false);
|
p.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public void check(int index) {
|
|
||||||
if (index < 0 || index >= getItemCount())
|
|
||||||
return;
|
|
||||||
checkedIndices.set(index);
|
|
||||||
final int changeIndex = checkedIndicesList.indexOf(index);
|
|
||||||
checkedIndicesList.callObservers(
|
|
||||||
new NonIterableChange.SimpleAddChange<>(changeIndex, changeIndex + 1, checkedIndicesList));
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void check(T item) {
|
|
||||||
int index = getItemIndex(item);
|
|
||||||
check(index);
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public void checkAll() {
|
|
||||||
for (int i = 0; i < getItemCount(); i++)
|
|
||||||
check(i);
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public void checkIndices(int... indices) {
|
|
||||||
for (int indice : indices)
|
|
||||||
check(indice);
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public void clearCheck(int index) {
|
|
||||||
if (index < 0 || index >= getItemCount())
|
|
||||||
return;
|
|
||||||
checkedIndices.clear(index);
|
|
||||||
|
|
||||||
final int changeIndex = checkedIndicesList.indexOf(index);
|
@LauncherAPI
|
||||||
checkedIndicesList.callObservers(
|
@Override
|
||||||
new NonIterableChange.SimpleRemovedChange<>(changeIndex, changeIndex, index, checkedIndicesList));
|
public void check(int index) {
|
||||||
}
|
if (index < 0 || index >= getItemCount())
|
||||||
@LauncherAPI
|
return;
|
||||||
@Override
|
checkedIndices.set(index);
|
||||||
public void clearCheck(T item) {
|
final int changeIndex = checkedIndicesList.indexOf(index);
|
||||||
int index = getItemIndex(item);
|
checkedIndicesList.callObservers(
|
||||||
clearCheck(index);
|
new NonIterableChange.SimpleAddChange<>(changeIndex, changeIndex + 1, checkedIndicesList));
|
||||||
}
|
}
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public void clearChecks() {
|
|
||||||
for (int index = 0; index < checkedIndices.length(); index++)
|
|
||||||
clearCheck(index);
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public ObservableList<Integer> getCheckedIndices() {
|
|
||||||
return checkedIndicesList;
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public ObservableList<T> getCheckedItems() {
|
|
||||||
return checkedItemsList;
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public abstract T getItem(int index);
|
|
||||||
@LauncherAPI
|
|
||||||
BooleanProperty getItemBooleanProperty(T item) {
|
|
||||||
return itemBooleanMap.get(item);
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public abstract int getItemCount();
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public abstract int getItemIndex(T item);
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public boolean isChecked(int index) {
|
|
||||||
return checkedIndices.get(index);
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public boolean isChecked(T item) {
|
|
||||||
int index = getItemIndex(item);
|
|
||||||
return isChecked(index);
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return checkedIndices.isEmpty();
|
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public void toggleCheckState(int index) {
|
|
||||||
if (isChecked(index))
|
|
||||||
clearCheck(index);
|
|
||||||
else
|
|
||||||
check(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
@Override
|
||||||
@Override
|
public void check(T item) {
|
||||||
public void toggleCheckState(T item) {
|
int index = getItemIndex(item);
|
||||||
int index = getItemIndex(item);
|
check(index);
|
||||||
toggleCheckState(index);
|
}
|
||||||
}
|
|
||||||
@LauncherAPI
|
|
||||||
protected void updateMap() {
|
|
||||||
itemBooleanMap.clear();
|
|
||||||
for (int i = 0; i < getItemCount(); i++) {
|
|
||||||
final int index = i;
|
|
||||||
final T item = getItem(index);
|
|
||||||
|
|
||||||
final BooleanProperty booleanProperty = new SimpleBooleanProperty(item, "selected", false); //$NON-NLS-1$
|
@LauncherAPI
|
||||||
itemBooleanMap.put(item, booleanProperty);
|
@Override
|
||||||
|
public void checkAll() {
|
||||||
|
for (int i = 0; i < getItemCount(); i++)
|
||||||
|
check(i);
|
||||||
|
}
|
||||||
|
|
||||||
booleanProperty.addListener(o -> {
|
@LauncherAPI
|
||||||
if (booleanProperty.get()) {
|
@Override
|
||||||
checkedIndices.set(index);
|
public void checkIndices(int... indices) {
|
||||||
final int changeIndex1 = checkedIndicesList.indexOf(index);
|
for (int indice : indices)
|
||||||
checkedIndicesList.callObservers(new NonIterableChange.SimpleAddChange<>(changeIndex1,
|
check(indice);
|
||||||
changeIndex1 + 1, checkedIndicesList));
|
}
|
||||||
} else {
|
|
||||||
final int changeIndex2 = checkedIndicesList.indexOf(index);
|
@LauncherAPI
|
||||||
checkedIndices.clear(index);
|
@Override
|
||||||
checkedIndicesList.callObservers(new NonIterableChange.SimpleRemovedChange<>(changeIndex2,
|
public void clearCheck(int index) {
|
||||||
changeIndex2, index, checkedIndicesList));
|
if (index < 0 || index >= getItemCount())
|
||||||
}
|
return;
|
||||||
});
|
checkedIndices.clear(index);
|
||||||
}
|
|
||||||
}
|
final int changeIndex = checkedIndicesList.indexOf(index);
|
||||||
|
checkedIndicesList.callObservers(
|
||||||
|
new NonIterableChange.SimpleRemovedChange<>(changeIndex, changeIndex, index, checkedIndicesList));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public void clearCheck(T item) {
|
||||||
|
int index = getItemIndex(item);
|
||||||
|
clearCheck(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public void clearChecks() {
|
||||||
|
for (int index = 0; index < checkedIndices.length(); index++)
|
||||||
|
clearCheck(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public ObservableList<Integer> getCheckedIndices() {
|
||||||
|
return checkedIndicesList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public ObservableList<T> getCheckedItems() {
|
||||||
|
return checkedItemsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public abstract T getItem(int index);
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
BooleanProperty getItemBooleanProperty(T item) {
|
||||||
|
return itemBooleanMap.get(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public abstract int getItemCount();
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public abstract int getItemIndex(T item);
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public boolean isChecked(int index) {
|
||||||
|
return checkedIndices.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public boolean isChecked(T item) {
|
||||||
|
int index = getItemIndex(item);
|
||||||
|
return isChecked(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return checkedIndices.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public void toggleCheckState(int index) {
|
||||||
|
if (isChecked(index))
|
||||||
|
clearCheck(index);
|
||||||
|
else
|
||||||
|
check(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
@Override
|
||||||
|
public void toggleCheckState(T item) {
|
||||||
|
int index = getItemIndex(item);
|
||||||
|
toggleCheckState(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
protected void updateMap() {
|
||||||
|
itemBooleanMap.clear();
|
||||||
|
for (int i = 0; i < getItemCount(); i++) {
|
||||||
|
final int index = i;
|
||||||
|
final T item = getItem(index);
|
||||||
|
|
||||||
|
final BooleanProperty booleanProperty = new SimpleBooleanProperty(item, "selected", false); //$NON-NLS-1$
|
||||||
|
itemBooleanMap.put(item, booleanProperty);
|
||||||
|
|
||||||
|
booleanProperty.addListener(o -> {
|
||||||
|
if (booleanProperty.get()) {
|
||||||
|
checkedIndices.set(index);
|
||||||
|
final int changeIndex1 = checkedIndicesList.indexOf(index);
|
||||||
|
checkedIndicesList.callObservers(new NonIterableChange.SimpleAddChange<>(changeIndex1,
|
||||||
|
changeIndex1 + 1, checkedIndicesList));
|
||||||
|
} else {
|
||||||
|
final int changeIndex2 = checkedIndicesList.indexOf(index);
|
||||||
|
checkedIndices.clear(index);
|
||||||
|
checkedIndicesList.callObservers(new NonIterableChange.SimpleRemovedChange<>(changeIndex2,
|
||||||
|
changeIndex2, index, checkedIndicesList));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,143 +17,144 @@
|
||||||
import ru.gravit.launcher.gui.choosebox.impl.CheckComboBoxSkin;
|
import ru.gravit.launcher.gui.choosebox.impl.CheckComboBoxSkin;
|
||||||
|
|
||||||
public class CheckComboBox<T> extends ControlsFXControl {
|
public class CheckComboBox<T> extends ControlsFXControl {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
private static class CheckComboBoxBitSetCheckModel<T> extends CheckBitSetModelBase<T> {
|
private static class CheckComboBoxBitSetCheckModel<T> extends CheckBitSetModelBase<T> {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
private final ObservableList<T> items;
|
private final ObservableList<T> items;
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
CheckComboBoxBitSetCheckModel(final ObservableList<T> items, final Map<T, BooleanProperty> itemBooleanMap) {
|
CheckComboBoxBitSetCheckModel(final ObservableList<T> items, final Map<T, BooleanProperty> itemBooleanMap) {
|
||||||
super(itemBooleanMap);
|
super(itemBooleanMap);
|
||||||
|
|
||||||
this.items = items;
|
this.items = items;
|
||||||
this.items.addListener((ListChangeListener<T>) c -> updateMap());
|
this.items.addListener((ListChangeListener<T>) c -> updateMap());
|
||||||
|
|
||||||
updateMap();
|
updateMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
public T getItem(int index) {
|
public T getItem(int index) {
|
||||||
return items.get(index);
|
return items.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return items.size();
|
return items.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
public int getItemIndex(T item) {
|
public int getItemIndex(T item) {
|
||||||
return items.indexOf(item);
|
return items.indexOf(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private final ObservableList<T> items;
|
|
||||||
private final Map<T, BooleanProperty> itemBooleanMap;
|
|
||||||
|
|
||||||
private CheckComboBoxSkin<T> checkComboBoxSkin;
|
private final ObservableList<T> items;
|
||||||
|
private final Map<T, BooleanProperty> itemBooleanMap;
|
||||||
|
|
||||||
@LauncherAPI
|
private CheckComboBoxSkin<T> checkComboBoxSkin;
|
||||||
private ObjectProperty<IndexedCheckModel<T>> checkModel = new SimpleObjectProperty<>(this, "checkModel");
|
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
private ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>(this,
|
private ObjectProperty<IndexedCheckModel<T>> checkModel = new SimpleObjectProperty<>(this, "checkModel");
|
||||||
"converter");
|
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
private StringProperty title = new SimpleStringProperty(null);
|
private ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>(this,
|
||||||
|
"converter");
|
||||||
|
|
||||||
public CheckComboBox() {
|
@LauncherAPI
|
||||||
this(null);
|
private StringProperty title = new SimpleStringProperty(null);
|
||||||
}
|
|
||||||
|
|
||||||
public CheckComboBox(final ObservableList<T> items) {
|
public CheckComboBox() {
|
||||||
final int initialSize = items == null ? 32 : items.size();
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
this.itemBooleanMap = new HashMap<>(initialSize);
|
public CheckComboBox(final ObservableList<T> items) {
|
||||||
this.items = items == null ? FXCollections.observableArrayList() : items;
|
final int initialSize = items == null ? 32 : items.size();
|
||||||
setCheckModel(new CheckComboBoxBitSetCheckModel<>(this.items, itemBooleanMap));
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
this.itemBooleanMap = new HashMap<>(initialSize);
|
||||||
public final ObjectProperty<IndexedCheckModel<T>> checkModelProperty() {
|
this.items = items == null ? FXCollections.observableArrayList() : items;
|
||||||
return checkModel;
|
setCheckModel(new CheckComboBoxBitSetCheckModel<>(this.items, itemBooleanMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final ObjectProperty<StringConverter<T>> converterProperty() {
|
public final ObjectProperty<IndexedCheckModel<T>> checkModelProperty() {
|
||||||
return converter;
|
return checkModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@LauncherAPI
|
||||||
protected Skin<?> createDefaultSkin() {
|
public final ObjectProperty<StringConverter<T>> converterProperty() {
|
||||||
checkComboBoxSkin = new CheckComboBoxSkin<>(this);
|
return converter;
|
||||||
return checkComboBoxSkin;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
@Override
|
||||||
public final IndexedCheckModel<T> getCheckModel() {
|
protected Skin<?> createDefaultSkin() {
|
||||||
return checkModel == null ? null : checkModel.get();
|
checkComboBoxSkin = new CheckComboBoxSkin<>(this);
|
||||||
}
|
return checkComboBoxSkin;
|
||||||
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final StringConverter<T> getConverter() {
|
public final IndexedCheckModel<T> getCheckModel() {
|
||||||
return converterProperty().get();
|
return checkModel == null ? null : checkModel.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public BooleanProperty getItemBooleanProperty(int index) {
|
public final StringConverter<T> getConverter() {
|
||||||
if (index < 0 || index >= items.size())
|
return converterProperty().get();
|
||||||
return null;
|
}
|
||||||
return getItemBooleanProperty(getItems().get(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public BooleanProperty getItemBooleanProperty(T item) {
|
public BooleanProperty getItemBooleanProperty(int index) {
|
||||||
return itemBooleanMap.get(item);
|
if (index < 0 || index >= items.size())
|
||||||
}
|
return null;
|
||||||
|
return getItemBooleanProperty(getItems().get(index));
|
||||||
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public ObservableList<T> getItems() {
|
public BooleanProperty getItemBooleanProperty(T item) {
|
||||||
return items;
|
return itemBooleanMap.get(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final String getTitle() {
|
public ObservableList<T> getItems() {
|
||||||
return title.getValue();
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public void hide() {
|
public final String getTitle() {
|
||||||
if (checkComboBoxSkin != null)
|
return title.getValue();
|
||||||
checkComboBoxSkin.hide();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final void setCheckModel(IndexedCheckModel<T> value) {
|
public void hide() {
|
||||||
checkModelProperty().set(value);
|
if (checkComboBoxSkin != null)
|
||||||
}
|
checkComboBoxSkin.hide();
|
||||||
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final void setConverter(StringConverter<T> value) {
|
public final void setCheckModel(IndexedCheckModel<T> value) {
|
||||||
converterProperty().set(value);
|
checkModelProperty().set(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final void setTitle(String value) {
|
public final void setConverter(StringConverter<T> value) {
|
||||||
title.setValue(value);
|
converterProperty().set(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public void show() {
|
public final void setTitle(String value) {
|
||||||
if (checkComboBoxSkin != null)
|
title.setValue(value);
|
||||||
checkComboBoxSkin.show();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final StringProperty titleProperty() {
|
public void show() {
|
||||||
return title;
|
if (checkComboBoxSkin != null)
|
||||||
}
|
checkComboBoxSkin.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@LauncherAPI
|
||||||
|
public final StringProperty titleProperty() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,30 +4,30 @@
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
import ru.gravit.launcher.LauncherAPI;
|
||||||
|
|
||||||
public interface CheckModel<T> {
|
public interface CheckModel<T> {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
void check(T item);
|
void check(T item);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
void checkAll();
|
void checkAll();
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
void clearCheck(T item);
|
void clearCheck(T item);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
void clearChecks();
|
void clearChecks();
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
ObservableList<T> getCheckedItems();
|
ObservableList<T> getCheckedItems();
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
int getItemCount();
|
int getItemCount();
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
boolean isChecked(T item);
|
boolean isChecked(T item);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
boolean isEmpty();
|
boolean isEmpty();
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
void toggleCheckState(T item);
|
void toggleCheckState(T item);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,18 @@
|
||||||
|
|
||||||
abstract class ControlsFXControl extends Control {
|
abstract class ControlsFXControl extends Control {
|
||||||
|
|
||||||
private String stylesheet;
|
private String stylesheet;
|
||||||
|
|
||||||
public ControlsFXControl() {
|
public ControlsFXControl() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
protected final String getUserAgentStylesheet(Class<?> clazz, String fileName) {
|
protected final String getUserAgentStylesheet(Class<?> clazz, String fileName) {
|
||||||
|
|
||||||
if (stylesheet == null)
|
if (stylesheet == null)
|
||||||
stylesheet = clazz.getResource(fileName).toExternalForm();
|
stylesheet = clazz.getResource(fileName).toExternalForm();
|
||||||
|
|
||||||
return stylesheet;
|
return stylesheet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,28 +4,28 @@
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
import ru.gravit.launcher.LauncherAPI;
|
||||||
|
|
||||||
public interface IndexedCheckModel<T> extends CheckModel<T> {
|
public interface IndexedCheckModel<T> extends CheckModel<T> {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
void check(int index);
|
void check(int index);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
void checkIndices(int... indices);
|
void checkIndices(int... indices);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
void clearCheck(int index);
|
void clearCheck(int index);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
ObservableList<Integer> getCheckedIndices();
|
ObservableList<Integer> getCheckedIndices();
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
T getItem(int index);
|
T getItem(int index);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
int getItemIndex(T item);
|
int getItemIndex(T item);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
boolean isChecked(int index);
|
boolean isChecked(int index);
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
void toggleCheckState(int index);
|
void toggleCheckState(int index);
|
||||||
|
|
||||||
}
|
}
|
|
@ -21,160 +21,157 @@
|
||||||
|
|
||||||
public class CheckComboBoxSkin<T> extends BehaviorSkinBase<CheckComboBox<T>, BehaviorBase<CheckComboBox<T>>> {
|
public class CheckComboBoxSkin<T> extends BehaviorSkinBase<CheckComboBox<T>, BehaviorBase<CheckComboBox<T>>> {
|
||||||
|
|
||||||
private final ComboBox<T> comboBox;
|
private final ComboBox<T> comboBox;
|
||||||
private final ListCell<T> buttonCell;
|
private final ListCell<T> buttonCell;
|
||||||
|
|
||||||
private final CheckComboBox<T> control;
|
private final CheckComboBox<T> control;
|
||||||
private final ObservableList<T> items;
|
private final ReadOnlyUnbackedObservableList<T> selectedItems;
|
||||||
private final ReadOnlyUnbackedObservableList<Integer> selectedIndices;
|
|
||||||
private final ReadOnlyUnbackedObservableList<T> selectedItems;
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public CheckComboBoxSkin(final CheckComboBox<T> control) {
|
public CheckComboBoxSkin(final CheckComboBox<T> control) {
|
||||||
super(control, new BehaviorBase<>(control, Collections.emptyList()));
|
super(control, new BehaviorBase<>(control, Collections.emptyList()));
|
||||||
|
|
||||||
this.control = control;
|
this.control = control;
|
||||||
this.items = control.getItems();
|
ObservableList<T> items = control.getItems();
|
||||||
|
|
||||||
selectedIndices = (ReadOnlyUnbackedObservableList<Integer>) control.getCheckModel().getCheckedIndices();
|
ReadOnlyUnbackedObservableList<Integer> selectedIndices = (ReadOnlyUnbackedObservableList<Integer>) control.getCheckModel().getCheckedIndices();
|
||||||
selectedItems = (ReadOnlyUnbackedObservableList<T>) control.getCheckModel().getCheckedItems();
|
selectedItems = (ReadOnlyUnbackedObservableList<T>) control.getCheckModel().getCheckedItems();
|
||||||
|
|
||||||
comboBox = new ComboBox<T>(items) {
|
comboBox = new ComboBox<T>(items) {
|
||||||
@Override
|
@Override
|
||||||
protected javafx.scene.control.Skin<?> createDefaultSkin() {
|
protected javafx.scene.control.Skin<?> createDefaultSkin() {
|
||||||
return createComboBoxListViewSkin(this);
|
return createComboBoxListViewSkin(this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
comboBox.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
comboBox.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
|
||||||
|
|
||||||
comboBox.setCellFactory(listView -> {
|
comboBox.setCellFactory(listView -> {
|
||||||
CheckBoxListCell<T> result = new CheckBoxListCell<>(control::getItemBooleanProperty);
|
CheckBoxListCell<T> result = new CheckBoxListCell<>(control::getItemBooleanProperty);
|
||||||
result.focusedProperty().addListener((o, ov, nv) -> {
|
result.focusedProperty().addListener((o, ov, nv) -> {
|
||||||
if (nv)
|
if (nv)
|
||||||
result.getParent().requestFocus();
|
result.getParent().requestFocus();
|
||||||
});
|
});
|
||||||
result.setOnMouseClicked(e -> {
|
result.setOnMouseClicked(e -> {
|
||||||
T item = result.getItem();
|
T item = result.getItem();
|
||||||
if (control.getCheckModel().isChecked(item))
|
if (control.getCheckModel().isChecked(item))
|
||||||
control.getCheckModel().clearCheck(item);
|
control.getCheckModel().clearCheck(item);
|
||||||
else
|
else
|
||||||
control.getCheckModel().check(item);
|
control.getCheckModel().check(item);
|
||||||
});
|
});
|
||||||
result.converterProperty().bind(control.converterProperty());
|
result.converterProperty().bind(control.converterProperty());
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonCell = new ListCell<T>() {
|
buttonCell = new ListCell<T>() {
|
||||||
@Override
|
@Override
|
||||||
protected void updateItem(T item, boolean empty) {
|
protected void updateItem(T item, boolean empty) {
|
||||||
setText(getTextString());
|
setText(getTextString());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
comboBox.setButtonCell(buttonCell);
|
comboBox.setButtonCell(buttonCell);
|
||||||
comboBox.setValue((T) getTextString());
|
comboBox.setValue((T) getTextString());
|
||||||
|
|
||||||
selectedIndices.addListener((ListChangeListener<Integer>) c -> buttonCell.updateIndex(0));
|
selectedIndices.addListener((ListChangeListener<Integer>) c -> buttonCell.updateIndex(0));
|
||||||
|
|
||||||
getChildren().add(comboBox);
|
getChildren().add(comboBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
private String buildString() {
|
private String buildString() {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
for (int i = 0, max = selectedItems.size(); i < max; i++) {
|
for (int i = 0, max = selectedItems.size(); i < max; i++) {
|
||||||
T item = selectedItems.get(i);
|
T item = selectedItems.get(i);
|
||||||
if (control.getConverter() == null)
|
if (control.getConverter() == null)
|
||||||
sb.append(item);
|
sb.append(item);
|
||||||
else
|
else
|
||||||
sb.append(control.getConverter().toString(item));
|
sb.append(control.getConverter().toString(item));
|
||||||
if (i < max - 1)
|
if (i < max - 1)
|
||||||
sb.append(", "); //$NON-NLS-1$
|
sb.append(", "); //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset,
|
protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset,
|
||||||
double leftInset) {
|
double leftInset) {
|
||||||
return getSkinnable().prefHeight(width);
|
return getSkinnable().prefHeight(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset,
|
protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset,
|
||||||
double leftInset) {
|
double leftInset) {
|
||||||
return getSkinnable().prefWidth(height);
|
return getSkinnable().prefWidth(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset,
|
protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset,
|
||||||
double leftInset) {
|
double leftInset) {
|
||||||
return comboBox.minHeight(width);
|
return comboBox.minHeight(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset,
|
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset,
|
||||||
double leftInset) {
|
double leftInset) {
|
||||||
return comboBox.minWidth(height);
|
return comboBox.minWidth(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset,
|
protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset,
|
||||||
double leftInset) {
|
double leftInset) {
|
||||||
return comboBox.prefHeight(width);
|
return comboBox.prefHeight(width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@Override
|
@Override
|
||||||
protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset,
|
protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset,
|
||||||
double leftInset) {
|
double leftInset) {
|
||||||
return comboBox.prefWidth(height);
|
return comboBox.prefWidth(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
private Skin<?> createComboBoxListViewSkin(ComboBox<T> comboBox) {
|
private Skin<?> createComboBoxListViewSkin(ComboBox<T> comboBox) {
|
||||||
final ComboBoxListViewSkin<T> comboBoxListViewSkin = new ComboBoxListViewSkin<T>(comboBox) {
|
final ComboBoxListViewSkin<T> comboBoxListViewSkin = new ComboBoxListViewSkin<T>(comboBox) {
|
||||||
@Override
|
@Override
|
||||||
protected boolean isHideOnClickEnabled() {
|
protected boolean isHideOnClickEnabled() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked") final ListView<T> listView = (ListView<T>) comboBoxListViewSkin.getPopupContent();
|
||||||
final ListView<T> listView = (ListView<T>) comboBoxListViewSkin.getPopupContent();
|
listView.setOnKeyPressed(e -> {
|
||||||
listView.setOnKeyPressed(e -> {
|
if (e.getCode() == KeyCode.SPACE) {
|
||||||
if (e.getCode() == KeyCode.SPACE) {
|
T item = listView.getSelectionModel().getSelectedItem();
|
||||||
T item = listView.getSelectionModel().getSelectedItem();
|
if (item != null) {
|
||||||
if (item != null) {
|
final IndexedCheckModel<T> checkModel = control.getCheckModel();
|
||||||
final IndexedCheckModel<T> checkModel = control.getCheckModel();
|
if (checkModel != null)
|
||||||
if (checkModel != null)
|
checkModel.toggleCheckState(item);
|
||||||
checkModel.toggleCheckState(item);
|
}
|
||||||
}
|
} else if (e.getCode() == KeyCode.ESCAPE)
|
||||||
} else if (e.getCode() == KeyCode.ESCAPE)
|
hide();
|
||||||
hide();
|
});
|
||||||
});
|
return comboBoxListViewSkin;
|
||||||
return comboBoxListViewSkin;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
protected String getTextString() {
|
protected String getTextString() {
|
||||||
|
|
||||||
if (control.getTitle() != null)
|
if (control.getTitle() != null)
|
||||||
return control.getTitle();
|
return control.getTitle();
|
||||||
return buildString();
|
return buildString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public void hide() {
|
public void hide() {
|
||||||
comboBox.hide();
|
comboBox.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public void show() {
|
public void show() {
|
||||||
comboBox.show();
|
comboBox.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public Integer getType() {
|
||||||
protected Void requestDo(HInput input, HOutput output) throws IOException {
|
protected Void requestDo(HInput input, HOutput output) throws IOException {
|
||||||
byte pong = (byte) input.readUnsignedByte();
|
byte pong = (byte) input.readUnsignedByte();
|
||||||
if (pong != SerializeLimits.EXPECTED_BYTE)
|
if (pong != SerializeLimits.EXPECTED_BYTE)
|
||||||
throw new IOException("Illegal ping response: " + pong);
|
throw new IOException("Illegal ping response: " + pong);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,12 @@
|
||||||
|
|
||||||
public abstract class Request<R> {
|
public abstract class Request<R> {
|
||||||
private static final long session = SecurityHelper.secureRandom.nextLong();
|
private static final long session = SecurityHelper.secureRandom.nextLong();
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public static void requestError(String message) throws RequestException {
|
public static void requestError(String message) throws RequestException {
|
||||||
throw new RequestException(message);
|
throw new RequestException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
protected final LauncherConfig config;
|
protected final LauncherConfig config;
|
||||||
|
|
||||||
|
@ -40,14 +42,14 @@ protected Request(LauncherConfig config) {
|
||||||
protected final void readError(HInput input) throws IOException {
|
protected final void readError(HInput input) throws IOException {
|
||||||
String error = input.readString(0);
|
String error = input.readString(0);
|
||||||
if (!error.isEmpty())
|
if (!error.isEmpty())
|
||||||
requestError(error);
|
requestError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
@SuppressWarnings("DesignForExtension")
|
@SuppressWarnings("DesignForExtension")
|
||||||
public R request() throws Exception {
|
public R request() throws Exception {
|
||||||
if (!started.compareAndSet(false, true))
|
if (!started.compareAndSet(false, true))
|
||||||
throw new IllegalStateException("Request already started");
|
throw new IllegalStateException("Request already started");
|
||||||
|
|
||||||
// Make request to LaunchServer
|
// Make request to LaunchServer
|
||||||
try (Socket socket = IOHelper.newSocket()) {
|
try (Socket socket = IOHelper.newSocket()) {
|
||||||
|
@ -73,7 +75,7 @@ private void writeHandshake(HInput input, HOutput output) throws IOException {
|
||||||
|
|
||||||
// Verify is accepted
|
// Verify is accepted
|
||||||
if (!input.readBoolean())
|
if (!input.readBoolean())
|
||||||
requestError("Serverside not accepted this connection");
|
requestError("Serverside not accepted this connection");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ private Result(PlayerProfile pp, String accessToken) {
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String login;
|
private final String login;
|
||||||
|
|
||||||
private final byte[] encryptedPassword;
|
private final byte[] encryptedPassword;
|
||||||
|
|
|
@ -46,6 +46,7 @@ public byte[] getSign() {
|
||||||
return sign.clone();
|
return sign.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class);
|
public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class);
|
||||||
|
|
||||||
|
@ -60,9 +61,9 @@ public static void update(LauncherConfig config, Result result) throws Signature
|
||||||
List<String> args = new ArrayList<>(8);
|
List<String> args = new ArrayList<>(8);
|
||||||
args.add(IOHelper.resolveJavaBin(null).toString());
|
args.add(IOHelper.resolveJavaBin(null).toString());
|
||||||
if (LogHelper.isDebugEnabled())
|
if (LogHelper.isDebugEnabled())
|
||||||
args.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, Boolean.toString(LogHelper.isDebugEnabled())));
|
args.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, Boolean.toString(LogHelper.isDebugEnabled())));
|
||||||
if (LauncherConfig.ADDRESS_OVERRIDE != null)
|
if (LauncherConfig.ADDRESS_OVERRIDE != null)
|
||||||
args.add(JVMHelper.jvmProperty(LauncherConfig.ADDRESS_OVERRIDE_PROPERTY, LauncherConfig.ADDRESS_OVERRIDE));
|
args.add(JVMHelper.jvmProperty(LauncherConfig.ADDRESS_OVERRIDE_PROPERTY, LauncherConfig.ADDRESS_OVERRIDE));
|
||||||
args.add("-jar");
|
args.add("-jar");
|
||||||
args.add(BINARY_PATH.toString());
|
args.add(BINARY_PATH.toString());
|
||||||
ProcessBuilder builder = new ProcessBuilder(args.toArray(new String[0]));
|
ProcessBuilder builder = new ProcessBuilder(args.toArray(new String[0]));
|
||||||
|
@ -117,7 +118,7 @@ protected Result requestDo(HInput input, HOutput output) throws Exception {
|
||||||
int count = input.readLength(0);
|
int count = input.readLength(0);
|
||||||
List<SignedObjectHolder<ClientProfile>> profiles = new ArrayList<>(count);
|
List<SignedObjectHolder<ClientProfile>> profiles = new ArrayList<>(count);
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
profiles.add(new SignedObjectHolder<>(input, publicKey, ClientProfile.RO_ADAPTER));
|
profiles.add(new SignedObjectHolder<>(input, publicKey, ClientProfile.RO_ADAPTER));
|
||||||
|
|
||||||
// Return request result
|
// Return request result
|
||||||
return new Result(null, sign, profiles);
|
return new Result(null, sign, profiles);
|
||||||
|
|
|
@ -38,6 +38,7 @@ public ProfilesRequest(LauncherConfig config) {
|
||||||
public Integer getType() {
|
public Integer getType() {
|
||||||
return RequestType.PROFILES.getNumber();
|
return RequestType.PROFILES.getNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Result requestDo(HInput input, HOutput output) throws Exception {
|
protected Result requestDo(HInput input, HOutput output) throws Exception {
|
||||||
output.writeBoolean(true);
|
output.writeBoolean(true);
|
||||||
|
@ -47,7 +48,7 @@ protected Result requestDo(HInput input, HOutput output) throws Exception {
|
||||||
int count = input.readLength(0);
|
int count = input.readLength(0);
|
||||||
List<SignedObjectHolder<ClientProfile>> profiles = new ArrayList<>(count);
|
List<SignedObjectHolder<ClientProfile>> profiles = new ArrayList<>(count);
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
profiles.add(new SignedObjectHolder<>(input, config.publicKey, ClientProfile.RO_ADAPTER));
|
profiles.add(new SignedObjectHolder<>(input, config.publicKey, ClientProfile.RO_ADAPTER));
|
||||||
|
|
||||||
// Return request result
|
// Return request result
|
||||||
return new Result(profiles);
|
return new Result(profiles);
|
||||||
|
|
|
@ -36,7 +36,7 @@ protected Set<String> requestDo(HInput input, HOutput output) throws IOException
|
||||||
// Read all update dirs names
|
// Read all update dirs names
|
||||||
Set<String> result = new HashSet<>(count);
|
Set<String> result = new HashSet<>(count);
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
result.add(IOHelper.verifyFileName(input.readString(255)));
|
result.add(IOHelper.verifyFileName(input.readString(255)));
|
||||||
|
|
||||||
// We're done. Make it unmodifiable and return
|
// We're done. Make it unmodifiable and return
|
||||||
return Collections.unmodifiableSet(result);
|
return Collections.unmodifiableSet(result);
|
||||||
|
|
|
@ -41,6 +41,7 @@ public static final class State {
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
void call(State state);
|
void call(State state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public final long fileDownloaded;
|
public final long fileDownloaded;
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
|
@ -70,7 +71,7 @@ public State(String filePath, long fileDownloaded, long fileSize, long totalDown
|
||||||
public double getBps() {
|
public double getBps() {
|
||||||
long seconds = duration.getSeconds();
|
long seconds = duration.getSeconds();
|
||||||
if (seconds == 0)
|
if (seconds == 0)
|
||||||
return -1.0D; // Otherwise will throw /0 exception
|
return -1.0D; // Otherwise will throw /0 exception
|
||||||
return totalDownloaded / (double) seconds;
|
return totalDownloaded / (double) seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ public double getBps() {
|
||||||
public Duration getEstimatedTime() {
|
public Duration getEstimatedTime() {
|
||||||
double bps = getBps();
|
double bps = getBps();
|
||||||
if (bps <= 0.0D)
|
if (bps <= 0.0D)
|
||||||
return null; // Otherwise will throw /0 exception
|
return null; // Otherwise will throw /0 exception
|
||||||
return Duration.ofSeconds((long) (getTotalRemaining() / bps));
|
return Duration.ofSeconds((long) (getTotalRemaining() / bps));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +96,7 @@ public double getFileDownloadedMiB() {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public double getFileDownloadedPart() {
|
public double getFileDownloadedPart() {
|
||||||
if (fileSize == 0)
|
if (fileSize == 0)
|
||||||
return 0.0D;
|
return 0.0D;
|
||||||
return (double) fileDownloaded / fileSize;
|
return (double) fileDownloaded / fileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +138,7 @@ public double getTotalDownloadedMiB() {
|
||||||
@LauncherAPI
|
@LauncherAPI
|
||||||
public double getTotalDownloadedPart() {
|
public double getTotalDownloadedPart() {
|
||||||
if (totalSize == 0)
|
if (totalSize == 0)
|
||||||
return 0.0D;
|
return 0.0D;
|
||||||
return (double) totalDownloaded / totalSize;
|
return (double) totalDownloaded / totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +167,7 @@ public double getTotalSizeMiB() {
|
||||||
return getTotalSizeKiB() / 1024.0D;
|
return getTotalSizeKiB() / 1024.0D;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void fillActionsQueue(Queue<UpdateAction> queue, HashedDir mismatch) {
|
private static void fillActionsQueue(Queue<UpdateAction> queue, HashedDir mismatch) {
|
||||||
for (Entry<String, HashedEntry> mapEntry : mismatch.map().entrySet()) {
|
for (Entry<String, HashedEntry> mapEntry : mismatch.map().entrySet()) {
|
||||||
String name = mapEntry.getKey();
|
String name = mapEntry.getKey();
|
||||||
|
@ -185,6 +187,7 @@ private static void fillActionsQueue(Queue<UpdateAction> queue, HashedDir mismat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance
|
// Instance
|
||||||
private final String dirName;
|
private final String dirName;
|
||||||
private final Path dir;
|
private final Path dir;
|
||||||
|
@ -257,12 +260,12 @@ private void downloadFile(Path file, HashedFile hFile, InputStream input) throws
|
||||||
int remaining = (int) Math.min(hFile.size - downloaded, bytes.length);
|
int remaining = (int) Math.min(hFile.size - downloaded, bytes.length);
|
||||||
int length = input.read(bytes, 0, remaining);
|
int length = input.read(bytes, 0, remaining);
|
||||||
if (length < 0)
|
if (length < 0)
|
||||||
throw new EOFException(String.format("%d bytes remaining", hFile.size - downloaded));
|
throw new EOFException(String.format("%d bytes remaining", hFile.size - downloaded));
|
||||||
|
|
||||||
// Update file
|
// Update file
|
||||||
fileOutput.write(bytes, 0, length);
|
fileOutput.write(bytes, 0, length);
|
||||||
if (digest != null)
|
if (digest != null)
|
||||||
digest.update(bytes, 0, length);
|
digest.update(bytes, 0, length);
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
downloaded += length;
|
downloaded += length;
|
||||||
|
@ -275,7 +278,7 @@ private void downloadFile(Path file, HashedFile hFile, InputStream input) throws
|
||||||
if (digest != null) {
|
if (digest != null) {
|
||||||
byte[] digestBytes = digest.digest();
|
byte[] digestBytes = digest.digest();
|
||||||
if (!hFile.isSameDigest(digestBytes))
|
if (!hFile.isSameDigest(digestBytes))
|
||||||
throw new SecurityException(String.format("File digest mismatch: '%s'", filePath));
|
throw new SecurityException(String.format("File digest mismatch: '%s'", filePath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +345,7 @@ protected SignedObjectHolder<HashedDir> requestDo(HInput input, HOutput output)
|
||||||
case GET:
|
case GET:
|
||||||
Path targetFile = currentDir.resolve(action.name);
|
Path targetFile = currentDir.resolve(action.name);
|
||||||
if (fileInput.read() != 0xFF)
|
if (fileInput.read() != 0xFF)
|
||||||
throw new IOException("Serverside cached size mismath for file " + action.name);
|
throw new IOException("Serverside cached size mismath for file " + action.name);
|
||||||
downloadFile(targetFile, (HashedFile) action.entry, fileInput);
|
downloadFile(targetFile, (HashedFile) action.entry, fileInput);
|
||||||
break;
|
break;
|
||||||
case CD_BACK:
|
case CD_BACK:
|
||||||
|
@ -368,7 +371,7 @@ public void setStateCallback(Callback callback) {
|
||||||
|
|
||||||
private void updateState(String filePath, long fileDownloaded, long fileSize) {
|
private void updateState(String filePath, long fileDownloaded, long fileSize) {
|
||||||
if (stateCallback != null)
|
if (stateCallback != null)
|
||||||
stateCallback.call(new State(filePath, fileDownloaded, fileSize,
|
stateCallback.call(new State(filePath, fileDownloaded, fileSize,
|
||||||
totalDownloaded, totalSize, Duration.between(startTime, Instant.now())));
|
totalDownloaded, totalSize, Duration.between(startTime, Instant.now())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue