// Sort delegate public static int DescendingMetricSquad(SquadRoster lhs, SquadRoster rhs) { if (lhs == null) { return ((rhs == null) ? 0 : -1); } else if (rhs == null) { return ((lhs == null) ? 0 : 1); } // Dividing by Clan Tag takes precedence, only when both are zero is the metric used if (lhs.ClanTagCount > 0 || rhs.ClanTagCount > 0) { if (lhs.ClanTagCount < rhs.ClanTagCount) { return 1; } if (lhs.ClanTagCount > rhs.ClanTagCount) { return -1; } return 0; } if (lhs.Metric < rhs.Metric) return 1; if (lhs.Metric > rhs.Metric) return -1; return 0; }
private void ScramblerLoop() { /* Strategy: Scan each team and build filtered team and optionally squad lists. The ScrambleBy metric of each item in the pool is calculated. The pool is sorted according to the ScrambleBy setting. The best player/squad is assigned to the losing team, then the team total is calculated. More strong players/squads are added to the losing team until its metric sum is greater than the winning team, then players/squads are added to the winning team until it is greater, and so on. If at any time a team is full, the remainder of the players/squads are added to the other team. Finally, each member of the new team is checked and if they need to be moved, a move command is issued. Since this is between rounds, a special move command that bypasses all move tracking is used. */ try { DateTime last = DateTime.MinValue; while (fIsEnabled) { double delay = 0; DateTime since = DateTime.MinValue; bool logOnly = false; fWhileScrambling = false; lock (fExtrasLock) { fExtraNames.Clear(); } lock (fScramblerLock) { while (fScramblerLock.MaxDelay == 0) { Monitor.Wait(fScramblerLock); if (!fIsEnabled) return; } if (fScramblerLock.MaxDelay == -1) { fScramblerLock.MaxDelay = 0; logOnly = true; } delay = fScramblerLock.MaxDelay; since = fScramblerLock.LastUpdate; fScramblerLock.MaxDelay = 0; fScramblerLock.LastUpdate = DateTime.MinValue; } if (since == DateTime.MinValue) continue; if (!logOnly && last != DateTime.MinValue && DateTime.Now.Subtract(last).TotalMinutes < 3) { DebugScrambler("^0Last scramble was less than 5 minutes ago, skipping!"); continue; } try { PerModeSettings perMode = GetPerModeSettings(); // wait specified number of seconds if (delay > 0) { bool listUpdated = false; while (DateTime.Now.Subtract(since).TotalSeconds < delay) { try { if (!listUpdated && delay - DateTime.Now.Subtract(since).TotalSeconds <= 5) { // update the player list within 5 seconds of the delay expiring listUpdated = true; DebugScrambler("Last chance player list update, account for players who have left"); ServerCommand("admin.listPlayers", "all"); } } catch (Exception) {} Thread.Sleep(1000); // 1 second if (!fIsEnabled) return; } } String extra = String.Empty; if (DivideBy == DivideByChoices.ClanTag) extra = " [" + ClanTagToDivideBy + "]"; String kst = String.Empty; if (KeepSquadsTogether) kst = ", KeepSquadsTogether"; String kctiss = String.Empty; if (KeepClanTagsInSameTeam) { kctiss = ", KeepClansTagsInSameTeam"; if (KeepFriendsInSameTeam) kctiss = kctiss + ", KeepFriendsInSameTeam"; } DebugScrambler("Starting scramble of " + this.TotalPlayerCount + " players, winner was " + GetTeamName(fWinner)); DebugScrambler("Using (" + ScrambleBy + kst + kctiss + ", DivideBy = " + DivideBy + extra + ")"); if (!logOnly) last = DateTime.Now; // Build a filtered list List<String> toScramble = new List<String>(); //List<String> exempt = new List<String>(); PlayerModel player = null; lock (fAllPlayers) { foreach (String egg in fAllPlayers) { try { player = GetPlayer(egg); if (player == null) continue; // For debugging if (player.Team > 0 && player.Team <= 2) { fDebugScramblerBefore[player.Team-1].Add(player.ClonePlayer()); } else continue; // skip joining players // Add this player to list of scramblers toScramble.Add(egg); } catch (Exception e) { if (DebugLevel >= 8) ConsoleException(e); } } // Now that we have captured our master list, handle new joins with care if (toScramble.Count > 0 && !logOnly) fWhileScrambling = true; } if (toScramble.Count == 0) continue; // Build squad tables, clan tables and overall list List<SquadRoster> all = new List<SquadRoster>(); List<PlayerModel> usHaveNoSquad = new List<PlayerModel>(); List<PlayerModel> ruHaveNoSquad = new List<PlayerModel>(); List<SquadRoster> usSquadOfOne = new List<SquadRoster>(); List<SquadRoster> ruSquadOfOne = new List<SquadRoster>(); Dictionary<int,SquadRoster> squads = new Dictionary<int,SquadRoster>(); // key int is (team * 1000) + squad List<PlayerModel> loneWolves = new List<PlayerModel>(); int key = 0; String debugMsg = String.Empty; foreach (String egg in toScramble) { try { if (!IsKnownPlayer(egg)) continue; // might have left while we were working player = GetPlayer(egg); if (player == null) continue; if (player.Team < 1) continue; // skip players that are still joining PlayerModel clone = player.ClonePlayer(); // from now on, use a clone if (clone.Squad < 1) { if (clone.Squad == 0) { if (clone.Team == 1) { usHaveNoSquad.Add(clone); } else if (clone.Team == 2) { ruHaveNoSquad.Add(clone); } } continue; // skip players not in a squad } key = 9000; // free pool int squadId = clone.Squad; if (KeepSquadsTogether) { key = (Math.Max(0, clone.Team) * 1000) + Math.Max(0, clone.Squad); if (key < 1000) { loneWolves.Add(clone); continue; } else { DebugScrambler("Keeping ^b" + clone.FullName + "^n together with squad, using key " + key); } AddPlayerToSquadRoster(squads, clone, key, squadId, true); } else if (KeepClanTagsInSameTeam) { String tt = ExtractTag(clone); if (tt == null) tt = String.Empty; int numInSquad = CountMatchingTags(clone, Scope.SameSquad); // Keep players with same clan tag in the same squad //if (numInSquad >= 2) { key = (Math.Max(0, clone.Team) * 1000) + Math.Max(0, clone.Squad); // 0 is okay, makes lone-wolf pool if (String.IsNullOrEmpty(tt) || key < 1000) { loneWolves.Add(clone); continue; } else if (numInSquad >= 2) { DebugScrambler("Keeping ^b" + clone.Name + "^n together with " + numInSquad + " tags [" + tt + "] with squad, using key " + key); } /* } else { loneWolves.Add(clone); continue; */ //} AddPlayerToSquadRoster(squads, clone, key, squadId, true); } else { loneWolves.Add(clone); } } catch (Exception e) { if (DebugLevel >= 8) ConsoleException(e); } } // Add lone wolves to empty squads int emptyId = 1; SquadRoster home = null; bool filling = false; foreach (PlayerModel wolf in loneWolves) { bool goback = true; while (goback) { if (!filling) { // Need to find an empty squad key = (wolf.Team * 1000) + emptyId; while (squads.ContainsKey(key)) { emptyId = emptyId + 1; if (emptyId > (SQUAD_NAMES.Length - 1)) break; key = (wolf.Team * 1000) + emptyId; } filling = true; } if (emptyId > (SQUAD_NAMES.Length - 1)) break; if (filling) { // Add wolf to the squad we are filling until full key = (wolf.Team * 1000) + emptyId; home = AddPlayerToSquadRoster(squads, wolf, key, emptyId, false); if (home == null || !home.Roster.Contains(wolf)) { // Full filling = false; continue; } else { // Next wolf DebugScrambler("Lone wolf ^b" + wolf.Name + "^n filled in empty squad " + wolf.Team + "/" + emptyId); goback = false; continue; } } } } // Sum up the metric for each squad foreach (int k in squads.Keys) { SquadRoster sr = squads[k]; if (sr.Roster.Count == 1) { if (sr.Roster[0].Team == 1) { usSquadOfOne.Add(sr); } else if (sr.Roster[0].Team == 2) { ruSquadOfOne.Add(sr); } } switch (ScrambleBy) { case DefineStrong.RoundScore: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + p.ScoreRound; } break; case DefineStrong.RoundSPM: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + p.SPMRound; } break; case DefineStrong.RoundKills: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + p.KillsRound; } break; case DefineStrong.RoundKDR: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + p.KDRRound; } break; case DefineStrong.PlayerRank: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + p.Rank; } break; case DefineStrong.RoundKPM: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + p.KPMRound; } break; case DefineStrong.BattlelogSPM: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + ((p.StatsVerified) ? p.SPM : p.SPMRound); } break; case DefineStrong.BattlelogKDR: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + ((p.StatsVerified) ? p.KDR : p.KDRRound); } break; case DefineStrong.BattlelogKPM: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + ((p.StatsVerified) ? p.KPM : p.KPMRound); } break; default: foreach (PlayerModel p in sr.Roster) { sr.Metric = sr.Metric + p.ScoreRound; } break; } String ot = (sr.Roster[0].Team == 1) ? "US" : "RU"; DebugScrambler(ot + "/" + SQUAD_NAMES[sr.Squad] + "(" + sr.Roster.Count + ") " + ScrambleBy + ":" + sr.Metric.ToString("F1")); switch (DivideBy) { case DivideByChoices.ClanTag: foreach (PlayerModel p in sr.Roster) { if (!String.IsNullOrEmpty(ClanTagToDivideBy) && ExtractTag(p) == ClanTagToDivideBy) sr.ClanTagCount = sr.ClanTagCount + 1; } debugMsg = "ClanTag[" + ClanTagToDivideBy + "] " + sr.ClanTagCount; break; case DivideByChoices.DispersalGroup: { int[] gCount = new int[3]{0,0,0}; foreach (PlayerModel p in sr.Roster) { if (IsDispersal(p, true)) { if (p.DispersalGroup == 1 || p.DispersalGroup == 2) { gCount[p.DispersalGroup] = gCount[p.DispersalGroup] + 1; } } } if (gCount[1] != 0 || gCount[2] != 0) { sr.DispersalGroup = (gCount[1] > gCount[2]) ? 1 : 2; } debugMsg = "Dispersal Group = " + sr.DispersalGroup; break; } case DivideByChoices.None: default: break; } if (DivideBy != DivideByChoices.None) DebugScrambler("Divide " + ot + "/" + SQUAD_NAMES[sr.Squad] + " by " + debugMsg); all.Add(sr); } squads.Clear(); if (all.Count == 0) continue; // Sort squads all.Sort(DescendingMetricSquad); DebugScrambler("After sorting:"); foreach (SquadRoster ds in all) { String oldt = (ds.Roster[0].Team == 1) ? "US" : "RU"; DebugScrambler(" " + ScrambleBy + ":" + ds.Metric.ToString("F1") + " " + oldt + "/" + SQUAD_NAMES[ds.Squad]); } // Prepare the new team lists List<PlayerModel> usScrambled = new List<PlayerModel>(); Dictionary<int,SquadRoster> usSquads = new Dictionary<int,SquadRoster>(); double usMetric = 0; List<PlayerModel> ruScrambled = new List<PlayerModel>(); Dictionary<int,SquadRoster> ruSquads = new Dictionary<int,SquadRoster>(); double ruMetric = 0; // Dole out squads, keeping metric in balance, starting with the losing team List<PlayerModel> target = (fWinner == 0 || fWinner == 1) ? ruScrambled : usScrambled; Dictionary<int,SquadRoster> targetSquadTable = (fWinner == 0 || fWinner == 1) ? ruSquads : usSquads; int teamMax = MaximumServerSize/2; debugMsg = String.Empty; // Pre-process DivideBy setting if (DivideBy == DivideByChoices.DispersalGroup) { // Skim the dispersal squads off the top List<PlayerModel> localTarget = null; List<SquadRoster> copy = new List<SquadRoster>(all); foreach (SquadRoster disp in copy) { if (disp.DispersalGroup == 1 && usScrambled.Count < teamMax) { localTarget = usScrambled; debugMsg = GetTeamName(1); } else if (disp.DispersalGroup == 2 && ruScrambled.Count < teamMax) { localTarget = ruScrambled; debugMsg = GetTeamName(2); } else { continue; } DebugScrambler("Squad " + SQUAD_NAMES[disp.Squad] + ", Dispersal Group " + disp.DispersalGroup + " to " + debugMsg + " team"); AssignSquadToTeam(disp, targetSquadTable, usScrambled, ruScrambled, localTarget); all.Remove(disp); } if (usScrambled == target && target.Count >= teamMax) target = ruScrambled; if (ruScrambled == target && target.Count >= teamMax) target = usScrambled; } SquadRoster squad = (all.Count > 0) ? all[0] : null; List<PlayerModel> opposing = null; Dictionary<int,SquadRoster> opposingSquadTable = null; do { if (squad == null) break; all.Remove(squad); AssignSquadToTeam(squad, targetSquadTable, usScrambled, ruScrambled, target); // Recalc team metrics SumMetricByTeam(usScrambled, ruScrambled, out usMetric, out ruMetric); if (logOnly || DebugLevel >= 6) DebugScrambler("Updated scrambler metrics " + ScrambleBy + ": US(" + usScrambled.Count + ") = " + usMetric.ToString("F1") + ", RU(" + ruScrambled.Count + ") = " + ruMetric.ToString("F1")); if (usScrambled.Count >= teamMax && ruScrambled.Count >= teamMax) { all.Clear(); // no more room, skip remaining squads break; } if (all.Count == 0) break; // Choose new target team based on metrics if (usScrambled.Count >= teamMax && ruScrambled.Count < teamMax) { target = ruScrambled; targetSquadTable = ruSquads; opposing = usScrambled; squad = all[0]; continue; // skip additional checks, no other choice } else if (ruScrambled.Count >= teamMax && usScrambled.Count < teamMax) { target = usScrambled; targetSquadTable = usSquads; opposing = ruScrambled; squad = all[0]; continue; // skip additional checks, no other choice } else if (usMetric < ruMetric) { target = usScrambled; targetSquadTable = usSquads; opposing = ruScrambled; debugMsg = "Scrambling to target = " + GetTeamName(1); } else { target = ruScrambled; targetSquadTable = ruSquads; opposing = usScrambled; debugMsg = "Scrambling to target = " + GetTeamName(2); } // Override choice if teams would be too unbalanced by player count if (target.Count > opposing.Count) { // Take a weak squad from the end of the list instead squad = all[all.Count-1]; // assign to the opposing team List<PlayerModel> tmp = target; target = opposing; opposing = tmp; if (target == usScrambled) { targetSquadTable = usSquads; debugMsg = "^4REVISED for count target = " + GetTeamName(1); } else { targetSquadTable = ruSquads; debugMsg = "^4REVISED for count target = " + GetTeamName(2); } } else { squad = all[0]; // use strongest squad } if (logOnly || DebugLevel >= 6) { DebugScrambler(" "); DebugScrambler(debugMsg + ", squad " + SQUAD_NAMES[squad.Squad] + " (" + squad.Roster.Count + ")"); } } while (all.Count > 0); if (!fIsEnabled) return; // Make sure player counts aren't too out of balance if (usScrambled.Count <= teamMax && ruScrambled.Count <= teamMax && Math.Abs(usScrambled.Count - ruScrambled.Count) > 1) { int needed = Math.Abs(usScrambled.Count - ruScrambled.Count)/2; int toTeamId = 0; int targetDispersalGroup = 0; List<PlayerModel> opposingCopy = new List<PlayerModel>(); List<PlayerModel> tmpCopy = new List<PlayerModel>(); List<PlayerModel> oppHaveNoSquad = null; List<SquadRoster> oppSquadOfOne = null; if (usScrambled.Count < ruScrambled.Count) { target = usScrambled; targetSquadTable = usSquads; targetDispersalGroup = 1; toTeamId = 1; opposing = ruScrambled; opposingSquadTable = ruSquads; oppHaveNoSquad = ruHaveNoSquad; oppSquadOfOne = ruSquadOfOne; debugMsg = GetTeamName(1) + " needs " + needed + " more players"; } else { target = ruScrambled; targetSquadTable = ruSquads; targetDispersalGroup = 2; toTeamId = 2; opposing = usScrambled; opposingSquadTable = usSquads; oppHaveNoSquad = usHaveNoSquad; oppSquadOfOne = usSquadOfOne; debugMsg = GetTeamName(2) + " needs " + needed + " more players"; } DebugScrambler("Adjusting team sizes, US(" + usScrambled.Count + "/" + fTeam1.Count + ") vs RU(" + ruScrambled.Count + "/" + fTeam2.Count + ") " + debugMsg); // See if we have some new players that joined after we started scrambling List<String> extras = null; lock (fExtrasLock) { if (fExtraNames.Count > 0) { extras = new List<String>(); extras.AddRange(fExtraNames); } } if (extras != null) { foreach (String ename in extras) { try { PlayerModel xtra = GetPlayer(ename); if (xtra == null) continue; SquadRoster sr = null; if (targetSquadTable.TryGetValue(xtra.Squad, out sr)) { if (sr.Roster.Count >= fMaxSquadSize) continue; sr.Roster.Add(xtra); } else { sr = new SquadRoster(xtra.Squad); sr.Roster.Add(xtra); targetSquadTable[xtra.Squad] = sr; } DebugScrambler("Adding new joining player ^b" + xtra.FullName + "^n to " + TeamName(toTeamId) + " team"); target.Add(xtra); lock (fExtrasLock) { if (fExtraNames.Contains(ename)) fExtraNames.Remove(ename); } --needed; if (needed == 0) break; } catch (Exception e) { ConsoleException(e); } } } // Rearrange opposing team scrambled list so that squad-of-one and have-no-squad players come first tmpCopy.AddRange(opposing); foreach (SquadRoster monoSquad in oppSquadOfOne) { PlayerModel op = monoSquad.Roster[0]; opposingCopy.Add(op); tmpCopy.Remove(op); } oppSquadOfOne.Clear(); foreach (PlayerModel op in oppHaveNoSquad) { opposingCopy.Add(op); tmpCopy.Remove(op); } oppHaveNoSquad.Clear(); // Since team list is sorted, take from the weak end of the team for (int j = tmpCopy.Count - 1; j >= 0; --j) { opposingCopy.Add(tmpCopy[j]); } tmpCopy.Clear(); // Move players from opposing team to target team until counts are in balance while (opposing.Count > 0 && (opposing.Count - target.Count) > 1) { PlayerModel filler = null; // Loop through the rearranged copy of opposing team to find a filler player to move to the target team // We use a copy since the original list has to be modified foreach (PlayerModel f in opposingCopy) { if (filler == null) break; // Check to make sure Dispersal isn't violated if (DivideBy == DivideByChoices.DispersalGroup && IsDispersal(filler, true) && filler.DispersalGroup != targetDispersalGroup) { filler = null; continue; } // Make sure player doesn't have clan tag being divided String ft = ExtractTag(filler); if (ft == null) ft = String.Empty; if (DivideBy == DivideByChoices.ClanTag && ft == ClanTagToDivideBy) { filler = null; continue; } // Make sure squad filler is coming from doesn't have clan tags to keep together int cmt = 0; SquadRoster fillerSquad = null; if ((KeepClanTagsInSameTeam || KeepSquadsTogether) && filler.Squad > 0 && opposingSquadTable.TryGetValue(filler.Squad, out fillerSquad) && fillerSquad != null) { foreach (PlayerModel mate in fillerSquad.Roster) { if (ft == ExtractTag(mate)) ++cmt; } int required = (KeepClanTagsInSameTeam) ? 1 : 2; if (cmt >= required) { filler = null; continue; } // TBD same check for friends if KeepFriendsInSameTeam is true } // Otherwise, our candidate filler player is the one to go try { int formerSquad = filler.Squad; AssignFillerToTeam(filler, toTeamId, target, targetSquadTable); opposing.Remove(filler); SquadRoster fromSquad = null; if (formerSquad > 0 && opposingSquadTable.TryGetValue(formerSquad, out fromSquad) && fromSquad != null) { fromSquad.Roster.Remove(filler); } } catch (Exception e) { ConsoleException(e); } // That's one down, how may more to go? Check in the outer while loop break; } // Check to make sure we found a filler if (filler == null) { DebugScrambler("^8Unable to balance teams for player count, giving up!"); break; } else { opposingCopy.Remove(filler); } } } // Final counts DebugScrambler("Final scrambled team counts: US(" + usScrambled.Count + "), RU(" + ruScrambled.Count + ")"); // Assert that everyone is in their proper team foreach (PlayerModel clone in usScrambled) { if (clone.Team != 1) { ConsoleDebug("WARNING: ^b" + clone.FullName + "^n was in " + GetTeamName(clone.Team) + ", correcting to " + GetTeamName(1)); clone.Team = 1; } } foreach (PlayerModel clone in ruScrambled) { if (clone.Team != 2) { ConsoleDebug("WARNING: ^b" + clone.FullName + "^n was in " + GetTeamName(clone.Team) + ", correcting to " + GetTeamName(2)); clone.Team = 2; } } if (!fIsEnabled) return; // Remember original squads foreach (PlayerModel clone in usScrambled) { if (clone.ScrambledSquad == -1) clone.ScrambledSquad = clone.Squad; if (clone.OriginalSquad == -1) clone.OriginalSquad = clone.Squad; } foreach (PlayerModel clone in ruScrambled) { if (clone.ScrambledSquad == -1) clone.ScrambledSquad = clone.Squad; if (clone.OriginalSquad == -1) clone.OriginalSquad = clone.Squad; } // Using live PlayerModels, move players into squad 0 of their unscrambled teams // to avoid movement order overflows of squad size List<String> unsquaded = new List<String>(); UnsquadMove(usSquads, ruSquads, logOnly, unsquaded); // uses live players, not clones! // Pause 2 seconds to let game server catch up DebugScrambler("Pause 2 seconds to let game server catch up"); Thread.Sleep(2*1000); // Swap players if they have the same clan tag if (!KeepSquadsTogether && KeepClanTagsInSameTeam) { if (DebugLevel >= 7) { DebugScrambler("BEFORE SWAPS"); ListSideBySide(usScrambled, ruScrambled, true, true); } SwapSameClanTags(ref usScrambled, ref ruScrambled); if (DebugLevel >= 7) { DebugScrambler("AFTER SWAPS"); ListSideBySide(usScrambled, ruScrambled, true, true); } } // Assert that no squad has more than fMaxSquadSize players Dictionary<int,int> playerCount = new Dictionary<int,int>(); foreach (PlayerModel clone in usScrambled) { int num = 0; if (clone.ScrambledSquad < 1 || clone.ScrambledSquad >= SQUAD_NAMES.Length) { ConsoleDebug("ASSERT: After unsquading " + GetTeamName(1) + ", ^b" + clone.FullName + "^n has invalid ScrambledSquad = " + clone.ScrambledSquad); continue; } clone.Squad = 0; // unsquad if (playerCount.TryGetValue(clone.ScrambledSquad, out num)) { num = num + 1; } playerCount[clone.Squad] = num; } foreach (int squadId in playerCount.Keys) { if (playerCount[squadId] > fMaxSquadSize) { ConsoleDebug("ASSERT: " + GetTeamName(1) + "/" + SQUAD_NAMES[squadId] + " has > " + fMaxSquadSize + " players! = " + playerCount[squadId]); } } playerCount.Clear(); foreach (PlayerModel clone in ruScrambled) { int num = 0; if (clone.ScrambledSquad < 1 || clone.ScrambledSquad >= SQUAD_NAMES.Length) { ConsoleDebug("ASSERT: After unsquading " + GetTeamName(2) + ", ^b" + clone.FullName + "^n has invalid ScrambledSquad = " + clone.ScrambledSquad); continue; } clone.Squad = 0; // unsquad if (playerCount.TryGetValue(clone.ScrambledSquad, out num)) { num = num + 1; } playerCount[clone.Squad] = num; } foreach (int squadId in playerCount.Keys) { if (playerCount[squadId] > fMaxSquadSize) { ConsoleDebug("ASSERT: " + GetTeamName(2) + "/" + SQUAD_NAMES[squadId] + " has > " + fMaxSquadSize + " players! = " + playerCount[squadId]); } } playerCount.Clear(); // Now run through each cloned list and move any players that need moving DebugScrambler("STARTING SCRAMBLE MOVES"); ScrambleStatus check = ScrambleTeams(usScrambled, ruScrambled, logOnly); DebugScrambler("FINISHED SCRAMBLE MOVES"); switch (check) { case ScrambleStatus.CompletelyFull: DebugScrambler("SERVER IS COMPLETELY FULL! No scrambling is possible."); break; case ScrambleStatus.Failure: DebugScrambler("UNABLE TO SCRAMBLE, no room to move!"); break; case ScrambleStatus.PartialSuccess: DebugScrambler("SCRAMBLE ABORTED! Some moves completed, some failed!"); break; case ScrambleStatus.Success: default: break; } ScheduleListPlayers(1); // refresh // For debugging foreach (PlayerModel clone in usScrambled) { if (!IsKnownPlayer(clone.Name)) continue; fDebugScramblerAfter[0].Add(clone); } foreach (PlayerModel clone in ruScrambled) { if (!IsKnownPlayer(clone.Name)) continue; fDebugScramblerAfter[1].Add(clone); } DebugScrambler("DONE!"); //if (logOnly || DebugLevel >= 6) CommandToLog("scrambled"); } catch (Exception e) { ConsoleException(e); } } } catch (Exception e) { ConsoleException(e); } finally { fWhileScrambling = false; ConsoleWrite("^bScramblerLoop^n thread stopped", 0); } }
private void RemapSquad(Dictionary<int,SquadRoster> squadTable, SquadRoster squad) { int emptyId = 1; while (squadTable.ContainsKey(emptyId)) { emptyId = emptyId + 1; if (emptyId > (SQUAD_NAMES.Length - 1)) { if (DebugLevel >= 8) ConsoleDebug("RemapSquad: ran out of empty squads!"); return; } } squad.Squad = emptyId; }
private void AssignSquadToTeam(SquadRoster squad, Dictionary<int,SquadRoster> squadTable, List<PlayerModel> usScrambled, List<PlayerModel> ruScrambled, List<PlayerModel> target) { /* The PlayerModel object is still live, so we can't change managed properties like Team or Squad. Instead, the assigned team is implied by the list (usScrambled or ruScrambled) the player is added to and the squad is remembered in the ScrambledSquad property. This is later used during the move command to assign the player to that squad in the destination team. */ int teamMax = MaximumServerSize/2; if (usScrambled.Count >= teamMax && ruScrambled.Count >= teamMax) { DebugScrambler("BOTH teams full! Skipping remaining free pool!"); return; } int wasSquad = squad.Roster[0].Squad; // Remap if there is a squad collision if (squadTable.ContainsKey(squad.Squad)) { RemapSquad(squadTable, squad); } squadTable[squad.Squad] = squad; int wasTeam = squad.Roster[0].Team; String st = GetTeamName(wasTeam); String gt = GetTeamName((target == usScrambled) ? 1 : 2); DebugScrambler(st + "/" + SQUAD_NAMES[wasSquad] + " scrambled to " + gt + "/" + SQUAD_NAMES[squad.Squad]); // Assign the squad to the target team int toTeam = (target == usScrambled) ? 1 : 2; foreach (PlayerModel clone in squad.Roster) { clone.ScrambledSquad = squad.Squad; if (target.Count < teamMax && IsKnownPlayer(clone.Name)) { clone.Team = toTeam; target.Add(clone); } } }
private void AssignFillerToTeam(PlayerModel filler, int toTeamId, List<PlayerModel> target, Dictionary<int,SquadRoster> targetSquadTable) { String who = GetTeamName(toTeamId); if ((target.Count + 1) > (MaximumServerSize/2)) { DebugScrambler("Team " + who + " is full, skipping filler assignment of ^b" + filler.FullName); return; } if (!IsKnownPlayer(filler.Name)) return; // might have left // Find a squad with room to add this player, otherwise create a squad int toSquadId = 0; int emptyId = 1; SquadRoster toSquad = null; foreach (int key in targetSquadTable.Keys) { toSquad = targetSquadTable[key]; if (toSquad.Roster.Count == fMaxSquadSize) continue; toSquadId = key; break; } if (toSquadId == 0) { // Create a new squad while (targetSquadTable.ContainsKey(emptyId)) { ++emptyId; if (emptyId >= SQUAD_NAMES.Length) { emptyId = 0; break; } } toSquadId = emptyId; ConsoleDebug("AssignFillerToTeam: created new squad " + SQUAD_NAMES[toSquadId]); } else { ConsoleDebug("AssignFillerToTeam: using existing squad " + SQUAD_NAMES[toSquadId]); } DebugScrambler("Filling in " + who + " team with player ^b" + filler.FullName + "^n to squad " + SQUAD_NAMES[toSquadId]); filler.ScrambledSquad = toSquadId; filler.Team = toTeamId; target.Add(filler); toSquad = null; if (!targetSquadTable.ContainsKey(toSquadId)) { toSquad = new SquadRoster(toSquadId); targetSquadTable[toSquadId] = toSquad; } else { toSquad = targetSquadTable[toSquadId]; } toSquad.Roster.Add(filler); }
private SquadRoster AddPlayerToSquadRoster(Dictionary<int,SquadRoster> squads, PlayerModel player, int key, int squadId, bool ignoreSize) { SquadRoster squad = null; if (squads.TryGetValue(key, out squad)) { if (ignoreSize || squad.Roster.Count < fMaxSquadSize) { squad.Roster.Add(player); } } else { squad = new SquadRoster(squadId); squad.Roster.Add(player); squads[key] = squad; } return squad; }