Files
chat_grid/docs/item-schema.md

9.1 KiB

Item Schema

World Item (server-authoritative)

{
  "id": "string",
  "type": "radio_station | dice | wheel | clock | widget | piano",
  "title": "string",
  "x": 0,
  "y": 0,
  "createdBy": "user-id",
  "createdByName": "username",
  "updatedBy": "user-id",
  "updatedByName": "username",
  "createdAt": 1735689600000,
  "updatedAt": 1735689600000,
  "version": 1,
  "capabilities": ["editable", "carryable", "deletable", "usable"],
  "useSound": "sounds/roll.ogg",
  "emitSound": "sounds/clock.ogg",
  "params": {},
  "carrierId": null
}
  • useSound: optional client-played one-shot sound when item use succeeds; global item field and not user-editable in V1.
  • emitSound: optional continuously-looping spatial sound emitted from the item on the grid; global item field and not user-editable in V1.
  • capabilities, useSound, and emitSound are derived from global item-type definitions at runtime (not stored per-instance in persisted state).
  • createdBy / updatedBy are stable user IDs.
  • createdByName / updatedByName are display-name snapshots used for inspect/readout text.
  • useCooldownMs: global per item type (radio_station=1000, dice=1000, wheel=4000, clock=1000, widget=1000, piano=1000), not per-instance editable.
  • emitRange: global spatial range default per item type (radio_station=10, dice=15, wheel=15, clock=10, widget=15, piano=15).
    • radio_station can override this per instance via params.emitRange (5..20).
  • directional: global directional attenuation flag per item type (radio_station=true, others false); widget can override per instance via params.directional.

Persisted Item State (server/runtime/items.json)

{
  "id": "string",
  "type": "radio_station | dice | wheel | clock | widget | piano",
  "title": "string",
  "x": 0,
  "y": 0,
  "createdBy": "user-id",
  "createdByName": "username",
  "updatedBy": "user-id",
  "updatedByName": "username",
  "createdAt": 1735689600000,
  "updatedAt": 1735689600000,
  "version": 1,
  "params": {},
  "carrierId": null
}
  • Persisted state stores only instance data.
  • Global/type-level properties are loaded from server registry in server/app/item_catalog.py.
  • Per-type use/update validation and message behavior are implemented in per-item modules under server/app/items/types/*/definition.py, validator.py, and actions.py, discovered via plugins in server/app/items/types/*/plugin.py.
  • Client-side add/edit metadata is consumed from welcome.uiDefinitions via client/src/items/itemRegistry.ts (no local fallback definitions).
  • End-to-end add-item template: docs/item-type-template.md.

Type Params

radio_station

{
  "streamUrl": "",
  "enabled": true,
  "mediaChannel": "stereo",
  "mediaVolume": 50,
  "mediaEffect": "off",
  "mediaEffectValue": 50,
  "stationName": "",
  "nowPlaying": "",
  "facing": 0,
  "emitRange": 10
}
  • streamUrl: string, empty allowed until configured.
  • enabled: boolean on/off flag.
    • UI behavior: in property menu, Enter toggles on/off directly.
  • mediaVolume: integer, range 0-100, default 50.
  • mediaChannel: one of stereo | mono | left | right, default stereo.
  • mediaEffect: one of reverb | echo | flanger | high_pass | low_pass | off, default off.
  • mediaEffectValue: number, range 0-100, precision 0.1.
  • UI visibility: mediaEffectValue is shown only when mediaEffect != off (visibleWhen: {"mediaEffect": "!off"}).
  • stationName: server-managed station label derived from ICY metadata when available.
  • nowPlaying: server-managed stream title derived from ICY metadata when available.
  • facing: number, range 0-360, step 1 (used when directional=true).
  • UI visibility: facing is shown only when directional=true (visibleWhen metadata).
  • emitRange: integer, range 5-20, default 10.

dice

{
  "sides": 6,
  "number": 2
}
  • sides: integer, range 1-100.
  • number: integer, range 1-100.

wheel

{
  "spaces": "yes, no"
}
  • spaces: comma-delimited string of values.
  • Server validation:
    • must include at least 1 value
    • max 100 values
    • each value max 80 chars

clock

