Skip to content

Commit a6f29e9

Browse files
committed
fix: implement actor readiness evaluation to prevent premature inventory sync
1 parent 90e23c0 commit a6f29e9

File tree

3 files changed

+66
-20
lines changed

3 files changed

+66
-20
lines changed

Code/client/Services/Generic/CharacterService.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <Structs/ActionEvent.h>
3939
#include <Messages/CancelAssignmentRequest.h>
4040
#include <Messages/AssignCharacterRequest.h>
41+
#include <ExtraData/ExtraContainerChanges.h>
4142
#include <Messages/AssignCharacterResponse.h>
4243
#include <Messages/ServerReferencesMoveRequest.h>
4344
#include <Messages/ClientReferencesMoveRequest.h>
@@ -1567,7 +1568,11 @@ void CharacterService::RunRemoteUpdates() noexcept
15671568
auto& waitingFor3D = waitingView.get<WaitingFor3D>(entity);
15681569

15691570
Actor* pActor = Cast<Actor>(TESForm::GetById(formIdComponent.Id));
1570-
if (!pActor || !pActor->GetNiNode())
1571+
if (!pActor)
1572+
continue;
1573+
1574+
ExtraContainerChanges::Data* pContainerChanges = pActor->GetContainerChanges();
1575+
if (!pActor->GetNiNode() || !pContainerChanges || !pContainerChanges->entries)
15711576
continue;
15721577

15731578
// By now, the actor has materialized in the world and is ready for further setup
@@ -1605,7 +1610,11 @@ void CharacterService::RunRemoteUpdates() noexcept
16051610
auto& pendingInventory = pendingInventoryView.get<PendingInventoryComponent>(entity);
16061611

16071612
Actor* pActor = Cast<Actor>(TESForm::GetById(formIdComponent.Id));
1608-
if (!pActor || !pActor->GetNiNode())
1613+
if (!pActor)
1614+
continue;
1615+
1616+
ExtraContainerChanges::Data* pContainerChanges = pActor->GetContainerChanges();
1617+
if (!pActor->GetNiNode() || !pContainerChanges || !pContainerChanges->entries)
16091618
continue;
16101619

16111620
pActor->SetActorInventory(pendingInventory.InventoryContent);

Code/client/Services/Generic/InventoryService.cpp

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,24 @@
3333
#include <Games/Primitives.h>
3434
#include <ExtraData/ExtraContainerChanges.h>
3535

36+
namespace
37+
{
38+
const char* DescribeReadiness(InventoryService::ActorReadinessStatus aStatus) noexcept
39+
{
40+
switch (aStatus)
41+
{
42+
case InventoryService::ActorReadinessStatus::MissingActor:
43+
return "actor reference";
44+
case InventoryService::ActorReadinessStatus::Missing3D:
45+
return "3D data";
46+
case InventoryService::ActorReadinessStatus::MissingContainerData:
47+
return "inventory data";
48+
default:
49+
return "ready state";
50+
}
51+
}
52+
}
53+
3654
InventoryService::InventoryService(World& aWorld, entt::dispatcher& aDispatcher, TransportService& aTransport) noexcept
3755
: m_world(aWorld)
3856
, m_dispatcher(aDispatcher)
@@ -217,7 +235,8 @@ void InventoryService::OnNotifyEquipmentChanges(const NotifyEquipmentChanges& ac
217235
return;
218236
}
219237

