Skip to content

Configuration

Everything a server owner touches lives in config/. Four files:

FileWhat it holds
settings.luaBehaviour, keys, audio, access, limits, webhooks
cabinets.luaThe carryable phonograph models
spots.luaCabinets bolted to the map
records.luaThe record catalogue

All four stay editable in the escrowed package.

settings.lua

General

SettingDefaultMeaning
language'en'Locale code; must match a file in locales/ (en, de, es, fr, it, pt)
framework'auto''auto' sniffs vorp_corersg-coreredem_roleplay, else standalone. Or pin one: 'vorp', 'rsg', 'redem', 'standalone'
standalone.placeCommand'phonograph'With no usable-item system, /phonograph starts placement instead
debug.enabledfalseVerbose console tracing
debug.log{}Silence one channel while debugging, e.g. { mixer = false }

Notifications — bridge

SettingDefaultMeaning
notice'builtin''builtin' = the resource's own themed toasts; 'framework' = hand off to your framework's notification; 'off' = silence
noticeTime3500Toast duration in milliseconds

Keys — keys

Control hashes. Defaults:

KeyDefault inputUsed for
tune[R]Open the deck panel
pack[Backspace] (hold)Pick a placed cabinet up
affirm[R] (hold)Placement: set it down
abort[Backspace]Placement: put it away
swingL / swingRArrow keysPlacement: rotate

Placement — placing

SettingDefaultMeaning
reach1.3Metres in front of the player the ghost sits
step1.5Degrees of rotation per frame while a swing key is held
alpha160Ghost transparency (0–255)
holdMs750How long the confirm key must be held

World streaming — world

SettingDefaultMeaning
streamSpan40.0Metres at which cabinet props appear
touchSpan2.0Metres at which the prompts light up
scanMs500Slow world-scan interval, in ms

Audio — audio

SettingDefaultMeaning
range15.0Metres at which a track fades to silence
defaultVolume0.40–1, starting volume of a fresh track
rolloff1.4Above 1 hugs the cabinet, below 1 carries further
maxUrl300Longest link the server accepts
crackle.enabledtrueProcedural vinyl-crackle bed, generated in the overlay — no file
crackle.level0.350–1, relative to the track's own volume; 0 disables

Access — access

SettingDefaultMeaning
ownerOnlyfalseWhen true, a player-placed cabinet obeys only its owner (and staff)
admins{}Character identifiers that count as staff everywhere
aceFlag'rm_phonograph.admin'Holders of this ace are staff too
reachSpan4.0Server-side arm's length for every action
packBlockSpan5.0Another player this close blocks a pickup

Who may paste a raw link into the panel. Catalogue records are always available to anyone the deck answers to — this gate is only for arbitrary URLs.

ValueMeaning
'everyone'Any player the deck answers to
'owners' (default)The deck's owner; for map spots, whoever the spot's access names — so a public 'none' spot becomes records-only
'staff'Staff only

Panel sections — panel

SettingDefaultMeaning
shelftrueShow the record shelf. When false, record items also refuse to play — enforced server-side
urlBoxtrueShow the link box. When false, no link is accepted from anyone, staff included

Limits — limits

SettingDefaultMeaning
queueCap8Tracks a deck's queue holds
queuePerPlayer3How many of those one player may own
playCooldownMs4000Between play/queue requests per player; staff exempt
maxTrackMinutes0Auto-advance after this much played time — pauses don't count. 0 = off
domains{}Allowed URL hosts; empty = any. e.g. { 'youtube.com', 'youtu.be' } — subdomains match automatically

Discord webhooks — audit

Shipped off. Fill a url and flip enabled per channel:

ChannelFires on
decksA cabinet placed or packed up
tracksA track played or queued — includes who, the resolved title, and the video thumbnail

Treat webhook URLs as secrets

Anyone holding the URL can post to that channel. Redact them before sharing a config.

cabinets.lua

The cabinets players can carry, keyed by name.

lua
brass = {
    item  = 'phonograph',    -- inventory item that plants one
    label = 'Phonograph',    -- shown on prompts and the panel
    prop  = 'p_phonograph01x',
    carry = {                -- how the prop rides the hand while carried
        bone = 'XH_R_Hand01',
        pos  = { x = 0.30, y = -0.04, z = -0.31 },
        rot  = { x = -239.0, y = 13.0, z = 217.0 },
    },
},

The key is what the database stores

Renaming a cabinet orphans every deck already placed with it. Orphaned rows are kept and reported at boot, never deleted — but treat keys as permanent once players have placed cabinets.

Add more cabinets with different props and items; each needs its own usable item registered in your framework.

spots.lua

Cabinets bolted to the map. They exist without anyone placing them and cannot be packed up.

lua
valentine_saloon = {
    cabinet = 'brass',
    access  = 'none',
    pos     = vector3(-817.682, -1392.679, 43.345),
    rot     = vector3(0.0, 0.0, -111.448),
},

access values:

ValueMeaning
'none'Anyone may use the deck
'ABCD1234...'One character identifier
{ 'saloon', 'bartender' }A list of jobs

This interacts with records.urlAccess

With the default urlAccess = 'owners', a 'none' spot is usable by everyone but accepts no raw links from non-staff — while a spot with an access list treats those people as its owners, and they can.

records.lua

The record catalogue. Each key is an inventory item; the deck shows the ones in the player's bags as sleeves on a shelf.

lua
record_camptown = {
    label  = 'Camptown Races',   -- on the sleeve and while playing
    artist = 'Stephen Foster',   -- optional second line
    url    = 'https://...',      -- YouTube link or direct audio file
},
  • The URL is read from this table on the server — never from the client — so records cannot be forged.
  • An entry with an empty url is skipped and named in the server console at boot.
  • To add your own: create the item in your framework, add an entry here, and drop a matching <item_name>.png into your inventory's image folder.

Documentation for RedMorrow. Scripts are licensed per server — redistribution is not permitted.