private void FastBalance(String trigger)
{
/* Useful variables */
PlayerModel player = null;
String simpleMode = String.Empty;
PerModeSettings perMode = GetPerModeSettings();
int winningTeam = 0;
int losingTeam = 0;
int biggestTeam = 0;
int smallestTeam = 0;
int[] ascendingSize = null;
int[] descendingTickets = null;
String strongMsg = String.Empty;
int diff = 0;
DateTime now = DateTime.Now;
String log = String.Empty;
int level = 6;
int adj = 1;
/* Sanity checks */
if (fServerInfo == null) {
return;
}
if (fGameState != GameState.Playing) {
return;
}
if (IsNonBalancingMode()) {
return;
}
if (trigger.Contains("Kill")) {
level = 8;
adj = 0;
}
if (fLastFastMoveTimestamp != DateTime.MinValue && now.Subtract(fLastFastMoveTimestamp).TotalSeconds < 25) {
if (DebugLevel >= (level + adj)) DebugFast("Too soon to check for fast balance again, wait another " + (25.0 - now.Subtract(fLastFastMoveTimestamp).TotalSeconds).ToString("F1") + " seconds");
return;
}
Speed balanceSpeed = GetBalanceSpeed(perMode);
if (balanceSpeed == Speed.Stop) {
if (DebugLevel >= (level + adj)) DebugFast("Speed is Stop, fast balance check skipped. " + trigger + " was trigger"); // DebugBalance on purpose to get repeat filtering
return;
}
int totalPlayerCount = TotalPlayerCount();
if (DebugLevel >= (level + adj)) DebugFast(trigger + "Checking if fast balance is needed, " + totalPlayerCount + " players");
if (totalPlayerCount >= (MaximumServerSize-1)) {
if (DebugLevel >= (level + adj)) DebugFast("Server is full, no balancing or unstacking will be attempted!");
return;
}
if (totalPlayerCount >= (perMode.MaxPlayers-1)) {
if (DebugLevel >= (level + adj)) DebugFast("Server is full by per-mode Max Players, no balancing or unstacking will be attempted!");
return;
}
int floorPlayers = (perMode.EnableLowPopulationAdjustments) ? 4 : 5;
if (totalPlayerCount < floorPlayers) {
if (DebugLevel >= (level + adj)) DebugFast("Not enough players in server, minimum is " + floorPlayers);
return;
}
if (totalPlayerCount > 0) {
AnalyzeTeams(out diff, out ascendingSize, out descendingTickets, out biggestTeam, out smallestTeam, out winningTeam, out losingTeam);
}
// Adjust speed to Fast?
if (balanceSpeed != Speed.Fast) {
if (diff > MaxFastDiff()) {
balanceSpeed = Speed.Fast;
}
}
if (balanceSpeed != Speed.Fast || diff <= MaxFastDiff()) {
if (diff > 1 && DebugLevel >= level) DebugFast("Fast balance not active, diff is only " + diff + ", requires more than " + MaxFastDiff());
return;
}
// Prepare for player selection
if (smallestTeam < 1) {
DebugFast("Cannot determine smallest team: " + smallestTeam);
return;
}
List<PlayerModel> big = new List<PlayerModel>();
List<PlayerModel> tmp = GetTeam(biggestTeam);
if (tmp == null || tmp.Count < 1 || biggestTeam < 1) {
DebugFast("Cannot determine biggest team: " + biggestTeam);
return;
}
big.AddRange(tmp);
tmp = new List<PlayerModel>();
foreach (PlayerModel p in big) {
if (p == null) continue;
if (fGameVersion != GameVersion.BF3 && p.Role >= 0 && p.Role < ROLE_NAMES.Length && p.Role != ROLE_PLAYER) {
if (DebugLevel >= 7) DebugFast("Excluding ^b" + p.Name + "^n, role is " + ROLE_NAMES[p.Role]);
continue;
} else if (OnWhitelist && CheckWhitelist(p, WL_BALANCE)) { // exclude if on whitelist
if (DebugLevel >= 7) DebugFast("Excluding ^b" + p.FullName + "^n: on Whitelist");
continue;
} else if (p.MovedByMBTimestamp != DateTime.MinValue) { // exclude if moved recently
double mins = now.Subtract(p.MovedByMBTimestamp).TotalMinutes;
if (mins < MinutesAfterBeingMoved) {
if (DebugLevel >= 7) DebugFast("Excluding ^b" + p.Name + "^n: last move was " + mins.ToString("F0") + " minutes ago, less than required " + MinutesAfterBeingMoved.ToString("F0") + " minutes");
continue;
} else {
// reset
p.MovedByMBTimestamp = DateTime.MinValue;
}
}
tmp.Add(p);
}
big = tmp;
// Select player
if (DebugLevel >= 7) ConsoleDebug("FastBalance selecting player");
if (big.Count < 1) {
if (DebugLevel >= level) DebugFast("All players on " + GetTeamName(biggestTeam) + " team were excluded, unable to select the " + SelectFastBalanceBy + " player");
return;
}
String kstat = String.Empty;
switch (SelectFastBalanceBy) {
case ForceMove.Weakest: {
switch (perMode.DetermineStrongPlayersBy) {
case DefineStrong.RoundScore:
big.Sort(DescendingRoundScore);
kstat = "S";
break;
case DefineStrong.RoundSPM:
big.Sort(DescendingRoundSPM);
kstat = "SPM";
break;
case DefineStrong.RoundKills:
big.Sort(DescendingRoundKills);
kstat = "K";
break;
case DefineStrong.RoundKDR:
big.Sort(DescendingRoundKDR);
kstat = "KDR";
break;
case DefineStrong.PlayerRank:
big.Sort(DescendingPlayerRank);
kstat = "R";
break;
case DefineStrong.RoundKPM:
big.Sort(DescendingRoundKPM);
kstat = "KPM";
break;
case DefineStrong.BattlelogSPM:
big.Sort(DescendingSPM);
kstat = "bSPM";
break;
case DefineStrong.BattlelogKDR:
big.Sort(DescendingKDR);
kstat = "bKDR";
break;
case DefineStrong.BattlelogKPM:
big.Sort(DescendingKPM);
kstat = "bKPM";
break;
default:
big.Sort(DescendingRoundScore);
break;
}
// Select weakest
player = big[big.Count-1];
DebugFast("Selected WEAKEST player ^b" + player.FullName + "^n, " + kstat + ": " + GetPlayerStat(player, perMode.DetermineStrongPlayersBy).ToString("F1"));
break;
}
case ForceMove.Newest: {
// Descending by elapsed join time
big.Sort(delegate(PlayerModel lhs, PlayerModel rhs) {
if (lhs == null) {
return ((rhs == null) ? 0 : -1);
} else if (rhs == null) {
return ((lhs == null) ? 0 : 1);
}
double lTime = GetPlayerJoinedTimeSpan(lhs).TotalSeconds;
double rTime = GetPlayerJoinedTimeSpan(rhs).TotalSeconds;
if (lTime < rTime) return 1;
if (lTime > rTime) return -1;
return 0;
});
// Select newest
player = big[big.Count-1];
DebugFast("Selected NEWEST player ^b" + player.FullName + "^n, joined " + GetPlayerJoinedTimeSpan(player).TotalMinutes.ToString("F1") + " minutes ago");
break;
}
case ForceMove.Random: {
Random rnd = new Random();
player = big[rnd.Next(big.Count)];
DebugFast("Selected RANDOM player ^b" + player.FullName);
break;
}
}
/* Move for fast balance */
if (DebugLevel >= 7) ConsoleDebug("Move for fast balance");
int origTeam = player.Team;
String origName = GetTeamName(player.Team);
int lastMoveFrom = player.LastMoveFrom;
if (lastMoveFrom != 0) {
origTeam = lastMoveFrom;
origName = GetTeamName(origTeam);
}
MoveInfo move = new MoveInfo(player.Name, player.Tag, origTeam, origName, smallestTeam, GetTeamName(smallestTeam), 0);
move.For = MoveType.Balance;
// private message to player before getting killed
move.Format(this, ChatMovedForBalance, false, true);
move.Format(this, YellMovedForBalance, true, true);
// regular message for after move
move.Format(this, ChatMovedForBalance, false, false);
move.Format(this, YellMovedForBalance, true, false);
move.Fast = true;
String why = "because difference is " + diff;
log = "^4^bFAST BALANCE^n^0 moving ^b" + player.FullName + "^n from " + move.SourceName + " team to " + move.DestinationName + " team " + why;
log = (EnableLoggingOnlyMode) ? "^9(SIMULATING)^0 " + log : log;
DebugWrite(log, 3);
DebugWrite("^9" + move, 8);
player.LastMoveFrom = player.Team;
fLastFastMoveTimestamp = DateTime.Now;
KillAndMoveAsync(move);
/*
if (EnableLoggingOnlyMode) {
// Simulate completion of move
OnPlayerTeamChange(name, toTeam, 0);
OnPlayerMovedByAdmin(name, toTeam, 0, false); // simulate reverse order
}
*/
}