From 95e4124b0c02b4e5be383c41ed4241566f40e6e6 Mon Sep 17 00:00:00 2001 From: David Skoland Date: Sat, 7 Feb 2026 21:53:59 +0100 Subject: Prefetch char.ini for characters present in area via playerlist Store player data (charId, area) in an in-memory Map on the client, updated by PR/PU packet handlers. Use this to eagerly load char.ini when a player's character appears in our area or when switching areas, eliminating the lazy-load delay on first IC message. Co-Authored-By: Claude Opus 4.6 --- webAO/dom/areaClick.ts | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'webAO/dom') diff --git a/webAO/dom/areaClick.ts b/webAO/dom/areaClick.ts index 1e41f4b..19953a5 100644 --- a/webAO/dom/areaClick.ts +++ b/webAO/dom/areaClick.ts @@ -1,5 +1,6 @@ import { client } from "../client"; import { updatePlayerAreas } from "./updatePlayerAreas"; +import { ensureCharIni } from "../client/handleCharacterInfo"; /** * Triggered when an item on the area list is clicked. * @param {HTMLElement} el @@ -14,5 +15,12 @@ export function area_click(el: HTMLElement) { document.getElementById("client_log")!.appendChild(areaHr); client.area = Number(el.id.substring(4)); updatePlayerAreas(client.area); + + // Prefetch char.ini for all characters present in the new area + for (const player of client.players.values()) { + if (player.area === client.area && player.charId >= 0) { + ensureCharIni(player.charId); + } + } } window.area_click = area_click; -- cgit From 020dfcda00ca06b9a06e7076eaf8a0164ae1327e Mon Sep 17 00:00:00 2001 From: David Skoland Date: Tue, 10 Feb 2026 23:38:17 +0100 Subject: Refactor playerlist to state-driven rendering with renderPlayerList handlePR and handlePU now only update client.playerlist state, and renderPlayerList handles all DOM rendering from that state. Co-Authored-By: Claude Opus 4.6 --- webAO/dom/areaClick.ts | 10 --------- webAO/dom/renderPlayerList.ts | 50 ++++++++++++++++++++++++++++++++++++++++++ webAO/dom/updatePlayerAreas.ts | 24 -------------------- 3 files changed, 50 insertions(+), 34 deletions(-) create mode 100644 webAO/dom/renderPlayerList.ts delete mode 100644 webAO/dom/updatePlayerAreas.ts (limited to 'webAO/dom') diff --git a/webAO/dom/areaClick.ts b/webAO/dom/areaClick.ts index 19953a5..f7b177e 100644 --- a/webAO/dom/areaClick.ts +++ b/webAO/dom/areaClick.ts @@ -1,6 +1,4 @@ import { client } from "../client"; -import { updatePlayerAreas } from "./updatePlayerAreas"; -import { ensureCharIni } from "../client/handleCharacterInfo"; /** * Triggered when an item on the area list is clicked. * @param {HTMLElement} el @@ -14,13 +12,5 @@ export function area_click(el: HTMLElement) { areaHr.textContent = `switched to ${el.textContent}`; document.getElementById("client_log")!.appendChild(areaHr); client.area = Number(el.id.substring(4)); - updatePlayerAreas(client.area); - - // Prefetch char.ini for all characters present in the new area - for (const player of client.players.values()) { - if (player.area === client.area && player.charId >= 0) { - ensureCharIni(player.charId); - } - } } window.area_click = area_click; diff --git a/webAO/dom/renderPlayerList.ts b/webAO/dom/renderPlayerList.ts new file mode 100644 index 0000000..43dab64 --- /dev/null +++ b/webAO/dom/renderPlayerList.ts @@ -0,0 +1,50 @@ +import { client } from "../client"; +import { AO_HOST } from "../client/aoHost"; + +export function renderPlayerList() { + const list = document.getElementById("client_playerlist") as HTMLTableElement; + list.innerHTML = ""; + + for (const [playerID, player] of client.playerlist) { + const playerRow = list.insertRow(); + playerRow.id = `client_playerlist_entry${playerID}`; + + const imgCell = playerRow.insertCell(0); + imgCell.style.width = "64px"; + const img = document.createElement("img"); + if (player.charId >= 0) { + const char = client.chars[player.charId]; + if (char) { + const iconExt = client.charicon_extensions[0] || ".png"; + img.src = `${AO_HOST}characters/${encodeURI(char.name.toLowerCase())}/char_icon${iconExt}`; + img.alt = char.name; + img.title = char.name; + } + } + imgCell.appendChild(img); + + const charNameCell = playerRow.insertCell(1); + charNameCell.textContent = + player.charId >= 0 ? `[${playerID}] ${player.charName}` : ""; + + const showNameCell = playerRow.insertCell(2); + showNameCell.textContent = player.showName; + + const oocNameCell = playerRow.insertCell(3); + oocNameCell.textContent = player.name; + + const kickCell = playerRow.insertCell(4); + kickCell.style.width = "64px"; + const kick = document.createElement("button"); + kick.innerText = "Kick"; + kick.onclick = () => window.kickPlayer(playerID); + kickCell.appendChild(kick); + + const banCell = playerRow.insertCell(5); + banCell.style.width = "64px"; + const ban = document.createElement("button"); + ban.innerText = "Ban"; + ban.onclick = () => window.banPlayer(playerID); + banCell.appendChild(ban); + } +} diff --git a/webAO/dom/updatePlayerAreas.ts b/webAO/dom/updatePlayerAreas.ts deleted file mode 100644 index 99eccf1..0000000 --- a/webAO/dom/updatePlayerAreas.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { client } from "../client"; -import { area_click } from "./areaClick"; -/** - * Triggered when someone switches areas - * @param {Number} ownarea - */ -export function updatePlayerAreas(ownarea: number) { - for (let i = 0; i < client.areas.length; i++) { - if (i === ownarea) - for (let classelement of Array.from( - document.getElementsByClassName( - `area${i}`, - ) as HTMLCollectionOf, - )) - classelement.style.display = ""; - else - for (let classelement of Array.from( - document.getElementsByClassName( - `area${i}`, - ) as HTMLCollectionOf, - )) - classelement.style.display = "none"; - } -} -- cgit From 9993c378613b20b6f6f74b324c22c3bfda4c71fc Mon Sep 17 00:00:00 2001 From: David Skoland Date: Tue, 10 Feb 2026 23:59:48 +0100 Subject: Use charName directly for playerlist rendering and add table styling Render char icons and names from the character name string (PU type 1) instead of gating on charId lookup. Add header row and row separators to the playerlist table. Co-Authored-By: Claude Opus 4.6 --- webAO/dom/renderPlayerList.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'webAO/dom') diff --git a/webAO/dom/renderPlayerList.ts b/webAO/dom/renderPlayerList.ts index 43dab64..d0f08c6 100644 --- a/webAO/dom/renderPlayerList.ts +++ b/webAO/dom/renderPlayerList.ts @@ -5,27 +5,32 @@ export function renderPlayerList() { const list = document.getElementById("client_playerlist") as HTMLTableElement; list.innerHTML = ""; + const header = list.createTHead().insertRow(); + for (const label of ["Icon", "Character", "Showname", "OOC Name"]) { + const th = document.createElement("th"); + th.textContent = label; + header.appendChild(th); + } + + const body = list.createTBody(); for (const [playerID, player] of client.playerlist) { - const playerRow = list.insertRow(); + const playerRow = body.insertRow(); playerRow.id = `client_playerlist_entry${playerID}`; const imgCell = playerRow.insertCell(0); imgCell.style.width = "64px"; const img = document.createElement("img"); - if (player.charId >= 0) { - const char = client.chars[player.charId]; - if (char) { - const iconExt = client.charicon_extensions[0] || ".png"; - img.src = `${AO_HOST}characters/${encodeURI(char.name.toLowerCase())}/char_icon${iconExt}`; - img.alt = char.name; - img.title = char.name; - } + if (player.charName) { + const iconExt = client.charicon_extensions[0] || ".png"; + img.src = `${AO_HOST}characters/${encodeURI(player.charName.toLowerCase())}/char_icon${iconExt}`; + img.alt = player.charName; + img.title = player.charName; } imgCell.appendChild(img); const charNameCell = playerRow.insertCell(1); charNameCell.textContent = - player.charId >= 0 ? `[${playerID}] ${player.charName}` : ""; + player.charName ? `[${playerID}] ${player.charName}` : ""; const showNameCell = playerRow.insertCell(2); showNameCell.textContent = player.showName; -- cgit From 6314a7e61ad85aaf9313ed2947853e8e1d2aea33 Mon Sep 17 00:00:00 2001 From: David Skoland Date: Wed, 11 Feb 2026 00:05:04 +0100 Subject: Add area column to playerlist Co-Authored-By: Claude Opus 4.6 --- webAO/dom/renderPlayerList.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'webAO/dom') diff --git a/webAO/dom/renderPlayerList.ts b/webAO/dom/renderPlayerList.ts index d0f08c6..a78851b 100644 --- a/webAO/dom/renderPlayerList.ts +++ b/webAO/dom/renderPlayerList.ts @@ -6,7 +6,7 @@ export function renderPlayerList() { list.innerHTML = ""; const header = list.createTHead().insertRow(); - for (const label of ["Icon", "Character", "Showname", "OOC Name"]) { + for (const label of ["Icon", "Character", "Showname", "OOC Name", "Area"]) { const th = document.createElement("th"); th.textContent = label; header.appendChild(th); @@ -38,14 +38,17 @@ export function renderPlayerList() { const oocNameCell = playerRow.insertCell(3); oocNameCell.textContent = player.name; - const kickCell = playerRow.insertCell(4); + const areaCell = playerRow.insertCell(4); + areaCell.textContent = String(player.area); + + const kickCell = playerRow.insertCell(5); kickCell.style.width = "64px"; const kick = document.createElement("button"); kick.innerText = "Kick"; kick.onclick = () => window.kickPlayer(playerID); kickCell.appendChild(kick); - const banCell = playerRow.insertCell(5); + const banCell = playerRow.insertCell(6); banCell.style.width = "64px"; const ban = document.createElement("button"); ban.innerText = "Ban"; -- cgit From c380112d5f29b68bfa301527405fdf372835900e Mon Sep 17 00:00:00 2001 From: David Skoland Date: Wed, 11 Feb 2026 00:10:05 +0100 Subject: Filter playerlist by area and remove Area column Hide players not in the client's current area. Re-render playerlist on area switch. Remove the now-redundant Area column. Co-Authored-By: Claude Opus 4.6 --- webAO/dom/areaClick.ts | 2 ++ webAO/dom/renderPlayerList.ts | 10 ++++------ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'webAO/dom') diff --git a/webAO/dom/areaClick.ts b/webAO/dom/areaClick.ts index f7b177e..27682c7 100644 --- a/webAO/dom/areaClick.ts +++ b/webAO/dom/areaClick.ts @@ -1,4 +1,5 @@ import { client } from "../client"; +import { renderPlayerList } from "./renderPlayerList"; /** * Triggered when an item on the area list is clicked. * @param {HTMLElement} el @@ -12,5 +13,6 @@ export function area_click(el: HTMLElement) { areaHr.textContent = `switched to ${el.textContent}`; document.getElementById("client_log")!.appendChild(areaHr); client.area = Number(el.id.substring(4)); + renderPlayerList(); } window.area_click = area_click; diff --git a/webAO/dom/renderPlayerList.ts b/webAO/dom/renderPlayerList.ts index a78851b..a382194 100644 --- a/webAO/dom/renderPlayerList.ts +++ b/webAO/dom/renderPlayerList.ts @@ -6,7 +6,7 @@ export function renderPlayerList() { list.innerHTML = ""; const header = list.createTHead().insertRow(); - for (const label of ["Icon", "Character", "Showname", "OOC Name", "Area"]) { + for (const label of ["Icon", "Character", "Showname", "OOC Name"]) { const th = document.createElement("th"); th.textContent = label; header.appendChild(th); @@ -16,6 +16,7 @@ export function renderPlayerList() { for (const [playerID, player] of client.playerlist) { const playerRow = body.insertRow(); playerRow.id = `client_playerlist_entry${playerID}`; + playerRow.style.display = player.area === client.area ? "" : "none"; const imgCell = playerRow.insertCell(0); imgCell.style.width = "64px"; @@ -38,17 +39,14 @@ export function renderPlayerList() { const oocNameCell = playerRow.insertCell(3); oocNameCell.textContent = player.name; - const areaCell = playerRow.insertCell(4); - areaCell.textContent = String(player.area); - - const kickCell = playerRow.insertCell(5); + const kickCell = playerRow.insertCell(4); kickCell.style.width = "64px"; const kick = document.createElement("button"); kick.innerText = "Kick"; kick.onclick = () => window.kickPlayer(playerID); kickCell.appendChild(kick); - const banCell = playerRow.insertCell(6); + const banCell = playerRow.insertCell(5); banCell.style.width = "64px"; const ban = document.createElement("button"); ban.innerText = "Ban"; -- cgit From 5bb35a981e2f35df7fbf126faa8655ee3b3ca142 Mon Sep 17 00:00:00 2001 From: David Skoland Date: Wed, 11 Feb 2026 00:12:51 +0100 Subject: Clamp playerlist char icons to 60x60 pixels Co-Authored-By: Claude Opus 4.6 --- webAO/dom/renderPlayerList.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'webAO/dom') diff --git a/webAO/dom/renderPlayerList.ts b/webAO/dom/renderPlayerList.ts index a382194..48354ae 100644 --- a/webAO/dom/renderPlayerList.ts +++ b/webAO/dom/renderPlayerList.ts @@ -21,6 +21,8 @@ export function renderPlayerList() { const imgCell = playerRow.insertCell(0); imgCell.style.width = "64px"; const img = document.createElement("img"); + img.style.maxWidth = "60px"; + img.style.maxHeight = "60px"; if (player.charName) { const iconExt = client.charicon_extensions[0] || ".png"; img.src = `${AO_HOST}characters/${encodeURI(player.charName.toLowerCase())}/char_icon${iconExt}`; -- cgit