Repository navigation
Conversation
bdbcaea to
bace526
Compare
| return Assert.Single(captured, sound => sound.Guid == (uint)guid); | ||
| } | ||
|
|
||
| private static List<Parameter> Unpack(byte[] packed) |
There was a problem hiding this comment.
I think this test file should have all of the implementation knowledge of how the parameters are packed removed, so it's just testing the API and that running the tests do not crash the server.
What might be useful is to create a test that emits a specific sound audible to all players using all of the available parameters. i.e. play the AK sound pitched down in a loop increasing in volume pitch and distance, and then a player can connect to the test server to verify the results manually (if needed) by running css_itest AudibleSoundTest (example name)
|
Done, and testing it turned up something worth having in the documentation. The test file no longer knows anything about how parameters are packed. No hashes, no type tags, no unpacking, no message id, no hooks. It exercises the API and checks a server survives it: handles, guids, every value type, setting the same parameter twice, emitting to one player, stopping, disposing twice.
Checking the sweep by ear is what caught the interesting part. Position appeared to do nothing, and I nearly wrote it up as delivered but inert, until I noticed I had been attaching the sound to the listener and then trying to move it away from them.
Zero is how the game emits placed sounds, which is visible in its own messages: they carry an entity index of 0 together with real map coordinates. With that, volume, pitch and position are all audible, including direction and falloff. That is now on The follow-up I mentioned, |
EmitSound goes through CBaseEntity::EmitSoundFilter, which sends the sound event message without any packed parameters, so the volume and pitch arguments never reach the client and have no effect. Build the CMsgSosStartSoundEvent message directly instead, so sound operator parameters can be set per emit. Volume and pitch work, and any other public.* parameter the sound stack reads can be set as well. EmitSound is left as it is, so nothing existing changes behaviour.
The tests asked whether specific bytes landed in specific places, which pinned them to the wire format rather than to the API. They now exercise the API and check that a server survives it. Adds an audible test that plays a sound to everyone with volume, pitch and then position swept, so the result can be checked by ear with css_itest AudibleSoundTest. Also documents what SourceEntityIndex does, which is what decides whether public.position is used at all.
Two of the tests looked for any player already on the server and failed on an empty one, which is what a smoke run starts from. They now ask CreateTestPlayerAsync for a bot, as the game and ray trace tests do, and attach the sound to its pawn. The audible sweep plays to human players only, so it returns at once when none is connected instead of holding an automated run for eleven seconds.
The example attached the sound to the controller. The game's own sound messages name the pawn for everything a player emits, so the example and the note on SourceEntityIndex now say the same.
9a9066b to
66adc4f
Compare
|
Rebased onto main (v1.0.376). The only conflict was the generated Two things changed besides the rebase:
On 1.41.8.8, Linux, I also had the runner problem backwards in the description. It printed nothing for me in August because I had copied the plugin without Unrelated to this PR, in case it is useful: on main |
EmitSoundtakes volume and pitch arguments that never reach the client. It goes throughCBaseEntity::EmitSoundFilter, and theCMsgSosStartSoundEventthat comes out of it carries nopacked_paramsat all, so there is nothing for the client to apply. Captured from a live server, this is the whole message the current path sends:The source already says as much:
m_nPitchis annotated "not working, can't fix, i think the game abandon it", andm_flVolumeis avolume_attenrather than a volume. This was raised in #919, and #126 is still open.This adds a
SoundEventthat builds the message directly, so sound operator parameters can be chosen per emit.Emitreturns the guid, whichSoundEvent.Stoptakes to end a sound early. Recipients come from aRecipientFilter, so a sound can go to one player, one team, or everyone.SourceEntityIndexdecides where the sound plays from: -1, the default, plays it at each recipient; 0 plays it atpublic.positionin the world; any other index attaches it to that entity. For a player that is the pawn, which is what the game's own messages name.SetParamtakes anypublic.*parameter name, not a fixed set, so anything the sound's operator stack reads can be driven from a plugin without a change here first:Volume, pitch and position are the three this was tested against. Whether any other parameter does something depends on the stack behind the sound, which is noted in the API documentation.
Implementation
Built on the core side using net messages, which is the approach suggested in the discussion.
packed_paramsis a flat sequence of parameters, each one a 32 bit hash of the parameter name, a type tag, a 16 bit length, then the value. The types that appear in game traffic are0x02for integers,0x08for floats and0x0Afor vectors. Parsing the parameters of the game's own messages leaves no remainder: 365 of them in August, and 1235 from 644 messages on 1.41.8.8.Two seeds are in play, and both go through the SDK's own
MurmurHash2LowerCase: sound event names hash with0x53524332, parameter names withSTRINGTOKEN_MURMURHASH_SEED. Nothing about hashing is reimplemented here.SetParamreplaces an earlier value for the same parameter rather than appending one, so a reused sound event does not grow its blob.EmitSoundis left alone, so no existing behaviour changes.Verification
Rebased onto v1.0.376 and run again on game build 1.41.8.8, Linux dedicated server:
css_smoke_teston a fresh server: 119 passed, 0 failed, 4 skipped, the same four that are skipped on mainChecked by ear in August and not repeated since: volume and pitch are audibly different across values, where the existing
EmitSoundpath plays 1.0 and 0.05 identically; a sound placed in the world is heard from its direction and falls off with distance;Stopcuts a sound short.css_itest AudibleSoundTestplays the sweep for anyone who wants to hear it.SoundEventTests.csexercises the API and checks that the server survives it: handles, guids, every value type, the same parameter twice, emitting to one player, stopping, disposing twice. It knows nothing about how parameters are packed; the wire checks above come from a separate local plugin that hooks the message.Builds on both platforms with no warnings in the new files: gcc 13 on Linux, and MSVC 19.44 through the same
vsdevcmdand CMake steps CI uses onwindows-latest. The Windows build was run on a server in August; after the rebase it was only compiled.Not covered
CMsgSosSetSoundEventParamswould let a playing sound be changed rather than only started and stopped, which is what a smooth fade needs. It reuses the same parameter blob and would be a small follow-up, left out here to keep this reviewable.A sound name is resolved by the client, so a name that does not exist plays nothing and reports no error. The existing
EmitSoundbehaves the same way. This is called out in the API documentation.The discussion and the research behind it are @samyycX's, including the finding that the parameters are entirely net message based.