Online service and W4 ID

The website you are reading has an online service behind it. It holds W4 ID accounts, the list of games, and a store of shared maps and assets. It never runs a match: matches run in the engine, or on a dedicated game server that lists itself with the service. The engine and the website use the same service.

This page describes what exists today. It is an alpha: the service is for testing, with test accounts.

W4 ID

A W4 ID is an email, a username and a tag. In the game it shows as Username#TAG, for example Rowan#Thistle. Sign up on the Account page with an email, a username and a password. The tag is picked for you.

  • Passwords are stored as argon2id hashes. Nobody can read them, not even the person who runs the service.
  • The website keeps you signed in with a cookie. The engine uses a game token made on the Settings page. You can keep several, one per computer, each with a label, an optional expiry and what it may do (play, upload). A token is shown once; revoke one without touching the others.
  • Rejoin your game. If you hold a seat in an unfinished match on a dedicated server (closing the engine never gives the seat up), the W4 ID page shows a Rejoin your game card with the map, how long ago it started, the server address and a w4://join/... button. The card is not there when you have no such game.
  • There is no mail server yet, so a new account is confirmed at once. Later, a link by mail will confirm the address, and the same mail will reset a forgotten password.
  • Test accounts: an administrator can add accounts that sign in with a plain name and a password and have the tag Admin. They exist only for testing and will be removed before the service is public.

Games

The Lobbies page lists the games people are hosting right now: the title, the map, the host, how many players are in, and how long ago it started. A listing ends by itself 45 seconds after its host stops reporting, so a crashed game does not stay on the list.

Some listings are marked mock: they are made up so the page is not empty while nobody is hosting. Joining a mock game only shows the link w4://join/...; it does not open the engine.

