diff --git a/tgui/packages/tgui-panel/index.jsx b/tgui/packages/tgui-panel/index.jsx
index 9f43a3091e25..6a365114cef2 100644
--- a/tgui/packages/tgui-panel/index.jsx
+++ b/tgui/packages/tgui-panel/index.jsx
@@ -23,6 +23,7 @@ import { pingMiddleware, pingReducer } from './ping';
import { settingsMiddleware, settingsReducer } from './settings';
import { telemetryMiddleware } from './telemetry';
import { setGlobalStore } from 'tgui/backend';
+import { websocketMiddleware } from './websocket';
perf.mark('inception', window.performance?.timing?.navigationStart);
perf.mark('init');
@@ -37,6 +38,7 @@ const store = configureStore({
}),
middleware: {
pre: [
+ websocketMiddleware,
chatMiddleware,
pingMiddleware,
telemetryMiddleware,
diff --git a/tgui/packages/tgui-panel/settings/SettingsPanel.jsx b/tgui/packages/tgui-panel/settings/SettingsPanel.jsx
index 07ef79754dfc..ee3af451764a 100644
--- a/tgui/packages/tgui-panel/settings/SettingsPanel.jsx
+++ b/tgui/packages/tgui-panel/settings/SettingsPanel.jsx
@@ -40,6 +40,8 @@ import {
selectHighlightSettings,
selectHighlightSettingById,
} from './selectors';
+import { reconnectWebsocket, disconnectWebsocket } from '../websocket';
+import { chatRenderer } from '../chat/renderer';
export const SettingsPanel = (props, context) => {
const activeTab = useSelector(context, selectActiveTab);
@@ -71,6 +73,7 @@ export const SettingsPanel = (props, context) => {
{activeTab === 'general' &&
localhost:1234
`,
+ );
+ return;
+ }
+ sendWSNotice(`Error creating websocket: ${e.name} - ${e.message}`);
+ return;
+ }
+
+ websocket.addEventListener('open', () => {
+ sendWSNotice('Websocket connected!', true);
+ });
+
+ websocket.addEventListener('close', function closeEventThing(ev) {
+ const { websocketEnabled } = selectSettings(store.getState());
+ if (!websocketEnabled) {
+ // Doing this because eitherwise it 'close' will get called
+ // thousands of times per second if the connection wasn't closed properly.
+ // I don't know WHY it does that but it just does.
+ ev.target?.removeEventListener('close', closeEventThing);
+ websocket?.removeEventListener('close', closeEventThing);
+ return;
+ }
+ if (ev.code !== WEBSOCKET_DISABLED && ev.code !== WEBSOCKET_REATTEMPT) {
+ sendWSNotice(
+ `Websocket disconnected! Code: ${ev.code} Reason: ${ev.reason || 'None provided'}`,
+ );
+ }
+ });
+
+ websocket.addEventListener('error', () => {
+ // Really don't think we should do anything here.
+ // setTimeout(() => setupWebsocket(store), 2000);
+ });
+ };
+
+ setTimeout(() => setupWebsocket(store));
+
+ return (next) => (action) => {
+ const { type, payload } = action as {
+ type: string;
+ payload: {
+ websocketEnabled: boolean;
+ websocketServer: string;
+ };
+ };
+ if (!payload) return next(action);
+ if (type === updateSettings.type || type === loadSettings.type) {
+ if (typeof payload?.websocketEnabled === 'undefined') {
+ store.dispatch(
+ updateSettings({
+ websocketEnabled: false,
+ }),
+ );
+ return next(action);
+ }
+ if (!payload.websocketEnabled) {
+ websocket?.close(WEBSOCKET_DISABLED);
+ websocket = null;
+ } else if (
+ !websocket ||
+ websocket.url !== payload.websocketServer ||
+ (payload.websocketEnabled &&
+ (!websocket || websocket.readyState !== websocket.OPEN))
+ ) {
+ websocket?.close(WEBSOCKET_REATTEMPT, 'Websocket settings changed');
+ sendWSNotice('Websocket enabled.', true);
+ setupWebsocket(store);
+ }
+ return next(action);
+ }
+
+ if (type === reconnectWebsocket.type) {
+ const settings = selectSettings(store.getState());
+ if (settings.websocketEnabled) setupWebsocket(store);
+ return next(action);
+ }
+
+ if (type === disconnectWebsocket.type) {
+ websocket?.close(WEBSOCKET_DISABLED);
+ websocket = null;
+ sendWSNotice('Websocket forcefully disconnected.', true);
+ }
+
+ websocket &&
+ websocket.readyState === websocket.OPEN &&
+ websocket?.send(
+ JSON.stringify({
+ type,
+ payload,
+ }),
+ );
+ return next(action);
+ };
+};