From d7981bf03f333636f9d65e1ee44887d1e34f0b63 Mon Sep 17 00:00:00 2001 From: Garrett Cox Date: Sun, 18 Jan 2026 22:38:48 -0600 Subject: [PATCH] Some tweaks to anchor (#6166) --- soh/soh/Network/Anchor/Anchor.cpp | 99 ++++++++++--------- soh/soh/Network/Anchor/Anchor.h | 2 +- soh/soh/Network/Anchor/JsonConversions.hpp | 24 ++--- .../Network/Anchor/Packets/AllClientState.cpp | 2 +- .../Network/Anchor/Packets/PlayerUpdate.cpp | 45 ++++----- 5 files changed, 87 insertions(+), 85 deletions(-) diff --git a/soh/soh/Network/Anchor/Anchor.cpp b/soh/soh/Network/Anchor/Anchor.cpp index 755f40e59..ccaedb4c6 100644 --- a/soh/soh/Network/Anchor/Anchor.cpp +++ b/soh/soh/Network/Anchor/Anchor.cpp @@ -93,8 +93,8 @@ void Anchor::OnIncomingJson(nlohmann::json payload) { std::string packetType = payload["type"].get(); - // Ignore packets from mismatched clients, except for ALL_CLIENT_STATE or UPDATE_CLIENT_STATE - if (packetType != ALL_CLIENT_STATE && packetType != UPDATE_CLIENT_STATE) { + // Ignore packets from mismatched clients, except for ALL_CLIENT_STATE, UPDATE_CLIENT_STATE, and PLAYER_UPDATE + if (packetType != ALL_CLIENT_STATE && packetType != UPDATE_CLIENT_STATE && packetType != PLAYER_UPDATE) { if (payload.contains("clientId")) { uint32_t clientId = payload["clientId"].get(); if (clients.contains(clientId) && clients[clientId].clientVersion != clientVersion) { @@ -103,12 +103,6 @@ void Anchor::OnIncomingJson(nlohmann::json payload) { } } - // Handle PLAYER_UPDATE packets immediately, no need to queue - if (packetType == PLAYER_UPDATE) { - HandlePacket_PlayerUpdate(payload); - return; - } - // Queue all packets to be processed on the game thread std::lock_guard lock(incomingPacketQueueMutex); incomingPacketQueue.push(payload); @@ -131,47 +125,54 @@ void Anchor::ProcessIncomingPacketQueue() { isProcessingIncomingPacket = true; - // packetType here is a string so we can't use a switch statement - if (packetType == ALL_CLIENT_STATE) - HandlePacket_AllClientState(payload); - else if (packetType == DAMAGE_PLAYER) - HandlePacket_DamagePlayer(payload); - else if (packetType == DISABLE_ANCHOR) - HandlePacket_DisableAnchor(payload); - else if (packetType == ENTRANCE_DISCOVERED) - HandlePacket_EntranceDiscovered(payload); - else if (packetType == GAME_COMPLETE) - HandlePacket_GameComplete(payload); - else if (packetType == GIVE_ITEM) - HandlePacket_GiveItem(payload); - else if (packetType == OCARINA_SFX) - HandlePacket_OcarinaSfx(payload); - else if (packetType == PLAYER_SFX) - HandlePacket_PlayerSfx(payload); - else if (packetType == UPDATE_TEAM_STATE) - HandlePacket_UpdateTeamState(payload); - else if (packetType == REQUEST_TEAM_STATE) - HandlePacket_RequestTeamState(payload); - else if (packetType == REQUEST_TELEPORT) - HandlePacket_RequestTeleport(payload); - else if (packetType == SERVER_MESSAGE) - HandlePacket_ServerMessage(payload); - else if (packetType == SET_CHECK_STATUS) - HandlePacket_SetCheckStatus(payload); - else if (packetType == SET_FLAG) - HandlePacket_SetFlag(payload); - else if (packetType == TELEPORT_TO) - HandlePacket_TeleportTo(payload); - else if (packetType == UNSET_FLAG) - HandlePacket_UnsetFlag(payload); - else if (packetType == UPDATE_BEANS_COUNT) - HandlePacket_UpdateBeansCount(payload); - else if (packetType == UPDATE_CLIENT_STATE) - HandlePacket_UpdateClientState(payload); - else if (packetType == UPDATE_ROOM_STATE) - HandlePacket_UpdateRoomState(payload); - else if (packetType == UPDATE_DUNGEON_ITEMS) - HandlePacket_UpdateDungeonItems(payload); + try { + // packetType here is a string so we can't use a switch statement + if (packetType == ALL_CLIENT_STATE) + HandlePacket_AllClientState(payload); + else if (packetType == DAMAGE_PLAYER) + HandlePacket_DamagePlayer(payload); + else if (packetType == DISABLE_ANCHOR) + HandlePacket_DisableAnchor(payload); + else if (packetType == ENTRANCE_DISCOVERED) + HandlePacket_EntranceDiscovered(payload); + else if (packetType == GAME_COMPLETE) + HandlePacket_GameComplete(payload); + else if (packetType == GIVE_ITEM) + HandlePacket_GiveItem(payload); + else if (packetType == OCARINA_SFX) + HandlePacket_OcarinaSfx(payload); + else if (packetType == PLAYER_UPDATE) + HandlePacket_PlayerUpdate(payload); + else if (packetType == PLAYER_SFX) + HandlePacket_PlayerSfx(payload); + else if (packetType == UPDATE_TEAM_STATE) + HandlePacket_UpdateTeamState(payload); + else if (packetType == REQUEST_TEAM_STATE) + HandlePacket_RequestTeamState(payload); + else if (packetType == REQUEST_TELEPORT) + HandlePacket_RequestTeleport(payload); + else if (packetType == SERVER_MESSAGE) + HandlePacket_ServerMessage(payload); + else if (packetType == SET_CHECK_STATUS) + HandlePacket_SetCheckStatus(payload); + else if (packetType == SET_FLAG) + HandlePacket_SetFlag(payload); + else if (packetType == TELEPORT_TO) + HandlePacket_TeleportTo(payload); + else if (packetType == UNSET_FLAG) + HandlePacket_UnsetFlag(payload); + else if (packetType == UPDATE_BEANS_COUNT) + HandlePacket_UpdateBeansCount(payload); + else if (packetType == UPDATE_CLIENT_STATE) + HandlePacket_UpdateClientState(payload); + else if (packetType == UPDATE_ROOM_STATE) + HandlePacket_UpdateRoomState(payload); + else if (packetType == UPDATE_DUNGEON_ITEMS) + HandlePacket_UpdateDungeonItems(payload); + } catch (const std::exception& e) { + SPDLOG_ERROR("[Anchor] Exception while processing incoming packet {}", e.what()); + SPDLOG_ERROR("[Anchor] Packet: {}", payload.dump()); + } isProcessingIncomingPacket = false; } diff --git a/soh/soh/Network/Anchor/Anchor.h b/soh/soh/Network/Anchor/Anchor.h index cce5e397a..8476d8c36 100644 --- a/soh/soh/Network/Anchor/Anchor.h +++ b/soh/soh/Network/Anchor/Anchor.h @@ -110,7 +110,7 @@ class Anchor : public Network { public: uint32_t ownClientId; - inline static const std::string clientVersion = (char*)gBuildVersion; + inline static const std::string clientVersion = (char*)gGitCommitHash; // Packet types // inline static const std::string ALL_CLIENT_STATE = "ALL_CLIENT_STATE"; diff --git a/soh/soh/Network/Anchor/JsonConversions.hpp b/soh/soh/Network/Anchor/JsonConversions.hpp index 02f10495a..8b28ba40e 100644 --- a/soh/soh/Network/Anchor/JsonConversions.hpp +++ b/soh/soh/Network/Anchor/JsonConversions.hpp @@ -52,18 +52,18 @@ inline void from_json(const json& j, PosRot& posRot) { } inline void from_json(const json& j, AnchorClient& client) { - j.contains("clientId") ? j.at("clientId").get_to(client.clientId) : client.clientId = 0; - j.contains("name") ? j.at("name").get_to(client.name) : client.name = "???"; - j.contains("color") ? j.at("color").get_to(client.color) : client.color = { 255, 255, 255 }; - j.contains("clientVersion") ? j.at("clientVersion").get_to(client.clientVersion) : client.clientVersion = "???"; - j.contains("teamId") ? j.at("teamId").get_to(client.teamId) : client.teamId = "default"; - j.contains("online") ? j.at("online").get_to(client.online) : client.online = false; - j.contains("seed") ? j.at("seed").get_to(client.seed) : client.seed = 0; - j.contains("isSaveLoaded") ? j.at("isSaveLoaded").get_to(client.isSaveLoaded) : client.isSaveLoaded = false; - j.contains("isGameComplete") ? j.at("isGameComplete").get_to(client.isGameComplete) : client.isGameComplete = false; - j.contains("sceneNum") ? j.at("sceneNum").get_to(client.sceneNum) : client.sceneNum = SCENE_ID_MAX; - j.contains("entranceIndex") ? j.at("entranceIndex").get_to(client.entranceIndex) : client.entranceIndex = 0; - j.contains("self") ? j.at("self").get_to(client.self) : client.self = false; + client.clientId = j.value("clientId", (u32)0); + client.name = j.value("name", "???"); + client.color = j.value("color", Color_RGB8{ 255, 255, 255 }); + client.clientVersion = j.value("clientVersion", "???"); + client.teamId = j.value("teamId", "default"); + client.online = j.value("online", false); + client.seed = j.value("seed", (u32)0); + client.isSaveLoaded = j.value("isSaveLoaded", false); + client.isGameComplete = j.value("isGameComplete", false); + client.sceneNum = j.value("sceneNum", (s16)SCENE_ID_MAX); + client.entranceIndex = j.value("entranceIndex", (s32)0); + client.self = j.value("self", false); } inline void to_json(json& j, const Inventory& inventory) { diff --git a/soh/soh/Network/Anchor/Packets/AllClientState.cpp b/soh/soh/Network/Anchor/Packets/AllClientState.cpp index 9e6bc6114..7de1a67ec 100644 --- a/soh/soh/Network/Anchor/Packets/AllClientState.cpp +++ b/soh/soh/Network/Anchor/Packets/AllClientState.cpp @@ -67,5 +67,5 @@ void Anchor::HandlePacket_AllClientState(nlohmann::json payload) { clients.erase(clientId); } - RefreshClientActors(); + shouldRefreshActors = true; } diff --git a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp index d37dff64a..ad373e38e 100644 --- a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp +++ b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp @@ -83,35 +83,36 @@ void Anchor::HandlePacket_PlayerUpdate(nlohmann::json payload) { if (clients.contains(clientId)) { auto& client = clients[clientId]; - if (client.linkAge != payload["linkAge"].get()) { + if (client.linkAge != payload.value("linkAge", (s32)LINK_AGE_ADULT)) { shouldRefreshActors = true; } - client.sceneNum = payload["sceneNum"].get(); - client.entranceIndex = payload["entranceIndex"].get(); - client.linkAge = payload["linkAge"].get(); - client.posRot = payload["posRot"].get(); - std::vector jointArray = payload["jointTable"]; + client.sceneNum = payload.value("sceneNum", (s16)SCENE_ID_MAX); + client.entranceIndex = payload.value("entranceIndex", (s32)0); + client.linkAge = payload.value("linkAge", (s32)LINK_AGE_ADULT); + client.posRot = payload.value("posRot", PosRot{ 0 }); + std::vector jointArray = payload.value("jointTable", std::vector{}); + jointArray.resize(24 * 3); // Ensure it has enough elements, in case of missing data for (int i = 0; i < 24; i++) { client.jointTable[i].x = jointArray[i * 3]; client.jointTable[i].y = jointArray[i * 3 + 1]; client.jointTable[i].z = jointArray[i * 3 + 2]; } - client.movementFlags = payload["movementFlags"].get(); - client.prevTransl = payload["prevTransl"].get(); - client.upperLimbRot = payload["upperLimbRot"].get(); - client.currentBoots = payload["currentBoots"].get(); - client.currentShield = payload["currentShield"].get(); - client.currentTunic = payload["currentTunic"].get(); - client.stateFlags1 = payload["stateFlags1"].get(); - client.stateFlags2 = payload["stateFlags2"].get(); - client.buttonItem0 = payload["buttonItem0"].get(); - client.itemAction = payload["itemAction"].get(); - client.heldItemAction = payload["heldItemAction"].get(); - client.modelGroup = payload["modelGroup"].get(); - client.invincibilityTimer = payload["invincibilityTimer"].get(); - client.unk_862 = payload["unk_862"].get(); - client.unk_85C = payload["unk_85C"].get(); - client.actionVar1 = payload["actionVar1"].get(); + client.movementFlags = payload.value("movementFlags", (u8)0); + client.prevTransl = payload.value("prevTransl", Vec3s{ 0 }); + client.upperLimbRot = payload.value("upperLimbRot", Vec3s{ 0 }); + client.currentBoots = payload.value("currentBoots", (s8)0); + client.currentShield = payload.value("currentShield", (s8)0); + client.currentTunic = payload.value("currentTunic", (s8)0); + client.stateFlags1 = payload.value("stateFlags1", (u32)0); + client.stateFlags2 = payload.value("stateFlags2", (u32)0); + client.buttonItem0 = payload.value("buttonItem0", (u8)0); + client.itemAction = payload.value("itemAction", (s8)0); + client.heldItemAction = payload.value("heldItemAction", (s8)0); + client.modelGroup = payload.value("modelGroup", (u8)0); + client.invincibilityTimer = payload.value("invincibilityTimer", (s8)0); + client.unk_862 = payload.value("unk_862", (s16)0); + client.unk_85C = payload.value("unk_85C", (f32)0); + client.actionVar1 = payload.value("actionVar1", (s8)0); } }