From f7947e17202cdafa63eef8ca6216aba0b62db9ed Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Sat, 28 Mar 2026 15:08:23 -0700 Subject: [PATCH] Refactor extensions: DRY, named constants, and cleanup Replace magic numbers with named constants across third-person camera, multiplayer, and common utilities. Extract duplicated code into shared helpers: CancelExternalAnim, StartEmotePhase, DeriveDependentIndices, ReaddROI, SendFixedMessage. Deduplicate finger-down handling via TryClaimFinger and tighten Extensions::Enable() dispatch with a table-driven approach. Fix missing include in sceneplayer. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../extensions/common/characteranimator.h | 5 ++ .../include/extensions/common/constants.h | 2 + .../extensions/common/customizestate.h | 3 + .../include/extensions/multiplayer/protocol.h | 30 ++++++++- .../extensions/multiplayer/remoteplayer.h | 2 +- .../extensions/thirdpersoncamera/controller.h | 1 + .../thirdpersoncamera/inputhandler.h | 4 ++ .../thirdpersoncamera/orbitcamera.h | 4 ++ extensions/src/common/characteranimator.cpp | 59 ++++++++---------- extensions/src/common/charactercustomizer.cpp | 5 +- extensions/src/common/customizestate.cpp | 12 ++-- extensions/src/extensions.cpp | 62 ++++++++++++------- .../src/multiplayer/animation/sceneplayer.cpp | 1 + extensions/src/multiplayer/networkmanager.cpp | 28 +++------ extensions/src/multiplayer/protocol.cpp | 4 +- extensions/src/multiplayer/remoteplayer.cpp | 27 ++++---- extensions/src/multiplayer/worldstatesync.cpp | 10 +-- extensions/src/thirdpersoncamera.cpp | 3 +- .../src/thirdpersoncamera/controller.cpp | 58 ++++++++--------- .../src/thirdpersoncamera/inputhandler.cpp | 37 ++++------- .../src/thirdpersoncamera/orbitcamera.cpp | 23 +++---- 21 files changed, 197 insertions(+), 183 deletions(-) diff --git a/extensions/include/extensions/common/characteranimator.h b/extensions/include/extensions/common/characteranimator.h index 604bf95b..8a957a5a 100644 --- a/extensions/include/extensions/common/characteranimator.h +++ b/extensions/include/extensions/common/characteranimator.h @@ -102,10 +102,15 @@ class CharacterAnimator { void SetAnimTime(float p_time) { m_animTime = p_time; } void ResetAnimState(); + static constexpr float ANIM_TIME_SCALE = 2000.0f; + static constexpr float EMOTE_TIME_SCALE = 1000.0f; + static constexpr float IDLE_DELAY_SECONDS = 2.5f; + private: using AnimCache = AnimUtils::AnimCache; AnimCache* GetOrBuildAnimCache(LegoROI* p_roi, const char* p_animName); + void StartEmotePhase(uint8_t p_emoteId, int p_phaseIndex, AnimCache* p_cache, LegoROI* p_roi); void ClearFrozenState(); void ClearPropGroup(PropGroup& p_group); void BuildEmoteProps(PropGroup& p_group, LegoAnim* p_anim, LegoROI* p_playerROI); diff --git a/extensions/include/extensions/common/constants.h b/extensions/include/extensions/common/constants.h index b4a1b952..df63af20 100644 --- a/extensions/include/extensions/common/constants.h +++ b/extensions/include/extensions/common/constants.h @@ -32,6 +32,8 @@ enum WorldChangeType : uint8_t { static const uint8_t DISPLAY_ACTOR_NONE = 0xFF; +static constexpr float FIXED_TICK_DELTA = 0.016f; // ~60 Hz + // Validate actorId is a playable character (1-5, not brickster) inline bool IsValidActorId(uint8_t p_actorId) { diff --git a/extensions/include/extensions/common/customizestate.h b/extensions/include/extensions/common/customizestate.h index 71f5d15b..ee65baa4 100644 --- a/extensions/include/extensions/common/customizestate.h +++ b/extensions/include/extensions/common/customizestate.h @@ -19,6 +19,9 @@ struct CustomizeState { void Unpack(const uint8_t p_in[5]); bool operator==(const CustomizeState& p_other) const; bool operator!=(const CustomizeState& p_other) const { return !(*this == p_other); } + +private: + void DeriveDependentIndices(); }; } // namespace Common diff --git a/extensions/include/extensions/multiplayer/protocol.h b/extensions/include/extensions/multiplayer/protocol.h index 0afc1316..7ae53b4b 100644 --- a/extensions/include/extensions/multiplayer/protocol.h +++ b/extensions/include/extensions/multiplayer/protocol.h @@ -1,6 +1,7 @@ #pragma once #include "extensions/common/constants.h" +#include "extensions/multiplayer/networktransport.h" #include #include @@ -10,6 +11,8 @@ namespace Multiplayer { +static constexpr size_t USERNAME_BUFFER_SIZE = 8; // 7 chars + null terminator + // Routing target constants for MessageHeader.target const uint32_t TARGET_BROADCAST = 0; // Broadcast to all except sender const uint32_t TARGET_HOST = 0xFFFFFFFF; // Send to host only @@ -100,7 +103,7 @@ struct PlayerStateMsg { float speed; uint8_t walkAnimId; // Index into walk animation table (0 = default) uint8_t idleAnimId; // Index into idle animation table (0 = default) - char name[8]; // Player display name (7 chars + null terminator) + char name[USERNAME_BUFFER_SIZE]; // Player display name (7 chars + null terminator) uint8_t displayActorIndex; // Index into g_actorInfoInit (0-65) uint8_t customizeData[5]; // Packed CustomizeState uint8_t customizeFlags; // Bit 0 = allowRemoteCustomize @@ -200,7 +203,7 @@ struct AnimStartMsg { struct AnimCompletionParticipant { uint32_t peerId; int8_t charIndex; // Participant's character (g_actorInfoInit index) - char displayName[8]; // 7 chars + null + char displayName[USERNAME_BUFFER_SIZE]; // 7 chars + null }; // Host -> All: animation completed successfully (natural completion only, not cancellation) @@ -214,11 +217,17 @@ struct AnimCompleteMsg { #pragma pack(pop) +// Bitmask constants for PlayerStateMsg::customizeFlags +static constexpr uint8_t CUSTOMIZE_FLAG_ALLOW_REMOTE = 0x01; +static constexpr uint8_t CUSTOMIZE_FLAG_FROZEN = 0x02; +static constexpr uint8_t CUSTOMIZE_FLAG_FROZEN_EMOTE_SHIFT = 2; +static constexpr uint8_t CUSTOMIZE_FLAG_FROZEN_EMOTE_MASK = 0x07; + using Extensions::Common::IsValidActorId; // Convert LegoGameState::Username letter indices (0-25 = A-Z) to ASCII. // Writes up to 7 characters + null terminator into p_out (must be at least 8 bytes). -void EncodeUsername(char p_out[8]); +void EncodeUsername(char p_out[USERNAME_BUFFER_SIZE]); using Extensions::Common::DISPLAY_ACTOR_NONE; @@ -255,4 +264,19 @@ inline bool DeserializeMsg(const uint8_t* p_data, size_t p_length, T& p_out) return true; } +// Serialize and send a fixed-size message via the transport. +template +inline void SendFixedMessage(NetworkTransport* p_transport, const T& p_msg) +{ + if (!p_transport || !p_transport->IsConnected()) { + return; + } + + uint8_t buf[sizeof(T)]; + size_t len = SerializeMsg(buf, sizeof(buf), p_msg); + if (len > 0) { + p_transport->Send(buf, len); + } +} + } // namespace Multiplayer diff --git a/extensions/include/extensions/multiplayer/remoteplayer.h b/extensions/include/extensions/multiplayer/remoteplayer.h index a4d4e247..7168157f 100644 --- a/extensions/include/extensions/multiplayer/remoteplayer.h +++ b/extensions/include/extensions/multiplayer/remoteplayer.h @@ -81,7 +81,7 @@ class RemotePlayer { uint8_t m_actorId; uint8_t m_displayActorIndex; char m_uniqueName[32]; - char m_displayName[8]; + char m_displayName[USERNAME_BUFFER_SIZE]; LegoROI* m_roi; bool m_spawned; diff --git a/extensions/include/extensions/thirdpersoncamera/controller.h b/extensions/include/extensions/thirdpersoncamera/controller.h index f04a3190..f2e86c1a 100644 --- a/extensions/include/extensions/thirdpersoncamera/controller.h +++ b/extensions/include/extensions/thirdpersoncamera/controller.h @@ -126,6 +126,7 @@ class Controller { static constexpr float MIN_DISTANCE = OrbitCamera::MIN_DISTANCE; private: + void CancelExternalAnim(); void Deactivate(); void ReinitForCharacter(); diff --git a/extensions/include/extensions/thirdpersoncamera/inputhandler.h b/extensions/include/extensions/thirdpersoncamera/inputhandler.h index 36784ebb..ffb8aea0 100644 --- a/extensions/include/extensions/thirdpersoncamera/inputhandler.h +++ b/extensions/include/extensions/thirdpersoncamera/inputhandler.h @@ -33,6 +33,10 @@ class InputHandler { static constexpr float CAMERA_ZONE_X = 0.5f; static constexpr float PINCH_TRANSITION_THRESHOLD = 0.03f; static constexpr Uint64 LMB_HOLD_THRESHOLD_MS = 300; + static constexpr float MOUSE_SENSITIVITY = 0.005f; + static constexpr float MOUSE_WHEEL_ZOOM_STEP = 0.5f; + static constexpr float TOUCH_YAW_PITCH_SCALE = 2.0f; + static constexpr float PINCH_ZOOM_SCALE = 6.0f; private: struct TouchState { diff --git a/extensions/include/extensions/thirdpersoncamera/orbitcamera.h b/extensions/include/extensions/thirdpersoncamera/orbitcamera.h index 36599deb..aef030af 100644 --- a/extensions/include/extensions/thirdpersoncamera/orbitcamera.h +++ b/extensions/include/extensions/thirdpersoncamera/orbitcamera.h @@ -57,6 +57,10 @@ class OrbitCamera { static constexpr float MIN_DISTANCE = 1.5f; static constexpr float SWITCH_TO_FIRST_PERSON_DISTANCE = 0.5f; static constexpr float MAX_DISTANCE = 15.0f; + static constexpr float CHARACTER_TURN_RATE = 10.0f; + static constexpr float JOYSTICK_CENTER = 50.0f; + static constexpr float JOYSTICK_DEAD_ZONE = 0.1f; + static constexpr float MOVEMENT_DIR_EPSILON = 0.001f; private: void ComputeOrbitVectors(float p_yaw, Mx3DPointFloat& p_at, Mx3DPointFloat& p_dir, Mx3DPointFloat& p_up) const; diff --git a/extensions/src/common/characteranimator.cpp b/extensions/src/common/characteranimator.cpp index c0c0b4b0..3d82fc54 100644 --- a/extensions/src/common/characteranimator.cpp +++ b/extensions/src/common/characteranimator.cpp @@ -89,7 +89,7 @@ void CharacterAnimator::Tick(float p_deltaTime, LegoROI* p_roi, bool p_isMoving) } if (p_isMoving) { - m_animTime += p_deltaTime * 2000.0f; + m_animTime += p_deltaTime * ANIM_TIME_SCALE; } float duration = (float) walkAnim->GetDuration(); if (duration > 0.0f) { @@ -104,7 +104,7 @@ void CharacterAnimator::Tick(float p_deltaTime, LegoROI* p_roi, bool p_isMoving) } else if (m_emoteActive && m_emoteAnimCache && m_emoteAnimCache->anim && m_emoteAnimCache->roiMap) { // Emote playback - m_emoteTime += p_deltaTime * 1000.0f; + m_emoteTime += p_deltaTime * EMOTE_TIME_SCALE; if (m_emoteTime >= m_emoteDuration) { if (IsMultiPartEmote(m_currentEmoteId) && m_frozenEmoteId < 0) { @@ -169,8 +169,8 @@ void CharacterAnimator::Tick(float p_deltaTime, LegoROI* p_roi, bool p_isMoving) m_idleTime += p_deltaTime; - // Hold standing pose for 2.5s, then loop breathing/swaying - if (m_idleTime >= 2.5f) { + // Hold standing pose, then loop breathing/swaying + if (m_idleTime >= IDLE_DELAY_SECONDS) { m_idleAnimTime += p_deltaTime * 1000.0f; } @@ -210,6 +210,25 @@ void CharacterAnimator::SetIdleAnimId(uint8_t p_idleAnimId, LegoROI* p_roi) } } +void CharacterAnimator::StartEmotePhase(uint8_t p_emoteId, int p_phaseIndex, AnimCache* p_cache, LegoROI* p_roi) +{ + StopClickAnimation(); + ClearPropGroup(m_emotePropGroup); + + m_currentEmoteId = p_emoteId; + m_emoteAnimCache = p_cache; + m_emoteTime = 0.0f; + m_emoteDuration = (float) p_cache->anim->GetDuration(); + m_emoteActive = true; + + BuildEmoteProps(m_emotePropGroup, p_cache->anim, p_roi); + + const char* sound = g_emoteEntries[p_emoteId].phases[p_phaseIndex].sound; + if (sound) { + PlayROISound(sound, p_roi); + } +} + void CharacterAnimator::TriggerEmote(uint8_t p_emoteId, LegoROI* p_roi, bool p_isMoving) { if (p_emoteId >= g_emoteAnimCount || !p_roi || m_currentVehicleType != VEHICLE_NONE) { @@ -224,21 +243,7 @@ void CharacterAnimator::TriggerEmote(uint8_t p_emoteId, LegoROI* p_roi, bool p_i return; } - StopClickAnimation(); - ClearPropGroup(m_emotePropGroup); - - m_currentEmoteId = p_emoteId; - m_emoteAnimCache = cache; - m_emoteTime = 0.0f; - m_emoteDuration = (float) cache->anim->GetDuration(); - m_emoteActive = true; - - BuildEmoteProps(m_emotePropGroup, cache->anim, p_roi); - - const char* sound = g_emoteEntries[p_emoteId].phases[1].sound; - if (sound) { - PlayROISound(sound, p_roi); - } + StartEmotePhase(p_emoteId, 1, cache, p_roi); if (m_config.saveEmoteTransform) { m_emoteParentTransform = m_frozenParentTransform; @@ -263,21 +268,7 @@ void CharacterAnimator::TriggerEmote(uint8_t p_emoteId, LegoROI* p_roi, bool p_i return; } - StopClickAnimation(); - ClearPropGroup(m_emotePropGroup); - - m_currentEmoteId = p_emoteId; - m_emoteAnimCache = cache; - m_emoteTime = 0.0f; - m_emoteDuration = (float) cache->anim->GetDuration(); - m_emoteActive = true; - - BuildEmoteProps(m_emotePropGroup, cache->anim, p_roi); - - const char* sound = g_emoteEntries[p_emoteId].phases[0].sound; - if (sound) { - PlayROISound(sound, p_roi); - } + StartEmotePhase(p_emoteId, 0, cache, p_roi); // Save clean transform to prevent scale accumulation during emote if (m_config.saveEmoteTransform) { diff --git a/extensions/src/common/charactercustomizer.cpp b/extensions/src/common/charactercustomizer.cpp index c46d6cf9..201c97e2 100644 --- a/extensions/src/common/charactercustomizer.cpp +++ b/extensions/src/common/charactercustomizer.cpp @@ -29,6 +29,7 @@ static const MxU32 g_characterAnimationId = 10; static const MxU32 g_maxSound = 9; static const MxU32 g_maxMove = 4; +static constexpr int COLORABLE_PARTS_COUNT = 10; static uint32_t s_variantCounter = 10000; // MARK: Private helpers @@ -62,7 +63,7 @@ bool CharacterCustomizer::SwitchColor( int p_partIndex ) { - if (p_partIndex < 0 || p_partIndex >= 10) { + if (p_partIndex < 0 || p_partIndex >= COLORABLE_PARTS_COUNT) { return false; } @@ -187,7 +188,7 @@ void CharacterCustomizer::ApplyChange( int CharacterCustomizer::MapClickedPartIndex(const char* p_partName) { - for (int i = 0; i < 10; i++) { + for (int i = 0; i < COLORABLE_PARTS_COUNT; i++) { if (!SDL_strcasecmp(p_partName, g_actorLODs[i + 1].m_name)) { return i; } diff --git a/extensions/src/common/customizestate.cpp b/extensions/src/common/customizestate.cpp index b19c104c..43758132 100644 --- a/extensions/src/common/customizestate.cpp +++ b/extensions/src/common/customizestate.cpp @@ -23,11 +23,7 @@ void CustomizeState::InitFromActorInfo(uint8_t p_actorInfoIndex) colorIndices[c_leglftPart] = info.m_parts[c_leglftPart].m_nameIndex; colorIndices[c_legrtPart] = info.m_parts[c_legrtPart].m_nameIndex; - // Derive dependent parts (must match Unpack derivation rules) - colorIndices[c_bodyPart] = colorIndices[c_infogronPart]; - colorIndices[c_headPart] = colorIndices[c_infohatPart]; - colorIndices[c_clawlftPart] = colorIndices[c_armlftPart]; - colorIndices[c_clawrtPart] = colorIndices[c_armrtPart]; + DeriveDependentIndices(); hatVariantIndex = info.m_parts[c_infohatPart].m_partNameIndex; sound = (uint8_t) info.m_sound; @@ -75,7 +71,11 @@ void CustomizeState::Unpack(const uint8_t p_in[5]) colorIndices[c_leglftPart] = p_in[4] & 0x0F; colorIndices[c_legrtPart] = (p_in[4] >> 4) & 0x0F; - // Derive non-independent parts + DeriveDependentIndices(); +} + +void CustomizeState::DeriveDependentIndices() +{ colorIndices[c_bodyPart] = colorIndices[c_infogronPart]; colorIndices[c_headPart] = colorIndices[c_infohatPart]; colorIndices[c_clawlftPart] = colorIndices[c_armlftPart]; diff --git a/extensions/src/extensions.cpp b/extensions/src/extensions.cpp index 51e880fb..e8fa731a 100644 --- a/extensions/src/extensions.cpp +++ b/extensions/src/extensions.cpp @@ -7,33 +7,47 @@ #include +using namespace Extensions; + +static void InitTextureLoader(std::map p_options) +{ + TextureLoaderExt::options = std::move(p_options); + TextureLoaderExt::enabled = true; + TextureLoaderExt::Initialize(); +} + +static void InitSiLoader(std::map p_options) +{ + SiLoaderExt::options = std::move(p_options); + SiLoaderExt::enabled = true; + SiLoaderExt::Initialize(); +} + +static void InitThirdPersonCamera(std::map p_options) +{ + ThirdPersonCameraExt::options = std::move(p_options); + ThirdPersonCameraExt::enabled = true; + ThirdPersonCameraExt::Initialize(); +} + +static void InitMultiplayer(std::map p_options) +{ + MultiplayerExt::options = std::move(p_options); + MultiplayerExt::enabled = true; + MultiplayerExt::Initialize(); +} + +using InitFn = void (*)(std::map); + +static const InitFn extensionInits[] = {InitTextureLoader, InitSiLoader, InitThirdPersonCamera, InitMultiplayer}; + void Extensions::Enable(const char* p_key, std::map p_options) { - for (const char* key : availableExtensions) { - if (!SDL_strcasecmp(p_key, key)) { - if (!SDL_strcasecmp(p_key, "extensions:texture loader")) { - TextureLoaderExt::options = std::move(p_options); - TextureLoaderExt::enabled = true; - TextureLoaderExt::Initialize(); - } - else if (!SDL_strcasecmp(p_key, "extensions:si loader")) { - SiLoaderExt::options = std::move(p_options); - SiLoaderExt::enabled = true; - SiLoaderExt::Initialize(); - } - else if (!SDL_strcasecmp(p_key, "extensions:third person camera")) { - ThirdPersonCameraExt::options = std::move(p_options); - ThirdPersonCameraExt::enabled = true; - ThirdPersonCameraExt::Initialize(); - } - else if (!SDL_strcasecmp(p_key, "extensions:multiplayer")) { - MultiplayerExt::options = std::move(p_options); - MultiplayerExt::enabled = true; - MultiplayerExt::Initialize(); - } - + for (int i = 0; i < (int) (sizeof(availableExtensions) / sizeof(availableExtensions[0])); i++) { + if (!SDL_strcasecmp(p_key, availableExtensions[i])) { + extensionInits[i](std::move(p_options)); SDL_Log("Enabled extension: %s", p_key); - break; + return; } } } diff --git a/extensions/src/multiplayer/animation/sceneplayer.cpp b/extensions/src/multiplayer/animation/sceneplayer.cpp index 67bf58ef..f2d913e2 100644 --- a/extensions/src/multiplayer/animation/sceneplayer.cpp +++ b/extensions/src/multiplayer/animation/sceneplayer.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/extensions/src/multiplayer/networkmanager.cpp b/extensions/src/multiplayer/networkmanager.cpp index ce252e64..0a7ea518 100644 --- a/extensions/src/multiplayer/networkmanager.cpp +++ b/extensions/src/multiplayer/networkmanager.cpp @@ -53,15 +53,7 @@ static void ExtractSlotPeerIds(const AnimUpdateMsg& p_msg, uint32_t p_out[8]) template void NetworkManager::SendMessage(const T& p_msg) { - if (!m_transport || !m_transport->IsConnected()) { - return; - } - - uint8_t buf[sizeof(T)]; - size_t len = SerializeMsg(buf, sizeof(buf), p_msg); - if (len > 0) { - m_transport->Send(buf, len); - } + SendFixedMessage(m_transport, p_msg); } NetworkManager::NetworkManager() @@ -148,7 +140,7 @@ MxResult NetworkManager::Tickle() // Create local name bubble when display ROI becomes available if (m_showNameBubbles && !m_localNameBubble && cam->GetDisplayROI()) { - char name[8]; + char name[USERNAME_BUFFER_SIZE]; EncodeUsername(name); m_localNameBubble = new NameBubbleRenderer(); m_localNameBubble->Create(name); @@ -221,7 +213,7 @@ MxResult NetworkManager::Tickle() } ProcessIncomingPackets(); - UpdateRemotePlayers(0.016f); + UpdateRemotePlayers(Common::FIXED_TICK_DELTA); TickAnimation(); // Re-read time; ProcessIncomingPackets may have advanced SDL_GetTicks. @@ -765,11 +757,11 @@ void NetworkManager::BroadcastLocalState() msg.displayActorIndex = cam->GetDisplayActorIndex(); cam->GetCustomizeState().Pack(msg.customizeData); - // Encode multi-part emote frozen state (0x02 = frozen, emote ID in bits 2-4, max 8 emotes) + // Encode multi-part emote frozen state int8_t frozenId = cam->GetFrozenEmoteId(); if (frozenId >= 0) { - msg.customizeFlags |= 0x02; - msg.customizeFlags |= (frozenId & 0x07) << 2; + msg.customizeFlags |= CUSTOMIZE_FLAG_FROZEN; + msg.customizeFlags |= (frozenId & CUSTOMIZE_FLAG_FROZEN_EMOTE_MASK) << CUSTOMIZE_FLAG_FROZEN_EMOTE_SHIFT; } // Zero speed when in any phase of a multi-part emote or animation playback @@ -778,7 +770,7 @@ void NetworkManager::BroadcastLocalState() } } - msg.customizeFlags |= m_localAllowRemoteCustomize ? 0x01 : 0x00; + msg.customizeFlags |= m_localAllowRemoteCustomize ? CUSTOMIZE_FLAG_ALLOW_REMOTE : 0x00; SendMessage(msg); } @@ -1960,10 +1952,10 @@ void NetworkManager::HandleAnimComplete(const AnimCompleteMsg& p_msg) } first = false; const AnimCompletionParticipant& p = p_msg.participants[i]; - // Ensure null-termination safety for displayName (protocol uses fixed char[8]) - char name[8]; + // Ensure null-termination safety for displayName + char name[USERNAME_BUFFER_SIZE]; SDL_memcpy(name, p.displayName, sizeof(name)); - name[7] = '\0'; + name[USERNAME_BUFFER_SIZE - 1] = '\0'; json += "{\"charIndex\":"; json += std::to_string(static_cast(p.charIndex)); json += ",\"displayName\":\""; diff --git a/extensions/src/multiplayer/protocol.cpp b/extensions/src/multiplayer/protocol.cpp index 987f001d..6ed8ab36 100644 --- a/extensions/src/multiplayer/protocol.cpp +++ b/extensions/src/multiplayer/protocol.cpp @@ -8,9 +8,9 @@ namespace Multiplayer { -void EncodeUsername(char p_out[8]) +void EncodeUsername(char p_out[USERNAME_BUFFER_SIZE]) { - SDL_memset(p_out, 0, 8); + SDL_memset(p_out, 0, USERNAME_BUFFER_SIZE); LegoGameState* gs = GameState(); if (gs && gs->m_playerCount > 0) { const LegoGameState::Username& username = gs->m_players[0]; diff --git a/extensions/src/multiplayer/remoteplayer.cpp b/extensions/src/multiplayer/remoteplayer.cpp index 63fd99c0..0dc7e522 100644 --- a/extensions/src/multiplayer/remoteplayer.cpp +++ b/extensions/src/multiplayer/remoteplayer.cpp @@ -28,6 +28,9 @@ using Common::g_walkAnimCount; using Common::IsLargeVehicle; using Common::WORLD_NOT_VISIBLE; +static constexpr float REMOTE_SPEED_THRESHOLD = 0.01f; +static constexpr float POSITION_LERP_FACTOR = 0.2f; + RemotePlayer::RemotePlayer(uint32_t p_peerId, uint8_t p_actorId, uint8_t p_displayActorIndex) : m_peerId(p_peerId), m_actorId(p_actorId), m_displayActorIndex(p_displayActorIndex), m_roi(nullptr), m_spawned(false), m_visible(false), m_targetSpeed(0.0f), m_targetVehicleType(VEHICLE_NONE), @@ -139,7 +142,7 @@ void RemotePlayer::UpdateFromNetwork(const PlayerStateMsg& p_msg) SET3(m_targetPosition, p_msg.position); SET3(m_targetDirection, p_msg.direction); SET3(m_targetUp, p_msg.up); - m_targetSpeed = posDelta > 0.01f ? posDelta : 0.0f; + m_targetSpeed = posDelta > REMOTE_SPEED_THRESHOLD ? posDelta : 0.0f; m_targetVehicleType = p_msg.vehicleType; m_targetWorldId = p_msg.worldId; m_lastUpdateTime = SDL_GetTicks(); @@ -153,9 +156,9 @@ void RemotePlayer::UpdateFromNetwork(const PlayerStateMsg& p_msg) } // Update display name (can change when player switches save file) - char newName[8]; + char newName[USERNAME_BUFFER_SIZE]; SDL_memcpy(newName, p_msg.name, sizeof(newName)); - newName[sizeof(newName) - 1] = '\0'; + newName[USERNAME_BUFFER_SIZE - 1] = '\0'; if (SDL_strcmp(m_displayName, newName) != 0) { SDL_memcpy(m_displayName, newName, sizeof(m_displayName)); @@ -179,11 +182,13 @@ void RemotePlayer::UpdateFromNetwork(const PlayerStateMsg& p_msg) } // Update allow remote customize flag - m_allowRemoteCustomize = (p_msg.customizeFlags & 0x01) != 0; + m_allowRemoteCustomize = (p_msg.customizeFlags & CUSTOMIZE_FLAG_ALLOW_REMOTE) != 0; // Sync multi-part emote frozen state from remote - bool isFrozen = (p_msg.customizeFlags & 0x02) != 0; - int8_t frozenEmoteId = isFrozen ? (int8_t) ((p_msg.customizeFlags >> 2) & 0x07) : -1; + bool isFrozen = (p_msg.customizeFlags & CUSTOMIZE_FLAG_FROZEN) != 0; + int8_t frozenEmoteId = + isFrozen ? (int8_t) ((p_msg.customizeFlags >> CUSTOMIZE_FLAG_FROZEN_EMOTE_SHIFT) & CUSTOMIZE_FLAG_FROZEN_EMOTE_MASK) + : -1; if (frozenEmoteId != m_animator.GetFrozenEmoteId()) { m_animator.SetFrozenEmoteId(frozenEmoteId, m_roi); } @@ -217,7 +222,7 @@ void RemotePlayer::Tick(float p_deltaTime) UpdateVehicleState(); UpdateTransform(p_deltaTime); - bool isMoving = m_targetSpeed > 0.01f; + bool isMoving = m_targetSpeed > REMOTE_SPEED_THRESHOLD; if (m_animator.GetFrozenEmoteId() >= 0) { isMoving = false; } @@ -281,7 +286,7 @@ void RemotePlayer::TriggerEmote(uint8_t p_emoteId) return; } - bool isMoving = m_targetSpeed > 0.01f; + bool isMoving = m_targetSpeed > REMOTE_SPEED_THRESHOLD; if (m_animator.GetFrozenEmoteId() >= 0) { isMoving = false; } @@ -290,9 +295,9 @@ void RemotePlayer::TriggerEmote(uint8_t p_emoteId) void RemotePlayer::UpdateTransform(float p_deltaTime) { - LERP3(m_currentPosition, m_currentPosition, m_targetPosition, 0.2f); - LERP3(m_currentDirection, m_currentDirection, m_targetDirection, 0.2f); - LERP3(m_currentUp, m_currentUp, m_targetUp, 0.2f); + LERP3(m_currentPosition, m_currentPosition, m_targetPosition, POSITION_LERP_FACTOR); + LERP3(m_currentDirection, m_currentDirection, m_targetDirection, POSITION_LERP_FACTOR); + LERP3(m_currentUp, m_currentUp, m_targetUp, POSITION_LERP_FACTOR); // The network sends forward-z (visual forward). Character meshes face -z, // so negate to get backward-z for the ROI (mesh faces the correct way). diff --git a/extensions/src/multiplayer/worldstatesync.cpp b/extensions/src/multiplayer/worldstatesync.cpp index 54c73118..45e2d1c1 100644 --- a/extensions/src/multiplayer/worldstatesync.cpp +++ b/extensions/src/multiplayer/worldstatesync.cpp @@ -26,15 +26,7 @@ using namespace Multiplayer; template void WorldStateSync::SendMessage(const T& p_msg) { - if (!m_transport || !m_transport->IsConnected()) { - return; - } - - uint8_t buf[sizeof(T)]; - size_t len = SerializeMsg(buf, sizeof(buf), p_msg); - if (len > 0) { - m_transport->Send(buf, len); - } + SendFixedMessage(m_transport, p_msg); } WorldStateSync::WorldStateSync() diff --git a/extensions/src/thirdpersoncamera.cpp b/extensions/src/thirdpersoncamera.cpp index 92e4f7e2..d516369b 100644 --- a/extensions/src/thirdpersoncamera.cpp +++ b/extensions/src/thirdpersoncamera.cpp @@ -2,6 +2,7 @@ #include "extensions/common/arearestriction.h" #include "extensions/common/charactercustomizer.h" +#include "extensions/common/constants.h" #include "extensions/thirdpersoncamera/controller.h" #include "islepathactor.h" #include "legoeventnotificationparam.h" @@ -39,7 +40,7 @@ class TickleAdapter : public MxCore { MxResult Tickle() override { if (m_camera) { - m_camera->Tick(0.016f); + m_camera->Tick(FIXED_TICK_DELTA); } return SUCCESS; } diff --git a/extensions/src/thirdpersoncamera/controller.cpp b/extensions/src/thirdpersoncamera/controller.cpp index 369fd013..1bcb9228 100644 --- a/extensions/src/thirdpersoncamera/controller.cpp +++ b/extensions/src/thirdpersoncamera/controller.cpp @@ -28,6 +28,14 @@ using namespace Extensions; using namespace Extensions::Common; using namespace Extensions::ThirdPersonCamera; +static constexpr float SPEED_EPSILON = 0.01f; + +static void ReaddROI(LegoROI& p_roi) +{ + VideoManager()->Get3DManager()->Remove(p_roi); + VideoManager()->Get3DManager()->Add(p_roi); +} + Controller::Controller() : m_animator(CharacterAnimatorConfig{/*.saveEmoteTransform=*/true, /*.propSuffix=*/0}), m_enabled(false), m_active(false), m_pendingWorldTransition(false), m_animPlaying(false), m_animLockDisplay(false), @@ -51,9 +59,8 @@ void Controller::Disable(bool p_preserveTouch) } } -void Controller::Deactivate() +void Controller::CancelExternalAnim() { - // Stop external animation before destroying the display ROI if (m_animPlaying) { if (m_animStopCallback) { m_animStopCallback(); @@ -61,6 +68,12 @@ void Controller::Deactivate() m_animPlaying = false; m_animStopCallback = nullptr; } +} + +void Controller::Deactivate() +{ + // Stop external animation before destroying the display ROI + CancelExternalAnim(); if (m_active && m_playerROI) { m_playerROI->SetVisibility(FALSE); @@ -107,13 +120,7 @@ void Controller::OnActorEnter(IslePathActor* p_actor) // Stop external animation before modifying ride/display state — // the ScenePlayer may hold a reference to the ride vehicle ROI. - if (m_animPlaying) { - if (m_animStopCallback) { - m_animStopCallback(); - } - m_animPlaying = false; - m_animStopCallback = nullptr; - } + CancelExternalAnim(); LegoROI* newROI = userActor->GetROI(); if (!newROI) { @@ -150,8 +157,7 @@ void Controller::OnActorEnter(IslePathActor* p_actor) m_playerROI->SetVisibility(TRUE); - VideoManager()->Get3DManager()->Remove(*m_playerROI); - VideoManager()->Get3DManager()->Add(*m_playerROI); + ReaddROI(*m_playerROI); m_animator.InitAnimCaches(m_playerROI); m_animator.ResetAnimState(); @@ -169,13 +175,7 @@ void Controller::OnActorExit(IslePathActor* p_actor) // Stop external animation before clearing ride animation state — // the ScenePlayer may hold a reference to the ride vehicle ROI. - if (m_animPlaying) { - if (m_animStopCallback) { - m_animStopCallback(); - } - m_animPlaying = false; - m_animStopCallback = nullptr; - } + CancelExternalAnim(); if (m_animator.GetCurrentVehicleType() != VEHICLE_NONE) { m_animator.ClearRideAnimation(); @@ -257,8 +257,8 @@ void Controller::Tick(float p_deltaTime) AnimUtils::EnsureROIMapVisibility(m_animator.GetRideRoiMap(), m_animator.GetRideRoiMapSize()); float speed = actor->GetWorldSpeed(); - if (SDL_fabsf(speed) > 0.01f) { - m_animator.SetAnimTime(m_animator.GetAnimTime() + p_deltaTime * 2000.0f); + if (SDL_fabsf(speed) > SPEED_EPSILON) { + m_animator.SetAnimTime(m_animator.GetAnimTime() + p_deltaTime * CharacterAnimator::ANIM_TIME_SCALE); } MxMatrix transform(actor->GetROI()->GetLocal2World()); @@ -300,7 +300,7 @@ void Controller::Tick(float p_deltaTime) } float speed = userActor->GetWorldSpeed(); - bool isMoving = SDL_fabsf(speed) > 0.01f; + bool isMoving = SDL_fabsf(speed) > SPEED_EPSILON; if (m_animator.IsInMultiPartEmote()) { isMoving = false; userActor->SetWorldSpeed(0.0f); @@ -341,7 +341,7 @@ void Controller::TriggerEmote(uint8_t p_emoteId) return; } - bool isMoving = SDL_fabsf(userActor->GetWorldSpeed()) > 0.01f; + bool isMoving = SDL_fabsf(userActor->GetWorldSpeed()) > SPEED_EPSILON; if (m_animator.IsInMultiPartEmote()) { isMoving = false; } @@ -385,13 +385,7 @@ void Controller::OnWorldDisabled(LegoWorld* p_world) } // Stop external animation before destroying the display ROI - if (m_animPlaying) { - if (m_animStopCallback) { - m_animStopCallback(); - } - m_animPlaying = false; - m_animStopCallback = nullptr; - } + CancelExternalAnim(); m_active = false; m_pendingWorldTransition = false; @@ -497,8 +491,7 @@ void Controller::ReinitForCharacter() m_pendingWorldTransition = false; - VideoManager()->Get3DManager()->Remove(*m_playerROI); - VideoManager()->Get3DManager()->Add(*m_playerROI); + ReaddROI(*m_playerROI); m_active = true; m_orbit.SetupCamera(userActor); m_animator.BuildRideAnimation(vehicleType, m_playerROI, 0); @@ -514,8 +507,7 @@ void Controller::ReinitForCharacter() m_playerROI->SetVisibility(TRUE); - VideoManager()->Get3DManager()->Remove(*m_playerROI); - VideoManager()->Get3DManager()->Add(*m_playerROI); + ReaddROI(*m_playerROI); m_animator.InitAnimCaches(m_playerROI); m_animator.ResetAnimState(); diff --git a/extensions/src/thirdpersoncamera/inputhandler.cpp b/extensions/src/thirdpersoncamera/inputhandler.cpp index 510b009b..bf59dcb4 100644 --- a/extensions/src/thirdpersoncamera/inputhandler.cpp +++ b/extensions/src/thirdpersoncamera/inputhandler.cpp @@ -16,7 +16,7 @@ InputHandler::InputHandler() bool InputHandler::TryClaimFinger(const SDL_TouchFingerEvent& p_event, bool p_active) { - if (!p_active || m_touch.count >= 2 || p_event.x < CAMERA_ZONE_X) { + if (!p_active || m_touch.count >= 2 || p_event.x < CAMERA_ZONE_X || IsFingerTracked(p_event.fingerID)) { return false; } @@ -104,7 +104,7 @@ void InputHandler::HandleSDLEvent(SDL_Event* p_event, OrbitCamera& p_orbit, bool m_wantsAutoDisable = true; break; } - p_orbit.AdjustDistance(-p_event->wheel.y * 0.5f); + p_orbit.AdjustDistance(-p_event->wheel.y * MOUSE_WHEEL_ZOOM_STEP); p_orbit.ClampDistance(); break; @@ -113,8 +113,8 @@ void InputHandler::HandleSDLEvent(SDL_Event* p_event, OrbitCamera& p_orbit, bool break; } if (m_rightButtonHeld) { - p_orbit.AdjustYaw(-p_event->motion.xrel * 0.005f); - p_orbit.AdjustPitch(p_event->motion.yrel * 0.005f); + p_orbit.AdjustYaw(-p_event->motion.xrel * MOUSE_SENSITIVITY); + p_orbit.AdjustPitch(p_event->motion.yrel * MOUSE_SENSITIVITY); p_orbit.ClampPitch(); } break; @@ -144,24 +144,9 @@ void InputHandler::HandleSDLEvent(SDL_Event* p_event, OrbitCamera& p_orbit, bool break; } - case SDL_EVENT_FINGER_DOWN: { - if (!IsFingerTracked(p_event->tfinger.fingerID) && m_touch.count < 2 && p_event->tfinger.x >= CAMERA_ZONE_X) { - int idx = m_touch.count; - m_touch.id[idx] = p_event->tfinger.fingerID; - m_touch.x[idx] = p_event->tfinger.x; - m_touch.y[idx] = p_event->tfinger.y; - m_touch.synced[idx] = true; - m_touch.count++; - - if (m_touch.count == 2) { - float dx = m_touch.x[1] - m_touch.x[0]; - float dy = m_touch.y[1] - m_touch.y[0]; - m_touch.initialPinchDist = SDL_sqrtf(dx * dx + dy * dy); - m_touch.gesturePinchDist = m_touch.initialPinchDist; - } - } + case SDL_EVENT_FINGER_DOWN: + TryClaimFinger(p_event->tfinger, p_active); break; - } case SDL_EVENT_FINGER_MOTION: { if (m_touch.count == 1) { @@ -183,8 +168,8 @@ void InputHandler::HandleSDLEvent(SDL_Event* p_event, OrbitCamera& p_orbit, bool float moveX = m_touch.x[0] - oldX; float moveY = m_touch.y[0] - oldY; - p_orbit.AdjustYaw(-moveX * 2.0f); - p_orbit.AdjustPitch(moveY * 2.0f); + p_orbit.AdjustYaw(-moveX * TOUCH_YAW_PITCH_SCALE); + p_orbit.AdjustPitch(moveY * TOUCH_YAW_PITCH_SCALE); p_orbit.ClampPitch(); } } @@ -246,15 +231,15 @@ void InputHandler::HandleSDLEvent(SDL_Event* p_event, OrbitCamera& p_orbit, bool } } - p_orbit.AdjustDistance(pinchDelta * 6.0f); + p_orbit.AdjustDistance(pinchDelta * PINCH_ZOOM_SCALE); p_orbit.ClampDistance(); m_touch.initialPinchDist = newDist; } float moveX = m_touch.x[idx] - oldX; float moveY = m_touch.y[idx] - oldY; - p_orbit.AdjustYaw(-moveX * 2.0f); - p_orbit.AdjustPitch(moveY * 2.0f); + p_orbit.AdjustYaw(-moveX * TOUCH_YAW_PITCH_SCALE); + p_orbit.AdjustPitch(moveY * TOUCH_YAW_PITCH_SCALE); p_orbit.ClampPitch(); } break; diff --git a/extensions/src/thirdpersoncamera/orbitcamera.cpp b/extensions/src/thirdpersoncamera/orbitcamera.cpp index 2bfc649c..a3ce94b4 100644 --- a/extensions/src/thirdpersoncamera/orbitcamera.cpp +++ b/extensions/src/thirdpersoncamera/orbitcamera.cpp @@ -13,8 +13,6 @@ using namespace Extensions::ThirdPersonCamera; -static constexpr float TURN_RATE = 10.0f; - OrbitCamera::OrbitCamera() : m_orbitPitch(DEFAULT_ORBIT_PITCH), m_orbitDistance(DEFAULT_ORBIT_DISTANCE), m_absoluteYaw(DEFAULT_ORBIT_YAW), m_smoothedSpeed(0.0f) @@ -131,11 +129,10 @@ void OrbitCamera::RestoreFirstPersonCamera() LegoWorld* world = CurrentWorld(); if (userActor && world && world->GetCameraController()) { - world->GetCameraController()->SetWorldTransform( - Mx3DPointFloat(0.0F, 1.25F, 0.0F), - Mx3DPointFloat(0.0F, 0.0F, 1.0F), - Mx3DPointFloat(0.0F, 1.0F, 0.0F) - ); + static const Mx3DPointFloat eyeOffset(0.0f, 1.25f, 0.0f); + static const Mx3DPointFloat forward(0.0f, 0.0f, 1.0f); + static const Mx3DPointFloat up(0.0f, 1.0f, 0.0f); + world->GetCameraController()->SetWorldTransform(eyeOffset, forward, up); userActor->TransformPointOfView(); } } @@ -189,13 +186,13 @@ MxBool OrbitCamera::HandleCameraRelativeMovement( if (keyFlags == 0 && !p_lmbHeld && inputManager) { MxU32 joystickX, joystickY, povPosition; if (inputManager->GetJoystickState(&joystickX, &joystickY, &povPosition) == SUCCESS) { - float jx = (joystickX - 50.0f) / 50.0f; - float jy = -(joystickY - 50.0f) / 50.0f; + float jx = (joystickX - JOYSTICK_CENTER) / JOYSTICK_CENTER; + float jy = -(joystickY - JOYSTICK_CENTER) / JOYSTICK_CENTER; - if (SDL_fabsf(jx) < 0.1f) { + if (SDL_fabsf(jx) < JOYSTICK_DEAD_ZONE) { jx = 0.0f; } - if (SDL_fabsf(jy) < 0.1f) { + if (SDL_fabsf(jy) < JOYSTICK_DEAD_ZONE) { jy = 0.0f; } @@ -205,7 +202,7 @@ MxBool OrbitCamera::HandleCameraRelativeMovement( } float moveDirLen = SDL_sqrtf(moveDirX * moveDirX + moveDirZ * moveDirZ); - bool hasInput = moveDirLen > 0.001f; + bool hasInput = moveDirLen > MOVEMENT_DIR_EPSILON; if (p_isInMultiPartEmote) { hasInput = false; @@ -256,7 +253,7 @@ MxBool OrbitCamera::HandleCameraRelativeMovement( angleDiff += 2.0f * SDL_PI_F; } - float maxTurn = TURN_RATE * p_deltaTime; + float maxTurn = CHARACTER_TURN_RATE * p_deltaTime; if (SDL_fabsf(angleDiff) > maxTurn) { angleDiff = angleDiff > 0 ? maxTurn : -maxTurn; }