Maps and content
Map format
A map is a Tiled document plus a layout of our own. The Tiled document draws the map: tiles, layers, collisions and text. The layout holds what people interact with: areas and their behaviours, placed entities, room settings and metadata. Authors draw in Tiled, upload a package, and place or change the rest with the editor in the game.
This page describes version 1. The files in schema/ are its machine-readable half:
| File | Contents |
|---|---|
schema/manifest.schema.json | JSON Schema of the package manifest |
schema/tiled.schema.json | JSON Schema of a Tiled document with the conventions below |
schema/layout.schema.json | JSON Schema of the layout document and the property vocabulary |
schema/problem.schema.json | JSON Schema of the error document the upload returns |
schema/tiled-types.json | the custom types to import into Tiled |
go test ./schema checks that the reference maps and the examples on this page are valid, that each rule in the error tables rejects a broken document, and that the custom types match the vocabulary.
Packages
A package is a zip archive that holds one map:
manifest.json required
plaza.tmj the Tiled document, at the root
layout.json an optional layout document, at the root
tiles.png tileset images, sounds, documents and other assets, in any folder
sounds/chime.wav
Manifest
{ "version": 1, "map": "plaza.tmj", "layout": "layout.json" }
| Field | Meaning |
|---|---|
version | package format version, 1 |
map | file name of the Tiled document at the root, ending in .tmj |
layout | file name of the layout document at the root, ending in .json; optional |
Files
| Kind | Extensions |
|---|---|
| Tiled document | .tmj |
| JSON | .json |
| Images | .png, .jpg, .jpeg, .webp, .gif, .svg |
| Sounds | .mp3, .ogg, .wav |
| Documents | .pdf |
| Scripts | .js, .mjs |
Paths use letters, digits, spaces and ._()+-, with / between folders. The upload skips files and folders whose name starts with a dot, the __MACOSX folder, and Tiled's .tiled-project and .tiled-session files. Every other file needs one of the extensions above. Tiled's XML formats and its external tileset and template files are refused with a hint: save the map as JSON, embed the tilesets and detach the templates.
Limits
| What | Limit |
|---|---|
| Upload | 100 MiB |
| Unpacked | 256 MiB and 1,000 files |
| One file | 32 MiB; 4 MiB for the layout, 64 KiB for the manifest |
| Path | 255 bytes, 8 folders deep |
| Map | 512 × 512 tiles |
| Tileset image | 4,096 pixels a side |
| Layout | 1,000 areas, 5,000 entities, 16 properties per area or entity, 50 collections |
The upload measures the bytes it unpacks, not the sizes the archive claims. Tenant quotas apply on top.
Upload
Admins of a tenant upload a package with POST /api/v1/maps to create a map, or with PUT /api/v1/maps/{map}/package to replace its package, as the multipart field package; so does an API key of the tenant with the scope maps:write (Partner API), sent as the bearer token to one of the tenant's hosts, which suits a build pipeline: no person's session expires under it. Each upload goes to the audit log as map.created or map.replaced, with the admin or the key as its actor. An Idempotency-Key header makes a repeated upload answer what the first one did, for a day. Authors push a folder or a zip archive from the command line:
vws maps push --url https://acme.example.com --token "$TOKEN" ./plaza
vws maps push --url https://acme.example.com --key "$KEY" --map <map id> ./plaza
--token is an access token of an admin of the site's tenant, and --key an API key with maps:write; without either, the command takes VWS_API_KEY, else VWS_ACCESS_TOKEN. The upload:
- The Tiled document is validated and its areas are imported into the layout, as Areas drawn in Tiled describes.
- A new map without a layout document gets a layout made of the imported areas, with metadata from the Tiled map properties.
- A package with a layout document replaces the stored layout. Areas in it whose id starts with
tiled-are replaced by the import, so a downloaded package uploads again unchanged. - A replacement without a layout document keeps the stored layout: areas made in the game stay, imported areas are replaced, and the Tiled map properties
name,description,copyrightandthumbnailoverwrite the metadata fields they set. - The package's files replace the map's files, and rooms using the map reload it: each sends its members a notice of kind
NOTICE_KIND_REFRESH_REQUIRED.
An error rejects the whole package; warnings come back with the result. Both use the error document.
GET /api/v1/maps lists the tenant's maps, newest first, for its admins; every map the API answers, from the listing, GET /api/v1/maps/{map} or an upload, names the rooms that use it with their paths, so that the administration shows what a replacement reloads.
Storage
A map's files live in object storage under tenants/<tenant id>/maps/<map id>/<version>/, where the version is the first 16 hexadecimal digits of a SHA-256 over the package's paths and contents. A folder therefore never changes, and GET /api/v1/maps/{map}/files/<version>/<path> serves its files as cacheable for a year. The room snapshot's tiled_url names the current version's Tiled document, whose relative paths resolve inside the same folder. A replacement keeps the previous version for the clients still loading it and deletes older ones. Every stored file has a file_object row of kind map_asset, deleted with the file. GET /api/v1/maps/{map}/layout serves the layout with an ETag that changes with the map.
layout.unknown-prefab and layout.unknown-collection check a layout against the tenant's entity catalogue.
Tiled documents
Tiled is a third-party editor with an open JSON format. The format accepts what Tiled 1.9 and later save, with these conventions.
Map
- Orthogonal and finite, with tiles of 32 × 32 pixels, at most 512 × 512 tiles.
- Tile layers saved as CSV, which Tiled writes as a JSON array; base64 and compression are refused.
- Tilesets embedded in the map, each based on one image. Tiles larger than the grid sit on the bottom-left corner of their cell, as in Tiled.
- No object templates: detach template instances before saving.
Layers
- Group layers flatten into
group/childnames, and children inherit the group's opacity, visibility and offset. Names should be unique after flattening, because scripts address layers by name. - Exactly one top-level object layer is named
avatars, and it stays empty. Layers before it draw under avatars, layers after it draw over them. - Hidden layers load but neither draw nor collide; scripts can show them.
- Image layers are not drawn.
Collisions
A cell blocks avatars when a visible tile layer has a tile in it whose tileset tile sets the property collides to true, or when the tile layer itself sets collides to true. A collision layer that nobody sees is a visible tile layer with collides and an opacity of 0. The layout's entities block the cells their prefabs' collision grids cover (Entities). Players never collide with each other.
Objects
| Object | Meaning |
|---|---|
Rectangle of class area | an area, see below |
| Text | drawn with its font, size, colour, wrapping and alignment; a string property font-family replaces the font with a CSS font list |
| Tile object | drawn in its layer |
| Anything else | ignored |
Tiled 1.10 and later save an object's class as type, Tiled 1.9 as class; both work.
Areas drawn in Tiled
An area is a rectangle of class area, not rotated. Its name is the area's name. Each behaviour is a custom property whose type is the behaviour's class from the vocabulary:
{
"id": 8,
"name": "meeting-room",
"type": "area",
"x": 96,
"y": 96,
"width": 160,
"height": 128,
"rotation": 0,
"visible": true,
"properties": [
{ "name": "capacity", "type": "class", "propertytype": "capacity", "value": { "max": 6 } },
{ "name": "meeting", "type": "class", "propertytype": "meeting", "value": { "name": "plaza-meeting", "hostTags": "staff, facilities" } }
]
}
- The class members are the behaviour's fields, with the same names and defaults.
- The property's name becomes the behaviour's id, so it follows the id rules.
- A list of tags is text, with the tags separated by commas.
- A colour may be
#RRGGBBor Tiled's#AARRGGBB; the alpha is dropped. - A module area's settings are JSON text, such as
{"title": "Welcome"}; empty text sets none. - Tiled does not save a member that equals its default. A required field therefore has no useful default in Tiled, and leaving it unset is an error.
Import schema/tiled-types.json with Tiled's Custom Types Editor to get the class area and every behaviour class with its members and defaults.
The upload copies each area into the layout with the id tiled- followed by the object id, its coordinates rounded to whole pixels. The area above becomes:
{
"id": "tiled-8",
"name": "meeting-room",
"x": 96,
"y": 96,
"width": 160,
"height": 128,
"properties": [
{ "id": "capacity", "type": "capacity", "data": { "max": 6 } },
{ "id": "meeting", "type": "meeting", "data": { "name": "plaza-meeting", "hostTags": ["staff", "facilities"] } }
]
}
Imported areas belong to the Tiled document: the editor in the game shows them but cannot change them, and the next upload replaces them.
Map, tileset and tile properties
| On | Property | Type | Meaning |
|---|---|---|---|
| Map | name | string | the metadata name |
| Map | description | string | the metadata description |
| Map | copyright | string | credits shown with the room's information |
| Map | thumbnail | file or string | path of an image in the package |
| Map | script | string | the map's scripts, one per line: scripts of the package, named relative to the Tiled document, or https URLs, at most 8 (Scripting API) |
| Map | any name | class variable | a room variable, see below |
| Tileset | copyright | string | credits for the tileset |
| Tile | collides | bool | the tile blocks avatars |
| Tile | name | string | a name scripts can place the tile by (Scripting API) |
| Tile layer | collides | bool | every tile of the layer blocks avatars |
The upload warns about every other property and ignores it.
Variables
A map property of class variable declares a room variable. The property's name is the variable's name: letters, digits and _.-, starting with a letter or _.
| Field | Default | Meaning |
|---|---|---|
default | null | the initial value, as JSON text |
persist | false | the value is kept in the database; otherwise it lasts until the room empties |
readTags | empty | tags that may read the variable; empty lets everyone read it |
writeTags | empty | tags that may change the variable; empty lets everyone change it |
Scripts can only set declared variables. The tags are those of a player's membership of the room's world; the realtime protocol describes how variables are read, written and kept.
Layout documents
{
"version": 1,
"areas": [
{
"id": "6f1d2c1e-5a43-4c0e-9d7a-3b8e2f4a9c10",
"name": "kiosk",
"x": 704,
"y": 544,
"width": 64,
"height": 64,
"properties": [
{ "id": "kiosk-website", "type": "website", "data": { "url": "https://example.com/kiosk", "trigger": "action" } },
{ "id": "kiosk-hint", "type": "tooltip", "data": { "text": "Press space for the kiosk" } }
]
}
],
"entities": [
{
"id": "3d0c3a43-91a4-4f0b-8a51-2f3a6c1b9e70",
"prefab": { "collection": "lounge", "id": "0f8e7d6c-5b4a-4c3d-8e2f-1a0b9c8d7e6f" },
"x": 512,
"y": 320,
"properties": [
{ "id": "radio-music", "type": "audio", "data": { "url": "sounds/lounge.mp3", "loop": true, "radius": 160, "shared": true } }
]
}
],
"collections": ["lounge"],
"settings": { "broadcast": { "enabled": true, "title": "Town hall", "tags": ["staff"] } },
"metadata": { "name": "Plaza", "thumbnail": "thumbnail.png", "tags": ["reference"] }
}
| Field | Meaning |
|---|---|
version | 1 |
areas | areas with their behaviours |
entities | placed entities |
collections | ids of the entity collections the editor offers for this map; empty offers every collection of the tenant |
settings | room settings |
metadata | how the map presents itself |
Every field but version is optional. Coordinates are whole map pixels from the top-left corner.
Ids
Areas, entities and properties have ids of 1 to 64 letters, digits and _.-, starting with a letter or a digit. Area and entity ids are unique in the layout, and property ids within their area or entity. The editor creates UUIDs; hand-written layouts may use readable ids. Ids that start with tiled- belong to areas imported from Tiled.
Areas
| Field | Meaning |
|---|---|
id | see Ids |
name | up to 100 characters; entry points use it, and players see it when they enter |
x, y, width, height | the rectangle, inside the map |
properties | behaviours from the vocabulary |
Entities
| Field | Meaning |
|---|---|
id, name | as for areas |
prefab | collection and id of the prefab, as the entity catalogue names them |
x, y | the top-left corner; the image, the size, the collision cells and the sorting against avatars come from the prefab (Entities) |
properties | website, file, audio, meeting and description behaviours, which act when a player activates the entity |
A player reaches an entity standing within 48 pixels of its image, and activates it there with the action key or a click on it, whatever the triggers of its behaviours. Its websites and files open as an area's do and close once the player walks out of reach, and its meeting holds the player until then. Its sounds fade to silence over their radius as the player walks away and stop beyond it; without a radius, a sound plays at full volume until the player walks out of reach. A shared sound plays for everyone in the room whom its radius reaches, or for everyone without a radius. An entity that an access area hides from a player does not act for it.
Properties
A property is { "id": ..., "type": ..., "data": { ... } }, where type names a behaviour of the vocabulary and data holds its fields. Fields left out take their defaults.
Settings
| Field | Default | Meaning |
|---|---|---|
broadcast.enabled | false | people allowed to broadcast get the broadcast control, and everyone the broadcast reaches sees and hears them while they broadcast |
broadcast.title | empty | the broadcast's name, up to 100 characters |
broadcast.scope | room | room reaches this room, world every room of the world |
broadcast.tags | empty | tags allowed to broadcast, besides editors |
broadcast.audienceVideo | false | speakers see the listeners' cameras |
broadcast.notificationSound | none | an asset played when a broadcast starts, instead of the built-in sound |
broadcast.notifications | true | play that sound |
recording.tags | empty | tags allowed to record the spaces of the room, besides editors, where the room offers the recording feature |
recording.sounds | true | a chime for everyone in a recorded space when its recording starts and stops |
access.editTags | empty | tags allowed to edit areas and entities anywhere in the map, besides editors |
Metadata
| Field | Default | Meaning |
|---|---|---|
name | empty | display name, up to 100 characters |
description | empty | up to 2,000 characters |
copyright | empty | credits, up to 2,000 characters |
thumbnail | none | an asset shown in room lists |
tags | empty | tags for search and room lists |
Assets
An asset field takes a path in the package or an absolute http or https URL. Paths are served from the map's files. website.url and embed.url take URLs only, because a page served from a package would run on the product's own origin.
Vocabulary
| Type | On | Behaviour |
|---|---|---|
start | areas | a spawn point and an entry point |
exit | areas | switches rooms |
website | areas, entities | opens a web page in a panel or a tab |
embed | areas | shows a web page on the map |
file | areas, entities | opens a document or an image |
audio | areas, entities | plays a sound |
meeting | areas, entities | joins a meeting |
speaker | areas | broadcasts to a stage's listeners |
listener | areas | follows a stage's broadcast |
silent | areas | keeps players out of proximity bubbles |
focus | areas | fits the camera to the area |
highlight | areas | darkens everything outside the area |
tooltip | areas | shows a text above the player |
description | areas, entities | describes it in the map explorer |
chat | areas | a chat for the people inside |
access | areas | limits who may enter and edit |
lock | areas | lets players lock the area |
capacity | areas | limits how many players fit |
desk | areas | a desk a member can own |
module | areas | a kind of area that a module adds |
An area or entity holds any number of website, file and audio properties and at most one of each other type. start and exit exclude each other, and so do meeting, speaker and listener.
Triggers decide when a behaviour acts: enter when the player enters the area, action when the player presses the action key after a prompt, and icon by adding the panel collapsed, as an icon. A user setting turns enter into action for websites and files. On entities, behaviours act when the player activates the entity, whatever the trigger.
start
| Field | Default | Meaning |
|---|---|---|
default | false | a join without an entry point starts here |
exit
| Field | Default | Meaning |
|---|---|---|
room | this room | a room slug of the same world, or /world/room for another world of the tenant |
start | the default start | the name of a start area in that room |
An exit needs room, start or both.
website
| Field | Default | Meaning |
|---|---|---|
url | required | http or https URL of the page |
trigger | enter | enter, action or icon |
actionMessage | empty | the prompt for the action trigger, up to 200 characters |
newTab | false | open a browser tab instead of a panel |
width | 50 | width of the panel, percent of the screen |
closable | true | the player may close the panel |
allowApi | false | the page may use the scripting API (Scripting API) |
policy | fullscreen; autoplay; clipboard-write; encrypted-media; picture-in-picture | the iframe's allow attribute |
hideUrl | false | the panel hides the page's address |
The panel closes when the player leaves the area.
embed
Shows the page inside the area's rectangle on the map, whether anyone stands there or not.
| Field | Default | Meaning |
|---|---|---|
url | required | http or https URL of the page |
allowApi | false | the page may use the scripting API (Scripting API) |
policy | as for website | the iframe's allow attribute |
file
| Field | Default | Meaning |
|---|---|---|
url | required | the asset to show, such as a PDF or an image |
name | required | the file's display name, up to 200 characters |
trigger, actionMessage, newTab, width, closable | as for website |
audio
| Field | Default | Meaning |
|---|---|---|
url | required | the asset to play |
volume | 1 | from 0 to 1 |
loop | false | play again at the end |
trigger | enter | enter or action |
actionMessage | empty | the prompt for the action trigger |
radius | 0 | on entities, pixels over which the sound fades to silence; 0 keeps it at full volume |
shared | false | on entities, everyone within radius hears a sound one player starts |
In an area, the sound stops when the player leaves.
meeting
| Field | Default | Meaning |
|---|---|---|
name | empty | areas of the room with the same name share one meeting; empty gives the area its own |
trigger | enter | enter or action |
actionMessage | empty | the prompt for the action trigger |
audioMuted | false | join with the microphone off |
videoMuted | false | join with the camera off |
chat | true | the meeting has a chat |
raiseHand | true | players can raise a hand |
hostTags | empty | tags whose players host the meeting: they can mute and remove people |
provider | none | an outside meeting tool that holds the meeting instead of the platform: zoom, teams, meet or other |
link | empty | the meeting's https address in that tool, up to 2000 characters; required with a provider, empty without one |
With a provider, the meeting takes place in the outside tool. The room starts no meeting of its own there, its players stay out of bubbles as in any meeting, and the action key opens link in a new browser tab, whatever the trigger, since browsers open a tab only on a gesture of the player's. A player who opened it shows as in a meeting while it stands in the area. A zoom link's host is zoom.us, zoomgov.com or one of their subdomains, a teams link's teams.microsoft.com or teams.live.com, and a meet link's meet.google.com, written in lower case as browsers write them; other takes any https address.
A meeting scheduled in a meeting area with an outside link (Identity) takes the area over the same way from 15 minutes before its start until its end, and the platform's meeting returns afterwards. The room tells its players the link as the meeting property's link state, checks every minute which scheduled meetings run, and learns at once of a meeting in an outside tool scheduled or cancelled.
speaker
| Field | Default | Meaning |
|---|---|---|
stage | required | the broadcast's name, up to 100 characters |
chat | false | the broadcast has a chat |
showAudience | false | speakers see who listens |
listener
| Field | Default | Meaning |
|---|---|---|
stage | required | the broadcast to follow |
chat | false | listeners can use the broadcast's chat |
canSpeak | false | listeners can talk to the speakers |
raiseHand | true | listeners can raise a hand |
waitingUrl | none | an asset shown while no speaker is live |
Speaker and listener areas with the same stage form one broadcast: the players in a speaker area broadcast to those in its listener areas, who see and hear them while one speaks and the waitingUrl while nobody does. Players in either area stay out of bubbles; their status shows as broadcasting or listening.
silent
Players inside show as silent and never join proximity bubbles. It has no fields.
focus
| Field | Default | Meaning |
|---|---|---|
margin | 0 | space kept around the area, as a fraction of its size |
highlight
| Field | Default | Meaning |
|---|---|---|
opacity | 0.6 | opacity of the darkness outside the area |
edgeWidth | 10 | width of the soft edge, in pixels |
duration | 250 | fade-in time, in milliseconds |
color | #000000 | colour of the darkness |
tooltip
| Field | Default | Meaning |
|---|---|---|
text | required | up to 500 characters |
duration | 5000 | milliseconds; 0 keeps the text until the player leaves the area |
description
| Field | Default | Meaning |
|---|---|---|
text | required | up to 2,000 characters |
searchable | false | the map explorer's search finds it |
chat
| Field | Default | Meaning |
|---|---|---|
channel | required | the chat's name. Players inside share the chat, which keeps its history while anyone is there; when the area empties, the chat is soft-deleted |
open | false | open the chat panel on entering |
access
| Field | Default | Meaning |
|---|---|---|
enterTags | empty | tags that may enter; empty lets everyone enter |
editTags | empty | tags that may place, move and remove the entities wholly inside, as large as their prefabs; empty leaves it to editors |
lock
| Field | Default | Meaning |
|---|---|---|
tags | empty | tags that may lock and unlock the area; empty lets everyone inside do it |
capacity
| Field | Default | Meaning |
|---|---|---|
max | required | players who fit, from 1 to 10,000 |
desk
| Field | Default | Meaning |
|---|---|---|
claim | self | self lets a member claim a free desk; with assigned, editors assign it |
claimTags | empty | tags that may claim the desk; empty lets every member claim it |
module
An area kind of a module the tenant added (Extensions). A player who enters the area opens the module's page for it in the side panel, where the room turns the module on; elsewhere the area does nothing.
| Field | Default | Meaning |
|---|---|---|
module | the id of the module | |
type | the area kind, as the module's manifest names it | |
settings | empty | the values of the kind's settings, at most 64, which the module's schema describes and the editor checks |
Runtime rules
Players and areas
- A player's position is the point under the avatar's feet, in map pixels. A player is in an area when
x ≤ px < x + widthandy ≤ py < y + height. - Every area that contains the player applies its behaviours. Where several do,
focusandhighlightcome from the smallest, and from the later one in the layout when sizes are equal; so does the meeting or stage the player talks in, of itsmeeting,speakerandlistenerareas, so that a speaker area inside a meeting room is a stage while the player stands in it. - Entering an area that has a name shows the name briefly.
- Editors are members with the editor, admin or owner role. They pass every tag check of this page.
Start position
The first of these that applies places a joining player:
- A resumed session keeps its position.
- An entry point, from an exit or the URL fragment (
#name), picks a start area with that name, at random when several share it; an exit withoutstartuses the rules below from step 3. - The position where the player last left this room, when it was saved less than an hour ago and can still be entered.
- The player's own desk.
- A start area with
default, at random. - Any start area, at random.
- The centre of the map.
The server saves each account's position per room in Postgres whenever the player leaves the room, and keeps it until the account or the room is deleted.
In an area, the player lands on a random walkable point at least 16 pixels from its edges, or on the middle of a side too short for that; an area without a walkable point is passed over. A blocked centre of the map gives way to the walkable cell nearest to it. Walkable means free of the Tiled document's collisions and of the cells the layout's entities block, as their prefabs' collision grids mark them (Entities); a player who returns within the hour to a point an entity now covers enters by these rules instead.
The server applies steps 2, 3, 5, 6 and 7, from the start area that Join.spawn names; resume brings step 1, and desks step 4. A join through an exit without start looks the same as any other join in protocol v1, so it starts from step 3.
Exits
Walking into an exit area switches rooms without reloading the page and places the player at the exit's start. An exit without room, or to the current room, moves the player to that start area. When the player may not enter the destination, a notice says so and the player stays. Standing in an exit area when the room loads does not count as walking into it.
Access, locks and capacity
- An
accessarea blocks players without one of itsenterTagslike a wall, and they do not see the entities whose centre it holds. - The people in a conversation can lock it: those inside a
meetingarea, the owner of a desk, and players inside alockarea with one of itstags. They lock and unlock with the action key. A locked area blocks players outside it, while players inside may stay, and the players in any area of a locked meeting walk between its areas; guests with a valid link to a scheduled meeting still enter its room (Identity). - A
capacityarea blocks players outside it whilemaxplayers are inside. - A blocked entry flashes the area and shows a notice. The client keeps the player out like a wall; a move the room refuses all the same, from a client that had not heard of a lock yet, returns the player to where the room keeps it.
Room-wide state of a property travels in the realtime protocol's AreaState, keyed by area id, property id and key:
| Property | Key | Value | Set by |
|---|---|---|---|
lock, meeting, desk | locked | boolean | the people allowed to lock, with SetAreaState |
capacity | occupants | number | the server |
desk | owner | user id, or null | the server |
meeting | link | the outside link of a scheduled meeting under way, or null | the server |
Behaviours in the client
The client applies what concerns only the player: it opens websites and files in a panel or a browser tab and closes the panel when the player leaves, plays sounds, joins meetings with the action key where they wait for it, opens the outside tools of meetings in a browser tab, shows silence, tooltips and the names of areas, fits the camera to focus areas, darkens the map round highlight areas and shows embeds over the map. Space is the action key.
Desks
A desk belongs to at most one member, who returns there after an absence of more than an hour, can walk back to it, and can lock it for a private meeting. Guests and anonymous users cannot own desks. Ownership is stored apart from the layout, so copying a map does not copy claims, and claims survive uploads as long as the area and property ids stay the same.
Errors
The upload answers an invalid package with an RFC 9457 problem whose issues list every finding: status 413 when the upload is too large, 422 otherwise. A valid package's result lists its warnings the same way.
{
"type": "about:blank",
"title": "Unprocessable Content",
"status": 422,
"detail": "The package was refused. Errors: 2, warnings: 1.",
"issues": [
{ "severity": "error", "code": "tiled.finite", "file": "plaza.tmj", "pointer": "/infinite", "message": "Infinite maps are not supported." },
{ "severity": "error", "code": "layout.website", "file": "plaza.tmj", "pointer": "/layers/7/objects/3/properties/2/value", "message": "The website behaviour needs a url." },
{ "severity": "warning", "code": "tiled.unknown-property", "file": "plaza.tmj", "pointer": "/properties/6", "message": "The property mood is not part of the format and is ignored." }
]
}
| Field | Meaning |
|---|---|
severity | error rejects the package, warning does not |
code | one of the codes below |
file | path of the file in the package, when the issue concerns one |
pointer | JSON pointer to the value in that file |
message | an explanation for people in the language of the request's Accept-Language, English when the server speaks none of them; programs use code |
A failed schema rule has the code <document>.<rule>, where the rule is the schema's $defs entry that holds the failed keyword, or document for the top level. A rule that requires an array to contain an item is reported once, at the array. Behaviours of areas drawn in Tiled fail with the layout code of their type. The upload performs the checks marked "upload", which need more than one document or the files themselves.
Package and manifest
| Code | Checked by | Rule |
|---|---|---|
package.too-large | upload | the upload is larger than 100 MiB; status 413 |
package.not-zip | upload | the upload is not a zip archive |
package.unpacked-size | upload | the files add up to more than 256 MiB |
package.too-many-files | upload | the package holds more than 1,000 files |
package.file-size | upload | a file is larger than its limit |
package.path | upload | a path is absolute, has an empty, . or .. segment, uses other characters than letters, digits, spaces and ._()+-, is longer than 255 bytes or deeper than 8 folders |
package.duplicate-path | upload | two paths differ only in case |
package.file-type | upload | a file has an extension the format does not accept |
package.content | upload | a file's content does not match its extension |
package.manifest | upload | there is no manifest.json at the root, or it is not JSON |
package.missing-file | upload | the manifest, the Tiled document or the layout names a file the package lacks |
manifest.document | schema | the manifest is not an object with version 1 and map, or has other fields |
manifest.tiled-file | schema | map is not a file name at the root ending in .tmj |
manifest.layout-file | schema | layout is not a file name at the root ending in .json, or is manifest.json |
Tiled document
| Code | Checked by | Rule |
|---|---|---|
tiled.json | upload | the Tiled document is not JSON |
tiled.document | schema | the document is not an object, or has no tileset |
tiled.map | schema | the document is not a Tiled map, or lacks a field every map has |
tiled.orthogonal | schema | the map is not orthogonal |
tiled.finite | schema | the map is infinite |
tiled.tile-size | schema | the map's tiles are not 32 × 32 pixels |
tiled.map-size | schema | the map is larger than 512 × 512 tiles |
tiled.avatars-layer | schema | there is no top-level object layer named avatars, there are several, or it holds objects |
tiled.layer | schema | a layer has an unknown type or no name, or an object or group layer lacks its contents |
tiled.tile-data | schema | a tile layer is not saved as CSV |
tiled.object | schema | an object is not a JSON object |
tiled.template | schema | an object is a template instance |
tiled.area-shape | schema | an area is not an unrotated rectangle with a positive size |
tiled.behaviour | schema | a behaviour of an area has no object value |
tiled.singletons | schema | an area has behaviours that exclude each other |
tiled.property | schema | a custom property lacks its name, type or value |
tiled.typed-property | schema | a property the format reads has the wrong type, such as a collides that is not a bool |
tiled.variable | schema | a variable has a malformed name or fields |
tiled.tileset | schema | a tileset is not a JSON object |
tiled.embedded-tileset | schema | a tileset is external instead of embedded |
tiled.image-tileset | schema | a tileset is not based on one image |
tiled.tags | schema | a tag list is not tags separated by commas |
tiled.color | schema | a colour is not #RRGGBB or #AARRGGBB |
tiled.layer-size | upload | a tile layer's size differs from the map's, or its data does not hold one tile per cell |
tiled.gid | upload | a tile refers to no tile of the map's tilesets |
tiled.tileset-image | upload | a tileset image is larger than 4,096 pixels a side, or its size differs from the tileset's imagewidth and imageheight |
tiled.area-bounds | upload | an area reaches outside the map |
tiled.variable-default | upload | a variable's default is not JSON |
tiled.scripts | upload | the script property lists more than 8 scripts, or one that is neither a script of the package nor an https URL |
tiled.unknown-property | upload | a warning: the format ignores a property, which may be a typo or belong to another tool |
tiled.image-layer | upload | a warning: image layers are not drawn |
Layout document
| Code | Checked by | Rule |
|---|---|---|
layout.json | upload | the layout document is not JSON |
layout.document | schema | the document is not an object with version 1, or has other fields |
layout.area | schema | an area lacks a field, has another field, or has a coordinate, size or name out of range |
layout.entity | schema | an entity lacks a field, has another field, or has a coordinate or name out of range |
layout.prefab | schema | a prefab reference lacks its collection or id |
layout.property | schema | a property lacks its id, type or data, or has an unknown type |
layout.entity-property | schema | an entity has a type of property entities do not support |
layout.singletons | schema | an area or entity has properties that exclude each other |
layout.start | schema | the data of a start property does not match its fields |
layout.exit | schema | the data of an exit property does not match its fields |
layout.website | schema | the data of a website property does not match its fields |
layout.embed | schema | the data of an embed property does not match its fields |
layout.file | schema | the data of a file property does not match its fields |
layout.audio | schema | the data of an audio property does not match its fields |
layout.meeting | schema | the data of a meeting property does not match its fields |
layout.speaker | schema | the data of a speaker property does not match its fields |
layout.listener | schema | the data of a listener property does not match its fields |
layout.silent | schema | a silent property has data |
layout.focus | schema | the data of a focus property does not match its fields |
layout.highlight | schema | the data of a highlight property does not match its fields |
layout.tooltip | schema | the data of a tooltip property does not match its fields |
layout.description | schema | the data of a description property does not match its fields |
layout.chat | schema | the data of a chat property does not match its fields |
layout.access | schema | the data of an access property does not match its fields |
layout.lock | schema | the data of a lock property does not match its fields |
layout.capacity | schema | the data of a capacity property does not match its fields |
layout.desk | schema | the data of a desk property does not match its fields |
layout.module | schema | the data of a module property names no module id or area kind, or holds more than 64 settings |
layout.settings | schema | settings has an unknown section |
layout.broadcast-settings | schema | settings.broadcast has an unknown or malformed field |
layout.recording-settings | schema | settings.recording has an unknown or malformed field |
layout.access-settings | schema | settings.access has an unknown or malformed field |
layout.metadata | schema | metadata has an unknown or malformed field |
layout.id | schema | an id breaks the id rules |
layout.room | schema | an exit's room is not a room slug or /world/room |
layout.http-url | schema | a URL is not an absolute http or https URL |
layout.asset | schema | an asset is neither a path in the package nor an http or https URL |
layout.tags | schema | a tag list holds a malformed or repeated tag |
layout.color | schema | a colour is not #RRGGBB |
layout.duplicate-id | upload | two areas or entities share an id, or two properties of one area or entity do |
layout.area-bounds | upload | an area reaches outside the map |
layout.entity-bounds | upload | an entity's position lies outside the map |
layout.unknown-prefab | upload | an entity's prefab is not in the tenant's collections |
layout.unknown-collection | upload | collections names a collection the tenant cannot use |
layout.asset-type | upload | an asset path names a file of the wrong kind, such as an image for audio |
layout.lonely-listener | upload | a warning: a listener's stage has no speaker area |
layout.insecure-url | upload | a warning: an http URL, which browsers block on https sites |
layout.replaced-area | upload | a warning: the layout held imported areas that differ from the Tiled document's, and the upload replaced them |
Versions
- The manifest and the layout carry a
version, which changes only with a breaking change. The Tiled conventions follow the manifest's version, and stored layouts record theirs inmap.layout_version. - An addition keeps the version: a new behaviour, a new optional field with a default, a new setting. Servers reject fields and types they do not know, so a package that uses an addition needs a server that has it.
- A breaking change raises the version: a field renamed or removed, or given another meaning or default. Servers then accept packages of earlier versions and migrate them on upload, migrate stored layouts in the release that brings the change, and serve only the current version.
- Each migration is listed below with test fixtures of both versions, and
schema/keeps the schemas of every version still accepted.
There are no migrations yet.
Found a mistake? Tell us.