mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-22 16:41:46 +03:00
IDEA Refractoring
This commit is contained in:
parent
78766d0c5c
commit
8580cd4403
145 changed files with 1992 additions and 1860 deletions
|
@ -166,6 +166,7 @@ public void verify() {
|
|||
VerifyHelper.verify(getAddress(), VerifyHelper.NOT_EMPTY, "LaunchServer address can't be empty");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -383,12 +389,12 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
|
|||
CRC32 crc = new CRC32();
|
||||
crc.update(publicKey.getModulus().toByteArray());
|
||||
LogHelper.subInfo("Modulus CRC32: 0x%08x", crc.getValue());
|
||||
|
||||
|
||||
// pre init modules
|
||||
modulesManager = new ModulesManager(this);
|
||||
modulesManager.autoload(dir.resolve("modules"));
|
||||
modulesManager.preInitModules();
|
||||
|
||||
|
||||
// Read LaunchServer config
|
||||
generateConfigIfNotExists();
|
||||
LogHelper.info("Reading LaunchServer config file");
|
||||
|
@ -404,7 +410,7 @@ public LaunchServer(Path dir, boolean portable) throws IOException, InvalidKeySp
|
|||
sessionManager = new SessionManager();
|
||||
GarbageManager.registerNeedGC(sessionManager);
|
||||
GarbageManager.registerNeedGC(limiter);
|
||||
|
||||
|
||||
// init modules
|
||||
modulesManager.initModules();
|
||||
|
||||
|
@ -415,25 +421,25 @@ 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();
|
||||
|
||||
|
||||
// Set server socket thread
|
||||
serverSocketHandler = new ServerSocketHandler(this, sessionManager);
|
||||
|
||||
|
||||
// post init modules
|
||||
modulesManager.postInitModules();
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -548,7 +550,7 @@ public void syncLauncherBinaries() throws IOException {
|
|||
LogHelper.warning("Missing launcher EXE binary file");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@LauncherAPI
|
||||
public void syncProfilesDir() throws IOException {
|
||||
LogHelper.info("Syncing profiles dir");
|
||||
|
@ -559,7 +561,7 @@ public void syncProfilesDir() throws IOException {
|
|||
newProfies.sort(Comparator.comparing(a -> a.object));
|
||||
profilesList = Collections.unmodifiableList(newProfies);
|
||||
}
|
||||
|
||||
|
||||
@LauncherAPI
|
||||
public void syncUpdatesDir(Collection<String> dirs) throws IOException {
|
||||
LogHelper.info("Syncing updates dir");
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
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 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 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 final Path proguard;
|
||||
public final Path config;
|
||||
public final Path mappings;
|
||||
public final Path words;
|
||||
public final ArrayList<String> confStrs;
|
||||
|
||||
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 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 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());
|
||||
}
|
||||
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 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,25 +13,27 @@
|
|||
import java.util.jar.JarFile;
|
||||
|
||||
public class StarterAgent {
|
||||
|
||||
public static final class StarterVisitor extends SimpleFileVisitor<Path> {
|
||||
private Instrumentation inst;
|
||||
|
||||
public StarterVisitor(Instrumentation inst) {
|
||||
this.inst = inst;
|
||||
}
|
||||
public static final class StarterVisitor extends SimpleFileVisitor<Path> {
|
||||
private Instrumentation 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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ public MySQLSourceConfig(String poolName, BlockConfigEntry block) {
|
|||
@Override
|
||||
public synchronized void close() {
|
||||
if (hikari)
|
||||
((HikariDataSource) source).close();
|
||||
((HikariDataSource) source).close();
|
||||
}
|
||||
|
||||
@LauncherAPI
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package ru.gravit.launchserver.auth.hwid;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
private void setConfig() {
|
||||
Config config = new Config();
|
||||
// Set string options
|
||||
config.setChdir(".");
|
||||
config.setErrTitle("JVM Error");
|
||||
config.setDownloadUrl(DOWNLOAD_URL);
|
||||
// Start building
|
||||
Builder builder = new Builder(Launch4JLog.INSTANCE);
|
||||
try {
|
||||
builder.build();
|
||||
} catch (Throwable e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Set boolean options
|
||||
config.setPriorityIndex(0);
|
||||
config.setHeaderType(Config.GUI_HEADER);
|
||||
config.setStayAlive(false);
|
||||
config.setRestartOnCrash(false);
|
||||
private void setConfig() {
|
||||
Config config = new Config();
|
||||
// Set string options
|
||||
config.setChdir(".");
|
||||
config.setErrTitle("JVM Error");
|
||||
config.setDownloadUrl(DOWNLOAD_URL);
|
||||
|
||||
// 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);
|
||||
// Set boolean options
|
||||
config.setPriorityIndex(0);
|
||||
config.setHeaderType(Config.GUI_HEADER);
|
||||
config.setStayAlive(false);
|
||||
config.setRestartOnCrash(false);
|
||||
|
||||
// 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 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);
|
||||
|
||||
// Set JAR wrapping options
|
||||
config.setDontWrapJar(false);
|
||||
config.setJar(server.launcherBinary.syncBinaryFile.toFile());
|
||||
config.setOutfile(binaryFile.toFile());
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 hashFunctionName = "SHA-256";
|
||||
private static final String MANIFEST_FN = "META-INF/MANIFEST.MF";
|
||||
private static final String SIG_FN = "META-INF/SIGNUMO.SF";
|
||||
|
||||
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 SIG_RSA_FN = "META-INF/SIGNUMO.RSA";
|
||||
|
||||
private final KeyStore keyStore;
|
||||
private static final String hashFunctionName = "SHA-256";
|
||||
|
||||
private final String keyAlias;
|
||||
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 password;
|
||||
private final Map<String, String> manifestAttributes;
|
||||
private String manifestHash;
|
||||
private String manifestMainHash;
|
||||
private static MessageDigest hasher() {
|
||||
try {
|
||||
return MessageDigest.getInstance(hashFunctionName);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, String> fileDigests;
|
||||
private final ZipOutputStream zos;
|
||||
|
||||
private final Map<String, String> sectionDigests;
|
||||
private final KeyStore keyStore;
|
||||
|
||||
/**
|
||||
* 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 keyAlias;
|
||||
|
||||
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 String password;
|
||||
private final Map<String, String> manifestAttributes;
|
||||
private String manifestHash;
|
||||
private String manifestMainHash;
|
||||
|
||||
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(contents));
|
||||
fileDigests.put(filename, hashCode64);
|
||||
}
|
||||
private final Map<String, String> fileDigests;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
private final Map<String, String> sectionDigests;
|
||||
|
||||
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(arr));
|
||||
fileDigests.put(filename, hashCode64);
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
manifestAttributes = new LinkedHashMap<>();
|
||||
fileDigests = new LinkedHashMap<>();
|
||||
sectionDigests = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
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();
|
||||
/**
|
||||
* 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(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();
|
||||
}
|
||||
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(contents));
|
||||
fileDigests.put(filename, hashCode64);
|
||||
}
|
||||
|
||||
/** Creates the beast that can actually sign the data. */
|
||||
private CMSSignedDataGenerator createSignedDataGenerator() throws Exception {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
/**
|
||||
* 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();
|
||||
|
||||
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;
|
||||
}
|
||||
String hashCode64 = Base64.getEncoder().encodeToString(hasher().digest(arr));
|
||||
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, 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
public ZipOutputStream getZos() {
|
||||
return zos;
|
||||
}
|
||||
/**
|
||||
* Creates the beast that can actually sign the data.
|
||||
*/
|
||||
private CMSSignedDataGenerator createSignedDataGenerator() throws Exception {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
/** 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;
|
||||
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;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
}
|
||||
public ZipOutputStream getZos() {
|
||||
return zos;
|
||||
}
|
||||
|
||||
/** Discards the specified byte. */
|
||||
@Override public void write(int b) {
|
||||
}
|
||||
}, hasher);
|
||||
manifest.write(o);
|
||||
return Base64.getEncoder().encodeToString(hasher.digest());
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/** 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();
|
||||
}
|
||||
manifest.getEntries().put(name, attributes);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
manifest.write(o);
|
||||
byte[] ob = o.toByteArray();
|
||||
ob = Arrays.copyOfRange(ob, emptyLen, ob.length);
|
||||
return Base64.getEncoder().encodeToString(hasher().digest(ob));
|
||||
}
|
||||
|
||||
// main section
|
||||
Attributes mainAttributes = man.getMainAttributes();
|
||||
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
/**
|
||||
* 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";
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : manifestAttributes.entrySet())
|
||||
mainAttributes.put(new Attributes.Name(entry.getKey()), entry.getValue());
|
||||
/** Discards the specified byte array. */
|
||||
@Override
|
||||
public void write(byte[] b) {
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
/** Discards the specified byte array. */
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
}
|
||||
|
||||
MessageDigest hasher = hasher();
|
||||
OutputStream out = new SignerJar.HashingOutputStream(zos, hasher);
|
||||
man.write(out);
|
||||
zos.closeEntry();
|
||||
/** Discards the specified byte. */
|
||||
@Override
|
||||
public void write(int b) {
|
||||
}
|
||||
}, hasher);
|
||||
manifest.write(o);
|
||||
return Base64.getEncoder().encodeToString(hasher.digest());
|
||||
}
|
||||
|
||||
manifestHash = Base64.getEncoder().encodeToString(hasher.digest());
|
||||
manifestMainHash = hashMainSection(man.getMainAttributes());
|
||||
}
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
/**
|
||||
* 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 : sectionDigests.entrySet()) {
|
||||
Attributes attributes = new Attributes();
|
||||
man.getEntries().put(entry.getKey(), attributes);
|
||||
attributes.put(digestAttr, entry.getValue());
|
||||
}
|
||||
// main section
|
||||
Attributes mainAttributes = man.getMainAttributes();
|
||||
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
|
||||
man.write(zos);
|
||||
zos.closeEntry();
|
||||
for (Map.Entry<String, String> entry : manifestAttributes.entrySet())
|
||||
mainAttributes.put(new Attributes.Name(entry.getKey()), entry.getValue());
|
||||
|
||||
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 : 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
MessageDigest hasher = hasher();
|
||||
OutputStream out = new SignerJar.HashingOutputStream(zos, hasher);
|
||||
man.write(out);
|
||||
zos.closeEntry();
|
||||
|
||||
manifestHash = Base64.getEncoder().encodeToString(hasher.digest());
|
||||
manifestMainHash = hashMainSection(man.getMainAttributes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
public boolean isContainsBlacklist(String clazz)
|
||||
{
|
||||
return INCLUDE_CLASS;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ public class Client {
|
|||
public long session;
|
||||
|
||||
public long timestamp;
|
||||
|
||||
public Client(long session) {
|
||||
this.session = session;
|
||||
timestamp = System.currentTimeMillis();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<>();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,13 +84,10 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
@LauncherAPI
|
||||
@Override
|
||||
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,8 @@
|
|||
|
||||
/**
|
||||
* Skin of the ring progress indicator where an arc grows and by the progress value up to 100% where the arc becomes a ring.
|
||||
*
|
||||
* @author Andrea Vacondio
|
||||
*
|
||||
* @author Andrea Vacondio
|
||||
*/
|
||||
@LauncherAPI
|
||||
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() {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ private Result(PlayerProfile pp, String accessToken) {
|
|||
this.accessToken = accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
private final String login;
|
||||
|
||||
private final byte[] encryptedPassword;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue