Skip to content

Resources ship web pages as views; the server keeps all authority ​

Status: Accepted (2026-11-06)

Context ​

The client draws its core interface (chat, dialogs, menus, HUD) natively and starts its embedded browser (Chromium Embedded Framework) only when a resource needs it. Some screens are too rich for the native widgets: a map editor studio, a sign-in screen, a queue, a territory map. For these the client has to get the resource's HTML, CSS and JavaScript, show it isolated from the game and the network, and exchange data with the resource without giving the page any authority.

The game connection is a poor carrier for files: its reliable lane is Go-Back-N and sized for small events, so a download over it would hold up chat messages and deaths until it finishes.

Decision ​

Files ​

  • A resource lists its client files explicitly in resource.toml as [client].files. Paths, allowed extensions with their fixed MIME types and the limits (64 files, 4 MiB per file, 16 MiB per resource) are in Resource client files. A violation refuses the resource's start (fail-closed).
  • On start and reload the server reads the files, hashes them with SHA-256 and keeps the bytes in memory: it serves exactly what it announced, even if the files on disk change. Every start of a resource gets a new generation from a counter shared by the whole server process.
  • HTTP over TCP, by default on the same port number as the game's UDP. A host may choose another TCP port (client_files_port, ADR 0050); ServerHello announces it, and the client downloads from the game session's IP on that port. The server has a minimal built-in HTTP/1.1 server: only GET /<resource>/<path> of declared files of running resources from the in-memory snapshot, no listings, one request per connection, with limits on the request head, timeouts and connections in total and per address. The files are as public as anything every player receives: they hold no secrets.
  • The manifest travels over the authenticated game session. Name, generation, and the path, size and SHA-256 of each file go on the reliable lane (ResourceFiles): on connect for every running resource with client files, to everyone on start or reload, and a removal on stop (ResourceRemove). The HTTP source is therefore untrusted, and the client checks size and hash before using a file. An external CDN base URL is left for when a real consumer needs it; the wire does not carry one.
  • The client caches by content. Files are stored by SHA-256 and written under their hash only after the hash matches, so a changed resource downloads only the files that changed and identical files are shared between servers. The cache is bounded (256 MiB); a manifest that cannot fit fails with download. Downloads start in the background as soon as the manifest arrives, resume interrupted transfers with Range, and back off on failures and on the server's 503; the exact client behavior is under Downloads.

Views ​

  • One view per player, like the dialog: a new view replaces the open one. A view is a page from the resource's client files; the resource chooses the entry page and the input mode (dlmp.open_view, ViewOpen).

  • Isolation. A page runs on its resource's own origin, https://<resource>.dlmp.invalid/, served only from verified cached files of the current generation. Every request outside that origin is canceled; popups, downloads, device permissions (camera, microphone and so on) and JavaScript dialogs are refused. Every response carries a Content Security Policy of the page's own origin without inline scripts. The page cannot open a file dialog or change its origin. Web storage lives only as long as the view and never reaches a resource of the same name on another server or in another session. The page has no access to the game, the network transport or the client's storage; its only channel is the darqlinemp bridge object below, whose calls are limited in size and rate. ADR 0055 adds the one exception: a sign-in view may talk to the server's configured auth sites.

  • Server to page: JSON snapshots by topic. The resource sends a Lua table under a topic name (view:send); the server serializes it to JSON of up to 16 KiB and sends it in parts (ViewData). The client assembles the parts of one topic and revision and hands the page the latest complete snapshot of each topic (darqlinemp.on), latest-wins, including those that arrived before the page loaded. A lost part does not break the view: the next snapshot replaces it.

  • Client theme to page. The client itself gives the page the look of its core interface (font, size, opacity, glow, colors) after load and on every change (darqlinemp.onTheme), without a revision and without any traffic: the client already has the theme (UiTheme), and a page styled like the core interface should not wait for the resource to notice a change.

  • Page to server: typed actions. An action is a name with up to 16 flat fields of type boolean, number or string (darqlinemp.action, ViewAction); the server needs no JSON parser. When opening the view, the resource declares the allowed actions and the schema of their fields. The server accepts an action only from the open, loaded view of that connection, and only by name, schema, ranges, revision and rate limit.

  • Revision (optimistic concurrency). The resource raises the view's revision together with a snapshot of its canonical state. The bridge stamps each action with the revision of the last snapshot the page received; an action with another revision is dropped, so a page never acts on state the resource has already replaced.

  • Status. The client reports that the page loaded, or a failure: download, integrity, no browser in this client build, or a page error (ViewStatus). A failure closes the view; the core native interface keeps working.

  • Input modes.

    • none: display only.
    • full: the view takes the mouse and keyboard while it is open and visible.
    • regions: cursor mode. The mouse no longer controls the camera or weapons. The page names the rectangles of its panels (darqlinemp.setInputRegions); over them the mouse goes to the page, outside them to nobody, or to the editor's tool in an edit session. A drag that starts in a panel keeps the mouse until release. The keyboard belongs to the page only while one of its input fields has focus, and key bindings do not fire then; otherwise it belongs to the game. Esc removes the page's focus.

    While a core widget such as a dialog or the chat input is open above the view, the page receives no input. The game's pause menu hides the view and releases input without closing it.

  • Closing. A view closes when the resource closes it (view:close), when a new view replaces it, when the resource stops or reloads, when the player disconnects, or when the client reports a failure. Each of these releases input and destroys the page. A replaced or failed view calls the resource's on_close with the reason (replaced, download, integrity, unavailable, page); a view the resource closed itself, a stop and a departure call back nothing.

Consequences ​

  • New packets on the reliable lane and a public Lua API: dlmp.open_view and the view handle.
  • A client built without the browser still accepts manifests but downloads nothing and answers every view with "no browser in this client build".
  • No gameplay logic runs on the client: the page is presentation only, and every decision stays with the resource on the server.

Alternatives considered ​

  • Files over the game connection's reliable lane. Discrete events would wait until a download finishes. Rejected.
  • A mandatory separate TCP port. Every host would have to open a second port number for no gain. Rejected; a separate number is optional for hosts who need it (the port is taken, firewall rules), and by default the number is the same.
  • Several layered views. Z-order and input routing between views have no consumer yet. Deferred until a real case.
  • A custom binary tree format both ways, or JSON both ways. Either more codec code, or a JSON parser with an indirect schema on the server. Rejected in favor of JSON down and typed flat fields up.
  • A cache keyed by resource path. Changing one file, or the same file on two servers, would download everything again. Rejected.

Documentation licensed under CC BY 4.0. DarqlineMP is not affiliated with Rockstar Games or Take-Two Interactive. Grand Theft Auto is a trademark of Take-Two Interactive Software, Inc.