public byte FindGroups() { byte proposals = 0; List <ObjectGuid> firstNew = new(); while (!newToQueueStore.Empty()) { ObjectGuid frontguid = newToQueueStore.First(); Log.outDebug(LogFilter.Lfg, "FindGroups: checking [{0}] newToQueue({1}), currentQueue({2})", frontguid, newToQueueStore.Count, currentQueueStore.Count); firstNew.Clear(); firstNew.Add(frontguid); RemoveFromNewQueue(frontguid); List <ObjectGuid> temporalList = new(currentQueueStore); LfgCompatibility compatibles = FindNewGroups(firstNew, temporalList); if (compatibles == LfgCompatibility.Match) { ++proposals; } else { AddToCurrentQueue(frontguid); // Lfg group not found, add this group to the queue. } } return(proposals); }
void SetCompatibles(string key, LfgCompatibility compatibles) { if (!CompatibleMapStore.ContainsKey(key)) { CompatibleMapStore[key] = new LfgCompatibilityData(); } CompatibleMapStore[key].compatibility = compatibles; }
string GetCompatibleString(LfgCompatibility compatibles) { switch (compatibles) { case LfgCompatibility.Pending: return("Pending"); case LfgCompatibility.BadStates: return("Compatibles (Bad States)"); case LfgCompatibility.Match: return("Match"); case LfgCompatibility.WithLessPlayers: return("Compatibles (Not enough players)"); case LfgCompatibility.HasIgnores: return("Has ignores"); case LfgCompatibility.MultipleLfgGroups: return("Multiple Lfg Groups"); case LfgCompatibility.NoDungeons: return("Incompatible dungeons"); case LfgCompatibility.NoRoles: return("Incompatible roles"); case LfgCompatibility.TooMuchPlayers: return("Too much players"); case LfgCompatibility.WrongGroupSize: return("Wrong group size"); default: return("Unknown"); } }
LfgCompatibility FindNewGroups(List <ObjectGuid> check, List <ObjectGuid> all) { string strGuids = ConcatenateGuids(check); LfgCompatibility compatibles = GetCompatibles(strGuids); Log.outDebug(LogFilter.Lfg, "FindNewGroup: ({0}): {1} - all({2})", strGuids, GetCompatibleString(compatibles), ConcatenateGuids(all)); if (compatibles == LfgCompatibility.Pending) // Not previously cached, calculate { compatibles = CheckCompatibility(check); } if (compatibles == LfgCompatibility.BadStates && Global.LFGMgr.AllQueued(check)) { Log.outDebug(LogFilter.Lfg, "FindNewGroup: ({0}) compatibles (cached) changed from bad states to match", strGuids); SetCompatibles(strGuids, LfgCompatibility.Match); return(LfgCompatibility.Match); } if (compatibles != LfgCompatibility.WithLessPlayers) { return(compatibles); } // Try to match with queued groups while (!all.Empty()) { check.Add(all.First()); all.RemoveAt(0); LfgCompatibility subcompatibility = FindNewGroups(check, all); if (subcompatibility == LfgCompatibility.Match) { return(LfgCompatibility.Match); } check.RemoveAt(check.Count - 1); } return(compatibles); }
public LfgCompatibilityData(LfgCompatibility _compatibility, Dictionary <ObjectGuid, LfgRoles> _roles) { compatibility = _compatibility; roles = _roles; }
public LfgCompatibilityData(LfgCompatibility _compatibility) { compatibility = _compatibility; }
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); }