A dedicated game server also lists its game here, with the address to connect to. When the service tells how many players hold a seat, the card shows seated, connected and, for the difference, reconnecting: a player whose connection dropped still owns their seat and can come back. With no such counts, nothing extra is shown. A game on a dedicated server also has a Watch link (w4://watch/<id>): spectators have no seat, see the game a few seconds behind, and the engine's link handler opens it. You can also watch in the browser: In browser opens Watch a game, a live top-down view (dots for units, squares for buildings, health bars, player colours) fed by the server's WebSocket gateway.

The Server status page shows whether the service is up, its version, the open games, the official servers (with their address, players and whether they use TLS) and how many people are in the chat, and a chart of finished games per day for two weeks. A third chart shows the official server's last 24 hours (players connected, and how many simulation steps ran over their 33 ms budget) from the server's per-minute /series. It is read on the site's own host from the loopback metrics port (AQUA4_SERIES_URL); where that is not set, the card says the series is not connected. The online service's /v1/metrics stays administrator-only.

Servers and ping

The Servers page lists the official servers and measures, in your browser, how far away each one is: six timed requests, the first thrown away, and the median is shown with the best time and the jitter (the spread). A word goes with the colour (Great under 60 ms, Good under 120, Playable under 200, Laggy above). A browser cannot open the game's own port, so this is the time to the online service on the same host; the game shows its own ping in the lobby. Under the list, dedicated game servers that list a game right now appear.

Players, leaderboard and moderation

  • Profiles. A player's name in the history is a link to /p/Name%23Tag: games, wins, losses, win rate, form of the last twenty games, totals, favourite maps and recent games. It is told from the latest 100 finished games, so it shows nothing the history does not. Typing /p/Name#Tag works too.
  • Leaderboard. Glicko-2 skill ratings per mode (1v1 to 4v4), with the ± showing how sure the rating is. A player appears after 5 placement games. A mode with no rated player yet falls back to wins counted in the latest 100 finished games, and the page says so.
  • Settings. Game tokens (make, list with last use, revoke each), every signed-in browser with a sign-out button each and "sign out all others", change password (other browsers are signed out), and change email (confirmed by the link mailed to the new address).
  • Moderation (administrators only, /admin/moderation). The queue of reports with Open, Handled and Dismissed tabs; for each report the reported chat line or file, a note for the log and the actions Dismiss, Hide, Mute (10 minutes to a week) and Ban (asks again first); and the log of every action.

Every online page has the same three honest states: a message with what to do when there is nothing yet, a red message with a Reload button when the service cannot be reached, and a shimmering placeholder while more is loading. The command palette (Ctrl or Cmd + K) opens every one of them.

Maps and assets

The Maps and Assets pages share files: maps, models, textures, shaders, sounds, icons and scripts. Every file has a name, a version, a description and a checksum (SHA-256). Two uploads with the same content are stored once.

  • A file is up to 100 MB, and an account may store up to 2 GB.
  • You confirm that you made the file or may share it, and that it holds no files from Blizzard's games.
  • Downloads can be paused and resumed.
  • Every file has a small picture in the list. A texture or icon that is a PNG, JPEG or WebP (up to 1 MB) shows itself; any other file shows a coloured card with its kind and extension. The picture is made by the service (GET /v1/items/{id}/thumb).

For the engine: the HTTP interface

The engine talks to the service with HTTP and JSON. Paths start with /v1/. There are two doors to the same service (the plain port 5179 listens on the server's own address only):

Address For
https://nebel.sh:5180/v1/... The engine, encrypted. The engine uses this one by default.
https://w4.nebel.sh/api/... The website. The same calls, with /api/ in place of /v1/ (for example /api/games).
Call Does
POST /v1/login {"who", "password"} Signs in. who is an email, Name#TAG, or an administrator's plain name.
GET /v1/items/{id}/thumb A thumbnail: the file itself when it is a small PNG, JPEG or WebP texture or icon (checked by its first bytes), else a generated SVG placeholder. Cacheable for a day.
POST /v1/me/game-token Makes a game token (needs the sign-in). Send it afterwards as Authorization: Bearer <token>.
GET /v1/me Who you are.
GET /v1/me/active-game The match a dedicated server has you seated in (id, address, map_name, started, slot), or null. Used for "Rejoin".
GET /v1/me/tokens, POST /v1/me/tokens {"label", "ttl_days"?, "scopes"?}, DELETE /v1/me/tokens/{id} Several labelled game tokens, each with an optional expiry and scopes (play, upload, admin). The secret is shown once.
GET /v1/me/sessions, DELETE /v1/me/sessions/{id} The browsers signed in, and signing one out.
POST /v1/me/password {"password", "new_password"}, POST /v1/me/email {"password", "email"} Change the password or the email (the new address must be confirmed).
GET /v1/games The list of games.
POST /v1/games {"title", "map_name", "map_hash", "max_players"} Starts a listing. Repeat POST /v1/games/{id}/heartbeat about every 15 seconds.
GET /v1/games?map=&phase=&region=&spectators=&has_slots= The list, filtered: map is part of the map name, phase is lobby, loading, playing or over, region as the server registered it, spectators=true only matches that can be watched, has_slots=true only games with a free seat, has_password=true or false only games that ask for a password or not. A listing that asks for one carries "password": true (the word itself is never listed or stored). A dedicated server's listing also carries phase, spectators and region.
POST /v1/games/{id}/join Join. The answer has the address of a dedicated server, if the game has one.
POST /v1/games/{id}/leave Leave.
GET /v1/health {"status", "version"}: is the service up. Shown on the Server status page.
GET /v1/chat?limit=50 The newest chat messages, oldest first: {"messages": [{"seq", "from", "text", "at", "guest"}], "last", "online"}.
GET /v1/chat?before=<seq>&limit=50 Older messages: {"messages", "first", "has_more"}. See Global chat.
GET /v1/chat?after=<seq>&wait=20 Long poll: answers at once with lines newer than seq, else waits up to 25 s for one. The website's chat page uses it to update by itself.
GET /v1/chat/log?from=<unix>&to=<unix>&format=text A day's chat (or any span) as a download.
GET /v1/chat/stream[?after=<seq>] The room as server-sent events, one event per line (id is the line number). A browser EventSource reconnects by itself. Use it instead of polling.
GET /v1/chat/search?q= Chat lines containing the text, newest first.
GET /v1/mentions?after=<seq> Lines that name you as @Name#TAG.
POST /v1/whispers {"to": "Name#TAG", "text"} / GET /v1/whispers?after=<seq> Private messages by W4 ID: send, and read what you sent and received.
POST /v1/invites {"to", "game"} Invites a player to a game (a whisper of kind invite holding the join link).
GET/POST /v1/blocks, DELETE /v1/blocks/{account} Your block list. A blocked player's whispers, invites and friend requests are dropped without telling them.
GET/POST /v1/friends, POST /v1/friends/{account}/accept, DELETE /v1/friends/{account} Friends and requests. The list shows who is online and who is in a game.
POST/DELETE /v1/items/{id}/like, GET /v1/items/{id}/likes, GET /v1/items/{id}/versions Likes on a shared file, and every version of the same name from the same author. Program files and blocked files are refused at upload.
GET/PATCH /v1/me/profile, GET /v1/avatars, GET /v1/players/{Name#TAG} Your avatar (a preset), your short text and privacy switches; the public page of a player with ratings and totals (totals are left out when they hid their history).
POST /v1/me/totp/setup, /enable, /disable; code in POST /v1/login Optional two-factor sign-in with any authenticator app.
GET /v1/me/export Everything the service holds about you, as one JSON file.
GET /v1/emotes, GET /v1/announcements The emote shortcodes, and the last five announcements from the administrators.
GET /v1/history?limit=20 Finished games, newest first. Filters player, map, and before=<id> to go further back. See Game history.
GET /v1/history/{id} One finished game with its players and stats.
GET /v1/history/{id}/chat The chat of that game.
GET /v1/items?kind=map List shared files of a kind.
GET /v1/items/{id}/download Download a file. Supports Range.
GET /v1/files/{sha256} Download a file by its checksum, which is how a map is fetched for a game.
GET /v1/me (ratings) Also holds ratings: per mode (1v1 to 4v4) the rating, deviation, games, wins, losses and whether it is still a placement rating (fewer than 5 games).
GET /v1/leaderboard?mode=1v1&limit=25&before=<rank> The best accounts of a mode, rank 1 first (accounts past their 5 placement games). before is the rank of the last row you already have.
POST /v1/queue {"mode", "maps": []} Join the matchmaking queue for 1v1, 2v2, 3v3 or 4v4; maps is an optional pool of map hashes. Needs a confirmed account of some age.
GET /v1/queue Your place: idle, queued (position, estimated wait, current rating window), matched (teams and map) or ready (with the address to connect to). Poll it about every 5 seconds; it is also the heartbeat that keeps you in the queue.
DELETE /v1/queue Leave. Leaving a match that was already made, before a server took it, is a dodge and costs a waiting time.

Errors come back as {"error": "..."} with a fitting status code.

Not built yet

Signing in to the engine from the start screen, hosting and joining from it, accounts with real mail, and moderation tools. See the start screen for what the engine does today.

What you see when a game goes wrong

Online states are shown as a strip on the game or the Room, not as a guess:

  • Connection lost and Reconnecting, with the game paused for you until the link is back.
  • Full, when the game has no free seat; Old version, when your W4 is not the host's version.
  • Kicked, and Host left.

A strip that has no real connection behind it is marked mock. Nothing here invents numbers: when the engine has no measurement, the value is hidden.

The connection meter

In an online game a small plate on the HUD shows your round-trip time to the server and, when packets are lost, how many. On the Room's player cards each player shows a ping chip, and -- ms while it is not measured. While a player is typing in chat, a "typing" line shows under the chat. The meter is part of the interface; the numbers come from the network client as they become available.