[FEATURE][EXPERIMENTAL] New ServerWrapper auth system

This commit is contained in:
Gravita 2021-09-25 17:40:08 +07:00
parent 4c78d00360
commit 958686a032
13 changed files with 152 additions and 189 deletions

View file

@ -58,6 +58,7 @@ public static void registerCommands(pro.gravit.utils.command.CommandHandler hand
service.registerCommand("signDir", new SignDirCommand(server)); service.registerCommand("signDir", new SignDirCommand(server));
service.registerCommand("pingServers", new PingServersCommand(server)); service.registerCommand("pingServers", new PingServersCommand(server));
service.registerCommand("securitycheck", new SecurityCheckCommand(server)); service.registerCommand("securitycheck", new SecurityCheckCommand(server));
service.registerCommand("token", new TokenCommand(server));
Category serviceCategory = new Category(service, "service", "Managing LaunchServer Components"); Category serviceCategory = new Category(service, "service", "Managing LaunchServer Components");
handler.registerCategory(serviceCategory); handler.registerCategory(serviceCategory);
} }

View file

@ -0,0 +1,46 @@
package pro.gravit.launchserver.command.service;
import io.jsonwebtoken.Jwts;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.command.Command;
import pro.gravit.utils.command.SubCommand;
public class TokenCommand extends Command {
private transient final Logger logger = LogManager.getLogger();
public TokenCommand(LaunchServer server) {
super(server);
this.childCommands.put("info", new SubCommand("[token]", "print token info") {
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
var parser = Jwts.parserBuilder().setSigningKey(server.keyAgreementManager.ecdsaPublicKey).build();
var claims = parser.parseClaimsJws(args[0]);
logger.info("Token: {}", claims.getBody());
}
});
this.childCommands.put("server", new SubCommand("[serverName] (authId)", "generate new server token") {
@Override
public void invoke(String... args) throws Exception {
String token = server.authManager.newCheckServerToken(args[0], args.length > 1 ? args[1] : server.config.getAuthProviderPair().name);
logger.info("Token: {}", token);
}
});
}
@Override
public String getArgsDescription() {
return "[new/info/token name] [args]";
}
@Override
public String getUsageDescription() {
return "jwt management";
}
@Override
public void invoke(String... args) throws Exception {
invokeSubcommands(args);
}
}

View file

