#include #include #include #include #include #include #include #include #include #include #include #include #include ObjectService::ObjectService(World& aWorld, entt::dispatcher& aDispatcher) : m_world(aWorld) { m_leaveCellConnection = aDispatcher.sink().connect<&ObjectService::OnPlayerLeaveCellEvent>(this); m_assignObjectConnection = aDispatcher.sink>().connect<&ObjectService::OnAssignObjectsRequest>(this); m_activateConnection = aDispatcher.sink>().connect<&ObjectService::OnActivate>(this); m_lockChangeConnection = aDispatcher.sink>().connect<&ObjectService::OnLockChange>(this); m_scriptAnimationConnection = aDispatcher.sink>().connect<&ObjectService::OnScriptAnimationRequest>(this); } // TODO(cosideci): the cell handling of objects need to be revamped. // We already store the location and worldspace of the mod through CellIdComponent. // Clients need a message saying the entity was destroyed. void ObjectService::OnPlayerLeaveCellEvent(const PlayerLeaveCellEvent& acEvent) noexcept { for (Player* pPlayer : m_world.GetPlayerManager()) { if (pPlayer->GetCellComponent().Cell == acEvent.OldCell) return; } auto objectView = m_world.view(); Vector toDestroy; for (auto entity : objectView) { const auto& cellIdComponent = objectView.get(entity); if (cellIdComponent.Cell != acEvent.OldCell) continue; toDestroy.push_back(entity); } for (auto& entity : toDestroy) { m_world.destroy(entity); } } // NOTE: this whole system kinda relies on all objects in a cell being static. // This is fine for containers and doors, but if this system is expanded, think of temporaries. void ObjectService::OnAssignObjectsRequest(const PacketEvent& acMessage) noexcept { auto view = m_world.view(); AssignObjectsResponse response; for (const ObjectData& object : acMessage.Packet.Objects) { const auto iter = std::find_if( std::begin(view), std::end(view), [view, id = object.Id](auto entity) { const auto& formIdComponent = view.get(entity); return formIdComponent.Id == id; }); if (iter != std::end(view)) { ObjectData objectData; objectData.ServerId = World::ToInteger(*iter); auto& formIdComponent = view.get(*iter); objectData.Id = formIdComponent.Id; auto& objectComponent = view.get(*iter); objectData.CurrentLockData = objectComponent.CurrentLockData; auto& inventoryComponent = view.get(*iter); objectData.CurrentInventory = inventoryComponent.Content; objectData.IsSenderFirst = false; response.Objects.push_back(objectData); } else { const auto cEntity = m_world.create(); m_world.emplace(cEntity, object.Id); auto& objectComponent = m_world.emplace(cEntity, acMessage.pPlayer); objectComponent.CurrentLockData = object.CurrentLockData; m_world.emplace(cEntity, object.CellId, object.WorldSpaceId, object.CurrentCoords); auto& inventoryComp = m_world.emplace(cEntity); inventoryComp.Content = object.CurrentInventory; ObjectData objectData; objectData.Id = object.Id; objectData.ServerId = World::ToInteger(cEntity); objectData.IsSenderFirst = true; response.Objects.push_back(objectData); } } if (!response.Objects.empty()) acMessage.pPlayer->Send(response); } void ObjectService::OnActivate(const PacketEvent& acMessage) const noexcept { NotifyActivate notifyActivate; notifyActivate.Id = acMessage.Packet.Id; notifyActivate.ActivatorId = acMessage.Packet.ActivatorId; notifyActivate.PreActivationOpenState = acMessage.Packet.PreActivationOpenState; for (auto pPlayer : m_world.GetPlayerManager()) { if (pPlayer != acMessage.pPlayer && pPlayer->GetCellComponent().Cell == acMessage.Packet.CellId) { pPlayer->Send(notifyActivate); } } } void ObjectService::OnLockChange(const PacketEvent& acMessage) const noexcept { NotifyLockChange notifyLockChange; notifyLockChange.Id = acMessage.Packet.Id; notifyLockChange.IsLocked = acMessage.Packet.IsLocked; notifyLockChange.LockLevel = acMessage.Packet.LockLevel; auto objectView = m_world.view(); const auto iter = std::find_if( std::begin(objectView), std::end(objectView), [objectView, id = acMessage.Packet.Id](auto entity) { const auto& formIdComponent = objectView.get(entity); return formIdComponent.Id == id; }); if (iter != std::end(objectView)) { auto& objectComponent = objectView.get(*iter); objectComponent.CurrentLockData.IsLocked = acMessage.Packet.IsLocked; objectComponent.CurrentLockData.LockLevel = acMessage.Packet.LockLevel; } for (Player* pPlayer : m_world.GetPlayerManager()) { if (pPlayer == acMessage.pPlayer) continue; if (pPlayer->GetCellComponent().Cell == acMessage.Packet.CellId) pPlayer->Send(notifyLockChange); } } void ObjectService::OnScriptAnimationRequest(const PacketEvent& acMessage) noexcept { auto& packet = acMessage.Packet; NotifyScriptAnimation message{}; message.FormID = packet.FormID; message.Animation = packet.Animation; message.EventName = packet.EventName; for (Player* pPlayer : m_world.GetPlayerManager()) { pPlayer->Send(message); } }