220-
if (!pActor->GetNiNode())
238+
const auto readiness = EvaluateActorReadiness(pActor);
239+
if (readiness != ActorReadinessStatus::Ready)
221240
{
222241
auto view = m_world.view<FormIdComponent>();
223242
const auto itor = std::find_if(std::begin(view), std::end(view), [formId = pActor->formID, view](entt::entity entity) { return view.get<FormIdComponent>(entity).Id == formId; });
@@ -229,7 +248,7 @@ void InventoryService::OnNotifyEquipmentChanges(const NotifyEquipmentChanges& ac
229248
pPending = &m_world.emplace<PendingEquipmentComponent>(*itor);
230249

231250
pPending->PendingChanges.push_back(acMessage);
232-
spdlog::debug("Queued equipment change for actor {:X} until 3D is ready", pActor->formID);
251+
spdlog::debug("Queued equipment change for actor {:X} (waiting for {})", pActor->formID, DescribeReadiness(readiness));
233252
}
234253
else
235254
{
@@ -331,8 +350,13 @@ void InventoryService::ProcessPendingEquipment() noexcept
331350
auto& pending = view.get<PendingEquipmentComponent>(entity);
332351

333352
Actor* pActor = Cast<Actor>(TESForm::GetById(formIdComponent.Id));
334-
if (!pActor || !pActor->GetNiNode())
353+
const auto readiness = EvaluateActorReadiness(pActor);
354+
if (readiness != ActorReadinessStatus::Ready)
355+
{
356+
if (readiness == ActorReadinessStatus::MissingActor)
357+
toClear.push_back(entity);
335358
continue;
359+
}
336360

337361
for (const auto& change : pending.PendingChanges)
338362
ApplyEquipmentChange(pActor, change);
@@ -384,22 +408,10 @@ bool InventoryService::SendEquipmentChange(const EquipmentChangeEvent& acEvent)
384408
}
385409

386410
Actor* pActor = Cast<Actor>(TESForm::GetById(acEvent.ActorId));
387-
if (!pActor)
388-
{
389-
spdlog::debug("{}: actor {:X} not ready, postponing equipment sync", __FUNCTION__, acEvent.ActorId);
390-
return false;
391-
}
392-
393-
if (!pActor->GetNiNode())
411+
const auto readiness = EvaluateActorReadiness(pActor);
412+
if (readiness != ActorReadinessStatus::Ready)
394413
{
395-
spdlog::debug("{}: actor {:X} missing 3D, postponing equipment sync", __FUNCTION__, acEvent.ActorId);
396-
return false;
397-
}
398-
399-
ExtraContainerChanges::Data* pContainerChanges = pActor->GetContainerChanges();
400-
if (!pContainerChanges || !pContainerChanges->entries)
401-
{
402-
spdlog::debug("{}: actor {:X} missing container data, postponing equipment sync", __FUNCTION__, acEvent.ActorId);
414+
spdlog::debug("{}: actor {:X} not ready (waiting for {}), postponing equipment sync", __FUNCTION__, acEvent.ActorId, DescribeReadiness(readiness));
403415
return false;
404416
}
405417

@@ -428,6 +440,21 @@ bool InventoryService::SendEquipmentChange(const EquipmentChangeEvent& acEvent)
428440
return true;
429441
}
430442

443+
InventoryService::ActorReadinessStatus InventoryService::EvaluateActorReadiness(Actor* pActor) const noexcept
444+
{
445+
if (!pActor)
446+
return ActorReadinessStatus::MissingActor;
447+
448+
if (!pActor->GetNiNode())
449+
return ActorReadinessStatus::Missing3D;
450+
451+
if (ExtraContainerChanges::Data* pContainerChanges = pActor->GetContainerChanges();
452+
!pContainerChanges || !pContainerChanges->entries)
453+
return ActorReadinessStatus::MissingContainerData;
454+
455+
return ActorReadinessStatus::Ready;
456+
}
457+
431458
void InventoryService::RunWeaponStateUpdates() noexcept
432459
{
433460
if (!m_transport.IsConnected())

Code/client/Services/InventoryService.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include <cstdint>
4+
35
struct World;
46
struct TransportService;
57
class Actor;
@@ -56,6 +58,14 @@ struct InventoryService
5658
void ProcessPendingEquipment() noexcept;
5759
void ProcessPendingEquipmentRequests() noexcept;
5860
bool SendEquipmentChange(const EquipmentChangeEvent& acEvent) noexcept;
61+
enum class ActorReadinessStatus : uint8_t
62+
{
63+
Ready,
64+
MissingActor,
65+
Missing3D,
66+
MissingContainerData
67+
};
68+
ActorReadinessStatus EvaluateActorReadiness(Actor* pActor) const noexcept;
5969

6070
/**
6171
* Checks whether local actors their weapon draw states have changed,

0 commit comments

Comments
 (0)