Skip to content

Commit 90e23c0

Browse files
committed
fix: crash that occurs when loading a new cell with equipment requests
1 parent 6c42af3 commit 90e23c0

File tree

2 files changed

+91
-42
lines changed

2 files changed

+91
-42
lines changed

Code/client/Services/Generic/InventoryService.cpp

Lines changed: 87 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <Forms/TESNPC.h>
3232
#include <DefaultObjectManager.h>
3333
#include <Games/Primitives.h>
34+
#include <ExtraData/ExtraContainerChanges.h>
3435

3536
InventoryService::InventoryService(World& aWorld, entt::dispatcher& aDispatcher, TransportService& aTransport) noexcept
3637
: m_world(aWorld)
@@ -47,6 +48,7 @@ InventoryService::InventoryService(World& aWorld, entt::dispatcher& aDispatcher,
4748
void InventoryService::OnUpdate(const UpdateEvent& acUpdateEvent) noexcept
4849
{
4950
ProcessPendingEquipment();
51+
ProcessPendingEquipmentRequests();
5052
RunWeaponStateUpdates();
5153
RunNakedNPCBugChecks();
5254
}
@@ -98,48 +100,7 @@ void InventoryService::OnInventoryChangeEvent(const InventoryChangeEvent& acEven
98100

99101
void InventoryService::OnEquipmentChangeEvent(const EquipmentChangeEvent& acEvent) noexcept
100102
{
101-
if (!m_transport.IsConnected())
102-
return;
103-
104-
auto view = m_world.view<FormIdComponent>();
105-
106-
const auto iter = std::find_if(std::begin(view), std::end(view), [view, formId = acEvent.ActorId](auto entity) { return view.get<FormIdComponent>(entity).Id == formId; });
107-
108-
if (iter == std::end(view))
109-
return;
110-
111-
std::optional<uint32_t> serverIdRes = Utils::GetServerId(*iter);
112-
if (!serverIdRes.has_value())
113-
{
114-
spdlog::error(__FUNCTION__ ": failed to find server id, actor id: {:X}, item id: {:X}, isAmmo: {}, unequip: {}, slot: {:X}", acEvent.ActorId, acEvent.ItemId, acEvent.IsAmmo, acEvent.Unequip, acEvent.EquipSlotId);
115-
return;
116-
}
117-
118-
Actor* pActor = Cast<Actor>(TESForm::GetById(acEvent.ActorId));
119-
if (!pActor)
120-
return;
121-
122-
auto& modSystem = World::Get().GetModSystem();
123-
124-
RequestEquipmentChanges request;
125-
request.ServerId = serverIdRes.value();
126-
127-
if (!modSystem.GetServerModId(acEvent.EquipSlotId, request.EquipSlotId))
128-
return;
129-
if (!modSystem.GetServerModId(acEvent.ItemId, request.ItemId))
130-
return;
131-
132-
const int32_t cEffectiveCount = acEvent.Count == 0 ? 1 : acEvent.Count;
133-
request.Count = cEffectiveCount;
134-
request.Unequip = acEvent.Unequip;
135-
request.IsSpell = acEvent.IsSpell;
136-
request.IsShout = acEvent.IsShout;
137-
request.IsAmmo = acEvent.IsAmmo;
138-
request.CurrentInventory = pActor->GetEquipment();
139-
140-
m_transport.Send(request);
141-
142-
spdlog::info("Sending equipment request, item: {:X}, count: {}, target object: {:X}", acEvent.ItemId, cEffectiveCount, acEvent.ActorId);
103+
m_pendingEquipmentRequests.push_back(acEvent);
143104
}
144105

145106
void InventoryService::OnNotifyInventoryChanges(const NotifyInventoryChanges& acMessage) noexcept
@@ -383,6 +344,90 @@ void InventoryService::ProcessPendingEquipment() noexcept
383344
m_world.remove<PendingEquipmentComponent>(entity);
384345
}
385346

347+
void InventoryService::ProcessPendingEquipmentRequests() noexcept
348+
{
349+
if (m_pendingEquipmentRequests.empty())
350+
return;
351+
352+
TiltedPhoques::Vector<EquipmentChangeEvent> remaining;
353+
remaining.reserve(m_pendingEquipmentRequests.size());
354+
355+
for (const auto& request : m_pendingEquipmentRequests)
356+
{
357+
if (!SendEquipmentChange(request))
358+
remaining.push_back(request);
359+
}
360+
361+
m_pendingEquipmentRequests = std::move(remaining);
362+
}
363+
364+
bool InventoryService::SendEquipmentChange(const EquipmentChangeEvent& acEvent) noexcept
365+
{
366+
if (!m_transport.IsConnected())
367+
return false;
368+
369+
auto view = m_world.view<FormIdComponent>();
370+
371+
const auto iter = std::find_if(std::begin(view), std::end(view), [view, formId = acEvent.ActorId](auto entity) { return view.get<FormIdComponent>(entity).Id == formId; });
372+
373+
if (iter == std::end(view))
374+
{
375+
spdlog::debug("{}: form id {:X} not found yet, postponing equipment sync", __FUNCTION__, acEvent.ActorId);
376+
return false;
377+
}
378+
379+
std::optional<uint32_t> serverIdRes = Utils::GetServerId(*iter);
380+
if (!serverIdRes.has_value())
381+
{
382+
spdlog::error("{}: failed to find server id, actor id: {:X}, item id: {:X}, isAmmo: {}, unequip: {}, slot: {:X}", __FUNCTION__, acEvent.ActorId, acEvent.ItemId, acEvent.IsAmmo, acEvent.Unequip, acEvent.EquipSlotId);
383+
return false;
384+
}
385+
386+
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())
394+
{
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);
403+
return false;
404+
}
405+
406+
auto& modSystem = World::Get().GetModSystem();
407+
408+
RequestEquipmentChanges request;
409+
request.ServerId = serverIdRes.value();
410+
411+
if (!modSystem.GetServerModId(acEvent.EquipSlotId, request.EquipSlotId))
412+
return true;
413+
if (!modSystem.GetServerModId(acEvent.ItemId, request.ItemId))
414+
return true;
415+
416+
const int32_t cEffectiveCount = acEvent.Count == 0 ? 1 : acEvent.Count;
417+
request.Count = cEffectiveCount;
418+
request.Unequip = acEvent.Unequip;
419+
request.IsSpell = acEvent.IsSpell;
420+
request.IsShout = acEvent.IsShout;
421+
request.IsAmmo = acEvent.IsAmmo;
422+
request.CurrentInventory = pActor->GetEquipment();
423+
424+
m_transport.Send(request);
425+
426+
spdlog::info("Sending equipment request, item: {:X}, count: {}, target object: {:X}", acEvent.ItemId, cEffectiveCount, acEvent.ActorId);
427+
428+
return true;
429+
}
430+
386431
void InventoryService::RunWeaponStateUpdates() noexcept
387432
{
388433
if (!m_transport.IsConnected())

Code/client/Services/InventoryService.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ struct InventoryService
5454
private:
5555
void ApplyEquipmentChange(Actor* pActor, const NotifyEquipmentChanges& acMessage) noexcept;
5656
void ProcessPendingEquipment() noexcept;
57+
void ProcessPendingEquipmentRequests() noexcept;
58+
bool SendEquipmentChange(const EquipmentChangeEvent& acEvent) noexcept;
5759

5860
/**
5961
* Checks whether local actors their weapon draw states have changed,
@@ -75,4 +77,6 @@ struct InventoryService
7577
entt::scoped_connection m_equipmentConnection;
7678
entt::scoped_connection m_inventoryChangeConnection;
7779
entt::scoped_connection m_equipmentChangeConnection;
80+
81+
TiltedPhoques::Vector<EquipmentChangeEvent> m_pendingEquipmentRequests;
7882
};

0 commit comments

Comments
 (0)