public override void OnServerInfo(CServerInfo serverInfo)
{
if (!fIsEnabled || serverInfo == null) return;
DebugWrite("^9^bGot OnServerInfo^n: Debug level = " + DebugLevel, 8);
DateTime debugTime = DateTime.Now;
try {
double elapsedTimeInSeconds = DateTime.Now.Subtract(fLastServerInfoTimestamp).TotalSeconds;
fLastServerInfoTimestamp = DateTime.Now;
if (fUpdateTicketsRequest != null) fUpdateTicketsRequest.LastUpdate = fLastServerInfoTimestamp;
// Update game state if just enabled (as of R38, CTF TeamScores may be null, does not mean round end)
if (fGameState == GameState.Unknown && serverInfo.GameMode != "CaptureTheFlag0") {
if (serverInfo.TeamScores == null || serverInfo.TeamScores.Count < 2) {
if (fGameVersion == GameVersion.BFH && Regex.Match(serverInfo.GameMode, @"(Heist|Hotwire|Bloodmoney)", RegexOptions.IgnoreCase).Success) {
// Special handling for BFH until bugs with TeamScores are fixed for these modes
DebugWrite("OnServerInfo: Ignoring null TeamScores for BFH mode: " + serverInfo.GameMode, 8);
} else {
fGameState = GameState.RoundEnding;
DebugWrite("OnServerInfo: ^b^3Game state = " + fGameState, 6);
}
}
}
// Show final status
if (fFinalStatus != null) {
try {
DebugWrite("^bFINAL STATUS FOR PREVIOUS ROUND:^n", 2);
foreach (TeamScore ts in fFinalStatus) {
if (ts.TeamID >= fTickets.Length) break;
fTickets[ts.TeamID] = (ts.Score == 1) ? 0 : ts.Score; // fix rounding
}
LogStatus(true, DebugLevel);
DebugWrite("+------------------------------------------------+", 2);
if (DebugLevel >= 3) CommandToLog("bad tags");
} catch (Exception) {}
fFinalStatus = null;
}
if (fServerInfo == null || fServerInfo.GameMode != serverInfo.GameMode || fServerInfo.Map != serverInfo.Map) {
ConsoleDebug("ServerInfo update: " + serverInfo.Map + "/" + serverInfo.GameMode);
}
// Check for server crash
if (fServerUptime > 0 && fServerUptime > serverInfo.ServerUptime + 2) { // +2 secs for rounding error in server!
fServerCrashed = true;
DebugWrite("^1^bDETECTED GAME SERVER CRASH^n (recorded uptime longer than latest serverInfo uptime)", 3);
}
fServerInfo = serverInfo;
fServerUptime = serverInfo.ServerUptime;
// Update max tickets
int totalPlayers = TotalPlayerCount();
PerModeSettings perMode = GetPerModeSettings();
bool isRush = IsRush();
double minTickets = Double.MaxValue;
double maxTickets = 0;
double attacker = 0;
double defender = 0;
double[] oldTickets = new double[]{0, fTickets[1], fTickets[2]};
if (fServerInfo.TeamScores == null || fServerInfo.TeamScores.Count < 2) return;
foreach (TeamScore ts in fServerInfo.TeamScores) {
if (ts.TeamID >= fTickets.Length) break;
fTickets[ts.TeamID] = ts.Score;
if (ts.Score > maxTickets) maxTickets = ts.Score;
if (ts.Score < minTickets) minTickets = ts.Score;
}
if (isRush) {
foreach (TeamScore ts in fServerInfo.TeamScores) {
if (ts.TeamID == 1) {
attacker = ts.Score;
} else if (ts.TeamID == 2) {
defender = ts.Score;
}
}
//attacker = fServerInfo.TeamScores[0].Score;
//defender = fServerInfo.TeamScores[1].Score;
if (fStageInProgress) {
if (attacker < fRushPrevAttackerTickets && attacker > 0) {
fRushAttackerStageLoss = fRushAttackerStageLoss + (fRushPrevAttackerTickets - attacker);
++fRushAttackerStageSamples;
}
}
String avl = String.Empty;
if (fStageInProgress) avl = ", avg loss = " + RushAttackerAvgLoss().ToString("F1") + "/" + Math.Min(perMode.SecondsToCheckForNewStage, elapsedTimeInSeconds).ToString("F0") + " secs";
if (totalPlayers > 3) DebugWrite("^7serverInfo: Rush attacker = " + attacker + ", was = " + fMaxTickets + avl + ", defender = " + defender, 7);
}
if (fMaxTickets == -1) {
if (!isRush) {
fMaxTickets = maxTickets;
ConsoleDebug("ServerInfo update: fMaxTickets = " + fMaxTickets.ToString("F0"));
} else {
fRushMaxTickets = defender;
fMaxTickets = attacker;
fRushStage = 1;
fRushPrevAttackerTickets = attacker;
fRushAttackerStageSamples = 0;
fRushAttackerStageLoss = 0;
fStageInProgress = false;
ConsoleDebug("ServerInfo update: fMaxTickets = " + fMaxTickets.ToString("F0") + ", fRushMaxTickets = " + fRushMaxTickets + ", fRushStage = " + fRushStage);
}
}
// Rush heuristic: if attacker tickets are higher than last check, new stage started
if (isRush && fServerInfo != null && !String.IsNullOrEmpty(fServerInfo.Map)) {
int maxStages = GetRushMaxStages(fServerInfo.Map);
if (fRushStage == 0) {
fRushMaxTickets = defender;
fMaxTickets = attacker;
fRushStage = 1;
fRushPrevAttackerTickets = attacker;
fRushAttackerStageSamples = 0;
fRushAttackerStageLoss = 0;
}
if (!fStageInProgress) {
// hysteresis, wait for attacker tickets to go below threshold before stage is in progress for sure
fStageInProgress = ((attacker + (2 * perMode.SecondsToCheckForNewStage / 5)) < fMaxTickets);
if (fStageInProgress) {
DebugWrite("^7serverInfo: stage " + fRushStage + " in progress!", 7);
}
} else if (attacker > fRushPrevAttackerTickets
&& (attacker - fRushPrevAttackerTickets) >= Math.Min(12, 2 * perMode.SecondsToCheckForNewStage / 5)
&& AttackerTicketsWithinRangeOfMax(attacker)
&& fRushStage < 5) {
fStageInProgress = false;
fRushMaxTickets = defender;
fMaxTickets = attacker;
fRushPrevAttackerTickets = attacker;
fRushStage = fRushStage + 1;
fRushAttackerStageSamples = 0;
fRushAttackerStageLoss = 0;
DebugWrite(".................................... ^b^1New rush stage detected^0^n ....................................", 3);
DebugBalance("Rush Stage " + fRushStage + " of " + maxStages);
}
// update last known attacker ticket value
fRushPrevAttackerTickets = attacker;
}
// Ticket loss rate updates
if ((EnableTicketLossRateLogging || perMode.EnableTicketLossRatio) && fGameState == GameState.Playing && totalPlayers >= 4) {
if (fUpdateTicketsRequest == null) SetupUpdateTicketsRequest();
AddTicketLossSample(1, oldTickets[1], fTickets[1], elapsedTimeInSeconds);
AddTicketLossSample(2, oldTickets[2], fTickets[2], elapsedTimeInSeconds);
} else {
ResetAverageTicketLoss();
}
if (EnableTicketLossRateLogging && IsConquest()) {
UpdateTicketLossRateLog(DateTime.Now, 0, 0);
}
if ((EnableTicketLossRateLogging || perMode.EnableTicketLossRatio) && fGameState == GameState.Playing && totalPlayers >= 4) {
try {
double a1 = GetAverageTicketLossRate(1, false);
double a2 = GetAverageTicketLossRate(2, false);
double ratio = (a1 > a2) ? (a1/Math.Max(1, a2)) : (a2/Math.Max(1, a1));
ratio = Math.Min(ratio, 50.0); // cap at 50x
ratio = ratio * 100.0;
fTicketLossHistogram.Add(Convert.ToInt32(Math.Round(ratio)));
} catch (Exception e) {
ConsoleException(e);
}
}
// Check for plugin updates periodically
if (fLastVersionCheckTimestamp != DateTime.MinValue
&& DateTime.Now.Subtract(fLastVersionCheckTimestamp).TotalMinutes > CHECK_FOR_UPDATES_MINS) {
LaunchCheckForPluginUpdate();
}
} catch (Exception e) {
ConsoleException(e);
} finally {
double elapsedTime = DateTime.Now.Subtract(debugTime).TotalMilliseconds;
if (DebugLevel >= 8 || (DebugLevel >= 7 && elapsedTime > 100.0)) {
DebugWrite("^8OnServerInfo took ^b" + elapsedTime.ToString("F0") + "^n ms", 1);
}
}
}