diff --git a/Launcher/src/main/java/pro/gravit/launcher/LauncherEngine.java b/Launcher/src/main/java/pro/gravit/launcher/LauncherEngine.java index 6231dad4..561d966a 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/LauncherEngine.java +++ b/Launcher/src/main/java/pro/gravit/launcher/LauncherEngine.java @@ -6,6 +6,7 @@ import pro.gravit.launcher.client.events.ClientPreGuiPhase; import pro.gravit.launcher.console.GetPublicKeyCommand; import pro.gravit.launcher.console.SignDataCommand; +import pro.gravit.launcher.events.request.*; import pro.gravit.launcher.guard.LauncherGuardInterface; import pro.gravit.launcher.guard.LauncherGuardManager; import pro.gravit.launcher.guard.LauncherNoGuard; @@ -14,14 +15,21 @@ import pro.gravit.launcher.gui.RuntimeProvider; import pro.gravit.launcher.managers.ClientGsonManager; import pro.gravit.launcher.managers.ConsoleManager; +import pro.gravit.launcher.modules.events.OfflineModeEvent; import pro.gravit.launcher.modules.events.PreConfigPhase; import pro.gravit.launcher.profiles.optional.actions.OptionalAction; import pro.gravit.launcher.profiles.optional.triggers.OptionalTrigger; import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.RequestException; -import pro.gravit.launcher.request.auth.AuthRequest; -import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest; +import pro.gravit.launcher.request.RequestService; +import pro.gravit.launcher.request.auth.*; +import pro.gravit.launcher.request.auth.details.AuthLoginOnlyDetails; +import pro.gravit.launcher.request.management.FeaturesRequest; +import pro.gravit.launcher.request.secure.GetSecureLevelInfoRequest; +import pro.gravit.launcher.request.secure.SecurityReportRequest; +import pro.gravit.launcher.request.update.LauncherRequest; import pro.gravit.launcher.request.websockets.ClientWebSocketService; +import pro.gravit.launcher.request.websockets.OfflineRequestService; import pro.gravit.launcher.request.websockets.StdWebSocketService; import pro.gravit.launcher.utils.NativeJVMHalt; import pro.gravit.utils.helper.*; @@ -34,7 +42,9 @@ import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -143,6 +153,35 @@ public static LauncherGuardInterface tryGetStdGuard() { return null; } + public static RequestService initOffline() { + OfflineRequestService service = new OfflineRequestService(); + applyBasicOfflineProcessors(service); + OfflineModeEvent event = new OfflineModeEvent(service); + modulesManager.invokeEvent(event); + return event.service; + } + + public static void applyBasicOfflineProcessors(OfflineRequestService service) { + service.registerRequestProcessor(LauncherRequest.class, (r) -> new LauncherRequestEvent(false, (String) null)); + service.registerRequestProcessor(CheckServerRequest.class, (r) -> { + throw new RequestException("CheckServer disabled in offline mode"); + }); + service.registerRequestProcessor(GetAvailabilityAuthRequest.class, (r) -> { + List details = new ArrayList<>(); + details.add(new AuthLoginOnlyDetails()); + GetAvailabilityAuthRequestEvent.AuthAvailability authAvailability = new GetAvailabilityAuthRequestEvent.AuthAvailability("offline", "Offline Mode", details); + List list = new ArrayList<>(1); + list.add(authAvailability); + return new GetAvailabilityAuthRequestEvent(list); + }); + service.registerRequestProcessor(JoinServerRequest.class, (r) -> new JoinServerRequestEvent(false)); + service.registerRequestProcessor(ExitRequest.class, (r) -> new ExitRequestEvent(ExitRequestEvent.ExitReason.CLIENT)); + service.registerRequestProcessor(SetProfileRequest.class, (r) -> new SetProfileRequestEvent(null)); + service.registerRequestProcessor(FeaturesRequest.class, (r) -> new FeaturesRequestEvent()); + service.registerRequestProcessor(GetSecureLevelInfoRequest.class, (r) -> new GetSecureLevelInfoRequestEvent(null, false)); + service.registerRequestProcessor(SecurityReportRequest.class, (r) -> new SecurityReportRequestEvent(SecurityReportRequestEvent.ReportAction.NONE)); + } + public static LauncherEngine clientInstance() { return new LauncherEngine(true); } @@ -193,18 +232,29 @@ public void start(String... args) throws Throwable { if (!Request.isAvailable()) { String address = Launcher.getConfig().address; LogHelper.debug("Start async connection to %s", address); - StdWebSocketService service = StdWebSocketService.initWebSockets(address, true); - Request.setRequestService(service); - service.reconnectCallback = () -> - { - LogHelper.debug("WebSocket connect closed. Try reconnect"); - try { - Request.reconnect(); - } catch (Exception e) { + RequestService service; + try { + service = StdWebSocketService.initWebSockets(address).get(); + } catch (Throwable e) { + if(LogHelper.isDebugEnabled()) { LogHelper.error(e); - throw new RequestException("Connection failed", e); } - }; + LogHelper.warning("Launcher in offline mode"); + service = initOffline(); + } + Request.setRequestService(service); + if(service instanceof StdWebSocketService) { + ((StdWebSocketService) service).reconnectCallback = () -> + { + LogHelper.debug("WebSocket connect closed. Try reconnect"); + try { + Request.reconnect(); + } catch (Exception e) { + LogHelper.error(e); + throw new RequestException("Connection failed", e); + } + }; + } service.registerEventHandler(new BasicLauncherEventHandler()); } Objects.requireNonNull(args, "args"); diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java b/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java index 5c799944..2c6e72b4 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java +++ b/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java @@ -4,12 +4,16 @@ import pro.gravit.launcher.api.AuthService; import pro.gravit.launcher.api.ClientService; import pro.gravit.launcher.client.events.client.*; +import pro.gravit.launcher.events.request.ProfileByUUIDRequestEvent; +import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent; import pro.gravit.launcher.guard.LauncherGuardManager; import pro.gravit.launcher.hasher.FileNameMatcher; import pro.gravit.launcher.hasher.HashedDir; import pro.gravit.launcher.hasher.HashedEntry; import pro.gravit.launcher.managers.ClientGsonManager; import pro.gravit.launcher.managers.ConsoleManager; +import pro.gravit.launcher.modules.LauncherModulesManager; +import pro.gravit.launcher.modules.events.OfflineModeEvent; import pro.gravit.launcher.modules.events.PreConfigPhase; import pro.gravit.launcher.patches.FMLPatcher; import pro.gravit.launcher.profiles.ClientProfile; @@ -19,8 +23,13 @@ import pro.gravit.launcher.profiles.optional.triggers.OptionalTrigger; import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.RequestException; +import pro.gravit.launcher.request.RequestService; import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest; +import pro.gravit.launcher.request.uuid.BatchProfileByUsernameRequest; +import pro.gravit.launcher.request.uuid.ProfileByUUIDRequest; +import pro.gravit.launcher.request.uuid.ProfileByUsernameRequest; +import pro.gravit.launcher.request.websockets.OfflineRequestService; import pro.gravit.launcher.request.websockets.StdWebSocketService; import pro.gravit.launcher.serialize.HInput; import pro.gravit.launcher.utils.DirWatcher; @@ -113,21 +122,25 @@ public static void main(String[] args) throws Throwable { List classpath = resolveClassPath(clientDir, params.actions, params.profile).map(IOHelper::toURL).collect(Collectors.toList()); // Start client with WatchService monitoring boolean digest = !profile.isUpdateFastCheck(); - StdWebSocketService service = StdWebSocketService.initWebSockets(Launcher.getConfig().address, false); - LogHelper.debug("Restore sessions"); - Request.restore(); - - service.registerEventHandler(new BasicLauncherEventHandler()); - service.reconnectCallback = () -> - { - LogHelper.debug("WebSocket connect closed. Try reconnect"); - try { - Request.reconnect(); - } catch (Exception e) { - LogHelper.error(e); - throw new RequestException("Connection failed", e); - } - }; + RequestService service; + if(params.offlineMode) { + service = initOffline(LauncherEngine.modulesManager, params); + } else { + service = StdWebSocketService.initWebSockets(Launcher.getConfig().address).get(); + LogHelper.debug("Restore sessions"); + Request.restore(); + service.registerEventHandler(new BasicLauncherEventHandler()); + ((StdWebSocketService) service).reconnectCallback = () -> + { + LogHelper.debug("WebSocket connect closed. Try reconnect"); + try { + Request.reconnect(); + } catch (Exception e) { + LogHelper.error(e); + throw new RequestException("Connection failed", e); + } + }; + } Request.setRequestService(service); ClientProfile.ClassLoaderConfig classLoaderConfig = profile.getClassLoaderConfig(); if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) { @@ -204,6 +217,30 @@ private static void initGson(ClientModuleManager moduleManager) { Launcher.gsonManager.initGson(); } + public static RequestService initOffline(LauncherModulesManager modulesManager, ClientLauncherProcess.ClientParams params) { + OfflineRequestService service = new OfflineRequestService(); + LauncherEngine.applyBasicOfflineProcessors(service); + applyClientOfflineProcessors(service, params); + OfflineModeEvent event = new OfflineModeEvent(service); + modulesManager.invokeEvent(event); + return event.service; + } + + public static void applyClientOfflineProcessors(OfflineRequestService service, ClientLauncherProcess.ClientParams params) { + service.registerRequestProcessor(ProfileByUsernameRequest.class, (r) -> { + if(params.playerProfile.username.equals(r.username)) { + return new ProfileByUsernameRequestEvent(params.playerProfile); + } + throw new RequestException("User not found"); + }); + service.registerRequestProcessor(ProfileByUUIDRequest.class, (r) -> { + if(params.playerProfile.uuid.equals(r.uuid)) { + return new ProfileByUUIDRequestEvent(params.playerProfile); + } + throw new RequestException("User not found"); + }); + } + public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException { //if (matcher != null) // matcher = matcher.verifyOnly(); diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherProcess.java b/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherProcess.java index 1dbe37ab..f2a6e111 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherProcess.java +++ b/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherProcess.java @@ -281,6 +281,8 @@ public static class ClientParams { public Map extendedTokens; + public boolean offlineMode; + public transient HashedDir assetHDir; public transient HashedDir clientHDir; diff --git a/Launcher/src/main/java/pro/gravit/launcher/debug/DebugMain.java b/Launcher/src/main/java/pro/gravit/launcher/debug/DebugMain.java index 2f5c88f8..27b1348b 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/debug/DebugMain.java +++ b/Launcher/src/main/java/pro/gravit/launcher/debug/DebugMain.java @@ -7,7 +7,12 @@ import pro.gravit.launcher.client.ClientModuleManager; import pro.gravit.launcher.managers.ConsoleManager; import pro.gravit.launcher.modules.LauncherModule; +import pro.gravit.launcher.modules.events.OfflineModeEvent; import pro.gravit.launcher.modules.events.PreConfigPhase; +import pro.gravit.launcher.request.Request; +import pro.gravit.launcher.request.RequestService; +import pro.gravit.launcher.request.websockets.OfflineRequestService; +import pro.gravit.launcher.request.websockets.StdWebSocketService; import pro.gravit.utils.helper.LogHelper; import java.lang.invoke.MethodHandles; @@ -22,6 +27,7 @@ public class DebugMain { public static String webSocketURL = System.getProperty("launcherdebug.websocket", "ws://localhost:9274/api"); public static String projectName = System.getProperty("launcherdebug.projectname", "Minecraft"); public static String unlockKey = System.getProperty("launcherdebug.unlockkey", "0000"); + public static boolean offlineMode = Boolean.getBoolean("launcherdebug.offlinemode"); public static String[] moduleClasses = System.getProperty("launcherdebug.modules", "").split(","); public static String[] moduleFiles = System.getProperty("launcherdebug.modulefiles", "").split(","); public static LauncherConfig.LauncherEnvironment environment = LauncherConfig.LauncherEnvironment.valueOf(System.getProperty("launcherdebug.env", "STD")); @@ -50,6 +56,17 @@ public static void main(String[] args) throws Throwable { LauncherEngine.initGson(LauncherEngine.modulesManager); ConsoleManager.initConsole(); LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase()); + RequestService service; + if(offlineMode) { + OfflineRequestService offlineRequestService = new OfflineRequestService(); + LauncherEngine.applyBasicOfflineProcessors(offlineRequestService); + OfflineModeEvent event = new OfflineModeEvent(offlineRequestService); + LauncherEngine.modulesManager.invokeEvent(event); + service = event.service; + } else { + service = StdWebSocketService.initWebSockets(webSocketURL).get(); + } + Request.setRequestService(service); LogHelper.debug("Initialization LauncherEngine"); LauncherEngine instance = LauncherEngine.newInstance(false); instance.start(args); diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/GetSecureLevelInfoRequestEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/GetSecureLevelInfoRequestEvent.java index bd5bbf6e..2b8fa051 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/GetSecureLevelInfoRequestEvent.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/events/request/GetSecureLevelInfoRequestEvent.java @@ -10,6 +10,11 @@ public GetSecureLevelInfoRequestEvent(byte[] verifySecureKey) { this.verifySecureKey = verifySecureKey; } + public GetSecureLevelInfoRequestEvent(byte[] verifySecureKey, boolean enabled) { + this.verifySecureKey = verifySecureKey; + this.enabled = enabled; + } + @Override public String getType() { return "getSecureLevelInfo"; diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/OfflineModeEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/OfflineModeEvent.java new file mode 100644 index 00000000..28a92579 --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/events/OfflineModeEvent.java @@ -0,0 +1,12 @@ +package pro.gravit.launcher.modules.events; + +import pro.gravit.launcher.modules.LauncherModule; +import pro.gravit.launcher.request.RequestService; + +public class OfflineModeEvent extends LauncherModule.Event { + public RequestService service; + + public OfflineModeEvent(RequestService service) { + this.service = service; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/Request.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/Request.java index 867008eb..e28bde03 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/request/Request.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/Request.java @@ -214,8 +214,9 @@ public void removeOAuthChangeHandler(BiConsumer implements WebS public static final ProviderMap providers = new ProviderMap<>(); private static boolean registerProviders = false; @LauncherNetworkAPI - private final String login; + public final String login; @LauncherNetworkAPI - private final AuthPasswordInterface password; + public final AuthPasswordInterface password; @LauncherNetworkAPI - private final String auth_id; + public final String auth_id; @LauncherNetworkAPI - private final boolean getSession; + public final boolean getSession; @LauncherNetworkAPI - private final ConnectTypes authType; + public final ConnectTypes authType; public AuthRequest(String login, String password, String auth_id, ConnectTypes authType) { this.login = login; diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientJSONPoint.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientJSONPoint.java index c2018379..c2dda916 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientJSONPoint.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientJSONPoint.java @@ -24,6 +24,8 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; public abstract class ClientJSONPoint { @@ -94,16 +96,26 @@ public void open() throws Exception { webSocketClientHandler.handshakeFuture().sync(); } - public void openAsync(Runnable onConnect) { + public void openAsync(Runnable onConnect, Consumer onFail) { //System.out.println("WebSocket Client connecting"); webSocketClientHandler = new WebSocketClientHandler( WebSocketClientHandshakerFactory.newHandshaker( uri, WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, 12800000), this); ChannelFuture future = bootstrap.connect(uri.getHost(), port); - future.addListener((e) -> { - ch = future.channel(); - webSocketClientHandler.handshakeFuture().addListener((e1) -> onConnect.run()); + future.addListener((l) -> { + if(l.isSuccess()) { + ch = future.channel(); + webSocketClientHandler.handshakeFuture().addListener((e) -> { + if(e.isSuccess()) { + onConnect.run(); + } else { + onFail.accept(webSocketClientHandler.handshakeFuture().cause()); + } + }); + } else { + onFail.accept(future.cause()); + } }); } diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientWebSocketService.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientWebSocketService.java index 4e035caf..7768daea 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientWebSocketService.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/ClientWebSocketService.java @@ -27,6 +27,7 @@ public abstract class ClientWebSocketService extends ClientJSONPoint { public static final ProviderMap results = new ProviderMap<>(); public static final ProviderMap requests = new ProviderMap<>(); + private static boolean resultsRegistered = false; public final Gson gson; public final Boolean onConnect; public OnCloseCallback onCloseCallback; @@ -83,38 +84,41 @@ public void registerRequests() { @SuppressWarnings("deprecation") public void registerResults() { - results.register("auth", AuthRequestEvent.class); - results.register("checkServer", CheckServerRequestEvent.class); - results.register("joinServer", JoinServerRequestEvent.class); - results.register("launcher", LauncherRequestEvent.class); - results.register("profileByUsername", ProfileByUsernameRequestEvent.class); - results.register("profileByUUID", ProfileByUUIDRequestEvent.class); - results.register("batchProfileByUsername", BatchProfileByUsernameRequestEvent.class); - results.register("profiles", ProfilesRequestEvent.class); - results.register("setProfile", SetProfileRequestEvent.class); - results.register("updateList", UpdateListRequestEvent.class); - results.register("error", ErrorRequestEvent.class); - results.register("update", UpdateRequestEvent.class); - results.register("restoreSession", RestoreSessionRequestEvent.class); - results.register("log", LogEvent.class); - results.register("getAvailabilityAuth", GetAvailabilityAuthRequestEvent.class); - results.register("exception", ExceptionEvent.class); - results.register("register", RegisterRequestEvent.class); - results.register("notification", NotificationEvent.class); - results.register("signal", SignalEvent.class); - results.register("exit", ExitRequestEvent.class); - results.register("getSecureLevelInfo", GetSecureLevelInfoRequestEvent.class); - results.register("verifySecureLevelKey", VerifySecureLevelKeyRequestEvent.class); - results.register("securityReport", SecurityReportRequestEvent.class); - results.register("hardwareReport", HardwareReportRequestEvent.class); - results.register("serverStatus", ServerStatusRequestEvent.class); - results.register("pingServerReport", PingServerReportRequestEvent.class); - results.register("pingServer", PingServerRequestEvent.class); - results.register("currentUser", CurrentUserRequestEvent.class); - results.register("features", FeaturesRequestEvent.class); - results.register("refreshToken", RefreshTokenRequestEvent.class); - results.register("restore", RestoreRequestEvent.class); - results.register("additionalData", AdditionalDataRequestEvent.class); + if(!resultsRegistered) { + results.register("auth", AuthRequestEvent.class); + results.register("checkServer", CheckServerRequestEvent.class); + results.register("joinServer", JoinServerRequestEvent.class); + results.register("launcher", LauncherRequestEvent.class); + results.register("profileByUsername", ProfileByUsernameRequestEvent.class); + results.register("profileByUUID", ProfileByUUIDRequestEvent.class); + results.register("batchProfileByUsername", BatchProfileByUsernameRequestEvent.class); + results.register("profiles", ProfilesRequestEvent.class); + results.register("setProfile", SetProfileRequestEvent.class); + results.register("updateList", UpdateListRequestEvent.class); + results.register("error", ErrorRequestEvent.class); + results.register("update", UpdateRequestEvent.class); + results.register("restoreSession", RestoreSessionRequestEvent.class); + results.register("log", LogEvent.class); + results.register("getAvailabilityAuth", GetAvailabilityAuthRequestEvent.class); + results.register("exception", ExceptionEvent.class); + results.register("register", RegisterRequestEvent.class); + results.register("notification", NotificationEvent.class); + results.register("signal", SignalEvent.class); + results.register("exit", ExitRequestEvent.class); + results.register("getSecureLevelInfo", GetSecureLevelInfoRequestEvent.class); + results.register("verifySecureLevelKey", VerifySecureLevelKeyRequestEvent.class); + results.register("securityReport", SecurityReportRequestEvent.class); + results.register("hardwareReport", HardwareReportRequestEvent.class); + results.register("serverStatus", ServerStatusRequestEvent.class); + results.register("pingServerReport", PingServerReportRequestEvent.class); + results.register("pingServer", PingServerRequestEvent.class); + results.register("currentUser", CurrentUserRequestEvent.class); + results.register("features", FeaturesRequestEvent.class); + results.register("refreshToken", RefreshTokenRequestEvent.class); + results.register("restore", RestoreRequestEvent.class); + results.register("additionalData", AdditionalDataRequestEvent.class); + resultsRegistered = true; + } } public void waitIfNotConnected() { diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/OfflineRequestService.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/OfflineRequestService.java new file mode 100644 index 00000000..fd353447 --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/OfflineRequestService.java @@ -0,0 +1,73 @@ +package pro.gravit.launcher.request.websockets; + +import pro.gravit.launcher.Launcher; +import pro.gravit.launcher.request.Request; +import pro.gravit.launcher.request.RequestException; +import pro.gravit.launcher.request.RequestService; +import pro.gravit.launcher.request.WebSocketEvent; +import pro.gravit.utils.helper.LogHelper; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +public class OfflineRequestService implements RequestService { + private final HashSet eventHandlers = new HashSet<>(); + private final Map, RequestProcessor> processors = new ConcurrentHashMap<>(); + @Override + @SuppressWarnings("unchecked") + public CompletableFuture request(Request request) throws IOException { + RequestProcessor> processor = (RequestProcessor>) processors.get(request.getClass()); + CompletableFuture future = new CompletableFuture<>(); + if(processor == null) { + future.completeExceptionally(new RequestException(String.format("Offline mode not support '%s'", request.getType()))); + return future; + } + if(LogHelper.isDevEnabled()) { + LogHelper.dev("Request %s: %s", request.getType(), Launcher.gsonManager.gson.toJson(request)); + } + try { + T event = processor.process(request); + if(LogHelper.isDevEnabled()) { + LogHelper.dev("Response %s: %s", event.getType(), Launcher.gsonManager.gson.toJson(event)); + } + future.complete(event); + } catch (Throwable e) { + if(e instanceof RequestException) { + future.completeExceptionally(e); + } else { + future.completeExceptionally(new RequestException(e)); + } + } + return future; + } + + @Override + public void registerEventHandler(EventHandler handler) { + eventHandlers.add(handler); + } + + @Override + public void unregisterEventHandler(EventHandler handler) { + eventHandlers.remove(handler); + } + + @Override + public boolean isClosed() { + return false; + } + + public void registerRequestProcessor(Class requestClazz, RequestProcessor function) { + processors.put(requestClazz, function); + } + + public void unregisterRequestProcessor(Class> requestClazz) { + processors.remove(requestClazz); + } + + public interface RequestProcessor { + T process(V request) throws RequestException; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/StdWebSocketService.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/StdWebSocketService.java index 578d1fb9..2c0664ee 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/StdWebSocketService.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/StdWebSocketService.java @@ -28,7 +28,7 @@ public StdWebSocketService(String address) throws SSLException { super(address); } - public static StdWebSocketService initWebSockets(String address, boolean async) { + public static CompletableFuture initWebSockets(String address) throws Exception { StdWebSocketService service; try { service = new StdWebSocketService(address); @@ -37,28 +37,26 @@ public static StdWebSocketService initWebSockets(String address, boolean async) } service.registerResults(); service.registerRequests(); - if (!async) { - try { - service.open(); - } catch (Exception e) { - LogHelper.error(e); - } - } else { - service.openAsync(() -> { - }); - } - JVMHelper.RUNTIME.addShutdownHook(new Thread(() -> { - try { - //if(service.isOpen()) - // service.closeBlocking(); - service.close(); - } catch (InterruptedException e) { - LogHelper.error(e); - } - })); - return service; + CompletableFuture future = new CompletableFuture<>(); + service.openAsync(() -> { + future.complete(service); + JVMHelper.RUNTIME.addShutdownHook(new Thread(() -> { + try { + //if(service.isOpen()) + // service.closeBlocking(); + service.close(); + } catch (InterruptedException e) { + LogHelper.error(e); + } + })); + }, (error) -> { + future.completeExceptionally(error); + }); + return future; } + + @Deprecated public void registerEventHandler(ClientWebSocketService.EventHandler handler) { legacyEventHandlers.add(handler); diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/VoidRequestService.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/VoidRequestService.java new file mode 100644 index 00000000..e08214c2 --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/VoidRequestService.java @@ -0,0 +1,43 @@ +package pro.gravit.launcher.request.websockets; + +import pro.gravit.launcher.request.Request; +import pro.gravit.launcher.request.RequestException; +import pro.gravit.launcher.request.RequestService; +import pro.gravit.launcher.request.WebSocketEvent; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +public class VoidRequestService implements RequestService { + private final Throwable ex; + + public VoidRequestService(Throwable ex) { + this.ex = ex; + } + + public VoidRequestService() { + this.ex = null; + } + + @Override + public CompletableFuture request(Request request) throws IOException { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(ex != null ? ex : new RequestException("Connection fail")); + return future; + } + + @Override + public void registerEventHandler(EventHandler handler) { + + } + + @Override + public void unregisterEventHandler(EventHandler handler) { + + } + + @Override + public boolean isClosed() { + return true; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/WebSocketClientHandler.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/WebSocketClientHandler.java index df633e6f..a3982161 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/WebSocketClientHandler.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/websockets/WebSocketClientHandler.java @@ -77,17 +77,15 @@ else if (frame instanceof BinaryWebSocketFrame) { // uncomment to print request // logger.info(frame.content().toString()); } - } @Override public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) { - LogHelper.error(cause); - if (!handshakeFuture.isDone()) { handshakeFuture.setFailure(cause); + } else { + LogHelper.error(cause); } - ctx.close(); } } \ No newline at end of file diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java index 194880ff..a0505386 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -113,7 +113,7 @@ public void run(String... args) throws Throwable { updateLauncherConfig(); if (config.env != null) Launcher.applyLauncherEnv(config.env); else Launcher.applyLauncherEnv(LauncherConfig.LauncherEnvironment.STD); - StdWebSocketService service = StdWebSocketService.initWebSockets(config.address, false); + StdWebSocketService service = StdWebSocketService.initWebSockets(config.address).get(); service.reconnectCallback = () -> { LogHelper.debug("WebSocket connect closed. Try reconnect"); diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/setup/ServerWrapperSetup.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/setup/ServerWrapperSetup.java index bdf3b117..b173e439 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/setup/ServerWrapperSetup.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/setup/ServerWrapperSetup.java @@ -61,7 +61,13 @@ public void run() throws Exception { if(!Request.isAvailable() || Request.getRequestService().isClosed()) { System.out.println("Print launchserver websocket host( ws://host:port/api ):"); wrapper.config.address = commands.commandHandler.readLine(); - StdWebSocketService service = StdWebSocketService.initWebSockets(wrapper.config.address, false); + StdWebSocketService service; + try { + service = StdWebSocketService.initWebSockets(wrapper.config.address).get(); + } catch (Throwable e) { + LogHelper.error(e); + continue; + } Request.setRequestService(service); } System.out.println("Print server token:"); diff --git a/build.gradle b/build.gradle index 8f1e2912..aba97988 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ id 'org.openjfx.javafxplugin' version '0.0.10' apply false } group = 'pro.gravit.launcher' -version = '5.2.5' +version = '5.2.6-SNAPSHOT' apply from: 'props.gradle'