@ -1,5 +1,8 @@
package pro.gravit.launchserver.manangers; package pro.gravit.launchserver.manangers;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.ClientPermissions; import pro.gravit.launcher.ClientPermissions;
@ -19,6 +22,7 @@
import pro.gravit.launchserver.auth.texture.TextureProvider; import pro.gravit.launchserver.auth.texture.TextureProvider;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.auth.AuthResponse; import pro.gravit.launchserver.socket.response.auth.AuthResponse;
import pro.gravit.launchserver.socket.response.auth.RestoreResponse;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.helper.SecurityHelper;
@ -33,9 +37,53 @@
public class AuthManager { public class AuthManager {
private transient final LaunchServer server; private transient final LaunchServer server;
private transient final Logger logger = LogManager.getLogger(); private transient final Logger logger = LogManager.getLogger();
private transient final JwtParser checkServerTokenParser;
public AuthManager(LaunchServer server) { public AuthManager(LaunchServer server) {
this.server = server; this.server = server;
this.checkServerTokenParser = Jwts.parserBuilder()
.requireIssuer("LaunchServer")
.require("tokenType", "checkServer")
.setSigningKey(server.keyAgreementManager.ecdsaPublicKey)
.build();
}
public String newCheckServerToken(String serverName, String authId) {
return Jwts.builder()
.setIssuer("LaunchServer")
.claim("serverName", serverName)
.claim("authId", authId)
.claim("tokenType", "checkServer")
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
.compact();
}
public record CheckServerTokenInfo(String serverName, String authId) {
}
public CheckServerTokenInfo parseCheckServerToken(String token) {
try {
var jwt = checkServerTokenParser.parseClaimsJws(token).getBody();
return new CheckServerTokenInfo(jwt.get("serverName", String.class), jwt.get("authId", String.class));
} catch (Exception e) {
return null;
}
}
public class CheckServerVerifier implements RestoreResponse.ExtendedTokenProvider {
@Override
public boolean accept(Client client, AuthProviderPair pair, String extendedToken) {
var info = parseCheckServerToken(extendedToken);
if(info == null) {
return false;
}
client.auth_id = info.authId;
client.auth = server.config.getAuthProviderPair(info.authId);
if(client.permissions == null) client.permissions = new ClientPermissions();
client.permissions.addAction("launchserver\\.checkserver");
return true;
}
} }
/** /**

View file

@ -5,6 +5,7 @@
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import pro.gravit.launcher.events.request.CheckServerRequestEvent; import pro.gravit.launcher.events.request.CheckServerRequestEvent;
import pro.gravit.launchserver.auth.AuthException; import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.manangers.AuthManager; import pro.gravit.launchserver.manangers.AuthManager;
import pro.gravit.launchserver.socket.Client; import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse; import pro.gravit.launchserver.socket.response.SimpleResponse;
@ -23,7 +24,7 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client pClient) { public void execute(ChannelHandlerContext ctx, Client pClient) {
if (!pClient.isAuth || pClient.type == AuthResponse.ConnectTypes.CLIENT) { if (pClient.permissions == null || !pClient.permissions.hasAction("launchserver.checkserver")) {
sendError("Permissions denied"); sendError("Permissions denied");
return; return;
} }
@ -46,5 +47,4 @@ public void execute(ChannelHandlerContext ctx, Client pClient) {
} }
sendResult(result); sendResult(result);
} }
} }

View file

@ -15,12 +15,14 @@
import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.AuthRequest;
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest; import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
import pro.gravit.launcher.request.auth.RestoreRequest;
import pro.gravit.launcher.request.update.ProfilesRequest; import pro.gravit.launcher.request.update.ProfilesRequest;
import pro.gravit.launcher.server.setup.ServerWrapperSetup; import pro.gravit.launcher.server.setup.ServerWrapperSetup;
import pro.gravit.utils.PublicURLClassLoader; import pro.gravit.utils.PublicURLClassLoader;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.LogHelper;
import java.io.IOException;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType; import java.lang.invoke.MethodType;
@ -30,14 +32,12 @@
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
public class ServerWrapper extends JsonConfigurable<ServerWrapper.Config> { public class ServerWrapper extends JsonConfigurable<ServerWrapper.Config> {
public static final Path modulesDir = Paths.get(System.getProperty("serverwrapper.modulesDir", "modules"));
public static final Path modulesConfigDir = Paths.get(System.getProperty("serverwrapper.modulesConfigDir", "modules-config"));
public static final Path configFile = Paths.get(System.getProperty("serverwrapper.configFile", "ServerWrapperConfig.json")); public static final Path configFile = Paths.get(System.getProperty("serverwrapper.configFile", "ServerWrapperConfig.json"));
public static final boolean disableSetup = Boolean.parseBoolean(System.getProperty("serverwrapper.disableSetup", "false")); public static final boolean disableSetup = Boolean.parseBoolean(System.getProperty("serverwrapper.disableSetup", "false"));
public static ServerWrapperModulesManager modulesManager;
public static ServerWrapper wrapper; public static ServerWrapper wrapper;
public Config config; public Config config;
public PublicURLClassLoader ucp; public PublicURLClassLoader ucp;
@ -51,48 +51,29 @@ public ServerWrapper(Type type, Path configPath) {
super(type, configPath); super(type, configPath);
} }
public static void initGson(ServerWrapperModulesManager modulesManager) { public static void initGson() {
Launcher.gsonManager = new ServerWrapperGsonManager(modulesManager); Launcher.gsonManager = new ServerWrapperGsonManager();
Launcher.gsonManager.initGson(); Launcher.gsonManager.initGson();
} }
public static void main(String... args) throws Throwable { public static void main(String... args) throws Throwable {
LogHelper.printVersion("ServerWrapper"); LogHelper.printVersion("ServerWrapper");
LogHelper.printLicense("ServerWrapper"); LogHelper.printLicense("ServerWrapper");
modulesManager = new ServerWrapperModulesManager(modulesDir, modulesConfigDir);
modulesManager.autoload();
modulesManager.initModules(null);
ServerWrapper.wrapper = new ServerWrapper(ServerWrapper.Config.class, configFile); ServerWrapper.wrapper = new ServerWrapper(ServerWrapper.Config.class, configFile);
ServerWrapper.wrapper.run(args); ServerWrapper.wrapper.run(args);
} }
public boolean auth() { public void restore() throws Exception {
try { if(config.oauth != null) {
Launcher.getConfig(); Request.setOAuth(config.authId, config.oauth, config.oauthExpireTime);
AuthRequest request = new AuthRequest(config.login, config.password, config.auth_id, AuthRequest.ConnectTypes.API);
AuthRequestEvent authResult = request.request();
if (config.saveSession) {
if (authResult.oauth != null) {
Request.setOAuth(config.auth_id, authResult.oauth);
config.oauth = authResult.oauth;
config.oauthExpireTime = Request.getTokenExpiredTime();
} else {
Request.setSession(authResult.session);
} }
saveConfig(); if(config.extendedTokens != null) {
} Request.addAllExtendedToken(config.extendedTokens);
permissions = authResult.permissions;
playerProfile = authResult.playerProfile;
return true;
} catch (Throwable e) {
LogHelper.error(e);
if (config.stopOnError) System.exit(-1);
return false;
} }
Request.restore();
} }
public ProfilesRequestEvent getProfiles() { public ProfilesRequestEvent getProfiles() throws Exception {
try {
ProfilesRequestEvent result = new ProfilesRequest().request(); ProfilesRequestEvent result = new ProfilesRequest().request();
for (ClientProfile p : result.profiles) { for (ClientProfile p : result.profiles) {
LogHelper.debug("Get profile: %s", p.getTitle()); LogHelper.debug("Get profile: %s", p.getTitle());
@ -113,15 +94,10 @@ public ProfilesRequestEvent getProfiles() {
LogHelper.warning("Not connected to ServerProfile. May be serverName incorrect?"); LogHelper.warning("Not connected to ServerProfile. May be serverName incorrect?");
} }
return result; return result;
} catch (Throwable e) {
LogHelper.error(e);
if (config.stopOnError) System.exit(-1);
return null;
}
} }
public void run(String... args) throws Throwable { public void run(String... args) throws Throwable {
initGson(modulesManager); initGson();
AuthRequest.registerProviders(); AuthRequest.registerProviders();
GetAvailabilityAuthRequest.registerProviders(); GetAvailabilityAuthRequest.registerProviders();
OptionalAction.registerProviders(); OptionalAction.registerProviders();
@ -133,7 +109,6 @@ public void run(String... args) throws Throwable {
setup.run(); setup.run();
System.exit(0); System.exit(0);
} }
modulesManager.invokeEvent(new PreConfigPhase());
LogHelper.debug("Read ServerWrapperConfig.json"); LogHelper.debug("Read ServerWrapperConfig.json");
loadConfig(); loadConfig();
updateLauncherConfig(); updateLauncherConfig();
@ -141,30 +116,9 @@ public void run(String... args) throws Throwable {
else Launcher.applyLauncherEnv(LauncherConfig.LauncherEnvironment.STD); else Launcher.applyLauncherEnv(LauncherConfig.LauncherEnvironment.STD);
if (config.logFile != null) LogHelper.addOutput(IOHelper.newWriter(Paths.get(config.logFile), true)); if (config.logFile != null) LogHelper.addOutput(IOHelper.newWriter(Paths.get(config.logFile), true));
{ {
if (config.saveSession) { restore();
boolean needRestore = false;
if (config.oauth != null) {
Request.setOAuth(config.auth_id, config.oauth, config.oauthExpireTime);
needRestore = true;
} else if (config.session != null) {
Request.setSession(config.session);
needRestore = true;
} else {
auth();
}
try {
if (needRestore)
Request.restore();
} catch (Exception e) {
LogHelper.error(e);
auth();
}
} else {
auth();
}
getProfiles(); getProfiles();
} }
modulesManager.invokeEvent(new ServerWrapperInitPhase(this));
String classname = (config.mainclass == null || config.mainclass.isEmpty()) ? args[0] : config.mainclass; String classname = (config.mainclass == null || config.mainclass.isEmpty()) ? args[0] : config.mainclass;
if (classname.length() == 0) { if (classname.length() == 0) {
LogHelper.error("MainClass not found. Please set MainClass for ServerWrapper.cfg or first commandline argument"); LogHelper.error("MainClass not found. Please set MainClass for ServerWrapper.cfg or first commandline argument");
@ -200,20 +154,15 @@ public void run(String... args) throws Throwable {
if (loader != null) mainClass = Class.forName(classname, true, loader); if (loader != null) mainClass = Class.forName(classname, true, loader);
else mainClass = Class.forName(classname); else mainClass = Class.forName(classname);
MethodHandle mainMethod = MethodHandles.publicLookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)); MethodHandle mainMethod = MethodHandles.publicLookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
modulesManager.invokeEvent(new PostInitPhase());
Request.service.reconnectCallback = () -> Request.service.reconnectCallback = () ->
{ {
LogHelper.debug("WebSocket connect closed. Try reconnect"); LogHelper.debug("WebSocket connect closed. Try reconnect");
if (config.saveSession) {
try { try {
Request.restore(); restore();
} catch (Exception e) {
auth();
}
} else {
auth();
}
getProfiles(); getProfiles();
} catch (Exception e) {
LogHelper.error(e);
}
}; };
LogHelper.info("ServerWrapper: Project %s, LaunchServer address: %s. Title: %s", config.projectname, config.address, Launcher.profile != null ? Launcher.profile.getTitle() : "unknown"); 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("Minecraft Version (for profile): %s", wrapper.profile == null ? "unknown" : wrapper.profile.getVersion().name);
@ -253,12 +202,10 @@ public Config getDefaultConfig() {
Config newConfig = new Config(); Config newConfig = new Config();
newConfig.serverName = "your server name"; newConfig.serverName = "your server name";
newConfig.projectname = "MineCraft"; newConfig.projectname = "MineCraft";
newConfig.login = "login";
newConfig.password = "password";
newConfig.mainclass = ""; newConfig.mainclass = "";
newConfig.extendedTokens = new HashMap<>();
newConfig.syncAuth = true; newConfig.syncAuth = true;
newConfig.stopOnError = true; newConfig.stopOnError = true;
newConfig.saveSession = true;
newConfig.reconnectCount = 10; newConfig.reconnectCount = 10;
newConfig.reconnectSleep = 1000; newConfig.reconnectSleep = 1000;
newConfig.address = "ws://localhost:9274/api"; newConfig.address = "ws://localhost:9274/api";
@ -280,14 +227,11 @@ public static final class Config {
public String classpath; public String classpath;
public String librariesDir; public String librariesDir;
public String mainclass; public String mainclass;
public String login;
public String[] args; public String[] args;
public String password; public String authId;
public String auth_id = "";
public boolean saveSession;
public AuthRequestEvent.OAuthRequestEvent oauth; public AuthRequestEvent.OAuthRequestEvent oauth;
public long oauthExpireTime; public long oauthExpireTime;
public UUID session; public Map<String, String> extendedTokens;
public LauncherConfig.LauncherEnvironment env; public LauncherConfig.LauncherEnvironment env;
} }

View file

@ -6,16 +6,13 @@
import pro.gravit.launcher.request.websockets.ClientWebSocketService; import pro.gravit.launcher.request.websockets.ClientWebSocketService;
public class ServerWrapperGsonManager extends GsonManager { public class ServerWrapperGsonManager extends GsonManager {
private final ServerWrapperModulesManager modulesManager;
public ServerWrapperGsonManager(ServerWrapperModulesManager modulesManager) { public ServerWrapperGsonManager() {
this.modulesManager = modulesManager;
} }
@Override @Override
public void registerAdapters(GsonBuilder builder) { public void registerAdapters(GsonBuilder builder) {
super.registerAdapters(builder); super.registerAdapters(builder);
ClientWebSocketService.appendTypeAdapters(builder); ClientWebSocketService.appendTypeAdapters(builder);
modulesManager.invokeEvent(new PreGsonPhase(builder));
} }
} }

View file

@ -1,11 +0,0 @@
package pro.gravit.launcher.server;
import pro.gravit.launcher.modules.LauncherInitContext;
public class ServerWrapperInitContext implements LauncherInitContext {
public final ServerWrapper serverWrapper;
public ServerWrapperInitContext(ServerWrapper serverWrapper) {
this.serverWrapper = serverWrapper;
}
}

View file

@ -1,11 +0,0 @@
package pro.gravit.launcher.server;
import pro.gravit.launcher.modules.LauncherModule;
public class ServerWrapperInitPhase extends LauncherModule.Event {
public final ServerWrapper serverWrapper;
public ServerWrapperInitPhase(ServerWrapper serverWrapper) {
this.serverWrapper = serverWrapper;
}
}

View file

@ -1,15 +0,0 @@
package pro.gravit.launcher.server;
import pro.gravit.launcher.modules.impl.SimpleModuleManager;
import java.nio.file.Path;
public class ServerWrapperModulesManager extends SimpleModuleManager {
public ServerWrapperModulesManager(Path modulesDir, Path configDir) {
super(modulesDir, configDir);
}
public void fullInitializeServerWrapper(ServerWrapper serverWrapper) {
initContext = new ServerWrapperInitContext(serverWrapper);
}
}

View file

@ -1,11 +0,0 @@
package pro.gravit.launcher.server.setup;
import pro.gravit.launcher.modules.LauncherModule;
public class ServerWrapperPostSetupEvent extends LauncherModule.Event {
public final ServerWrapperSetup setup;
public ServerWrapperPostSetupEvent(ServerWrapperSetup setup) {
this.setup = setup;
}
}

View file

@ -1,11 +0,0 @@
package pro.gravit.launcher.server.setup;
import pro.gravit.launcher.modules.LauncherModule;
public class ServerWrapperPreSetupEvent extends LauncherModule.Event {
public final ServerWrapperSetup setup;
public ServerWrapperPreSetupEvent(ServerWrapperSetup setup) {
this.setup = setup;
}
}

View file

@ -1,5 +1,6 @@
package pro.gravit.launcher.server.setup; package pro.gravit.launcher.server.setup;
import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.server.ServerWrapper; import pro.gravit.launcher.server.ServerWrapper;
import pro.gravit.utils.PublicURLClassLoader; import pro.gravit.utils.PublicURLClassLoader;
import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.IOHelper;
@ -12,6 +13,7 @@
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap;
import java.util.jar.JarFile; import java.util.jar.JarFile;
public class ServerWrapperSetup { public class ServerWrapperSetup {
@ -22,9 +24,8 @@ public ServerWrapperSetup() throws IOException {
commands = new ServerWrapperCommands(); commands = new ServerWrapperCommands();
} }
public void run() throws IOException { public void run() throws Exception {
ServerWrapper wrapper = ServerWrapper.wrapper; ServerWrapper wrapper = ServerWrapper.wrapper;
ServerWrapper.modulesManager.invokeEvent(new ServerWrapperPreSetupEvent(this));
System.out.println("Print server jar filename:"); System.out.println("Print server jar filename:");
String jarName = commands.commandHandler.readLine(); String jarName = commands.commandHandler.readLine();
Path jarPath = Paths.get(jarName); Path jarPath = Paths.get(jarName);
@ -53,24 +54,22 @@ public void run() throws IOException {
} }
System.out.println("Print your server name:"); System.out.println("Print your server name:");
wrapper.config.serverName = commands.commandHandler.readLine(); 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; wrapper.config.mainclass = mainClassName;
wrapper.config.address = address;
boolean stopOnError = wrapper.config.stopOnError; boolean stopOnError = wrapper.config.stopOnError;
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
System.out.println("Print server account login:"); System.out.println("Print launchserver websocket host( ws://host:port/api ):");
String login = commands.commandHandler.readLine(); wrapper.config.address = commands.commandHandler.readLine();
System.out.println("Print server account password:"); System.out.println("Print server token:");
String password = commands.commandHandler.readLine(); String checkServerToken = commands.commandHandler.readLine();
wrapper.config.login = login; wrapper.config.extendedTokens.put("checkServer", checkServerToken);
wrapper.config.password = password;
wrapper.config.stopOnError = false; wrapper.config.stopOnError = false;
wrapper.updateLauncherConfig(); wrapper.updateLauncherConfig();
if (wrapper.auth() && wrapper.getProfiles() != null) { try {
break; wrapper.restore();
} else { wrapper.getProfiles();
LogHelper.error("Auth error. Recheck account params"); } catch (Throwable e) {
LogHelper.error(e);
Request.service.close();
} }
} }
wrapper.config.stopOnError = stopOnError; wrapper.config.stopOnError = stopOnError;
@ -84,7 +83,6 @@ public void run() throws IOException {
Path startScriptBak = Paths.get("start.bak"); Path startScriptBak = Paths.get("start.bak");
IOHelper.move(startScript, startScriptBak); IOHelper.move(startScript, startScriptBak);
} }
ServerWrapper.modulesManager.invokeEvent(new ServerWrapperSetupEvent(this));
try (Writer writer = IOHelper.newWriter(startScript)) { try (Writer writer = IOHelper.newWriter(startScript)) {
if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX) { if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX) {
writer.append("#!/bin/bash\n\n"); writer.append("#!/bin/bash\n\n");
@ -113,6 +111,5 @@ public void run() throws IOException {
writer.append(ServerWrapper.class.getName()); writer.append(ServerWrapper.class.getName());
writer.append("\n"); writer.append("\n");
} }
ServerWrapper.modulesManager.invokeEvent(new ServerWrapperPostSetupEvent(this));
} }
} }

View file

@ -1,11 +0,0 @@
package pro.gravit.launcher.server.setup;
import pro.gravit.launcher.modules.LauncherModule;
public class ServerWrapperSetupEvent extends LauncherModule.Event {
public final ServerWrapperSetup setup;
public ServerWrapperSetupEvent(ServerWrapperSetup setup) {
this.setup = setup;
}
}