#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include PartyService::PartyService(World& aWorld, entt::dispatcher& aDispatcher, TransportService& aTransportService) noexcept : m_world(aWorld) , m_transport(aTransportService) { m_updateConnection = aDispatcher.sink().connect<&PartyService::OnUpdate>(this); m_disconnectConnection = aDispatcher.sink().connect<&PartyService::OnDisconnected>(this); m_playerListConnection = aDispatcher.sink().connect<&PartyService::OnPlayerList>(this); m_partyInfoConnection = aDispatcher.sink().connect<&PartyService::OnPartyInfo>(this); m_partyInviteConnection = aDispatcher.sink().connect<&PartyService::OnPartyInvite>(this); m_partyJoinedConnection = aDispatcher.sink().connect<&PartyService::OnPartyJoined>(this); m_partyLeftConnection = aDispatcher.sink().connect<&PartyService::OnPartyLeft>(this); } void PartyService::CreateParty() const noexcept { PartyCreateRequest request; m_transport.Send(request); } void PartyService::LeaveParty() const noexcept { PartyLeaveRequest request; m_transport.Send(request); } void PartyService::CreateInvite(const uint32_t aPlayerId) const noexcept { PartyInviteRequest request; request.PlayerId = aPlayerId; m_transport.Send(request); } void PartyService::AcceptInvite(const uint32_t aInviterId) const noexcept { if (!m_invitations.contains(aInviterId)) return; PartyAcceptInviteRequest request; request.InviterId = aInviterId; m_transport.Send(request); } void PartyService::KickPartyMember(const uint32_t aPlayerId) const noexcept { PartyKickRequest kickMessage; kickMessage.PartyMemberPlayerId = aPlayerId; m_transport.Send(kickMessage); } void PartyService::ChangePartyLeader(const uint32_t aPlayerId) const noexcept { PartyChangeLeaderRequest changeMessage; changeMessage.PartyMemberPlayerId = aPlayerId; m_transport.Send(changeMessage); } void PartyService::OnUpdate(const UpdateEvent& acEvent) noexcept { const auto cCurrentTick = m_transport.GetClock().GetCurrentTick(); if (m_nextUpdate > cCurrentTick) return; // Update once every second m_nextUpdate = cCurrentTick + 1000; auto itor = std::begin(m_invitations); while (itor != std::end(m_invitations)) { if (itor->second < cCurrentTick) itor = m_invitations.erase(itor); else ++itor; } } void PartyService::OnDisconnected(const DisconnectedEvent& acEvent) noexcept { DestroyParty(); } void PartyService::OnPlayerList(const NotifyPlayerList& acPlayerList) noexcept { m_players = acPlayerList.Players; } void PartyService::OnPartyInfo(const NotifyPartyInfo& acPartyInfo) noexcept { if (m_inParty) { spdlog::debug("[PartyService]: Got party info update"); m_isLeader = acPartyInfo.IsLeader; m_leaderPlayerId = acPartyInfo.LeaderPlayerId; m_partyMembers = acPartyInfo.PlayerIds; // TODO: this can be done a bit prettier if (m_isLeader) { TESGlobal* pWorldEncountersEnabled = Cast(TESForm::GetById(0xB8EC1)); pWorldEncountersEnabled->f = 1.f; } auto pArguments = CefListValue::Create(); auto pPlayerIds = CefListValue::Create(); for (int i = 0; i < m_partyMembers.size(); i++) pPlayerIds->SetInt(i, m_partyMembers[i]); pArguments->SetList(0, pPlayerIds); pArguments->SetInt(1, acPartyInfo.LeaderPlayerId); m_world.GetOverlayService().GetOverlayApp()->ExecuteAsync("partyInfo", pArguments); } } void PartyService::OnPartyInvite(const NotifyPartyInvite& acPartyInvite) noexcept { spdlog::debug("[PartyService]: Got party invite from {}", acPartyInvite.InviterId); m_invitations[acPartyInvite.InviterId] = acPartyInvite.ExpiryTick; auto pArguments = CefListValue::Create(); pArguments->SetInt(0, acPartyInvite.InviterId); m_world.GetOverlayService().GetOverlayApp()->ExecuteAsync("partyInviteReceived", pArguments); } void PartyService::OnPartyJoined(const NotifyPartyJoined& acPartyJoined) noexcept { spdlog::debug("[PartyService]: Joined party. LeaderId: {}, IsLeader: {}", acPartyJoined.LeaderPlayerId, acPartyJoined.IsLeader); m_inParty = true; m_isLeader = acPartyJoined.IsLeader; m_leaderPlayerId = acPartyJoined.LeaderPlayerId; m_partyMembers = acPartyJoined.PlayerIds; m_world.GetDispatcher().trigger(PartyJoinedEvent(m_isLeader)); } void PartyService::OnPartyLeft(const NotifyPartyLeft& acPartyLeft) noexcept { spdlog::debug("[PartyService]: Left party"); DestroyParty(); m_world.GetDispatcher().trigger(PartyLeftEvent()); } void PartyService::DestroyParty() noexcept { m_inParty = false; m_isLeader = false; m_leaderPlayerId = -1; m_partyMembers.clear(); }