3131#include < Forms/TESNPC.h>
3232#include < DefaultObjectManager.h>
3333#include < Games/Primitives.h>
34+ #include < ExtraData/ExtraContainerChanges.h>
3435
3536InventoryService::InventoryService (World& aWorld, entt::dispatcher& aDispatcher, TransportService& aTransport) noexcept
3637 : m_world(aWorld)
@@ -47,6 +48,7 @@ InventoryService::InventoryService(World& aWorld, entt::dispatcher& aDispatcher,
4748void 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
99101void 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
145106void 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+
386431void InventoryService::RunWeaponStateUpdates () noexcept
387432{
388433 if (!m_transport.IsConnected ())
0 commit comments