From 217a1fc582919290ceb5ddcd6c9b27601378ea3f Mon Sep 17 00:00:00 2001 From: Mats Date: Tue, 20 Feb 2024 18:42:14 +0100 Subject: [PATCH 1/5] WIP, reconnecting --- client/public/en.lang | 2 + client/public/metadata.json | 2 +- .../client/services/socket/SocketModule.jsx | 8 ++ .../ServerConnectionWarning.jsx | 28 +++++ client/src/components/tabwindow/TabWindow.jsx | 2 + client/src/metadata.json | 2 +- client/src/state/store.jsx | 1 + .../networking/drivers/SystemDriver.java | 98 +++++++++++++-- .../networking/io/SocketConnection.java | 116 ++++++++++++------ .../state/states/ReconnectingState.java | 33 +++++ .../voicechat/enums/VoiceApiStatus.java | 1 + .../subcommands/SpamTestSubCommand.java | 41 +++++++ 12 files changed, 282 insertions(+), 52 deletions(-) create mode 100644 client/src/components/connectionwarning/ServerConnectionWarning.jsx create mode 100644 plugin/src/main/java/com/craftmend/openaudiomc/generic/state/states/ReconnectingState.java create mode 100644 plugin/src/main/java/com/craftmend/openaudiomc/spigot/modules/commands/subcommands/SpamTestSubCommand.java diff --git a/client/public/en.lang b/client/public/en.lang index 27405c2fa..93456e320 100644 --- a/client/public/en.lang +++ b/client/public/en.lang @@ -143,4 +143,6 @@ settings.interpolation.title=Location smoothing settings.interpolation.body=Automatically smooth movements to minimize speaker/voicechat stuttering while walking. This adds extra movement delay, but sounds better. settings.interpolation.button=Enable smoothing +network.serverUnhealthy={serverName}'s connection to OpenAudioMc is unhealthy, client functionality may be limited until the connection is restored. + tooltip.close=Close diff --git a/client/public/metadata.json b/client/public/metadata.json index a1bc7c1ef..e6c6db507 100644 --- a/client/public/metadata.json +++ b/client/public/metadata.json @@ -1 +1 @@ -{"buildMajor":1,"buildMinor":125,"buildRevision":214,"buildTag":"dev","buildDate":"Mon Feb 19 2024","build":"1.125.214 dev"} \ No newline at end of file +{"buildMajor":1,"buildMinor":125,"buildRevision":218,"buildTag":"dev","buildDate":"Tue Feb 20 2024","build":"1.125.218 dev"} \ No newline at end of file diff --git a/client/src/client/services/socket/SocketModule.jsx b/client/src/client/services/socket/SocketModule.jsx index 1e8c28ad1..c2fadb676 100644 --- a/client/src/client/services/socket/SocketModule.jsx +++ b/client/src/client/services/socket/SocketModule.jsx @@ -53,6 +53,14 @@ export const SocketManager = new class ISocketManager { TimeService.sync(timeStamp, hoursOffset); }); + this.socket.on('server-reconnecting', () => { + setGlobalState({ isServerHealthy: false }); + }); + + this.socket.on('server-reconnected', () => { + setGlobalState({ isServerHealthy: true }); + }); + this.socket.on('disconnect', () => { MediaManager.destroySounds(null, true); diff --git a/client/src/components/connectionwarning/ServerConnectionWarning.jsx b/client/src/components/connectionwarning/ServerConnectionWarning.jsx new file mode 100644 index 000000000..3b506d7f3 --- /dev/null +++ b/client/src/components/connectionwarning/ServerConnectionWarning.jsx @@ -0,0 +1,28 @@ +import { connect } from 'react-redux'; +import React from 'react'; +import { msg } from '../../client/OpenAudioAppContainer'; + +function ServerConnectionWarning(props) { + if (props.isServerHealthy) { + return null; + } + + const message = msg('network.serverUnhealthy').replace('{serverName}', props.settings.serverDisplayName || 'the server'); + + return ( +
+
+ {message} +
+
+ ); +} + +export default connect(mapStateToProps)(ServerConnectionWarning); + +function mapStateToProps(state) { + return { + isServerHealthy: state.isServerHealthy, + settings: state.settings, + }; +} diff --git a/client/src/components/tabwindow/TabWindow.jsx b/client/src/components/tabwindow/TabWindow.jsx index a7b6f6245..d5bccca57 100644 --- a/client/src/components/tabwindow/TabWindow.jsx +++ b/client/src/components/tabwindow/TabWindow.jsx @@ -5,6 +5,7 @@ import { connect } from 'react-redux'; import { showTextModal } from '../modal/InputModal'; import { setGlobalState } from '../../state/store'; import { msg } from '../../client/OpenAudioAppContainer'; +import ServerConnectionWarning from '../connectionwarning/ServerConnectionWarning'; export const setTab = (tab) => { setGlobalState({ @@ -57,6 +58,7 @@ class TabWindow extends Component {
+ {pages[this.props.currentTab].content}
diff --git a/client/src/metadata.json b/client/src/metadata.json index a1bc7c1ef..e6c6db507 100644 --- a/client/src/metadata.json +++ b/client/src/metadata.json @@ -1 +1 @@ -{"buildMajor":1,"buildMinor":125,"buildRevision":214,"buildTag":"dev","buildDate":"Mon Feb 19 2024","build":"1.125.214 dev"} \ No newline at end of file +{"buildMajor":1,"buildMinor":125,"buildRevision":218,"buildTag":"dev","buildDate":"Tue Feb 20 2024","build":"1.125.218 dev"} \ No newline at end of file diff --git a/client/src/state/store.jsx b/client/src/state/store.jsx index f6278dad3..9e7d94f61 100644 --- a/client/src/state/store.jsx +++ b/client/src/state/store.jsx @@ -28,6 +28,7 @@ const initialState = { isPremium: false, isClaimed: false, + isServerHealthy: true, clientSupportsVoiceChat: false, // not valid https browserSupportsVoiceChat: false, // no webrtc at all browserSupportIsLimited: false, // operagx, broken settings? diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java index b96020f71..c2705cdc4 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java @@ -7,6 +7,7 @@ import com.craftmend.openaudiomc.generic.networking.interfaces.NetworkingService; import com.craftmend.openaudiomc.generic.platform.interfaces.TaskService; import com.craftmend.openaudiomc.generic.state.StateService; +import com.craftmend.openaudiomc.generic.state.states.ReconnectingState; import com.craftmend.openaudiomc.generic.storage.enums.StorageKey; import com.craftmend.openaudiomc.generic.client.objects.ClientConnection; import com.craftmend.openaudiomc.generic.networking.interfaces.SocketDriver; @@ -18,17 +19,26 @@ import java.time.Duration; import java.time.Instant; +import java.util.concurrent.atomic.AtomicBoolean; public class SystemDriver implements SocketDriver { - private TaskService taskService = OpenAudioMc.resolveDependency(TaskService.class); - private StateService stateService = OpenAudioMc.getService(StateService.class); + private final TaskService taskService = OpenAudioMc.resolveDependency(TaskService.class); + private final StateService stateService = OpenAudioMc.getService(StateService.class); + private final SocketConnection parent; + private Instant lastHeartbeat = Instant.now(); private Socket lastSocket; + private boolean announcedShutdown = false; + + public SystemDriver(SocketConnection socketConnection) { + this.parent = socketConnection; + } @Override public void boot(Socket socket, SocketConnection connector) { this.lastSocket = socket; + final AtomicBoolean died = new AtomicBoolean(false); taskService.scheduleAsyncRepeatingTask(() -> { if (stateService.getCurrentState().isConnected()) { @@ -44,18 +54,40 @@ public void boot(Socket socket, SocketConnection connector) { OpenAudioMc.getService(TimeService.class).pushServerUpdate(timeStamp, offset); }); + socket.on("announce-shutdown", args -> { + OpenAudioLogger.info("The server announced its intention to close our connection.."); + announcedShutdown = true; + }); + socket.on(Socket.EVENT_CONNECT, args -> { // connected with success - OpenAudioMc.getService(StateService.class).setState(new ConnectedState(connector.getLastUsedRelay())); + announcedShutdown = false; + OpenAudioMc.getService(StateService.class).setState(new ConnectedState(connector.getPreviousLogin().getRelay())); pingHeartbeat(); OpenAudioMc.getService(OpenaudioAccountService.class).startVoiceHandshake(); }); - socket.on(Socket.EVENT_DISCONNECT, args -> handleDisconnect()); + socket.on(Socket.EVENT_DISCONNECT, args -> { + if (!died.get()) { + handleDisconnect(); + died.set(true); + } + }); socket.on(Socket.EVENT_CONNECT_TIMEOUT, args -> { // failed to connect - OpenAudioMc.getService(StateService.class).setState(new IdleState("Connecting timed out, something wrong with the api, network or token?")); + if (!died.get()) { + handleDisconnect(); + died.set(true); + } + }); + + socket.on(Socket.EVENT_CONNECT_ERROR, args -> { + // failed to connect + if (!died.get()) { + handleDisconnect(); + died.set(true); + } }); } @@ -87,8 +119,52 @@ private void pingHeartbeat() { } private void handleDisconnect() { + boolean mayReconnect = true; + if (stateService.getCurrentState() instanceof ReconnectingState) { + ReconnectingState state = (ReconnectingState) stateService.getCurrentState(); + if (state.getAttempts() >= ReconnectingState.MAX_ATTEMPTS) { + mayReconnect = false; + } + } + + try { + lastSocket.disconnect(); + } catch (Exception e) { + // ignored + } + + + if (announcedShutdown) { + OpenAudioLogger.info("The server closed the primary connection, but we were already aware of this. Ignoring."); + shutdown("Graceful."); + } else { + if (mayReconnect) { + // lets try again lol + // are we currently reconnecting? + if (stateService.getCurrentState() instanceof ReconnectingState) { + ReconnectingState state = (ReconnectingState) stateService.getCurrentState(); + state.incrementAttempts(); + } else { + ReconnectingState reconnect = new ReconnectingState(); + reconnect.incrementAttempts(); + OpenAudioMc.getService(StateService.class).setState(reconnect); + } + OpenAudioLogger.warn("The server closed the primary connection unexpectedly, attempting reconnect in 2 seconds."); + OpenAudioMc.resolveDependency(TaskService.class).schduleSyncDelayedTask(() -> { + parent.setupConnection(); + }, 20 * 2); + } else { + OpenAudioLogger.warn("The server closed the primary connection unexpectedly, and the system has given up trying to reconnect."); + shutdown("Reached reconnect limit."); + // set to idle + OpenAudioMc.getService(StateService.class).setState(new IdleState("Disconnected from the socket. Reached reconnect limit.")); + } + } + } + + private void shutdown(String reason) { // disconnected, probably with a reason or something - OpenAudioMc.getService(StateService.class).setState(new IdleState("Disconnected from the socket")); + OpenAudioMc.getService(StateService.class).setState(new IdleState("Disconnected from the socket. " + reason)); String message = Platform.translateColors(OpenAudioMc.getInstance().getConfiguration().getString(StorageKey.MESSAGE_LINK_EXPIRED)); for (ClientConnection client : OpenAudioMc.getService(NetworkingService.class).getClients()) { @@ -100,13 +176,9 @@ private void handleDisconnect() { client.onDisconnect(); } } - - try { - lastSocket.disconnect(); - } catch (Exception e) { - // ignored - } - OpenAudioMc.getService(OpenaudioAccountService.class).getVoiceApiConnection().stop(); + + // reset + announcedShutdown = false; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/io/SocketConnection.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/io/SocketConnection.java index 0f0e5c0a0..5097389fd 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/io/SocketConnection.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/io/SocketConnection.java @@ -18,25 +18,32 @@ import com.craftmend.openaudiomc.generic.rest.routes.Endpoint; import com.craftmend.openaudiomc.generic.rest.types.RelayLoginResponse; import com.craftmend.openaudiomc.generic.state.StateService; +import com.craftmend.openaudiomc.generic.state.states.*; import com.craftmend.openaudiomc.generic.storage.enums.StorageKey; import com.craftmend.openaudiomc.generic.networking.interfaces.Authenticatable; import com.craftmend.openaudiomc.generic.networking.interfaces.SocketDriver; -import com.craftmend.openaudiomc.generic.state.states.AssigningRelayState; -import com.craftmend.openaudiomc.generic.state.states.ConnectedState; -import com.craftmend.openaudiomc.generic.state.states.ConnectingState; -import com.craftmend.openaudiomc.generic.state.states.IdleState; import com.craftmend.openaudiomc.generic.uploads.UploadIndexService; import io.socket.client.IO; +import io.socket.client.Manager; import io.socket.client.Socket; import lombok.Getter; +import okhttp3.Call; import okhttp3.OkHttpClient; +import okhttp3.WebSocket; import java.io.IOException; +import java.lang.reflect.Field; import java.net.ProxySelector; import java.net.URISyntaxException; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; public class SocketConnection { @@ -46,12 +53,12 @@ public class SocketConnection { private RestRequest relayLogoutRequest; private boolean registeredLogout = false; @Getter - private String lastUsedRelay = "none"; + private RelayLoginResponse previousLogin; private ServerKeySet keySet; private final SocketDriver[] drivers = new SocketDriver[]{ new NotificationDriver(), - new SystemDriver(), + new SystemDriver(this), new ClientDriver(), }; @@ -60,10 +67,19 @@ public SocketConnection(ServerKeySet keySet) { } public void setupConnection() { - if (!OpenAudioMc.getService(StateService.class).getCurrentState().canConnect()) return; + boolean isReconnect = OpenAudioMc.getService(StateService.class).getCurrentState() instanceof ReconnectingState; + int attempt = isReconnect ? ((ReconnectingState) OpenAudioMc.getService(StateService.class).getCurrentState()).getAttempts() : 0; + UUID stateId = isReconnect ? ((ReconnectingState) OpenAudioMc.getService(StateService.class).getCurrentState()).getStateId() : null; + + if (!isReconnect && !OpenAudioMc.getService(StateService.class).getCurrentState().canConnect()) return; // update state - OpenAudioMc.getService(StateService.class).setState(new AssigningRelayState()); + if (!isReconnect) { + // only override the state if we are not reconnecting + OpenAudioMc.getService(StateService.class).setState(new AssigningRelayState()); + } else { + OpenAudioLogger.info("Attempting to restore connection to OpenAudioMc, attempt " + attempt); + } if (!registeredLogout) { relayLoginRequest = new RestRequest(RelayLoginResponse.class, Endpoint.RELAY_LOGIN); @@ -83,68 +99,91 @@ public void setupConnection() { OkHttpClient okHttpClient = CertificateHelper.ignore(new OkHttpClient.Builder().proxySelector(new NullProxySelector())).build(); IO.Options opts = new IO.Options(); - opts.callFactory = okHttpClient; + opts.callFactory = (Call.Factory) okHttpClient; opts.reconnection = false; - opts.webSocketFactory = okHttpClient; + opts.webSocketFactory = (WebSocket.Factory) okHttpClient; + opts.forceNew = true; + opts.rememberUpgrade = false; // authentication headers opts.query = String.format( - "type=server&private=%s&public=%s", + "type=server&private=%s&public=%s&reconnect=%s", keySet.getPrivateKey().getValue(), - keySet.getPublicKey().getValue() + keySet.getPublicKey().getValue(), + isReconnect ? "true" : "false" ); - // schedule timeout check - OpenAudioMc.resolveDependency(TaskService.class).schduleSyncDelayedTask(() -> { - if (OpenAudioMc.getService(StateService.class).getCurrentState() instanceof AssigningRelayState) { - OpenAudioLogger.info("Connecting timed out."); - OpenAudioMc.getService(StateService.class).setState(new IdleState("Connecting to OpenAudioMc timed out")); - } - }, 20 * 35); + // only do login handling if we're not reconnecting, because then we'd be re-using the same config + if (!isReconnect) { + OpenAudioMc.resolveDependency(TaskService.class).schduleSyncDelayedTask(() -> { + if (OpenAudioMc.getService(StateService.class).getCurrentState() instanceof AssigningRelayState) { + OpenAudioLogger.info("Connecting timed out."); + OpenAudioMc.getService(StateService.class).setState(new IdleState("Connecting to OpenAudioMc timed out")); + } + }, 20 * 35); - relayLoginRequest.run(); + relayLoginRequest.run(); - if (relayLoginRequest.hasError()) { - OpenAudioMc.getService(StateService.class).setState(new IdleState("Failed to do the initial handshake. Error: " + relayLoginRequest.getError())); - OpenAudioLogger.info("Failed to get instance: " + relayLoginRequest.getError().getMessage()); - try { - throw new IOException("Failed to get instance! see console for error information"); - } catch (IOException e) { - e.printStackTrace(); + if (relayLoginRequest.hasError()) { + OpenAudioMc.getService(StateService.class).setState(new IdleState("Failed to do the initial handshake. Error: " + relayLoginRequest.getError())); + OpenAudioLogger.info("Failed to get instance: " + relayLoginRequest.getError().getMessage()); + try { + throw new IOException("Failed to get instance! see console for error information"); + } catch (IOException e) { + e.printStackTrace(); + } + return; } - return; - } - RelayLoginResponse loginResponse = relayLoginRequest.getResponse(); + RelayLoginResponse loginResponse = relayLoginRequest.getResponse(); + previousLogin = loginResponse; + OpenAudioMc.getService(UploadIndexService.class).setContent(loginResponse.getFiles()); + } - lastUsedRelay = loginResponse.getRelay(); - OpenAudioMc.getService(UploadIndexService.class).setContent(loginResponse.getFiles()); + if (previousLogin == null) { + OpenAudioMc.getService(StateService.class).setState(new IdleState("Failed to get a relay instance")); + OpenAudioLogger.warn("Entered a track which should only be entered during recovery, but there was no previous login. reconnect: " + isReconnect + " attempt: " + attempt); + return; + } try { - String endpoint = loginResponse.getRelayEndpoint(); + String endpoint = previousLogin.getRelayEndpoint(); endpoint = endpoint.replace("https", "http"); socket = IO.socket(endpoint, opts); } catch (URISyntaxException e) { OpenAudioLogger.error(e, "Received an invalid endpoint"); } - // register state to be connecting - OpenAudioMc.getService(StateService.class).setState(new ConnectingState()); + // this should again, only be done if we're not reconnecting + if (!isReconnect) { + // register state to be connecting + OpenAudioMc.getService(StateService.class).setState(new ConnectingState()); - // clear session cache - OpenAudioMc.getService(AuthenticationService.class).getDriver().initCache(); + // clear session cache + OpenAudioMc.getService(AuthenticationService.class).getDriver().initCache(); + } // schedule timeout check OpenAudioMc.resolveDependency(TaskService.class).schduleSyncDelayedTask(() -> { + // was it a normal connection? if (OpenAudioMc.getService(StateService.class).getCurrentState() instanceof ConnectingState) { OpenAudioLogger.warn("Connect timed out."); OpenAudioMc.getService(StateService.class).setState(new IdleState("Connecting to the assigned instance timed out (socket)")); } + + // alternatively, check if we're still in the same reconnect cycle + if (OpenAudioMc.getService(StateService.class).getCurrentState() instanceof ReconnectingState) { + ReconnectingState state = (ReconnectingState) OpenAudioMc.getService(StateService.class).getCurrentState(); + if (state.getStateId().equals(stateId) && state.getAttempts() == attempt) { + socket.emit(Socket.EVENT_CONNECT_TIMEOUT); + } + } }, 20 * 35); // register drivers for (SocketDriver driver : drivers) driver.boot(socket, this); + socket.connect(); } @@ -153,6 +192,8 @@ public void disconnect() { relayLogoutRequest.run(); } if (this.socket != null) { + // let them know that we intend to shut down + this.socket.emit("announce-shutdown", "goodbye"); this.socket.disconnect(); } OpenAudioMc.getService(StateService.class).setState(new IdleState()); @@ -166,4 +207,5 @@ public void send(Authenticatable client, AbstractPacket packet) { socket.emit("data", OpenAudioMc.getGson().toJson(packet)); } } + } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/state/states/ReconnectingState.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/state/states/ReconnectingState.java new file mode 100644 index 000000000..04d84de09 --- /dev/null +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/state/states/ReconnectingState.java @@ -0,0 +1,33 @@ +package com.craftmend.openaudiomc.generic.state.states; + +import com.craftmend.openaudiomc.generic.state.interfaces.State; +import lombok.Getter; + +import java.util.UUID; + +public class ReconnectingState implements State { + + public static final int MAX_ATTEMPTS = 7; + @Getter private int attempts = 0; + + @Getter private UUID stateId = UUID.randomUUID(); + + @Override + public String getDescription() { + return "Attempting to reconnect.. Attempt " + attempts + " of " + MAX_ATTEMPTS; + } + + @Override + public boolean isConnected() { + return false; + } + + @Override + public boolean canConnect() { + return false; + } + + public void incrementAttempts() { + attempts++; + } +} diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/enums/VoiceApiStatus.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/enums/VoiceApiStatus.java index 8c763203d..1eb498031 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/enums/VoiceApiStatus.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/enums/VoiceApiStatus.java @@ -4,6 +4,7 @@ public enum VoiceApiStatus { IDLE, CONNECTING, + RECONNECTING, CONNECTED } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/spigot/modules/commands/subcommands/SpamTestSubCommand.java b/plugin/src/main/java/com/craftmend/openaudiomc/spigot/modules/commands/subcommands/SpamTestSubCommand.java new file mode 100644 index 000000000..1f523dfb0 --- /dev/null +++ b/plugin/src/main/java/com/craftmend/openaudiomc/spigot/modules/commands/subcommands/SpamTestSubCommand.java @@ -0,0 +1,41 @@ +package com.craftmend.openaudiomc.spigot.modules.commands.subcommands; + +import com.craftmend.openaudiomc.OpenAudioMc; +import com.craftmend.openaudiomc.generic.commands.interfaces.SubCommand; +import com.craftmend.openaudiomc.generic.commands.objects.Argument; +import com.craftmend.openaudiomc.generic.database.DatabaseService; +import com.craftmend.openaudiomc.generic.user.User; +import com.craftmend.openaudiomc.spigot.modules.shortner.AliasService; +import com.craftmend.openaudiomc.spigot.modules.shortner.data.Alias; +import org.bukkit.ChatColor; + +public class SpamTestSubCommand extends SubCommand { + + public SpamTestSubCommand() { + super("alias"); + registerArguments( + new Argument(" ", + "Register a Alias for a source URL so you can easaly memorize them and can paste them onto signs without having to type a complete dictionary." + + " When an alias like onride_music is set, you can trigger it by using a:onride_music as your source.") + ); + } + + @Override + public void onExecute(User sender, String[] args) { + if (args.length == 2) { + String aliasName = args[0].toLowerCase(); + String aliasSource = args[1]; + Alias alias = new Alias(aliasName, aliasSource); + OpenAudioMc.getService(AliasService.class).getAliasMap().put(aliasName, alias); + + OpenAudioMc.getService(DatabaseService.class).getRepository(Alias.class) + .save(alias); + + message(sender, ChatColor.GREEN + "Success! the alias " + ChatColor.YELLOW + "a:" + aliasName.toLowerCase() + ChatColor.GRAY + " will be read as " + ChatColor.YELLOW + aliasSource); + return; + } + + sender.makeExecuteCommand("oa help " + getCommand()); + } + +} From 9adbd2d08f7255f45d2a8f9840d467d46ca81d23 Mon Sep 17 00:00:00 2001 From: Mats Date: Tue, 20 Feb 2024 18:50:45 +0100 Subject: [PATCH 2/5] Mark some packets as queuable between reconnects --- .../generic/networking/abstracts/AbstractPacket.java | 10 ++++++++-- .../generic/networking/drivers/SystemDriver.java | 4 +--- .../packets/PacketAcknowledgeClientRequest.java | 1 + .../networking/packets/PacketSocketKickClient.java | 1 + .../packets/client/media/PacketClientCreateMedia.java | 2 ++ .../packets/client/media/PacketClientDestroyMedia.java | 4 ++++ .../packets/client/media/PacketClientUpdateMedia.java | 1 + .../client/speakers/PacketClientCreateSpeaker.java | 1 + .../client/speakers/PacketClientRemoveSpeaker.java | 1 + .../client/ui/PacketClientModerationStatus.java | 1 + .../packets/client/voice/PacketClientBlurVoiceUi.java | 1 + .../client/voice/PacketClientDropVoiceStream.java | 1 + .../client/voice/PacketClientSubscribeToVoice.java | 1 + .../client/voice/PacketClientUnlockVoiceChat.java | 1 + .../client/voice/PacketClientVoiceOptionsUpdate.java | 1 + 15 files changed, 26 insertions(+), 5 deletions(-) diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/abstracts/AbstractPacket.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/abstracts/AbstractPacket.java index 0e63dc1de..02008d5f4 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/abstracts/AbstractPacket.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/abstracts/AbstractPacket.java @@ -1,6 +1,5 @@ package com.craftmend.openaudiomc.generic.networking.abstracts; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -9,7 +8,6 @@ @Getter @NoArgsConstructor -@AllArgsConstructor public class AbstractPacket { /** @@ -21,4 +19,12 @@ public class AbstractPacket { private PacketChannel packetChannel; @Setter private UUID client; + public AbstractPacket(AbstractPacketPayload data, PacketChannel packetChannel, UUID client) { + this.data = data; + this.packetChannel = packetChannel; + this.client = client; + } + + protected transient boolean queueableIfReconnecting = false; + } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java index c2705cdc4..4c5a44903 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java @@ -150,9 +150,7 @@ private void handleDisconnect() { OpenAudioMc.getService(StateService.class).setState(reconnect); } OpenAudioLogger.warn("The server closed the primary connection unexpectedly, attempting reconnect in 2 seconds."); - OpenAudioMc.resolveDependency(TaskService.class).schduleSyncDelayedTask(() -> { - parent.setupConnection(); - }, 20 * 2); + OpenAudioMc.resolveDependency(TaskService.class).schduleSyncDelayedTask(parent::setupConnection, 20 * 2); } else { OpenAudioLogger.warn("The server closed the primary connection unexpectedly, and the system has given up trying to reconnect."); shutdown("Reached reconnect limit."); diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/PacketAcknowledgeClientRequest.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/PacketAcknowledgeClientRequest.java index ff19ad2a8..42bd36870 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/PacketAcknowledgeClientRequest.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/PacketAcknowledgeClientRequest.java @@ -8,6 +8,7 @@ public class PacketAcknowledgeClientRequest extends AbstractPacket { public PacketAcknowledgeClientRequest(AcknowledgeClientPayload data) { super(data, PacketChannel.SOCKET_OUT_ACKNOWLEDGEMENT, null); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/PacketSocketKickClient.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/PacketSocketKickClient.java index cfffe9126..4bac6c194 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/PacketSocketKickClient.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/PacketSocketKickClient.java @@ -7,6 +7,7 @@ public class PacketSocketKickClient extends AbstractPacket { public PacketSocketKickClient() { super(null, PacketChannel.SOCKET_OUT_KICK_CLIENT, null); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientCreateMedia.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientCreateMedia.java index c469b10d8..e6b3efab0 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientCreateMedia.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientCreateMedia.java @@ -9,10 +9,12 @@ public class PacketClientCreateMedia extends AbstractPacket { public PacketClientCreateMedia(Media media) { super(new ClientCreateMediaPayload(media), PacketChannel.CLIENT_OUT_CREATE_MEDIA, null); + this.queueableIfReconnecting = true; } public PacketClientCreateMedia(Media media, int distance, int maxDistance) { super(new ClientCreateMediaPayload(media, distance, maxDistance), PacketChannel.CLIENT_OUT_CREATE_MEDIA, null); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientDestroyMedia.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientDestroyMedia.java index d22918567..7e936765e 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientDestroyMedia.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientDestroyMedia.java @@ -10,18 +10,22 @@ public class PacketClientDestroyMedia extends AbstractPacket { public PacketClientDestroyMedia(String soundId, boolean deleteSpecial, int fadeTime) { super(new ClientDestroyMediaPayload(soundId, deleteSpecial, fadeTime), PacketChannel.CLIENT_OUT_DESTROY_MEDIA, null); + this.queueableIfReconnecting = true; } public PacketClientDestroyMedia(String soundId, boolean deleteSpecial) { super(new ClientDestroyMediaPayload(soundId, deleteSpecial, DEFAULT_FADE_TIME), PacketChannel.CLIENT_OUT_DESTROY_MEDIA, null); + this.queueableIfReconnecting = true; } public PacketClientDestroyMedia(String soundId, int fadeTime) { super(new ClientDestroyMediaPayload(soundId, false, fadeTime), PacketChannel.CLIENT_OUT_DESTROY_MEDIA, null); + this.queueableIfReconnecting = true; } public PacketClientDestroyMedia(String soundId) { super(new ClientDestroyMediaPayload(soundId, false, DEFAULT_FADE_TIME), PacketChannel.CLIENT_OUT_DESTROY_MEDIA, null); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientUpdateMedia.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientUpdateMedia.java index 5a7aee3c8..70929f0df 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientUpdateMedia.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/media/PacketClientUpdateMedia.java @@ -9,6 +9,7 @@ public class PacketClientUpdateMedia extends AbstractPacket { public PacketClientUpdateMedia(MediaUpdate update) { super(new ClientUpdateMediaPayload(update), PacketChannel.CLIENT_OUT_UPDATE_MEDIA, null); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/speakers/PacketClientCreateSpeaker.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/speakers/PacketClientCreateSpeaker.java index 52bbb1788..6609f2c8c 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/speakers/PacketClientCreateSpeaker.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/speakers/PacketClientCreateSpeaker.java @@ -12,6 +12,7 @@ public PacketClientCreateSpeaker(ClientSpeakerCreatePayload payload) { PacketChannel.CLIENT_OUT_SPEAKER_CREATE, null ); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/speakers/PacketClientRemoveSpeaker.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/speakers/PacketClientRemoveSpeaker.java index f0ca2925c..4fdcc4389 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/speakers/PacketClientRemoveSpeaker.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/speakers/PacketClientRemoveSpeaker.java @@ -12,6 +12,7 @@ public PacketClientRemoveSpeaker(ClientSpeakerDestroyPayload payload) { PacketChannel.CLIENT_OUT_SPEAKER_DESTROY, null ); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/ui/PacketClientModerationStatus.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/ui/PacketClientModerationStatus.java index 3d7e0a9ba..3f736e72f 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/ui/PacketClientModerationStatus.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/ui/PacketClientModerationStatus.java @@ -12,6 +12,7 @@ public PacketClientModerationStatus(boolean isModerating) { PacketChannel.CLIENT_OUT_MODERATION_STATUS, null ); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientBlurVoiceUi.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientBlurVoiceUi.java index d98d7e30b..ebf5a02bc 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientBlurVoiceUi.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientBlurVoiceUi.java @@ -12,6 +12,7 @@ public PacketClientBlurVoiceUi(ClientVoiceBlurUiPayload payload) { PacketChannel.CLIENT_OUT_VOICE_BLUR, null ); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientDropVoiceStream.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientDropVoiceStream.java index 1d7b0e2d1..fd9c993de 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientDropVoiceStream.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientDropVoiceStream.java @@ -12,6 +12,7 @@ public PacketClientDropVoiceStream(ClientVoiceDropPayload payload) { PacketChannel.CLIENT_OUT_VOICE_DROP_STREAM, null ); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientSubscribeToVoice.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientSubscribeToVoice.java index 35cbfffa3..46b1b8fae 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientSubscribeToVoice.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientSubscribeToVoice.java @@ -12,6 +12,7 @@ public PacketClientSubscribeToVoice(ClientVoiceSubscribePayload payload) { PacketChannel.CLIENT_OUT_VOICE_SUBSCRIBE, null ); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientUnlockVoiceChat.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientUnlockVoiceChat.java index 8e5e1258c..01a8bee2c 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientUnlockVoiceChat.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientUnlockVoiceChat.java @@ -12,6 +12,7 @@ public PacketClientUnlockVoiceChat(ClientVoiceChatUnlockPayload payload) { PacketChannel.CLIENT_OUT_VOICE_UNLOCK, null ); + this.queueableIfReconnecting = true; } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientVoiceOptionsUpdate.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientVoiceOptionsUpdate.java index 663fcc3c7..b53d3f4a7 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientVoiceOptionsUpdate.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/packets/client/voice/PacketClientVoiceOptionsUpdate.java @@ -12,6 +12,7 @@ public PacketClientVoiceOptionsUpdate(ClientVoiceOptionsPayload payload) { PacketChannel.CLIENT_OUT_PEER_OPTIONS, null ); + this.queueableIfReconnecting = true; } } From 2421a787cfa123f4c969dd33f83114d18ede0261 Mon Sep 17 00:00:00 2001 From: Mats Date: Tue, 20 Feb 2024 20:40:03 +0100 Subject: [PATCH 3/5] Queue packets --- .../networking/DefaultNetworkingService.java | 26 +++++- .../networking/drivers/SystemDriver.java | 48 +++++++++++ .../networking/io/SocketConnection.java | 14 ++-- .../generic/networking/queue/PacketQueue.java | 79 +++++++++++++++++++ 4 files changed, 157 insertions(+), 10 deletions(-) create mode 100644 plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/queue/PacketQueue.java diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/DefaultNetworkingService.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/DefaultNetworkingService.java index 54a5e479b..8f78e0af6 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/DefaultNetworkingService.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/DefaultNetworkingService.java @@ -6,6 +6,7 @@ import com.craftmend.openaudiomc.generic.authentication.AuthenticationService; import com.craftmend.openaudiomc.generic.client.helpers.SerializableClient; import com.craftmend.openaudiomc.generic.client.objects.ClientConnection; +import com.craftmend.openaudiomc.generic.networking.queue.PacketQueue; import com.craftmend.openaudiomc.generic.oac.OpenaudioAccountService; import com.craftmend.openaudiomc.generic.environment.MagicValue; import com.craftmend.openaudiomc.generic.logging.OpenAudioLogger; @@ -22,6 +23,7 @@ import com.craftmend.openaudiomc.generic.platform.interfaces.TaskService; import com.craftmend.openaudiomc.generic.proxy.interfaces.UserHooks; import com.craftmend.openaudiomc.generic.state.StateService; +import com.craftmend.openaudiomc.generic.state.states.ReconnectingState; import com.craftmend.openaudiomc.generic.user.User; import com.craftmend.openaudiomc.spigot.OpenAudioMcSpigot; import com.craftmend.openaudiomc.spigot.modules.proxy.enums.OAClientMode; @@ -37,6 +39,7 @@ public class DefaultNetworkingService extends NetworkingService { private final Set eventHandlers = new HashSet<>(); private final Map clientMap = new ConcurrentHashMap<>(); private final Map> packetHandlerMap = new HashMap<>(); + private final PacketQueue packetQueue = new PacketQueue(); private SocketConnection socketConnection; private int packetThroughput = 0; @@ -99,7 +102,7 @@ private void init() { 20, 20); try { - socketConnection = new SocketConnection(OpenAudioMc.getService(AuthenticationService.class).getServerKeySet()); + socketConnection = new SocketConnection(getService(AuthenticationService.class).getServerKeySet(), this); } catch (Exception e) { OpenAudioLogger.error(e, "The plugin was unable to start because of a connection problem when requesting the initial private key. Please contact support in https://discord.openaudiomc.net/"); } @@ -129,6 +132,20 @@ public void connectIfDown() { @Override public void send(Authenticatable client, AbstractPacket packet) { for (INetworkingEvents event : getEvents()) event.onPacketSend(client, packet); + + // are we in a compromised state? + if (getService(StateService.class).getCurrentState() instanceof ReconnectingState) { + // is this packet important? + if (packet.isQueueableIfReconnecting()) { + // we need to queue it + packetQueue.addPacket(client.getOwner().getUniqueId(), packet); + return; + } else { + // drop this packet + return; + } + } + socketConnection.send(client, packet); } @@ -259,4 +276,11 @@ public void addEventHandler(INetworkingEvents events) { eventHandlers.add(events); } + public void discardQueue() { + this.packetQueue.clearAll(); + } + + public void flushQueue() { + this.packetQueue.flush(this); + } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java index 4c5a44903..e4ecead13 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java @@ -15,10 +15,16 @@ import com.craftmend.openaudiomc.generic.platform.Platform; import com.craftmend.openaudiomc.generic.state.states.ConnectedState; import com.craftmend.openaudiomc.generic.state.states.IdleState; +import com.google.gson.JsonArray; import io.socket.client.Socket; +import org.json.JSONArray; +import org.json.JSONException; import java.time.Duration; import java.time.Instant; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; public class SystemDriver implements SocketDriver { @@ -59,6 +65,48 @@ public void boot(Socket socket, SocketConnection connector) { announcedShutdown = true; }); + socket.on("reconnect-clients", args -> { + JSONArray rawData = (JSONArray) args[0]; + String[] data = new String[rawData.length()]; + for (int i = 0; i < rawData.length(); i++) { + try { + data[i] = rawData.getString(i); + } catch (JSONException e) { + OpenAudioLogger.error(e, "Failed to parse reconnect data"); + } + } + Set stillConnected = new HashSet<>(); + // get the UUID's of all connected clients, we need this to update our local list of connected clients + for (String s : data) { + if (s != null && !s.isEmpty()) { + stillConnected.add(UUID.fromString(s)); + } + } + + for (ClientConnection client : this.parent.getParent().getClients()) { + if (stillConnected.contains(client.getOwner().getUniqueId())) { + // are they still connected? + if (!client.isConnected()) { + // no, so reconnect + client.onConnect(); + } + } else { + // they are not in the list, so they should be disconnected + if (client.isConnected()) { + client.onDisconnect(); + } + } + } + }); + + socket.on("flush-queue", args -> { + this.parent.getParent().flushQueue(); + }); + + socket.on("discard-queue", args -> { + this.parent.getParent().discardQueue(); + }); + socket.on(Socket.EVENT_CONNECT, args -> { // connected with success announcedShutdown = false; diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/io/SocketConnection.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/io/SocketConnection.java index 5097389fd..5b9f35bf1 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/io/SocketConnection.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/io/SocketConnection.java @@ -5,6 +5,7 @@ import com.craftmend.openaudiomc.generic.authentication.AuthenticationService; import com.craftmend.openaudiomc.generic.authentication.objects.ServerKeySet; import com.craftmend.openaudiomc.generic.events.events.StateChangeEvent; +import com.craftmend.openaudiomc.generic.networking.DefaultNetworkingService; import com.craftmend.openaudiomc.generic.oac.OpenaudioAccountService; import com.craftmend.openaudiomc.generic.logging.OpenAudioLogger; import com.craftmend.openaudiomc.generic.networking.certificate.CertificateHelper; @@ -19,13 +20,11 @@ import com.craftmend.openaudiomc.generic.rest.types.RelayLoginResponse; import com.craftmend.openaudiomc.generic.state.StateService; import com.craftmend.openaudiomc.generic.state.states.*; -import com.craftmend.openaudiomc.generic.storage.enums.StorageKey; import com.craftmend.openaudiomc.generic.networking.interfaces.Authenticatable; import com.craftmend.openaudiomc.generic.networking.interfaces.SocketDriver; import com.craftmend.openaudiomc.generic.uploads.UploadIndexService; import io.socket.client.IO; -import io.socket.client.Manager; import io.socket.client.Socket; import lombok.Getter; @@ -35,18 +34,14 @@ import okhttp3.WebSocket; import java.io.IOException; -import java.lang.reflect.Field; import java.net.ProxySelector; import java.net.URISyntaxException; import java.util.UUID; -import java.util.function.Consumer; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; public class SocketConnection { + @Getter private final DefaultNetworkingService parent; + private Socket socket; @Getter private RestRequest relayLoginRequest; @@ -62,8 +57,9 @@ public class SocketConnection { new ClientDriver(), }; - public SocketConnection(ServerKeySet keySet) { + public SocketConnection(ServerKeySet keySet, DefaultNetworkingService defaultNetworkingService) { this.keySet = keySet; + this.parent = defaultNetworkingService; } public void setupConnection() { diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/queue/PacketQueue.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/queue/PacketQueue.java new file mode 100644 index 000000000..bc5b3b590 --- /dev/null +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/queue/PacketQueue.java @@ -0,0 +1,79 @@ +package com.craftmend.openaudiomc.generic.networking.queue; + +import com.craftmend.openaudiomc.OpenAudioMc; +import com.craftmend.openaudiomc.generic.client.objects.ClientConnection; +import com.craftmend.openaudiomc.generic.logging.OpenAudioLogger; +import com.craftmend.openaudiomc.generic.networking.DefaultNetworkingService; +import com.craftmend.openaudiomc.generic.networking.abstracts.AbstractPacket; +import com.craftmend.openaudiomc.generic.state.StateService; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class PacketQueue { + + private Map> packetQueue = new ConcurrentHashMap<>(); + + public void addPacket(UUID uuid, AbstractPacket packet) { + if (!packetQueue.containsKey(uuid)) { + packetQueue.put(uuid, new LinkedList<>()); + } + packetQueue.get(uuid).add(packet); + } + + public void clearAll() { + packetQueue.clear(); + } + + public void flush(DefaultNetworkingService networkingService) { + if (packetQueue.isEmpty()) return; + + // gradually flush + new Thread(() -> { + boolean cancelled = false; + List toRemove = new LinkedList<>(); + for (Map.Entry> entry : packetQueue.entrySet()) { + // is this user still connected? + ClientConnection clientConnection = networkingService.getClient(entry.getKey()); + if (clientConnection != null && clientConnection.isConnected()) { + // send all packets + int flushed = 0; + for (AbstractPacket packet : entry.getValue()) { + networkingService.send(clientConnection, packet); + flushed++; + } + + if (flushed > 0) { + OpenAudioLogger.info("Flushed " + flushed + " packets for " + entry.getKey()); + // wait 15 MS before flushing the next user + try { + Thread.sleep(15); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + toRemove.add(entry.getKey()); + } + + // check if the connection is still OK before we continue + if (!OpenAudioMc.getService(StateService.class).getCurrentState().isConnected()) { + OpenAudioLogger.warn("Connection was lost, stopping packet flush"); + cancelled = true; + break; + } + } + + if (cancelled) { + // only remove the ones we already flushed + for (UUID uuid : toRemove) { + packetQueue.remove(uuid); + } + } else { + clearAll(); + } + }).start(); + } +} From 732bfd16a7a25b5a99d36d69d7a3962ed37e1a9c Mon Sep 17 00:00:00 2001 From: Mats Date: Wed, 21 Feb 2024 17:31:02 +0100 Subject: [PATCH 4/5] Reconnect for voicechat, implement fix for tabcompletion with casing --- client/public/metadata.json | 2 +- client/src/metadata.json | 2 +- .../vistas-server/dependency-reduced-pom.xml | 6 -- plugin/src/main/bash/data.bin | 2 +- .../craftmend/openaudiomc/OpenAudioMc.java | 4 +- .../generic/commands/CommandService.java | 3 +- .../generic/oac/OpenaudioAccountService.java | 3 - .../rest/response/IntermediateResponse.java | 1 + .../voicechat/bus/VoiceApiConnection.java | 87 ++++++++++++++----- .../generic/voicechat/bus/VoiceWebsocket.java | 49 ++++++++--- .../subcommands/SpamTestSubCommand.java | 41 --------- plugin/src/main/resources/data.bin | 2 +- .../resources/openaudiomc-build.properties | 4 +- 13 files changed, 113 insertions(+), 93 deletions(-) delete mode 100644 plugin/src/main/java/com/craftmend/openaudiomc/spigot/modules/commands/subcommands/SpamTestSubCommand.java diff --git a/client/public/metadata.json b/client/public/metadata.json index e6c6db507..734910b1a 100644 --- a/client/public/metadata.json +++ b/client/public/metadata.json @@ -1 +1 @@ -{"buildMajor":1,"buildMinor":125,"buildRevision":218,"buildTag":"dev","buildDate":"Tue Feb 20 2024","build":"1.125.218 dev"} \ No newline at end of file +{"buildMajor":1,"buildMinor":125,"buildRevision":221,"buildTag":"dev","buildDate":"Wed Feb 21 2024","build":"1.125.221 dev"} \ No newline at end of file diff --git a/client/src/metadata.json b/client/src/metadata.json index e6c6db507..734910b1a 100644 --- a/client/src/metadata.json +++ b/client/src/metadata.json @@ -1 +1 @@ -{"buildMajor":1,"buildMinor":125,"buildRevision":218,"buildTag":"dev","buildDate":"Tue Feb 20 2024","build":"1.125.218 dev"} \ No newline at end of file +{"buildMajor":1,"buildMinor":125,"buildRevision":221,"buildTag":"dev","buildDate":"Wed Feb 21 2024","build":"1.125.221 dev"} \ No newline at end of file diff --git a/modules/vistas-server/dependency-reduced-pom.xml b/modules/vistas-server/dependency-reduced-pom.xml index 3cb256284..922179d83 100644 --- a/modules/vistas-server/dependency-reduced-pom.xml +++ b/modules/vistas-server/dependency-reduced-pom.xml @@ -174,12 +174,6 @@ junit 4.13.2 test - - - hamcrest-core - org.hamcrest - - diff --git a/plugin/src/main/bash/data.bin b/plugin/src/main/bash/data.bin index b6276108e..f31a2bc27 100755 --- a/plugin/src/main/bash/data.bin +++ b/plugin/src/main/bash/data.bin @@ -1 +1 @@ -BUILD_NUM="1309" +BUILD_NUM="1385" diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/OpenAudioMc.java b/plugin/src/main/java/com/craftmend/openaudiomc/OpenAudioMc.java index cf2a2c064..8352049e8 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/OpenAudioMc.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/OpenAudioMc.java @@ -176,11 +176,11 @@ public void disable() { serviceManager.getService(ModuleLoaderService.class).fire(ModuleEvent.SHUTDOWN); try { - serviceManager.getService(OpenaudioAccountService.class).shutdown(); - serviceManager.getService(RedisService.class).shutdown(); if (serviceManager.getService(StateService.class).getCurrentState().isConnected()) { serviceManager.getService(NetworkingService.class).stop(); } + serviceManager.getService(OpenaudioAccountService.class).shutdown(); + serviceManager.getService(RedisService.class).shutdown(); } catch (NoClassDefFoundError exception) { OpenAudioLogger.warn("Core dependencies were already unloaded by the classloader, skipping shutdown"); } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/commands/CommandService.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/commands/CommandService.java index 2105cf787..236949619 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/commands/CommandService.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/commands/CommandService.java @@ -159,7 +159,7 @@ public List getTabCompletions(CommandContext context, String[] args, Use try { Set completions = new HashSet<>(); for (String subCommand : getSubCommands(context)) { - if (args.length <= 1 && subCommand.startsWith(args[0])) { + if (args.length <= 1 && subCommand.toLowerCase(Locale.ROOT).startsWith(args[0].toLowerCase(Locale.ROOT))) { // Not typing yet, add the entire damn thing completions.add(subCommand); } @@ -180,6 +180,7 @@ public List getTabCompletions(CommandContext context, String[] args, Use int localArgIndex = args.length - 2; boolean isMatch = true; + for (int i = 0; i < localArgIndex; i++) { if (args.length - 1 < i + 1 || argumentSyntaxParts.length < i + 1) { isMatch = false; diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/oac/OpenaudioAccountService.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/oac/OpenaudioAccountService.java index 41c5a5ba0..d3791f86d 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/oac/OpenaudioAccountService.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/oac/OpenaudioAccountService.java @@ -181,9 +181,6 @@ public void startVoiceHandshake(boolean ignoreLocal) { return; } - if (OpenAudioMc.SERVER_ENVIRONMENT == ServerEnvironment.PRODUCTION) { - OpenAudioLogger.info("Preparing voice chat..."); - } // do magic, somehow fail, or login to the voice server isAttemptingVcConnect = true; RestRequest voiceLoginRequest = new RestRequest<>(VoiceSessionRequestResponse.class, Endpoint.VOICE_REQUEST_PASSWORD); diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/rest/response/IntermediateResponse.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/rest/response/IntermediateResponse.java index 4f819e0ac..f4b3317fb 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/rest/response/IntermediateResponse.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/rest/response/IntermediateResponse.java @@ -1,6 +1,7 @@ package com.craftmend.openaudiomc.generic.rest.response; import com.craftmend.openaudiomc.OpenAudioMc; +import com.craftmend.openaudiomc.generic.logging.OpenAudioLogger; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.internal.LinkedTreeMap; diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceApiConnection.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceApiConnection.java index 8feeb116c..55103259e 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceApiConnection.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceApiConnection.java @@ -16,15 +16,20 @@ import com.craftmend.openaudiomc.generic.rest.RestRequest; import com.craftmend.openaudiomc.generic.rest.response.NoResponse; import com.craftmend.openaudiomc.generic.rest.routes.Endpoint; +import com.craftmend.openaudiomc.generic.state.StateService; +import com.craftmend.openaudiomc.generic.state.states.ReconnectingState; import com.craftmend.openaudiomc.generic.storage.enums.StorageKey; import com.craftmend.openaudiomc.generic.voicechat.enums.VoiceApiStatus; import com.craftmend.openaudiomc.generic.voicechat.enums.VoiceServerEventType; import lombok.Getter; import lombok.Setter; +import okhttp3.OkHttpClient; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; public class VoiceApiConnection { @@ -35,6 +40,8 @@ public class VoiceApiConnection { @Getter private int maxSlots = 0; @Getter private String host = "none"; + private String lastToken = ""; + private int reconnectAttempts = 0; public VoiceApiConnection() { // setup tasks @@ -108,29 +115,37 @@ public VoiceApiConnection() { public void start(String server, String password, int slots) { // only connect when idle - if (status != VoiceApiStatus.IDLE) return; + if (status != VoiceApiStatus.IDLE && status != VoiceApiStatus.RECONNECTING) return; + boolean isReconnect = status == VoiceApiStatus.RECONNECTING; status = VoiceApiStatus.CONNECTING; + lastToken = password; maxSlots = slots; host = server; taskService.runAsync(() -> { // setup link - voiceWebsocket = new VoiceWebsocket(server, password); + voiceWebsocket = new VoiceWebsocket(server, password, isReconnect); // setup hooks voiceWebsocket.onError(this::onWsClose); voiceWebsocket.onReady(this::onWsOpen); // start? - boolean success = voiceWebsocket.start(); - if (success) { - OpenAudioLogger.info("Attempting to login to voice chat..."); - } else { - OpenAudioLogger.warn("Failed to initialize voice events."); - status = VoiceApiStatus.IDLE; + try { + boolean success = voiceWebsocket.start(); + if (success) { + OpenAudioLogger.info("Attempting to login to voice chat..."); + } else { + OpenAudioLogger.warn("Failed to initialize voice events."); + status = VoiceApiStatus.IDLE; + } + } catch (Exception e) { + OpenAudioLogger.error(e, "Failed to initialize voice events."); + onWsClose(); } }); } public void stop() { if (voiceWebsocket == null) return; + pushEvent(VoiceServerEventType.LOGOUT, new HashMap<>()); this.voiceWebsocket.stop(); this.onWsClose(); } @@ -139,26 +154,56 @@ private void onWsClose() { if (!(status == VoiceApiStatus.CONNECTED || status == VoiceApiStatus.CONNECTING)) { return; } - pushEvent(VoiceServerEventType.LOGOUT, new HashMap<>()); - status = VoiceApiStatus.IDLE; - // we disconnected! only fires once - // logout, since we're not using this session anymore - new RestRequest(NoResponse.class, Endpoint.VOICE_INVALIDATE_PASSWORD).run(); - for (ClientConnection client : OpenAudioMc.getService(NetworkingService.class).getClients()) { - if (client.getRtcSessionManager().isReady()) { - client.getUser().sendMessage(Platform.translateColors(StorageKey.MESSAGE_VC_UNSTABLE.getString())); - client.kick(() -> { - OpenAudioLogger.warn("Kicked " + client.getUser().getName() + " because the voicechat connection was lost"); - }); + + StateService stateService = OpenAudioMc.getService(StateService.class); + boolean shouldAttemptReconnect = stateService.getCurrentState().isConnected() || stateService.getCurrentState() instanceof ReconnectingState; + + // or was it intentional? + if (this.voiceWebsocket != null && this.voiceWebsocket.isAnnouncedShutdown()) { + shouldAttemptReconnect = false; + } + + if (reconnectAttempts >= 5) { + OpenAudioLogger.warn("Failed to reconnect to voice chat 5 times, giving up."); + status = VoiceApiStatus.IDLE; + reconnectAttempts = 0; + return; + } + + + if (shouldAttemptReconnect) { + reconnectAttempts++; + OpenAudioLogger.warn("Voice chat connection lost, attempting to reconnect in 2 seconds... (attempt " + reconnectAttempts + "/5)"); + status = VoiceApiStatus.RECONNECTING; + OpenAudioMc.resolveDependency(TaskService.class).schduleSyncDelayedTask(() -> { + OpenAudioLogger.warn("Reconnecting to voice chat..."); + start(host, lastToken, maxSlots); + }, 20 * 2); + return; + } else { + reconnectAttempts = 0; + pushEvent(VoiceServerEventType.LOGOUT, new HashMap<>()); + status = VoiceApiStatus.IDLE; + // we disconnected! only fires once + // logout, since we're not using this session anymore + new RestRequest(NoResponse.class, Endpoint.VOICE_INVALIDATE_PASSWORD).run(); + for (ClientConnection client : OpenAudioMc.getService(NetworkingService.class).getClients()) { + if (client.getRtcSessionManager().isReady()) { + client.getUser().sendMessage(Platform.translateColors(StorageKey.MESSAGE_VC_UNSTABLE.getString())); + client.kick(() -> { + OpenAudioLogger.warn("Kicked " + client.getUser().getName() + " because the voicechat connection was lost"); + }); + } } } } private void onWsOpen() { if (status == VoiceApiStatus.CONNECTED) return; - Thread.currentThread().setName("OaVoiceAPI"); - OpenAudioLogger.info("VoiceChat prepared and connected!"); + Thread.currentThread().setName("OaVoiceWorker"); + OpenAudioLogger.info("Voice chat connected!"); status = VoiceApiStatus.CONNECTED; + reconnectAttempts = 0; // seed online players pushEvent(VoiceServerEventType.HEARTBEAT, EMPTY_PAYLOAD); pushEvent(VoiceServerEventType.HEARTBEAT, EMPTY_PAYLOAD); diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceWebsocket.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceWebsocket.java index 49196c3fd..60e8d9414 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceWebsocket.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceWebsocket.java @@ -6,6 +6,7 @@ import com.craftmend.openaudiomc.generic.rest.RestRequest; import com.craftmend.openaudiomc.generic.rest.response.NoResponse; import com.craftmend.openaudiomc.generic.rest.routes.Endpoint; +import lombok.Getter; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @@ -17,21 +18,27 @@ import java.util.HashSet; import java.util.Set; +import java.util.concurrent.TimeUnit; public class VoiceWebsocket extends WebSocketListener { + private static final OkHttpClient CLIENT = new OkHttpClient.Builder() + .connectTimeout(2, TimeUnit.SECONDS).build(); + private final Set onEerror = new HashSet<>(); private final Set onReady = new HashSet<>(); - private final OkHttpClient client = new OkHttpClient.Builder().build(); private boolean isReady = false; private final String server; private final String password; - private boolean closed; + private boolean closed = false; private WebSocket webSocket; + private final boolean isReconnectAttempt; + @Getter private boolean announcedShutdown = false; - public VoiceWebsocket(String server, String password) { + public VoiceWebsocket(String server, String password, boolean isReconnectAttempt) { this.server = server; this.password = password; + this.isReconnectAttempt = isReconnectAttempt; } public boolean start() { @@ -41,6 +48,7 @@ public boolean start() { preAuthCheck.setQuery("publicKey", authenticationService.getServerKeySet().getPublicKey().getValue()); preAuthCheck.setQuery("privateKey", authenticationService.getServerKeySet().getPrivateKey().getValue()); preAuthCheck.setQuery("password", this.password); + preAuthCheck.setQuery("reconnect", String.valueOf(this.isReconnectAttempt)); preAuthCheck.setBaseUrl(this.server); preAuthCheck.run(); @@ -55,6 +63,7 @@ public boolean start() { .setQuery("publicKey", authenticationService.getServerKeySet().getPublicKey().getValue()) .setQuery("privateKey", authenticationService.getServerKeySet().getPrivateKey().getValue()) .setQuery("password", this.password) + .setQuery("reconnect", String.valueOf(this.isReconnectAttempt)) .setBaseUrl(this.server) .buildURL(); @@ -65,18 +74,17 @@ public boolean start() { .build(); this.isReady = false; - webSocket = client.newWebSocket(request, this); + webSocket = CLIENT.newWebSocket(request, this); + CLIENT.connectionPool().evictAll(); return true; } public void stop() { closed = true; + announcedShutdown = true; if (webSocket != null) { webSocket.close(1000, "Goodbye"); } - if (client != null && client.dispatcher() != null) { - client.dispatcher().executorService().shutdown(); - } this.isReady = false; } @@ -90,28 +98,43 @@ public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String rea if (code != 1000) { OpenAudioLogger.warn("RTC connection closed with code " + code + " and reason " + reason); } - handleError(); + handleError(false); } @Override public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) { + // closed by server + webSocket.close(1000, null); if (code != 1000) { OpenAudioLogger.warn("Voicechat ws closing: " + reason + " - " + code); } - handleError(); + handleError(false); } @Override public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { + // Second Change + webSocket.close(1000, null); + String nullableMessage = ""; if (response != null) nullableMessage = response.message(); + // did we get a normal http response? + if (response != null && response.code() != 101) { + OpenAudioLogger.warn("Got unexpected http: " + t.getMessage() + " - " + nullableMessage); + handleError(true); + return; + } OpenAudioLogger.warn("Voicechat ws error: " + t.getMessage() + " - " + nullableMessage); - handleError(); + handleError(false); } @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { - + switch (text) { + case "INTENT_TO_DISCONNECT": + announcedShutdown = true; + return; + } } @Override @@ -136,8 +159,8 @@ public void onReady(Runnable runnable) { this.onReady.add(runnable); } - private void handleError() { - if (!this.isReady) return; + private void handleError(boolean ignoreReady) { + if (!this.isReady && !ignoreReady) return; if (closed) return; this.isReady = false; for (Runnable runnable : this.onEerror) { diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/spigot/modules/commands/subcommands/SpamTestSubCommand.java b/plugin/src/main/java/com/craftmend/openaudiomc/spigot/modules/commands/subcommands/SpamTestSubCommand.java deleted file mode 100644 index 1f523dfb0..000000000 --- a/plugin/src/main/java/com/craftmend/openaudiomc/spigot/modules/commands/subcommands/SpamTestSubCommand.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.craftmend.openaudiomc.spigot.modules.commands.subcommands; - -import com.craftmend.openaudiomc.OpenAudioMc; -import com.craftmend.openaudiomc.generic.commands.interfaces.SubCommand; -import com.craftmend.openaudiomc.generic.commands.objects.Argument; -import com.craftmend.openaudiomc.generic.database.DatabaseService; -import com.craftmend.openaudiomc.generic.user.User; -import com.craftmend.openaudiomc.spigot.modules.shortner.AliasService; -import com.craftmend.openaudiomc.spigot.modules.shortner.data.Alias; -import org.bukkit.ChatColor; - -public class SpamTestSubCommand extends SubCommand { - - public SpamTestSubCommand() { - super("alias"); - registerArguments( - new Argument(" ", - "Register a Alias for a source URL so you can easaly memorize them and can paste them onto signs without having to type a complete dictionary." + - " When an alias like onride_music is set, you can trigger it by using a:onride_music as your source.") - ); - } - - @Override - public void onExecute(User sender, String[] args) { - if (args.length == 2) { - String aliasName = args[0].toLowerCase(); - String aliasSource = args[1]; - Alias alias = new Alias(aliasName, aliasSource); - OpenAudioMc.getService(AliasService.class).getAliasMap().put(aliasName, alias); - - OpenAudioMc.getService(DatabaseService.class).getRepository(Alias.class) - .save(alias); - - message(sender, ChatColor.GREEN + "Success! the alias " + ChatColor.YELLOW + "a:" + aliasName.toLowerCase() + ChatColor.GRAY + " will be read as " + ChatColor.YELLOW + aliasSource); - return; - } - - sender.makeExecuteCommand("oa help " + getCommand()); - } - -} diff --git a/plugin/src/main/resources/data.bin b/plugin/src/main/resources/data.bin index b6276108e..f31a2bc27 100755 --- a/plugin/src/main/resources/data.bin +++ b/plugin/src/main/resources/data.bin @@ -1 +1 @@ -BUILD_NUM="1309" +BUILD_NUM="1385" diff --git a/plugin/src/main/resources/openaudiomc-build.properties b/plugin/src/main/resources/openaudiomc-build.properties index 8c8a1858e..c85b76e06 100644 --- a/plugin/src/main/resources/openaudiomc-build.properties +++ b/plugin/src/main/resources/openaudiomc-build.properties @@ -1,3 +1,3 @@ -BUILD_VERSION="1309" -BUILD_COMMIT="41249c65469a6559ff4adf1b0fd180257b3598af" +BUILD_VERSION="1385" +BUILD_COMMIT="2421a787cfa123f4c969dd33f83114d18ede0261" BUILD_AUTHOR="Mats" From 35991221a99e04a2218a3fb475f61f142a02138a Mon Sep 17 00:00:00 2001 From: Mats Date: Wed, 21 Feb 2024 18:52:38 +0100 Subject: [PATCH 5/5] Finish reconnect --- plugin/src/main/bash/data.bin | 2 +- .../networking/drivers/SystemDriver.java | 5 +++ .../voicechat/bus/VoiceApiConnection.java | 32 +++++++++++-------- .../{VoiceWebsocket.java => VoiceSocket.java} | 4 +-- plugin/src/main/resources/data.bin | 2 +- .../resources/openaudiomc-build.properties | 4 +-- 6 files changed, 29 insertions(+), 20 deletions(-) rename plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/{VoiceWebsocket.java => VoiceSocket.java} (97%) diff --git a/plugin/src/main/bash/data.bin b/plugin/src/main/bash/data.bin index f31a2bc27..ba1ca5a7c 100755 --- a/plugin/src/main/bash/data.bin +++ b/plugin/src/main/bash/data.bin @@ -1 +1 @@ -BUILD_NUM="1385" +BUILD_NUM="1386" diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java index e4ecead13..87470ea99 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/networking/drivers/SystemDriver.java @@ -1,6 +1,7 @@ package com.craftmend.openaudiomc.generic.networking.drivers; import com.craftmend.openaudiomc.OpenAudioMc; +import com.craftmend.openaudiomc.generic.authentication.AuthenticationService; import com.craftmend.openaudiomc.generic.logging.OpenAudioLogger; import com.craftmend.openaudiomc.generic.oac.OpenaudioAccountService; import com.craftmend.openaudiomc.generic.media.time.TimeService; @@ -65,6 +66,10 @@ public void boot(Socket socket, SocketConnection connector) { announcedShutdown = true; }); + socket.on("client-token-cache", args -> { + OpenAudioMc.getService(AuthenticationService.class).getDriver().initCache(); + }); + socket.on("reconnect-clients", args -> { JSONArray rawData = (JSONArray) args[0]; String[] data = new String[rawData.length()]; diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceApiConnection.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceApiConnection.java index 55103259e..6cad2b875 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceApiConnection.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceApiConnection.java @@ -23,19 +23,17 @@ import com.craftmend.openaudiomc.generic.voicechat.enums.VoiceServerEventType; import lombok.Getter; import lombok.Setter; -import okhttp3.OkHttpClient; +import java.net.ConnectException; import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; public class VoiceApiConnection { private static final Map EMPTY_PAYLOAD = new HashMap<>(); @Setter @Getter private VoiceApiStatus status = VoiceApiStatus.IDLE; - private VoiceWebsocket voiceWebsocket; + private VoiceSocket voiceSocket; private final TaskService taskService; @Getter private int maxSlots = 0; @@ -123,13 +121,13 @@ public void start(String server, String password, int slots) { host = server; taskService.runAsync(() -> { // setup link - voiceWebsocket = new VoiceWebsocket(server, password, isReconnect); + voiceSocket = new VoiceSocket(server, password, isReconnect); // setup hooks - voiceWebsocket.onError(this::onWsClose); - voiceWebsocket.onReady(this::onWsOpen); + voiceSocket.onError(this::onWsClose); + voiceSocket.onReady(this::onWsOpen); // start? try { - boolean success = voiceWebsocket.start(); + boolean success = voiceSocket.start(); if (success) { OpenAudioLogger.info("Attempting to login to voice chat..."); } else { @@ -137,16 +135,22 @@ public void start(String server, String password, int slots) { status = VoiceApiStatus.IDLE; } } catch (Exception e) { - OpenAudioLogger.error(e, "Failed to initialize voice events."); - onWsClose(); + // is it a connect exception? + if (e instanceof ConnectException) { + OpenAudioLogger.warn("Failed to connect to voice chat (ConnectException)"); + onWsClose(); + } else { + OpenAudioLogger.error(e, "Failed to initialize voice events."); + onWsClose(); + } } }); } public void stop() { - if (voiceWebsocket == null) return; + if (voiceSocket == null) return; pushEvent(VoiceServerEventType.LOGOUT, new HashMap<>()); - this.voiceWebsocket.stop(); + this.voiceSocket.stop(); this.onWsClose(); } @@ -159,7 +163,7 @@ private void onWsClose() { boolean shouldAttemptReconnect = stateService.getCurrentState().isConnected() || stateService.getCurrentState() instanceof ReconnectingState; // or was it intentional? - if (this.voiceWebsocket != null && this.voiceWebsocket.isAnnouncedShutdown()) { + if (this.voiceSocket != null && this.voiceSocket.isAnnouncedShutdown()) { shouldAttemptReconnect = false; } @@ -281,7 +285,7 @@ private void pushEvent(VoiceServerEventType event, Map arguments eventData.append("~").append(entry.getKey()).append("=").append(entry.getValue()); } - this.voiceWebsocket.pushEventBody(eventData.toString()); + this.voiceSocket.pushEventBody(eventData.toString()); } } diff --git a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceWebsocket.java b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceSocket.java similarity index 97% rename from plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceWebsocket.java rename to plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceSocket.java index 60e8d9414..dccaac5ac 100644 --- a/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceWebsocket.java +++ b/plugin/src/main/java/com/craftmend/openaudiomc/generic/voicechat/bus/VoiceSocket.java @@ -20,7 +20,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; -public class VoiceWebsocket extends WebSocketListener { +public class VoiceSocket extends WebSocketListener { private static final OkHttpClient CLIENT = new OkHttpClient.Builder() .connectTimeout(2, TimeUnit.SECONDS).build(); @@ -35,7 +35,7 @@ public class VoiceWebsocket extends WebSocketListener { private final boolean isReconnectAttempt; @Getter private boolean announcedShutdown = false; - public VoiceWebsocket(String server, String password, boolean isReconnectAttempt) { + public VoiceSocket(String server, String password, boolean isReconnectAttempt) { this.server = server; this.password = password; this.isReconnectAttempt = isReconnectAttempt; diff --git a/plugin/src/main/resources/data.bin b/plugin/src/main/resources/data.bin index f31a2bc27..ba1ca5a7c 100755 --- a/plugin/src/main/resources/data.bin +++ b/plugin/src/main/resources/data.bin @@ -1 +1 @@ -BUILD_NUM="1385" +BUILD_NUM="1386" diff --git a/plugin/src/main/resources/openaudiomc-build.properties b/plugin/src/main/resources/openaudiomc-build.properties index c85b76e06..1cb9835d9 100644 --- a/plugin/src/main/resources/openaudiomc-build.properties +++ b/plugin/src/main/resources/openaudiomc-build.properties @@ -1,3 +1,3 @@ -BUILD_VERSION="1385" -BUILD_COMMIT="2421a787cfa123f4c969dd33f83114d18ede0261" +BUILD_VERSION="1386" +BUILD_COMMIT="732bfd16a7a25b5a99d36d69d7a3962ed37e1a9c" BUILD_AUTHOR="Mats"