mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-23 00:51:01 +03:00
Merge branch 'release/5.1.9'
This commit is contained in:
commit
1b2be8389a
43 changed files with 682 additions and 164 deletions
70
.github/workflows/codeql-analysis.yml
vendored
Normal file
70
.github/workflows/codeql-analysis.yml
vendored
Normal 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
17
Dockerfile
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
package pro.gravit.launchserver.auth;
|
||||
|
||||
public interface RequiredDAO {
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
|||
Subproject commit 71c224e4d7a0950d3b5599ba4dec8d6fd04ededd
|
||||
Subproject commit 5c4f6850bd4feeee0caff5561564b7e54bb94774
|
Loading…
Reference in a new issue