diff options
| author | David Skoland <davidskoland@gmail.com> | 2026-04-01 13:59:13 +0200 |
|---|---|---|
| committer | David Skoland <davidskoland@gmail.com> | 2026-04-01 13:59:13 +0200 |
| commit | 10b413c0f0a31bc9476eed86812b6bb90f82caed (patch) | |
| tree | 94ac6676fcad76dc76e901e2889a30f7ba611d8d /webAO/viewport/viewport.ts | |
| parent | d6163543f483c35737da52b7e307cf6f65828f82 (diff) | |
Add asset preloading system for IC message rendering
Fix rendering race conditions where character sprites, pre-animations,
and paired character assets were displayed before being downloaded.
All assets referenced in an MS packet are now resolved and preloaded
into the browser cache before the animation timeline starts.
- Add unified assetCache module with session-wide promise caching
- Add preloadMessageAssets orchestrator for parallel asset resolution
- Cache fileExists HEAD requests so missing files aren't re-probed
- Preload all SFX (emote, shout, realization, stab) alongside sprites
- Use synchronous setEmoteFromUrl at all render transition points
- Graceful fallback to legacy setEmote if preloading times out
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'webAO/viewport/viewport.ts')
| -rw-r--r-- | webAO/viewport/viewport.ts | 90 |
1 files changed, 42 insertions, 48 deletions
diff --git a/webAO/viewport/viewport.ts b/webAO/viewport/viewport.ts index aea43f1..b37ff06 100644 --- a/webAO/viewport/viewport.ts +++ b/webAO/viewport/viewport.ts @@ -3,6 +3,7 @@ import { client, delay } from "../client"; import { UPDATE_INTERVAL } from "../client"; import setEmote from "../client/setEmote"; +import setEmoteFromUrl from "../client/setEmoteFromUrl"; import { AO_HOST } from "../client/aoHost"; import { Viewport } from "./interfaces/Viewport"; import { createBlipsChannels } from "./utils/createBlipChannels"; @@ -162,7 +163,9 @@ const viewport = (): Viewport => { const nextCharacterElement = chatmsg.parsed[textnow.length]; const flash = async () => { const effectlayer = document.getElementById("client_fg"); - playSFX(`${AO_HOST}sounds/general/sfx-realization.opus`, false); + const realizationUrl = chatmsg.preloadedAssets?.realizationSfxUrl + ?? `${AO_HOST}sounds/general/sfx-realization.opus`; + playSFX(realizationUrl, false); effectlayer.style.animation = "flash 0.4s 1"; await delay(400); effectlayer.style.removeProperty("animation"); @@ -170,7 +173,9 @@ const viewport = (): Viewport => { const shake = async () => { const gamewindow = document.getElementById("client_gamewindow"); - playSFX(`${AO_HOST}sounds/general/sfx-stab.opus`, false); + const stabUrl = chatmsg.preloadedAssets?.stabSfxUrl + ?? `${AO_HOST}sounds/general/sfx-stab.opus`; + playSFX(stabUrl, false); gamewindow.style.animation = "shake 0.2s 1"; await delay(200); gamewindow.style.removeProperty("animation"); @@ -247,15 +252,11 @@ const viewport = (): Viewport => { if (textnow === chatmsg.content) { animating = false; - setEmote( - AO_HOST, - client, - charName, - charEmote, - "(a)", - false, - chatmsg.side, - ); + if (chatmsg.preloadedAssets) { + setEmoteFromUrl(chatmsg.preloadedAssets.idleUrl, false, chatmsg.side); + } else { + setEmote(AO_HOST, client, charName, charEmote, "(a)", false, chatmsg.side); + } charLayers.style.opacity = "1"; waitingBox.style.opacity = "1"; clearTimeout(updater); @@ -332,12 +333,16 @@ const viewport = (): Viewport => { // Effect stuff if (chatmsg.screenshake === 1) { // Shake screen - playSFX(`${AO_HOST}sounds/general/sfx-stab.opus`, false); + const stabUrl = chatmsg.preloadedAssets?.stabSfxUrl + ?? `${AO_HOST}sounds/general/sfx-stab.opus`; + playSFX(stabUrl, false); gamewindow.style.animation = "shake 0.2s 1"; } if (chatmsg.flash === 1) { // Flash screen - playSFX(`${AO_HOST}sounds/general/sfx-realization.opus`, false); + const realizationUrl = chatmsg.preloadedAssets?.realizationSfxUrl + ?? `${AO_HOST}sounds/general/sfx-realization.opus`; + playSFX(realizationUrl, false); effectlayer.style.animation = "flash 0.4s 1"; } @@ -345,8 +350,12 @@ const viewport = (): Viewport => { if (chatmsg.preanimdelay > 0) { shoutSprite.style.display = "none"; shoutSprite.style.animation = ""; - const preanim = chatmsg.preanim.toLowerCase(); - setEmote(AO_HOST, client, charName, preanim, "", false, chatmsg.side); + if (chatmsg.preloadedAssets) { + setEmoteFromUrl(chatmsg.preloadedAssets.preanimUrl, false, chatmsg.side); + } else { + const preanim = chatmsg.preanim.toLowerCase(); + setEmote(AO_HOST, client, charName, preanim, "", false, chatmsg.side); + } } if (chatmsg.other_name) { @@ -432,41 +441,29 @@ const viewport = (): Viewport => { } if (chatmsg.other_name) { - setEmote( - AO_HOST, - client, - pairName, - pairEmote, - "(a)", - true, - chatmsg.side, - ); + if (chatmsg.preloadedAssets) { + setEmoteFromUrl(chatmsg.preloadedAssets.pairIdleUrl, true, chatmsg.side); + } else { + setEmote(AO_HOST, client, pairName, pairEmote, "(a)", true, chatmsg.side); + } pairLayers.style.opacity = "1"; } else { pairLayers.style.opacity = "0"; } - setEmote( - AO_HOST, - client, - charName, - charEmote, - "(b)", - false, - chatmsg.side, - ); + if (chatmsg.preloadedAssets) { + setEmoteFromUrl(chatmsg.preloadedAssets.talkingUrl, false, chatmsg.side); + } else { + setEmote(AO_HOST, client, charName, charEmote, "(b)", false, chatmsg.side); + } charLayers.style.opacity = "1"; if (textnow === chatmsg.content) { - setEmote( - AO_HOST, - client, - charName, - charEmote, - "(a)", - false, - chatmsg.side, - ); + if (chatmsg.preloadedAssets) { + setEmoteFromUrl(chatmsg.preloadedAssets.idleUrl, false, chatmsg.side); + } else { + setEmote(AO_HOST, client, charName, charEmote, "(a)", false, chatmsg.side); + } charLayers.style.opacity = "1"; waitingBox.style.opacity = "1"; animating = false; @@ -491,12 +488,9 @@ const viewport = (): Viewport => { chatmsg.sound !== undefined && (chatmsg.type == 1 || chatmsg.type == 2 || chatmsg.type == 6) ) { - playSFX( - `${AO_HOST}sounds/general/${encodeURI( - chatmsg.sound.toLowerCase(), - )}.opus`, - chatmsg.looping_sfx, - ); + const sfxUrl = chatmsg.preloadedAssets?.emoteSfxUrl + ?? `${AO_HOST}sounds/general/${encodeURI(chatmsg.sound.toLowerCase())}.opus`; + playSFX(sfxUrl, chatmsg.looping_sfx); } } if (textnow === chatmsg.content) { |
