// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Views.Party; /// /// Manages party creation and tracks character-to-party membership for member reconnection. /// public sealed class PartyManager : IPartyManager { private readonly System.Collections.Concurrent.ConcurrentDictionary _partyByCharacterName = new(StringComparer.Ordinal); private readonly ILogger _partyLogger; private readonly byte _maxPartySize; /// /// Initializes a new instance of the class. /// /// The maximum number of members per party. /// The logger for party instances. public PartyManager(byte maxPartySize, ILogger partyLogger) { this._maxPartySize = maxPartySize; this._partyLogger = partyLogger; } /// public Party CreateParty() => new(this, this._maxPartySize, this._partyLogger); /// public async ValueTask OnMemberReconnectedAsync(IPartyMember member) { if (!this._partyByCharacterName.TryGetValue(member.Name, out var party)) { return; } // Find the offline snapshot that was created when the member disconnected. var snapshot = party.PartyList.FirstOrDefault(m => m.Name == member.Name && !m.IsConnected); if (snapshot is null) { // Already replaced (e.g., duplicate reconnect event) — nothing to do. return; } await party.ReplaceMemberAsync(snapshot, member).ConfigureAwait(false); // Send the full party state to the rejoined player since they missed updates while offline. await member.InvokeViewPlugInAsync(p => p.UpdatePartyListAsync()).ConfigureAwait(false); await member.InvokeViewPlugInAsync(p => p.UpdatePartyHealthAsync()).ConfigureAwait(false); } /// void IPartyManager.TrackMembership(string characterName, Party party) => this._partyByCharacterName[characterName] = party; /// void IPartyManager.UntrackMembership(string characterName) => this._partyByCharacterName.TryRemove(characterName, out _); }