private void LogStatus(bool isFinal, int level)
{
try {
String tmsg = null;
// If server is empty, log status only every 60 minutes
int totalPlayers = TotalPlayerCount();
if (!isFinal && level < 9 && totalPlayers == 0) {
if (fRoundStartTimestamp != DateTime.MinValue && DateTime.Now.Subtract(fRoundStartTimestamp).TotalMinutes <= 60) {
return;
} else {
fRoundStartTimestamp = DateTime.Now;
}
}
if (!isFinal && (level == 4)) ConsoleWrite("+------------------------------------------------+", 0);
if (isFinal && fWinner != 0) {
tmsg = "^1Winner was team " + fWinner + " (" + GetTeamName(fWinner) + ")^0";
DebugWrite("^bStatus^n: " + tmsg, 2);
ProconChat(tmsg);
}
Speed balanceSpeed = Speed.Adaptive;
String tm = fTickets[1] + "/" + fTickets[2];
if (IsSQDM()) tm = tm + "/" + fTickets[3] + "/" + fTickets[4];
if (IsRush()) tm = tm + "(" + Math.Max(fTickets[1]/2, fMaxTickets - (fRushMaxTickets - fTickets[2])) + ")";
bool isCTF = IsCTF();
bool isCarrierAssault = IsCarrierAssault();
bool isObliteration = IsObliteration();
if (isCTF || isCarrierAssault || isObliteration) tm = GetTeamPoints(1) + "/" + GetTeamPoints(2);
double goal = 0;
bool countDown = true;
if (IsCountUp()) {
countDown = false;
if (fServerInfo.TeamScores != null && fServerInfo.TeamScores.Count > 1) {
foreach (TeamScore ts in fServerInfo.TeamScores) {
if (ts.TeamID == 1) {
goal = ts.WinningScore;
break;
}
}
}
}
if (goal == 0) {
if (fMaxTickets != -1) {
tm = tm + " <- [" + fMaxTickets.ToString("F0") + "]";
goal = fMaxTickets;
}
} else {
tm = tm + " -> [" + goal.ToString("F0") + "]";
}
String rt = GetTimeInRoundString();
PerModeSettings perMode = GetPerModeSettings();
String metroAdj = (perMode.EnableMetroAdjustments) ? ", Metro Adjustments Enabled" : String.Empty;
String unstackDisabled = (!EnableUnstacking) ? ", Unstacking Disabled" : String.Empty;
String logOnly = (EnableLoggingOnlyMode) ? ", Logging Only Mode Enabled" : String.Empty;
String weakOnly = (perMode.OnlyMoveWeakPlayers) ? ", Only Move Weak Players" : String.Empty;
String fastBalance = (EnableAdminKillForFastBalance) ? ", Admin Kill Enabled": String.Empty;
if (level >= 6) DebugWrite("^bStatus^n: Plugin state = " + fPluginState + ", game state = " + fGameState + fastBalance + weakOnly + metroAdj + unstackDisabled + logOnly, 0);
int useLevel = (isFinal) ? 2 : 4;
if (IsRush()) {
tmsg = "Map = " + this.FriendlyMap + ", mode = " + this.FriendlyMode + ", stage = " + fRushStage + ", time in round = " + rt + ", tickets = " + tm;
} else if (isCTF || isCarrierAssault || isObliteration) {
tmsg = "Map = " + this.FriendlyMap + ", mode = " + this.FriendlyMode + ", time in round = " + rt + ", score = " + tm;
} else {
tmsg = "Map = " + this.FriendlyMap + ", mode = " + this.FriendlyMode + ", time in round = " + rt + ", tickets = " + tm;
}
if (level >= useLevel)
DebugWrite("^bStatus^n: " + tmsg, 0);
if (isFinal)
ProconChat(tmsg);
int ticketGap = Math.Abs(fTickets[1] - fTickets[2]);
if (IsRush()) ticketGap = Convert.ToInt32(Math.Abs(fTickets[1] - Math.Max(fTickets[1]/2, fMaxTickets - (fRushMaxTickets - fTickets[2]))));
if (perMode.EnableTicketLossRatio && false) { // disable for this release
double a1 = GetAverageTicketLossRate(1, !EnableTicketLossRateLogging);
double a2 = GetAverageTicketLossRate(2, !EnableTicketLossRateLogging);
double rat = (a1 > a2) ? (a1/Math.Max(1, a2)) : (a2/Math.Max(1, a1));
rat = Math.Min(rat, 50.0); // cap at 50x
rat = rat * 100.0;
if (level >= useLevel) DebugWrite("^bStatus^n: Ticket difference = " + ticketGap + ", average ticket loss = " + a1.ToString("F2") + "(US) vs " + a2.ToString("F2") + " (RU)" + " for " + perMode.TicketLossSampleCount + " samples, ratio is " + rat.ToString("F0") + "%", 0);
} else if (!IsSQDM() && fServerInfo.GameMode != "GunMaster0") {
bool privIsRush = IsRush();
double a1 = fTickets[1];
double a2 = (privIsRush) ? (Math.Max(fTickets[1]/2, fMaxTickets - (fRushMaxTickets - fTickets[2]))) : fTickets[2];
double rat = (a1 > a2) ? (a1/Math.Max(1, a2)) : (a2/Math.Max(1, a1));
// For end of round, use standard function for ratio
if (fTickets[1] < 1 || fTickets[2] < 1) {
String cmsg = String.Empty;
a1 = fTickets[1];
a2 = fTickets[2];
rat = ComputeTicketRatio(a1, a2, goal, countDown, out cmsg);
DebugWrite("^9DEBUG: " + cmsg, 7);
}
rat = Math.Min(rat, 50.0); // cap at 50x
rat = rat * 100.0;
String extra = ", score " + GetTeamPoints(1) + "/" + GetTeamPoints(2);
if (perMode.EnableUnstackingByPlayerStats) {
a1 = GetAveragePlayerStats(1, perMode.DetermineStrongPlayersBy);
a2 = GetAveragePlayerStats(2, perMode.DetermineStrongPlayersBy);
double ratio = (a1 > a2) ? (a1/Math.Max(0.01, a2)) : (a2/Math.Max(0.01, a1));
ratio = Math.Min(ratio, 50.0); // cap at 50x
String cmp = (a1 > a2) ? (a1.ToString("F1") + "/" + a2.ToString("F1")) : (a2.ToString("F1") + "/" + a1.ToString("F1"));
extra = ", average " + perMode.DetermineStrongPlayersBy + " stats ratio = " + (ratio*100.0).ToString("F0") + "% (" + cmp + ")";
} else if ((privIsRush && perMode.EnableAdvancedRushUnstacking) || isCTF || isCarrierAssault || isObliteration) {
// Check team points as well as tickets
double usPoints = GetTeamPoints(1);
double ruPoints = GetTeamPoints(2);
if (usPoints <= 0) usPoints = 1;
if (ruPoints <= 0) ruPoints = 1;
double sratio = (usPoints > ruPoints) ? (usPoints/ruPoints) : (ruPoints/usPoints);
String cr = (usPoints > ruPoints) ? (usPoints.ToString("F0") + "/" + ruPoints.ToString("F0")) : (ruPoints.ToString("F0") + "/" + usPoints.ToString("F0")) ;
extra = ", score ratio = " + (sratio * 100).ToString("F0") + "% (" + cr + ")";
}
if (level >= useLevel) DebugWrite("^bStatus^n: Ticket difference = " + ticketGap + ", ticket ratio percentage is " + rat.ToString("F0") + "%" + extra, 0);
}
if (fPluginState == PluginState.Active) {
double secs = DateTime.Now.Subtract(fLastBalancedTimestamp).TotalSeconds;
if (!fBalanceIsActive || fLastBalancedTimestamp == DateTime.MinValue) secs = 0;
/*
PerModeSettings perMode = null;
String simpleMode = String.Empty;
if (fModeToSimple.TryGetValue(fServerInfo.GameMode, out simpleMode)
&& fPerMode.TryGetValue(simpleMode, out perMode) && perMode != null) {
*/
if (perMode != null) {
balanceSpeed = GetBalanceSpeed(perMode);
double unstackRatio = GetUnstackTicketRatio(perMode);
String activeTime = (secs > 0) ? "^1active (" + secs.ToString("F0") + " secs)^0" : "not active";
if (level >= 4) DebugWrite("^bStatus^n: Autobalance is " + activeTime + ", phase = " + GetPhase(perMode, false) + ", population = " + GetPopulation(perMode, false) + ", speed = " + balanceSpeed + ", unstack when ratio >= " + (unstackRatio * 100).ToString("F0") + "%", 0);
}
}
if (!IsModelInSync()) {
double toj = (fTimeOutOfJoint == 0) ? 0 : GetTimeInRoundMinutes() - fTimeOutOfJoint;
if (level >= 6) DebugWrite("^bStatus^n: Model not in sync for " + toj.ToString("F1") + " mins: fMoving = " + fMoving.Count + ", fReassigned = " + fReassigned.Count, 0);
}
String raged = fRageQuits.ToString() + "/" + fTotalQuits + " raged, ";
useLevel = (isFinal) ? 2 : 5;
if (level >= useLevel) DebugWrite("^bStatus^n: " + raged + fReassignedRound + " reassigned, " + fBalancedRound + " balanced, " + fUnstackedRound + " unstacked, " + fUnswitchedRound + " unswitched, " + fExcludedRound + " excluded, " + fExemptRound + " exempted, " + fFailedRound + " failed; of " + fTotalRound + " TOTAL", 0);
useLevel = (isFinal) ? 2 : 4;
String bf4Extras = (fGameVersion != GameVersion.BF3) ? ", " + fBF4CommanderCount + " commanders, " + fBF4SpectatorCount + " spectators" : String.Empty;
if (IsSQDM()) {
if (level >= useLevel) DebugWrite("^bStatus^n: Team counts [" + totalPlayers + "] = " + fTeam1.Count + "(A) vs " + fTeam2.Count + "(B) vs " + fTeam3.Count + "(C) vs " + fTeam4.Count + "(D), with " + fUnassigned.Count + " unassigned" + bf4Extras, 0);
} else {
if (level >= useLevel) DebugWrite("^bStatus^n: Team counts [" + totalPlayers + "] = " + fTeam1.Count + "(" + GetTeamName(1) + ") vs " + fTeam2.Count + "(" + GetTeamName(2) + "), with " + fUnassigned.Count + " unassigned" + bf4Extras, 0);
}
List<int> counts = new List<int>();
counts.Add(fTeam1.Count);
counts.Add(fTeam2.Count);
if (IsSQDM()) {
counts.Add(fTeam3.Count);
counts.Add(fTeam4.Count);
}
// Announce autobalancing status
counts.Sort();
int diff = Math.Abs(counts[0] - counts[counts.Count-1]);
String next = "^n";
String annType = null;
if (EnableAdminKillForFastBalance && diff > MaxFastDiff()) {
next = "^n^0 ... fast balance with admin kills in progress!";
annType = "USING ADMIN KILL";
} else if ((totalPlayers >= 6 && diff > MaxDiff() && fGameState == GameState.Playing && balanceSpeed != Speed.Stop && !fBalanceIsActive)) {
next = "^n^0 ... autobalance will activate as soon as possible!";
if (fUnassigned.Count >= (diff - MaxDiff())) {
annType = "WAITING FOR " + fUnassigned.Count + " PLAYERS TO JOIN";
} else {
annType = "MOVE ON DEATH";
}
}
// Team difference
if (level >= 4) {
String md = ((diff > MaxDiff()) ? "^8^b" : "^b") + diff + ((diff > MaxFastDiff() && EnableAdminKillForFastBalance) ? " (FAST)" : String.Empty);
DebugWrite("^bStatus^n: Team difference = " + md + next, 0);
}
// chats and yells
if (fLastAutoChatTimestamp == DateTime.MinValue || DateTime.Now.Subtract(fLastAutoChatTimestamp).TotalSeconds > (YellDurationSeconds + 2.0)) {
String cab = ChatAutobalancing;
String yab = YellAutobalancing;
if (!String.IsNullOrEmpty(cab) && cab.Contains("%technicalDetails%"))
cab = cab.Replace("%technicalDetails%", annType);
if (!String.IsNullOrEmpty(yab) && yab.Contains("%technicalDetails%"))
yab = yab.Replace("%technicalDetails%", annType);
if (annType != null && !String.IsNullOrEmpty(cab)) {
fLastAutoChatTimestamp = DateTime.Now;
Chat("all", cab);
}
if (annType != null && !String.IsNullOrEmpty(yab)) {
fLastAutoChatTimestamp = DateTime.Now;
Yell("all", yab);
}
}
} catch (Exception e) {
ConsoleException(e);
}
}