Merge branch 'release/5.1.9'

This commit is contained in:
Gravita 2020-12-25 11:45:35 +07:00
commit 1b2be8389a
43 changed files with 682 additions and 164 deletions

70
.github/workflows/codeql-analysis.yml vendored Normal file
View file

@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master, dev ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master, dev ]
schedule:
- cron: '28 4 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

17
Dockerfile Normal file
View file

@ -0,0 +1,17 @@
FROM archlinux/base
RUN pacman -Sy --noconfirm jdk11-openjdk unzip && pacman -Scc --noconfirm
ADD https://download2.gluonhq.com/openjfx/11.0.2/openjfx-11.0.2_linux-x64_bin-jmods.zip .
RUN unzip openjfx-11.0.2_linux-x64_bin-jmods.zip && mv javafx-jmods-11.0.2/* /usr/lib/jvm/java-11-openjdk/jmods/ && rmdir javafx-jmods-11.0.2 && rm openjfx-11.0.2_linux-x64_bin-jmods.zip
RUN mkdir ./libraries ./launcher-libraries ./launcher-libraries-compile
COPY ./LaunchServer/build/libs/LaunchServer.jar .
COPY ./LaunchServer/build/libs/libraries ./libraries
COPY ./LaunchServer/build/libs/launcher-libraries ./launcher-libraries
COPY ./LaunchServer/build/libs/launcher-libraries-compile ./launcher-libraries-compile
RUN mkdir ./compat/
COPY ./compat/authlib/authlib-clean.jar ./compat
COPY ./LauncherAuthlib/build/libs/* ./compat/
COPY ./ServerWrapper/build/libs/ServerWrapper.jar ./compat/
RUN mkdir ./compat/modules
COPY ./modules/*_module/build/libs/* ./compat/modules/
COPY ./modules/*_lmodule/build/libs/* ./compat/modules/
CMD java -javaagent:LaunchServer.jar -jar LaunchServer.jar

View file

@ -10,6 +10,7 @@
import pro.gravit.launcher.modules.events.ClosePhase;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.session.MemorySessionStorage;
import pro.gravit.launchserver.binary.*;
import pro.gravit.launchserver.config.LaunchServerConfig;
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
@ -80,6 +81,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
public final ReconfigurableManager reconfigurableManager;
public final ConfigManager configManager;
public final PingServerManager pingServerManager;
public final FeaturesManager featuresManager;
// HWID ban + anti-brutforce
public final CertificateManager certificateManager;
public final ProguardConf proguardConf;
@ -128,6 +130,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
runtime.verify();
config.verify();
if(config.sessions == null) config.sessions = new MemorySessionStorage();
if (config.components != null) {
LogHelper.debug("PreInit components");
config.components.forEach((k, v) -> {
@ -139,15 +142,17 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
// build hooks, anti-brutforce and other
proguardConf = new ProguardConf(this);
sessionManager = new SessionManager();
sessionManager = new SessionManager(this);
mirrorManager = new MirrorManager();
reconfigurableManager = new ReconfigurableManager();
authHookManager = new AuthHookManager();
configManager = new ConfigManager();
pingServerManager = new PingServerManager(this);
featuresManager = new FeaturesManager(this);
//Generate or set new Certificate API
certificateManager.orgName = config.projectName;
if (config.certificate != null && config.certificate.enabled) {
/*
if (false) {
if (IOHelper.isFile(caCertFile) && IOHelper.isFile(caKeyFile)) {
certificateManager.ca = certificateManager.readCertificate(caCertFile);
certificateManager.caKey = certificateManager.readPrivateKey(caKeyFile);
@ -175,6 +180,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
}
}
}
*/
config.init(ReloadType.FULL);
registerObject("launchServer", this);
GarbageManager.registerNeedGC(sessionManager);

View file

