public void UpdateBestCompatibleInQueue(ObjectGuid guid, LfgQueueData queueData, string key, Dictionary <ObjectGuid, LfgRoles> roles) { byte storedSize = (byte)(string.IsNullOrEmpty(queueData.bestCompatible) ? 0 : queueData.bestCompatible.Count(p => p == '|') + 1); byte size = (byte)(key.Count(p => p == '|') + 1); if (size <= storedSize) { return; } Log.outDebug(LogFilter.Lfg, "UpdateBestCompatibleInQueue: Changed ({0}) to ({1}) as best compatible group for {2}", queueData.bestCompatible, key, guid); queueData.bestCompatible = key; queueData.tanks = SharedConst.LFGTanksNeeded; queueData.healers = SharedConst.LFGHealersNeeded; queueData.dps = SharedConst.LFGDPSNeeded; foreach (var it in roles) { LfgRoles role = it.Value; if (role.HasAnyFlag(LfgRoles.Tank)) { --queueData.tanks; } else if (role.HasAnyFlag(LfgRoles.Healer)) { --queueData.healers; } else { --queueData.dps; } } }
void FindBestCompatibleInQueue(ObjectGuid guid, LfgQueueData data) { Log.outDebug(LogFilter.Lfg, "FindBestCompatibleInQueue: {0}", guid); foreach (var pair in CompatibleMapStore) { if (pair.Value.compatibility == LfgCompatibility.WithLessPlayers && pair.Key.Contains(guid.ToString())) { UpdateBestCompatibleInQueue(guid, data, pair.Key, pair.Value.roles); } } }
LfgCompatibility CheckCompatibility(List <ObjectGuid> check) { string strGuids = ConcatenateGuids(check); LfgProposal proposal = new(); List <uint> proposalDungeons; Dictionary <ObjectGuid, ObjectGuid> proposalGroups = new(); Dictionary <ObjectGuid, LfgRoles> proposalRoles = new(); // Check for correct size if (check.Count > MapConst.MaxGroupSize || check.Empty()) { Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}): Size wrong - Not compatibles", strGuids); return(LfgCompatibility.WrongGroupSize); } // Check all-but-new compatiblitity if (check.Count > 2) { ObjectGuid frontGuid = check.First(); check.RemoveAt(0); // Check all-but-new compatibilities (New, A, B, C, D) -. check(A, B, C, D) LfgCompatibility child_compatibles = CheckCompatibility(check); if (child_compatibles < LfgCompatibility.WithLessPlayers) // Group not compatible { Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) child {1} not compatibles", strGuids, ConcatenateGuids(check)); SetCompatibles(strGuids, child_compatibles); return(child_compatibles); } check.Insert(0, frontGuid); } // Check if more than one LFG group and number of players joining byte numPlayers = 0; byte numLfgGroups = 0; foreach (var guid in check) { if (!(numLfgGroups < 2) && !(numPlayers <= MapConst.MaxGroupSize)) { break; } var itQueue = QueueDataStore.LookupByKey(guid); if (itQueue == null) { Log.outError(LogFilter.Lfg, "CheckCompatibility: [{0}] is not queued but listed as queued!", guid); RemoveFromQueue(guid); return(LfgCompatibility.Pending); } // Store group so we don't need to call Mgr to get it later (if it's player group will be 0 otherwise would have joined as group) foreach (var it2 in itQueue.roles) { proposalGroups[it2.Key] = guid.IsPlayer() ? guid : ObjectGuid.Empty; } numPlayers += (byte)itQueue.roles.Count; if (Global.LFGMgr.IsLfgGroup(guid)) { if (numLfgGroups == 0) { proposal.group = guid; } ++numLfgGroups; } } // Group with less that MAXGROUPSIZE members always compatible if (check.Count == 1 && numPlayers != MapConst.MaxGroupSize) { Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) sigle group. Compatibles", strGuids); var guid = check.First(); var itQueue = QueueDataStore.LookupByKey(guid); LfgCompatibilityData data = new(LfgCompatibility.WithLessPlayers); data.roles = itQueue.roles; Global.LFGMgr.CheckGroupRoles(data.roles); UpdateBestCompatibleInQueue(guid, itQueue, strGuids, data.roles); SetCompatibilityData(strGuids, data); return(LfgCompatibility.WithLessPlayers); } if (numLfgGroups > 1) { Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) More than one Lfggroup ({1})", strGuids, numLfgGroups); SetCompatibles(strGuids, LfgCompatibility.MultipleLfgGroups); return(LfgCompatibility.MultipleLfgGroups); } if (numPlayers > MapConst.MaxGroupSize) { Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) Too much players ({1})", strGuids, numPlayers); SetCompatibles(strGuids, LfgCompatibility.TooMuchPlayers); return(LfgCompatibility.TooMuchPlayers); } // If it's single group no need to check for duplicate players, ignores, bad roles or bad dungeons as it's been checked before joining if (check.Count > 1) { foreach (var it in check) { Dictionary <ObjectGuid, LfgRoles> roles = QueueDataStore[it].roles; foreach (var rolePair in roles) { KeyValuePair <ObjectGuid, LfgRoles> itPlayer = new(); foreach (var _player in proposalRoles) { itPlayer = _player; if (rolePair.Key == itPlayer.Key) { Log.outError(LogFilter.Lfg, "CheckCompatibility: ERROR! Player multiple times in queue! [{0}]", rolePair.Key); } else if (Global.LFGMgr.HasIgnore(rolePair.Key, itPlayer.Key)) { break; } } if (itPlayer.Key == proposalRoles.LastOrDefault().Key) { proposalRoles[rolePair.Key] = rolePair.Value; } } } byte playersize = (byte)(numPlayers - proposalRoles.Count); if (playersize != 0) { Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) not compatible, {1} players are ignoring each other", strGuids, playersize); SetCompatibles(strGuids, LfgCompatibility.HasIgnores); return(LfgCompatibility.HasIgnores); } StringBuilder o; Dictionary <ObjectGuid, LfgRoles> debugRoles = proposalRoles; if (!Global.LFGMgr.CheckGroupRoles(proposalRoles)) { o = new StringBuilder(); foreach (var it in debugRoles) { o.AppendFormat(", {0}: {1}", it.Key, GetRolesString(it.Value)); } Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) Roles not compatible{1}", strGuids, o.ToString()); SetCompatibles(strGuids, LfgCompatibility.NoRoles); return(LfgCompatibility.NoRoles); } var itguid = check.First(); proposalDungeons = QueueDataStore[itguid].dungeons; o = new StringBuilder(); o.AppendFormat(", {0}: ({1})", itguid, Global.LFGMgr.ConcatenateDungeons(proposalDungeons)); foreach (var guid in check) { if (guid == itguid) { continue; } List <uint> dungeons = QueueDataStore[itguid].dungeons; o.AppendFormat(", {0}: ({1})", guid, Global.LFGMgr.ConcatenateDungeons(dungeons)); List <uint> temporal = proposalDungeons.Intersect(dungeons).ToList(); proposalDungeons = temporal; } if (proposalDungeons.Empty()) { Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) No compatible dungeons{1}", strGuids, o.ToString()); SetCompatibles(strGuids, LfgCompatibility.NoDungeons); return(LfgCompatibility.NoDungeons); } } else { ObjectGuid gguid = check.First(); LfgQueueData queue = QueueDataStore[gguid]; proposalDungeons = queue.dungeons; proposalRoles = queue.roles; Global.LFGMgr.CheckGroupRoles(proposalRoles); // assing new roles } // Enough players? if (numPlayers != MapConst.MaxGroupSize) { Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) Compatibles but not enough players({1})", strGuids, numPlayers); LfgCompatibilityData data = new(LfgCompatibility.WithLessPlayers); data.roles = proposalRoles; foreach (var guid in check) { var queueData = QueueDataStore.LookupByKey(guid); UpdateBestCompatibleInQueue(guid, queueData, strGuids, data.roles); } SetCompatibilityData(strGuids, data); return(LfgCompatibility.WithLessPlayers); } ObjectGuid _guid = check.First(); proposal.queues = check; proposal.isNew = numLfgGroups != 1 || Global.LFGMgr.GetOldState(_guid) != LfgState.Dungeon; if (!Global.LFGMgr.AllQueued(check)) { Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) Group MATCH but can't create proposal!", strGuids); SetCompatibles(strGuids, LfgCompatibility.BadStates); return(LfgCompatibility.BadStates); } // Create a new proposal proposal.cancelTime = Time.UnixTime + SharedConst.LFGTimeProposal; proposal.state = LfgProposalState.Initiating; proposal.leader = ObjectGuid.Empty; proposal.dungeonId = proposalDungeons.SelectRandom(); bool leader = false; foreach (var rolePair in proposalRoles) { // Assing new leader if (rolePair.Value.HasAnyFlag(LfgRoles.Leader)) { if (!leader || proposal.leader.IsEmpty() || Convert.ToBoolean(RandomHelper.IRand(0, 1))) { proposal.leader = rolePair.Key; } leader = true; } else if (!leader && (proposal.leader.IsEmpty() || Convert.ToBoolean(RandomHelper.IRand(0, 1)))) { proposal.leader = rolePair.Key; } // Assing player data and roles LfgProposalPlayer data = new(); data.role = rolePair.Value; data.group = proposalGroups.LookupByKey(rolePair.Key); if (!proposal.isNew && !data.group.IsEmpty() && data.group == proposal.group) // Player from existing group, autoaccept { data.accept = LfgAnswer.Agree; } proposal.players[rolePair.Key] = data; } // Mark proposal members as not queued (but not remove queue data) foreach (var guid in proposal.queues) { RemoveFromNewQueue(guid); RemoveFromCurrentQueue(guid); } Global.LFGMgr.AddProposal(proposal); Log.outDebug(LogFilter.Lfg, "CheckCompatibility: ({0}) MATCH! Group formed", strGuids); SetCompatibles(strGuids, LfgCompatibility.Match); return(LfgCompatibility.Match); }
public void UpdateQueueTimers(byte queueId, long currTime) { Log.outDebug(LogFilter.Lfg, "Updating queue timers..."); foreach (var itQueue in QueueDataStore) { LfgQueueData queueinfo = itQueue.Value; uint dungeonId = queueinfo.dungeons.FirstOrDefault(); uint queuedTime = (uint)(currTime - queueinfo.joinTime); LfgRoles role = LfgRoles.None; int waitTime = -1; if (!waitTimesTankStore.ContainsKey(dungeonId)) { waitTimesTankStore[dungeonId] = new LfgWaitTime(); } if (!waitTimesHealerStore.ContainsKey(dungeonId)) { waitTimesHealerStore[dungeonId] = new LfgWaitTime(); } if (!waitTimesDpsStore.ContainsKey(dungeonId)) { waitTimesDpsStore[dungeonId] = new LfgWaitTime(); } if (!waitTimesAvgStore.ContainsKey(dungeonId)) { waitTimesAvgStore[dungeonId] = new LfgWaitTime(); } int wtTank = waitTimesTankStore[dungeonId].time; int wtHealer = waitTimesHealerStore[dungeonId].time; int wtDps = waitTimesDpsStore[dungeonId].time; int wtAvg = waitTimesAvgStore[dungeonId].time; foreach (var itPlayer in queueinfo.roles) { role |= itPlayer.Value; } role &= ~LfgRoles.Leader; switch (role) { case LfgRoles.None: // Should not happen - just in case waitTime = -1; break; case LfgRoles.Tank: waitTime = wtTank; break; case LfgRoles.Healer: waitTime = wtHealer; break; case LfgRoles.Damage: waitTime = wtDps; break; default: waitTime = wtAvg; break; } if (string.IsNullOrEmpty(queueinfo.bestCompatible)) { FindBestCompatibleInQueue(itQueue.Key, itQueue.Value); } LfgQueueStatusData queueData = new(queueId, dungeonId, waitTime, wtAvg, wtTank, wtHealer, wtDps, queuedTime, queueinfo.tanks, queueinfo.healers, queueinfo.dps); foreach (var itPlayer in queueinfo.roles) { ObjectGuid pguid = itPlayer.Key; Global.LFGMgr.SendLfgQueueStatus(pguid, queueData); } } }
public void AddQueueData(ObjectGuid guid, long joinTime, List <uint> dungeons, Dictionary <ObjectGuid, LfgRoles> rolesMap) { QueueDataStore[guid] = new LfgQueueData(joinTime, dungeons, rolesMap); AddToQueue(guid); }