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");
}
}
public static class ExeConf extends ConfigObject {
public final boolean enabled;
public String productName;
@ -201,6 +202,7 @@ private ExeConf(BlockConfigEntry block) {
: String.format("%s, build %d", Launcher.getVersion().getVersionString(), Launcher.getVersion().build);
}
}
private final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
private final Collection<SignedObjectHolder<ClientProfile>> result;
@ -224,6 +226,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
return super.visitFile(file, attrs);
}
}
public static class SignConf extends ConfigObject {
public final boolean enabled;
public String algo;
@ -233,22 +236,24 @@ public static class SignConf extends ConfigObject {
public boolean hasPass;
public String pass;
public String keyalias;
private SignConf(BlockConfigEntry block, Path coredir) {
super(block);
enabled = block.getEntryValue("enabled", BooleanConfigEntry.class);
storepass = null;
pass = null;
if (enabled) {
algo = block.hasEntry("storeType") ? block.getEntryValue("storeType", StringConfigEntry.class) : "JKS";
key = coredir.resolve(block.getEntryValue("keyFile", StringConfigEntry.class));
hasStorePass = block.hasEntry("keyStorePass");
if (hasStorePass) storepass = block.getEntryValue("keyStorePass", StringConfigEntry.class);
keyalias = block.getEntryValue("keyAlias", StringConfigEntry.class);
hasPass = block.hasEntry("keyPass");
if (hasPass) pass = block.getEntryValue("keyPass", StringConfigEntry.class);
algo = block.hasEntry("storeType") ? block.getEntryValue("storeType", StringConfigEntry.class) : "JKS";
key = coredir.resolve(block.getEntryValue("keyFile", StringConfigEntry.class));
hasStorePass = block.hasEntry("keyStorePass");
if (hasStorePass) storepass = block.getEntryValue("keyStorePass", StringConfigEntry.class);
keyalias = block.getEntryValue("keyAlias", StringConfigEntry.class);
hasPass = block.hasEntry("keyPass");
if (hasPass) pass = block.getEntryValue("keyPass", StringConfigEntry.class);
}
}
}
public static void main(String... args) throws Throwable {
JVMHelper.verifySystemProperties(LaunchServer.class, true);
LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log"));
@ -267,6 +272,7 @@ public static void main(String... args) throws Throwable {
Instant end = Instant.now();
LogHelper.debug("LaunchServer started in %dms", Duration.between(start, end).toMillis());
}
// Constant paths
@LauncherAPI
public final Path dir;
@ -317,7 +323,7 @@ public static void main(String... args) throws Throwable {
@LauncherAPI
public final ServerSocketHandler serverSocketHandler;
private final AtomicBoolean started = new AtomicBoolean(false);
private final AtomicBoolean started = new AtomicBoolean(false);
// Updates and profiles
private volatile List<SignedObjectHolder<ClientProfile>> profilesList;
@ -346,9 +352,9 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
// Set command handler
CommandHandler localCommandHandler;
if (portable)
localCommandHandler = new StdCommandHandler(this, false);
else
try {
localCommandHandler = new StdCommandHandler(this, false);
else
try {
Class.forName("jline.Terminal");
// JLine2 available
@ -366,7 +372,7 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
publicKey = SecurityHelper.toPublicRSAKey(IOHelper.read(publicKeyFile));
privateKey = SecurityHelper.toPrivateRSAKey(IOHelper.read(privateKeyFile));
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 {
LogHelper.info("Generating RSA keypair");
KeyPair pair = SecurityHelper.genRSAKeyPair();
@ -415,12 +421,12 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
// Sync updates dir
if (!IOHelper.isDir(updatesDir))
Files.createDirectory(updatesDir);
Files.createDirectory(updatesDir);
syncUpdatesDir(null);
// Sync profiles dir
if (!IOHelper.isDir(profilesDir))
Files.createDirectory(profilesDir);
Files.createDirectory(profilesDir);
syncProfilesDir();
@ -432,8 +438,8 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
}
private LauncherBinary binary() {
if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this);
return new EXELauncherBinary(this);
if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this);
return new EXELauncherBinary(this);
}
@LauncherAPI
@ -462,11 +468,7 @@ public void close() {
} catch (IOException e) {
LogHelper.error(e);
}
try {
config.hwidHandler.close();
} catch (IOException e) {
LogHelper.error(e);
}
config.hwidHandler.close();
modulesManager.close();
// Print last message before death :(
LogHelper.info("LaunchServer stopped");
@ -474,7 +476,7 @@ public void close() {
private void generateConfigIfNotExists() throws IOException {
if (IOHelper.isFile(configFile))
return;
return;
// Create new config
LogHelper.info("Creating LaunchServer config");
@ -524,7 +526,7 @@ public void rebindServerSocket() {
@Override
public void run() {
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
if (!portable) {
@ -567,7 +569,7 @@ public void syncUpdatesDir(Collection<String> dirs) throws IOException {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(updatesDir)) {
for (Path updateDir : dirStream) {
if (Files.isHidden(updateDir))
continue; // Skip hidden
continue; // Skip hidden
// Resolve name and verify is dir
String name = IOHelper.getFileName(updateDir);

View file

@ -10,78 +10,76 @@
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import ru.gravit.utils.helper.IOHelper;
import ru.gravit.utils.helper.JVMHelper;
import ru.gravit.utils.helper.LogHelper;
import ru.gravit.utils.helper.SecurityHelper;
public class ProguardConf {
private static final String charsFirst = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
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;
private static final String charsFirst = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ";
private static final String chars = "1aAbBcC2dDeEfF3gGhHiI4jJkKl5mMnNoO6pPqQrR7sStT8uUvV9wWxX0yYzZ";
public ProguardConf(LaunchServer srv) {
this.srv = srv;
proguard = this.srv.dir.resolve("proguard");
config = proguard.resolve("proguard.config");
mappings = proguard.resolve("mappings.pro");
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());
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();
}
}
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 {
if (IOHelper.exists(config) && !force) return;
Files.deleteIfExists(config);
config.toFile().createNewFile();
try (OutputStream out = IOHelper.newOutput(config); InputStream in = IOHelper.newInput(IOHelper.getResourceURL("ru/gravit/launchserver/defaults/proguard.cfg"))) {
IOHelper.transfer(in, out);
}
}
public ProguardConf(LaunchServer srv) {
LaunchServer srv1 = srv;
proguard = srv1.dir.resolve("proguard");
config = proguard.resolve("proguard.config");
mappings = proguard.resolve("mappings.pro");
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) {
try {
IOHelper.createParentDirs(config);
genWords(force);
genConfig(force);
} catch (IOException e) {
LogHelper.error(e);
}
}
private void genConfig(boolean force) throws IOException {
if (IOHelper.exists(config) && !force) return;
Files.deleteIfExists(config);
config.toFile().createNewFile();
try (OutputStream out = IOHelper.newOutput(config); InputStream in = IOHelper.newInput(IOHelper.getResourceURL("ru/gravit/launchserver/defaults/proguard.cfg"))) {
IOHelper.transfer(in, out);
}
}
private String readConf() {
return "@".concat(config.toFile().getName());
}
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) {
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 static final class StarterVisitor extends SimpleFileVisitor<Path> {
private Instrumentation inst;
public static final class StarterVisitor extends SimpleFileVisitor<Path> {
private Instrumentation inst;
public StarterVisitor(Instrumentation inst) {
this.inst = inst;
}
public StarterVisitor(Instrumentation inst) {
this.inst = inst;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toFile().getName().endsWith(".jar")) 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));
} catch (IOException e) {
e.printStackTrace(System.err);
}
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toFile().getName().endsWith(".jar"))
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));
} catch (IOException e) {
e.printStackTrace(System.err);
}
}
}

View file

@ -20,16 +20,17 @@ public AuthEntry(int i, long l) {
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
return true;
if (obj == null)
return false;
return false;
if (!(obj instanceof AuthEntry))
return false;
return false;
AuthEntry other = (AuthEntry) obj;
if (ts != other.ts)
return false;
return false;
return value == other.value;
}
@Override
public int hashCode() {
final int prime = 31;
@ -44,6 +45,7 @@ public String toString() {
return String.format("AuthEntry {value=%s, ts=%s}", value, ts);
}
}
@LauncherAPI
public static final long TIMEOUT = 10 * 60 * 1000; //10 минут
public final int rateLimit;
@ -78,7 +80,7 @@ public boolean isLimit(String ip) {
rate.ts = currenttime;
return false;
}
map.put(ip, new AuthEntry(1, System.currentTimeMillis()));
return false;
map.put(ip, new AuthEntry(1, System.currentTimeMillis()));
return false;
}
}

View file

