Skip to content

FEAT(positional-audio): Plugin for Teardown#7142

Open
sqwishy wants to merge 1 commit intomumble-voip:masterfrom
sqwishy:teardown-positional-audio
Open

FEAT(positional-audio): Plugin for Teardown#7142
sqwishy wants to merge 1 commit intomumble-voip:masterfrom
sqwishy:teardown-positional-audio

Conversation

@sqwishy
Copy link
Copy Markdown
Contributor

@sqwishy sqwishy commented Mar 29, 2026

Positional audio support for the 2026 multiplayer release of Teardown. I think it's game version 2.0. (That's what it says in the bottom right corner of the screen in the main menu.)


Hi. I left a lot of notes in comments in the source. I hope it's not too much. A couple things came up while writing the plugin that I wanted to make obvious and explain in the comments.

  1. The game uses quaternions for rotations so there is some math to calculate the forward and top vectors. Normally this involves a couple cross products but the implementations are simplified because the forward and top vectors have two zero components each. (I thought I could just write a function for a general cross product and the compiler would optimize vec3_cross(quat, Vec3 { 0.f, 1.f, 0.f }) into a special case but, when I checked on godbolt, that didn't seem to actually happen. So I wrote the special case implementations and added comments to show the math and make up for them being less readable.)

  2. The player/camera positional information can be read in main menu but we don't want positional audio to start there yet. So the plugin tries to read the game state, compares it to known states where positional audio doesn't make sense to apply, and does not read positional audio in that case. Otherwise, if it can't read the state or isn't in a single-player/menu/lobby state, it will try to read positional audio and set the context string so that in-game players don't hear positional data from in-menu players located at zero.

  3. There are also some remarks about why certain memory regions were used. I haven't been able to test this much but I think it does a pretty okay job and supports third-person.

    The player's rotation is a little suspect at times. I almost wonder if it should use the camera's rotation in place of the player rotation. But I'd have to test more to see how that changes the perception of things.

    The game is modable with a lua API and custom characters. You can query player model bones from lua and get their transforms in lua scripts. (I assume you can make characters with any number of heads too, since they're just labels/tags on bones/bodies in the game?) It would be really hekin' cool to get the head's global transform and share that with mumble but I haven't come up with a way to do that from lua yet. It's all pretty locked down just like with factorio.

  4. Finally, it uses a pattern to search for a base pointer to start the pointer chain. I'm not too sure if this will survive game updates because there haven't been a lot of versions of Teardown with multiplayer to test on.

Also, I tested this plugin with one other person but only in a limited amount. We're waiting for our friend group to all get the game when it goes on sale :3

Checks

Positional audio support for the 2026 multiplayer release of Teardown. I
think it's game version 2.0. (That's what it says in the bottom right
corner of the screen in the main menu.)
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 29, 2026

Walkthrough

A new Mumble plugin for the game Teardown is being introduced across three files. The teardown plugin is added to the list of available plugins for Windows and Linux builds in the root CMakeLists.txt. A CMakeLists.txt file for the plugin defines a new shared library target built from teardown.cpp and platform-specific common source files. The teardown.cpp implementation file adds the complete plugin code, including Mumble plugin API entry points for initialization, shutdown, metadata, and positional audio data fetching, along with coordinate system conversion logic for translating Teardown's coordinate space to Mumble's format.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a positional audio plugin for the Teardown game with appropriate feature prefix.
Description check ✅ Passed The description provides comprehensive context about the plugin's purpose, implementation details, known limitations, and testing status; the required commit guidelines check is marked as completed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
plugins/teardown/teardown.cpp (1)

286-292: Use a session-specific context instead of a constant string.

Line 291 sets one global context for all in-game users, which can cause positional sharing between different Teardown multiplayer sessions on the same Mumble server.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/teardown/teardown.cpp` around lines 286 - 292, Currently *contextPtr
is assigned a constant literal "tearing it down with your buddies", which makes
the context identical across sessions; instead build a session-unique string
(e.g., include the current session id, server port, or a GUID) and assign that
to *contextPtr so each multiplayer session gets its own context. Locate the
place using contextPtr in teardown.cpp and replace the literal with a
dynamically-created string formatted with the session identifier (using
snprintf/std::string + strdup or equivalent) and ensure the allocated memory
lifetime matches the API expectations (free or clear it when the session ends or
when context is replaced) so no dangling pointer or leak occurs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugins/teardown/CMakeLists.txt`:
- Around line 6-17: The CMake targets currently add "../ProcessWindows.cpp"
unconditionally which breaks Linux builds and omits "../ProcessLinux.cpp";
update the teardown target_sources for the library named teardown so that
"../ProcessWindows.cpp" and "../HostWindows.cpp" are only added inside the
if(WIN32) branch and "../ProcessLinux.cpp" and "../HostLinux.cpp" are added in
the else() branch, i.e. move the ProcessWindows.cpp entry into the WIN32 block
and add ProcessLinux.cpp to the else() block while keeping
HostWindows.cpp/HostLinux.cpp selection as-is to guard platform-specific backend
sources.

In `@plugins/teardown/teardown.cpp`:
- Around line 42-61: The TeardownPositional struct is read raw from another
process so add compile-time layout guards: use static_assert with
offsetof(TeardownPositional, playerPos) == 0x58, offsetof(..., cameraPos) ==
0x1cc, and static_assert(sizeof(TeardownPositional) == 0x???) (replace ??? with
the actual expected total size) to catch layout drift; also assert sizeof(Vec3)
and sizeof(Quat) if needed and ensure the struct's packing/alignment matches the
remote process (e.g., pragma pack or attribute if the project uses it) so
offsets are reliable.

---

Nitpick comments:
In `@plugins/teardown/teardown.cpp`:
- Around line 286-292: Currently *contextPtr is assigned a constant literal
"tearing it down with your buddies", which makes the context identical across
sessions; instead build a session-unique string (e.g., include the current
session id, server port, or a GUID) and assign that to *contextPtr so each
multiplayer session gets its own context. Locate the place using contextPtr in
teardown.cpp and replace the literal with a dynamically-created string formatted
with the session identifier (using snprintf/std::string + strdup or equivalent)
and ensure the allocated memory lifetime matches the API expectations (free or
clear it when the session ends or when context is replaced) so no dangling
pointer or leak occurs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c4ef2ad8-860f-476e-8d7b-4a22bcc9a0f5

📥 Commits

Reviewing files that changed from the base of the PR and between f2a0d8e and 72733e9.

📒 Files selected for processing (3)
  • plugins/CMakeLists.txt
  • plugins/teardown/CMakeLists.txt
  • plugins/teardown/teardown.cpp

Comment on lines +6 to +17
add_library(teardown SHARED
"teardown.cpp"

"../Module.cpp"
"../ProcessBase.cpp"
"../ProcessWindows.cpp")

if(WIN32)
target_sources(teardown PRIVATE "../HostWindows.cpp")
else()
target_sources(teardown PRIVATE "../HostLinux.cpp")
endif()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify process backend files and current plugin backend patterns.

fd -t f 'Process.*\.(cpp|h)' plugins
rg -n -C2 'add_library\(|ProcessWindows\.cpp|ProcessLinux\.cpp|HostLinux\.cpp|HostWindows\.cpp' plugins/**/CMakeLists.txt
rg -n -C2 'class ProcessWindows|#include <windows\.h>|_WIN32' plugins/ProcessWindows.cpp plugins/ProcessWindows.h

Repository: mumble-voip/mumble

Length of output: 16908


🏁 Script executed:

head -30 plugins/ProcessWindows.cpp plugins/ProcessWindows.h

Repository: mumble-voip/mumble

Length of output: 1740


🏁 Script executed:

head -30 plugins/ProcessLinux.cpp

Repository: mumble-voip/mumble

Length of output: 818


🏁 Script executed:

grep -A 5 "add_library(se SHARED" plugins/se/CMakeLists.txt

Repository: mumble-voip/mumble

Length of output: 158


🏁 Script executed:

grep -A 5 "else()" plugins/se/CMakeLists.txt | head -3

Repository: mumble-voip/mumble

Length of output: 145


Guard process backend sources by platform.

Line 11 unconditionally includes ../ProcessWindows.cpp, which contains Windows-specific code (including mumble_positional_audio_win32_internals.h and Windows PE parsing logic) that cannot compile on Linux. Meanwhile, ../ProcessLinux.cpp—which uses ELF-specific structures—is never included, breaking Linux builds.

Move ProcessWindows.cpp into the if(WIN32) block and add ProcessLinux.cpp to the else() block:

Suggested CMake adjustment
 add_library(teardown SHARED
 	"teardown.cpp"

 	"../Module.cpp"
-	"../ProcessBase.cpp"
-	"../ProcessWindows.cpp")
+	"../ProcessBase.cpp")

 if(WIN32)
