IDEA Refractoring

This commit is contained in:
Gravit 2018-09-22 21:33:00 +07:00
parent 78766d0c5c
commit 8580cd4403
145 changed files with 1992 additions and 1860 deletions

View file

@ -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);

View file

@ -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());
}
} }

View file

@ -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);
}
}
} }

View file

@ -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;
} }
} }

View file

@ -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

View file

@ -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;
} }
} }

View file

@ -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;

View file

@ -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;
} }

View file

@ -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");

View file

@ -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));

View file

@ -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() {

View file

@ -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);

View file

@ -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;

View file

@ -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;
} }

View file

@ -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) {

View file

@ -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;

View file

@ -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");

View file

@ -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) {

View file

@ -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");
} }
} }

View file

@ -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);

View 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

View file

@ -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();

View file

@ -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);

View file

@ -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() {

View file

@ -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();
} }
} }

View file

@ -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);
}
} }

View file

@ -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);
} }
} }
} }

View file

@ -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;
} }
} }

View file

@ -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())));
} }
} }
} }
} }

View file

@ -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;

View file

@ -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();
}
} }

View file

@ -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());
} }
} }

View file

@ -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);
} }

View file

@ -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);

View file

@ -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);
} }

View file

@ -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);

View file

@ -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);
} }
} }

View file

@ -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());
} }
} }

View file

@ -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);
} }
} }

View file

@ -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);
} }
} }

View file

@ -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);
} }
} }

View file

@ -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);
} }
} }

View file

@ -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();
} }
} }

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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();
} }
} }

View file

@ -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);

View file

@ -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;
} }
} }

View file

@ -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);
} }
} }

View file

@ -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;
} }
} }

View file

@ -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());
}
} }

View file

@ -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;
} }

View file

@ -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
} }
} }

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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());

View file

@ -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);
} }
} }

View file

@ -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]);
} }
} }

View file

@ -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);
} }
} }

View file

@ -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);
} }
} }

View file

@ -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);
} }
} }

View file

@ -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();
} }
} }

View file

@ -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();

View file

@ -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

View file

@ -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);
} }
} }
} }

View file

@ -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);
} }
} }

View file

@ -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);
} }
} }

View file

@ -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));

View file

@ -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

View file

@ -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)),

View file

@ -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;

View file

@ -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;

View file

@ -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;
} }
} }

View file

@ -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);

View file

@ -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)");
} }
} }

View file

@ -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();
} }
} }

View file

@ -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);

View 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() {

View file

@ -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;

View file

@ -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<>();
} }

View file

@ -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();

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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;
} }
} }

View file

@ -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() {

View file

@ -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));
}
});
}
}
} }

View file

@ -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;
}
} }

View file

@ -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);
} }

View file

@ -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;
} }
} }

View file

@ -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);
} }

View file

@ -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();
} }
} }

View file

@ -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;
} }
} }

View file

@ -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");
} }
} }

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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