@ -67,7 +67,7 @@ public MySQLSourceConfig(String poolName, BlockConfigEntry block) {
@Override
public synchronized void close() {
if (hikari)
((HikariDataSource) source).close();
((HikariDataSource) source).close();
}
@LauncherAPI

View file

@ -45,7 +45,7 @@ public static void registerHandlers() {
registerHandler("binaryFile", BinaryFileAuthHandler::new);
registerHandler("textFile", TextFileAuthHandler::new);
registerHandler("mysql", MySQLAuthHandler::new);
registerHandler("json",JsonAuthHandler::new);
registerHandler("json", JsonAuthHandler::new);
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);
}
}
private final Map<UUID, Entry> entryCache = 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
protected CachedAuthHandler(BlockConfigEntry 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
protected void addEntry(Entry entry) {
Entry previous = entryCache.put(entry.uuid, entry);
if (previous != null)
usernamesCache.remove(CommonHelper.low(previous.username));
usernamesCache.remove(CommonHelper.low(previous.username));
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 {
Entry entry = getEntry(result.username);
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)
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 {
UUID uuid = usernamesCache.get(CommonHelper.low(username));
if (uuid != null)
return getEntry(uuid);
return getEntry(uuid);
// Fetch entry by username
Entry entry = fetchEntry(username);
if (entry != null)
addEntry(entry);
addEntry(entry);
// Return what we got
return entry;
@ -95,7 +97,7 @@ private Entry getEntry(UUID uuid) throws IOException {
if (entry == null) {
entry = fetchEntry(uuid);
if (entry != null)
addEntry(entry);
addEntry(entry);
}
return entry;
}
@ -105,16 +107,17 @@ public synchronized boolean joinServer(String username, String accessToken, Stri
Entry entry = getEntry(username);
if (entry == null || !username.equals(entry.username) || !accessToken.equals(entry.accessToken) ||
!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
entry.serverID = serverID;
return true;
}
public synchronized void garbageCollection()
{
public synchronized void garbageCollection() {
entryCache.clear();
}
@LauncherAPI
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()) {
accessToken = SecurityHelper.verifyToken(input.readASCII(-SecurityHelper.TOKEN_STRING_LENGTH));
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) {
this(username);
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
this.accessToken = accessToken == null ? null : SecurityHelper.verifyToken(accessToken);
@ -84,7 +84,7 @@ public String getUsername() {
private boolean joinServer(String username, String accessToken, String serverID) {
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
this.serverID = serverID;
@ -99,10 +99,11 @@ public void write(HOutput output) throws IOException {
output.writeASCII(accessToken, -SecurityHelper.TOKEN_STRING_LENGTH);
output.writeBoolean(serverID != null);
if (serverID != null)
output.writeASCII(serverID, 41);
output.writeASCII(serverID, 41);
}
}
}
@LauncherAPI
public final Path file;
@LauncherAPI
@ -143,7 +144,7 @@ protected final void addAuth(UUID uuid, Entry entry) {
try {
Entry previous = entryMap.put(uuid, entry);
if (previous != null)
usernamesMap.remove(CommonHelper.low(previous.username));
usernamesMap.remove(CommonHelper.low(previous.username));
usernamesMap.put(CommonHelper.low(entry.username), uuid);
} finally {
lock.writeLock().unlock();
@ -210,15 +211,15 @@ private UUID genUUIDFor(String username) {
if (offlineUUIDs) {
UUID md5UUID = PlayerProfile.offlineUUID(username);
if (!entryMap.containsKey(md5UUID))
return md5UUID;
return md5UUID;
LogHelper.warning("Offline UUID collision, using random: '%s'", username);
}
// Pick random UUID
UUID uuid;
do
uuid = new UUID(random.nextLong(), random.nextLong());
while (entryMap.containsKey(uuid));
uuid = new UUID(random.nextLong(), random.nextLong());
while (entryMap.containsKey(uuid));
return uuid;
}

View file

@ -72,19 +72,20 @@ public UUID checkServer(String username, String serverID) throws IOException {
JsonObject result = jsonRequestChecked(request, urlCheckServer);
String value;
if ((value = result.getString(uuidKeyName, null)) != null)
return UUID.fromString(value);
throw new IOException("Service error");
return UUID.fromString(value);
throw new IOException("Service error");
}
@Override
public void close() {
}
@Override
public boolean joinServer(String username, String accessToken, String serverID) throws IOException {
JsonObject request = Json.object().add(userKeyName, username).add(serverIDKeyName, serverID).add(accessTokenKeyName, accessToken);
HTTPRequest.jsonRequest(request, urlJoinServer);
return request.getString(responseOKKeyName,null).equals("OK");
return request.getString(responseOKKeyName, null).equals("OK");
}
@Override
@ -107,8 +108,8 @@ public String uuidToUsername(UUID uuid) throws IOException {
throw new IOException("Service error");
}
public JsonObject jsonRequestChecked(JsonObject object,URL url) throws IOException {
JsonValue result = HTTPRequest.jsonRequest(object,url);
public JsonObject jsonRequestChecked(JsonObject object, URL url) throws IOException {
JsonValue result = HTTPRequest.jsonRequest(object, url);
if (!result.isObject())
authError("Authentication server response is malformed");

View file

@ -18,7 +18,7 @@ private static String toUsername(UUID uuid) {
// Find username end
int length = 0;
while (length < bytes.length && bytes[length] != 0)
length++;
length++;
// Decode and verify
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 {
AuthHandler handler = this.handler;
if (handler != null)
handler.close();
handler.close();
}
private AuthHandler getHandler() {

View file

@ -72,17 +72,17 @@ protected void writeAuthFileTmp() throws IOException {
authMap.put("username", cc(auth.getUsername()));
String accessToken = auth.getAccessToken();
if (accessToken != null)
authMap.put("accessToken", cc(accessToken));
authMap.put("accessToken", cc(accessToken));
String serverID = auth.getServerID();
if (serverID != null)
authMap.put("serverID", cc(serverID));
authMap.put("serverID", cc(serverID));
// Create and add auth block
BlockConfigEntry authBlock = new BlockConfigEntry(authMap, true, 5);
if (next)
authBlock.setComment(0, "\n"); // Pre-name
else
next = true;
authBlock.setComment(0, "\n"); // Pre-name
else
next = true;
authBlock.setComment(2, " "); // Pre-value
authBlock.setComment(4, "\n"); // Post-comment
map.put(uuid.toString(), authBlock);

View file

@ -1,6 +1,5 @@
package ru.gravit.launchserver.auth.hwid;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

View file

@ -9,9 +9,11 @@ public class HWID {
public static HWID fromData(HInput in) throws IOException {
return gen(in.readLong(), in.readLong(), in.readLong());
}
public static HWID gen(long hwid_hdd, long hwid_bios, long hwid_cpu) {
return new HWID(hwid_hdd, hwid_bios, hwid_cpu);
}
private long hwid_bios;
private long hwid_hdd;
@ -27,16 +29,16 @@ private HWID(long hwid_hdd, long hwid_bios, long hwid_cpu) {
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
return true;
if (obj == null)
return false;
return false;
if (!(obj instanceof HWID))
return false;
return false;
HWID other = (HWID) obj;
if (hwid_bios != other.hwid_bios)
return false;
return false;
if (hwid_cpu != other.hwid_cpu)
return false;
return false;
return hwid_hdd == other.hwid_hdd;
}

View file

@ -2,11 +2,11 @@
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) {

View file

@ -29,17 +29,20 @@ public static void registerHandler(String name, Adapter<HWIDHandler> adapter) {
VerifyHelper.putIfAbsent(HW_HANDLERS, name, Objects.requireNonNull(adapter, "adapter"),
String.format("HWID handler has been already registered: '%s'", name));
}
public static void registerHandlers() {
if (!registredHandl) {
registerHandler("accept", AcceptHWIDHandler::new);
registerHandler("mysql",MysqlHWIDHandler::new);
registerHandler("json",JsonHWIDHandler::new);
registerHandler("mysql", MysqlHWIDHandler::new);
registerHandler("json", JsonHWIDHandler::new);
registredHandl = true;
}
}
protected HWIDHandler(BlockConfigEntry block) {
super(block);
}
public abstract void ban(List<HWID> hwid) 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;
@Override
public abstract void close() throws IOException;
public abstract void close();
public abstract List<HWID> getHwid(String username) throws HWIDException;

View file

@ -58,16 +58,17 @@ public final class JsonHWIDHandler extends HWIDHandler {
@Override
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());
try {
request(request,urlBan);
request(request, urlBan);
} catch (IOException e) {
LogHelper.error(e);
throw new HWIDException("HWID service error");
}
}
}
public JsonObject request(JsonObject request, URL url) throws HWIDException, IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
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");
JsonObject response = content.asObject();
return response;
return response;
}
@Override
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;
try {
response = request(request,url);
response = request(request, url);
} catch (IOException e) {
LogHelper.error(e);
throw new HWIDException("HWID service error");
}
boolean isBanned = response.getBoolean(isBannedKeyName,false);
if(isBanned) throw new HWIDException("You will BANNED!");
boolean isBanned = response.getBoolean(isBannedKeyName, false);
if (isBanned) throw new HWIDException("You will BANNED!");
}
@Override
@ -118,24 +120,23 @@ public void close() {
@Override
public List<HWID> getHwid(String username) throws HWIDException {
JsonObject request = Json.object().add(loginKeyName,username);
JsonObject request = Json.object().add(loginKeyName, username);
JsonObject responce;
try {
responce = request(request,urlGet);
responce = request(request, urlGet);
} catch (IOException e) {
LogHelper.error(e);
throw new HWIDException("HWID service error");
}
JsonArray array = responce.get("hwids").asArray();
ArrayList<HWID> hwids = new ArrayList<>();
for(JsonValue i : array)
{
long hdd,cpu,bios;
for (JsonValue i : array) {
long hdd, cpu, bios;
JsonObject object = i.asObject();
hdd = object.getLong(hddKeyName,0);
cpu = object.getLong(cpuKeyName,0);
bios = object.getLong(biosKeyName,0);
HWID hwid = HWID.gen(hdd,cpu,bios);
hdd = object.getLong(hddKeyName, 0);
cpu = object.getLong(cpuKeyName, 0);
bios = object.getLong(biosKeyName, 0);
HWID hwid = HWID.gen(hdd, cpu, bios);
hwids.add(hwid);
}
return hwids;
@ -143,10 +144,10 @@ public List<HWID> getHwid(String username) throws HWIDException {
@Override
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());
try {
request(request,urlUnBan);
request(request, urlUnBan);
} catch (IOException e) {
LogHelper.error(e);
throw new HWIDException("HWID service error");

View file

@ -22,7 +22,7 @@ public class MysqlHWIDHandler extends HWIDHandler {
private final String banMessage;
private final String isBannedName;
private final String loginName;
private final String hddName,cpuName,biosName;
private final String hddName, cpuName, biosName;
private final String[] queryParams;
private final String queryUpd;
private final String[] queryParamsUpd;
@ -72,7 +72,7 @@ public void check0(HWID hwid, String username) throws HWIDException {
Connection c = mySQLHolder.getConnection();
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++) {
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()) {
boolean isOne = false;
boolean needWrite = true;
while(set.next()) {
while (set.next()) {
isOne = true;
boolean isBanned = set.getBoolean(isBannedName);
if (isBanned) throw new HWIDException(banMessage);
@ -95,8 +95,7 @@ public void check0(HWID hwid, String username) throws HWIDException {
writeHWID(hwid, username, c);
return;
}
if(needWrite)
{
if (needWrite) {
writeHWID(hwid, username, c);
return;
}
@ -105,9 +104,9 @@ public void check0(HWID hwid, String username) throws HWIDException {
e.printStackTrace();
}
}
public void writeHWID(HWID hwid, String username, Connection c)
{
LogHelper.debug("Write HWID %s from username %s",hwid.toString(),username);
public void writeHWID(HWID hwid, String username, Connection c) {
LogHelper.debug("Write HWID %s from username %s", hwid.toString(), username);
try (PreparedStatement a = c.prepareStatement(queryUpd)) {
//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};
@ -120,9 +119,9 @@ public void writeHWID(HWID hwid, String username, Connection c)
e.printStackTrace();
}
}
public void setIsBanned(HWID hwid,boolean isBanned)
{
LogHelper.debug("%s Request HWID: %s",isBanned ? "Ban" : "UnBan",hwid.toString());
public void setIsBanned(HWID hwid, boolean isBanned) {
LogHelper.debug("%s Request HWID: %s", isBanned ? "Ban" : "UnBan", hwid.toString());
Connection c = null;
try {
c = mySQLHolder.getConnection();
@ -141,36 +140,35 @@ public void setIsBanned(HWID hwid,boolean isBanned)
e.printStackTrace();
}
}
@Override
public void ban(List<HWID> list) throws HWIDException {
for(HWID hwid : list)
{
setIsBanned(hwid,true);
@Override
public void ban(List<HWID> list) {
for (HWID hwid : list) {
setIsBanned(hwid, true);
}
}
@Override
public void unban(List<HWID> list) throws HWIDException {
for(HWID hwid : list)
{
setIsBanned(hwid,false);
public void unban(List<HWID> list) {
for (HWID hwid : list) {
setIsBanned(hwid, false);
}
}
@Override
public List<HWID> getHwid(String username) throws HWIDException {
public List<HWID> getHwid(String username) {
try {
LogHelper.debug("Try find HWID from username %s",username);
LogHelper.debug("Try find HWID from username %s", username);
Connection c = mySQLHolder.getConnection();
PreparedStatement s = c.prepareStatement(querySelect);
String[] replaceParams = {"login", username};
for (int i = 0; i < queryParamsSelect.length; i++) {
s.setString(i + 1, CommonHelper.replace(queryParamsSelect[i], replaceParams));
}
long hdd,cpu,bios;
long hdd, cpu, bios;
try (ResultSet set = s.executeQuery()) {
if(!set.next()) {
if (!set.next()) {
LogHelper.error(new HWIDException("HWID not found"));
return new ArrayList<>();
}
@ -179,11 +177,12 @@ public List<HWID> getHwid(String username) throws HWIDException {
bios = set.getLong(biosName);
}
ArrayList<HWID> list = new ArrayList<>();
HWID hwid = HWID.gen(hdd,bios,cpu);
if(hdd == 0 && cpu == 0 && bios == 0) {LogHelper.warning("Null HWID");}
else {
HWID hwid = HWID.gen(hdd, bios, cpu);
if (hdd == 0 && cpu == 0 && bios == 0) {
LogHelper.warning("Null HWID");
} else {
list.add(hwid);
LogHelper.debug("Username: %s HWID: %s",username,hwid.toString());
LogHelper.debug("Username: %s HWID: %s", username, hwid.toString());
}
return list;
} catch (SQLException e) {

View file

@ -20,16 +20,16 @@ protected DigestAuthProvider(BlockConfigEntry block) {
protected final void verifyDigest(String validDigest, String password) throws AuthException {
boolean valid;
if (digest == DigestAlgorithm.PLAIN)
valid = password.equals(validDigest);
else if (validDigest == null)
valid = false;
else {
valid = password.equals(validDigest);
else if (validDigest == null)
valid = false;
else {
byte[] actualDigest = SecurityHelper.digest(digest, password);
valid = SecurityHelper.toHex(actualDigest).equals(validDigest);
}
// Verify is 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
verifyDigest(entry == null ? null : entry.password, password);
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
return new AuthProviderResult(entry.username, SecurityHelper.randomStringToken());
@ -80,7 +80,7 @@ public void close() {
private void updateCache() throws IOException {
FileTime lastModified = IOHelper.readAttributes(file).lastModifiedTime();
if (lastModified.equals(cacheLastModified))
return; // Not modified, so cache is up-to-date
return; // Not modified, so cache is up-to-date
// Read file
LogHelper.info("Recaching auth provider file: '%s'", file);

View file

@ -1,12 +1,7 @@
package ru.gravit.launchserver.auth.provider;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
@ -49,19 +44,19 @@ public final class JsonAuthProvider extends AuthProvider {
@Override
public AuthProviderResult auth(String login, String password, String ip) throws IOException {
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())
return authError("Authentication server response is malformed");
return authError("Authentication server response is malformed");
JsonObject response = content.asObject();
String value;
if ((value = response.getString(responseUserKeyName, null)) != null)
return new AuthProviderResult(value, SecurityHelper.randomStringToken());
else if ((value = response.getString(responseErrorKeyName, null)) != null)
return authError(value);
else
return authError("Authentication server response is malformed");
return new AuthProviderResult(value, SecurityHelper.randomStringToken());
else if ((value = response.getString(responseErrorKeyName, null)) != null)
return authError(value);
else
return authError("Authentication server response is malformed");
}
@Override

View file

@ -66,10 +66,10 @@ public AuthProviderResult auth(String login, String password, String ip) throws
// Verify there's no error
JsonObject response = makeJSONRequest(URL, request);
if (response == null)
authError("Empty mojang response");
authError("Empty mojang response");
JsonValue errorMessage = response.get("errorMessage");
if (errorMessage != null)
authError(errorMessage.asString());
authError(errorMessage.asString());
// Parse JSON data
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);
String[] replaceParams = {"login", login, "password", password, "ip", ip};
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
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 {
AuthProvider provider = this.provider;
if (provider != null)
provider.close();
provider.close();
}
private AuthProvider getProvider() {

View file

@ -17,28 +17,31 @@ public BuildContext(ZipOutputStream output, JAConfigurator config) {
this.output = output;
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);
output.putNextEntry(zip);
IOHelper.transfer(inputStream,output);
IOHelper.transfer(inputStream, output);
}
public void pushJarFile(JarInputStream input) throws IOException {
ZipEntry e = input.getNextEntry();
while (e != null) {
output.putNextEntry(e);
IOHelper.transfer(input,output);
IOHelper.transfer(input, output);
e = input.getNextEntry();
}
}
public void pushJarFile(JarInputStream input, Set<String> blacklist) throws IOException {
ZipEntry e = input.getNextEntry();
while (e != null) {
if(blacklist.contains(e.getName())){
if (blacklist.contains(e.getName())) {
e = input.getNextEntry();
continue;
}
output.putNextEntry(e);
IOHelper.transfer(input,output);
IOHelper.transfer(input, output);
e = input.getNextEntry();
}
}

View file

@ -17,106 +17,107 @@
import net.sf.launch4j.config.VersionInfo;
public final class EXEL4JLauncherBinary extends LauncherBinary {
private final static class Launch4JLog extends Log {
private static final Launch4JLog INSTANCE = new Launch4JLog();
private final static class Launch4JLog extends Log {
private static final Launch4JLog INSTANCE = new Launch4JLog();
@Override
public void append(String s) {
LogHelper.subInfo(s);
}
@Override
public void append(String s) {
LogHelper.subInfo(s);
}
@Override
public void clear() {
// Do nothing
}
}
@Override
public void clear() {
// Do nothing
}
}
// URL constants
private static final String DOWNLOAD_URL = "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"; // Oracle
// JRE
// 8
// URL constants
private static final String DOWNLOAD_URL = "http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html"; // Oracle
// JRE
// 8
// File constants
private final Path faviconFile;
// File constants
private final Path faviconFile;
@LauncherAPI
public EXEL4JLauncherBinary(LaunchServer server) {
super(server, server.dir.resolve(server.config.binaryName + ".exe"));
faviconFile = server.dir.resolve("favicon.ico");
setConfig();
}
@LauncherAPI
public EXEL4JLauncherBinary(LaunchServer server) {
super(server, server.dir.resolve(server.config.binaryName + ".exe"));
faviconFile = server.dir.resolve("favicon.ico");
setConfig();
}
@Override
public void build() throws IOException {
LogHelper.info("Building launcher EXE binary file (Using Launch4J)");
@Override
public void build() throws IOException {
LogHelper.info("Building launcher EXE binary file (Using Launch4J)");
// Set favicon path
Config config = ConfigPersister.getInstance().getConfig();
if (IOHelper.isFile(faviconFile))
config.setIcon(faviconFile.toFile());
else {
config.setIcon(null);
LogHelper.warning("Missing favicon.ico file");
}
// Set favicon path
Config config = ConfigPersister.getInstance().getConfig();
if (IOHelper.isFile(faviconFile))
config.setIcon(faviconFile.toFile());
else {
config.setIcon(null);
LogHelper.warning("Missing favicon.ico file");
}
// Start building
Builder builder = new Builder(Launch4JLog.INSTANCE);
try {
builder.build();
} catch (Throwable e) {
throw new IOException(e);
}
}
// Start building
Builder builder = new Builder(Launch4JLog.INSTANCE);
try {
builder.build();
} catch (Throwable e) {
throw new IOException(e);
}
}
private void setConfig() {
Config config = new Config();
// Set string options
config.setChdir(".");
config.setErrTitle("JVM Error");
config.setDownloadUrl(DOWNLOAD_URL);
private void setConfig() {
Config config = new Config();
// Set string options
config.setChdir(".");
config.setErrTitle("JVM Error");
config.setDownloadUrl(DOWNLOAD_URL);
// Set boolean options
config.setPriorityIndex(0);
config.setHeaderType(Config.GUI_HEADER);
config.setStayAlive(false);
config.setRestartOnCrash(false);
// Set boolean options
config.setPriorityIndex(0);
config.setHeaderType(Config.GUI_HEADER);
config.setStayAlive(false);
config.setRestartOnCrash(false);
// Prepare JRE
Jre jre = new Jre();
jre.setMinVersion("1.8.0");
jre.setRuntimeBits(Jre.RUNTIME_BITS_64_AND_32);
jre.setJdkPreference(Jre.JDK_PREFERENCE_PREFER_JRE);
config.setJre(jre);
// Prepare JRE
Jre jre = new Jre();
jre.setMinVersion("1.8.0");
jre.setRuntimeBits(Jre.RUNTIME_BITS_64_AND_32);
jre.setJdkPreference(Jre.JDK_PREFERENCE_PREFER_JRE);
config.setJre(jre);
// Prepare version info (product)
VersionInfo info = new VersionInfo();
info.setProductName(server.config.launch4j.productName);
info.setProductVersion(formatVars(server.config.launch4j.productVer));
info.setFileDescription(server.config.launch4j.fileDesc);
info.setFileVersion(formatVars(server.config.launch4j.fileVer));
info.setCopyright(server.config.launch4j.copyright);
info.setTrademarks(server.config.launch4j.trademarks);
info.setInternalName(formatVars(server.config.launch4j.internalName));
// Prepare version info (file)
info.setTxtFileVersion(formatVars(server.config.launch4j.txtFileVersion));
info.setTxtProductVersion(formatVars(server.config.launch4j.txtProductVersion));
// Prepare version info (misc)
info.setOriginalFilename(binaryFile.getFileName().toString());
info.setLanguage(LanguageID.RUSSIAN);
config.setVersionInfo(info);
// Prepare version info (product)
VersionInfo info = new VersionInfo();
info.setProductName(server.config.launch4j.productName);
info.setProductVersion(formatVars(server.config.launch4j.productVer));
info.setFileDescription(server.config.launch4j.fileDesc);
info.setFileVersion(formatVars(server.config.launch4j.fileVer));
info.setCopyright(server.config.launch4j.copyright);
info.setTrademarks(server.config.launch4j.trademarks);
info.setInternalName(formatVars(server.config.launch4j.internalName));
// Prepare version info (file)
info.setTxtFileVersion(formatVars(server.config.launch4j.txtFileVersion));
info.setTxtProductVersion(formatVars(server.config.launch4j.txtProductVersion));
// Prepare version info (misc)
info.setOriginalFilename(binaryFile.getFileName().toString());
info.setLanguage(LanguageID.RUSSIAN);
config.setVersionInfo(info);
// Set JAR wrapping options
config.setDontWrapJar(false);
config.setJar(server.launcherBinary.syncBinaryFile.toFile());
config.setOutfile(binaryFile.toFile());
// Set JAR wrapping options
config.setDontWrapJar(false);
config.setJar(server.launcherBinary.syncBinaryFile.toFile());
config.setOutfile(binaryFile.toFile());
// Return prepared config
ConfigPersister.getInstance().setAntConfig(config, null);
}
private static String VERSION = Launcher.getVersion().getVersionString();
private static int BUILD = Launcher.getVersion().build;
public static String formatVars(String mask)
{
return String.format(mask, VERSION, BUILD);
}
// Return prepared config
ConfigPersister.getInstance().setAntConfig(config, null);
}
private static String VERSION = Launcher.getVersion().getVersionString();
private static int BUILD = Launcher.getVersion().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 EXELauncherBinary(LaunchServer server) {
super(server, server.dir.resolve(server.config.binaryName + ".exe"));
}
public EXELauncherBinary(LaunchServer server) {
super(server, server.dir.resolve(server.config.binaryName + ".exe"));
}
@Override
public void build() throws IOException {
@Override
public void build() throws IOException {
if (IOHelper.isFile(binaryFile)) {
LogHelper.subWarning("Deleting obsolete launcher EXE binary file");
Files.delete(binaryFile);
}
}
}
}

View file

@ -13,6 +13,7 @@ public class JAConfigurator implements AutoCloseable {
StringBuilder body;
StringBuilder moduleBody;
int autoincrement;
public JAConfigurator(Class<?> configclass) throws NotFoundException {
classname = configclass.getName();
ctClass = pool.get(classname);
@ -22,8 +23,8 @@ public JAConfigurator(Class<?> configclass) throws NotFoundException {
moduleBody = new StringBuilder("{ isInitModules = true; ");
autoincrement = 0;
}
public void addModuleClass(String fullName)
{
public void addModuleClass(String fullName) {
moduleBody.append("ru.gravit.launcher.modules.Module mod");
moduleBody.append(autoincrement);
moduleBody.append(" = new ");
@ -34,10 +35,12 @@ public void addModuleClass(String fullName)
moduleBody.append(" , true );");
autoincrement++;
}
@Override
public void close() {
ctClass.defrost();
}
public byte[] getBytecode() throws IOException, CannotCompileException {
body.append("}");
moduleBody.append("}");
@ -45,31 +48,30 @@ public byte[] getBytecode() throws IOException, CannotCompileException {
initModuleMethod.insertAfter(moduleBody.toString());
return ctClass.toBytecode();
}
public String getZipEntryPath()
{
return classname.replace('.','/').concat(".class");
public String getZipEntryPath() {
return classname.replace('.', '/').concat(".class");
}
public void setAddress(String address)
{
public void setAddress(String address) {
body.append("this.address = \"");
body.append(address);
body.append("\";");
}
public void setProjectName(String name)
{
public void setProjectName(String name) {
body.append("this.projectname = \"");
body.append(name);
body.append("\";");
}
public void setPort(int port)
{
public void setPort(int port) {
body.append("this.port = ");
body.append(port);
body.append(";");
}
public ClassPool getPool()
{
public ClassPool getPool() {
return pool;
}
}

View file

@ -36,193 +36,193 @@
import proguard.ProGuard;
public final class JARLauncherBinary extends LauncherBinary {
private final class RuntimeDirVisitor extends SimpleFileVisitor<Path> {
private final ZipOutputStream output;
private final Map<String, byte[]> runtime;
private final class RuntimeDirVisitor extends SimpleFileVisitor<Path> {
private final ZipOutputStream output;
private final Map<String, byte[]> runtime;
private RuntimeDirVisitor(ZipOutputStream output, Map<String, byte[]> runtime) {
this.output = output;
this.runtime = runtime;
}
private RuntimeDirVisitor(ZipOutputStream output, Map<String, byte[]> runtime) {
this.output = output;
this.runtime = runtime;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String dirName = IOHelper.toString(runtimeDir.relativize(dir));
output.putNextEntry(newEntry(dirName + '/'));
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String dirName = IOHelper.toString(runtimeDir.relativize(dir));
output.putNextEntry(newEntry(dirName + '/'));
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = IOHelper.toString(runtimeDir.relativize(file));
runtime.put(fileName, SecurityHelper.digest(DigestAlgorithm.MD5, file));
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = IOHelper.toString(runtimeDir.relativize(file));
runtime.put(fileName, SecurityHelper.digest(DigestAlgorithm.MD5, file));
// Create zip entry and transfer contents
output.putNextEntry(newEntry(fileName));
IOHelper.transfer(file, output);
// Create zip entry and transfer contents
output.putNextEntry(newEntry(fileName));
IOHelper.transfer(file, output);
// Return result
return super.visitFile(file, attrs);
}
}
// Return result
return super.visitFile(file, attrs);
}
}
private static ZipEntry newEntry(String fileName) {
return newZipEntry(Launcher.RUNTIME_DIR + IOHelper.CROSS_SEPARATOR + fileName);
}
private static ZipEntry newEntry(String fileName) {
return newZipEntry(Launcher.RUNTIME_DIR + IOHelper.CROSS_SEPARATOR + fileName);
}
@LauncherAPI
public final Path runtimeDir;
@LauncherAPI
public final Path runtimeDir;
@LauncherAPI
public final Path initScriptFile;
@LauncherAPI
public final Path initScriptFile;
@LauncherAPI
public final Path obfJar;
@LauncherAPI
public final Path obfJar;
@LauncherAPI
public JARLauncherBinary(LaunchServer server) throws IOException {
super(server, server.dir.resolve(server.config.binaryName + ".jar"),
server.dir.resolve(server.config.binaryName + (server.config.sign.enabled ? "-sign.jar" : "-obf.jar")));
runtimeDir = server.dir.resolve(Launcher.RUNTIME_DIR);
initScriptFile = runtimeDir.resolve(Launcher.INIT_SCRIPT_FILE);
obfJar = server.config.sign.enabled ? server.dir.resolve(server.config.binaryName + "-obf.jar")
: syncBinaryFile;
tryUnpackRuntime();
}
@LauncherAPI
public JARLauncherBinary(LaunchServer server) throws IOException {
super(server, server.dir.resolve(server.config.binaryName + ".jar"),
server.dir.resolve(server.config.binaryName + (server.config.sign.enabled ? "-sign.jar" : "-obf.jar")));
runtimeDir = server.dir.resolve(Launcher.RUNTIME_DIR);
initScriptFile = runtimeDir.resolve(Launcher.INIT_SCRIPT_FILE);
obfJar = server.config.sign.enabled ? server.dir.resolve(server.config.binaryName + "-obf.jar")
: syncBinaryFile;
tryUnpackRuntime();
}
@Override
public void build() throws IOException {
tryUnpackRuntime();
@Override
public void build() throws IOException {
tryUnpackRuntime();
// Build launcher binary
LogHelper.info("Building launcher binary file");
stdBuild();
// Build launcher binary
LogHelper.info("Building launcher binary file");
stdBuild();
// ProGuard
Configuration proguard_cfg = new Configuration();
ConfigurationParser parser = new ConfigurationParser(
server.proguardConf.confStrs.toArray(new String[0]),
server.proguardConf.proguard.toFile(), System.getProperties());
try {
parser.parse(proguard_cfg);
ProGuard proGuard = new ProGuard(proguard_cfg);
proGuard.execute();
} catch (ParseException e1) {
e1.printStackTrace();
}
if (server.config.sign.enabled)
signBuild();
}
// ProGuard
Configuration proguard_cfg = new Configuration();
ConfigurationParser parser = new ConfigurationParser(
server.proguardConf.confStrs.toArray(new String[0]),
server.proguardConf.proguard.toFile(), System.getProperties());
try {
parser.parse(proguard_cfg);
ProGuard proGuard = new ProGuard(proguard_cfg);
proGuard.execute();
} catch (ParseException e1) {
e1.printStackTrace();
}
if (server.config.sign.enabled)
signBuild();
}
private void signBuild() throws IOException {
try (SignerJar output = new SignerJar(IOHelper.newOutput(syncBinaryFile),
SignerJar.getStore(server.config.sign.key, server.config.sign.storepass, server.config.sign.algo),
server.config.sign.keyalias, server.config.sign.pass);
ZipInputStream input = new ZipInputStream(IOHelper.newInput(obfJar))) {
ZipEntry e = input.getNextEntry();
while (e != null) {
output.addFileContents(e, input);
e = input.getNextEntry();
}
}
}
private void signBuild() throws IOException {
try (SignerJar output = new SignerJar(IOHelper.newOutput(syncBinaryFile),
SignerJar.getStore(server.config.sign.key, server.config.sign.storepass, server.config.sign.algo),
server.config.sign.keyalias, server.config.sign.pass);
ZipInputStream input = new ZipInputStream(IOHelper.newInput(obfJar))) {
ZipEntry e = input.getNextEntry();
while (e != null) {
output.addFileContents(e, input);
e = input.getNextEntry();
}
}
}
private void stdBuild() throws IOException {
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(binaryFile));
JAConfigurator jaConfigurator = new JAConfigurator(AutogenConfig.class)) {
BuildContext context = new BuildContext(output, jaConfigurator);
server.buildHookManager.preHook(context);
jaConfigurator.setAddress(server.config.getAddress());
jaConfigurator.setPort(server.config.port);
jaConfigurator.setProjectName(server.config.projectName);
server.buildHookManager.registerAllClientModuleClass(jaConfigurator);
try (ZipInputStream input = new ZipInputStream(
IOHelper.newInput(IOHelper.getResourceURL("Launcher.jar")))) {
ZipEntry e = input.getNextEntry();
while (e != null) {
String filename = e.getName();
if (server.buildHookManager.isContainsBlacklist(filename)) {
e = input.getNextEntry();
continue;
}
try {
output.putNextEntry(e);
} catch (ZipException ex) {
LogHelper.error(ex);
e = input.getNextEntry();
continue;
}
if (filename.endsWith(".class")) {
CharSequence classname = filename.replace('/', '.').subSequence(0,
filename.length() - ".class".length());
byte[] bytes;
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048)) {
IOHelper.transfer(input, outputStream);
bytes = outputStream.toByteArray();
}
bytes = server.buildHookManager.classTransform(bytes, classname);
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
IOHelper.transfer(inputStream, output);
}
} else
IOHelper.transfer(input, output);
// }
e = input.getNextEntry();
}
}
// write additional classes
for (Entry<String, byte[]> ent : server.buildHookManager.getIncludeClass().entrySet()) {
output.putNextEntry(newZipEntry(ent.getKey().replace('.', '/').concat(".class")));
output.write(server.buildHookManager.classTransform(ent.getValue(), ent.getKey()));
}
// map for runtime
Map<String, byte[]> runtime = new HashMap<>(256);
if (server.buildHookManager.buildRuntime()) {
// Verify has init script file
if (!IOHelper.isFile(initScriptFile))
throw new IOException(String.format("Missing init script file ('%s')", Launcher.INIT_SCRIPT_FILE));
// Write launcher runtime dir
IOHelper.walk(runtimeDir, new RuntimeDirVisitor(output, runtime), false);
}
// Create launcher config file
byte[] launcherConfigBytes;
try (ByteArrayOutputStream configArray = IOHelper.newByteArrayOutput()) {
try (HOutput configOutput = new HOutput(configArray)) {
new LauncherConfig(server.config.getAddress(), server.config.port, server.publicKey, runtime)
.write(configOutput);
}
launcherConfigBytes = configArray.toByteArray();
}
private void stdBuild() throws IOException {
try (ZipOutputStream output = new ZipOutputStream(IOHelper.newOutput(binaryFile));
JAConfigurator jaConfigurator = new JAConfigurator(AutogenConfig.class)) {
BuildContext context = new BuildContext(output, jaConfigurator);
server.buildHookManager.preHook(context);
jaConfigurator.setAddress(server.config.getAddress());
jaConfigurator.setPort(server.config.port);
jaConfigurator.setProjectName(server.config.projectName);
server.buildHookManager.registerAllClientModuleClass(jaConfigurator);
try (ZipInputStream input = new ZipInputStream(
IOHelper.newInput(IOHelper.getResourceURL("Launcher.jar")))) {
ZipEntry e = input.getNextEntry();
while (e != null) {
String filename = e.getName();
if (server.buildHookManager.isContainsBlacklist(filename)) {
e = input.getNextEntry();
continue;
}
try {
output.putNextEntry(e);
} catch (ZipException ex) {
LogHelper.error(ex);
e = input.getNextEntry();
continue;
}
if (filename.endsWith(".class")) {
CharSequence classname = filename.replace('/', '.').subSequence(0,
filename.length() - ".class".length());
byte[] bytes;
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(2048)) {
IOHelper.transfer(input, outputStream);
bytes = outputStream.toByteArray();
}
bytes = server.buildHookManager.classTransform(bytes, classname);
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes)) {
IOHelper.transfer(inputStream, output);
}
} else
IOHelper.transfer(input, output);
// }
e = input.getNextEntry();
}
}
// write additional classes
for (Entry<String, byte[]> ent : server.buildHookManager.getIncludeClass().entrySet()) {
output.putNextEntry(newZipEntry(ent.getKey().replace('.', '/').concat(".class")));
output.write(server.buildHookManager.classTransform(ent.getValue(), ent.getKey()));
}
// map for runtime
Map<String, byte[]> runtime = new HashMap<>(256);
if (server.buildHookManager.buildRuntime()) {
// Verify has init script file
if (!IOHelper.isFile(initScriptFile))
throw new IOException(String.format("Missing init script file ('%s')", Launcher.INIT_SCRIPT_FILE));
// Write launcher runtime dir
IOHelper.walk(runtimeDir, new RuntimeDirVisitor(output, runtime), false);
}
// Create launcher config file
byte[] launcherConfigBytes;
try (ByteArrayOutputStream configArray = IOHelper.newByteArrayOutput()) {
try (HOutput configOutput = new HOutput(configArray)) {
new LauncherConfig(server.config.getAddress(), server.config.port, server.publicKey, runtime)
.write(configOutput);
}
launcherConfigBytes = configArray.toByteArray();
}
// Write launcher config file
output.putNextEntry(newZipEntry(Launcher.CONFIG_FILE));
output.write(launcherConfigBytes);
ZipEntry e = newZipEntry(jaConfigurator.getZipEntryPath());
output.putNextEntry(e);
output.write(jaConfigurator.getBytecode());
server.buildHookManager.postHook(context);
} catch (CannotCompileException | NotFoundException e) {
LogHelper.error(e);
}
}
// Write launcher config file
output.putNextEntry(newZipEntry(Launcher.CONFIG_FILE));
output.write(launcherConfigBytes);
ZipEntry e = newZipEntry(jaConfigurator.getZipEntryPath());
output.putNextEntry(e);
output.write(jaConfigurator.getBytecode());
server.buildHookManager.postHook(context);
} catch (CannotCompileException | NotFoundException e) {
LogHelper.error(e);
}
}
@LauncherAPI
public void tryUnpackRuntime() throws IOException {
// Verify is runtime dir unpacked
if (IOHelper.isDir(runtimeDir))
return; // Already unpacked
@LauncherAPI
public void tryUnpackRuntime() throws IOException {
// Verify is runtime dir unpacked
if (IOHelper.isDir(runtimeDir))
return; // Already unpacked
// Unpack launcher runtime files
Files.createDirectory(runtimeDir);
LogHelper.info("Unpacking launcher runtime files");
try (ZipInputStream input = IOHelper.newZipInput(IOHelper.getResourceURL("runtime.zip"))) {
for (ZipEntry entry = input.getNextEntry(); entry != null; entry = input.getNextEntry()) {
if (entry.isDirectory())
continue; // Skip dirs
// Unpack launcher runtime files
Files.createDirectory(runtimeDir);
LogHelper.info("Unpacking launcher runtime files");
try (ZipInputStream input = IOHelper.newZipInput(IOHelper.getResourceURL("runtime.zip"))) {
for (ZipEntry entry = input.getNextEntry(); entry != null; entry = input.getNextEntry()) {
if (entry.isDirectory())
continue; // Skip dirs
// Unpack runtime file
IOHelper.transfer(input, runtimeDir.resolve(IOHelper.toPath(entry.getName())));
}
}
}
// Unpack runtime file
IOHelper.transfer(input, runtimeDir.resolve(IOHelper.toPath(entry.getName())));
}
}
}
}

View file

@ -22,6 +22,7 @@ protected LauncherBinary(LaunchServer server, Path binaryFile) {
this.binaryFile = binaryFile;
syncBinaryFile = binaryFile;
}
@LauncherAPI
protected LauncherBinary(LaunchServer server, Path binaryFile, Path syncBinaryFile) {
this.server = server;

View file

@ -59,365 +59,384 @@
* </pre>
*/
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 {
private final OutputStream out;
private final MessageDigest hasher;
/**
* Helper output stream that also sends the data to the given {@link com.google.common.hash.Hasher}.
*/
private static class HashingOutputStream extends OutputStream {
private final OutputStream out;
private final MessageDigest hasher;
public HashingOutputStream(OutputStream out, MessageDigest hasher) {
this.out = out;
this.hasher = hasher;
}
public HashingOutputStream(OutputStream out, MessageDigest hasher) {
this.out = out;
this.hasher = hasher;
}
@Override
public void close() throws IOException {
out.close();
}
@Override
public void close() throws IOException {
out.close();
}
@Override
public void flush() throws IOException {
out.flush();
}
@Override
public void flush() throws IOException {
out.flush();
}
@Override
public void write(byte[] b) throws IOException {
out.write(b);
hasher.update(b);
}
@Override
public void write(byte[] b) throws IOException {
out.write(b);
hasher.update(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
hasher.update(b, off, len);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
hasher.update(b, off, len);
}
@Override
public void write(int b) throws IOException {
out.write(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";
@Override
public void write(int b) throws IOException {
out.write(b);
hasher.update((byte) b);
}
}
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 {
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 static final String hashFunctionName = "SHA-256";
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 Map<String, String> manifestAttributes;
private String manifestHash;
private String manifestMainHash;
private final ZipOutputStream zos;
private final Map<String, String> fileDigests;
private final KeyStore keyStore;
private final Map<String, String> sectionDigests;
private final String keyAlias;
/**
* Constructor.
*
* @param out the output stream to write JAR data to
* @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;
private final String password;
private final Map<String, String> manifestAttributes;
private String manifestHash;
private String manifestMainHash;
manifestAttributes = new LinkedHashMap<>();
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();
private final Map<String, String> fileDigests;
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(contents));
fileDigests.put(filename, hashCode64);
}
private final Map<String, String> sectionDigests;
/**
* 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, InputStream contents) throws IOException {
zos.putNextEntry(new ZipEntry(filename));
byte[] arr = IOHelper.toByteArray(contents);
zos.write(arr);
zos.closeEntry();
/**
* Constructor.
*
* @param out the output stream to write JAR data to
* @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;
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(arr));
fileDigests.put(filename, hashCode64);
}
manifestAttributes = new LinkedHashMap<>();
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 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, byte[] contents) throws IOException {
zos.putNextEntry(entry);
zos.write(contents);
zos.closeEntry();
/**
* 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));
fileDigests.put(entry.getName(), hashCode64);
}
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(contents));
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
* 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();
/**
* 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, InputStream contents) throws IOException {
zos.putNextEntry(new ZipEntry(filename));
byte[] arr = IOHelper.toByteArray(contents);
zos.write(arr);
zos.closeEntry();
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(arr));
fileDigests.put(entry.getName(), hashCode64);
}
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(arr));
fileDigests.put(filename, 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);
}
/**
* 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, 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
* underlying stream.
*
* @throws java.io.IOException
* @throws RuntimeException if the signing goes wrong
*/
@Override
public void close() throws IOException {
finish();
zos.close();
}
/**
* Closes the JAR file by writing the manifest and signature data to it and finishing the ZIP entries. It closes the
* underlying stream.
*
* @throws java.io.IOException
* @throws RuntimeException if the signing goes wrong
*/
@Override
public void close() throws IOException {
finish();
zos.close();
}
/** Creates the beast that can actually sign the data. */
private CMSSignedDataGenerator createSignedDataGenerator() throws Exception {
Security.addProvider(new BouncyCastleProvider());
/**
* Creates the beast that can actually sign the data.
*/
private CMSSignedDataGenerator createSignedDataGenerator() throws Exception {
Security.addProvider(new BouncyCastleProvider());
List<Certificate> certChain = new ArrayList<>(Arrays.asList(keyStore.getCertificateChain(keyAlias)));
Store certStore = new JcaCertStore(certChain);
Certificate cert = keyStore.getCertificate(keyAlias);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, password != null ? password.toCharArray() : null);
ContentSigner signer = new JcaContentSignerBuilder("SHA256WITHRSA").setProvider("BC").build(privateKey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
generator.addSignerInfoGenerator(sig);
generator.addCertificates(certStore);
return generator;
}
List<Certificate> certChain = new ArrayList<>(Arrays.asList(keyStore.getCertificateChain(keyAlias)));
Store certStore = new JcaCertStore(certChain);
Certificate cert = keyStore.getCertificate(keyAlias);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias, password != null ? password.toCharArray() : null);
ContentSigner signer = new JcaContentSignerBuilder("SHA256WITHRSA").setProvider("BC").build(privateKey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
DigestCalculatorProvider dcp = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
SignerInfoGenerator sig = new JcaSignerInfoGeneratorBuilder(dcp).build(signer, (X509Certificate) cert);
generator.addSignerInfoGenerator(sig);
generator.addCertificates(certStore);
return generator;
}
/**
* Finishes the JAR file by writing the manifest and signature data to it and finishing the ZIP entries. It leaves the
* underlying stream open.
*
* @throws java.io.IOException
* @throws RuntimeException if the signing goes wrong
*/
public void finish() throws IOException {
writeManifest();
byte sig[] = writeSigFile();
writeSignature(sig);
zos.finish();
}
/**
* Finishes the JAR file by writing the manifest and signature data to it and finishing the ZIP entries. It leaves the
* underlying stream open.
*
* @throws java.io.IOException
* @throws RuntimeException if the signing goes wrong
*/
public void finish() throws IOException {
writeManifest();
byte sig[] = writeSigFile();
writeSignature(sig);
zos.finish();
}
public ZipOutputStream getZos() {
return zos;
}
public ZipOutputStream getZos() {
return zos;
}
/** Helper for {@link #writeManifest()} that creates the digest of one entry. */
private String hashEntrySection(String name, Attributes attributes) throws IOException {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
ByteArrayOutputStream o = new ByteArrayOutputStream();
manifest.write(o);
int emptyLen = o.toByteArray().length;
/**
* Helper for {@link #writeManifest()} that creates the digest of one entry.
*/
private String hashEntrySection(String name, Attributes attributes) throws IOException {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
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);
byte[] ob = o.toByteArray();
ob = Arrays.copyOfRange(ob, emptyLen, ob.length);
return Base64.getEncoder().encodeToString(hasher().digest(ob));
}
manifest.write(o);
byte[] ob = o.toByteArray();
ob = Arrays.copyOfRange(ob, emptyLen, ob.length);
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 {
Manifest manifest = new Manifest();
manifest.getMainAttributes().putAll(attributes);
MessageDigest hasher = hasher();
SignerJar.HashingOutputStream o = new SignerJar.HashingOutputStream(new OutputStream() {
@Override
public String toString() {
return "NullOutputStream";
}
/** Discards the specified byte array. */
@Override public void write(byte[] b) {
}
/** Discards the specified byte array. */
@Override public void write(byte[] b, int off, int len) {
}
/**
* Helper for {@link #writeManifest()} that creates the digest of the main section.
*/
private String hashMainSection(Attributes attributes) throws IOException {
Manifest manifest = new Manifest();
manifest.getMainAttributes().putAll(attributes);
MessageDigest hasher = hasher();
SignerJar.HashingOutputStream o = new SignerJar.HashingOutputStream(new OutputStream() {
@Override
public String toString() {
return "NullOutputStream";
}
/** Discards the specified byte. */
@Override public void write(int b) {
}
}, hasher);
manifest.write(o);
return Base64.getEncoder().encodeToString(hasher.digest());
}
/** Discards the specified byte array. */
@Override
public void write(byte[] b) {
}
/** Returns the CMS signed data. */
private byte[] signSigFile(byte[] sigContents) throws Exception {
CMSSignedDataGenerator gen = createSignedDataGenerator();
CMSTypedData cmsData = new CMSProcessableByteArray(sigContents);
CMSSignedData signedData = gen.generate(cmsData, true);
return signedData.getEncoded();
}
/** Discards the specified byte array. */
@Override
public void write(byte[] b, int off, int len) {
}
/**
* 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();
/** Discards the specified byte. */
@Override
public void write(int b) {
}
}, hasher);
manifest.write(o);
return Base64.getEncoder().encodeToString(hasher.digest());
}
// main section
Attributes mainAttributes = man.getMainAttributes();
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
/**
* Returns the CMS signed data.
*/
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
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));
}
// main section
Attributes mainAttributes = man.getMainAttributes();
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
MessageDigest hasher = hasher();
OutputStream out = new SignerJar.HashingOutputStream(zos, hasher);
man.write(out);
zos.closeEntry();
for (Map.Entry<String, String> entry : manifestAttributes.entrySet())
mainAttributes.put(new Attributes.Name(entry.getKey()), entry.getValue());
manifestHash = Base64.getEncoder().encodeToString(hasher.digest());
manifestMainHash = hashMainSection(man.getMainAttributes());
}
// individual files sections
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));
}
/**
* 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);
MessageDigest hasher = hasher();
OutputStream out = new SignerJar.HashingOutputStream(zos, hasher);
man.write(out);
zos.closeEntry();
// individual files sections
Attributes.Name digestAttr = new Attributes.Name(hashFunctionName + "-Digest");
for (Map.Entry<String, String> entry : sectionDigests.entrySet()) {
Attributes attributes = new Attributes();
man.getEntries().put(entry.getKey(), attributes);
attributes.put(digestAttr, entry.getValue());
}
manifestHash = Base64.getEncoder().encodeToString(hasher.digest());
manifestMainHash = hashMainSection(man.getMainAttributes());
}
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();
man.write(baos);
return baos.toByteArray();
}
// individual files sections
Attributes.Name digestAttr = new Attributes.Name(hashFunctionName + "-Digest");
for (Map.Entry<String, String> entry : sectionDigests.entrySet()) {
Attributes attributes = new Attributes();
man.getEntries().put(entry.getKey(), attributes);
attributes.put(digestAttr, entry.getValue());
}
/**
* Signs the .SIG file and writes the signature (.RSA file) to the JAR.
*
* @throws java.io.IOException
* @throws RuntimeException if the signing failed
*/
private void writeSignature(byte[] sigFile) throws IOException {
zos.putNextEntry(new ZipEntry(SIG_RSA_FN));
try {
byte[] signature = signSigFile(sigFile);
zos.write(signature);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Signing failed.", e);
}
zos.closeEntry();
}
man.write(zos);
zos.closeEntry();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
man.write(baos);
return baos.toByteArray();
}
/**
* Signs the .SIG file and writes the signature (.RSA file) to the JAR.
*
* @throws java.io.IOException
* @throws RuntimeException if the signing failed
*/
private void writeSignature(byte[] sigFile) throws IOException {
zos.putNextEntry(new ZipEntry(SIG_RSA_FN));
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
protected final void verifyArgs(String[] args, int min) throws CommandException {
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
public void invoke(String... args) throws Exception {
verifyArgs(args,1);
verifyArgs(args, 1);
List<HWID> target = server.config.hwidHandler.getHwid(args[0]);
server.config.hwidHandler.ban(target);
}

View file

@ -31,7 +31,7 @@ public void invoke(String... args) throws CommandException, IOException {
// Get UUID by username
String username = server.config.authHandler.uuidToUsername(uuid);
if (username == null)
throw new CommandException("Unknown UUID: " + uuid);
throw new CommandException("Unknown UUID: " + uuid);
// Print username
LogHelper.subInfo("Username of player %s: '%s'", uuid, username);

View file

@ -23,7 +23,7 @@ public String getUsageDescription() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args,1);
verifyArgs(args, 1);
List<HWID> target = server.config.hwidHandler.getHwid(args[0]);
server.config.hwidHandler.unban(target);
}

View file

@ -31,7 +31,7 @@ public void invoke(String... args) throws CommandException, IOException {
// Get UUID by username
UUID uuid = server.config.authHandler.usernameToUUID(username);
if (uuid == null)
throw new CommandException(String.format("Unknown username: '%s'", username));
throw new CommandException(String.format("Unknown username: '%s'", username));
// Print 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]);
LogHelper.setDebugEnabled(newValue);
} else
newValue = LogHelper.isDebugEnabled();
newValue = LogHelper.isDebugEnabled();
LogHelper.subInfo("Debug enabled: " + newValue);
}
}

View file

@ -44,6 +44,6 @@ private void printCommand(String name) throws CommandException {
private void printCommands() {
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]);
server.serverSocketHandler.logConnections = newValue;
} else
newValue = server.serverSocketHandler.logConnections;
newValue = server.serverSocketHandler.logConnections;
LogHelper.subInfo("Log connections: " + newValue);
}
}

View file

@ -4,22 +4,22 @@
import ru.gravit.launchserver.command.Command;
public class ProguardCleanCommand extends Command {
public ProguardCleanCommand(LaunchServer server) {
super(server);
}
public ProguardCleanCommand(LaunchServer server) {
super(server);
}
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getUsageDescription() {
return "Resets proguard config";
}
@Override
public String getUsageDescription() {
return "Resets proguard config";
}
@Override
public void invoke(String... args) {
server.proguardConf.prepare(true);
}
@Override
public void invoke(String... args) {
server.proguardConf.prepare(true);
}
}

View file

@ -7,23 +7,23 @@
public class RegenProguardDictCommand extends Command {
public RegenProguardDictCommand(LaunchServer server) {
super(server);
}
public RegenProguardDictCommand(LaunchServer server) {
super(server);
}
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getUsageDescription() {
return "Regenerates proguard dictonary";
}
@Override
public String getUsageDescription() {
return "Regenerates proguard dictonary";
}
@Override
public void invoke(String... args) throws IOException {
server.proguardConf.genWords(true);
}
@Override
public void invoke(String... args) throws IOException {
server.proguardConf.genWords(true);
}
}

View file

@ -8,23 +8,23 @@
public class RemoveMappingsProguardCommand extends Command {
public RemoveMappingsProguardCommand(LaunchServer server) {
super(server);
}
public RemoveMappingsProguardCommand(LaunchServer server) {
super(server);
}
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getUsageDescription() {
return "Removes proguard mappings (if you want to gen new mappings).";
}
@Override
public String getUsageDescription() {
return "Removes proguard mappings (if you want to gen new mappings).";
}
@Override
public void invoke(String... args) throws IOException {
Files.deleteIfExists(server.proguardConf.mappings);
}
@Override
public void invoke(String... args) throws IOException {
Files.deleteIfExists(server.proguardConf.mappings);
}
}

View file

@ -3,16 +3,14 @@
import ru.gravit.launchserver.LaunchServer;
import ru.gravit.launchserver.command.Command;
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 TestCommand(LaunchServer server) {
super(server);
}
NettyServerSocketHandler handler;
@Override
public String getArgsDescription() {
return null;
@ -25,11 +23,10 @@ public String getUsageDescription() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args,1);
if(handler == null)
handler = new NettyServerSocketHandler(server);
if(args[0].equals("start"))
{
verifyArgs(args, 1);
if (handler == null)
handler = new NettyServerSocketHandler(server);
if (args[0].equals("start")) {
handler.run();
}
}

View file

@ -48,11 +48,11 @@ private static String[] parse(CharSequence line) throws CommandException {
// Maybe we should read next argument?
if (end || !quoted && Character.isWhitespace(ch)) {
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)
if (wasQuoted || builder.length() > 0)
result.add(builder.toString());
result.add(builder.toString());
// Reset string builder
wasQuoted = false;
@ -68,7 +68,7 @@ private static String[] parse(CharSequence line) throws CommandException {
break;
case '\\': // All escapes, including spaces etc
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);
builder.append(next);
i++;
@ -101,7 +101,7 @@ protected CommandHandler(LaunchServer server) {
registerCommand("logConnections", new LogConnectionsCommand(server));
registerCommand("loadModule", new LoadModuleCommand(server));
registerCommand("modules", new ModulesCommand(server));
registerCommand("test",new TestCommand(server));
registerCommand("test", new TestCommand(server));
// Register sync commands
registerCommand("indexAsset", new IndexAssetCommand(server));
@ -151,7 +151,7 @@ public final void eval(String line, boolean bell) {
@LauncherAPI
public final void eval(String[] args, boolean bell) {
if (args.length == 0)
return;
return;
// Measure start time and invoke command
Instant startTime = Instant.now();
@ -164,7 +164,7 @@ public final void eval(String[] args, boolean bell) {
// Bell if invocation took > 1s
Instant endTime = Instant.now();
if (bell && Duration.between(startTime, endTime).getSeconds() >= 5)
try {
try {
bell();
} catch (IOException e) {
LogHelper.error(e);
@ -175,7 +175,7 @@ public final void eval(String[] args, boolean bell) {
public final Command lookup(String name) throws CommandException {
Command command = commands.get(name);
if (command == null)
throw new CommandException(String.format("Unknown command: '%s'", name));
throw new CommandException(String.format("Unknown command: '%s'", name));
return command;
}
@ -184,7 +184,7 @@ public final Command lookup(String name) throws CommandException {
private void readLoop() throws IOException {
for (String line = readLine(); line != null; line = readLine())
eval(line, true);
eval(line, true);
}
@LauncherAPI

View file

@ -48,6 +48,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
return super.visitFile(file, attrs);
}
}
public static final String INDEXES_DIR = "indexes";
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 outputAssetDir = server.updatesDir.resolve(outputAssetDirName);
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
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 outputAssetDir = server.updatesDir.resolve(outputAssetDirName);
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
LogHelper.subInfo("Creating unindexed asset dir: '%s'", outputAssetDirName);

View file

@ -19,7 +19,7 @@ public String getUsageDescription() {
}
@Override
public void invoke(String... args) throws Exception {
server.modulesManager.printModules();
public void invoke(String... args) {
server.modulesManager.printModules();
}
}

View file

@ -17,6 +17,7 @@ public final class LaunchServerPluginBridge implements Runnable, AutoCloseable {
* Err text.
*/
public static final String nonInitText = "Лаунчсервер не был полностью загружен";
static {
//SecurityHelper.verifyCertificates(LaunchServer.class);
JVMHelper.verifySystemProperties(LaunchServer.class, false);

View file

@ -19,9 +19,9 @@ public boolean onCommand(CommandSender sender, Command command, String alias, St
// Eval command
LaunchServerPluginBridge bridge = plugin.bridge;
if (bridge == null)
sender.sendMessage(ChatColor.RED + LaunchServerPluginBridge.nonInitText);
else
bridge.eval(args);
sender.sendMessage(ChatColor.RED + LaunchServerPluginBridge.nonInitText);
else
bridge.eval(args);
return true;
}
}

View file

@ -24,8 +24,8 @@ public void execute(CommandSender sender, String[] args) {
// Eval command
LaunchServerPluginBridge bridge = plugin.bridge;
if (bridge == null)
sender.sendMessage(NOT_INITIALIZED_MESSAGE);
else
bridge.eval(args);
sender.sendMessage(NOT_INITIALIZED_MESSAGE);
else
bridge.eval(args);
}
}

View file

@ -12,97 +12,102 @@
public class BuildHookManager {
@FunctionalInterface
public interface PostBuildHook
{
void build(BuildContext context);
}
@FunctionalInterface
public interface PreBuildHook
{
public interface PostBuildHook {
void build(BuildContext context);
}
@FunctionalInterface
public interface Transformer
{
public interface PreBuildHook {
void build(BuildContext context);
}
@FunctionalInterface
public interface Transformer {
byte[] transform(byte[] input, CharSequence classname);
}
private boolean BUILDRUNTIME;
private final Set<PostBuildHook> POST_HOOKS;
private final Set<PreBuildHook> PRE_HOOKS;
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 Map<String, byte[]> INCLUDE_CLASS;
public BuildHookManager() {
POST_HOOKS = new HashSet<>(4);
PRE_HOOKS = new HashSet<>(4);
CLASS_BLACKLIST = new HashSet<>(4);
MODULE_CLASS = new HashSet<>(4);
INCLUDE_CLASS = new HashMap<>(4);
POST_HOOKS = new HashSet<>(4);
PRE_HOOKS = new HashSet<>(4);
CLASS_BLACKLIST = new HashSet<>(4);
MODULE_CLASS = new HashSet<>(4);
INCLUDE_CLASS = new HashMap<>(4);
CLASS_TRANSFORMER = new HashSet<>(4);
BUILDRUNTIME = true;
autoRegisterIgnoredClass(AutogenConfig.class.getName());
autoRegisterIgnoredClass(AutogenConfig.class.getName());
registerIgnoredClass("META-INF/DEPENDENCIES");
registerIgnoredClass("META-INF/LICENSE");
registerIgnoredClass("META-INF/NOTICE");
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() {
return BUILDRUNTIME;
}
public byte[] classTransform(byte[] clazz, CharSequence classname)
{
return BUILDRUNTIME;
}
public byte[] classTransform(byte[] clazz, CharSequence classname) {
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;
}
public void registerIncludeClass(String classname, byte[] classdata) {
INCLUDE_CLASS.put(classname, classdata);
INCLUDE_CLASS.put(classname, classdata);
}
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);
}
public void postHook(BuildContext context)
{
for(PostBuildHook hook : POST_HOOKS) hook.build(context);
public void postHook(BuildContext context) {
for (PostBuildHook hook : POST_HOOKS) hook.build(context);
}
public void preHook(BuildContext context)
{
for(PreBuildHook hook : PRE_HOOKS) hook.build(context);
public void preHook(BuildContext context) {
for (PreBuildHook hook : PRE_HOOKS) hook.build(context);
}
public void registerAllClientModuleClass(JAConfigurator cfg)
{
for(String clazz : MODULE_CLASS) cfg.addModuleClass(clazz);
public void registerAllClientModuleClass(JAConfigurator cfg) {
for (String clazz : MODULE_CLASS) cfg.addModuleClass(clazz);
}
public void registerClassTransformer(Transformer transformer)
{
public void registerClassTransformer(Transformer transformer) {
CLASS_TRANSFORMER.add(transformer);
}
public void registerClientModuleClass(String clazz)
{
public void registerClientModuleClass(String clazz) {
MODULE_CLASS.add(clazz);
}
public void registerIgnoredClass(String clazz)
{
public void registerIgnoredClass(String clazz) {
CLASS_BLACKLIST.add(clazz);
}
public void registerPostHook(PostBuildHook hook)
{
public void registerPostHook(PostBuildHook hook) {
POST_HOOKS.add(hook);
}
public void registerPreHook(PreBuildHook hook)
{
public void registerPreHook(PreBuildHook hook) {
PRE_HOOKS.add(hook);
}
public void setBuildRuntime(boolean runtime) {
BUILDRUNTIME = runtime;
BUILDRUNTIME = runtime;
}
}

View file

@ -10,12 +10,13 @@
import ru.gravit.launchserver.modules.LaunchServerModuleContext;
public class ModulesManager extends SimpleModuleManager {
public ModulesManager(LaunchServer lsrv) {
modules = new ArrayList<>(1);
classloader = new PublicURLClassLoader(new URL[0], ClassLoader.getSystemClassLoader());
context = new LaunchServerModuleContext(lsrv, classloader);
}
private void registerCoreModule() {
load(new CoreModule());
}
public ModulesManager(LaunchServer lsrv) {
modules = new ArrayList<>(1);
classloader = new PublicURLClassLoader(new URL[0], ClassLoader.getSystemClassLoader());
context = new LaunchServerModuleContext(lsrv, classloader);
}
private void registerCoreModule() {
load(new CoreModule());
}
}

View file

@ -19,7 +19,7 @@ public boolean addClient(Client client) {
}
@Override
@LauncherAPI
@LauncherAPI
public void garbageCollection() {
long time = System.currentTimeMillis();
clientSet.removeIf(c -> c.timestamp + SESSION_TIMEOUT < time);
@ -28,14 +28,14 @@ public void garbageCollection() {
@LauncherAPI
public Client getClient(long session) {
for (Client c : clientSet)
if (c.session == session) return c;
if (c.session == session) return c;
return null;
}
@LauncherAPI
public Client getOrNewClient(long session) {
for (Client c : clientSet)
if (c.session == session) return c;
if (c.session == session) return c;
Client newClient = new Client(session);
clientSet.add(newClient);
return newClient;
@ -44,7 +44,7 @@ public Client getOrNewClient(long session) {
@LauncherAPI
public void updateClient(long session) {
for (Client c : clientSet)
if (c.session == session) {
if (c.session == session) {
c.up();
return;
}

View file

@ -6,39 +6,39 @@
import ru.gravit.launcher.modules.ModuleContext;
public class CoreModule implements Module {
@Override
public void close() {
// nothing to do
}
@Override
public void close() {
// nothing to do
}
@Override
public String getName() {
return "LaunchServer";
}
@Override
public String getName() {
return "LaunchServer";
}
@Override
public Version getVersion() {
return Launcher.getVersion();
}
@Override
public Version getVersion() {
return Launcher.getVersion();
}
@Override
public int getPriority() {
return 0;
}
@Override
public int getPriority() {
return 0;
}
@Override
public void init(ModuleContext context) {
// nothing to do
}
@Override
public void init(ModuleContext context) {
// nothing to do
}
@Override
public void postInit(ModuleContext context) {
// nothing to do
}
@Override
public void postInit(ModuleContext context) {
// nothing to do
}
@Override
public void preInit(ModuleContext context) {
// nothing to do
}
@Override
public void preInit(ModuleContext context) {
// nothing to do
}
}

View file

@ -8,11 +8,12 @@
public class LaunchServerModuleContext implements ModuleContext {
public final LaunchServer launchServer;
public final PublicURLClassLoader classloader;
public LaunchServerModuleContext(LaunchServer server, PublicURLClassLoader classloader)
{
public LaunchServerModuleContext(LaunchServer server, PublicURLClassLoader classloader) {
launchServer = server;
this.classloader = classloader;
}
@Override
public Type getType() {
return Type.LAUNCHSERVER;

View file

@ -6,9 +6,9 @@
public class SimpleModule implements Module {
@Override
public void close() {
// on stop
}
public void close() {
// on stop
}
@Override
public String getName() {
@ -17,7 +17,7 @@ public String getName() {
@Override
public Version getVersion() {
return new Version(1,0,0,0, Version.Type.UNKNOWN);
return new Version(1, 0, 0, 0, Version.Type.UNKNOWN);
}
@Override

View file

@ -28,13 +28,17 @@ public interface Factory<R> {
@LauncherAPI
Response newResponse(LaunchServer server, long id, HInput input, HOutput output, String ip);
}
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) {
return RESPONSES.get(type).newResponse(server, session, input, output, ip);
}
public static void registerResponse(int type, Factory<?> factory) {
RESPONSES.put(type, factory);
}
public static void registerResponses() {
registerResponse(RequestType.PING.getNumber(), PingResponse::new);
registerResponse(RequestType.AUTH.getNumber(), AuthResponse::new);
@ -50,6 +54,7 @@ public static void registerResponses() {
registerResponse(RequestType.UPDATE.getNumber(), UpdateResponse::new);
registerResponse(RequestType.PROFILES.getNumber(), ProfilesResponse::new);
}
@LauncherAPI
public static void requestError(String message) throws RequestException {
throw new RequestException(message);

View file

@ -68,10 +68,10 @@ public void reply() throws Exception {
return;
}
Collection<SignedObjectHolder<ClientProfile>> profiles = server.getProfiles();
for(SignedObjectHolder<ClientProfile> p : profiles)
if(p.object.getTitle().equals(client))
if(!p.object.isWhitelistContains(login))
throw new AuthException(server.config.whitelistRejectString);
for (SignedObjectHolder<ClientProfile> p : profiles)
if (p.object.getTitle().equals(client))
if (!p.object.isWhitelistContains(login))
throw new AuthException(server.config.whitelistRejectString);
server.config.hwidHandler.check(HWID.gen(hwid_hdd, hwid_bios, hwid_cpu), result.username);
} catch (AuthException | HWIDException e) {
requestError(e.getMessage());

View file

@ -43,6 +43,6 @@ public void reply() throws IOException {
// Write profile and UUID
output.writeBoolean(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
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();
output.writeLength(profiles.size(), 0);
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();
output.writeLength(profiles.size(), 0);
for (SignedObjectHolder<ClientProfile> profile : profiles) {
LogHelper.debug("Writted profile: %s",profile.object.getTitle());
LogHelper.debug("Writted profile: %s", profile.object.getTitle());
profile.write(output);
}
}

View file

@ -23,6 +23,6 @@ public void reply() throws Exception {
// Write all update dirs names
output.writeLength(updateDirs.size(), 0);
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
int length = input.readLength(actionsSlice.length);
for (int i = 0; i < length; i++)
actionsSlice[i] = new UpdateAction(input);
actionsSlice[i] = new UpdateAction(input);
// Perform actions
for (int i = 0; i < length; i++) {
@ -69,7 +69,7 @@ public void reply() throws IOException {
// Get hashed dir (for validation)
HashedEntry hSubdir = dirStack.getLast().getEntry(action.name);
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);
// Resolve dir
@ -81,7 +81,7 @@ public void reply() throws IOException {
// Get hashed file (for validation)
HashedEntry hFile = dirStack.getLast().getEntry(action.name);
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
Path file = dir.resolve(action.name);
@ -101,7 +101,7 @@ public void reply() throws IOException {
// Remove from hashed dir stack
dirStack.removeLast();
if (dirStack.isEmpty())
throw new IOException("Empty hDir stack");
throw new IOException("Empty hDir stack");
// Get parent
dir = dir.getParent();
@ -119,6 +119,6 @@ public void reply() throws IOException {
// So we've updated :)
if (fileOutput instanceof DeflaterOutputStream)
((DeflaterOutputStream) fileOutput).finish();
((DeflaterOutputStream) fileOutput).finish();
}
}

View file

@ -4,6 +4,7 @@ public class Client {
public long session;
public long timestamp;
public Client(long session) {
this.session = session;
timestamp = System.currentTimeMillis();

View file

@ -15,9 +15,6 @@
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
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.LauncherTrustManager;
import ru.gravit.launchserver.LaunchServer;
@ -55,8 +52,6 @@ public final class NettyServerSocketHandler implements Runnable, AutoCloseable {
@LauncherAPI
public volatile boolean logConnections = Boolean.getBoolean("launcher.logConnections");
// Instance
private final LaunchServer server;
private final AtomicReference<ServerSocket> serverSocket = new AtomicReference<>();
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 AtomicLong idCounter = new AtomicLong(0L);
private Set<Socket> sockets;
private Selector selector;
private ServerSocketChannel serverChannel;
private volatile Listener listener;
public NettyServerSocketHandler(LaunchServer server) {
this.server = server;
// Instance
LaunchServer server1 = server;
}
@Override
@ -86,10 +80,10 @@ public void close() {
}
public SSLContext SSLContextInit() throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, KeyManagementException, IOException, CertificateException {
TrustManager[] trustAllCerts = new TrustManager[] {
TrustManager[] trustAllCerts = new TrustManager[]{
new LauncherTrustManager()
};
KeyStore ks = LauncherKeyStore.getKeyStore("keystore","PSP1000");
KeyStore ks = LauncherKeyStore.getKeyStore("keystore", "PSP1000");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
@ -120,8 +114,8 @@ public void run() {
//System.setProperty( "javax.net.ssl.keyStore","keystore");
//System.setProperty( "javax.net.ssl.keyStorePassword","PSP1000");
try {
selector = Selector.open();
serverChannel = ServerSocketChannel.open();
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
} catch (IOException e) {
e.printStackTrace();
@ -138,7 +132,7 @@ public void run() {
.handler(new LoggingHandler(LogLevel.DEBUG))
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
public void initChannel(NioSocketChannel ch) throws Exception {
public void initChannel(NioSocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
//p.addLast(new LoggingHandler(LogLevel.INFO));
System.out.println("P!");
@ -198,7 +192,7 @@ public void initChannel(NioSocketChannel ch) throws Exception {
public void registerCustomResponse(String name, Response.Factory factory) {
VerifyHelper.verifyIDName(name);
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

View file

@ -26,6 +26,7 @@ public Handshake(int type, long session) {
this.session = session;
}
}
private final LaunchServer server;
private final Socket socket;
@ -45,18 +46,16 @@ private Handshake readHandshake(HInput input, HOutput output) throws IOException
// Verify magic number
int magicNumber = input.readInt();
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;
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;
legacy = true;
}
else if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY){
} else if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY) {
} else
throw new IOException("Invalid Handshake");
throw new IOException("Invalid Handshake");
// Verify key modulus
BigInteger keyModulus = input.readBigInteger(SecurityHelper.RSA_KEY_LENGTH + 1);
if (!legacy) {
@ -68,7 +67,7 @@ else if (magicNumber == Launcher.PROTOCOL_MAGIC_LEGACY){
throw new IOException(String.format("#%d Key modulus mismatch", session));
}
// Read request type
Integer type = input.readVarInt();
int type = input.readVarInt();
if (!server.serverSocketHandler.onHandshake(session, type)) {
output.writeBoolean(false);
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 {
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
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
public void run() {
if (!server.serverSocketHandler.logConnections)
LogHelper.debug("Connection from %s", IOHelper.getIP(socket.getRemoteSocketAddress()));
LogHelper.debug("Connection from %s", IOHelper.getIP(socket.getRemoteSocketAddress()));
// Process connection
boolean cancelled = false;
@ -121,7 +120,7 @@ public void run() {
} finally {
IOHelper.close(socket);
if (!cancelled)
server.serverSocketHandler.onDisconnect(savedError);
server.serverSocketHandler.onDisconnect(savedError);
}
}
}

View file

@ -28,6 +28,7 @@ public interface Listener {
@LauncherAPI
boolean onHandshake(long session, Integer type);
}
private static final ThreadFactory THREAD_FACTORY = r -> CommonHelper.newThread("Network Thread", true, r);
@LauncherAPI
@ -68,7 +69,7 @@ public void close() {
/*package*/ void onDisconnect(Exception e) {
if (listener != null)
listener.onDisconnect(e);
listener.onDisconnect(e);
}
/*package*/ boolean onHandshake(long session, Integer type) {
@ -80,7 +81,7 @@ public void run() {
LogHelper.info("Starting server socket thread");
try (ServerSocket serverSocket = new 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
serverSocket.setReuseAddress(true);
@ -96,7 +97,7 @@ public void run() {
// Invoke pre-connect listener
long id = idCounter.incrementAndGet();
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
threadPool.execute(new ResponseThread(server, id, socket, sessionManager));
@ -104,7 +105,7 @@ public void run() {
} catch (IOException e) {
// Ignore error after close/rebind
if (serverSocket.get() != null)
LogHelper.error(e);
LogHelper.error(e);
}
}

View file

@ -13,11 +13,13 @@
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void channelActive(ChannelHandlerContext ctx) {
LogHelper.debug("New client %s", IOHelper.getIP(ctx.channel().remoteAddress()));
channels.add(ctx.channel());
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
// ping and pong frames already handled
@ -26,6 +28,6 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) thr
long handshake = input.readLong();
long connection_flags = input.readLong();
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
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) {
// Handle a bad request.
if (!req.decoderResult().isSuccess()) {
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 {
TextureProvider provider = this.provider;
if (provider != null)
provider.close();
provider.close();
}
@Override

View file

@ -24,6 +24,7 @@ private static Texture getTexture(String url, boolean cloak) throws IOException
return null; // Simply not found
}
}
private static String getTextureURL(String url, UUID uuid, String username, String client) {
return CommonHelper.replace(url, "username", IOHelper.urlEncode(username),
"uuid", IOHelper.urlEncode(uuid.toString()), "hash", IOHelper.urlEncode(Launcher.toHash(uuid)),

View file

@ -10,16 +10,18 @@ public enum Type {
CAPE,
ELYTRA
}
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();
private static String baseName(String url) {
String name = url.substring(url.lastIndexOf('/') + 1);
// Remove index
int extensionIndex = name.lastIndexOf('.');
if (extensionIndex >= 0)
name = name.substring(0, extensionIndex);
name = name.substring(0, extensionIndex);
// We're done
return name;

View file

@ -28,7 +28,7 @@ public static CompatProfile checkServer(String username, String serverID) throws
@SuppressWarnings("unused")
public static boolean joinServer(String username, String accessToken, String serverID) throws Exception {
if (!ClientLauncher.isLaunched())
throw new IllegalStateException("Bad Login (Cheater)");
throw new IllegalStateException("Bad Login (Cheater)");
// Join server
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
CompatProfile[] resultProfiles = new CompatProfile[profiles.length];
for (int i = 0; i < profiles.length; i++)
resultProfiles[i] = CompatProfile.fromPlayerProfile(profiles[i]);
resultProfiles[i] = CompatProfile.fromPlayerProfile(profiles[i]);
// We're dones
return resultProfiles;

View file

@ -24,6 +24,7 @@ public static CompatProfile fromPlayerProfile(PlayerProfile profile) {
profile.cloak == null ? null : SecurityHelper.toHex(profile.cloak.digest)
);
}
// Instance
public final UUID uuid;
public final String uuidHash, username;
@ -44,13 +45,13 @@ public CompatProfile(UUID uuid, String username, String skinURL, String skinDige
public int countProperties() {
int count = 0;
if (skinURL != null)
count++;
count++;
if (skinDigest != null)
count++;
count++;
if (cloakURL != null)
count++;
count++;
if (cloakDigest != null)
count++;
count++;
return count;
}
}

View file

@ -34,7 +34,7 @@ public static String getSkinURL(String username) {
@SuppressWarnings("unused")
public static String joinServer(String username, String accessToken, String serverID) {
if (!ClientLauncher.isLaunched())
return "Bad Login (Cheater)";
return "Bad Login (Cheater)";
// Join server
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) {
LogHelper.debug("fillTextureProperties, Username: '%s'", profile.getName());
if (NO_TEXTURES)
return;
return;
// Fill textures map
PropertyMap properties = profile.getProperties();
@ -66,14 +66,14 @@ private static void getTexturesMojang(Map<Type, MinecraftProfileTexture> texture
// Fetch textures from textures JSON
for (Type type : MinecraftProfileTexture.PROFILE_TEXTURE_TYPES) {
if (textures.containsKey(type))
continue; // Overriden by launcher
continue; // Overriden by launcher
// Get texture from JSON
JsonElement textureJSON = texturesJSON.get(type.name());
if (textureJSON != null && textureJSON.isJsonObject()) {
JsonElement urlValue = textureJSON.getAsJsonObject().get("url");
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();
LogHelper.debug("fillProfileProperties, UUID: %s", uuid);
if (uuid == null)
return profile;
return profile;
// Make profile request
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 skinDigest = Iterables.getFirst(profile.getProperties().get(ClientLauncher.SKIN_DIGEST_PROPERTY), 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
Property cloakURL = Iterables.getFirst(profile.getProperties().get(ClientLauncher.CLOAK_URL_PROPERTY), null);
Property cloakDigest = Iterables.getFirst(profile.getProperties().get(ClientLauncher.CLOAK_DIGEST_PROPERTY), 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)
if (textures.size() != MinecraftProfileTexture.PROFILE_TEXTURE_COUNT) {
Property texturesMojang = Iterables.getFirst(profile.getProperties().get("textures"), 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 {
return hasJoinedServer(profile, serverID);
}
public YggdrasilAuthenticationService getAuthenticationService() {
return (YggdrasilAuthenticationService)super.getAuthenticationService();
return (YggdrasilAuthenticationService) super.getAuthenticationService();
}
@Override
public void joinServer(GameProfile profile, String accessToken, String serverID) throws AuthenticationException {
if (!ClientLauncher.isLaunched())
throw new AuthenticationException("Bad Login (Cheater)");
throw new AuthenticationException("Bad Login (Cheater)");
// Join server
String username = profile.getName();
@ -193,6 +195,6 @@ public void joinServer(GameProfile profile, String accessToken, String serverID)
// Verify is 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;
public class AvanguardStarter {
static class SecurityThread implements Runnable {
static class SecurityThread implements Runnable {
@Override
public void run() {
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;
private static Path handle(Path mustdiedll, String resource) {
@ -51,13 +52,13 @@ private static Path handle(Path mustdiedll, String resource) {
in.close();
if (IOHelper.exists(mustdiedll)) {
if (!matches(mustdiedll, orig))
transfer(orig, mustdiedll);
transfer(orig, mustdiedll);
} else
transfer(orig, mustdiedll);
transfer(orig, mustdiedll);
} catch (Exception e) {
if (e instanceof RuntimeException)
throw (RuntimeException) e;
throw new RuntimeException(e);
throw new RuntimeException(e);
}
return mustdiedll;
}
@ -107,8 +108,8 @@ public static void start(Path path1) throws IOException {
handle(path.resolve("Avanguard64.dll"), "Avanguard64.dll"),
handle(path.resolve(NAME + "32.exe"), "wrapper32.exe"),
handle(path.resolve(NAME + "64.exe"), "wrapper64.exe"));
HashedDir guard = new HashedDir(path,null,true,false);
try(DirWatcher dirWatcher = new DirWatcher(path, guard, null, false)){
HashedDir guard = new HashedDir(path, null, true, false);
try (DirWatcher dirWatcher = new DirWatcher(path, guard, null, false)) {
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();
LogHelper.debug("Launcher started in %dms", Duration.between(start, end).toMillis());
}
// Instance
private final AtomicBoolean started = new AtomicBoolean(false);
@ -175,7 +176,7 @@ public Object loadScript(String path) throws IOException, ScriptException {
URL url = Launcher.getResourceURL(path);
LogHelper.debug("Loading script: '%s'", 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();
Objects.requireNonNull(args, "args");
if (started.getAndSet(true))
throw new IllegalStateException("Launcher has been already started");
throw new IllegalStateException("Launcher has been already started");
Launcher.modulesManager.initModules();
// Load init.js script
loadScript(Launcher.API_SCRIPT_FILE);

View file

@ -62,10 +62,11 @@ private ClassPathFileVisitor(Collection<Path> result) {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
result.add(file);
result.add(file);
return super.visitFile(file, attrs);
}
}
public static final class Params extends StreamObject {
// Client paths
@LauncherAPI
@ -147,6 +148,7 @@ public void write(HOutput output) throws IOException {
output.writeVarInt(height);
}
}
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 boolean isUsingWrapper = true;
@ -215,7 +217,7 @@ private static void addClientArgs(Collection<String> args, ClientProfile profile
Collections.addAll(args, "--assetIndex", profile.getAssetIndex());
}
} else
Collections.addAll(args, "--session", params.accessToken);
Collections.addAll(args, "--session", params.accessToken);
// Add version and dirs args
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, "--resourcePackDir", params.clientDir.resolve(RESOURCEPACKS_DIR).toString());
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
if (params.autoEnter) {
@ -233,7 +235,7 @@ private static void addClientArgs(Collection<String> args, ClientProfile profile
// Add window size args
if (params.fullScreen)
Collections.addAll(args, "--fullscreen", Boolean.toString(true));
Collections.addAll(args, "--fullscreen", Boolean.toString(true));
if (params.width > 0 && params.height > 0) {
Collections.addAll(args, "--width", Integer.toString(params.width));
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, "--assetsDir", params.assetDir.toString());
}
@LauncherAPI
public static void checkJVMBitsAndVersion() {
if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) {
@ -264,6 +267,7 @@ public static void checkJVMBitsAndVersion() {
JOptionPane.showMessageDialog(null, error);
}
}
@LauncherAPI
public static boolean isLaunched() {
return LAUNCHED.get();
@ -281,9 +285,9 @@ private static void launch(ClientProfile profile, Params params) throws Throwabl
// Add client args
Collection<String> args = new LinkedList<>();
if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0)
addClientArgs(args, profile, params);
else
addClientLegacyArgs(args, profile, params);
addClientArgs(args, profile, params);
else
addClientLegacyArgs(args, profile, params);
Collections.addAll(args, profile.getClientArgs());
LogHelper.debug("Args: " + args);
// 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)
LogHelper.debug("Writing ClientLauncher params");
Path paramsFile = Files.createTempFile("ClientLauncherParams", ".bin");
CommonHelper.newThread("Client params writter",false,() ->
CommonHelper.newThread("Client params writter", false, () ->
{
try {
try (ServerSocket socket = new ServerSocket()) {
@ -333,13 +337,14 @@ public static Process launch(
// Resolve java bin and set permissions
LogHelper.debug("Resolving JVM binary");
//Path javaBin = IOHelper.resolveJavaBin(jvmDir);
checkJVMBitsAndVersion();
checkJVMBitsAndVersion();
// Fill CLI arguments
List<String> args = new LinkedList<>();
boolean wrapper = isUsingWrapper();
Path javaBin;
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");
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");
args.add(javaBin.toString());
args.add(MAGICAL_INTEL_OPTION);
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())));
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_VERSION.startsWith("10.")) {
LogHelper.debug("MustDie 10 fix is applied");
@ -363,10 +368,10 @@ public static Process launch(
StringBuilder classPathString = new StringBuilder(pathLauncher);
LinkedList<Path> classPath = resolveClassPathList(params.clientDir, profile.object.getClassPath());
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, "-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());
//if(wrapper)
//Collections.addAll(args, "-Djava.class.path=".concat(classPathString.toString())); // Add Class Path
@ -379,8 +384,8 @@ public static Process launch(
// Build client process
LogHelper.debug("Launching client instance");
ProcessBuilder builder = new ProcessBuilder(args);
if(wrapper)
builder.environment().put("JAVA_HOME", System.getProperty("java.home"));
if (wrapper)
builder.environment().put("JAVA_HOME", System.getProperty("java.home"));
//else
//builder.environment().put("CLASSPATH", classPathString.toString());
EnvHelper.addEnv(builder);
@ -427,8 +432,7 @@ public static void main(String... args) throws Throwable {
clientHDir = new SignedObjectHolder<>(input, publicKey, HashedDir::new);
}
}
} catch (IOException ex)
{
} catch (IOException ex) {
LogHelper.error(ex);
try (HInput input = new HInput(IOHelper.newInput(paramsFile))) {
params = new Params(input);
@ -509,12 +513,12 @@ public static void setProfile(ClientProfile profile) {
@LauncherAPI
public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
if (matcher != null)
matcher = matcher.verifyOnly();
matcher = matcher.verifyOnly();
// Hash directory and compare (ignore update-only matcher entries, it will break offline-mode)
HashedDir currentHDir = new HashedDir(dir, matcher, false, digest);
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() {

View file

@ -7,10 +7,11 @@
public class ClientModuleContext implements ModuleContext {
public final LauncherEngine engine;
ClientModuleContext(LauncherEngine engine)
{
ClientModuleContext(LauncherEngine engine) {
this.engine = engine;
}
@Override
public Type getType() {
return Type.CLIENT;

View file

@ -7,8 +7,7 @@
import ru.gravit.launcher.modules.SimpleModuleManager;
public class ClientModuleManager extends SimpleModuleManager {
public ClientModuleManager(LauncherEngine engine)
{
public ClientModuleManager(LauncherEngine engine) {
context = new ClientModuleContext(engine);
modules = new ArrayList<>();
}

View file

@ -9,6 +9,7 @@
@SuppressWarnings("AbstractClassNeverImplemented")
public abstract class JSApplication extends Application {
private static final AtomicReference<JSApplication> INSTANCE = new AtomicReference<>();
@LauncherAPI
public static JSApplication getInstance() {
return INSTANCE.get();

View file

@ -43,12 +43,14 @@ public boolean isOverfilled() {
return onlinePlayers >= maxPlayers;
}
}
// Constants
private static final String LEGACY_PING_HOST_MAGIC = "§1";
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 int PACKET_LENGTH = 65535;
private static String readUTF16String(HInput input) throws IOException {
int length = input.readUnsignedShort() << 1;
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.stream.write(s.getBytes(StandardCharsets.UTF_16BE));
}
// Instance
private final InetSocketAddress address;
private final ClientProfile.Version version;
@ -114,25 +117,25 @@ private Result legacyPing(HInput input, HOutput output, boolean mc16) throws IOE
// Raed kick (response) packet
int kickPacketID = input.readUnsignedByte();
if (kickPacketID != 0xFF)
throw new IOException("Illegal kick packet ID: " + kickPacketID);
throw new IOException("Illegal kick packet ID: " + kickPacketID);
// Read and parse response
String response = readUTF16String(input);
LogHelper.debug("Ping response (legacy): '%s'", response);
String[] splitted = LEGACY_PING_HOST_DELIMETER.split(response);
if (splitted.length != 6)
throw new IOException("Tokens count mismatch");
throw new IOException("Tokens count mismatch");
// Verify all parts
String magic = splitted[0];
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]);
if (protocol != version.protocol)
throw new IOException("Protocol mismatch: " + protocol);
throw new IOException("Protocol mismatch: " + protocol);
String clientVersion = splitted[2];
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]),
VerifyHelper.NOT_NEGATIVE, "onlinePlayers can't be < 0");
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)
int 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
String response;
@ -175,7 +178,7 @@ private Result modernPing(HInput input, HOutput output) throws IOException {
try (HInput packetInput = new HInput(statusPacket)) {
int statusPacketID = packetInput.readVarInt();
if (statusPacketID != 0x0)
throw new IOException("Illegal status packet ID: " + statusPacketID);
throw new IOException("Illegal status packet ID: " + statusPacketID);
response = packetInput.readString(PACKET_LENGTH);
LogHelper.debug("Ping response (modern): '%s'", response);
}
@ -209,9 +212,9 @@ public Result ping() throws IOException {
// Verify is result available
if (cache == null) {
if (cacheException instanceof IOException)
throw (IOException) cacheException;
throw (IOException) cacheException;
if (cacheException instanceof IllegalArgumentException)
throw (IllegalArgumentException) cacheException;
throw (IllegalArgumentException) cacheException;
cacheException = new IOException("Unavailable");
throw (IOException) cacheException;
}

View file

@ -18,22 +18,22 @@
@LauncherAPI
abstract class ProgressCircleIndicator extends ProgressIndicator {
@LauncherAPI
@LauncherAPI
public ProgressCircleIndicator() {
this.getStylesheets().add(ProgressCircleIndicator.class.getResource("/runtime/launcher/overlay/update/circleprogress.css").toExternalForm());
}
@LauncherAPI
@LauncherAPI
public final void setInnerCircleRadius(int value) {
innerCircleRadiusProperty().set(value);
}
@LauncherAPI
@LauncherAPI
public final DoubleProperty innerCircleRadiusProperty() {
return innerCircleRadius;
}
@LauncherAPI
@LauncherAPI
public final double getInnerCircleRadius() {
return innerCircleRadiusProperty().get();
}
@ -68,13 +68,14 @@ public boolean isSettable(ProgressCircleIndicator n) {
}
@SuppressWarnings("unchecked")
@Override
@Override
public StyleableProperty<Number> getStyleableProperty(ProgressCircleIndicator n) {
return (StyleableProperty<Number>) n.innerCircleRadiusProperty();
}
};
public static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
static {
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Control.getClassCssMetaData());
styleables.add(INNER_CIRCLE_RADIUS);
@ -83,9 +84,6 @@ public StyleableProperty<Number> getStyleableProperty(ProgressCircleIndicator n)
}
@LauncherAPI
/**
* @return The CssMetaData associated with this class, which may include the CssMetaData of its super classes.
*/
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return StyleableProperties.STYLEABLES;
}

View file

@ -75,13 +75,14 @@ public boolean isSettable(RingProgressIndicator n) {
}
@SuppressWarnings("unchecked")
@Override
@Override
public StyleableProperty<Number> getStyleableProperty(RingProgressIndicator n) {
return (StyleableProperty<Number>) n.ringWidth;
}
};
public static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
static {
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(Control.getClassCssMetaData());
styleables.addAll(getClassCssMetaData());
@ -93,6 +94,6 @@ public StyleableProperty<Number> getStyleableProperty(RingProgressIndicator n) {
@LauncherAPI
@Override
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.
*
* @author Andrea Vacondio
*
*/
@LauncherAPI
public class RingProgressIndicatorSkin implements Skin<RingProgressIndicator> {
@ -29,6 +28,7 @@ public class RingProgressIndicatorSkin implements Skin<RingProgressIndicator> {
private final StackPane container = new StackPane();
private final Arc fillerArc = new Arc();
private final RotateTransition transition = new RotateTransition(Duration.millis(2000), fillerArc);
@LauncherAPI
public RingProgressIndicatorSkin(final RingProgressIndicator indicator) {
this.indicator = indicator;
@ -108,16 +108,19 @@ private void initIndeterminate(boolean newVal) {
transition.stop();
}
}
@LauncherAPI
@Override
public RingProgressIndicator getSkinnable() {
return indicator;
}
@LauncherAPI
@Override
public Node getNode() {
return container;
}
@LauncherAPI
@Override
public void dispose() {

View file

@ -7,7 +7,6 @@
import com.sun.javafx.collections.NonIterableChange;
import com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList;
import javafx.beans.InvalidationListener;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ListChangeListener;
@ -15,222 +14,240 @@
import ru.gravit.launcher.LauncherAPI;
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 ReadOnlyUnbackedObservableList<Integer> checkedIndicesList;
private final ReadOnlyUnbackedObservableList<T> checkedItemsList;
private final BitSet checkedIndices;
private final ReadOnlyUnbackedObservableList<Integer> checkedIndicesList;
private final ReadOnlyUnbackedObservableList<T> checkedItemsList;
CheckBitSetModelBase(final Map<T, BooleanProperty> itemBooleanMap) {
this.itemBooleanMap = itemBooleanMap;
CheckBitSetModelBase(final Map<T, BooleanProperty> itemBooleanMap) {
this.itemBooleanMap = itemBooleanMap;
this.checkedIndices = new BitSet();
this.checkedIndices = new BitSet();
this.checkedIndicesList = new ReadOnlyUnbackedObservableList<Integer>() {
@Override
public boolean contains(Object o) {
if (o instanceof Number) {
Number n = (Number) o;
int index = n.intValue();
this.checkedIndicesList = new ReadOnlyUnbackedObservableList<Integer>() {
@Override
public boolean contains(Object o) {
if (o instanceof Number) {
Number n = (Number) o;
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
public Integer get(int index) {
if (index < 0 || index >= getItemCount())
return -1;
@Override
public Integer get(int index) {
if (index < 0 || index >= getItemCount())
return -1;
for (int pos = 0, val = checkedIndices.nextSetBit(0); val >= 0
|| pos == index; pos++, val = checkedIndices.nextSetBit(val + 1))
if (pos == index)
return val;
for (int pos = 0, val = checkedIndices.nextSetBit(0); val >= 0
|| pos == index; pos++, val = checkedIndices.nextSetBit(val + 1))
if (pos == index)
return val;
return -1;
}
return -1;
}
@Override
public int size() {
return checkedIndices.cardinality();
}
};
@Override
public int size() {
return checkedIndices.cardinality();
}
};
this.checkedItemsList = new ReadOnlyUnbackedObservableList<T>() {
@Override
public T get(int i) {
int pos = checkedIndicesList.get(i);
if (pos < 0 || pos >= getItemCount())
return null;
return getItem(pos);
}
this.checkedItemsList = new ReadOnlyUnbackedObservableList<T>() {
@Override
public T get(int i) {
int pos = checkedIndicesList.get(i);
if (pos < 0 || pos >= getItemCount())
return null;
return getItem(pos);
}
@Override
public int size() {
return checkedIndices.cardinality();
}
};
@Override
public int size() {
return checkedIndices.cardinality();
}
};
final MappingChange.Map<Integer, T> map = this::getItem;
final MappingChange.Map<Integer, T> map = this::getItem;
checkedIndicesList.addListener((ListChangeListener<Integer>) c -> {
boolean hasRealChangeOccurred = false;
while (c.next() && !hasRealChangeOccurred)
hasRealChangeOccurred = c.wasAdded() || c.wasRemoved();
checkedIndicesList.addListener((ListChangeListener<Integer>) c -> {
boolean hasRealChangeOccurred = false;
while (c.next() && !hasRealChangeOccurred)
hasRealChangeOccurred = c.wasAdded() || c.wasRemoved();
if (hasRealChangeOccurred) {
c.reset();
checkedItemsList.callObservers(new MappingChange<>(c, map, checkedItemsList));
}
c.reset();
});
getCheckedItems().addListener((ListChangeListener<T>) c -> {
while (c.next()) {
if (c.wasAdded())
for (T item : c.getAddedSubList()) {
BooleanProperty p = getItemBooleanProperty(item);
if (p != null)
p.set(true);
}
if (hasRealChangeOccurred) {
c.reset();
checkedItemsList.callObservers(new MappingChange<>(c, map, checkedItemsList));
}
c.reset();
});
getCheckedItems().addListener((ListChangeListener<T>) c -> {
while (c.next()) {
if (c.wasAdded())
for (T item : c.getAddedSubList()) {
BooleanProperty p = getItemBooleanProperty(item);
if (p != null)
p.set(true);
}
if (c.wasRemoved())
for (T item : c.getRemoved()) {
BooleanProperty p = getItemBooleanProperty(item);
if (p != null)
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);
if (c.wasRemoved())
for (T item : c.getRemoved()) {
BooleanProperty p = getItemBooleanProperty(item);
if (p != null)
p.set(false);
}
}
});
}
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 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));
}
@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);
@Override
public void check(T item) {
int index = getItemIndex(item);
check(index);
}
final BooleanProperty booleanProperty = new SimpleBooleanProperty(item, "selected", false); //$NON-NLS-1$
itemBooleanMap.put(item, booleanProperty);
@LauncherAPI
@Override
public void checkAll() {
for (int i = 0; i < getItemCount(); i++)
check(i);
}
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));
}
});
}
}
@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);
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;
public class CheckComboBox<T> extends ControlsFXControl {
@LauncherAPI
private static class CheckComboBoxBitSetCheckModel<T> extends CheckBitSetModelBase<T> {
@LauncherAPI
private final ObservableList<T> items;
@LauncherAPI
private static class CheckComboBoxBitSetCheckModel<T> extends CheckBitSetModelBase<T> {
@LauncherAPI
private final ObservableList<T> items;
@LauncherAPI
CheckComboBoxBitSetCheckModel(final ObservableList<T> items, final Map<T, BooleanProperty> itemBooleanMap) {
super(itemBooleanMap);
@LauncherAPI
CheckComboBoxBitSetCheckModel(final ObservableList<T> items, final Map<T, BooleanProperty> itemBooleanMap) {
super(itemBooleanMap);
this.items = items;
this.items.addListener((ListChangeListener<T>) c -> updateMap());
this.items = items;
this.items.addListener((ListChangeListener<T>) c -> updateMap());
updateMap();
}
updateMap();
}
@LauncherAPI
@Override
public T getItem(int index) {
return items.get(index);
}
@LauncherAPI
@Override
public T getItem(int index) {
return items.get(index);
}
@LauncherAPI
@Override
public int getItemCount() {
return items.size();
}
@LauncherAPI
@Override
public int getItemCount() {
return items.size();
}
@LauncherAPI
@Override
public int getItemIndex(T item) {
return items.indexOf(item);
}
}
private final ObservableList<T> items;
private final Map<T, BooleanProperty> itemBooleanMap;
@LauncherAPI
@Override
public int getItemIndex(T item) {
return items.indexOf(item);
}
}
private CheckComboBoxSkin<T> checkComboBoxSkin;
private final ObservableList<T> items;
private final Map<T, BooleanProperty> itemBooleanMap;
@LauncherAPI
private ObjectProperty<IndexedCheckModel<T>> checkModel = new SimpleObjectProperty<>(this, "checkModel");
private CheckComboBoxSkin<T> checkComboBoxSkin;
@LauncherAPI
private ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>(this,
"converter");
@LauncherAPI
private ObjectProperty<IndexedCheckModel<T>> checkModel = new SimpleObjectProperty<>(this, "checkModel");
@LauncherAPI
private StringProperty title = new SimpleStringProperty(null);
@LauncherAPI
private ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>(this,
"converter");
public CheckComboBox() {
this(null);
}
@LauncherAPI
private StringProperty title = new SimpleStringProperty(null);
public CheckComboBox(final ObservableList<T> items) {
final int initialSize = items == null ? 32 : items.size();
public CheckComboBox() {
this(null);
}
this.itemBooleanMap = new HashMap<>(initialSize);
this.items = items == null ? FXCollections.observableArrayList() : items;
setCheckModel(new CheckComboBoxBitSetCheckModel<>(this.items, itemBooleanMap));
}
public CheckComboBox(final ObservableList<T> items) {
final int initialSize = items == null ? 32 : items.size();
@LauncherAPI
public final ObjectProperty<IndexedCheckModel<T>> checkModelProperty() {
return checkModel;
}
this.itemBooleanMap = new HashMap<>(initialSize);
this.items = items == null ? FXCollections.observableArrayList() : items;
setCheckModel(new CheckComboBoxBitSetCheckModel<>(this.items, itemBooleanMap));
}
@LauncherAPI
public final ObjectProperty<StringConverter<T>> converterProperty() {
return converter;
}
@LauncherAPI
public final ObjectProperty<IndexedCheckModel<T>> checkModelProperty() {
return checkModel;
}
@Override
protected Skin<?> createDefaultSkin() {
checkComboBoxSkin = new CheckComboBoxSkin<>(this);
return checkComboBoxSkin;
}
@LauncherAPI
public final ObjectProperty<StringConverter<T>> converterProperty() {
return converter;
}
@LauncherAPI
public final IndexedCheckModel<T> getCheckModel() {
return checkModel == null ? null : checkModel.get();
}
@Override
protected Skin<?> createDefaultSkin() {
checkComboBoxSkin = new CheckComboBoxSkin<>(this);
return checkComboBoxSkin;
}
@LauncherAPI
public final StringConverter<T> getConverter() {
return converterProperty().get();
}
@LauncherAPI
public final IndexedCheckModel<T> getCheckModel() {
return checkModel == null ? null : checkModel.get();
}
@LauncherAPI
public BooleanProperty getItemBooleanProperty(int index) {
if (index < 0 || index >= items.size())
return null;
return getItemBooleanProperty(getItems().get(index));
}
@LauncherAPI
public final StringConverter<T> getConverter() {
return converterProperty().get();
}
@LauncherAPI
public BooleanProperty getItemBooleanProperty(T item) {
return itemBooleanMap.get(item);
}
@LauncherAPI
public BooleanProperty getItemBooleanProperty(int index) {
if (index < 0 || index >= items.size())
return null;
return getItemBooleanProperty(getItems().get(index));
}
@LauncherAPI
public ObservableList<T> getItems() {
return items;
}
@LauncherAPI
public BooleanProperty getItemBooleanProperty(T item) {
return itemBooleanMap.get(item);
}
@LauncherAPI
public final String getTitle() {
return title.getValue();
}
@LauncherAPI
public ObservableList<T> getItems() {
return items;
}
@LauncherAPI
public void hide() {
if (checkComboBoxSkin != null)
checkComboBoxSkin.hide();
}
@LauncherAPI
public final String getTitle() {
return title.getValue();
}
@LauncherAPI
public final void setCheckModel(IndexedCheckModel<T> value) {
checkModelProperty().set(value);
}
@LauncherAPI
public void hide() {
if (checkComboBoxSkin != null)
checkComboBoxSkin.hide();
}
@LauncherAPI
public final void setConverter(StringConverter<T> value) {
converterProperty().set(value);
}
@LauncherAPI
public final void setCheckModel(IndexedCheckModel<T> value) {
checkModelProperty().set(value);
}
@LauncherAPI
public final void setTitle(String value) {
title.setValue(value);
}
@LauncherAPI
public final void setConverter(StringConverter<T> value) {
converterProperty().set(value);
}
@LauncherAPI
public void show() {
if (checkComboBoxSkin != null)
checkComboBoxSkin.show();
}
@LauncherAPI
public final void setTitle(String value) {
title.setValue(value);
}
@LauncherAPI
public final StringProperty titleProperty() {
return title;
}
@LauncherAPI
public void show() {
if (checkComboBoxSkin != null)
checkComboBoxSkin.show();
}
@LauncherAPI
public final StringProperty titleProperty() {
return title;
}
}

View file

@ -4,30 +4,30 @@
import ru.gravit.launcher.LauncherAPI;
public interface CheckModel<T> {
@LauncherAPI
void check(T item);
@LauncherAPI
void check(T item);
@LauncherAPI
void checkAll();
@LauncherAPI
void checkAll();
@LauncherAPI
void clearCheck(T item);
@LauncherAPI
void clearCheck(T item);
@LauncherAPI
void clearChecks();
@LauncherAPI
void clearChecks();
@LauncherAPI
ObservableList<T> getCheckedItems();
@LauncherAPI
ObservableList<T> getCheckedItems();
@LauncherAPI
int getItemCount();
@LauncherAPI
int getItemCount();
@LauncherAPI
boolean isChecked(T item);
@LauncherAPI
boolean isChecked(T item);
@LauncherAPI
boolean isEmpty();
@LauncherAPI
boolean isEmpty();
@LauncherAPI
void toggleCheckState(T item);
@LauncherAPI
void toggleCheckState(T item);
}

View file

@ -5,18 +5,18 @@
abstract class ControlsFXControl extends Control {
private String stylesheet;
private String stylesheet;
public ControlsFXControl() {
public ControlsFXControl() {
}
}
@LauncherAPI
protected final String getUserAgentStylesheet(Class<?> clazz, String fileName) {
@LauncherAPI
protected final String getUserAgentStylesheet(Class<?> clazz, String fileName) {
if (stylesheet == null)
stylesheet = clazz.getResource(fileName).toExternalForm();
if (stylesheet == null)
stylesheet = clazz.getResource(fileName).toExternalForm();
return stylesheet;
}
return stylesheet;
}
}

View file

@ -4,28 +4,28 @@
import ru.gravit.launcher.LauncherAPI;
public interface IndexedCheckModel<T> extends CheckModel<T> {
@LauncherAPI
@LauncherAPI
void check(int index);
@LauncherAPI
@LauncherAPI
void checkIndices(int... indices);
@LauncherAPI
@LauncherAPI
void clearCheck(int index);
@LauncherAPI
@LauncherAPI
ObservableList<Integer> getCheckedIndices();
@LauncherAPI
@LauncherAPI
T getItem(int index);
@LauncherAPI
@LauncherAPI
int getItemIndex(T item);
@LauncherAPI
@LauncherAPI
boolean isChecked(int index);
@LauncherAPI
@LauncherAPI
void toggleCheckState(int index);
}

View file

@ -21,160 +21,157 @@
public class CheckComboBoxSkin<T> extends BehaviorSkinBase<CheckComboBox<T>, BehaviorBase<CheckComboBox<T>>> {
private final ComboBox<T> comboBox;
private final ListCell<T> buttonCell;
private final ComboBox<T> comboBox;
private final ListCell<T> buttonCell;
private final CheckComboBox<T> control;
private final ObservableList<T> items;
private final ReadOnlyUnbackedObservableList<Integer> selectedIndices;
private final ReadOnlyUnbackedObservableList<T> selectedItems;
private final CheckComboBox<T> control;
private final ReadOnlyUnbackedObservableList<T> selectedItems;
@SuppressWarnings("unchecked")
public CheckComboBoxSkin(final CheckComboBox<T> control) {
super(control, new BehaviorBase<>(control, Collections.emptyList()));
@SuppressWarnings("unchecked")
public CheckComboBoxSkin(final CheckComboBox<T> control) {
super(control, new BehaviorBase<>(control, Collections.emptyList()));
this.control = control;
this.items = control.getItems();
this.control = control;
ObservableList<T> items = control.getItems();
selectedIndices = (ReadOnlyUnbackedObservableList<Integer>) control.getCheckModel().getCheckedIndices();
selectedItems = (ReadOnlyUnbackedObservableList<T>) control.getCheckModel().getCheckedItems();
ReadOnlyUnbackedObservableList<Integer> selectedIndices = (ReadOnlyUnbackedObservableList<Integer>) control.getCheckModel().getCheckedIndices();
selectedItems = (ReadOnlyUnbackedObservableList<T>) control.getCheckModel().getCheckedItems();
comboBox = new ComboBox<T>(items) {
@Override
protected javafx.scene.control.Skin<?> createDefaultSkin() {
return createComboBoxListViewSkin(this);
}
};
comboBox.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
comboBox = new ComboBox<T>(items) {
@Override
protected javafx.scene.control.Skin<?> createDefaultSkin() {
return createComboBoxListViewSkin(this);
}
};
comboBox.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
comboBox.setCellFactory(listView -> {
CheckBoxListCell<T> result = new CheckBoxListCell<>(control::getItemBooleanProperty);
result.focusedProperty().addListener((o, ov, nv) -> {
if (nv)
result.getParent().requestFocus();
});
result.setOnMouseClicked(e -> {
T item = result.getItem();
if (control.getCheckModel().isChecked(item))
control.getCheckModel().clearCheck(item);
else
control.getCheckModel().check(item);
});
result.converterProperty().bind(control.converterProperty());
return result;
});
comboBox.setCellFactory(listView -> {
CheckBoxListCell<T> result = new CheckBoxListCell<>(control::getItemBooleanProperty);
result.focusedProperty().addListener((o, ov, nv) -> {
if (nv)
result.getParent().requestFocus();
});
result.setOnMouseClicked(e -> {
T item = result.getItem();
if (control.getCheckModel().isChecked(item))
control.getCheckModel().clearCheck(item);
else
control.getCheckModel().check(item);
});
result.converterProperty().bind(control.converterProperty());
return result;
});
buttonCell = new ListCell<T>() {
@Override
protected void updateItem(T item, boolean empty) {
setText(getTextString());
}
};
comboBox.setButtonCell(buttonCell);
comboBox.setValue((T) getTextString());
buttonCell = new ListCell<T>() {
@Override
protected void updateItem(T item, boolean empty) {
setText(getTextString());
}
};
comboBox.setButtonCell(buttonCell);
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
private String buildString() {
final StringBuilder sb = new StringBuilder();
for (int i = 0, max = selectedItems.size(); i < max; i++) {
T item = selectedItems.get(i);
if (control.getConverter() == null)
sb.append(item);
else
sb.append(control.getConverter().toString(item));
if (i < max - 1)
sb.append(", "); //$NON-NLS-1$
}
return sb.toString();
}
@LauncherAPI
private String buildString() {
final StringBuilder sb = new StringBuilder();
for (int i = 0, max = selectedItems.size(); i < max; i++) {
T item = selectedItems.get(i);
if (control.getConverter() == null)
sb.append(item);
else
sb.append(control.getConverter().toString(item));
if (i < max - 1)
sb.append(", "); //$NON-NLS-1$
}
return sb.toString();
}
@LauncherAPI
@Override
protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset,
double leftInset) {
return getSkinnable().prefHeight(width);
}
@LauncherAPI
@Override
protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset,
double leftInset) {
return getSkinnable().prefHeight(width);
}
@LauncherAPI
@Override
protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset,
double leftInset) {
return getSkinnable().prefWidth(height);
}
@LauncherAPI
@Override
protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset,
double leftInset) {
return getSkinnable().prefWidth(height);
}
@LauncherAPI
@Override
protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset,
double leftInset) {
return comboBox.minHeight(width);
}
@LauncherAPI
@Override
protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset,
double leftInset) {
return comboBox.minHeight(width);
}
@LauncherAPI
@Override
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset,
double leftInset) {
return comboBox.minWidth(height);
}
@LauncherAPI
@Override
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset,
double leftInset) {
return comboBox.minWidth(height);
}
@LauncherAPI
@Override
protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset,
double leftInset) {
return comboBox.prefHeight(width);
}
@LauncherAPI
@Override
protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset,
double leftInset) {
return comboBox.prefHeight(width);
}
@LauncherAPI
@Override
protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset,
double leftInset) {
return comboBox.prefWidth(height);
}
@LauncherAPI
@Override
protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset,
double leftInset) {
return comboBox.prefWidth(height);
}
@LauncherAPI
private Skin<?> createComboBoxListViewSkin(ComboBox<T> comboBox) {
final ComboBoxListViewSkin<T> comboBoxListViewSkin = new ComboBoxListViewSkin<T>(comboBox) {
@Override
protected boolean isHideOnClickEnabled() {
return false;
}
};
@SuppressWarnings("unchecked")
final ListView<T> listView = (ListView<T>) comboBoxListViewSkin.getPopupContent();
listView.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.SPACE) {
T item = listView.getSelectionModel().getSelectedItem();
if (item != null) {
final IndexedCheckModel<T> checkModel = control.getCheckModel();
if (checkModel != null)
checkModel.toggleCheckState(item);
}
} else if (e.getCode() == KeyCode.ESCAPE)
hide();
});
return comboBoxListViewSkin;
}
@LauncherAPI
private Skin<?> createComboBoxListViewSkin(ComboBox<T> comboBox) {
final ComboBoxListViewSkin<T> comboBoxListViewSkin = new ComboBoxListViewSkin<T>(comboBox) {
@Override
protected boolean isHideOnClickEnabled() {
return false;
}
};
@SuppressWarnings("unchecked") final ListView<T> listView = (ListView<T>) comboBoxListViewSkin.getPopupContent();
listView.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.SPACE) {
T item = listView.getSelectionModel().getSelectedItem();
if (item != null) {
final IndexedCheckModel<T> checkModel = control.getCheckModel();
if (checkModel != null)
checkModel.toggleCheckState(item);
}
} else if (e.getCode() == KeyCode.ESCAPE)
hide();
});
return comboBoxListViewSkin;
}
@LauncherAPI
protected String getTextString() {
@LauncherAPI
protected String getTextString() {
if (control.getTitle() != null)
return control.getTitle();
return buildString();
if (control.getTitle() != null)
return control.getTitle();
return buildString();
}
}
@LauncherAPI
public void hide() {
comboBox.hide();
}
@LauncherAPI
public void hide() {
comboBox.hide();
}
@LauncherAPI
public void show() {
comboBox.show();
}
@LauncherAPI
public void show() {
comboBox.show();
}
}

View file

@ -29,7 +29,7 @@ public Integer getType() {
protected Void requestDo(HInput input, HOutput output) throws IOException {
byte pong = (byte) input.readUnsignedByte();
if (pong != SerializeLimits.EXPECTED_BYTE)
throw new IOException("Illegal ping response: " + pong);
throw new IOException("Illegal ping response: " + pong);
return null;
}
}

View file

@ -14,10 +14,12 @@
public abstract class Request<R> {
private static final long session = SecurityHelper.secureRandom.nextLong();
@LauncherAPI
public static void requestError(String message) throws RequestException {
throw new RequestException(message);
}
@LauncherAPI
protected final LauncherConfig config;
@ -40,14 +42,14 @@ protected Request(LauncherConfig config) {
protected final void readError(HInput input) throws IOException {
String error = input.readString(0);
if (!error.isEmpty())
requestError(error);
requestError(error);
}
@LauncherAPI
@SuppressWarnings("DesignForExtension")
public R request() throws Exception {
if (!started.compareAndSet(false, true))
throw new IllegalStateException("Request already started");
throw new IllegalStateException("Request already started");
// Make request to LaunchServer
try (Socket socket = IOHelper.newSocket()) {
@ -73,7 +75,7 @@ private void writeHandshake(HInput input, HOutput output) throws IOException {
// Verify is accepted
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;
}
}
private final String login;
private final byte[] encryptedPassword;

View file

@ -46,6 +46,7 @@ public byte[] getSign() {
return sign.clone();
}
}
@LauncherAPI
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);
args.add(IOHelper.resolveJavaBin(null).toString());
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)
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(BINARY_PATH.toString());
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);
List<SignedObjectHolder<ClientProfile>> profiles = new ArrayList<>(count);
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 new Result(null, sign, profiles);

View file

@ -38,6 +38,7 @@ public ProfilesRequest(LauncherConfig config) {
public Integer getType() {
return RequestType.PROFILES.getNumber();
}
@Override
protected Result requestDo(HInput input, HOutput output) throws Exception {
output.writeBoolean(true);
@ -47,7 +48,7 @@ protected Result requestDo(HInput input, HOutput output) throws Exception {
int count = input.readLength(0);
List<SignedObjectHolder<ClientProfile>> profiles = new ArrayList<>(count);
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 new Result(profiles);

View file

@ -36,7 +36,7 @@ protected Set<String> requestDo(HInput input, HOutput output) throws IOException
// Read all update dirs names
Set<String> result = new HashSet<>(count);
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
return Collections.unmodifiableSet(result);

View file

@ -41,6 +41,7 @@ public static final class State {
public interface Callback {
void call(State state);
}
@LauncherAPI
public final long fileDownloaded;
@LauncherAPI
@ -70,7 +71,7 @@ public State(String filePath, long fileDownloaded, long fileSize, long totalDown
public double getBps() {
long seconds = duration.getSeconds();
if (seconds == 0)
return -1.0D; // Otherwise will throw /0 exception
return -1.0D; // Otherwise will throw /0 exception
return totalDownloaded / (double) seconds;
}
@ -78,7 +79,7 @@ public double getBps() {
public Duration getEstimatedTime() {
double bps = getBps();
if (bps <= 0.0D)
return null; // Otherwise will throw /0 exception
return null; // Otherwise will throw /0 exception
return Duration.ofSeconds((long) (getTotalRemaining() / bps));
}
@ -95,7 +96,7 @@ public double getFileDownloadedMiB() {
@LauncherAPI
public double getFileDownloadedPart() {
if (fileSize == 0)
return 0.0D;
return 0.0D;
return (double) fileDownloaded / fileSize;
}
@ -137,7 +138,7 @@ public double getTotalDownloadedMiB() {
@LauncherAPI
public double getTotalDownloadedPart() {
if (totalSize == 0)
return 0.0D;
return 0.0D;
return (double) totalDownloaded / totalSize;
}
@ -166,6 +167,7 @@ public double getTotalSizeMiB() {
return getTotalSizeKiB() / 1024.0D;
}
}
private static void fillActionsQueue(Queue<UpdateAction> queue, HashedDir mismatch) {
for (Entry<String, HashedEntry> mapEntry : mismatch.map().entrySet()) {
String name = mapEntry.getKey();
@ -185,6 +187,7 @@ private static void fillActionsQueue(Queue<UpdateAction> queue, HashedDir mismat
}
}
}
// Instance
private final String dirName;
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 length = input.read(bytes, 0, remaining);
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
fileOutput.write(bytes, 0, length);
if (digest != null)
digest.update(bytes, 0, length);
digest.update(bytes, 0, length);
// Update state
downloaded += length;
@ -275,7 +278,7 @@ private void downloadFile(Path file, HashedFile hFile, InputStream input) throws
if (digest != null) {
byte[] digestBytes = digest.digest();
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:
Path targetFile = currentDir.resolve(action.name);
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);
break;
case CD_BACK:
@ -368,7 +371,7 @@ public void setStateCallback(Callback callback) {
private void updateState(String filePath, long fileDownloaded, long fileSize) {
if (stateCallback != null)
stateCallback.call(new State(filePath, fileDownloaded, fileSize,
stateCallback.call(new State(filePath, fileDownloaded, fileSize,
totalDownloaded, totalSize, Duration.between(startTime, Instant.now())));
}
}

Some files were not shown because too many files have changed in this diff Show more