using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using PhoenixLib.Logging; using WingsAPI.Game.Extensions.Groups; using WingsEmu.Game._ItemUsage.Configuration; using WingsEmu.Game.Characters; using WingsEmu.Game.Groups; namespace WingsEmu.Plugins.BasicImplementations.Managers; public sealed class GroupManager : IGroupManager { private readonly List _groups; private readonly ConcurrentQueue<(IPlayerEntity, PlayerGroup)> _groupsToAdd = new(); private readonly ConcurrentQueue<(IPlayerEntity, PlayerGroup)> _groupsToRemove = new(); private readonly ConcurrentQueue<(long, PlayerGroup)> _leaderToChange = new(); private readonly ConcurrentQueue<(IPlayerEntity, PlayerGroup)> _playersToAdd = new(); private readonly ConcurrentQueue<(IPlayerEntity, PlayerGroup)> _playersToRemove = new(); private readonly ISpPartnerConfiguration _spPartnerConfiguration; private int _lastGroupId; private DateTime _lastGroupUiRefresh; public GroupManager(ISpPartnerConfiguration spPartnerConfiguration) { _spPartnerConfiguration = spPartnerConfiguration; _groups = new List(); _lastGroupUiRefresh = DateTime.MinValue; Id = Guid.NewGuid(); } public Guid Id { get; } public string Name => nameof(GroupManager); public void ProcessTick(DateTime date) { RemoveGroup(); AddNewGroup(); PlayerRemoveFromGroup(); PlayerAddToGroup(); ChangeLeader(); if (_lastGroupUiRefresh.AddSeconds(1) > date) { return; } _lastGroupUiRefresh = DateTime.UtcNow; var stopWatch = Stopwatch.StartNew(); ProcessGroupRefresh(); stopWatch.Stop(); } public int GetNextGroupId() { Interlocked.Increment(ref _lastGroupId); return _lastGroupId; } public void JoinGroup(PlayerGroup group, IPlayerEntity character) { _groupsToAdd.Enqueue((character, group)); } public void RemoveGroup(PlayerGroup group, IPlayerEntity character) { _groupsToRemove.Enqueue((character, group)); } public void AddMemberGroup(PlayerGroup group, IPlayerEntity character) { _playersToAdd.Enqueue((character, group)); } public void RemoveMemberGroup(PlayerGroup group, IPlayerEntity character) { _playersToRemove.Enqueue((character, group)); } public void ChangeLeader(PlayerGroup group, long newLeaderId) { _leaderToChange.Enqueue((newLeaderId, group)); } private void ProcessGroupRefresh() { foreach (PlayerGroup group in _groups) { try { IReadOnlyList groupMembers = group.Members; foreach (IPlayerEntity member in groupMembers) { member.Session.RefreshPartyUi(); } } catch (Exception e) { Log.Error("Group.RefreshMembers", e); } } } private void RemoveGroup() { while (_groupsToRemove.TryDequeue(out (IPlayerEntity, PlayerGroup) playerGroup)) { PlayerGroup group = playerGroup.Item2; IPlayerEntity character = playerGroup.Item1; character.RemoveGroup(); foreach (IPlayerEntity member in group.Members) { member.Session.RefreshParty(_spPartnerConfiguration); member.Session.BroadcastPidx(); } _groups.Remove(group); } } private void AddNewGroup() { while (_groupsToAdd.TryDequeue(out (IPlayerEntity, PlayerGroup) playerGroup)) { IPlayerEntity character = playerGroup.Item1; PlayerGroup group = playerGroup.Item2; _groups.Add(group); character.SetGroup(group); foreach (IPlayerEntity member in group.Members) { member.Session.RefreshParty(_spPartnerConfiguration); if (!member.IsLeaderOfGroup(member.Id)) { continue; } member.Session.BroadcastPidx(); } } } private void PlayerRemoveFromGroup() { while (_playersToRemove.TryDequeue(out (IPlayerEntity, PlayerGroup) playerGroup)) { IPlayerEntity character = playerGroup.Item1; PlayerGroup group = playerGroup.Item2; group.RemoveMember(character); character.RemoveGroup(); character.Session.RefreshParty(_spPartnerConfiguration); character.Session.BroadcastPidx(); foreach (IPlayerEntity member in group.Members) { member.Session.RefreshParty(_spPartnerConfiguration); member.Session.BroadcastPidx(); } } } private void PlayerAddToGroup() { while (_playersToAdd.TryDequeue(out (IPlayerEntity character, PlayerGroup) playerGroup)) { IPlayerEntity character = playerGroup.Item1; PlayerGroup group = playerGroup.Item2; character.SetGroup(group); group.AddMember(character); foreach (IPlayerEntity member in group.Members) { member.Session.RefreshParty(_spPartnerConfiguration); if (character.Id != member.Id) { continue; } member.Session.BroadcastPidx(); } } } private void ChangeLeader() { while (_leaderToChange.TryDequeue(out (long, PlayerGroup) playerGroup)) { playerGroup.Item2.OwnerId = playerGroup.Item1; } } }