Web interface SDK: the JavaScript a web interface's pages call - the KcdMp object's functions, handlers and properties, the feeds of the game and the mock for working in a browser # Web interface SDK > Everything a page of a server's web interface may use - the KcdMp object every page gets, its properties, functions and handlers - each with its own page. A server's **web interface** is a set of pages - HTML, CSS and JavaScript - that players' games draw over the game: a menu, a shop, a login screen, a scoreboard, a new chat. Each page is a **frame** named in the interface's `ui.toml`, and the game gives every page one global object, **`KcdMp`**, before the page's own scripts run. It is how the page learns where it runs, hears the game mode and the client scripts, answers them, reads the live values the game keeps for it, and asks for the keyboard and the mouse cursor. Nothing else of the game is reachable from a page: what it may receive and send is what its frame's entry in `ui.toml` allows (`events_in`, `events_out`, `feeds`, `focus`). ```js KcdMp.onServer("shop.items", (value) => render(value.items)); // the game mode sent a "shop.items" event document.querySelector("#buy").onclick = () => KcdMp.emitServer("shop.buy", { item: "bread" }); ``` The page also runs in a plain browser while it is being written: `KcdMp.env.mock` is then `true`, every call answers on the spot and [`KcdMp.mock`](/js/web/properties/mock/) plays the game's part. The types of the whole object are in [`KcdMp-web.d.ts`](https://docs.kcd-mp.com/KcdMp-web.d.ts), for TypeScript and for editors that read it. The guide [Make a web interface](/guides/web-interface/) puts a first frame on the screen from start to finish. | Topic | | |---|---| | [The page](#the-page) | 9 properties, 1 function | | [Messages and events](#messages-and-events) | 4 functions, 2 handlers | | [Feeds](#feeds) | 1 property, 2 handlers | | [Focus and visibility](#focus-and-visibility) | 2 functions, 3 handlers | | [Drawing a component](#drawing-a-component) | 1 handler | | [The game's pictures and typefaces](#the-games-pictures-and-typefaces) | 1 function | | [Pages on surfaces in the world](#pages-on-surfaces-in-the-world) | 1 property, 1 handler | | [Logging and errors](#logging-and-errors) | 3 functions, 1 handler | | [Mock mode](#mock-mode) | 1 property, 8 functions | ## The page What a page knows about itself: its frame's name, the version of the page API, where it runs and how big its screen is - and `KcdMp.ready()`, the call that tells the game the page is listening. A page needs no set-up: `KcdMp` is there before the page's own scripts run. | Property | What it holds | |---|---| | [`KcdMp.frame`](/js/web/properties/frame/) | The name of this page's frame. | | [`KcdMp.api`](/js/web/properties/api/) | The version of the page API the game runs. | | [`KcdMp.contract`](/js/web/properties/contract/) | The contract this frame implements, when it draws one of the game's interface components. | | [`KcdMp.env`](/js/web/properties/env/) | What the page knows about where it runs. | | [`KcdMp.env.mock`](/js/web/properties/env-mock/) | Whether the page runs outside the game. | | [`KcdMp.env.screen`](/js/web/properties/env-screen/) | The size of the page and its display's pixel ratio. | | [`KcdMp.env.locale`](/js/web/properties/env-locale/) | The player's language, as a language tag. | | [`KcdMp.env.dev`](/js/web/properties/env-dev/) | Whether the player's game runs in developer mode. | | [`KcdMp.env.component`](/js/web/properties/env-component/) | The game's interface component this frame draws, if any. | | Function | What it does | |---|---| | [`KcdMp.ready`](/js/web/functions/ready/) | Tells the game the page is loaded and listening. | ### KcdMpScreen The size of the page and of the display it is drawn on. | Field | Type | | |---|---|---| | `w` | `number` | the page's width in CSS pixels | | `h` | `number` | the page's height in CSS pixels | | `scale` | `number` | the display's pixel ratio (`2` on a display that draws two device pixels per CSS pixel) | ## Messages and events A page talks to two others: the **client scripts** that run in the player's own game, and the **game mode** on the server. | The page wants to | With | The other side | |---|---|---| | hear a client script, or a message the mode sent to this frame | [`KcdMp.onMessage`](/js/web/handlers/onmessage/) | a client script's `SendWebMessage(frame, data)`; the mode's `SendPlayerWebMessage(pid, frame, data)` | | ask a client script something and wait for the answer | [`KcdMp.post`](/js/web/functions/post/) | the script's `OnWebEvent(frame, name, data)`, whose return value is the answer | | hear an event of the game mode | [`KcdMp.onServer`](/js/web/handlers/onserver/) | the mode's `SendClientEvent(pid, name, payload)` | | send the game mode an event | [`KcdMp.emitServer`](/js/web/functions/emitserver/) | the mode's `OnClientEvent(pid, name, payload, source)`, with the frame's name as `source` | | use an action of the interface component it draws | [`KcdMp.call`](/js/web/functions/call/) | the game itself | What a page may receive and send is decided by its frame's entry in `ui.toml`: `events_in` lists the event names it may hear and `events_out` the names it may send. Without them a frame may use the names that start with its own name and a dot (`shop.open` for a frame called `shop`). Names are 1 to 64 characters of `a-z 0-9 _ . : -`, and those that start with `kcdmp:` belong to the game itself and are never delivered or accepted. Everything a page sends is JSON - at most **16 KB** of it, nested at most 16 levels deep - and everything it receives is at most 64 KB. A page may make about **100 calls and send 64 KB a second**; more is refused with an error until the budget fills again. Sends to the game mode also share the player's budget of events with their client scripts: 30 events and 64 KB a second in all. | Function | What it does | |---|---| | [`KcdMp.post`](/js/web/functions/post/) | Asks a client script something and waits for its answer. | | [`KcdMp.emitServer`](/js/web/functions/emitserver/) | Sends the game mode an event. | | [`KcdMp.call`](/js/web/functions/call/) | Calls an action of the game's interface component this frame draws. | | [`KcdMp.openInBrowser`](/js/web/functions/openinbrowser/) | Opens a website in the player's own web browser, after the game asks them. | | Handler | When it fires | |---|---| | [`KcdMp.onMessage`](/js/web/handlers/onmessage/) | Registers a function that receives what a client script or the game mode sent this frame with a message. | | [`KcdMp.onServer`](/js/web/handlers/onserver/) | Registers a function that receives the game mode's events with a given name. | ## Feeds A **feed** is a live value the game keeps for a page and only reads to it: who the player is, whether the interface is on screen. A page subscribes to a feed and is called at once with the latest value and again at every change. A frame gets only the feeds its `ui.toml` entry lists (`feeds = ["session", "visibility"]`); subscribing to any other name is allowed and logs a warning, but no value ever arrives. Two feeds exist: `session`, the connection and the player, and `visibility`, whether the interface is drawn. A subscription before [`KcdMp.ready`](/js/web/functions/ready/) is fine: the latest values are delivered ahead of the queued messages when the page is ready. | Property | What it holds | |---|---| | [`KcdMp.feed`](/js/web/properties/feed/) | The feeds a page may read. | | Handler | When it fires | |---|---| | [`KcdMp.feed.session.subscribe`](/js/web/handlers/feed-session-subscribe/) | Registers a function that receives the connection and the player. | | [`KcdMp.feed.visibility.subscribe`](/js/web/handlers/feed-visibility-subscribe/) | Registers a function that is told when the interface is drawn or hidden. | ### KcdMpSessionFeed The connection and the player, as the `session` feed carries them. | Field | Type | | |---|---|---| | `server` | `string` | the server's name | | `address` | `string` | `host:port` as the player connected to it | | `level` | `string` | the level the server runs | | `playerId` | `number` | the player's id on the server - the game mode's `pid` | | `name` | `string` | the player's name | | `rtt` | `number` | the round-trip time to the server in milliseconds, `0` until it has been measured | ### KcdMpVisibility Whether the interface is drawn, and if not, why. | Field | Type | | |---|---|---| | `visible` | `boolean` | `true` while the interface is drawn over the game | | `reason` | `"loading" \| "menu" \| "game_screen" \| "panic" \| "hidden" \| ""` | why it is not drawn: `"loading"` (a level is loading), `"menu"` (a game menu is open), `"game_screen"` (one of the game's own screens is up), `"panic"` (the player switched the interface off), `"hidden"` (it is turned off), and `""` while it is drawn | ## Focus and visibility While a page is on screen the game keeps playing: keys and the mouse go to the game unless the page asks for them. A page asks with [`KcdMp.focus`](/js/web/functions/focus/) and gives them back with [`KcdMp.blur`](/js/web/functions/blur/); the game mode and the client scripts can hand a frame the focus as well. **The player has the last word.** The frame's `focus` setting in `ui.toml` decides what a request gets: - `"never"` - the frame gets no focus at all; - `"input"` (the default) - the cursor at once, the keyboard only within five seconds of the player's own click or key press in that frame, so a page can take the keyboard from a click handler but not out of the blue; - `"open"` - the cursor at any time and the keyboard at once together with it, so a menu the server opens gets the keys straight away (a keyboard asked for without the cursor waits for the player's first click in the frame). Asking for the keyboard more than six times a minute without any input from the player is refused. Esc gives the focus back unless the page handles the key itself. [`KcdMp.onVisibility`](/js/web/handlers/onvisibility/) tells a page when the interface stops being drawn - a game menu opened, a level loading - and [`KcdMp.onKeyBinding`](/js/web/handlers/onkeybinding/) hears the keys a server has declared for its interface. | Function | What it does | |---|---| | [`KcdMp.focus`](/js/web/functions/focus/) | Asks the game to give this frame the keyboard and the mouse cursor. | | [`KcdMp.blur`](/js/web/functions/blur/) | Gives the keyboard and the mouse cursor back to the game. | | Handler | When it fires | |---|---| | [`KcdMp.onVisibility`](/js/web/handlers/onvisibility/) | Registers a function that is told when the interface is drawn or hidden. | | [`KcdMp.onPlayerCursor`](/js/web/handlers/onplayercursor/) | Registers a function that is told when the player brings up their cursor or takes it away. | | [`KcdMp.onKeyBinding`](/js/web/handlers/onkeybinding/) | Registers a function for a key binding the server declared. | ### KcdMpFocusOptions What a page asks for with `KcdMp.focus`. | Field | Type | | |---|---|---| | `keyboard` | `boolean` | `true` to receive the keys (typed text goes to the page's text fields) *(optional)* | | `cursor` | `boolean` | `true` to receive the mouse cursor *(optional)* | | `keepInput` | `boolean` | `true` to let the game keep the player's keys while no text field of the page has the focus, so the player can walk with the page open *(optional)* | ## Drawing a component Twelve of the game's interface components can be drawn by a page of the server's interface instead of KCD:MP's own: the **chat**, the **scoreboard**, the mode's **HUD texts** and GameText (`hudtext`), the **sleep** fade, the **notices**, the **death** screen, the **party** frames with the invitation, the **voice** indicator, the **emote** wheel, a mode's **shop**, the **dialogs** (a mode's dialogue page, the game's own questions) and the **loading** screen. The game mode gives a component to a frame - `SetPlayerInterface(pid, "chat", "mychat")`, or `[components.chat] drawer = "mychat"` in `ui.toml` (the loading screen's only there) - and from then on that frame gets the component's events through [`KcdMp.onComponent`](/js/web/handlers/oncomponent/) and calls its actions through [`KcdMp.call`](/js/web/functions/call/); the game's own piece steps aside. Until the page is ready, and whenever it cannot draw (its renderer stopped, the player's web layer is off), the game's own piece draws the component, so a player never has none. A frame draws one component at a time; [`KcdMp.env.component`](/js/web/properties/env-component/) and [`KcdMp.contract`](/js/web/properties/contract/) say which (`"chat"`, `"chat@1"`). Each component's **contract** is what such a page serves: the events it hears, the actions it may call and the keys the game hands it. Every text in an event is the server's or another player's: show it with `textContent`, never as HTML. **The chat (`chat@1`).** Enter opens the chat: the game gives the frame the keyboard and the mouse cursor and sends `chat:open`; the page shows its box, and sends what the player typed with `chat.send`, then `chat.close` (Esc in the page should do the same: call `event.preventDefault()` in its handler and then `chat.close`). While the box is closed the scroll keys (Page Up, Page Down unless the player rebound them) arrive as `chat:scroll`. | Event | Data | When | |---|---|---| | `chat:config` | `{ maxChars, scrollStep, layout: { anchor, marginX, marginY, width, lines, backingAlpha, backingAlphaOpen, fontScale }, channels: [{ name, label, color, canSend }], suggestions: [{ name, help, params }], secretCommands: [names] }` | at the start and whenever any of it changes (the mode's chat options, channels, commands) | | `chat:history` | `{ lines: [line] }` - the last 200 | at the start | | `chat:message` | `line` = `{ seq, id, ts, kind: "player"/"mode"/"server"/"local", authorId, author, color, text, spans: [{ text, color, bold, italic }], channel, channelLabel, channelColor, template, params }` | each line | | `chat:open` | `{ prefill }` | the player opened the chat | | `chat:close` | `{}` | the game closed the box (a menu opened, the chat was hidden, the frame lost the keyboard) | | `chat:clear` | `{}` | the mode cleared the chat | | `chat:scroll` | `{ lines }` - up when positive | a scroll key while the box is closed | | Action | Data | What it does | |---|---|---| | `chat.send` | `{ text, channel }` | says the line (a `/` command too) on a channel the player may write in - `""` is the default one; only while the box is open, at most 4 a second | | `chat.close` | `{}` | the box is closed: the keyboard and the cursor go back to the game | | `chat.layout` | `{ x, y, w, h }` | where the page's chat is, in pixels of a screen 1080 tall: the game's own panels that share the corner make room for it | | `chat.typing` | `{ active }` | reserved | **The scoreboard (`scoreboard@1`).** Tab shows the board while it is held, or opens and closes it (the scoreboard's `mode` option). In `toggle` mode the frame gets the mouse cursor while the board is open - its own clicks and mouse wheel. Page Up and Page Down arrive as `scoreboard:page` while it is open, and so does the mouse wheel in `hold` mode. | Event | Data | When | |---|---|---| | `server:info` | `{ name, address, title }` - `title` is the scoreboard's `title` option, `""` for the usual one | at the start and on a change | | `scoreboard:columns` | `{ columns: [{ key, label, width, align: "left"/"center"/"right", format: ""/"number"/"time"/"percent", color }] }` - the mode's `SetScoreboardColumns` | at the start and on a change | | `roster:snapshot` | `{ players: [entry] }` with `entry` = `{ id, name, label, color, team, ping, inMenu, talking, muted, party, self, cells: { key: text } }` | at the start | | `roster:update` | `{ players: [entry] }` - the entries that changed | a few times a second while the board is open, once a second otherwise | | `roster:remove` | `{ ids: [id] }` | a player left | | `scoreboard:show` | `{ open, mode: "hold"/"toggle" }` | Tab | | `scoreboard:page` | `{ delta }` - `1` a page down, `-1` up | Page Down / Page Up, the wheel in `hold` mode | | Action | Data | What it does | |---|---|---| | `roster.mute` | `{ id, muted }` | the player stops (or starts again) hearing that player's voice - on their own game | | `scoreboard.close` | `{}` | closes a board the player opened in `toggle` mode | **The HUD texts (`hudtext@1`).** What the mode puts on the screen with `CreateHudText` and `GameText`. No actions, no keys: the frame never gets the keyboard or the cursor for it. | Event | Data | When | |---|---|---| | `hudtext:snapshot` | `{ elements: [element] }` with `element` = `{ id, x, y, text, color, opacity, scale, align: "left"/"center"/"right" }` - `x` and `y` are fractions of the screen from the top left (`y` is the line's top, `x` its left edge, centre or right edge by `align`), `scale` `1` is the HUD's own size | at the start (several messages when there are many: the rest come as `hudtext:update`) | | `hudtext:update` | `{ elements: [element] }` - the elements that changed or came | the mode changed them | | `hudtext:remove` | `{ ids: [id] }` | the mode hid or destroyed them | | `gametext:show` | `{ text, style: "center"/"top"/"lower", durationMs, remainingMs, until }` - shown for `remainingMs`, `""` = none; `until` is when it is gone, in `Date.now()` | each GameText (the newest replaces the last), and at the start while one runs | **The sleep fade (`sleep@1`).** A bed's sleep or a book's reading darkens the screen a moment with the hours it took (the clock is the server's: the game's own time skip is stopped). It comes from the player's own game - no mode sends it. No actions, no keys. | Event | Data | When | |---|---|---| | `sleep:slept` | `{ hours, reading, elapsedMs, inMs, holdMs, outMs, until }` - the fade started `elapsedMs` ago: dark over `inMs`, dark until `holdMs` from its start, then light again over `outMs`; `reading` for a book's | a sleep or a reading ends, and at the start while one fades | **The notices (`notices@1`).** The mode's `ShowInfoText`, `ShowNotification`, `ShowGameLog` and `ShowObjectiveEvent` (and the client's own short notices). The tutorial box - a mode's tips, the party's invitation, the death box - stays the game's own. A notice is an event of the moment: a frame is never told of one that came before it. No actions, no keys. | Event | Data | When | |---|---|---| | `notice:show` | `{ id, kind, text, raw, durationMs, local }` and by kind: `"info"` + `{ priority, background }` (one line at a time; a higher `priority` goes before the ones waiting), `"notification"`, `"log"` + `{ type, level }` (the game's log kinds; `0, 0` is a plain line), `"objective"` + `{ title, status: "new"/"done"/"failed" }` - `text` is the line without the game's markup, `raw` as the mode sent it; `durationMs` `0` = the page's own time for the kind; `local` = the player's own game said it | each notice | | `notice:hide` | `{ kind: "info" }` | `HideInfoText`: the info line shown now goes | | `notice:clear` | `{}` | every notice goes, shown or waiting | **The death screen (`death@1`).** What the server's death box says, in fields: who killed the player and when they are back on their feet. The game's own box does not show while a frame draws the death. No actions yet, no keys. | Event | Data | When | |---|---|---| | `death:show` | `{ killerId, killerName, cause, timer, respawnMs, until }` - `killerId` a player's id (`-1` none), `killerName` that player's name or an NPC's label (`""` = no one), `cause` the server's words when no one killed them (`"You fell to your death."`), `timer` = the respawn comes by itself, in `respawnMs` (`0` = due), at `until` (`Date.now()`); `timer` false = the game mode decides | the player died, and at the start while they are dead | | `death:hide` | `{}` | the player is on their feet again | **The party (`party@1`).** The party frames - the other members with their health and stamina - and the party's invitation. The invitation's keys (Y and N unless the player changed them) stay the game's own: they answer it whoever draws the box. The frame takes no keys; it may get the cursor (its own `KcdMp.focus`), and then its own Accept / Decline buttons answer with `party.answer`. | Event | Data | When | |---|---|---| | `party:config` | `{ layout: { anchor, marginX, marginY, showStamina } }` - the party component's options | at the start and on a change | | `party:state` | `{ id, leader, name, frames, members: [{ id, name, label, leader, self }] }` - `id` `0` = in no party, `frames` = the mode lets the frames show; every member in the order they joined, the player's own entry among them | at the start and when the party changes | | `party:vitals` | `{ members: [{ id, known, health, maxHealth, stamina, maxStamina, injuries, dead, bleeding, loading, pos }] }` - the others (the game's HUD shows the player's own); `known` false until the first; `pos` `[x, y, z]` in metres, absent while that member is not in the world | at the start, then at most twice a second | | `party:invite` | `{ fromId, fromName, seconds, until, acceptKey, declineKey }` | an invitation: it runs out in `seconds` | | `party:invite-end` | `{}` | it was answered, withdrawn or ran out | | Action | Data | What it does | |---|---|---| | `party.answer` | `{ accepted }` | answers the invitation - only right after the player's own click or key in the page, never a script's | **The voice indicator (`voice@1`).** Who is talking, the player's own microphone and the voice level they talk at. The game has no piece of its own for it (the talking marks are in the scoreboard and the name plates): a web drawer adds one. All of it is the player's own game's state. The one line the game shows - "Voice: Shout (50 m)" under the compass when the level changes - is left out while a page draws this component: the page shows the level its own way. | Event | Data | When | |---|---|---| | `voice:self` | `{ mode: "off"/"push-to-talk"/"voice", micOpen, transmitting, level, key }` - `level` `0`..`1` while it sends, `key` the push-to-talk key's name | at the start, on a change, several a second while the player talks | | `voice:talking` | `{ ids: [id], players: [{ id, name }] }` - the players heard talking now | at the start and on a change | | `voice:muted` | `{ ids: [id], players: [{ id, name }] }` - the players this player muted | at the start and on a change | | `voice:levels` | `{ levels: [{ name, range }], current, key }` - the mode's voice levels (its `SetVoiceLevels`; `range` in metres, a level without its own range shows the server's), `current` the one the player talks at (from `0`, `-1` for none), `key` the level key's name (`"lctrl"`); `levels` is empty on a server without levels | at the start and on a change | | Action | Data | What it does | |---|---|---| | `voice.mute` | `{ id, muted }` | stop (or start again) hearing that player - on this player's game alone | | `voice.mode` | `{ mode }` | the microphone's mode; `"push-to-talk"` and `"voice"` only right after the player's own click or key in the page | | `voice.level` | `{ index }` or `{ next: true }` | asks the server for that level (from `0`) or the next one, as the level key does; the mode may refuse, and the level that holds comes back in `voice:levels` | **The emote wheel (`emotes@1`).** The ring G opens. It stays the game's in everything but its pixels: G held or tapped, the number keys, Esc, the mouse's pointer and the pick are the game's own, and the page is told what to show (the mouse moves the pointer, never a cursor of the page). | Event | Data | When | |---|---|---| | `emotes:list` | `{ entries: [{ name, label }] }` - the server's wheel, clockwise from the top (`[]` = none) | at the start and on a change | | `emotes:open` | `{ entries: [{ name, label }], sticky }` - the wheel as it opened; `sticky`: a tap left it open, it waits for a click, a number key, G again or Esc | when it opens, and again when it turns sticky | | `emotes:pointer` | `{ dx, dy, index }` - the pointer, `-1`..`1` of the ring's reach (y down); `index` the entry pointed at, `-1` none | while it is open, as the mouse moves | | `emotes:close` | `{ picked }` - the emote chosen, `""` none | when it closes | | Action | Data | What it does | |---|---|---| | `emotes.pick` | `{ name }` | plays that emote of the wheel and closes it - only while the wheel is open | | `emotes.close` | `{}` | closes the wheel - only while it is open | | `emotes.stop` | `{}` | the emote playing ends - only while the wheel is open | **The shop (`shop@1`).** A mode's shop on the list panel (`OpenShop`, a shop that keeps the panel); a vendor's own trade screen stays the game's. The keys stay the game's (W / S, A / D the quantity, Tab, Enter, Esc) and move what the page is told; the page holds the pointer while the shop is open, so its rows and buttons can be clicked. | Event | Data | When | |---|---|---| | `shop:open` | `{ shopId, title, vendorId, sellTab, bottomless, vendorMoney, rowsPerPage }` - `vendorId` `-1` = none; `bottomless`: the vendor's purse is no limit | when a shop opens | | `shop:list` | `{ tab, entries: [{ itemClass, name, price, amount, itemId }], vendorMoney }` - `tab` `0` Buy, `1` Sell; `price` in tenths of a Groschen a piece; `amount` the stock (`-1` unlimited) on Buy, what the player carries on Sell; `itemId` only on a row of one of the mode's tagged items (a row of its own, under its own `name`) | at the opening and when a tab's rows change | | `shop:state` | `{ tab, index, page, quantity }` - the row under the cursor (`-1` = an empty list), the first row shown, the quantity (1-99) | at the opening and on every move | | `player:purse` | `{ money }` - the player's purse, tenths of a Groschen | at the opening and when it changes | | `shop:close` | `{}` | when the shop closes | | Action | Data | What it does | |---|---|---| | `shop.select` | `{ tab, index }` | puts the cursor on that row (and that tab) | | `shop.quantity` | `{ amount }` | the quantity, 1-99 | | `shop.buy`, `shop.sell` | `{ itemClass, amount, itemId }` | buys or sells that row (the amount kept to the stock or what is carried; `itemId` the row's, for a tagged row) - only right after the player's own click or key in the page | | `shop.close` | `{}` | closes the shop | **The dialogs (`dialog@1`).** A mode's dialogue page (`ShowPlayerDialogue`) and a question of the player's own game (a body change asking to restart), one at a time - the question first. The keys stay the game's (W / S, 1-8, E / Enter, Esc on a page; Y / Enter, N on a question); the page holds the pointer while a dialog is open. A kick's or a refusal's notice stays the game's own box. | Event | Data | When | |---|---|---| | `dialog:open` | `{ id, kind, title, text, options: [{ text, enabled, key }], cursor, actorId }` - `kind` `"dialogue"` (`title` the speaker, `options` the choices with `key` `"1"`..`"8"`, `cursor` the highlighted one) or `"question"` (`options` Accept `"y"` and Decline `"n"`, `cursor` `-1`); a new page is a new `id` | when a dialog opens or the page changes | | `dialog:cursor` | `{ id, index }` - the page's highlight moved | when the keys move it | | `dialog:close` | `{ id }` | when it closes | | Action | Data | What it does | |---|---|---| | `dialog.answer` | `{ id, choice }` | a page's choice 1-8 (`0` leaves the conversation), a question's `1` (yes) or `0` (no) - only right after the player's own click or key in the page | | `dialog.select` | `{ id, index }` | moves a page's highlight (as the pointer moves over the choices) | **The loading screen (`loading@1`).** What covers the game while the player connects and the level loads, until they stand in the world - and, when `ui.toml` asks for it, after that. Its drawer is `ui.toml`'s alone: `[components.loading] drawer` is `"default"` (KCD:MP's own, with the interface's theme) or the name of a frame; without the section a frame called `loading` is the screen (the launcher fetches its files before the game starts). A mode's `SetPlayerInterface` cannot change it: the screen shows while the level loads, before anything the mode sends reaches the game. The page shows over everything - the rest of the interface is not drawn while it covers the game, so give it an opaque background - and the game's own screen draws until the page has drawn its first picture. A loading frame does not wait for the whole interface: it draws as soon as its own files are on the player's PC - its page's folder and the files at the top of `ui/`, which the launcher fetches before the game starts (on a server without a password) and the game otherwise asks for first - while the rest still comes. Until everything has arrived, a file outside those answers *not found*, and a page that asked for one is loaded again once the interface is complete: keep a loading page's files in its folder. `manual_shutdown = true` under `[components.loading]` keeps it past the spawn until the mode's `ClosePlayerLoadingScreen`, the page's `loading.done` or 240 seconds; `cursor = true` gives the page the mouse cursor while it is held (there is none during the level load itself). No keys. (The `visibility` feed says `"loading"` while the screen is up - to the loading page too, which is drawn all the same.) | Event | Data | When | |---|---|---| | `loading:state` | `{ phase, line, server: { name, motd, host, port, level, levelName }, fraction, manual, cursor, ending, tips, version }` - `phase` `"connecting"`, `"handshaking"`, `"loading"` (the level loads), `"ready"` (loaded, the spawn awaited), `"spawned"` or `"ended"` (refused, kicked, the server gone: `line` says why); `line` what the game's own screen says; `fraction` `0`..`1`, or `null` while it cannot be measured; `manual` = the screen is held past the spawn; `tips` the game's own lines | at the start and on every change | | `loading:handover` | `{ data }` - the mode's `SetPlayerLoadingHandover`, as it wrote it (`null` = none any more) | at the start and whenever it changes - it may come during the level load | | `loading:end` | `{}` - the screen fades out now (1.2 seconds), then it is gone | the player stands in the world and nothing holds the screen | | Action | Data | What it does | |---|---|---| | `loading.done` | `{}` | ends a screen held past the spawn (`manual_shutdown`) - as the mode's `ClosePlayerLoadingScreen`; before the spawn it waits for it | (The page's `KcdMp` tells the game itself when the page has drawn - the action `loading.shown`; a page never calls it.) Colours are `"#rrggbb"` texts (`""` = none of its own), `id` and `authorId` are player ids (`-1` = none), `ts` the game's own clock in milliseconds. A cell is the player's state value under the column's key, as text - the page formats it. | Handler | When it fires | |---|---| | [`KcdMp.onComponent`](/js/web/handlers/oncomponent/) | Registers a function that receives the events of the game component this frame draws. | ## The game's pictures and typefaces A page can use the game's own item pictures and typefaces. Every player's game makes them from its own copy of the game: they are never downloaded and never part of your interface, so they cost your server nothing. - **Item pictures** - [`KcdMp.itemIcon`](/js/web/functions/itemicon/) gives the address of an item's inventory picture for an `` or a CSS background. - **Typefaces** - `"KcdMp Regular"` (with its bold and italic), `"KcdMp Light"` (with its bold and italic), `"KcdMp Display"` and `"KcdMp Manuscript"` work in any `font-family`, with no `@font-face` rule of your own. Put a fallback after them for the browser, where they do not exist: `font-family: "KcdMp Regular", Georgia, serif;`. Both are there to be shown - in an ``, a CSS background or a `font-family`; a `fetch()` of either is refused. | Function | What it does | |---|---| | [`KcdMp.itemIcon`](/js/web/functions/itemicon/) | The address of an item's picture, for an `` or a CSS background. | ## Pages on surfaces in the world A page can also be drawn **in the world** - on a notice board, a shop sign, a screen on a wall. Such a page is a **surface** page, declared in `ui.toml` under `[surfaces.]` instead of `[frames.]`, and the game mode places surfaces that show it (its `CreateWebSurface`). Every player near a surface sees it drawn on a board, lit by the world like the game's own signs, and each player's game draws its **own copy** of the page: what one copy shows is what the mode sent that player. A surface page works like a frame's page - [`KcdMp.onMessage`](/js/web/handlers/onmessage/) hears the mode (its `SendWebSurfaceMessage` / `SendPlayerWebSurfaceMessage`), [`KcdMp.emitServer`](/js/web/functions/emitserver/) sends the mode events (its `OnPlayerWebSurfaceEvent`, for the names the surface's `events_out` allows), [`KcdMp.ready`](/js/web/functions/ready/) is sent for it when the page has loaded (the mode's `OnPlayerWebSurfaceReady`, the moment to send it what it should show) and the game's typefaces and item pictures work - with a few differences: - It is drawn at the size `ui.toml` gives it (`width` and `height`, in pixels): lay it out for exactly that size, with type large enough to read from a few metres away. - Nobody clicks or types on it: no keyboard, no mouse cursor ([`KcdMp.focus`](/js/web/functions/focus/) is refused). A surface with a use prompt hears the player's use key instead ([`KcdMp.onSurfaceUse`](/js/web/handlers/onsurfaceuse/)). - It is drawn only near the player (`range` in `ui.toml`, 30 metres unless it says) and only for the closest few surfaces; a page out of range is closed and loads again, from its start, when the player comes back. Keep what it shows in the game mode, which sends it again at every ready. - It paints at most `fps` pictures a second (10 unless `ui.toml` says): a page that changes rarely should ask for few. - [`KcdMp.post`](/js/web/functions/post/), [`KcdMp.call`](/js/web/functions/call/), feeds, key bindings and components are a frame's alone. [`KcdMp.surface`](/js/web/properties/surface/) tells a surface page which surface it is drawn on. | Property | What it holds | |---|---| | [`KcdMp.surface`](/js/web/properties/surface/) | The surface this page is drawn on - or `null` in a frame. | | Handler | When it fires | |---|---| | [`KcdMp.onSurfaceUse`](/js/web/handlers/onsurfaceuse/) | Registers a function for the player's use key on this surface. | ### KcdMpSurface The surface a copy of a surface page is drawn on. | Field | Type | | |---|---|---| | `name` | `string` | the page's name in `ui.toml` (`[surfaces.]`) | | `id` | `number` | the surface's entity id - the id the game mode's `CreateWebSurface` returned; `0` until the page is ready | | `width` | `number` | the page's width in pixels (its `ui.toml` `width`) | | `height` | `number` | the page's height in pixels (its `ui.toml` `height`) | ## Logging and errors A page has no console the player can see, so it writes to the **interface log**, a file of the player's game next to its other logs, and it can hear its own errors. The log takes about twenty lines a second per player; the rest are counted and dropped with one note. What a page logs stays on the player's computer: it is never sent to the server, and a script error reaches the game mode only when the server asked for those (`[client] ui_report_errors` in the server's configuration, which the mode hears as `OnPlayerWebError`). Uncaught errors and unhandled promise rejections are logged as errors by themselves and passed to [`KcdMp.onError`](/js/web/handlers/onerror/). In a browser, in mock mode, these calls write nothing: the browser's own console is the place to look there. | Function | What it does | |---|---| | [`KcdMp.log.info`](/js/web/functions/log-info/) | Writes a line to the interface log. | | [`KcdMp.log.warn`](/js/web/functions/log-warn/) | Writes a warning to the interface log. | | [`KcdMp.log.error`](/js/web/functions/log-error/) | Writes an error to the interface log. | | Handler | When it fires | |---|---| | [`KcdMp.onError`](/js/web/handlers/onerror/) | Registers a function that is told of the page's errors and of the game's complaints about the page. | ## Mock mode A page can be written and tried in a plain browser, without the game. Outside the game `KcdMp.env.mock` is `true` and every call answers on the spot: `KcdMp.ready`, `KcdMp.emitServer`, `KcdMp.focus`, `KcdMp.blur` and `KcdMp.call` settle with `{ ok: true, mock: true }`, and `KcdMp.post` does the same unless [`KcdMp.mock.reply`](/js/web/functions/mock-reply/) says what it should answer. Nothing arrives by itself: no events, no messages and no feeds until the page plays the game's part with `KcdMp.mock`. The object exists only in mock mode - inside the game `KcdMp.mock` is `undefined`. | Property | What it holds | |---|---| | [`KcdMp.mock`](/js/web/properties/mock/) | The controls that play the game's part when the page runs in a browser. | | Function | What it does | |---|---| | [`KcdMp.mock.emit`](/js/web/functions/mock-emit/) | Fires the page's event handlers as if the game mode had sent the event. | | [`KcdMp.mock.message`](/js/web/functions/mock-message/) | Fires the page's message handlers as if a client script or the game mode had sent a message. | | [`KcdMp.mock.feed`](/js/web/functions/mock-feed/) | Publishes a feed value as if the game had. | | [`KcdMp.mock.reply`](/js/web/functions/mock-reply/) | Sets what `KcdMp.post` answers for a name. | | [`KcdMp.mock.key`](/js/web/functions/mock-key/) | Fires a key binding's handlers as if the player had pressed - or let go of - the key. | | [`KcdMp.mock.playerCursor`](/js/web/functions/mock-playercursor/) | Fires the player-cursor handlers as if the player had brought up - or taken away - their cursor. | | [`KcdMp.mock.use`](/js/web/functions/mock-use/) | Fires the surface-use handlers as if the player had pressed the use key on the surface. | | [`KcdMp.mock.component`](/js/web/functions/mock-component/) | Plays an event of the component the page draws - or its assignment - as the game would. | # KcdMp.blur > Gives the keyboard and the mouse cursor back to the game. Gives the keyboard and the mouse cursor back to the game. The frame gives up what it holds: the game's keys and mouse work again. The page's script calls it itself when the player presses Esc, unless the page's own key handler kept the key with `preventDefault()`. ## Syntax ```js await KcdMp.blur() ``` ## Returns `Promise` - settles when the game has answered ## Example ```js document.querySelector("#close").addEventListener("click", () => KcdMp.blur()); ``` ## See also [KcdMp.focus](/js/web/functions/focus/) · the [Focus and visibility](/js/web/#focus-and-visibility) group of the index # KcdMp.call > Calls an action of the game's interface component this frame draws. Calls an action of the game's interface component this frame draws. A frame that draws one of the game's components in place of the standard drawing - the chat, for one - can call the component's **actions** (`"chat.send"`): what the component does through the game itself. The actions of each component are part of its contract ([`KcdMp.onComponent`](/js/web/handlers/oncomponent/) lists the chat's and the scoreboard's), and an action is available to that frame alone, while it draws the component. An action that needs the player's own input (buying, answering a dialogue) is refused unless a real key or click reached the frame in the last second. Any other action name - and any name from a frame that draws no component - rejects with `"unknown action"`; wrong data rejects with what is wrong with it, a `chat.send` while the chat's box is closed with `"the chat box is not open"`, and more than four lines a second with `"too many lines"`. ## Syntax ```js await KcdMp.call(action, data?) ``` | Parameter | Type | | |---|---|---| | `action` | `string` | the action's name, as its component's contract gives it | | `data` | `unknown` | the action's arguments, any JSON value *(optional)* | ## Returns `Promise` - the action's answer, read from JSON ## Example ```js try { await KcdMp.call("chat.send", { text: "Good day!" }); } catch (e) { KcdMp.log.warn("chat.send: " + e.message); } ``` ## See also [KcdMp.contract](/js/web/properties/contract/) · [KcdMp.env.component](/js/web/properties/env-component/) · the [Messages and events](/js/web/#messages-and-events) group of the index # KcdMp.emitServer > Sends the game mode an event. Sends the game mode an event. The event arrives in the mode's `OnClientEvent(pid, name, payload, source)` with the frame's name as `source`. `value` is sent as it is when it is a text and as JSON when it is anything else (nothing sends an empty payload); it is at most **16 KB**. The promise resolves once the game has sent the event - not when the mode has handled it. It **rejects** with an `Error` when the event cannot be sent: `"that event is not in the frame's events_out"` (the name is not one the frame may send), `"too many events"` (the player is over their budget of 30 events and 64 KB a second, shared with their client scripts), `"the payload is over 16 KB"`, `"not connected to a server"`. The mode treats what a page sends like any other input from a player: `source` says which frame sent it, and nothing more. ## Syntax ```js await KcdMp.emitServer(name, value?) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | the event's name, 1 to 64 characters of `a-z 0-9 _ . : -`; not one that starts with `kcdmp:` | | `value` | `unknown` | the payload - a text as it is, anything else as JSON *(optional)* | ## Returns `Promise` - settles once the game has sent the event ## Example ```js document.querySelector("#buy").addEventListener("click", async () => { try { await KcdMp.emitServer("shop.buy", { item: "bread", count: 1 }); } catch (e) { KcdMp.log.warn("could not send: " + e.message); } }); ``` ## See also [KcdMp.onServer](/js/web/handlers/onserver/) · [KcdMp.post](/js/web/functions/post/) · the [Messages and events](/js/web/#messages-and-events) group of the index # KcdMp.focus > Asks the game to give this frame the keyboard and the mouse cursor. Asks the game to give this frame the keyboard and the mouse cursor. Neither `keyboard` nor `cursor` is the same as [`KcdMp.blur`](/js/web/functions/blur/). With `keepInput` the frame holds the keyboard without shutting the game's out: the game still gets the player's keys while no text field of the page has the focus. Whether the frame gets what it asked for is decided by its `focus` setting in `ui.toml`: `"never"` refuses everything, `"input"` (the default) grants the cursor at once and the keyboard only within five seconds of the player's own click or key press in the frame, `"open"` grants the cursor at any time and the keyboard at once together with it (the keyboard alone after the player's first click in the frame). The promise **rejects** with an `Error` that says why when the request is refused: `"the frame's focus policy is never"`, `"the keyboard comes with the player's click in the frame"` (a request for the keyboard the player has not made a click or key press for), `"too many keyboard requests without the player's input"`, `"the frame is hidden"` and `"the web layer is not running"`. ## Syntax ```js await KcdMp.focus(options?) ``` | Parameter | Type | | |---|---|---| | `options` | `KcdMpFocusOptions` | what the page asks for; leave it out, or leave both `keyboard` and `cursor` off, to give the focus up *(optional)* | ## Returns `Promise` - settles when the game has answered; rejects when the request is refused ## KcdMpFocusOptions What a page asks for with `KcdMp.focus`. | Field | Type | | |---|---|---| | `keyboard` | `boolean` | `true` to receive the keys (typed text goes to the page's text fields) *(optional)* | | `cursor` | `boolean` | `true` to receive the mouse cursor *(optional)* | | `keepInput` | `boolean` | `true` to let the game keep the player's keys while no text field of the page has the focus, so the player can walk with the page open *(optional)* | ## Example ```js // a text field that takes the keyboard when the player clicks into it document.querySelector("#say").addEventListener("click", async () => { try { await KcdMp.focus({ keyboard: true, cursor: true }); } catch (e) { KcdMp.log.warn("no focus: " + e.message); } }); ``` ## See also [KcdMp.blur](/js/web/functions/blur/) · [KcdMp.onVisibility](/js/web/handlers/onvisibility/) · the [Focus and visibility](/js/web/#focus-and-visibility) group of the index # KcdMp.itemIcon > The address of an item's picture, for an or a CSS background. The address of an item's picture, for an `` or a CSS background. `key` is the item class's GUID or its game name (`"longSwordDuel"`, in any case) - the `class` and the `name` a game mode's `GetItemInfo` gives; the numeric id is the server's own and does not work here. The picture is the game's inventory icon, 64 by 64 pixels on a transparent background; size it with CSS. An item without a picture, or a key the game does not know, fails to load like any missing image: the ``'s `error` event fires. The address is the same in every player's game, so a stylesheet may use it too (`https://ui.kcd-mp.invalid/icons/items/.png`). In a browser, in mock mode, every valid key gives a plain placeholder square. A key of another shape gives `""`. ## Syntax ```js KcdMp.itemIcon(key) ``` | Parameter | Type | | |---|---|---| | `key` | `string` | the item class's GUID or game name | ## Returns `string` - the picture's address, or `""` when `key` is neither a GUID nor a name ## Example ```js const img = document.createElement("img"); img.src = KcdMp.itemIcon("longSwordDuel"); img.alt = "Duelling longsword"; img.width = 48; img.addEventListener("error", () => img.remove()); // no picture for this item document.getElementById("slot").append(img); ``` ## See also [KcdMp.env.mock](/js/web/properties/env-mock/) · the [The game's pictures and typefaces](/js/web/#the-games-pictures-and-typefaces) group of the index # KcdMp.log.error > Writes an error to the interface log. Writes an error to the interface log. The same as [`KcdMp.log.info`](/js/web/functions/log-info/), marked as an error. When the server asked to hear the errors of its pages, the game mode is told of it as well (with the text cut short, at most five a minute for a player). ## Syntax ```js KcdMp.log.error(...args) ``` | Parameter | Type | | |---|---|---| | `...args` | `unknown[]` | what to write | ## Returns nothing ## Example ```js try { render(data); } catch (e) { KcdMp.log.error("render failed:", e.message); } ``` ## See also [KcdMp.log.info](/js/web/functions/log-info/) · [KcdMp.log.warn](/js/web/functions/log-warn/) · [KcdMp.onError](/js/web/handlers/onerror/) · the [Logging and errors](/js/web/#logging-and-errors) group of the index # KcdMp.log.info > Writes a line to the interface log. Writes a line to the interface log. The arguments are turned into text - a text as it is, anything else as JSON, or as `String(x)` when that fails - and joined with spaces; a line is cut to 2000 characters. ## Syntax ```js KcdMp.log.info(...args) ``` | Parameter | Type | | |---|---|---| | `...args` | `unknown[]` | what to write | ## Returns nothing ## Example ```js KcdMp.log.info("shop opened", { items: items.length }); ``` ## See also [KcdMp.log.warn](/js/web/functions/log-warn/) · [KcdMp.log.error](/js/web/functions/log-error/) · [KcdMp.onError](/js/web/handlers/onerror/) · the [Logging and errors](/js/web/#logging-and-errors) group of the index # KcdMp.log.warn > Writes a warning to the interface log. Writes a warning to the interface log. The same as [`KcdMp.log.info`](/js/web/functions/log-info/), marked as a warning. ## Syntax ```js KcdMp.log.warn(...args) ``` | Parameter | Type | | |---|---|---| | `...args` | `unknown[]` | what to write | ## Returns nothing ## Example ```js if (items.length === 0) { KcdMp.log.warn("the shop has nothing to sell"); } ``` ## See also [KcdMp.log.info](/js/web/functions/log-info/) · [KcdMp.log.error](/js/web/functions/log-error/) · the [Logging and errors](/js/web/#logging-and-errors) group of the index # KcdMp.mock.component > Plays an event of the component the page draws - or its assignment - as the game would. Plays an event of the component the page draws - or its assignment - as the game would. Runs the handlers registered with [`KcdMp.onComponent`](/js/web/handlers/oncomponent/) for `name` with `data`, and keeps what the game would keep: a `chat:message` joins the lines a late handler of `chat:history` is told, a `roster:update` the roster. The name `"assign"` with `{ component, contract }` gives the page a component (`{ component: "chat", contract: "chat@1" }`) or takes it away (`{ component: "" }`), as the game mode's `SetPlayerInterface` does in the game. [`KcdMp.call`](/js/web/functions/call/) answers `{ ok: true, mock: true }` meanwhile, so a page can be tried with the whole contract in a browser. ## Syntax ```js KcdMp.mock.component(name, data) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | an event of the component's contract (`"chat:message"` ...), or `"assign"` | | `data` | `unknown` | the event's data, as the contract describes it | ## Returns nothing ## Example ```js if (KcdMp.env.mock) { KcdMp.mock.component("assign", { component: "chat", contract: "chat@1" }); KcdMp.mock.component("chat:history", { lines: [] }); KcdMp.mock.component("chat:message", { seq: 1, kind: "player", authorId: 1, author: "Henry", color: "#ffffff", text: "Good day!", spans: [], channel: "", channelLabel: "", channelColor: "", template: "", params: [] }); KcdMp.mock.component("chat:open", { prefill: "" }); } ``` ## See also [KcdMp.onComponent](/js/web/handlers/oncomponent/) · [KcdMp.mock.emit](/js/web/functions/mock-emit/) · the [Mock mode](/js/web/#mock-mode) group of the index # KcdMp.mock.emit > Fires the page's event handlers as if the game mode had sent the event. Fires the page's event handlers as if the game mode had sent the event. Runs the handlers registered with [`KcdMp.onServer`](/js/web/handlers/onserver/) for `name` - and for `"*"` - with `value` and its text: a text is its own text, anything else is written as JSON. ## Syntax ```js KcdMp.mock.emit(name, value?) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | the event's name | | `value` | `unknown` | the payload *(optional)* | ## Returns nothing ## Example ```js KcdMp.onServer("shop.items", (value) => render(value.items)); KcdMp.mock.emit("shop.items", { items: [{ id: "bread", price: 4 }] }); ``` ## See also [KcdMp.onServer](/js/web/handlers/onserver/) · [KcdMp.mock.message](/js/web/functions/mock-message/) · the [Mock mode](/js/web/#mock-mode) group of the index # KcdMp.mock.feed > Publishes a feed value as if the game had. Publishes a feed value as if the game had. Runs the handlers subscribed to the feed with `data` and keeps it as the feed's latest value, which a later subscription receives at once - exactly as in the game. Any feed name works: the frame's `feeds` list is not checked in mock mode. ## Syntax ```js KcdMp.mock.feed(name, data) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | the feed's name (`"session"`, `"visibility"`) | | `data` | `unknown` | the feed's value | ## Returns nothing ## Example ```js KcdMp.mock.feed("visibility", { visible: false, reason: "menu" }); ``` ## See also [KcdMp.feed](/js/web/properties/feed/) · [KcdMp.mock.emit](/js/web/functions/mock-emit/) · the [Mock mode](/js/web/#mock-mode) group of the index # KcdMp.mock.key > Fires a key binding's handlers as if the player had pressed - or let go of - the key. Fires a key binding's handlers as if the player had pressed - or let go of - the key. Runs the handlers registered with [`KcdMp.onKeyBinding`](/js/web/handlers/onkeybinding/) for `name` with `pressed`, as the game does when the player's key for a binding of the game mode goes down or comes up. Any name works: in mock mode no server has declared the bindings. A press and a release are two calls - `pressed` is `true` unless it says `false`. ## Syntax ```js KcdMp.mock.key(name, pressed?) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | the binding's name | | `pressed` | `boolean` | `true` for the press (the default), `false` for the release *(optional)* | ## Returns nothing ## Example ```js KcdMp.onKeyBinding("map.toggle", (pressed) => { if (pressed) toggleMap(); }); if (KcdMp.env.mock) { KcdMp.mock.key("map.toggle"); // the press KcdMp.mock.key("map.toggle", false); // the release } ``` ## See also [KcdMp.onKeyBinding](/js/web/handlers/onkeybinding/) · [KcdMp.mock.emit](/js/web/functions/mock-emit/) · the [Mock mode](/js/web/#mock-mode) group of the index # KcdMp.mock.message > Fires the page's message handlers as if a client script or the game mode had sent a message. Fires the page's message handlers as if a client script or the game mode had sent a message. Runs the handlers registered with [`KcdMp.onMessage`](/js/web/handlers/onmessage/) with `data`, untouched. ## Syntax ```js KcdMp.mock.message(data) ``` | Parameter | Type | | |---|---|---| | `data` | `unknown` | the message | ## Returns nothing ## Example ```js KcdMp.onMessage((data) => showNotice(data.text)); KcdMp.mock.message({ text: "The gate is open." }); ``` ## See also [KcdMp.onMessage](/js/web/handlers/onmessage/) · [KcdMp.mock.emit](/js/web/functions/mock-emit/) · the [Mock mode](/js/web/#mock-mode) group of the index # KcdMp.mock.playerCursor > Fires the player-cursor handlers as if the player had brought up - or taken away - their cursor. Fires the player-cursor handlers as if the player had brought up - or taken away - their cursor. Runs the handlers registered with [`KcdMp.onPlayerCursor`](/js/web/handlers/onplayercursor/) with `shown`, as the game does when the player's cursor key brings the cursor up or takes it away; a handler registered later is told the same state. `shown` is `true` unless it says `false`. ## Syntax ```js KcdMp.mock.playerCursor(shown?) ``` | Parameter | Type | | |---|---|---| | `shown` | `boolean` | `true` for the cursor up (the default), `false` for it gone *(optional)* | ## Returns nothing ## Example ```js KcdMp.onPlayerCursor((shown) => console.log("cursor", shown)); if (KcdMp.env.mock) { KcdMp.mock.playerCursor(); // up KcdMp.mock.playerCursor(false); // gone } ``` ## See also [KcdMp.onPlayerCursor](/js/web/handlers/onplayercursor/) · [KcdMp.mock.key](/js/web/functions/mock-key/) · the [Mock mode](/js/web/#mock-mode) group of the index # KcdMp.mock.reply > Sets what KcdMp.post answers for a name. Sets what `KcdMp.post` answers for a name. In mock mode `KcdMp.post(name, data)` calls `fn(data)` and resolves to what it returns (or to what a returned promise resolves to), where in the game a client script's `OnWebEvent` would answer. A name without a reply answers `{ ok: true, mock: true }`. ## Syntax ```js KcdMp.mock.reply(name, fn) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | the call's name | | `fn` | `(data: unknown) => unknown` | answers the call | ## Returns nothing ## Example ```js KcdMp.mock.reply("buy", (data) => ({ ok: data.item === "bread" })); const reply = await KcdMp.post("buy", { item: "bread" }); // { ok: true } ``` ## See also [KcdMp.post](/js/web/functions/post/) · [KcdMp.mock.emit](/js/web/functions/mock-emit/) · the [Mock mode](/js/web/#mock-mode) group of the index # KcdMp.mock.use > Fires the surface-use handlers as if the player had pressed the use key on the surface. Fires the surface-use handlers as if the player had pressed the use key on the surface. Runs the handlers registered with [`KcdMp.onSurfaceUse`](/js/web/handlers/onsurfaceuse/), as the game does when the player uses a surface page's prompt - a way to try a surface page's use in a browser. It works for a frame's page too, where nothing else would call them. ## Syntax ```js KcdMp.mock.use() ``` ## Returns nothing ## Example ```js KcdMp.onSurfaceUse(() => console.log("used")); if (KcdMp.env.mock) { KcdMp.mock.use(); } ``` ## See also [KcdMp.onSurfaceUse](/js/web/handlers/onsurfaceuse/) · the [Mock mode](/js/web/#mock-mode) group of the index # KcdMp.openInBrowser > Opens a website in the player's own web browser, after the game asks them. Opens a website in the player's own web browser, after the game asks them. For what must not happen inside the game - a sign-in with Discord or Google, a donation page, the server's forum. The game never loads such a page itself: it has no address bar and a player could not tell a real sign-in page from a fake one there (Google refuses to sign in inside a game's browser anyway). Instead the game asks the player, on its own dialog, whether to open the site's host in their web browser; `Y` opens it, `N` does not. The address must be `https://`, on the default port, with no user name in it, and its host must be listed in `ui.toml`'s `[permissions] links` (exactly: list `discord.com` and `accounts.google.com` both if you need both). It is only taken right after the player's own click or key in the page, one question at a time. **A sign-in** finishes outside the game: point the provider's redirect at your own website, which links the account to the player - for one, the page puts a one-time code of the mode's in the address (`state=`), and your site hands it back to the game mode with the account (through the mode's database or a web request the mode makes). The page then hears from the mode as usual ([`KcdMp.onServer`](/js/web/handlers/onserver/)). Rejects with the reason when the game would not ask: not after a click or key, not https, a host not in `links`, or another question already waiting. In a browser, in mock mode, it resolves `true` at once and opens nothing. ## Syntax ```js await KcdMp.openInBrowser(url) ``` | Parameter | Type | | |---|---|---| | `url` | `string` | the https address to open | ## Returns `Promise` - `true` when the player said yes and the browser opened, `false` when they said no ## Example ```js // ui.toml: [permissions] links = ["discord.com"] document.getElementById("discord").addEventListener("click", async () => { const state = await KcdMp.post("signin.code"); // a one-time code from the mode, via a client script const url = "https://discord.com/oauth2/authorize?client_id=123456&response_type=code&scope=identify" + "&redirect_uri=" + encodeURIComponent("https://example-server.net/discord") + "&state=" + state; if (await KcdMp.openInBrowser(url)) { document.getElementById("hint").textContent = "Finish signing in in your browser, then come back."; } }); ``` ## See also [KcdMp.onServer](/js/web/handlers/onserver/) · [KcdMp.emitServer](/js/web/functions/emitserver/) · the [Messages and events](/js/web/#messages-and-events) group of the index # KcdMp.post > Asks a client script something and waits for its answer. Asks a client script something and waits for its answer. A client script of the server answers in its `OnWebEvent(frame, name, data)` callback; the value the callback returns is what the promise resolves to, read from JSON. `data` is any JSON value (or nothing) of at most 16 KB. The promise **rejects** with an `Error` whose message says why: `"no handler"` when no client script answers, `"handler failed"` when the script raised an error, `"timeout"` when no answer came within about ten seconds, `"too many calls"` over the budget of about a hundred calls a second, and a text of the game's when the interface reloaded or the game left the server while the call waited. A call never hangs. Use [`KcdMp.emitServer`](/js/web/functions/emitserver/) for the game mode: the mode's own answers come back as events or messages. ## Syntax ```js await KcdMp.post(name, data?) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | what the call is, 1 to 64 characters of `a-z 0-9 _ . : -` - the script's `OnWebEvent` gets it as `name` | | `data` | `unknown` | any JSON value *(optional)* | ## Returns `Promise` - the script's answer, read from JSON ## Example ```js try { const reply = await KcdMp.post("buy", { item: "bread" }); showResult(reply.ok); } catch (e) { showError(e.message); // "no handler" when the server has no client script for it } ``` ## See also [KcdMp.onMessage](/js/web/handlers/onmessage/) · [KcdMp.emitServer](/js/web/functions/emitserver/) · the [Messages and events](/js/web/#messages-and-events) group of the index # KcdMp.ready > Tells the game the page is loaded and listening. Tells the game the page is loaded and listening. Until it is called the game keeps everything addressed to the page - messages, events, feeds - and hands it over in order once it is (up to 256 messages or 1 MB per frame; past that the oldest are dropped). The page's script calls it by itself when the document has loaded; a page that has more to set up first says so in its `` tag with the attribute `data-kcdmp-manual-ready` and calls `KcdMp.ready()` when it is ready. The game mode's `OnPlayerWebReady` and the client scripts' `OnWebFrameReady` run at that moment. A page that loads anew - the frame is made again, the interface changed - calls it again, and the queue and the feeds start over for the new document. ## Syntax ```js await KcdMp.ready() ``` ## Returns `Promise` - settles once the game has taken it ## Example ```js // - nothing reaches the page until it says it is ready async function start() { await loadTranslations(); KcdMp.onMessage(handleMessage); await KcdMp.ready(); } start(); ``` ## See also [KcdMp.onMessage](/js/web/handlers/onmessage/) · [KcdMp.onServer](/js/web/handlers/onserver/) · the [The page](/js/web/#the-page) group of the index # KcdMp.feed.session.subscribe > Registers a function that receives the connection and the player. Registers a function that receives the connection and the player. `fn(session)` is called with the latest value at once when there is one, and again whenever it changes: when the player has joined the server, and about every five seconds while the round-trip time moves. The value never holds anything that is private to the player's game - it is the server's name and address, the level, and the player's id, name and ping. The frame must list `session` in its `feeds` in `ui.toml`. ## Syntax ```js KcdMp.feed.session.subscribe(fn) ``` | Parameter | Type | | |---|---|---| | `fn` | `(session: KcdMpSessionFeed) => void` | called with each value | ## Returns `() => void` - a function that takes the handler off again ## KcdMpSessionFeed The connection and the player, as the `session` feed carries them. | Field | Type | | |---|---|---| | `server` | `string` | the server's name | | `address` | `string` | `host:port` as the player connected to it | | `level` | `string` | the level the server runs | | `playerId` | `number` | the player's id on the server - the game mode's `pid` | | `name` | `string` | the player's name | | `rtt` | `number` | the round-trip time to the server in milliseconds, `0` until it has been measured | ## Example ```js const off = KcdMp.feed.session.subscribe((session) => { document.querySelector("#who").textContent = session.name + " on " + session.server; document.querySelector("#ping").textContent = session.rtt + " ms"; }); ``` ## See also [KcdMp.feed](/js/web/properties/feed/) · [KcdMp.feed.visibility.subscribe](/js/web/handlers/feed-visibility-subscribe/) · the [Feeds](/js/web/#feeds) group of the index # KcdMp.feed.visibility.subscribe > Registers a function that is told when the interface is drawn or hidden. Registers a function that is told when the interface is drawn or hidden. `fn({ visible, reason })` is called with the latest value at once and again whenever it changes. It is the same news as [`KcdMp.onVisibility`](/js/web/handlers/onvisibility/) gives, from the feed's list: use the feed when the frame lists `visibility` in its `feeds`, the handler when it does not. The frame must list `visibility` in its `feeds` in `ui.toml`. ## Syntax ```js KcdMp.feed.visibility.subscribe(fn) ``` | Parameter | Type | | |---|---|---| | `fn` | `(visibility: KcdMpVisibility) => void` | called with each value | ## Returns `() => void` - a function that takes the handler off again ## KcdMpVisibility Whether the interface is drawn, and if not, why. | Field | Type | | |---|---|---| | `visible` | `boolean` | `true` while the interface is drawn over the game | | `reason` | `"loading" \| "menu" \| "game_screen" \| "panic" \| "hidden" \| ""` | why it is not drawn: `"loading"` (a level is loading), `"menu"` (a game menu is open), `"game_screen"` (one of the game's own screens is up), `"panic"` (the player switched the interface off), `"hidden"` (it is turned off), and `""` while it is drawn | ## Example ```js KcdMp.feed.visibility.subscribe(({ visible, reason }) => { document.body.classList.toggle("paused", !visible); KcdMp.log.info(visible ? "drawn" : "hidden: " + reason); }); ``` ## See also [KcdMp.onVisibility](/js/web/handlers/onvisibility/) · [KcdMp.feed](/js/web/properties/feed/) · the [Feeds](/js/web/#feeds) group of the index # KcdMp.onComponent > Registers a function that receives the events of the game component this frame draws. Registers a function that receives the events of the game component this frame draws. `fn(data, name)` is called for every event named `name` of the component the frame draws - the tables above list them - for `"*"` with every event, and for `"assign"` when the frame is given a component or loses it, with `{ component, contract }` (`component` is `""` when it draws none any more; [`KcdMp.env.component`](/js/web/properties/env-component/) and [`KcdMp.contract`](/js/web/properties/contract/) change with it). A handler registered while the frame draws a component is told the assignment at once. The events that describe a **state** - `chat:config`, `chat:history`, `server:info`, `scoreboard:columns`, `scoreboard:show`, `roster:snapshot`, `hudtext:snapshot`, `gametext:show`, `sleep:slept`, `death:show`, `party:config`, `party:state`, `party:vitals`, `party:invite`, `voice:self`, `voice:talking`, `voice:muted`, `voice:levels`, `emotes:list`, `emotes:open`, `emotes:pointer`, `shop:open`, `shop:list` (each tab's), `shop:state`, `player:purse`, `dialog:open`, `dialog:cursor`, `loading:state`, `loading:handover` and `loading:end` - are kept by the page's `KcdMp` and handed to a handler that registers late, as they are now: `chat:history` with the lines that arrived since and without the ones a clear took away, `roster:snapshot` and `hudtext:snapshot` with the updates applied, a GameText, a sleep's fade or an invitation only while it lasts (its `remainingMs`, `elapsedMs` or `seconds` moved on), `death:show` until `death:hide`, the wheel until `emotes:close`, the shop until `shop:close`, a dialog until `dialog:close`. The others - a line, an open, a scroll, a notice - are only ever told as they happen. When the frame loses the component, what was kept is forgotten. ## Syntax ```js KcdMp.onComponent(name, fn) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | an event's name (`"chat:message"`), `"*"` for all of them, or `"assign"` | | `fn` | `(data: unknown, name: string) => void` | called with each event's data and its name | ## Returns `() => void` - a function that takes the handler off again ## Example ```js const list = document.querySelector("#lines"); function addLine(line) { const row = document.createElement("div"); row.textContent = (line.author ? line.author + ": " : "") + line.text; // never innerHTML: the words are the players' row.style.color = line.color; list.append(row); } KcdMp.onComponent("chat:history", ({ lines }) => { list.replaceChildren(); lines.forEach(addLine); }); KcdMp.onComponent("chat:message", addLine); KcdMp.onComponent("chat:open", () => document.querySelector("#box").focus()); document.querySelector("#box").addEventListener("keydown", async (e) => { if (e.key === "Enter" && e.target.value) { await KcdMp.call("chat.send", { text: e.target.value, channel: "" }); e.target.value = ""; await KcdMp.call("chat.close"); } else if (e.key === "Escape") { e.preventDefault(); // the page closes the box itself await KcdMp.call("chat.close"); } }); ``` ## See also [KcdMp.call](/js/web/functions/call/) · [KcdMp.env.component](/js/web/properties/env-component/) · [KcdMp.contract](/js/web/properties/contract/) · [KcdMp.mock.component](/js/web/functions/mock-component/) · the [Drawing a component](/js/web/#drawing-a-component) group of the index # KcdMp.onError > Registers a function that is told of the page's errors and of the game's complaints about the page. Registers a function that is told of the page's errors and of the game's complaints about the page. `fn(error)` is called with an `Error` for each uncaught script error and each unhandled promise rejection of the page, and for the game's own complaints about it: when messages toward the page were dropped because more arrived at once than the game delivers (256 KB a second), the error's `code` is `"budget"`; the other errors have no `code`. One handler that throws is logged and does not stop the rest. ## Syntax ```js KcdMp.onError(fn) ``` | Parameter | Type | | |---|---|---| | `fn` | `(error: Error) => void` | called with each error | ## Returns `() => void` - a function that takes the handler off again ## Example ```js KcdMp.onError((error) => { if (error.code === "budget") { showBanner("The game could not keep up - some updates were skipped."); } }); ``` ## See also [KcdMp.log.error](/js/web/functions/log-error/) · the [Logging and errors](/js/web/#logging-and-errors) group of the index # KcdMp.onKeyBinding > Registers a function for a key binding the server declared. Registers a function for a key binding the server declared. A **key binding** is a named key the game mode declared with its `RegisterKeyBinding`: it starts on the mode's default key, and a player can put it on another with the command `KcdMp_bind` in the game's F9 window - when the server has opened that window to its players (it is closed by default). `fn(pressed)` is called with `true` when the key goes down and `false` when it comes up, on whichever key the player has it. The name is the binding's name as the mode declared it - a name is 1 to 64 characters of `a-z 0-9 _ . : -`, and the mode's are at most 32. Every frame of the interface that is ready hears every press; each handler is called only for the name it asked for. The handler only ever runs for a binding the mode has declared: until then the registration is harmless and silent. The game does not get the key while it is bound, and a press is never delivered while a page holds the keyboard, the chat box or the F9 window is open, or one of the game's own menus is up. A key that went down always gets its release. A page that was not ready when the key went down hears nothing of it, and a press over the frame's message budget is dropped like any other message - a release never is. ## Syntax ```js KcdMp.onKeyBinding(name, fn) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | the binding's name | | `fn` | `(pressed: boolean) => void` | called on the key's press and release | ## Returns `() => void` - a function that takes the handler off again ## Example ```js KcdMp.onKeyBinding("toggle_map", (pressed) => { if (pressed) toggleMap(); }); ``` ## See also [KcdMp.focus](/js/web/functions/focus/) · the [Focus and visibility](/js/web/#focus-and-visibility) group of the index # KcdMp.onMessage > Registers a function that receives what a client script or the game mode sent this frame with a message. Registers a function that receives what a client script or the game mode sent this frame with a message. `fn(data)` is called with **exactly the value** that was sent - a client script's `SendWebMessage(frame, data)` or the mode's `SendPlayerWebMessage(pid, frame, data)`: a table arrives as an object or an array, a text as a text, and a page never receives a message that only looks like the game's own. Messages arrive in the order they were sent, and one sent before the page called [`KcdMp.ready`](/js/web/functions/ready/) is kept and delivered when it does. Any number of functions may be registered; each gets every message, and one that throws is logged and does not stop the others. ## Syntax ```js KcdMp.onMessage(fn) ``` | Parameter | Type | | |---|---|---| | `fn` | `(data: unknown) => void` | called with each message | ## Returns `() => void` - a function that takes the handler off again ## Example ```js const off = KcdMp.onMessage((data) => { if (data && data.type === "notice") { showNotice(data.text); } }); // later: off(); ``` ## See also [KcdMp.onServer](/js/web/handlers/onserver/) · [KcdMp.post](/js/web/functions/post/) · [KcdMp.ready](/js/web/functions/ready/) · the [Messages and events](/js/web/#messages-and-events) group of the index # KcdMp.onPlayerCursor > Registers a function that is told when the player brings up their cursor or takes it away. Registers a function that is told when the player brings up their cursor or takes it away. The player brings up their cursor with the cursor key held for a second (left Alt unless they picked another in the launcher), a client script with its `SetCursorShown` and the game mode with its `SetPlayerCursor`. `fn(shown)` is called at once with the current state, then again at every change. While the cursor is up and no frame holds the mouse cursor, the interface's own parts (the chat, the scoreboard ...) take the clicks; a page that wants them asks for the cursor itself - `KcdMp.focus({ cursor: true })` - and gives it back when the player's cursor goes. The cursor goes away by itself when one of the game's own menus opens and at a load. ## Syntax ```js KcdMp.onPlayerCursor(fn) ``` | Parameter | Type | | |---|---|---| | `fn` | `(shown: boolean) => void` | called with `true` while the cursor is up, `false` while it is not | ## Returns `() => void` - a function that takes the handler off again ## Example ```js KcdMp.onPlayerCursor((shown) => { document.body.classList.toggle("clickable", shown); KcdMp.focus({ cursor: shown }).catch(() => {}); }); ``` ## See also [KcdMp.focus](/js/web/functions/focus/) · [KcdMp.blur](/js/web/functions/blur/) · [KcdMp.mock.playerCursor](/js/web/functions/mock-playercursor/) · the [Focus and visibility](/js/web/#focus-and-visibility) group of the index # KcdMp.onServer > Registers a function that receives the game mode's events with a given name. Registers a function that receives the game mode's events with a given name. The game mode's `SendClientEvent(pid, name, payload)` - and `SendClientEventToAll` - reaches every page whose `events_in` in `ui.toml` lets it receive `name`. `fn(value, raw)` is called with `value`, the payload read as JSON when it is JSON (a table the mode sent arrives as an object or an array), or the text itself when it is not, and `raw`, the payload exactly as a text. The name `"*"` hears every event the frame may receive; `fn` then gets the event's name as a third argument. Events arrive in the order they were sent, and one sent before the page called [`KcdMp.ready`](/js/web/functions/ready/) is kept and delivered when it does. An event the frame's `events_in` does not list never arrives. ## Syntax ```js KcdMp.onServer(name, fn) ``` | Parameter | Type | | |---|---|---| | `name` | `string` | the event's name (1 to 64 characters of `a-z 0-9 _ . : -`), or `"*"` for every event the frame may receive | | `fn` | `(value: unknown, raw: string, name: string) => void` | called with each event; `name` is only there for `"*"` | ## Returns `() => void` - a function that takes the handler off again ## Example ```js // the game mode sent a "shop.items" event whose payload is a table with a list called items KcdMp.onServer("shop.items", (value) => { renderItems(value.items); }); // everything the frame may hear, for a log KcdMp.onServer("*", (value, raw, name) => KcdMp.log.info(name + ": " + raw)); ``` ## See also [KcdMp.emitServer](/js/web/functions/emitserver/) · [KcdMp.onMessage](/js/web/handlers/onmessage/) · [KcdMp.ready](/js/web/functions/ready/) · the [Messages and events](/js/web/#messages-and-events) group of the index # KcdMp.onSurfaceUse > Registers a function for the player's use key on this surface. Registers a function for the player's use key on this surface. A surface the game mode gave a use prompt (`CreateWebSurface`'s `prompt` option, or `SetEntityPrompt`) shows it in the game's own use hint when a player looks at the surface from close by; their use key then calls `fn()` - on that player's copy of the page alone - and the mode's `OnPlayerUseWebSurface`. A page uses it for what only that player should see (the next page of a book, the next notice), and tells the mode what it needs to know with [`KcdMp.emitServer`](/js/web/functions/emitserver/). ## Syntax ```js KcdMp.onSurfaceUse(fn) ``` | Parameter | Type | | |---|---|---| | `fn` | `() => void` | called at each use | ## Returns `() => void` - a function that takes the handler off again ## Example ```js let page = 0; KcdMp.onSurfaceUse(() => { page = (page + 1) % pages.length; show(page); KcdMp.emitServer("book.page", page).catch(() => {}); }); ``` ## See also [KcdMp.surface](/js/web/properties/surface/) · [KcdMp.emitServer](/js/web/functions/emitserver/) · [KcdMp.mock.use](/js/web/functions/mock-use/) · the [Pages on surfaces in the world](/js/web/#pages-on-surfaces-in-the-world) group of the index # KcdMp.onVisibility > Registers a function that is told when the interface is drawn or hidden. Registers a function that is told when the interface is drawn or hidden. `fn({ visible, reason })` is called at once with the current state, then again at every change. `visible` is `false` while the interface is not drawn over the game, and `reason` says why: `"loading"` while a level loads, `"menu"` while a game menu is open, `"game_screen"` while one of the game's own screens is up, `"panic"` after the player switched the interface off, `"hidden"` while it is turned off, and `""` while it is drawn. A page that plays sound or animation can pause it meanwhile. ## Syntax ```js KcdMp.onVisibility(fn) ``` | Parameter | Type | | |---|---|---| | `fn` | `(visibility: KcdMpVisibility) => void` | called with the state | ## Returns `() => void` - a function that takes the handler off again ## KcdMpVisibility Whether the interface is drawn, and if not, why. | Field | Type | | |---|---|---| | `visible` | `boolean` | `true` while the interface is drawn over the game | | `reason` | `"loading" \| "menu" \| "game_screen" \| "panic" \| "hidden" \| ""` | why it is not drawn: `"loading"` (a level is loading), `"menu"` (a game menu is open), `"game_screen"` (one of the game's own screens is up), `"panic"` (the player switched the interface off), `"hidden"` (it is turned off), and `""` while it is drawn | ## Example ```js KcdMp.onVisibility(({ visible }) => { document.body.classList.toggle("paused", !visible); }); ``` ## See also [KcdMp.feed.visibility.subscribe](/js/web/handlers/feed-visibility-subscribe/) · [KcdMp.focus](/js/web/functions/focus/) · the [Focus and visibility](/js/web/#focus-and-visibility) group of the index # KcdMp.api > The version of the page API the game runs. The version of the page API the game runs. A whole number, `1`. It moves only when something a page relies on changes in a way old pages cannot follow; the interface's `ui.toml` says which version it was written for (`api = 1`), and a page can check the one it actually got. ## Syntax ```js KcdMp.api ``` ## Type `number` ## Example ```js if (KcdMp.api < 1) { document.body.textContent = "This page needs a newer game."; } ``` ## See also [KcdMp.env](/js/web/properties/env/) · the [The page](/js/web/#the-page) group of the index # KcdMp.contract > The contract this frame implements, when it draws one of the game's interface components. The contract this frame implements, when it draws one of the game's interface components. A frame can take over one of the game's own interface components - the chat, the scoreboard - from the standard drawing; the component's **contract** is the list of events and actions such a frame must serve ([`KcdMp.onComponent`](/js/web/handlers/oncomponent/)). This is its name and version (`"chat@1"`), or `""` for a frame that draws no component. It changes when the game mode gives the frame a component or takes it away - [`KcdMp.onComponent`](/js/web/handlers/oncomponent/)'s `"assign"` says when. ## Syntax ```js KcdMp.contract ``` ## Type `string` ## Example ```js if (KcdMp.contract === "") { KcdMp.log.info("an ordinary frame"); } ``` ## See also [KcdMp.env.component](/js/web/properties/env-component/) · [KcdMp.call](/js/web/functions/call/) · the [The page](/js/web/#the-page) group of the index # KcdMp.env > What the page knows about where it runs. What the page knows about where it runs. A small read-only object: whether the game or a browser runs the page, the size of its screen, the player's language and whether the game is in developer mode. Its members have their own pages. ## Syntax ```js KcdMp.env ``` ## Example ```js const { screen, locale } = KcdMp.env; document.title = locale + " " + screen.w + "x" + screen.h; ``` ## See also [KcdMp.env.mock](/js/web/properties/env-mock/) · [KcdMp.env.screen](/js/web/properties/env-screen/) · [KcdMp.env.locale](/js/web/properties/env-locale/) · the [The page](/js/web/#the-page) group of the index # KcdMp.env.component > The game's interface component this frame draws, if any. The game's interface component this frame draws, if any. The component's name (`"chat"`), or `""` for a frame that draws none. It goes with [`KcdMp.contract`](/js/web/properties/contract/). ## Syntax ```js KcdMp.env.component ``` ## Type `string` ## Example ```js if (KcdMp.env.component === "chat") { startChatWindow(); } ``` ## See also [KcdMp.contract](/js/web/properties/contract/) · the [The page](/js/web/#the-page) group of the index # KcdMp.env.dev > Whether the player's game runs in developer mode. Whether the player's game runs in developer mode. `true` in a game that runs in developer mode, which is not how players run it; a page may show extra diagnostics there. `false` in every other game, and outside the game. ## Syntax ```js KcdMp.env.dev ``` ## Type `boolean` ## Example ```js document.getElementById("debug").hidden = !KcdMp.env.dev; ``` ## See also [KcdMp.env](/js/web/properties/env/) · the [The page](/js/web/#the-page) group of the index # KcdMp.env.locale > The player's language, as a language tag. The player's language, as a language tag. A tag such as `"en-US"` or `"cs"`, the language the player's game runs in. ## Syntax ```js KcdMp.env.locale ``` ## Type `string` ## Example ```js const czech = KcdMp.env.locale.startsWith("cs"); document.documentElement.lang = czech ? "cs" : "en"; ``` ## See also [KcdMp.env](/js/web/properties/env/) · the [The page](/js/web/#the-page) group of the index # KcdMp.env.mock > Whether the page runs outside the game. Whether the page runs outside the game. `true` when the page is opened in a plain browser - while it is being written, or in a test - and not in a player's game. Every call then answers on the spot with a plain reply and [`KcdMp.mock`](/js/web/properties/mock/) drives the page's handlers. ## Syntax ```js KcdMp.env.mock ``` ## Type `boolean` ## Example ```js if (KcdMp.env.mock) { KcdMp.mock.emit("shop.items", { items: [{ id: "bread", price: 4 }] }); // stand in for the game mode } ``` ## See also [KcdMp.mock](/js/web/properties/mock/) · the [The page](/js/web/#the-page) group of the index # KcdMp.env.screen > The size of the page and its display's pixel ratio. The size of the page and its display's pixel ratio. Read it when you need it: every read gives a fresh object with the size at that moment, so a page that adapts to its size reads it again on a `resize` event. ## Syntax ```js KcdMp.env.screen ``` ## Type `KcdMpScreen` ## KcdMpScreen The size of the page and of the display it is drawn on. | Field | Type | | |---|---|---| | `w` | `number` | the page's width in CSS pixels | | `h` | `number` | the page's height in CSS pixels | | `scale` | `number` | the display's pixel ratio (`2` on a display that draws two device pixels per CSS pixel) | ## Example ```js window.addEventListener("resize", () => { const { w, h } = KcdMp.env.screen; document.body.classList.toggle("narrow", w < 900 || h < 500); }); ``` ## See also [KcdMp.env](/js/web/properties/env/) · the [The page](/js/web/#the-page) group of the index # KcdMp.feed > The feeds a page may read. The feeds a page may read. `KcdMp.feed.session` and `KcdMp.feed.visibility` are the two feeds; each has a `subscribe` function. Only the feeds a frame's `feeds` list in `ui.toml` names deliver values. ## Syntax ```js KcdMp.feed ``` ## Example ```js KcdMp.feed.session.subscribe((session) => { document.querySelector("#server").textContent = session.server; }); ``` ## See also [KcdMp.feed.session.subscribe](/js/web/handlers/feed-session-subscribe/) · [KcdMp.feed.visibility.subscribe](/js/web/handlers/feed-visibility-subscribe/) · the [Feeds](/js/web/#feeds) group of the index # KcdMp.frame > The name of this page's frame. The name of this page's frame. The name the interface's `ui.toml` gives the frame the page is drawn in (`"menu"`, `"scoreboard"`) - the name the game mode and the client scripts use for it, and the `source` of the events the page sends. It is `""` outside the game, in mock mode. ## Syntax ```js KcdMp.frame ``` ## Type `string` ## Example ```js KcdMp.log.info("this page is the " + KcdMp.frame + " frame"); ``` ## See also [KcdMp.env.mock](/js/web/properties/env-mock/) · [KcdMp.emitServer](/js/web/functions/emitserver/) · the [The page](/js/web/#the-page) group of the index # KcdMp.mock > The controls that play the game's part when the page runs in a browser. The controls that play the game's part when the page runs in a browser. Present only when [`KcdMp.env.mock`](/js/web/properties/env-mock/) is `true`. Its functions fire the page's own handlers as the game would: [`KcdMp.mock.emit`](/js/web/functions/mock-emit/) for the game mode's events, [`KcdMp.mock.message`](/js/web/functions/mock-message/) for messages, [`KcdMp.mock.feed`](/js/web/functions/mock-feed/) for feeds, [`KcdMp.mock.reply`](/js/web/functions/mock-reply/) for the answers of client scripts, [`KcdMp.mock.key`](/js/web/functions/mock-key/) for the keys the game mode declared, and [`KcdMp.mock.component`](/js/web/functions/mock-component/) for the events of a component the page draws. ## Syntax ```js KcdMp.mock ``` ## Example ```js if (KcdMp.env.mock) { KcdMp.mock.feed("session", { server: "Test server", address: "127.0.0.1:7777", level: "kutnohorsko", playerId: 1, name: "Henry", rtt: 20 }); } ``` ## See also [KcdMp.env.mock](/js/web/properties/env-mock/) · the [Mock mode](/js/web/#mock-mode) group of the index # KcdMp.surface > The surface this page is drawn on - or null in a frame. The surface this page is drawn on - or `null` in a frame. On a surface page, its surface: `name` (the page's name in `ui.toml`), `id` (the entity id the game mode knows it by, so the page can tell the mode which board it is - the mode's surface callbacks are given it too) and the page's size in pixels. Every surface the mode places with this page draws its own copy of it, so `id` is what tells the copies apart. `id` is `0` until the page is ready - read it in a handler, or after [`KcdMp.ready`](/js/web/functions/ready/) answered. `null` in a frame's page and outside the game. ## Syntax ```js KcdMp.surface ``` ## Type `KcdMpSurface | null` ## Example ```js KcdMp.ready().then(() => { if (KcdMp.surface) { KcdMp.log.info(`drawn on surface ${KcdMp.surface.id} (${KcdMp.surface.name})`); } }); ``` ## See also [KcdMp.onSurfaceUse](/js/web/handlers/onsurfaceuse/) · [KcdMp.frame](/js/web/properties/frame/) · the [Pages on surfaces in the world](/js/web/#pages-on-surfaces-in-the-world) group of the index