From 004e3b3bbfdc8e827b5fdd613797a383078de1e7 Mon Sep 17 00:00:00 2001 From: foxtacles Date: Thu, 12 Mar 2026 20:52:58 -0700 Subject: [PATCH] Fix bugs (#16) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix camera drifting during multi-part emotes Block translational movement in HandleCameraRelativeMovement() when a multi-part emote is active. Previously, movement input was processed unconditionally, causing the native ROI (and camera) to move forward while the display ROI stayed pinned at the emote position. Now the emote check forces hasInput=false and zeros m_smoothedSpeed so neither position updates nor coasting occur during any phase of a multi-part emote. Camera orbit controls (rotation/pan/zoom) remain unaffected. https://claude.ai/code/session_01QMcZSa3ysdyACVea66QA5K * Fix use-after-free crash in NameBubbleRenderer::Update Lego3DSound::FUN_10011a60 (used by LegoCacheSound::Play) did not reset m_isActor and m_enabled when reassigning the ROI reference. When a cached sound was reused — first for a known actor name (setting m_isActor=TRUE), then for a multiplayer clone found via FindROI — the stale m_isActor flag caused Reset() to call ReleaseActor on the clone's ROI, freeing it while ThirdPersonCamera still held a pointer. The next Tick then dereferenced the dangling pointer in NameBubbleRenderer::Update. Reset the ownership flags at the top of the reassignment path so they match the clean-state semantics of Lego3DSound::Create. Also guard multi-part emotes behind an active 3rd-person camera check and remove a leftover debug log in RemotePlayer. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude --- LEGO1/lego/legoomni/src/audio/lego3dsound.cpp | 6 ++++++ extensions/src/multiplayer/networkmanager.cpp | 7 +++++++ extensions/src/multiplayer/remoteplayer.cpp | 8 -------- extensions/src/multiplayer/thirdpersoncamera.cpp | 7 +++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/LEGO1/lego/legoomni/src/audio/lego3dsound.cpp b/LEGO1/lego/legoomni/src/audio/lego3dsound.cpp index a6224ccf..8d3445b1 100644 --- a/LEGO1/lego/legoomni/src/audio/lego3dsound.cpp +++ b/LEGO1/lego/legoomni/src/audio/lego3dsound.cpp @@ -186,6 +186,12 @@ void Lego3DSound::FUN_10011a60(ma_sound* p_sound, const char* p_name) } } else { + // Reset ownership flags before reassigning. Reset() only clears m_roi + // but not these flags, so stale values from a previous actor-backed play + // would cause an incorrect ReleaseActor call for non-actor ROIs + m_isActor = FALSE; + m_enabled = FALSE; + if (CharacterManager()->IsActor(p_name)) { m_roi = CharacterManager()->GetActorROI(p_name, TRUE); m_enabled = m_isActor = TRUE; diff --git a/extensions/src/multiplayer/networkmanager.cpp b/extensions/src/multiplayer/networkmanager.cpp index e54c657e..8aa397dc 100644 --- a/extensions/src/multiplayer/networkmanager.cpp +++ b/extensions/src/multiplayer/networkmanager.cpp @@ -1,5 +1,6 @@ #include "extensions/multiplayer/networkmanager.h" +#include "extensions/multiplayer/animdata.h" #include "extensions/multiplayer/charactercloner.h" #include "extensions/multiplayer/charactercustomizer.h" #include "legoanimationmanager.h" @@ -552,6 +553,12 @@ void NetworkManager::SendEmote(uint8_t p_emoteId) return; } + // Multi-part emotes require 3rd person camera to be active (they need the display clone). + // In 1st person mode, skip them entirely to avoid broadcasting an emote the local player can't play. + if (!m_thirdPersonCamera.IsActive() && IsMultiPartEmote(p_emoteId)) { + return; + } + m_thirdPersonCamera.TriggerEmote(p_emoteId); EmoteMsg msg{}; diff --git a/extensions/src/multiplayer/remoteplayer.cpp b/extensions/src/multiplayer/remoteplayer.cpp index 11b5cfe0..e35098a9 100644 --- a/extensions/src/multiplayer/remoteplayer.cpp +++ b/extensions/src/multiplayer/remoteplayer.cpp @@ -11,7 +11,6 @@ #include "realtime/realtime.h" #include "roi/legoroi.h" -#include #include #include #include @@ -141,13 +140,6 @@ void RemotePlayer::UpdateFromNetwork(const PlayerStateMsg& p_msg) SDL_memcpy(newName, p_msg.name, sizeof(newName)); newName[sizeof(newName) - 1] = '\0'; if (SDL_strcmp(m_displayName, newName) != 0) { - SDL_Log( - "RemotePlayer[%u] name changed: '%s' -> '%s' (spawned=%d)", - m_peerId, - m_displayName, - newName, - m_spawned - ); SDL_memcpy(m_displayName, newName, sizeof(m_displayName)); // Recreate bubble with new name (or create for the first time) diff --git a/extensions/src/multiplayer/thirdpersoncamera.cpp b/extensions/src/multiplayer/thirdpersoncamera.cpp index 7ee33732..64dffe5b 100644 --- a/extensions/src/multiplayer/thirdpersoncamera.cpp +++ b/extensions/src/multiplayer/thirdpersoncamera.cpp @@ -664,6 +664,13 @@ MxBool ThirdPersonCamera::HandleCameraRelativeMovement( // Normalize movement direction float moveDirLen = SDL_sqrtf(moveDirX * moveDirX + moveDirZ * moveDirZ); bool hasInput = moveDirLen > 0.001f; + + // Block translation during multi-part emotes (rotation/pan/zoom handled separately) + if (m_animator.IsInMultiPartEmote()) { + hasInput = false; + m_smoothedSpeed = 0.0f; + } + if (hasInput) { moveDirX /= moveDirLen; moveDirZ /= moveDirLen;