aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--public/client.html31
-rw-r--r--tsconfig.json2
-rw-r--r--webAO/client.ts29
-rw-r--r--webAO/client/appendICNotice.ts10
-rw-r--r--webAO/client/handleBans.ts3
-rw-r--r--webAO/dom/disconnectButton.ts12
-rw-r--r--webAO/dom/reconnectButton.ts24
-rw-r--r--webAO/dom/window.ts3
-rw-r--r--webAO/packets/handlers/handleDONE.ts29
-rw-r--r--webAO/packets/handlers/handleMC.ts4
-rw-r--r--webAO/packets/handlers/handlePV.ts1
-rw-r--r--webAO/styles/client.css68
-rw-r--r--webAO/utils/queryParser.ts4
-rw-r--r--webAO/viewport/utils/setSide.ts2
14 files changed, 180 insertions, 42 deletions
diff --git a/public/client.html b/public/client.html
index ef60e15..d6ac1b6 100644
--- a/public/client.html
+++ b/public/client.html
@@ -131,20 +131,22 @@
<br /><br />
<div id="client_chartable"></div>
</div>
- <div id="client_error" class="error" style="display: none">
- <h1 id="client_errortext">Connection error.</h1>
- <p style="color: #a00">
- Code:
- <span id="error_id">(none)</span>
+ </div>
+ <div id="client_error_overlay" style="display: none">
+ <div id="client_error">
+ <div id="client_error_icon">&#x26A0;</div>
+ <h2 id="client_errortext"></h2>
+ <p id="client_error_code">
+ Error code: <span id="error_id">(none)</span>
+ </p>
+ <button id="client_reconnect" onclick="ReconnectButton()">
+ Reconnect
+ </button>
+ <p id="client_error_help">
+ Having trouble?
+ <a href="https://discord.gg/9rYQVVQ" target="_blank">Join us on Discord</a>
</p>
</div>
- <p>
- Having trouble?
- <a href="https://discord.gg/9rYQVVQ">Join us on Discord</a>
- </p>
- <button id="client_reconnect" onclick="ReconnectButton()">
- Reconnect
- </button>
</div>
</body>
@@ -954,6 +956,11 @@
If you don't agree, disable cookies for this site in your
browser.</span
>
+ <br />
+ <br />
+ <button id="client_disconnect" onclick="DisconnectButton()">
+ Disconnect
+ </button>
</span>
<!-- About section -->
diff --git a/tsconfig.json b/tsconfig.json
index 9a5045a..f33e7c1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -10,5 +10,5 @@
"resolveJsonModule": true,
"esModuleInterop": true
},
- "include": ["./webAO/*", "./webAO/viewport/viewport.ts"]
+ "include": ["./webAO/**/*"]
}
diff --git a/webAO/client.ts b/webAO/client.ts
index 05a40c9..703ce73 100644
--- a/webAO/client.ts
+++ b/webAO/client.ts
@@ -13,6 +13,7 @@ import { Viewport } from "./viewport/interfaces/Viewport";
import { EventEmitter } from "events";
import { onReplayGo } from "./dom/onReplayGo";
import { packetHandler } from "./packets/packetHandler";
+import { appendICNotice } from "./client/appendICNotice";
import { loadResources } from "./client/loadResources";
import { AO_HOST } from "./client/aoHost";
import {
@@ -22,7 +23,8 @@ import {
} from "./client/fetchLists";
import getCookie from "./utils/getCookie";
import setCookie from "./utils/setCookie";
-const { ip: serverIP, connect, mode, theme, serverName } = queryParser();
+const { ip: serverIP, connect, mode, theme, serverName, char: autoChar, area: autoArea } = queryParser();
+export { autoChar, autoArea };
document.title = serverName;
@@ -111,6 +113,8 @@ export enum clientState {
Connected,
// Should be set once the client has joined the server (after handshake)
Joined,
+ // Set when a reconnect attempt is in progress
+ Reconnecting,
}
export let lastICMessageTime = new Date(0);
@@ -274,6 +278,11 @@ class Client extends EventEmitter {
*/
onOpen(_e: Event) {
client.state = clientState.Connected;
+ document.getElementById("client_error_overlay").style.display = "none";
+ document.getElementById("client_waiting").style.display = "block";
+ document.getElementById("client_loading").style.display = "block";
+ document.getElementById("client_charselect").style.display = "none";
+ appendICNotice("Connected");
client.joinServer();
}
@@ -282,19 +291,21 @@ class Client extends EventEmitter {
* @param {CloseEvent} e
*/
onClose(e: CloseEvent) {
- client.state = clientState.NotConnected;
console.error(`The connection was closed: ${e.reason} (${e.code})`);
+ if (this.state === clientState.Reconnecting) return;
+ client.state = clientState.NotConnected;
if (this.banned === false) {
if (this.areas.length > 0) {
document.getElementById("client_errortext").textContent =
"You were disconnected from the server.";
+ appendICNotice("Disconnected");
} else {
document.getElementById("client_errortext").textContent =
"Could not connect to the server.";
}
+ (<HTMLElement>document.getElementById("client_reconnect")).style.display = "";
}
- document.getElementById("client_waiting").style.display = "block";
- document.getElementById("client_error").style.display = "flex";
+ document.getElementById("client_error_overlay").style.display = "flex";
document.getElementById("client_loading").style.display = "none";
document.getElementById("error_id").textContent = String(e.code);
this.cleanup();
@@ -364,10 +375,14 @@ class Client extends EventEmitter {
* @param {ErrorEvent} e
*/
onError(e: ErrorEvent) {
- client.state = clientState.NotConnected;
console.error(`A network error occurred`);
console.error(e);
- document.getElementById("client_error").style.display = "flex";
+ if (this.state === clientState.Reconnecting) return;
+ client.state = clientState.NotConnected;
+ document.getElementById("client_errortext").textContent =
+ "Could not connect to the server.";
+ (<HTMLElement>document.getElementById("client_reconnect")).style.display = "";
+ document.getElementById("client_error_overlay").style.display = "flex";
this.cleanup();
}
@@ -376,7 +391,7 @@ class Client extends EventEmitter {
*/
cleanup() {
clearInterval(this.checkUpdater);
- this.serv.close();
+ if (this.serv) this.serv.close();
}
/**
diff --git a/webAO/client/appendICNotice.ts b/webAO/client/appendICNotice.ts
new file mode 100644
index 0000000..29065db
--- /dev/null
+++ b/webAO/client/appendICNotice.ts
@@ -0,0 +1,10 @@
+/**
+ * Appends a notice (hrtext divider) to the IC log.
+ * @param {string} msg the notice text
+ */
+export function appendICNotice(msg: string) {
+ const el = document.createElement("div");
+ el.className = "hrtext";
+ el.textContent = msg;
+ document.getElementById("client_log")!.appendChild(el);
+}
diff --git a/webAO/client/handleBans.ts b/webAO/client/handleBans.ts
index 9eec9be..004b27e 100644
--- a/webAO/client/handleBans.ts
+++ b/webAO/client/handleBans.ts
@@ -6,10 +6,9 @@ import { safeTags } from "../encoding";
* @param {string} reason why
*/
export const handleBans = (type: string, reason: string) => {
- document.getElementById("client_error")!.style.display = "flex";
+ document.getElementById("client_error_overlay")!.style.display = "flex";
document.getElementById("client_errortext")!.innerHTML =
`${type}:<br>${safeTags(reason).replace(/\n/g, "<br />")}`;
(<HTMLElement>document.getElementById("client_reconnect")).style.display =
"none";
- alert(type + ":\r" + reason);
};
diff --git a/webAO/dom/disconnectButton.ts b/webAO/dom/disconnectButton.ts
new file mode 100644
index 0000000..35daf16
--- /dev/null
+++ b/webAO/dom/disconnectButton.ts
@@ -0,0 +1,12 @@
+import { client } from "../client";
+
+/**
+ * Triggered when the disconnect button in settings is pushed.
+ * Forces a disconnection for testing purposes.
+ */
+export function DisconnectButton() {
+ if (client.serv && client.serv.readyState === WebSocket.OPEN) {
+ client.serv.close();
+ }
+}
+window.DisconnectButton = DisconnectButton;
diff --git a/webAO/dom/reconnectButton.ts b/webAO/dom/reconnectButton.ts
index 079e7fc..eecb915 100644
--- a/webAO/dom/reconnectButton.ts
+++ b/webAO/dom/reconnectButton.ts
@@ -1,16 +1,28 @@
-import Client, { client, setClient } from "../client";
+import Client, { client, clientState, setClient } from "../client";
import queryParser from "../utils/queryParser";
-const { ip: serverIP } = queryParser();
+const { ip: serverIP, connect } = queryParser();
/**
* Triggered when the reconnect button is pushed.
*/
export function ReconnectButton() {
- client.cleanup();
- setClient(new Client(serverIP));
+ document.getElementById("client_errortext")!.textContent = "Reconnecting...";
- if (client) {
- document.getElementById("client_error")!.style.display = "none";
+ // Build the connection string the same way the initial connection does
+ let connectionString = connect;
+ if (!connectionString && serverIP) {
+ // if connectionString is not set, try IP
+ // and just guess ws, though it could be wss
+ connectionString = `ws://${serverIP}`;
}
+
+ const hdid = client.hdid;
+ client.state = clientState.Reconnecting;
+ client.cleanup();
+
+ const newClient = new Client(connectionString);
+ setClient(newClient);
+ newClient.hdid = hdid;
+ newClient.connect();
}
window.ReconnectButton = ReconnectButton;
diff --git a/webAO/dom/window.ts b/webAO/dom/window.ts
index ee1b121..ba19982 100644
--- a/webAO/dom/window.ts
+++ b/webAO/dom/window.ts
@@ -29,7 +29,8 @@ declare global {
pickEmotion: (emo: number) => void;
pickChar: (ccharacter: any) => void;
chartable_filter: (_event: any) => void;
- ReconnectButton: (_event: any) => void;
+ ReconnectButton: () => void;
+ DisconnectButton: () => void;
opusCheck: (channel: HTMLAudioElement) => OnErrorEventHandlerNonNull;
imgError: (image: any) => void;
charError: (image: any) => void;
diff --git a/webAO/packets/handlers/handleDONE.ts b/webAO/packets/handlers/handleDONE.ts
index 676c807..947e2a1 100644
--- a/webAO/packets/handlers/handleDONE.ts
+++ b/webAO/packets/handlers/handleDONE.ts
@@ -1,5 +1,6 @@
import queryParser from "../../utils/queryParser";
-import { client, clientState } from "../../client";
+import { client, clientState, autoChar, autoArea } from "../../client";
+import { area_click } from "../../dom/areaClick";
const { mode } = queryParser();
/**
@@ -16,4 +17,30 @@ export const handleDONE = (_args: string[]) => {
// Spectators don't need to pick a character
document.getElementById("client_waiting")!.style.display = "none";
}
+
+ if (autoArea) {
+ const areaIndex = client.areas.findIndex(
+ (a: any) => a && a.name.toLowerCase() === autoArea.toLowerCase()
+ );
+ if (areaIndex !== -1) {
+ const el = document.getElementById(`area${areaIndex}`);
+ if (el) {
+ area_click(el as HTMLElement);
+ }
+ }
+ }
+
+ if (autoChar) {
+ // Hide charselect immediately (spectator mode) so the user isn't stuck
+ // on the selection screen. If the CC request succeeds, PV will confirm it.
+ document.getElementById("client_waiting")!.style.display = "none";
+ document.getElementById("client_charselect")!.style.display = "none";
+
+ const charIndex = client.chars.findIndex(
+ (c: any) => c && c.name.toLowerCase() === autoChar.toLowerCase()
+ );
+ if (charIndex !== -1) {
+ client.sender.sendCharacter(charIndex);
+ }
+ }
};
diff --git a/webAO/packets/handlers/handleMC.ts b/webAO/packets/handlers/handleMC.ts
index 44237ea..187a9cd 100644
--- a/webAO/packets/handlers/handleMC.ts
+++ b/webAO/packets/handlers/handleMC.ts
@@ -39,9 +39,9 @@ export const handleMC = (args: string[]) => {
if (charID >= 0) {
musicname = client.chars[charID].name;
- appendICLog(`${musicname} changed music to ${track} ${looptext}`,showname);
+ appendICLog(`changed music to ${track} ${looptext}`, showname, musicname);
} else {
- appendICLog(`The music was changed to ${track} ${looptext}`,showname);
+ appendICLog(`The music was changed to ${track} ${looptext}`, showname);
}
document.getElementById("client_trackstatustext")!.innerText = track;
diff --git a/webAO/packets/handlers/handlePV.ts b/webAO/packets/handlers/handlePV.ts
index 1ee13c8..90e47f1 100644
--- a/webAO/packets/handlers/handlePV.ts
+++ b/webAO/packets/handlers/handlePV.ts
@@ -102,4 +102,5 @@ export const handlePV = async (args: string[]) => {
} else {
document.getElementById("button_4")!.style.display = "none";
}
+
};
diff --git a/webAO/styles/client.css b/webAO/styles/client.css
index e3ee820..673d176 100644
--- a/webAO/styles/client.css
+++ b/webAO/styles/client.css
@@ -58,25 +58,75 @@
}
}
+#client_error_overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.6);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 200;
+}
+
#client_error {
- position: absolute;
display: flex;
flex-direction: column;
- padding: 10px;
- top: 50%;
- left: 50%;
- margin-right: -50%;
- transform: translate(-50%, -50%);
+ padding: 24px 36px;
justify-content: center;
align-items: center;
- background: #a00;
+ background: rgba(0, 0, 0, 0.9);
+ border: 2px solid #c00;
+ border-radius: 8px;
color: #fff;
font-size: large;
- z-index: 100;
+ min-width: 280px;
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
+}
+
+#client_error_icon {
+ font-size: 48px;
+ color: #f44;
+ margin-bottom: 4px;
}
#client_errortext {
- animation: error_blink 3s ease-in-out infinite;
+ margin: 4px 0 8px;
+ text-align: center;
+}
+
+#client_error_code {
+ color: #999;
+ font-size: 12px;
+ margin: 0 0 16px;
+}
+
+#client_reconnect {
+ background: #c00;
+ color: #fff;
+ border: none;
+ border-radius: 4px;
+ padding: 10px 32px;
+ font-size: 16px;
+ cursor: pointer;
+ margin-bottom: 12px;
+ transition: background 0.2s;
+}
+
+#client_reconnect:hover {
+ background: #e22;
+}
+
+#client_error_help {
+ color: #aaa;
+ font-size: 12px;
+ margin: 0;
+}
+
+#client_error_help a {
+ color: #6af;
}
#client_secondfactor {
diff --git a/webAO/utils/queryParser.ts b/webAO/utils/queryParser.ts
index 5263f6e..9a91795 100644
--- a/webAO/utils/queryParser.ts
+++ b/webAO/utils/queryParser.ts
@@ -7,6 +7,8 @@ interface QueryParams {
asset: string;
theme: string;
serverName: string;
+ char: string;
+ area: string;
}
const queryParser = (): QueryParams => {
@@ -19,6 +21,8 @@ const queryParser = (): QueryParams => {
asset: urlParams.get("asset") || `${protocol}//attorneyoffline.de/base/`,
theme: urlParams.get("theme") || "default",
serverName: urlParams.get("serverName") || "Attorney Online session",
+ char: urlParams.get("char") || "",
+ area: urlParams.get("area") || "",
};
return queryParams as QueryParams;
};
diff --git a/webAO/viewport/utils/setSide.ts b/webAO/viewport/utils/setSide.ts
index 658d959..564d477 100644
--- a/webAO/viewport/utils/setSide.ts
+++ b/webAO/viewport/utils/setSide.ts
@@ -77,7 +77,7 @@ export const set_side = async ({
if (showSpeedLines === true) {
court.src = `${AO_HOST}themes/default/${encodeURI(speedLines)}`;
} else {
- setBackgroundImage("client_court_classic",client.viewport.getBackgroundName(),bg);
+ setBackgroundImage(court.id, client.viewport.getBackgroundName(), bg);
}
if (showDesk === true && desk) {