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 " + TotalPlayerCount() + " players, winner was T" + fWinner + "(" + 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 team
//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 if (CheckWhitelist(clone, WL_BALANCE)) { // Leave Whitelisted players in same team and squad
key = (Math.Max(0, clone.Team) * 1000) + Math.Max(0, clone.Squad); // 0 is okay, makes lone-wolf pool
DebugScrambler("Keeping whitelisted ^b" + clone.FullName + "^n in same team and squad, using key " + key);
SquadRoster tsr = AddPlayerToSquadRoster(squads, clone, key, squadId, true);
if (tsr != null) {
tsr.WhitelistCount = tsr.WhitelistCount + 1;
}
} else {
loneWolves.Add(clone);
}
} catch (Exception e) {
if (DebugLevel >= 8) ConsoleException(e);
}
}
// Add lone wolves to empty squads
ScrambleLoneWolves(loneWolves, squads, 1);
ScrambleLoneWolves(loneWolves, squads, 2);
/*
bool filling = false;
// Do Team 1 first
foreach (PlayerModel wolf in loneWolves) {
if (wolf.Team != 1)
continue;
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) ? "T1" : "T2";
DebugScrambler(ot + "/" + GetSquadName(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 (IsInDispersalList(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 + "/" + GetSquadName(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) ? "T1" : "T2";
DebugScrambler(" " + ScrambleBy + ":" + ds.Metric.ToString("F1") + " " + oldt + "/" + GetSquadName(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 = "T1 (" + GetTeamName(1) + ")";
} else if (disp.DispersalGroup == 2 && ruScrambled.Count < teamMax) {
localTarget = ruScrambled;
debugMsg = "T2 (" + GetTeamName(2) + ")";
} else {
continue;
}
DebugScrambler("Squad " + GetSquadName(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 + ": T1(" + usScrambled.Count + ") = " + usMetric.ToString("F1") + ", T2(" + 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 = T1 (" + GetTeamName(1) + ")";
} else {
target = ruScrambled;
targetSquadTable = ruSquads;
opposing = usScrambled;
debugMsg = "Scrambling to target = T2 (" + 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 = T1 (" + GetTeamName(1) + ")";
} else {
targetSquadTable = ruSquads;
debugMsg = "^4REVISED for count target = T2 (" + GetTeamName(2) + ")";
}
} else {
squad = all[0]; // use strongest squad
}
if (logOnly || DebugLevel >= 6) {
DebugScrambler(" ");
DebugScrambler(debugMsg + ", squad " + GetSquadName(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 = "T1 (" + GetTeamName(1) + ") needs " + needed + " more players";
} else {
target = ruScrambled;
targetSquadTable = ruSquads;
targetDispersalGroup = 2;
toTeamId = 2;
opposing = usScrambled;
opposingSquadTable = usSquads;
oppHaveNoSquad = usHaveNoSquad;
oppSquadOfOne = usSquadOfOne;
debugMsg = "T2 (" + GetTeamName(2) + ") needs " + needed + " more players";
}
DebugScrambler("Adjusting team sizes, T1(" + usScrambled.Count + "/" + fTeam1.Count + ") vs T2(" + 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 " + GetTeamName(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 (f == null) break;
filler = f;
// Check to make sure Dispersal isn't violated
if (DivideBy == DivideByChoices.DispersalGroup && IsInDispersalList(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
}
// Make sure player isn't whitelisted
if (CheckWhitelist(filler, WL_BALANCE)) {
filler = null;
continue;
}
// 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: T1(" + usScrambled.Count + "), T2(" + 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 T" + clone.Team + "(" + GetTeamName(clone.Team) + "), correcting to T1");
clone.Team = 1;
}
}
foreach (PlayerModel clone in ruScrambled) {
if (clone.Team != 2) {
ConsoleDebug("WARNING: ^b" + clone.FullName + "^n was in T" + clone.Team + "(" + GetTeamName(clone.Team) + "), correcting to T2");
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 T1, ^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: T1/" + GetSquadName(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 T2, ^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: T2/" + GetSquadName(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 (ThreadAbortException) {
fAborted = true;
return;
} catch (Exception e) {
ConsoleException(e);
} finally {
fWhileScrambling = false;
if (!fAborted) ConsoleWrite("^bScramblerLoop^n thread stopped", 6);
}
}