@ -10,6 +10,7 @@
import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
import pro.gravit.launchserver.auth.provider.AuthProvider;
import pro.gravit.launchserver.auth.session.SessionStorage;
import pro.gravit.launchserver.auth.texture.TextureProvider;
import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.config.LaunchServerConfig;
@ -215,6 +216,7 @@ public static void registerAll() {
AuthRequest.registerProviders();
HWIDProvider.registerProviders();
OptionalAction.registerProviders();
SessionStorage.registerProviders();
}
public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException {

View file

@ -0,0 +1,4 @@
package pro.gravit.launchserver.auth;
public interface RequiredDAO {
}

View file

@ -1,10 +1,11 @@
package pro.gravit.launchserver.auth.handler;
import pro.gravit.launchserver.auth.RequiredDAO;
import pro.gravit.launchserver.dao.User;
import java.util.UUID;
public class HibernateAuthHandler extends CachedAuthHandler {
public class HibernateAuthHandler extends CachedAuthHandler implements RequiredDAO {
@Override
protected Entry fetchEntry(String username) {
User user = srv.config.dao.userDAO.findByUsername(username);

View file

@ -84,11 +84,11 @@ public boolean addPublicKeyToHardwareInfo(HardwareReportRequest.HardwareInfo har
for (MemoryHWIDEntity e : db) {
HardwareInfoCompareResult result = compareHardwareInfo(e.hardware, hardwareInfo);
if (warningSpoofingLevel > 0 && result.firstSpoofingLevel > warningSpoofingLevel && !isAlreadyWarning) {
LogHelper.warning("HardwareInfo spoofing level too high: %d", result.firstSpoofingLevel);
LogHelper.warning("HardwareInfo spoofing level too high: %f", result.firstSpoofingLevel);
isAlreadyWarning = true;
}
if (result.compareLevel > criticalCompareLevel) {
LogHelper.debug("HardwareInfo publicKey change: compareLevel %d", result.compareLevel);
LogHelper.debug("HardwareInfo publicKey change: compareLevel %f", result.compareLevel);
if (e.banned) throw new HWIDException("You HWID banned");
e.publicKey = publicKey;
return true;

View file

@ -3,11 +3,12 @@
import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.RequiredDAO;
import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.manangers.hook.AuthHookManager;
import pro.gravit.utils.helper.SecurityHelper;
public class HibernateAuthProvider extends AuthProvider {
public class HibernateAuthProvider extends AuthProvider implements RequiredDAO {
public boolean autoReg;
@Override

View file

@ -0,0 +1,128 @@
package pro.gravit.launchserver.auth.session;
import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.manangers.SessionManager;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;
public class MemorySessionStorage extends SessionStorage implements NeedGarbageCollection {
private final Map<UUID, Entry> clientSet = new ConcurrentHashMap<>(128);
private final Map<UUID, Set<Entry>> uuidIndex = new ConcurrentHashMap<>(32);
@Override
public byte[] getSessionData(UUID session) {
Entry e = clientSet.get(session);
if(e == null) return null;
return e.data;
}
@Override
public Stream<UUID> getSessionsFromUserUUID(UUID userUUID) {
Set<Entry> set = uuidIndex.get(userUUID);
if(set != null) return set.stream().map((e) -> e.sessionUuid);
return null;
}
@Override
public boolean writeSession(UUID userUUID, UUID sessionUUID, byte[] data) {
deleteSession(sessionUUID);
Entry e = new Entry(data, sessionUUID);
clientSet.put(sessionUUID, e);
if(userUUID != null) {
Set<Entry> uuidSet = uuidIndex.computeIfAbsent(userUUID, k -> ConcurrentHashMap.newKeySet());
uuidSet.add(e);
}
return false;
}
@Override
public boolean deleteSession(UUID sessionUUID) {
Entry e =clientSet.remove(sessionUUID);
if(e != null) {
Set<Entry> set = uuidIndex.get(sessionUUID);
if(set != null) {
removeUuidFromIndexSet(set, e, sessionUUID);
}
return true;
}
return false;
}
@Override
public boolean deleteSessionsByUserUUID(UUID userUUID) {
Set<Entry> set = uuidIndex.get(userUUID);
if(set != null) {
for(Entry e : set) {
clientSet.remove(e.sessionUuid);
}
set.clear();
uuidIndex.remove(userUUID);
}
return true;
}
@Override
public void clear() {
clientSet.clear();
uuidIndex.clear();
}
@Override
public void lockSession(UUID sessionUUID) {
}
@Override
public void lockUser(UUID userUUID) {
}
@Override
public void unlockSession(UUID sessionUUID) {
}
@Override
public void unlockUser(UUID userUUID) {
}
private void removeUuidFromIndexSet(Set<Entry> set, Entry e, UUID session) {
set.remove(e);
if(set.isEmpty()) {
uuidIndex.remove(session);
}
}
@Override
public void garbageCollection() {
long time = System.currentTimeMillis();
long session_timeout = server.config.netty.performance.sessionLifetimeMs;
Set<UUID> to_delete = new HashSet<>(32);
clientSet.forEach((uuid, entry) -> {
long timestamp = entry.timestamp;
if(timestamp + session_timeout < time)
to_delete.add(uuid);
});
for(UUID session : to_delete) {
deleteSession(session);
}
to_delete.clear();
}
private static class Entry {
public byte[] data;
public UUID sessionUuid;
public long timestamp;
public Entry(byte[] data, UUID sessionUuid) {
this.data = data;
this.sessionUuid = sessionUuid;
this.timestamp = System.currentTimeMillis();
}
}
}

View file

@ -0,0 +1,37 @@
package pro.gravit.launchserver.auth.session;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.ProviderMap;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
public abstract class SessionStorage {
protected transient LaunchServer server;
public static ProviderMap<SessionStorage> providers = new ProviderMap<>();
private static boolean registeredProviders = false;
public abstract byte[] getSessionData(UUID session);
public abstract Stream<UUID> getSessionsFromUserUUID(UUID userUUID);
public abstract boolean writeSession(UUID userUUID, UUID sessionUUID, byte[] data);
public abstract boolean deleteSession(UUID sessionUUID);
public boolean deleteSessionsByUserUUID(UUID userUUID) {
getSessionsFromUserUUID(userUUID).forEach(this::deleteSession);
return true;
}
public abstract void clear();
public abstract void lockSession(UUID sessionUUID);
public abstract void lockUser(UUID userUUID);
public abstract void unlockSession(UUID sessionUUID);
public abstract void unlockUser(UUID userUUID);
public void init(LaunchServer server)
{
this.server = server;
}
public static void registerProviders() {
if(!registeredProviders) {
providers.register("memory", MemorySessionStorage.class);
registeredProviders = true;
}
}
}

View file

@ -1,67 +0,0 @@
package pro.gravit.launchserver.command.dump;
import com.google.gson.reflect.TypeToken;
import pro.gravit.launcher.Launcher;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.command.Command;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.command.SubCommand;
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Type;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class DumpSessionsCommand extends Command {
public DumpSessionsCommand(LaunchServer server) {
super(server);
childCommands.put("load", new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
LogHelper.info("Sessions read from %s", args[0]);
int size;
try (Reader reader = IOHelper.newReader(Paths.get(args[0]))) {
Type setType = new TypeToken<HashSet<Client>>() {
}.getType();
Set<Client> clientSet = Launcher.gsonManager.configGson.fromJson(reader, setType);
size = clientSet.size();
server.sessionManager.loadSessions(clientSet);
}
LogHelper.subInfo("Readed %d sessions", size);
}
});
childCommands.put("unload", new SubCommand() {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
LogHelper.info("Sessions write to %s", args[0]);
Collection<Client> clientSet = server.sessionManager.getSessions();
try (Writer writer = IOHelper.newWriter(Paths.get(args[0]))) {
Launcher.gsonManager.configGson.toJson(clientSet, writer);
}
LogHelper.subInfo("Write %d sessions", clientSet.size());
}
});
}
@Override
public String getArgsDescription() {
return "[load/unload] [filename]";
}
@Override
public String getUsageDescription() {
return "Load or unload sessions";
}
@Override
public void invoke(String... args) throws Exception {
invokeSubcommands(args);
}
}

View file

@ -5,7 +5,6 @@
import pro.gravit.launchserver.command.auth.UUIDToUsernameCommand;
import pro.gravit.launchserver.command.auth.UsernameToUUIDCommand;
import pro.gravit.launchserver.command.basic.*;
import pro.gravit.launchserver.command.dump.DumpSessionsCommand;
import pro.gravit.launchserver.command.hash.*;
import pro.gravit.launchserver.command.install.CheckInstallCommand;
import pro.gravit.launchserver.command.install.MultiCommand;
@ -61,12 +60,6 @@ public static void registerCommands(pro.gravit.utils.command.CommandHandler hand
Category authCategory = new Category(auth, "auth", "User Management");
handler.registerCategory(authCategory);
//Register dump commands
BaseCommandCategory dump = new BaseCommandCategory();
dump.registerCommand("dumpSessions", new DumpSessionsCommand(server));
Category dumpCategory = new Category(dump, "dump", "Dump runtime data");
handler.registerCategory(dumpCategory);
//Register service commands
BaseCommandCategory service = new BaseCommandCategory();
service.registerCommand("config", new ConfigCommand(server));
@ -78,6 +71,7 @@ public static void registerCommands(pro.gravit.utils.command.CommandHandler hand
service.registerCommand("clients", new ClientsCommand(server));
service.registerCommand("signJar", new SignJarCommand(server));
service.registerCommand("signDir", new SignDirCommand(server));
service.registerCommand("pingServers", new PingServersCommand(server));
service.registerCommand("securitycheck", new SecurityCheckCommand(server));
Category serviceCategory = new Category(service, "service", "Managing LaunchServer Components");
handler.registerCategory(serviceCategory);

View file

@ -8,6 +8,8 @@
import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper;
import java.util.Base64;
public class ClientsCommand extends Command {
public ClientsCommand(LaunchServer server) {
super(server);
@ -34,8 +36,12 @@ public void invoke(String... args) {
LogHelper.info("Channel %s | connectUUID %s | checkSign %s", ip, frameHandler.getConnectUUID(), client.checkSign ? "true" : "false");
else {
LogHelper.info("Client name %s | ip %s | connectUUID %s", client.username == null ? "null" : client.username, ip, frameHandler.getConnectUUID());
LogHelper.subInfo("userUUID: %s | session %s", client.uuid == null ? "null" : client.uuid.toString(), client.session == null ? "null" : client.session);
LogHelper.subInfo("Data: checkSign %s | auth_id %s", client.checkSign ? "true" : "false",
client.auth_id);
if(client.trustLevel != null) {
LogHelper.subInfo("trustLevel | key %s | pubkey %s", client.trustLevel.keyChecked ? "checked" : "unchecked", client.trustLevel.publicKey == null ? "null" : Base64.getEncoder().encode(client.trustLevel.publicKey));
}
LogHelper.subInfo("Permissions: %s (permissions %d | flags %d)", client.permissions == null ? "null" : client.permissions.toString(), client.permissions == null ? 0 : client.permissions.permissions, client.permissions == null ? 0 : client.permissions.flags);
}
}));

View file

@ -0,0 +1,36 @@
package pro.gravit.launchserver.command.service;
import pro.gravit.launcher.request.management.PingServerReportRequest;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.command.Command;
import pro.gravit.utils.helper.LogHelper;
public class PingServersCommand extends Command {
public PingServersCommand(LaunchServer server) {
super(server);
}
@Override
public String getArgsDescription() {
return "[]";
}
@Override
public String getUsageDescription() {
return "show modern pings status";
}
@Override
public void invoke(String... args) throws Exception {
server.pingServerManager.map.forEach((name, data) -> {
LogHelper.info("[%s] online %d / %d", name, data.lastReport == null ? -1 : data.lastReport.playersOnline, data.lastReport == null ? -1 : data.lastReport.maxPlayers);
if(data.lastReport != null && data.lastReport.users != null)
{
for(PingServerReportRequest.PingServerReport.UsernameInfo user : data.lastReport.users)
{
LogHelper.subInfo("User %s", user.username == null ? "null" : user.username);
}
}
});
}
}

View file

@ -158,8 +158,10 @@ public void invoke(String... args) throws Exception {
if (tokenizer.hasMoreTokens() && tokenizer.nextToken().equals("mods")) {
String nextToken = tokenizer.nextToken();
if (!tokenizer.hasMoreTokens()) {
printCheckResult(LogHelper.Level.INFO, profileModuleName, String.format("updateExclusions %s not safe. Cheats may be injected very easy!", exc), false);
bad = true;
if(!exc.endsWith("/")) {
printCheckResult(LogHelper.Level.INFO, profileModuleName, String.format("updateExclusions %s not safe. Cheats may be injected very easy!", exc), false);
bad = true;
}
} else {
if (nextToken.equals("memory_repo") || nextToken.equals(profile.getVersion().name)) {
printCheckResult(LogHelper.Level.INFO, profileModuleName, String.format("updateExclusions %s not safe. Cheats may be injected very easy!", exc), false);

View file

@ -38,7 +38,7 @@ public void invoke(String... args) {
for (CommandHandler.Category category : server.commandHandler.getCategories()) {
commands += category.category.commandsMap().size();
}
LogHelper.info("Sessions: %d | Commands: %d(%d categories)", server.sessionManager.getSessions().size(), commands, server.commandHandler.getCategories().size() + 1);
LogHelper.info("Commands: %d(%d categories)", commands, server.commandHandler.getCategories().size() + 1);
for (AuthProviderPair pair : server.config.auth.values()) {
if (pair.handler instanceof CachedAuthHandler) {
LogHelper.info("AuthHandler %s: EntryCache: %d | usernameCache: %d", pair.name, ((CachedAuthHandler) pair.handler).getEntryCache().size(), ((CachedAuthHandler) pair.handler).getUsernamesCache().size());

View file

@ -10,6 +10,8 @@
import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.protect.StdProtectHandler;
import pro.gravit.launchserver.auth.provider.RejectAuthProvider;
import pro.gravit.launchserver.auth.session.MemorySessionStorage;
import pro.gravit.launchserver.auth.session.SessionStorage;
import pro.gravit.launchserver.auth.texture.RequestTextureProvider;
import pro.gravit.launchserver.binary.tasks.exe.Launch4JTask;
import pro.gravit.launchserver.components.AuthLimiterComponent;
@ -33,6 +35,7 @@ public final class LaunchServerConfig {
public LauncherConfig.LauncherEnvironment env;
public Map<String, AuthProviderPair> auth;
public DaoProvider dao;
public SessionStorage sessions;
// Handlers & Providers
public ProtectHandler protectHandler;
@ -40,7 +43,6 @@ public final class LaunchServerConfig {
public ExeConf launch4j;
public NettyConfig netty;
public LauncherConf launcher;
public CertificateConf certificate;
public JarSignerConf sign;
public String startScript;
private transient LaunchServer server = null;
@ -71,6 +73,7 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
a.displayName = "Default";
newConfig.auth.put("std", a);
newConfig.protectHandler = new StdProtectHandler();
newConfig.sessions = new MemorySessionStorage();
newConfig.binaryName = "Launcher";
newConfig.netty = new NettyConfig();
@ -97,8 +100,6 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
newConfig.launcher.stripLineNumbers = true;
newConfig.launcher.proguardGenMappings = true;
newConfig.certificate = new LaunchServerConfig.CertificateConf();
newConfig.certificate.enabled = false;
newConfig.sign = new JarSignerConf();
newConfig.components = new HashMap<>();
@ -187,6 +188,10 @@ public void init(LaunchServer.ReloadType type) {
protectHandler.init(server);
protectHandler.checkLaunchServerLicense();
}
if(sessions != null) {
sessions.init(server);
server.registerObject("sessions", sessions);
}
if (components != null) {
components.forEach((k, v) -> server.registerObject("component.".concat(k), v));
}
@ -229,6 +234,9 @@ public void close(LaunchServer.ReloadType type) {
server.unregisterObject("protectHandler", protectHandler);
protectHandler.close();
}
if(sessions != null) {
server.unregisterObject("sessions", sessions);
}
if (dao != null) {
server.unregisterObject("dao", dao);
if (dao instanceof AutoCloseable) {
@ -260,10 +268,6 @@ public static class ExeConf {
public String txtProductVersion;
}
public static class CertificateConf {
public boolean enabled;
}
public static class JarSignerConf {
public boolean enabled = false;
public String keyStore = "pathToKey";
@ -312,6 +316,8 @@ public static class NettyPerformanceConfig {
public boolean usingEpoll;
public int bossThread;
public int workerThread;
public long sessionLifetimeMs = 24 * 60 * 60 * 1000;
public int maxWebSocketRequestBytes = 1024 * 1024;
}
public static class NettyBindAddress {

View file

@ -0,0 +1,35 @@
package pro.gravit.launchserver.manangers;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.utils.Version;
import java.util.HashMap;
import java.util.Map;
public class FeaturesManager {
private transient LaunchServer server;
private Map<String, String> map;
public FeaturesManager(LaunchServer server) {
this.server = server;
map = new HashMap<>();
addFeatureInfo("version", Version.getVersion().getVersionString());
}
public Map<String, String> getMap() {
return map;
}
public String getFeatureInfo(String name) {
return map.get(name);
}
public String addFeatureInfo(String name, String featureInfo) {
return map.put(name, featureInfo);
}
public String removeFeatureInfo(String name) {
return map.remove(name);
}
}

View file

@ -11,6 +11,7 @@
import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
import pro.gravit.launchserver.auth.provider.AuthProvider;
import pro.gravit.launchserver.auth.session.SessionStorage;
import pro.gravit.launchserver.auth.texture.TextureProvider;
import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.dao.provider.DaoProvider;
@ -41,6 +42,7 @@ public void registerAdapters(GsonBuilder builder) {
builder.registerTypeAdapter(AuthRequest.AuthPasswordInterface.class, new UniversalJsonAdapter<>(AuthRequest.providers));
builder.registerTypeAdapter(HWIDProvider.class, new UniversalJsonAdapter<>(HWIDProvider.providers));
builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers));
builder.registerTypeAdapter(SessionStorage.class, new UniversalJsonAdapter<>(SessionStorage.providers));
modulesManager.invokeEvent(new PreGsonPhase(builder));
//ClientWebSocketService.appendTypeAdapters(builder);
}

View file

@ -1,63 +1,112 @@
package pro.gravit.launchserver.manangers;
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.NeedGarbageCollection;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.RequiredDAO;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.utils.HookSet;
import pro.gravit.utils.helper.LogHelper;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
public class SessionManager implements NeedGarbageCollection {
public static final long SESSION_TIMEOUT = 3 * 60 * 60 * 1000; // 3 часа
private final Map<UUID, Client> clientSet = new HashMap<>(128);
private final LaunchServer server;
public HookSet<Client> clientRestoreHook = new HookSet<>();
public SessionManager(LaunchServer server) {
this.server = server;
}
public boolean addClient(Client client) {
clientSet.put(client.session, client);
return true;
if(client == null || client.session == null) return false;
return server.config.sessions.writeSession(client.uuid, client.session, compressClient(client));
}
public Stream<UUID> findSessionsByUUID(UUID uuid) {
return server.config.sessions.getSessionsFromUserUUID(uuid);
}
public boolean removeByUUID(UUID uuid) {
return server.config.sessions.deleteSessionsByUserUUID(uuid);
}
@Deprecated
public Set<UUID> getSavedUUIDs()
{
throw new UnsupportedOperationException();
}
public void clear() {
server.config.sessions.clear();
}
private byte[] compressClient(Client client) {
return Launcher.gsonManager.gson.toJson(client).getBytes(StandardCharsets.UTF_8); //Compress using later
}
private Client decompressClient(byte[] client) {
return Launcher.gsonManager.gson.fromJson(new String(client, StandardCharsets.UTF_8), Client.class); //Compress using later
}
private Client restoreFromString(byte[] data) {
Client result = decompressClient(data);
result.updateAuth(server);
if(result.auth != null && (result.username != null)) {
if(result.auth.handler instanceof RequiredDAO || result.auth.provider instanceof RequiredDAO || result.auth.textureProvider instanceof RequiredDAO) {
result.daoObject = server.config.dao.userDAO.findByUsername(result.username);
}
}
if(result.refCount == null) result.refCount = new AtomicInteger(1);
clientRestoreHook.hook(result);
return result;
}
@Override
public void garbageCollection() {
long time = System.currentTimeMillis();
clientSet.entrySet().removeIf(entry -> {
Client c = entry.getValue();
return (c.timestamp + SESSION_TIMEOUT < time);
});
}
public Client getClient(UUID session) {
return clientSet.get(session);
if(session == null) return null;
byte[] data = server.config.sessions.getSessionData(session);
if(data == null) return null;
return restoreFromString(data);
}
public Client getOrNewClient(UUID session) {
return clientSet.computeIfAbsent(session, Client::new);
Client client = getClient(session);
return client == null ? new Client(session) : client;
}
public Client removeClient(UUID session) {
return clientSet.remove(session);
public boolean remove(UUID session) {
return server.config.sessions.deleteSession(session);
}
@Deprecated
public void removeClient(UUID session) {
remove(session);
}
@Deprecated
public void updateClient(UUID session) {
Client c = clientSet.get(session);
if (c != null) {
c.up();
return;
}
Client newClient = new Client(session);
clientSet.put(session, newClient);
LogHelper.warning("Using deprecated method: sessionManager.updateClient");
}
@Deprecated
public Set<Client> getSessions() {
// TODO: removeme
return new HashSet<>(clientSet.values());
LogHelper.warning("Using deprecated method: sessionManager.getSession");
return new HashSet<>();
}
@Deprecated
public void loadSessions(Set<Client> set) {
clientSet.putAll(set.stream().collect(Collectors.toMap(c -> c.session, Function.identity())));
LogHelper.warning("Using deprecated method: sessionManager.loadSessions");
//clientSet.putAll(set.stream().collect(Collectors.toMap(c -> c.session, Function.identity())));
}
}

View file

@ -11,6 +11,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
public class Client {
public UUID session;
@ -22,6 +23,7 @@ public class Client {
public boolean checkSign;
public ClientPermissions permissions;
public String username;
public UUID uuid;
public TrustLevel trustLevel;
public transient AuthProviderPair auth;
@ -30,6 +32,8 @@ public class Client {
public transient Map<String, Object> properties;
public transient AtomicInteger refCount = new AtomicInteger(1);
public Client(UUID session) {
this.session = session;
timestamp = System.currentTimeMillis();

View file

@ -56,11 +56,11 @@ public void initChannel(SocketChannel ch) {
NettyConnectContext context = new NettyConnectContext();
//p.addLast(new LoggingHandler(LogLevel.INFO));
pipeline.addLast("http-codec", new HttpServerCodec());
pipeline.addLast("http-codec-compressor", new HttpObjectAggregator(65536));
pipeline.addLast("http-codec-compressor", new HttpObjectAggregator(server.config.netty.performance.maxWebSocketRequestBytes));
if (server.config.netty.ipForwarding)
pipeline.addLast("forward-http", new NettyIpForwardHandler(context));
pipeline.addLast("websock-comp", new WebSocketServerCompressionHandler());
pipeline.addLast("websock-codec", new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true));
pipeline.addLast("websock-codec", new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true, server.config.netty.performance.maxWebSocketRequestBytes));
if (!server.config.netty.disableWebApiInterface)
pipeline.addLast("webapi", new NettyWebAPIHandler(context));
if (server.config.netty.fileServerEnabled)

View file

@ -11,12 +11,15 @@
import pro.gravit.launcher.events.ExceptionEvent;
import pro.gravit.launcher.events.RequestEvent;
import pro.gravit.launcher.events.request.ErrorRequestEvent;
import pro.gravit.launcher.events.request.ExitRequestEvent;
import pro.gravit.launcher.request.WebSocketEvent;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.dao.User;
import pro.gravit.launchserver.socket.handlers.WebSocketFrameHandler;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import pro.gravit.launchserver.socket.response.WebSocketServerResponse;
import pro.gravit.launchserver.socket.response.auth.*;
import pro.gravit.launchserver.socket.response.management.FeaturesResponse;
import pro.gravit.launchserver.socket.response.management.PingServerReportResponse;
import pro.gravit.launchserver.socket.response.management.PingServerResponse;
import pro.gravit.launchserver.socket.response.management.ServerStatusResponse;
@ -36,8 +39,10 @@
import pro.gravit.utils.helper.LogHelper;
import java.lang.reflect.Type;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class WebSocketService {
public static final ProviderMap<WebSocketServerResponse> providers = new ProviderMap<>();
@ -68,12 +73,12 @@ public WebSocketService(ChannelGroup channels, LaunchServer server) {
}
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {
channels.forEach((channel) -> {
if (channel == null || channel.pipeline() == null) return;
for(Channel channel : channels) {
if (channel == null || channel.pipeline() == null) continue;
WebSocketFrameHandler wsHandler = channel.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null) return;
if (wsHandler == null) continue;
callback.accept(channel, wsHandler);
});
};
}
public static void registerResponses() {
@ -99,6 +104,7 @@ public static void registerResponses() {
providers.register("pingServerReport", PingServerReportResponse.class);
providers.register("pingServer", PingServerResponse.class);
providers.register("currentUser", CurrentUserResponse.class);
providers.register("features", FeaturesResponse.class);
}
public void process(ChannelHandlerContext ctx, TextWebSocketFrame frame, Client client, String ip) {
@ -208,6 +214,88 @@ public void sendObjectAll(Object obj, Type type) {
}
}
public void sendObjectToUUID(UUID userUuid, Object obj, Type type) {
for (Channel ch : channels) {
if (ch == null || ch.pipeline() == null) continue;
WebSocketFrameHandler wsHandler = ch.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null) continue;
Client client = wsHandler.getClient();
if(client == null || !userUuid.equals(client.uuid)) continue;
ch.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, type)), ch.voidPromise());
}
}
public void updateDaoObject(UUID userUuid, User daoObject, Consumer<Channel> callback) {
for(Channel ch : channels) {
if (ch == null || ch.pipeline() == null) continue;
WebSocketFrameHandler wsHandler = ch.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null) continue;
Client client = wsHandler.getClient();
if(client == null || client.daoObject == null || !userUuid.equals(client.uuid)) continue;
client.daoObject = daoObject;
if(callback != null) callback.accept(ch);
}
}
public Channel getChannelFromConnectUUID(UUID connectUuid) {
for(Channel ch : channels) {
if (ch == null || ch.pipeline() == null) continue;
WebSocketFrameHandler wsHandler = ch.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null) continue;
if(connectUuid.equals(wsHandler.getConnectUUID())) {
return ch;
}
}
return null;
}
public boolean kickByUserUUID(UUID userUuid, boolean isClose) {
boolean result = false;
for(Channel ch : channels) {
if (ch == null || ch.pipeline() == null) continue;
WebSocketFrameHandler wsHandler = ch.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null) continue;
Client client = wsHandler.getClient();
if(client == null || client.daoObject == null || !userUuid.equals(client.uuid)) continue;
ExitResponse.exit(server, wsHandler, ch, ExitRequestEvent.ExitReason.SERVER);
if(isClose) ch.close();
result = true;
}
return result;
}
public boolean kickByConnectUUID(UUID connectUuid, boolean isClose) {
for(Channel ch : channels) {
if (ch == null || ch.pipeline() == null) continue;
WebSocketFrameHandler wsHandler = ch.pipeline().get(WebSocketFrameHandler.class);
if (wsHandler == null) continue;
if(connectUuid.equals(wsHandler.getConnectUUID())) {
ExitResponse.exit(server, wsHandler, ch, ExitRequestEvent.ExitReason.SERVER);
if(isClose) ch.close();
return true;
}
}
return false;
}
public boolean kickByIP(String ip, boolean isClose) {
boolean result = false;
for(Channel ch : channels) {
if (ch == null || ch.pipeline() == null) continue;
WebSocketFrameHandler wsHandler = ch.pipeline().get(WebSocketFrameHandler.class);
if(wsHandler == null) continue;
String clientIp;
if(wsHandler.context != null && wsHandler.context.ip != null) clientIp = wsHandler.context.ip;
else clientIp = IOHelper.getIP(ch.remoteAddress());
if(ip.equals(clientIp)) {
ExitResponse.exit(server, wsHandler, ch, ExitRequestEvent.ExitReason.SERVER);
if(isClose) ch.close();
result = true;
}
}
return result;
}
public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj) {
ctx.writeAndFlush(new TextWebSocketFrame(gson.toJson(obj, WebSocketEvent.class))).addListener(ChannelFutureListener.CLOSE);
}

View file

@ -37,7 +37,9 @@ public Client getClient() {
}
public void setClient(Client client) {
if(this.client != null) this.client.refCount.decrementAndGet();
this.client = client;
if(client != null) client.refCount.incrementAndGet();
}
public final UUID getConnectUUID() {
@ -85,6 +87,7 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
} else if ((frame instanceof PongWebSocketFrame)) {
LogHelper.dev("WebSocket Client received pong");
} else if ((frame instanceof CloseWebSocketFrame)) {
int statusCode = ((CloseWebSocketFrame) frame).statusCode();
ctx.channel().close();
} else {
String message = "unsupported frame type: " + frame.getClass().getName();
@ -95,6 +98,18 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (future != null) future.cancel(true);
if(LogHelper.isDevEnabled()) {
LogHelper.dev("Client %s disconnected", IOHelper.getIP(ctx.channel().remoteAddress()));
}
int refCount = client.refCount.decrementAndGet();
if(client.session != null) {
if(refCount == 0) {
srv.sessionManager.addClient(client);
}
else if(refCount < 0) {
LogHelper.warning("Client session %s reference counter invalid - %d", client.session, refCount);
}
}
super.channelInactive(ctx);
}
}

View file

@ -7,6 +7,7 @@
import io.netty.handler.stream.ChunkedFile;
import io.netty.util.CharsetUtil;
import pro.gravit.launchserver.socket.handlers.ContentType;
import pro.gravit.utils.helper.LogHelper;
import pro.gravit.utils.helper.VerifyHelper;
import java.io.File;
@ -24,6 +25,7 @@
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
@ -220,11 +222,17 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr
// Only compare up to the second because the datetime format we send to the client
// does not have milliseconds
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.get(ChronoField.INSTANT_SECONDS);
long fileLastModifiedSeconds = file.lastModified() / 1000;
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
sendNotModified(ctx);
return;
try {
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getLong(ChronoField.INSTANT_SECONDS);
long fileLastModifiedSeconds = file.lastModified() / 1000;
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
sendNotModified(ctx);
return;
}
} catch (UnsupportedTemporalTypeException e) {
if(LogHelper.isDebugEnabled()) {
LogHelper.warning("Request access If-Modifed-Since: %s not parsed correctly", ifModifiedSince);
}
}
}

View file

@ -100,21 +100,21 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
if (getSession) {
if (clientData.session == null) {
clientData.session = UUID.randomUUID();
server.sessionManager.addClient(clientData);
//server.sessionManager.addClient(clientData);
}
result.session = clientData.session;
}
UUID uuid;
if (authType == ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) {
uuid = pair.handler.auth(aresult);
clientData.uuid = pair.handler.auth(aresult);
if (LogHelper.isDebugEnabled()) {
LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, uuid.toString());
LogHelper.debug("Auth: %s accessToken %s uuid: %s", login, result.accessToken, clientData.uuid.toString());
}
} else {
uuid = pair.handler.usernameToUUID(aresult.username);
clientData.uuid = pair.handler.usernameToUUID(aresult.username);
result.accessToken = null;
}
result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, aresult.username, client, clientData.auth.textureProvider);
result.playerProfile = ProfileByUUIDResponse.getProfile(clientData.uuid, aresult.username, client, clientData.auth.textureProvider);
clientData.type = authType;
sendResult(result);

View file

@ -23,7 +23,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
public static CurrentUserRequestEvent.UserInfo collectUserInfoFromClient(Client client) throws IOException {
CurrentUserRequestEvent.UserInfo result = new CurrentUserRequestEvent.UserInfo();
if (client.auth != null && client.isAuth && client.username != null) {
UUID uuid = client.auth.handler.usernameToUUID(client.username);
UUID uuid = client.uuid != null ? client.uuid : client.auth.handler.usernameToUUID(client.username);
if (uuid != null) {
result.playerProfile = ProfileByUUIDResponse.getProfile(uuid, client.username, client.profile == null ? null : client.profile.getTitle(), client.auth.textureProvider);
}

View file

@ -38,7 +38,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
Client newClient = new Client(null);
newClient.checkSign = client.checkSign;
handler.setClient(newClient);
if (client.session != null) server.sessionManager.removeClient(client.session);
if (client.session != null) server.sessionManager.remove(client.session);
if (exitAll) {
service.forEachActiveChannels(((channel, webSocketFrameHandler) -> {
Client client1 = webSocketFrameHandler.getClient();
@ -68,7 +68,7 @@ public static void exit(LaunchServer server, WebSocketFrameHandler wsHandler, Ch
Client newCusClient = new Client(null);
newCusClient.checkSign = chClient.checkSign;
wsHandler.setClient(newCusClient);
if (chClient.session != null) server.sessionManager.removeClient(chClient.session);
if (chClient.session != null) server.sessionManager.remove(chClient.session);
ExitRequestEvent event = new ExitRequestEvent(reason);
event.requestUUID = RequestEvent.eventUUID;
wsHandler.service.sendObject(channel, event);

View file

@ -21,15 +21,28 @@ public String getType() {
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
Client rClient = server.sessionManager.getClient(session);
if (rClient == null) {
if(session == null) {
sendError("Session invalid");
return;
}
final Client[] rClient = {null};
service.forEachActiveChannels((channel, handler) -> {
Client c = handler.getClient();
if(c != null && session.equals(c.session)) {
rClient[0] = c;
}
});
if(rClient[0] == null) {
rClient[0] = server.sessionManager.getClient(session);
}
if (rClient[0] == null) {
sendError("Session invalid");
return;
}
WebSocketFrameHandler frameHandler = ctx.pipeline().get(WebSocketFrameHandler.class);
frameHandler.setClient(rClient);
frameHandler.setClient(rClient[0]);
if (needUserInfo) {
sendResult(new RestoreSessionRequestEvent(CurrentUserResponse.collectUserInfoFromClient(rClient)));
sendResult(new RestoreSessionRequestEvent(CurrentUserResponse.collectUserInfoFromClient(rClient[0])));
} else {
sendResult(new RestoreSessionRequestEvent());
}

View file

@ -0,0 +1,18 @@
package pro.gravit.launchserver.socket.response.management;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.events.request.FeaturesRequestEvent;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
public class FeaturesResponse extends SimpleResponse {
@Override
public String getType() {
return "features";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
sendResult(new FeaturesRequestEvent(server.featuresManager.getMap()));
}
}

View file

@ -63,6 +63,6 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
sendError(String.format("ProfileByUUIDResponse: User with uuid %s not found or AuthProvider#uuidToUsername returned null", uuid));
return;
}
sendResult(new ProfileByUUIDRequestEvent(getProfile(uuid, username, this.client, client.auth.textureProvider)));
sendResult(new ProfileByUUIDRequestEvent(getProfile(uuid, username, this.client, pair.textureProvider)));
}
}

View file

@ -159,8 +159,7 @@ public static void main(String[] args) throws Throwable {
verifyHDir(clientDir, params.clientHDir, clientMatcher, digest);
if (javaWatcher != null)
verifyHDir(javaDir, params.javaHDir, null, digest);
if (params.javaHDir != null)
LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params));
LauncherEngine.modulesManager.invokeEvent(new ClientProcessLaunchEvent(engine, params));
launch(profile, params);
}
}

View file

@ -103,7 +103,6 @@ private void applyClientProfile() {
this.systemEnv.put("JAVA_HOME", javaDir.toString());
Collections.addAll(this.systemClassPath, this.params.profile.getAlternativeClassPath());
if (params.ram > 0) {
this.jvmArgs.add("-Xms" + params.ram + 'M');
this.jvmArgs.add("-Xmx" + params.ram + 'M');
}
this.params.session = Request.getSession();

View file

@ -0,0 +1,21 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
import java.util.Map;
public class FeaturesRequestEvent extends RequestEvent {
public Map<String, String> features;
public FeaturesRequestEvent() {
}
public FeaturesRequestEvent(Map<String, String> features) {
this.features = features;
}
@Override
public String getType() {
return "features";
}
}

View file

@ -352,8 +352,6 @@ public void verify() {
// Client
VerifyHelper.verify(getTitle(), VerifyHelper.NOT_EMPTY, "Profile title can't be empty");
VerifyHelper.verify(getInfo(), VerifyHelper.NOT_EMPTY, "Profile info can't be empty");
VerifyHelper.verify(getServerAddress(), VerifyHelper.NOT_EMPTY, "Server address can't be empty");
VerifyHelper.verifyInt(getServerPort(), VerifyHelper.range(0, 65535), "Illegal server port: " + getServerPort());
// Client launcher
VerifyHelper.verify(getTitle(), VerifyHelper.NOT_EMPTY, "Main class can't be empty");
@ -428,7 +426,8 @@ public enum Version {
MC1152("1.15.2", 578),
MC1161("1.16.1", 736),
MC1162("1.16.2", 751),
MC1163("1.16.3", 753);
MC1163("1.16.3", 753),
MC1164("1.16.4", 754);
private static final Map<String, Version> VERSIONS;
static {

View file

@ -0,0 +1,11 @@
package pro.gravit.launcher.request.management;
import pro.gravit.launcher.events.request.FeaturesRequestEvent;
import pro.gravit.launcher.request.Request;
public class FeaturesRequest extends Request<FeaturesRequestEvent> {
@Override
public String getType() {
return "features";
}
}

View file

@ -16,6 +16,7 @@ public final class BatchProfileByUsernameRequest extends Request<BatchProfileByU
public BatchProfileByUsernameRequest(String... usernames) throws IOException {
this.list = new Entry[usernames.length];
for (int i = 0; i < usernames.length; ++i) {
this.list[i] = new Entry();
this.list[i].client = "";
this.list[i].username = usernames[i];
}

View file

@ -106,6 +106,7 @@ public void registerResults() {
results.register("pingServerReport", PingServerReportRequestEvent.class);
results.register("pingServer", PingServerRequestEvent.class);
results.register("currentUser", CurrentUserRequestEvent.class);
results.register("features", FeaturesRequestEvent.class);
}
public void waitIfNotConnected() {

View file

@ -6,7 +6,7 @@ public final class Version {
public static final int MAJOR = 5;
public static final int MINOR = 1;
public static final int PATCH = 8;
public static final int PATCH = 9;
public static final int BUILD = 1;
public static final Version.Type RELEASE = Type.STABLE;
public final int major;

View file

@ -4,10 +4,12 @@
import pro.gravit.launcher.Launcher;
import pro.gravit.launcher.LauncherConfig;
import pro.gravit.launcher.config.JsonConfigurable;
import pro.gravit.launcher.events.request.AuthRequestEvent;
import pro.gravit.launcher.events.request.ProfilesRequestEvent;
import pro.gravit.launcher.modules.events.PostInitPhase;
import pro.gravit.launcher.modules.events.PreConfigPhase;
import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launcher.profiles.PlayerProfile;
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.RequestException;
@ -46,6 +48,8 @@ public class ServerWrapper extends JsonConfigurable<ServerWrapper.Config> {
public ClassLoader loader;
public ClientPermissions permissions;
public ClientProfile profile;
public PlayerProfile playerProfile;
public ClientProfile.ServerProfile serverProfile;
public ServerWrapper(Type type, Path configPath) {
super(type, configPath);
@ -70,20 +74,28 @@ public boolean auth() {
try {
Launcher.getConfig();
AuthRequest request = new AuthRequest(config.login, config.password, config.auth_id, AuthRequest.ConnectTypes.API);
permissions = request.request().permissions;
AuthRequestEvent authResult = request.request();
permissions = authResult.permissions;
playerProfile = authResult.playerProfile;
ProfilesRequestEvent result = new ProfilesRequest().request();
for (ClientProfile p : result.profiles) {
LogHelper.debug("Get profile: %s", p.getTitle());
if (p.getTitle().equals(config.title)) {
profile = p;
Launcher.profile = p;
LogHelper.debug("Found profile: %s", Launcher.profile.getTitle());
break;
boolean isFound = false;
for(ClientProfile.ServerProfile srv : p.getServers())
{
if(srv != null && srv.name.equals(config.serverName)) {
this.serverProfile = srv;
this.profile = p;
Launcher.profile = p;
LogHelper.debug("Found profile: %s", Launcher.profile.getTitle());
isFound = true;
break;
}
}
if(isFound) break;
}
if (profile == null) {
LogHelper.error("Your profile not found");
if (config.stopOnError) System.exit(-1);
LogHelper.warning("Not connected to ServerProfile. May be serverName incorrect?");
}
return true;
} catch (Throwable e) {
@ -183,7 +195,7 @@ public void run(String... args) throws Throwable {
}
auth();
};
LogHelper.info("ServerWrapper: Project %s, LaunchServer address: %s. Title: %s", config.projectname, config.address, config.title);
LogHelper.info("ServerWrapper: Project %s, LaunchServer address: %s. Title: %s", config.projectname, config.address, Launcher.profile != null ? Launcher.profile.getTitle() : "unknown");
LogHelper.info("Minecraft Version (for profile): %s", wrapper.profile == null ? "unknown" : wrapper.profile.getVersion().name);
LogHelper.info("Start Minecraft Server");
LogHelper.debug("Invoke main method %s", mainClass.getName());
@ -228,7 +240,7 @@ public void setConfig(Config config) {
@Override
public Config getDefaultConfig() {
Config newConfig = new Config();
newConfig.title = "Your profile title";
newConfig.serverName = "your server name";
newConfig.projectname = "MineCraft";
newConfig.login = "login";
newConfig.password = "password";
@ -244,9 +256,11 @@ public Config getDefaultConfig() {
}
public static final class Config {
@Deprecated
public String title;
public String projectname;
public String address;
public String serverName;
public WebSocketConf websocket;
public int reconnectCount;
public int reconnectSleep;

View file

@ -46,6 +46,8 @@ public void run() throws IOException {
}
}
LogHelper.info("Found MainClass %s", mainClassName);
System.out.println("Print your server name:");
wrapper.config.serverName = commands.commandHandler.readLine();
System.out.println("Print launchserver websocket host( ws://host:port/api ):");
String address = commands.commandHandler.readLine();
wrapper.config.mainclass = mainClassName;
@ -56,14 +58,10 @@ public void run() throws IOException {
String login = commands.commandHandler.readLine();
System.out.println("Print server account password:");
String password = commands.commandHandler.readLine();
System.out.println("Print profile title:");
String title = commands.commandHandler.readLine();
wrapper.config.login = login;
wrapper.config.password = password;
wrapper.config.title = title;
wrapper.config.stopOnError = false;
wrapper.updateLauncherConfig();
if (wrapper.auth()) {
break;
} else {

View file

@ -5,7 +5,7 @@
id 'org.openjfx.javafxplugin' version '0.0.8' apply false
}
group = 'pro.gravit.launcher'
version = '5.1.8'
version = '5.1.9'
apply from: 'props.gradle'

@ -1 +1 @@
Subproject commit 71c224e4d7a0950d3b5599ba4dec8d6fd04ededd
Subproject commit 5c4f6850bd4feeee0caff5561564b7e54bb94774