diff --git a/soh/soh/Network/Anchor/Anchor.cpp b/soh/soh/Network/Anchor/Anchor.cpp index bc5614f47..e996ac6a0 100644 --- a/soh/soh/Network/Anchor/Anchor.cpp +++ b/soh/soh/Network/Anchor/Anchor.cpp @@ -3,6 +3,7 @@ #include #include "soh/OTRGlobals.h" #include "soh/Enhancements/nametag.h" +#include "soh/ObjectExtension/ObjectExtension.h" extern "C" { #include "variables.h" @@ -143,6 +144,21 @@ void Anchor::ProcessIncomingPacketQueue() { // MARK: - Misc/Helpers // Kills all existing anchor actors and respawns them with the new client data + +struct DummyPlayerClientId { + uint32_t clientId = 0; +}; +static ObjectExtension::Register DummyPlayerClientIdRegister; + +uint32_t Anchor::GetDummyPlayerClientId(const Actor* actor) { + const DummyPlayerClientId* clientId = ObjectExtension::GetInstance().Get(actor); + return clientId != nullptr ? clientId->clientId : 0; +} + +void Anchor::SetDummyPlayerClientId(const Actor* actor, uint32_t clientId) { + ObjectExtension::GetInstance().Set(actor, DummyPlayerClientId{ clientId }); +} + void Anchor::RefreshClientActors() { if (!IsSaveLoaded()) { return; @@ -158,23 +174,21 @@ void Anchor::RefreshClientActors() { actor = actor->next; } - actorIndexToClientId.clear(); - refreshingActors = true; for (auto& [clientId, client] : clients) { if (!client.online || client.self) { continue; } - actorIndexToClientId.push_back(clientId); + spawningDummyPlayerForClientId = clientId; // We are using a hook `ShouldActorInit` to override the init/update/draw/destroy functions of the Player we // spawn We quickly store a mapping of "index" to clientId, then within the init function we use this to get the // clientId and store it on player->zTargetActiveTimer (unused s32 for the dummy) for convenience - auto dummy = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_PLAYER, client.posRot.pos.x, - client.posRot.pos.y, client.posRot.pos.z, client.posRot.rot.x, client.posRot.rot.y, - client.posRot.rot.z, actorIndexToClientId.size() - 1, false); + auto dummy = + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_PLAYER, client.posRot.pos.x, client.posRot.pos.y, + client.posRot.pos.z, client.posRot.rot.x, client.posRot.rot.y, client.posRot.rot.z, 0, false); client.player = (Player*)dummy; } - refreshingActors = false; + spawningDummyPlayerForClientId = 0; } bool Anchor::IsSaveLoaded() { diff --git a/soh/soh/Network/Anchor/Anchor.h b/soh/soh/Network/Anchor/Anchor.h index a5e831153..93c404617 100644 --- a/soh/soh/Network/Anchor/Anchor.h +++ b/soh/soh/Network/Anchor/Anchor.h @@ -63,7 +63,8 @@ typedef struct { class Anchor : public Network { private: - bool refreshingActors = false; + uint32_t spawningDummyPlayerForClientId = 0; + bool shouldRefreshActors = false; bool justLoadedSave = false; bool isHandlingUpdateTeamState = false; bool isProcessingIncomingPacket = false; @@ -74,6 +75,8 @@ class Anchor : public Network { nlohmann::json PrepRoomState(); void RegisterHooks(); void RefreshClientActors(); + void SetDummyPlayerClientId(const Actor* actor, uint32_t clientId); + void HandlePacket_AllClientState(nlohmann::json payload); void HandlePacket_ConsumeAdultTradeItem(nlohmann::json payload); void HandlePacket_DamagePlayer(nlohmann::json payload); @@ -125,7 +128,6 @@ class Anchor : public Network { static Anchor* Instance; std::map clients; - std::vector actorIndexToClientId; RoomState roomState; void Enable(); @@ -138,6 +140,7 @@ class Anchor : public Network { void SendJsonToRemote(nlohmann::json packet); bool IsSaveLoaded(); bool CanTeleportTo(uint32_t clientId); + uint32_t GetDummyPlayerClientId(const Actor* actor); void SendPacket_ClearTeamState(std::string teamId); void SendPacket_DamagePlayer(u32 clientId, u8 damageEffect, u8 damage); diff --git a/soh/soh/Network/Anchor/DummyPlayer.cpp b/soh/soh/Network/Anchor/DummyPlayer.cpp index d0eb58d93..3bc085413 100644 --- a/soh/soh/Network/Anchor/DummyPlayer.cpp +++ b/soh/soh/Network/Anchor/DummyPlayer.cpp @@ -12,9 +12,6 @@ void Player_UseItem(PlayState* play, Player* player, s32 item); void Player_Draw(Actor* actor, PlayState* play); } -// Hijacking player->zTargetActiveTimer (unused s32 for the dummy) to store the clientId for convenience -#define DUMMY_CLIENT_ID player->zTargetActiveTimer - static DamageTable DummyPlayerDamageTable = { /* Deku nut */ DMG_ENTRY(0, DUMMY_PLAYER_HIT_RESPONSE_STUN), /* Deku stick */ DMG_ENTRY(2, DUMMY_PLAYER_HIT_RESPONSE_NORMAL), @@ -53,15 +50,14 @@ static DamageTable DummyPlayerDamageTable = { void DummyPlayer_Init(Actor* actor, PlayState* play) { Player* player = (Player*)actor; - uint32_t clientId = Anchor::Instance->actorIndexToClientId[actor->params]; - DUMMY_CLIENT_ID = clientId; + uint32_t clientId = Anchor::Instance->GetDummyPlayerClientId(actor); - if (!Anchor::Instance->clients.contains(DUMMY_CLIENT_ID)) { + if (!Anchor::Instance->clients.contains(clientId)) { Actor_Kill(actor); return; } - AnchorClient& client = Anchor::Instance->clients[DUMMY_CLIENT_ID]; + AnchorClient& client = Anchor::Instance->clients[clientId]; // Hack to account for usage of gSaveContext in Player_Init s32 originalAge = gSaveContext.linkAge; @@ -104,12 +100,14 @@ void Math_Vec3s_Copy(Vec3s* dest, Vec3s* src) { void DummyPlayer_Update(Actor* actor, PlayState* play) { Player* player = (Player*)actor; - if (!Anchor::Instance->clients.contains(DUMMY_CLIENT_ID)) { + uint32_t clientId = Anchor::Instance->GetDummyPlayerClientId(actor); + + if (!Anchor::Instance->clients.contains(clientId)) { Actor_Kill(actor); return; } - AnchorClient& client = Anchor::Instance->clients[DUMMY_CLIENT_ID]; + AnchorClient& client = Anchor::Instance->clients[clientId]; if (client.sceneNum != gPlayState->sceneNum || !client.online || !client.isSaveLoaded) { actor->world.pos.x = -9999.0f; @@ -195,12 +193,14 @@ void DummyPlayer_Update(Actor* actor, PlayState* play) { void DummyPlayer_Draw(Actor* actor, PlayState* play) { Player* player = (Player*)actor; - if (!Anchor::Instance->clients.contains(DUMMY_CLIENT_ID)) { + uint32_t clientId = Anchor::Instance->GetDummyPlayerClientId(actor); + + if (!Anchor::Instance->clients.contains(clientId)) { Actor_Kill(actor); return; } - AnchorClient& client = Anchor::Instance->clients[DUMMY_CLIENT_ID]; + AnchorClient& client = Anchor::Instance->clients[clientId]; if (client.sceneNum != gPlayState->sceneNum || !client.online || !client.isSaveLoaded) { return; diff --git a/soh/soh/Network/Anchor/HookHandlers.cpp b/soh/soh/Network/Anchor/HookHandlers.cpp index 15886c527..9a4622c29 100644 --- a/soh/soh/Network/Anchor/HookHandlers.cpp +++ b/soh/soh/Network/Anchor/HookHandlers.cpp @@ -67,7 +67,9 @@ void Anchor::RegisterHooks() { COND_ID_HOOK(ShouldActorInit, ACTOR_PLAYER, isConnected, [&](void* actorRef, bool* should) { Actor* actor = (Actor*)actorRef; - if (refreshingActors) { + if (spawningDummyPlayerForClientId != 0) { + SetDummyPlayerClientId(actor, spawningDummyPlayerForClientId); + // By the time we get here, the actor was already added to the ACTORCAT_PLAYER list, so we need to move it Actor_ChangeCategory(gPlayState, &gPlayState->actorCtx, actor, ACTORCAT_NPC); actor->id = ACTOR_EN_OE2; @@ -84,6 +86,12 @@ void Anchor::RegisterHooks() { justLoadedSave = false; SendPacket_RequestTeamState(); } + + if (shouldRefreshActors) { + shouldRefreshActors = false; + RefreshClientActors(); + } + SendPacket_PlayerUpdate(); }); diff --git a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp index 34bacaf11..a4ee17b76 100644 --- a/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp +++ b/soh/soh/Network/Anchor/Packets/PlayerUpdate.cpp @@ -77,8 +77,6 @@ void Anchor::SendPacket_PlayerUpdate() { void Anchor::HandlePacket_PlayerUpdate(nlohmann::json payload) { uint32_t clientId = payload["clientId"].get(); - bool shouldRefreshActors = false; - if (clients.contains(clientId)) { auto& client = clients[clientId]; @@ -110,8 +108,4 @@ void Anchor::HandlePacket_PlayerUpdate(nlohmann::json payload) { client.unk_862 = payload["unk_862"].get(); client.actionVar1 = payload["actionVar1"].get(); } - - if (shouldRefreshActors) { - RefreshClientActors(); - } }