Sound and music
Deadlock ships 79,222 soundevents and you can play any of them per player from the server with no client files at all. Custom audio is a completely different problem, and this page says exactly where the line is and what crossing it would take.
Playing a stock sound
// To one player. The Deadworks source documents this as "Plays a soundevent to just this player".
uint guid = Players.FromSlot(slot)?.PlaySound(name) ?? 0;
// Other routes
pawn.EmitSound(name, pitch, volume, delay);
Sounds.Play(name, filter, volume, pitch);
Sounds.PlayAt(name, position, ...);
PlaySound returns the soundevent GUID. A zero means the engine resolved
nothing for that name. That is the difference between "the name is wrong" and "the name is
fine and you cannot hear it", which are otherwise identical from the outside and which you will
otherwise spend an hour confusing.
private readonly HashSet<string> _missing = new();
private uint PlayForSlot(int slot, string sound) {
uint guid = Players.FromSlot(slot)?.PlaySound(sound) ?? 0;
if (guid == 0 && _missing.Add(sound))
Console.WriteLine($"[sound] '{sound}' returned GUID 0, the engine has no such soundevent");
return guid;
}
The HashSet is there so a bad name in a per-tick path logs once rather than forever.
Every Music.* and Stinger.* event sits on a music mix
group. Read out of the game's own music.vsndevts:
Base.Music.2d mixer_mixgroup = "Music" send_name = "Music"
Music.Brawl.Round.Won mixer_mixgroup = "Music-Objective"
A player with music turned down in their audio settings hears nothing from those events even when the GUID is valid and the server did everything right. If a sound must be heard, pick one that is not on a music group.
Worth noting as a nuance rather than a contradiction: those same events carry
event_use_music_convar = false, so the music convar does not gate them. The mix
group volume does.
The round-based soundtrack that is already in the game
Deadlock has a Brawl mode, so it already contains a complete set of round music. Verified present
in pak01_dir/soundevents/music.vsndevts. This is why neither of our gamemodes needs any
custom audio to feel finished.
| Soundevent | Use |
|---|---|
Music.Brawl.RoundStart1 .. 5 | Five different round-start tracks. Index by round number for an escalating arc. |
Music.Brawl.Round.Won / Music.Brawl.Round.Lost | Per-squad round result |
Music.Brawl.Match.Won / Music.Brawl.Match.Lost | Per-squad match result |
Music.Brawl.Draft | Buy or draft phase |
Stinger.Brawl.Overtime.Announce | Pairs well with an on-screen card |
Stinger.Death | Death |
Stinger.KillStreak.FirstBlood | First blood |
Stinger.Idol.PickedUp | Good generic "a thing has appeared" |
Powerup.Pickup.Punch | Punch feedback |
Powerup.Pickup.Generic | Generic pickup |
Gameplay.Pause.Resume.Countdown.Tick | The buzzer tick for the last seconds of a countdown |
A wrong soundevent name should be a config edit, not a redeploy. Both gamemodes hold every name in their config object, so a GUID 0 in the log is fixed from the panel in seconds.
Finding a soundevent name
Every soundevent name in the build, extracted from the game's own 149 .vsndevts files
on 2026-10-27, is in 04_scripting/soundevents_all.txt as
name<TAB>file.
79,220 names, and the split is the useful part: 75,710 of them are generated hero
voice lines, which is why the number is so absurd. The other 3,510 are everything a
gamemode would ever reach for, and they are listed first in the file under a
### NON-VO EVENTS ### header so you can stop reading at the second header.
grep -i brawl 04_scripting/soundevents_all.txt
grep -iP '^UI\.' 04_scripting/soundevents_all.txt
grep -i powerup 04_scripting/soundevents_all.txt
Rough shape of the 3,510 by prefix: Mods 283, Base 123,
UI 117, Stinger 51, Player 45, Ability 36,
NPC 41, amb 79, plus 30 to 45 per hero under that hero's name
(Haze, Bebop, Lash and so on).
To go to the source, the files themselves are the authority:
cd 09_research_log/github_clones/GameTracking-Deadlock/game/citadel/pak01_dir/soundevents
ls # ambience/ base/ hero/ npc/ mods/ music.vsndevts damage.vsndevts ...
grep -rn "Brawl" . # every Brawl event, with the file and line
They are KV3 text, so they are readable as-is. The header tells you the format:
<!-- kv3 encoding:text:version{...} format:generic:version{...} -->
{
Music.Brawl.Round.Won =
{
base = "Base.Music.2d"
volume = 4.000000
event_voice_layer = "ui"
mixer_mixgroup = "Music-Objective"
vsnd_files = [ ... ]
}
}
Note base: soundevents inherit. If an event looks thin, read its base to find the mix
group and the volume it is really using.
Confirming one in game before you write code
Both gamemodes have a test command for exactly this. The pattern is worth copying:
/bbtest sound Music.Brawl.RoundStart3
// which reports one of two things, and they mean different things:
// "played, guid=12345. Silence now means the MUSIC slider, not the name."
// "resolved to NOTHING (guid 0), that soundevent does not exist"
Positional sound
Sounds.PlayAt(name, position) plays at a point. There is also a
point_soundevent entity for positional sound with a self-kill output, confirmed by the
community, if you want the sound tied to a thing in the world rather than fired once.
Custom audio, with the launcher and nothing else
Deadlock does load an extra soundevents file out of a search path, and custom soundevent names resolve. This is no longer an open question.
A file was placed at <install>/game/citadel/soundevents/soundevents_addon.vsndevts
declaring two invented names, both pointing at a sound the game already ships. On a live server both
resolved, repeatedly:
[BattleBox][bbtest] sound 'Test.Amir.Base' guid=8
[BattleBox][bbtest] sound 'Test.Amir.Base' guid=9
[BattleBox][bbtest] sound 'Test.Amir.Base' guid=10
[BattleBox][bbtest] sound 'Test.Amir.Standalone' guid=11
[BattleBox][bbtest] sound 'Test.Amir.Standalone' guid=12
A non-zero GUID means the engine found the soundevent. A name that does not exist returns
zero. Both the form that inherits Base.Ability from another file and the
standalone form that inherits nothing worked, and the Counter Strike 2 filename convention
(soundevents_addon.vsndevts) is the one Deadlock honoured.
It was silent, and that is the expected result. The file was only on the server. The server resolved the name and handed the client a sound id the client could not map to any audio, because the client had never seen our soundevents file. Resolution and playback are two halves, and this experiment proved the half that was in doubt.
So: yes, custom audio works, and players need nothing but the Deadworks launcher they already need for the HUD. What remains is content delivery to the client, which is the mechanism that already ships our custom maps.
The hard rule you cannot design around
The official Deadworks sound docs are blunt about it:
"A plugin can play any soundevent. It can't play a sound file directly."
So PlaySound resolves a name, and the client must already hold both
halves of what that name points at:
- the audio itself, as a compiled
.vsnd, and - a soundevent declaring that name and pointing at that file
No server call can create either one. Custom audio is therefore a content delivery problem, not a scripting problem. Which is good news, because content delivery to the client is the thing the launcher exists to do.
What the launcher actually does to the client, which is the key fact
From the Deadworks content-discovery guide, in the section written for launcher developers (so this describes the launcher's own behaviour, not a manual step for players):
- It installs each advertised addon to
citadel/deadworks_addons/vpks/<name>.vpk. - It writes the
SearchPathsblock insideFileSystemin the client'scitadel/gameinfo.gi, declaring bothGame citadel/deadworks_modsandaddonroot citadel/deadworks_addons. - On that entry the guide says: "Use exactly this
addonrootvalue. The Deadworks launcher replaces any otheraddonrootentry with it." - It re-checks those entries before every launch, because Deadlock Mod Manager rewrites the whole
SearchPathsblock each time it launches the game, and Steam's file verification can restore the original.
addonroot is Source 2's own native addon mount point. The launcher
is not inventing a private delivery channel for maps; it is pointing the engine's standard addon
system at a folder it controls, on the player's machine, automatically. Anything the engine
normally loads out of an addon is therefore in scope, and the player does nothing but run the
launcher.
The chain, with each link's status
| # | Link | Status |
|---|---|---|
| 1 | Compile your audio to a .vsnd. Needs the Deadlock SDK; there is no resourcecompiler.exe on a dedicated-server install. | needs SDK tooling |
| 2 | Declare a soundevent in a .vsndevts pointing at it. Readable KV3, shape above. | done, uncompiled text worked |
| 3 | Get that .vsndevts loaded by the engine | PROVEN 2026-10-27 |
| 4 | Ship it to the client in a content addon vpk, advertised via ContentAddons and fetched by the launcher before connect | mechanism proven by our maps |
| 5 | Mount it on the server from deadworks_mods/vpks/<name>.vpk, or simply drop it loose in game/citadel/ | loose files work |
- An uncompiled, loose
.vsndevtswas read. The probe was plain KV3 text dropped straight intogame/citadel/soundevents/, with no vpk and no.vsndevts_ccompile step. The server runs with-dev, which may be why. Worth retesting without-devbefore relying on it in production. - No edit to the game's own manifest was needed. The five
soundevents_manifest*.vrman_cfiles were left completely alone. The engine picked up the addon file by its filename, which means the frightening failure mode this page used to warn about, an addon replacing the game's manifest and killing all audio, never has to be risked.
Link 3, and why it was the one in doubt
Soundevent files are not found by scanning a folder. The base game lists them in resource manifests, and these five are in the pak:
soundevents/soundevents_manifest.vrman_c 971 bytes
soundevents/soundevents_manifest_citadel.vrman_c 5633
soundevents/soundevents_manifest_devonly.vrman_c 912
soundevents/soundevents_manifest_heroes.vrman_c 8982
soundevents/soundevents_manifest_tools.vrman_c 870
Source 2's answer for addons is a known filename the engine looks for inside an
addon, rather than an edit to the game's manifest. In Counter Strike 2 that file is
soundevents/soundevents_addon.vsndevts: you put your sounds in the addon, declare your
events in that file pointing at the compiled .vsnd files, and per Valve's own addon
documentation that is all that is required, including to override an existing soundevent.
This page previously argued there is "no soundevents_addon hook in Deadlock" because
no such file appears in the pak listing. That reasoning was wrong. In CS2 that file
is not in the base pak either; it is a file you create in your addon and the engine looks
for it. Its absence from Deadlock's pak proves nothing either way.
So the real question is narrower and much less frightening than "will this break all audio": it is simply whether Deadlock's build of the engine honours the addon soundevent file, since Valve enables these conventions per game.
What is left to do
Server-side resolution is proven. The remaining work is getting the same two files onto the client, and that is the mechanism our custom maps already use:
- Produce the audio. A
.wavcompiled to.vsnd. This needs the Deadlock SDK, so it happens on a machine with the authoring tools; a dedicated-server install has noresourcecompiler.exeand novpk.exe. - Pack a vpk containing
soundevents/soundevents_addon.vsndevtsandsounds/<yours>.vsnd. - Advertise it from the plugin's
ContentAddonsoverride and publish it to the FastDL host, exactly as a map is published. The launcher then installs it tocitadel/deadworks_addons/vpks/and theaddonrootentry it already writes into the client'sgameinfo.gimakes the engine load it. - Mount the same vpk server-side so both halves agree.
The one genuinely untested step is whether a compiled soundevents file inside a vpk behaves the same as the loose uncompiled one did. Given the loose file worked without touching any manifest, there is no reason to expect trouble, and the test is the same one command.
Two other things worth knowing before you start
- The server-admin page for custom content is currently a placeholder: "A new way to add custom maps and content addons to your server is on its way." So the upstream flow may be about to change. Worth asking what it will be in the same message.
- Our own plumbing is already in place and empty. BattleBox declares
public override IReadOnlyList<string> ContentAddonsand ships an empty list, so dropping a vpk indeadworks_mods/vpks/and adding its name is the entire server-side change.
What to do in the meantime, and where to test it
- Use the stock library. 79,222 soundevents including a full round-based mode soundtrack is a lot of range, and neither gamemode has hit a wall it needed custom audio to get past.
- Test on a lobby slot, never server 1. Mounting an addon that confuses the audio system should happen on a disposable instance.
- Expect one client-side wrinkle even when it works: the guide notes Deadlock Mod
Manager rewrites the whole
SearchPathsblock on launch, so a player who uses that tool may lose the addon root until the launcher puts it back. Launch through the launcher.
One more open question
not proven Whether looping music needs a start and stop user
message rather than a plain PlaySound. In practice our gamemodes fire one-shot tracks at
phase boundaries and have not needed a loop, so this has never been settled. If you want ambient
music under a whole round rather than a sting at the start of it, this is the thing to find out
first.