{
  "timeZone": "America/Detroit",
  "use24Hour": false,
  "topOfHourAnnounce": true,
  "alarmEnabled": false,
  "alarmTime": "12:00 AM"
}
  • timeZone: one representative IANA zone per world UTC offset. Includes: America/Anchorage, America/Argentina/Buenos_Aires, America/Chicago, America/Detroit, America/Halifax, America/Indiana/Indianapolis, America/Kentucky/Louisville, America/Los_Angeles, America/St_Johns, Asia/Bangkok, Asia/Dhaka, Asia/Dubai, Asia/Hong_Kong, Asia/Kabul, Asia/Karachi, Asia/Kathmandu, Asia/Kolkata, Asia/Seoul, Asia/Singapore, Asia/Tehran, Asia/Tokyo, Asia/Yangon, Atlantic/Azores, Atlantic/South_Georgia, Australia/Brisbane, Australia/Darwin, Australia/Eucla, Australia/Lord_Howe, Europe/Berlin, Europe/Helsinki, Europe/London, Europe/Moscow, Pacific/Apia, Pacific/Auckland, Pacific/Chatham, Pacific/Honolulu, Pacific/Kiritimati, Pacific/Noumea, Pacific/Pago_Pago, UTC.
  • use24Hour: boolean (or on/off in updates), default false.
  • topOfHourAnnounce: boolean (or on/off in updates), default true.
  • alarmEnabled: boolean (or on/off in updates), default false.
  • alarmTime: default 12:00 AM; accepts HH:MM (24-hour mode) or H:MM AM/PM (12-hour mode).
  • UI visibility: alarmTime is shown only when alarmEnabled=true (visibleWhen metadata).
  • Global defaults: useSound=none, emitSound=sounds/clock.ogg.
  • Clock speech announcement audio is emitted via item_clock_announce packets using /sounds/clock/el640/*.ogg.

widget

{
  "enabled": true,
  "directional": false,
  "facing": 0,
  "emitRange": 15,
  "emitVolume": 100,
  "emitSoundSpeed": 50,
  "emitSoundTempo": 50,
  "emitEffect": "off",
  "emitEffectValue": 50,
  "useSound": "",
  "emitSound": ""
}
  • enabled: boolean (or on/off in updates), default true.
  • directional: boolean (or on/off in updates), default false.
  • facing: number, range 0-360, step 1.
  • UI visibility: facing is shown only when directional=true (visibleWhen metadata).
  • emitRange: integer, range 1-20, default 15.
  • emitVolume: integer, range 0-100, default 100.
  • emitSoundSpeed: integer, range 0-100, default 50; controls emitted sound speed/pitch (0=0.5x, 50=1.0x, 100=2.0x).
  • emitSoundTempo: integer, range 0-100, default 50; controls emitted sound tempo (0=0.5x, 50=1.0x, 100=2.0x).
  • emitEffect: one of reverb | echo | flanger | high_pass | low_pass | off, default off.
  • emitEffectValue: number, range 0-100, precision 0.1, default 50.
  • useSound: empty, filename (assumed under sounds/), or full URL.
  • emitSound: empty, filename (assumed under sounds/), or full URL.

piano

{
  "instrument": "piano",
  "voiceMode": "poly",
  "octave": 0,
  "attack": 15,
  "decay": 45,
  "release": 35,
  "brightness": 55,
  "emitRange": 15
}
  • instrument: one of piano | electric_piano | guitar | organ | bass | violin | synth_lead | brass | nintendo | drum_kit.
  • voiceMode: one of poly | mono.
  • octave: integer, range -2..2 (default 0; bass defaults to -1).
  • Selecting a new instrument resets voiceMode/octave/attack/decay/release/brightness to that instrument's defaults.
  • attack: integer, range 0-100, default 15.
  • decay: integer, range 0-100, default 45.
  • release: integer, range 0-100, default 35.
  • brightness: integer, range 0-100, default 55.
  • emitRange: integer, range 5-20, default 15.
  • songId: server-managed song reference used for piano demo/playback content.
  • Recorded/demo song payload is stored in server song registry (runtime/piano_songs.json) using compact format:
    • meta: shared synth parameters
    • keys: keyId dictionary
    • states: parameter-state dictionary (for mid-song instrument/param changes)
    • events: [t, keyIndex, midi, on, stateIndex]

Packet Shapes

  • item_upsert:
{
  "type": "item_upsert",
  "item": { "..." : "World Item" }
}
  • item_remove:
{
  "type": "item_remove",
  "itemId": "item-id"
}
  • item_action_result:
{
  "type": "item_action_result",
  "ok": true,
  "action": "add | pickup | drop | delete | use | update",
  "message": "human-readable status",
  "itemId": "optional-item-id"
}
  • item_use_sound:
{
  "type": "item_use_sound",
  "itemId": "item-id",
  "sound": "sounds/roll.ogg",
  "x": 12,
  "y": 8
}
  • item_clock_announce:
{
  "type": "item_clock_announce",
  "itemId": "item-id",
  "sounds": ["/sounds/clock/el640/its.ogg", "/sounds/clock/el640/2.ogg", "/sounds/clock/el640/PM.ogg"],
  "x": 12,
  "y": 8
}
  • item_piano_note:
{
  "type": "item_piano_note",
  "itemId": "item-id",
  "senderId": "user-id",
  "keyId": "KeyA",
  "midi": 60,
  "on": true,
  "instrument": "piano",
  "voiceMode": "poly",
  "octave": 0,
  "attack": 15,
  "decay": 45,
  "release": 35,
  "brightness": 55,
  "x": 12,
  "y": 8,
  "emitRange": 15
}