Skip to content

Development ​

Layout ​

PathPurpose
fxmanifest.luaDependencies, the provide lines for xsound and InteractSound, the client file list.
init.luaThe TmSound export table (every function assigned to it is exported with its case flipped alias) and the Shared table.
data/defaults.luaEvery setting and its default. The admin panel is generated from it.
locales/en.jsonSetting labels, descriptions and the few messages players see.
modules/shared/config.luaShared.config: the settings as a nested table that follows changes.
modules/clientThe NUI bridge, the listener loop, acoustics, entity tracking, exports, the xsound and InteractSound events.
modules/serverTracking and late joiner sync, exports, the YouTube resolver, the InteractSound events.
ui/The audio engine (TypeScript, Howler). It has no visible interface.
sounds/Your sound files for InteractSound names.

Reading settings in code ​

Shared.config holds every setting as a nested table (the key acoustic.indoor.frequency is Shared.config.acoustic.indoor.frequency). It starts from the defaults, so a value is never nil, and it is updated when an admin changes a setting, so read it when you need it instead of keeping a copy. The client and the server each have their own copy of the table, and settings marked scope = 'server' only reach the server.

Building the interface ​

The audio engine is bundled with esbuild. Nothing rebuilds it at boot, so build it after changing anything under ui/:

bash
cd tm_sound/ui
pnpm install
pnpm build        # type checks, then bundles app.js and the hls and dash players
pnpm watch        # rebuild on save

From the workspace root, actions\Build - UIs.bat tm_sound does the same.

The engine receives messages from Lua (play, pause, position, eq and so on, see modules/client/nui.lua) and reports back with NUI callbacks (init, events, data). The NUI page does the volume falloff, panning, filters and fades, so the game thread only sends the listener position when it changes.

Adding a source ​

Providers live in ui/src/providers. A provider decides from the link whether it can play it (detect.ts) and wraps the third party player behind the same small interface as the file player.

Third party code ​

See THIRD_PARTY.md for the libraries the interface bundles and their licenses.