| Age | Commit message (Collapse) | Author |
|
Bring in the subprotocol (the same as what's used on the desktop client
for public-key authentication) to carry the relevant messages:
- AuthRequest: first step in the flow, the client sends it to signal the
intent to authenticate to the server.
- AssertCredential and AssertionFinish: server's challenge and client's
response, respectively, to finalize the flow.
- RegisterCredential and RegistrationFinish: same structure as the
above. Unlike the simple public-key auth with an out-of-band setup,
passkeys require user interaction to register. User must be
authorized.
Validate all relevant checks on the API side, and hand the data over to
the server for it to verify attestations and assertions.
Because it's a primary auth mechanism (not a second factor), require
user verification.
As we don't use any other method on web, add a passkey button as the
only sign-in interface. Passkeys are discoverable, we don't even need a
username.
|
|
The hCaptcha integration has been abandoned for a while. It added yet
another questionable third-party API (which also set a Cloudflare
cookie), and its effectiveness is unclear considering its client-side
nature.
A custom CAPTCHA implementation (such as PoW challenge) is an
interesting prospect, but it'll require proper server-side support.
|
|
Cookies's use case is to store persistent data and send it to the server
in subsequent requests, such as to remember logged-in sessions. WebAO is
using them to store site settings like ad-hoc hash tables that require
parsing and serialization.
As a nasty side-effect of how cookies work, clients send all their
settings every time they connect to the server. Server has absolutely no
use for them, but each client sends them anyway, which is an
uncalled-for privacy leak.
Remove this mechanism entirely, switch to localStorage which serves
exactly the purpose of per-origin store with data that never leaves the
browser.
|
|
Fix relevant breaking changes.
|
|
Delete reference to the AO2 Discord server so users don't accidentally
complain there if I break something here.
|
|
It only caused chat_config.ini to be requested a lot.
|
|
These should obey the extension data, but instead they're hardcoded
to predefined PNG and GIF files.
|
|
Blips aren't handled correctly every time, resulting in a lot of 404
URLs and invalid blips.
|
|
|
|
|
|
|
|
Production builds are done via `bun run build` now rather than `bun run
deploy`. Webpack doesn't recognize even recognize flag `-p`, and WebAO
doesn't provide cross-env.
|
|
Historically, MC packet ended up in a ridiculous spot. It had this
single structure:
MC#something#cid#%
It used to change music track to `something`, and the character ID `cid`
was used in clientside muting (blindly trusted, by the way). Then,
this packet was expanded to mean area change as well, so the same
generic structure carried two completely different meanings.
How does one differentiate the two? Whether the client tried to move to
an area `something`, or played a music track called `something`?
The solution was to assume that having ".extension" at the end magically
implied that it was a name of a music file, check the string `something`
within the MC packet, and pray that you guessed correctly. So,
understanding the protocol message required penetrating into one of its
data fields and ambiguously inferring what the whole message even meant.
Modern AO gives us a more logical solution. Not as good as having two
separate packets for two unrelated actions, but we can at least discern
the area and music change directly from the framing.
Area change uses the same two-field structure: MC#area#cid#%
Music change, however, has acquired two additional fields:
MC#music#cid#showname#flags#%
We consider four-field MC to be music change, and two-field MC to be
area change, resolving the ambiguity and eliminating odd constraints
on area and music names.
WebAO still uses the old logic and sends two-field MC packets for both
cases. CSDWASASH server, as a result, thinks that web users try to
change areas when they play music. This commit fixes this behavior and
adds special sendAreaChange instead of using sendMusicChange for both.
The flags are hardcoded to 0 because WebAO can't set fade-in, fade-out,
or position sync, and it ignores the server flags.
|
|
Sometimes, WebP icons won't load despite extensions.json clearly
defining it as the only extension used for all image data.
I suspect there's a race condition between fetching extensions.json,
parsing it into client, and checking what extension we should use to get
character icons during loading. Sometimes it correctly loads images,
sometimes it falls back and starts requesting PNG instead.
I couldn't precisely identify where it happens and what's the root
cause. As a workaround, this commit instead makes WebP the
first-priority extension and a fallback.
|
|
If there's no sound section in the INI, that means no sounds are
intended for the character, no need to warn us for that.
|
|
Note: this is an old commit that was relevant prior to 10b413c
("Add asset preloading system for IC message rendering")
WebAO assumes every character has an idle and talking animations which
reside in fixed "(a)" and "(b)" subdirectories. This assumption, of
course, breaks many valid characters that don't use these magical
directory names, as it prepends them to every emote URL.
This commit removes this fossil. Animation, or any character structure
at all, shouldn't depend on magical subdirectory names, and instead
defined explicitly.
|
|
The following packets are unused and have no reason to have handlers:
- CI: character information, unused and handled by SC.
- EI: evidence information, unused and handled by LE.
- EM: area and music information, handled by SM, and obsoleted by
dedicated FA and FM.
- MM: clientside toggle of whether a client is allowed to change music.
The handler doesn't even do anything beyond acknowledging its
existence.
- RMC: plays a music track with an offset. Unused in practice, not
present in AO2 Client.
- ackMS: sent by the server to acknowledge an IC message, a relic from
AO1.
|
|
In an IC message, x and y offsets are separated by an ampersand, which
is unfortunately a conventional separator within packet fields. So, it
looks like `25&10` for x = 25 and y = 10. A consequence of that is you
have to substitute '&' if you actually want to send it. AO does it by
substituting it to `<and>` and back.
The MS handler instead expected it to never be decoded, and instead
assumed `25<and>10`. By the time the MS packet reaches the handler, the
token has already been decoded into '&', however, thus breaking offsets.
|
|
INI parser normalized everything in INI (except for "showname" value)
and recomputed regex match twice for parameters and sections.
|
|
For whatever reason, WebAO decides to normalize almost every string
component in URLs, packets, and INI files to lower case.
First, the glaring issue. In the URLs, this handling of paths is utterly
broken and corrupts data. By mangling characters, you change the
resource identity and break valid URLs. According to section 6.2.2.1 of
RFC 3986 (Case Normalization):
> When a URI uses components of the generic syntax, the component syntax
> equivalence rules always apply; namely, that the scheme and host are
> case-insensitive and therefore should be normalized to lowercase. For
> example, the URI <HTTP://www.EXAMPLE.com/> is equivalent to
> <http://www.example.com/>. The other generic syntax components are
> assumed to be case-sensitive unless specifically defined otherwise by
> the scheme (see Section 6.2.3)
Scheme and host _are_ case-insensitive. Path is _not_, so isn't
everything else. Section 6.2.3 doesn't define any normalization for the
path component in HTTP schemes. Thus, example.com/item and
example.com/Item are two different resources.
I can only think of idiotic conventions of a particular poorly designed
file system when it comes to this absurdity. There's no reason to drag
them around in our developments. For these systems, case doesn't matter
anyway, normalization is their job, not server hosts' who end up having
to either rewrite every URL request for every asset, or mangle their
asset directory and then rewrite almost every INI config (and spam
"showname=Name" everywhere because now your character directory has to
be "name").
So, instead of using absurd ad-hoc solutions to a broken implementation
such as forcing everything to lower case on the server side, this commit
attempts to fix the root issue and make URL handling conformant to
relevant standards.
Similar situation with strings within packets, although not as severe
in practice. Case must be preserved, otherwise it's corrupting data for
no reason. If a normalization is needed, it should be done at the call
site of whatever requires it (like a filtering function), not by the
parser.
As for the INI, it's opinionated. While the values absolutely must not
be normalized, a case can be made for keys and section names: why not
allow "Options", "options", or even "oPtiOnS"? It's more convenient, and
corresponds to the platform quirk of Windows (which Qt unfortunately
inherits in AO2 Client). I don't think there's a good reason to allow
such leniency in parsing, and removing superfluous normalization is a
better move: less data transformations, less ambiguity, more strictness.
In practice, INIs tend to be well-formed, and it's good discipline to
write them this way.
In several places, the case-folding does make sense: callwords,
OOC commands, CSS class names for areas, and character list filters.
These will behave weirdly and inconveniently without it. In most places,
however, it only causes unnecessary breakage.
|
|
CH is an application-level keepalive packet that clients periodically
send for two reasons:
1. It tells the server they're still connected, preventing timeouts.
2. By measuring latency between sending CH and receiving CHECK, a client
can display ping.
Keepalive is redundant because WebSocket can handle that via PING frames on a
transport layer. WebAO also completely ignores CHECK and sends CH every
five seconds, which is superfluous (AO2 Client sends it once every 45
seconds, in comparison).
Sending CH via `setInterval` was also problematic: browsers seem to
throttle it when the tab becomes inactive, preventing periodic pings and
leading to the server disconnecting inactive browser clients.
|
|
|
|
|
|
|
|
Fix IC rendering race conditions with asset preloading
|
|
Add reconnect UI
|
|
Hide character name in music log when showname is set
|
|
Fix speedlines bug
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
The early return guard `textnow === chatmsg.content` was firing on the
first tick for empty messages ("" === ""), short-circuiting the entire
animation timeline before the preanim or shout phases could run.
Now the guard also checks that both tick phases have completed before
allowing the early exit.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
|
|
|
Enable auto pick area and char
|
|
|
|
|
|
|
|
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>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Fix all TypeScript errors with Window interface declarations
|
|
|
|
|
|
- Redesign disconnect overlay as a full-screen modal with dark backdrop
- Add working Reconnect button that properly re-establishes WebSocket connection
- Add Disconnect button in Settings for testing
- Separate disconnect and ban/kick codepaths (no reconnect on ban)
- Log disconnect notice in IC log using hrtext style
- Refactor area list rendering from client state (renderAreaList)
- Extract appendICNotice for reusable IC log notices
- Clean up charselect: hide during loading, simplify toolbar layout
- Freshen loading screen and charselect styling
- Remove loading progress text updates (just show "Loading...")
- Guard against undefined client.chars and client.serv
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
|
|
|
Extend the global Window interface with custom properties assigned
at runtime (e.g. pickEmotion, resizeChatbox, area_click) to resolve
18 TS2339/TS2551 errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|