+	target_sources(teardown PRIVATE "../ProcessWindows.cpp")
 	target_sources(teardown PRIVATE "../HostWindows.cpp")
 else()
+	target_sources(teardown PRIVATE "../ProcessLinux.cpp")
 	target_sources(teardown PRIVATE "../HostLinux.cpp")
 endif()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
add_library(teardown SHARED
"teardown.cpp"
"../Module.cpp"
"../ProcessBase.cpp"
"../ProcessWindows.cpp")
if(WIN32)
target_sources(teardown PRIVATE "../HostWindows.cpp")
else()
target_sources(teardown PRIVATE "../HostLinux.cpp")
endif()
add_library(teardown SHARED
"teardown.cpp"
"../Module.cpp"
"../ProcessBase.cpp")
if(WIN32)
target_sources(teardown PRIVATE "../ProcessWindows.cpp")
target_sources(teardown PRIVATE "../HostWindows.cpp")
else()
target_sources(teardown PRIVATE "../ProcessLinux.cpp")
target_sources(teardown PRIVATE "../HostLinux.cpp")
endif()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/teardown/CMakeLists.txt` around lines 6 - 17, The CMake targets
currently add "../ProcessWindows.cpp" unconditionally which breaks Linux builds
and omits "../ProcessLinux.cpp"; update the teardown target_sources for the
library named teardown so that "../ProcessWindows.cpp" and "../HostWindows.cpp"
are only added inside the if(WIN32) branch and "../ProcessLinux.cpp" and
"../HostLinux.cpp" are added in the else() branch, i.e. move the
ProcessWindows.cpp entry into the WIN32 block and add ProcessLinux.cpp to the
else() block while keeping HostWindows.cpp/HostLinux.cpp selection as-is to
guard platform-specific backend sources.

Comment on lines +42 to +61
struct TeardownPositional {
/* This information isn't perfect, the quaternion flops around when the
* player is seated in a vehicle. But the position is pretty good! When
* in a lift vehicle, for example, your body is in the upper part that
* moves vertically, and playerPos will report that position
* consistently in both first and third person perspectives. That is
* what we want because other players will always see you on the
* upper/vertical part of the lift, so your voice should always come
* from there. */
Vec3 playerPos; /* 0x58 */
Quat playerRot;
/* There is a third-person camera position and quaternion immediately
* after the player above, but it doesn't work right when you get into
* a vehicle. At 0x1cc there is a more accurate camera structure that
* works in first-person, third-person, in a vehicle, and in the bird's
* eye view when hitting tab. */
uint8_t _[0x158];
Vec3 cameraPos; /* 0x1cc */
Quat cameraRot;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add compile-time layout guards for remote-memory structs.

Line 246 reads TeardownPositional as a raw block from another process; without static_assert offset/size checks, compiler/layout drift can silently corrupt positional data.

Suggested hardening patch
+#include <cstddef>
+
 struct Vec3 {
 	float x;
 	float y;
 	float z;
 };
@@
 struct TeardownPositional {
@@
 	Quat cameraRot;
 };
+
+static_assert(sizeof(Vec3) == 0x0C, "Vec3 layout changed");
+static_assert(sizeof(Quat) == 0x10, "Quat layout changed");
+static_assert(offsetof(TeardownPositional, playerPos) == 0x00, "playerPos offset changed");
+static_assert(offsetof(TeardownPositional, playerRot) == 0x0C, "playerRot offset changed");
+static_assert(offsetof(TeardownPositional, cameraPos) == 0x174, "cameraPos offset changed");
+static_assert(offsetof(TeardownPositional, cameraRot) == 0x180, "cameraRot offset changed");

Also applies to: 246-251

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugins/teardown/teardown.cpp` around lines 42 - 61, The TeardownPositional
struct is read raw from another process so add compile-time layout guards: use
static_assert with offsetof(TeardownPositional, playerPos) == 0x58,
offsetof(..., cameraPos) == 0x1cc, and static_assert(sizeof(TeardownPositional)
== 0x???) (replace ??? with the actual expected total size) to catch layout
drift; also assert sizeof(Vec3) and sizeof(Quat) if needed and ensure the
struct's packing/alignment matches the remote process (e.g., pragma pack or
attribute if the project uses it) so offsets are reliable.

@Hartmnt Hartmnt added this to the 1.7.0 milestone Apr 6, 2026
@Hartmnt
Copy link
Copy Markdown
Member

Hartmnt commented Apr 29, 2026

No time to review this fully, just dropping in to say: Don't forget to add your .dll file to the ClientInstaller.cs as install target this time :3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants