private void CommandToLog(string cmd)
{
try {
Match m = null;
String msg = String.Empty;
ConsoleDump("Command: " + cmd);
if (Regex.Match(cmd, @"^bad\s+tags?", RegexOptions.IgnoreCase).Success) {
List<String> failures = new List<String>();
lock (fKnownPlayers) {
foreach (String name in fKnownPlayers.Keys) {
PlayerModel p = fKnownPlayers[name];
if (p.Role != ROLE_PLAYER)
continue;
double joinedMinutesAgo = GetPlayerJoinedTimeSpan(p).TotalMinutes;
double enabledForMinutes = DateTime.Now.Subtract(fEnabledTimestamp).TotalMinutes;
if ((enabledForMinutes > MinutesAfterJoining)
&& (joinedMinutesAgo > MinutesAfterJoining)
&& (!p.TagVerified || p.TagFetchStatus.State == FetchState.Failed || p.TagFetchStatus.State == FetchState.Aborted)) {
failures.Add(name);
}
}
}
if (failures.Count == 0) {
ConsoleDump("^bNo clan tag fetch failures to report");
} else {
String tmp = String.Join(", ", failures.ToArray());
// Limit string to less than 1000
if (tmp.Length > 1000) {
tmp = tmp.Substring(0, 1000) + " ...";
}
tmp = tmp + " (" + failures.Count + " total)";
ConsoleDump("^bUnable to fetch clan tags for: " + tmp);
int aborted = 0;
int failed = 0;
foreach (String pn in failures) {
PlayerModel p = GetPlayer(pn);
if (p == null) continue;
if (p.TagFetchStatus.State == FetchState.Aborted) ++aborted;
if (p.TagFetchStatus.State == FetchState.Failed) ++failed;
}
ConsoleDump("^bClan tag fetches aborted: " + aborted);
ConsoleDump("^bClan tag fetches failed: " + failed);
}
return;
}
if (Regex.Match(cmd, @"^bad\s+stats?", RegexOptions.IgnoreCase).Success) {
List<String> failures = new List<String>();
lock (fKnownPlayers) {
foreach (String name in fKnownPlayers.Keys) {
PlayerModel p = fKnownPlayers[name];
if (p.Role != ROLE_PLAYER)
continue;
double joinedMinutesAgo = GetPlayerJoinedTimeSpan(p).TotalMinutes;
double enabledForMinutes = DateTime.Now.Subtract(fEnabledTimestamp).TotalMinutes;
if ((enabledForMinutes > MinutesAfterJoining)
&& (joinedMinutesAgo > MinutesAfterJoining)
&& !p.StatsVerified
&& (p.StatsFetchStatus.State == FetchState.Failed || p.StatsFetchStatus.State == FetchState.Requesting)) {
failures.Add(name);
}
}
}
if (failures.Count == 0) {
ConsoleDump("^bNo stats fetch failures to report");
} else {
String tmp = String.Join(", ", failures.ToArray());
// Limit string to less than 1000
if (tmp.Length > 1000) {
tmp = tmp.Substring(0, 1000) + " ...";
}
tmp = tmp + " (" + failures.Count + " total)";
ConsoleDump("^bUnable to fetch stats for: " + tmp);
int aborted = 0;
int failed = 0;
foreach (String pn in failures) {
PlayerModel p = GetPlayer(pn);
if (p == null) continue;
if (p.TagFetchStatus.State == FetchState.Aborted) ++aborted;
if (p.TagFetchStatus.State == FetchState.Failed) ++failed;
}
ConsoleDump("^bClan tag fetches aborted: " + aborted);
ConsoleDump("^bClan tag fetches failed: " + failed);
}
return;
}
if (Regex.Match(cmd, @"^delay", RegexOptions.IgnoreCase).Success) {
if (fTotalRoundEndingRounds < 1) {
ConsoleDump("Not enough rounds timed to make a recommendation yet");
return;
}
double total = (fTotalRoundEndingSeconds/fTotalRoundEndingRounds); // total amount of time between rounds
double backoff = (TotalPlayerCount() / 15) * 5; // scrambler needs about 5 seconds per 15 players
backoff = Math.Max(5, backoff);
double advice = total - backoff;
advice = Math.Max(((fGameVersion == GameVersion.BFH) ? 10 : 50), advice); // never less than 50 seconds (10 for BFH)
ConsoleDump("Recommended scrambler delay, based on " + fTotalRoundEndingRounds + " rounds, is " + advice.ToString("F0") + " seconds");
return;
}
m = Regex.Match(cmd, @"^gen\s+((?:cs|cl|ctf|gm|r|sqdm|sr|s|tdm|u|dom|ob|sob|def|crl|crs|bm|hs|hot|bh)|[1234569])", RegexOptions.IgnoreCase);
if (m.Success) {
String what = m.Groups[1].Value;
int section = 8;
if (!Int32.TryParse(what, out section)) section = 8;
List<CPluginVariable> vars = GetDisplayPluginVariables();
String sm = section.ToString() + " -";
if (section == 8) {
switch (what) {
case "cs":
if (fGameVersion != GameVersion.BF3)
sm = "for Conquest Small";
else
sm = "for Conq Small, Dom, Scav";
break;
case "cl": sm = "for Conquest Large"; break;
case "ctf": sm = "for CTF"; break;
case "gm": sm = "for Gun Master"; break;
case "r": sm = "for Rush"; break;
case "sqdm": sm = "for Squad Deathmatch"; break;
case "sr": sm = "for Squad Rush"; break;
case "s": sm = "for Superiority"; break;
case "tdm": sm = "for Team Deathmatch"; break;
case "u": sm = "for Unknown or New Mode"; break;
case "def": sm = "for Defuse"; break; //bf4
case "dom": sm = "for Domination"; break; // bf4
case "ob": sm = "for Obliteration"; break; // bf4
case "sob": sm = "for Squad Obliteration"; break; // bf4
case "crl": sm = "for NS Carrier Large"; break; // bf4
case "crs": sm = "for NS Carrier Small"; break; // bf4
case "bm": sm = "for Blood Money"; break; // bfh
case "hs": sm = "for Heist"; break; // bfh
case "hot": sm = "for Hotwire"; break; //bfh
case "bh": sm = "for Bounty Hunter"; break; //bfh
default: ConsoleDump("Unknown mode: " + what); return;
}
}
foreach (CPluginVariable var in vars) {
if (section == 8) {
if (var.Name.Contains(sm)) {
ConsoleDump(var.Name + ": " + var.Value);
}
} else {
if (var.Name.Contains(sm)) {
ConsoleDump(var.Name + ": " + var.Value);
}
}
}
return;
}
if (Regex.Match(cmd, @"^histogram", RegexOptions.IgnoreCase).Success) {
if (fTicketLossHistogram.Total < 1) return;
List<String> graph = fTicketLossHistogram.Log(60);
foreach (String line in graph) {
ConsoleDump(line);
}
return;
}
if (Regex.Match(cmd, @"^lists", RegexOptions.IgnoreCase).Success) {
ConsoleDump("Whitelist(" + fSettingWhitelist.Count + "):");
foreach (String item in fSettingWhitelist) {
ConsoleDump(item);
}
ConsoleDump(" ");
ConsoleDump("Friends List(" + fFriends.Keys.Count +"):");
foreach (int k in fFriends.Keys) {
ConsoleDump(k.ToString() + ": " + String.Join(", ", fFriends[k].ToArray()));
}
ConsoleDump(" ");
ConsoleDump("Disperse Evenly List(" + fSettingDisperseEvenlyList.Count + "):");
foreach (String item in fSettingDisperseEvenlyList) {
ConsoleDump(item);
}
ConsoleDump(" ");
for (int i = 1; i <= 4; ++i) { // 1 to 4 teams
if (fDispersalGroups[i].Count > 0) {
msg = "Dispersal Group " + i + " (" + fDispersalGroups[i].Count + "): " + String.Join(", ", fDispersalGroups[i].ToArray());
ConsoleDump(msg);
}
}
ConsoleDump(" ");
msg = "Group assignments: ";
for (int i = 1; i <= 4; ++i) { // 1 to 4 teams
msg = msg + fGroupAssignments[i];
if (i < 4) msg = msg + "/";
}
ConsoleDump(msg);
return;
}
if (Regex.Match(cmd, @"^modes", RegexOptions.IgnoreCase).Success) {
List<String> modeList = GetSimplifiedModes();
ConsoleDump("modes(" + modeList.Count + "):");
foreach (String mode in modeList) {
ConsoleDump(mode);
}
return;
}
if (Regex.Match(cmd, @"^moved", RegexOptions.IgnoreCase).Success) {
lock (fKnownPlayers) {
ConsoleDump("^bMoved by " + GetPluginName() + ":");
foreach (String name in fKnownPlayers.Keys) {
PlayerModel p = fKnownPlayers[name];
if (p.Role != ROLE_PLAYER)
continue;
if ((p.MovesByMBTotal + p.MovesByMBRound) < 1) continue;
String minsAgo = "(reset)";
String interval = "(never)";
if (p.MovedByMBTimestamp != DateTime.MinValue) {
minsAgo = DateTime.Now.Subtract(p.MovedByMBTimestamp).TotalMinutes.ToString("F0");
}
lock (p.MovedByMBHistory) {
if (p.MovedByMBHistory.Count > 0) {
if (p.MovedByMBHistory.Count == 1) {
interval = "(first)";
} else {
int last = p.MovedByMBHistory.Count - 1;
interval = p.MovedByMBHistory[last].Subtract(p.MovedByMBHistory[last-1]).TotalMinutes.ToString("F0") + " minutes apart";
}
}
}
ConsoleDump("^b" + p.FullName + "^n was moved " + p.MovesByMBRound + " times this round, " + (p.MovesByMBTotal + p.MovesByMBRound) + " total, the last was " + interval + " and " + minsAgo + " minutes ago");
}
ConsoleDump(" ");
ConsoleDump("^bMoved by someone or something else:");
foreach (String name in fKnownPlayers.Keys) {
PlayerModel p = fKnownPlayers[name];
if (p.Role != ROLE_PLAYER)
continue;
if (p.MovesRound > 0) {
ConsoleDump("^b" + p.FullName + "^n was moved " + p.MovesRound + " times this round, " + (p.MovesTotal + p.MovesRound) + " total");
}
}
ConsoleDump(" ");
}
return;
}
if (Regex.Match(cmd, @"^rage", RegexOptions.IgnoreCase).Success) {
ConsoleDump("Rage stats: " + fGrandRageQuits + " rage of " + fGrandTotalQuits + " total, this round " + fRageQuits + " rage of " + fTotalQuits + " total");
return;
}
if (Regex.Match(cmd, @"^refetch", RegexOptions.IgnoreCase).Success) {
List<String> fetch = new List<String>();
lock (fAllPlayers) {
foreach (String name in fAllPlayers) {
PlayerModel p = GetPlayer(name);
if (p == null) continue;
/*
if (!p.TagVerified) {
fetch.Add(name);
continue;
}
*/
if ((p.TagFetchStatus.State == FetchState.InQueue || p.TagFetchStatus.State == FetchState.Requesting)
&& (p.StatsFetchStatus.State == FetchState.InQueue || p.StatsFetchStatus.State == FetchState.Requesting)) continue;
fetch.Add(name);
}
}
if (fetch.Count == 0) {
ConsoleDump("No active players need info, nothing to refetch!");
return;
}
ConsoleDump("^bRefetching Battlelog info for " + fetch.Count + " players");
foreach (String name in fetch) {
PlayerModel p = GetPlayer(name);
p.TagFetchStatus.State = FetchState.New;
p.StatsFetchStatus.State = FetchState.New;
p.TagVerified = false;
AddPlayerFetch(name);
}
return;
}
if (Regex.Match(cmd, @"^refresh", RegexOptions.IgnoreCase).Success) {
fRefreshCommand = true;
ConsoleDump("Player models will be revalidated on next listPlayers event");
ScheduleListPlayers(1);
return;
}
if (Regex.Match(cmd, @"^reset settings", RegexOptions.IgnoreCase).Success) {
ConsoleDump("^8^bRESETTING ALL PLUGIN SETTINGS (except Whitelist and Dispersal list) TO DEFAULT!");
ResetSettings();
return;
}
if (Regex.Match(cmd, @"^scramble[d]?", RegexOptions.IgnoreCase).Success) {
if (fDebugScramblerBefore[0].Count == 0
|| fDebugScramblerBefore[1].Count == 0
|| fDebugScramblerAfter[0].Count == 0
|| fDebugScramblerAfter[1].Count == 0) {
ConsoleDump("No scrambler data available");
return;
}
ConsoleDump("===== BEFORE =====");
ListSideBySide(fDebugScramblerBefore[0], fDebugScramblerBefore[1], false, (KeepSquadsTogether || KeepClanTagsInSameTeam));
ConsoleDump("===== AFTER =====");
ListSideBySide(fDebugScramblerAfter[0], fDebugScramblerAfter[1], false, (KeepSquadsTogether || KeepClanTagsInSameTeam));
if (KeepSquadsTogether) {
ConsoleDump(" ");
// After scramble, compare squads: use both after teams to account for cross-team moves
CompareSquads(1, 1, fDebugScramblerBefore[0], fDebugScramblerAfter[0], 2, fDebugScramblerAfter[1], false);
CompareSquads(2, 2, fDebugScramblerBefore[1], fDebugScramblerAfter[1], 1, fDebugScramblerAfter[0], false);
}
if (fDebugScramblerStartRound[0].Count > 0 && fDebugScramblerStartRound[1].Count > 0) {
ConsoleDump("===== START OF ROUND =====");
ListSideBySide(fDebugScramblerStartRound[0], fDebugScramblerStartRound[1], false, (KeepSquadsTogether || KeepClanTagsInSameTeam));
if (KeepSquadsTogether) {
ConsoleDump(" ");
// After team swaps, compare squads
CompareSquads(2, 1, fDebugScramblerAfter[1], fDebugScramblerStartRound[0], 2, fDebugScramblerStartRound[1], true);
CompareSquads(1, 2, fDebugScramblerAfter[0], fDebugScramblerStartRound[1], 1, fDebugScramblerStartRound[0], true);
}
}
ConsoleDump("===== END =====");
return;
}
if (Regex.Match(cmd, @"^size[s]?", RegexOptions.IgnoreCase).Success) {
int kp = fKnownPlayers.Count;
int ap = fAllPlayers.Count;
int old = 0;
int validTags = 0;
int commanders = 0;
int spectators = 0;
lock (fKnownPlayers) {
// count player records more than 12 hours old
foreach (String name in fKnownPlayers.Keys) {
PlayerModel p = fKnownPlayers[name];
if (DateTime.Now.Subtract(p.LastSeenTimestamp).TotalMinutes > 12*60) {
if (!IsKnownPlayer(name)) {
++old;
}
}
if (p.TagVerified) ++validTags;
bool playing = false;
lock (fAllPlayers) {
playing = fAllPlayers.Contains(name);
}
if (playing) {
if (p.Role == ROLE_SPECTATOR)
++spectators;
else if (p.Role == ROLE_COMMANDER_MOBILE || p.Role == ROLE_COMMANDER_PC)
++commanders;
}
}
}
ConsoleDump("Plugin has been enabled for " + fRoundsEnabled + " rounds");
ConsoleDump("fKnownPlayers.Count = " + kp + ", not playing = " + (kp-ap) + ", more than 12 hours old = " + old + ", current commanders = " + commanders + ", current spectators = " + spectators);
ConsoleDump("fPriorityFetchQ.Count = " + PriorityQueueCount() + ", verified tags = " + validTags);
ConsoleDump("MULTIbalancerUtils.HTML_DOC.Length = " + MULTIbalancerUtils.HTML_DOC.Length);
return;
}
m = Regex.Match(cmd, @"^sort\s+([1-4])\s+(score|spm|kills|kdr|rank|kpm|bspm|bkdr|bkpm)", RegexOptions.IgnoreCase);
if (m.Success) {
String teamID = m.Groups[1].Value;
String propID = m.Groups[2].Value;
int team = 0;
if (!Int32.TryParse(teamID, out team) || team < 1 || team > 4) {
ConsoleDump("Invalid team: " + teamID);
return;
}
List<PlayerModel> fromList = GetTeam(team);
if (fromList == null || fromList.Count < 3) {
ConsoleDump("Invalid team or not enough players in team: " + team);
return;
}
switch (propID.ToLower()) {
case "score":
fromList.Sort(DescendingRoundScore);
break;
case "spm":
fromList.Sort(DescendingRoundSPM);
break;
case "kills":
fromList.Sort(DescendingRoundKills);
break;
case "kdr":
fromList.Sort(DescendingRoundKDR);
break;
case "rank":
fromList.Sort(DescendingPlayerRank);
break;
case "kpm":
fromList.Sort(DescendingRoundKPM);
break;
case "bspm":
fromList.Sort(DescendingSPM);
break;
case "bkdr":
fromList.Sort(DescendingKDR);
break;
case "bkpm":
fromList.Sort(DescendingKPM);
break;
default:
fromList.Sort(DescendingRoundScore);
break;
}
int n = 1;
foreach (PlayerModel p in fromList) {
switch (propID.ToLower()) {
case "score":
ConsoleDump("#" + String.Format("{0,2}", n) + ") Score: " + String.Format("{0,6:F0}", p.ScoreRound) + ", ^b" + p.FullName);
break;
case "spm":
ConsoleDump("#" + String.Format("{0,2}", n) + ") SPM: " + String.Format("{0,6:F0}", p.SPMRound) + ", ^b" + p.FullName);
break;
case "kills":
ConsoleDump("#" + String.Format("{0,2}", n) + ") Kills: " + String.Format("{0,6:F0}", p.KillsRound) + ", ^b" + p.FullName);
break;
case "kdr":
ConsoleDump("#" + String.Format("{0,2}", n) + ") KDR: " + String.Format("{0,6:F1}", p.KDRRound) + ", ^b" + p.FullName);
break;
case "rank":
ConsoleDump("#" + String.Format("{0,2}", n) + ") Rank: " + String.Format("{0,6:F0}", p.Rank) + ", ^b" + p.FullName);
break;
case "kpm":
ConsoleDump("#" + String.Format("{0,2}", n) + ") KPM: " + String.Format("{0,6:F1}", p.KPMRound) + ", ^b" + p.FullName);
break;
case "bspm":
ConsoleDump("#" + String.Format("{0,2}", n) + ") bSPM: " + String.Format("{0,6:F0}", ((p.StatsVerified) ? p.SPM : p.SPMRound)) + ", ^b" + p.FullName);
break;
case "bkdr":
ConsoleDump("#" + String.Format("{0,2}", n) + ") bKDR: " + String.Format("{0,6:F1}", ((p.StatsVerified) ? p.KDR : p.KDRRound)) + ", ^b" + p.FullName);
break;
case "bkpm":
ConsoleDump("#" + String.Format("{0,2}", n) + ") bKPM: " + String.Format("{0,6:F0}", ((p.StatsVerified) ? p.KPM : p.KPMRound)) + ", ^b" + p.FullName);
break;
default:
ConsoleDump("#" + String.Format("{0,2}", n) + ") Score: " + String.Format("{0,6:F0}", p.ScoreRound) + ", ^b" + p.FullName);
break;
}
n = n + 1;
}
return;
}
if (Regex.Match(cmd, @"^status", RegexOptions.IgnoreCase).Success) {
LogStatus(false, 7);
return;
}
if (Regex.Match(cmd, @"^subscribed", RegexOptions.IgnoreCase).Success) {
lock (fAllPlayers) {
foreach (String name in fAllPlayers) {
PlayerModel p = GetPlayer(name);
if (p != null && p.Subscribed) {
ConsoleDump("^b" + p.FullName + "^n is subscribed to all balancer messages in chat");
}
}
}
return;
}
if (Regex.Match(cmd, @"^tags?", RegexOptions.IgnoreCase).Success) {
Dictionary<String,List<PlayerModel>> byTag = new Dictionary<String,List<PlayerModel>>();
lock (fAllPlayers) {
foreach (String name in fAllPlayers) {
PlayerModel player = GetPlayer(name);
if (player == null || player.Team < 1 || player.Team > 2) continue;
String tag = ExtractTag(player);
if (String.IsNullOrEmpty(tag)) continue;
if (!byTag.ContainsKey(tag)) {
byTag[tag] = new List<PlayerModel>();
}
byTag[tag].Add(player);
}
}
List<String> tags = new List<String>();
foreach (String t in byTag.Keys) {
tags.Add(t);
byTag[t].Sort(delegate(PlayerModel lhs, PlayerModel rhs) { // ascending by team/squad
if (lhs == null && rhs == null) return 0;
if (lhs == null) return -1;
if (rhs == null) return 1;
// by team, then by squad
if (lhs.Team < rhs.Team) return -1;
if (lhs.Team > rhs.Team) return 1;
if (lhs.Team == rhs.Team) {
if (lhs.Squad < 1 || rhs.Squad < 1) return 0;
if (lhs.Squad < rhs.Squad) return -1;
if (lhs.Squad > rhs.Squad) return 1;
}
return 0;
});
}
tags.Sort();
foreach (String t in tags) {
ConsoleDump("Tag [" + t + "]:");
List<PlayerModel> clan = byTag[t];
foreach (PlayerModel p in clan) {
ConsoleDump(String.Format(" {0}, {1}, {2}",
p.Name,
GetTeamName(p.Team),
GetSquadName(p.Squad)
));
}
}
ConsoleDump(" === END OF TAGS === ");
return;
}
if (Regex.Match(cmd, @"^whitelist", RegexOptions.IgnoreCase).Success) {
ConsoleDump("Whitelist:");
String bCodes = String.Empty;
String uCodes = String.Empty;
String sCodes = String.Empty;
String dCodes = String.Empty;
String rCodes = String.Empty;
String all = String.Empty;
List<String> plist = null;
lock (fAllPlayers) {
plist = new List<String>(fAllPlayers);
}
foreach (String item in fSettingWhitelist) {
List<String> tokens = new List<String>(Regex.Split(item, @"\s+"));
if (tokens.Count < 1) {
ConsoleError("tokens.Count < 1!");
continue;
}
String line = String.Empty;
for (int i = 0; i < tokens.Count ; ++i) {
line = line + tokens[i] + " ";
}
ConsoleDump("WL: " + line);
}
foreach (String name in plist) {
try {
PlayerModel player = GetPlayer(name);
if (player == null) continue;
if (CheckWhitelist(player, WL_ALL)) {
if (String.IsNullOrEmpty(all)) {
all = " All: " + player.Name;
} else {
all = all + ", " + player.Name;
}
continue;
}
if (CheckWhitelist(player, WL_BALANCE)) {
if (String.IsNullOrEmpty(bCodes)) {
bCodes = " Balance only: " + player.Name;
} else {
bCodes = bCodes + ", " + player.Name;
}
}
if (CheckWhitelist(player, WL_UNSTACK)) {
if (String.IsNullOrEmpty(uCodes)) {
uCodes = " Unstack only: " + player.Name;
} else {
uCodes = uCodes + ", " + player.Name;
}
}
if (CheckWhitelist(player, WL_SWITCH)) {
if (String.IsNullOrEmpty(sCodes)) {
sCodes = " Switch only: " + player.Name;
} else {
sCodes = sCodes + ", " + player.Name;
}
}
if (CheckWhitelist(player, WL_DISPERSE)) {
if (String.IsNullOrEmpty(dCodes)) {
dCodes = " Disperse only: " + player.Name;
} else {
dCodes = dCodes + ", " + player.Name;
}
}
if (CheckWhitelist(player, WL_RANK)) {
if (String.IsNullOrEmpty(rCodes)) {
rCodes = " Rank only: " + player.Name;
} else {
rCodes = rCodes + ", " + player.Name;
}
}
} catch (Exception e) {
ConsoleException(e);
}
}
if (!String.IsNullOrEmpty(all)) ConsoleDump(all);
if (!String.IsNullOrEmpty(bCodes)) ConsoleDump(bCodes);
if (!String.IsNullOrEmpty(uCodes)) ConsoleDump(uCodes);
if (!String.IsNullOrEmpty(sCodes)) ConsoleDump(sCodes);
if (!String.IsNullOrEmpty(dCodes)) ConsoleDump(dCodes);
if (!String.IsNullOrEmpty(rCodes)) ConsoleDump(rCodes);
return;
}
// test BF3 fetch
Match testF3 = Regex.Match(cmd, @"^test f3 ([^\s]+)", RegexOptions.IgnoreCase);
if (testF3.Success) {
int oldLevel = DebugLevel;
DebugLevel = 7;
try {
ConsoleDump("Testing BF3 Clantag fetch:");
String tn = testF3.Groups[1].Value;
PlayerModel dummy = GetPlayer(tn);
if (dummy == null) {
ConsoleDump("Player ^b" + tn + "^n seems to have left the server");
dummy = new PlayerModel(tn, 1);
} else {
ConsoleDump("Player ^b" + tn + "^n, TagVerified: " + dummy.TagVerified + ", TagFetchStatus: " + dummy.TagFetchStatus.State + ", PersonaId: " + dummy.PersonaId);
}
SendBattlelogRequest(dummy.Name, "clanTag", dummy);
ConsoleDump("Status = " + dummy.TagFetchStatus.State);
dummy.TagVerified = (dummy.TagFetchStatus.State != FetchState.Failed);
} catch (Exception e) {
ConsoleException(e);
}
DebugLevel = oldLevel;
return;
}
// test BF4 fetch
Match testF4 = Regex.Match(cmd, @"^test f4 ([^\s]+)", RegexOptions.IgnoreCase);
if (testF4.Success) {
int oldLevel = DebugLevel;
DebugLevel = 7;
try {
ConsoleDump("Testing BF4 Clantag fetch:");
String tn = testF4.Groups[1].Value;
PlayerModel dummy = GetPlayer(tn);
if (dummy == null) {
ConsoleDump("Player ^b" + tn + "^n seems to have left the server");
dummy = new PlayerModel(tn, 1);
} else {
ConsoleDump("Player ^b" + tn + "^n, TagVerified: " + dummy.TagVerified + ", TagFetchStatus: " + dummy.TagFetchStatus.State + ", PersonaId: " + dummy.PersonaId);
}
SendBattlelogRequestBF4(dummy.Name, "clanTag", dummy);
ConsoleDump("Status = " + dummy.TagFetchStatus.State);
dummy.TagVerified = (dummy.TagFetchStatus.State != FetchState.Failed);
} catch (Exception e) {
ConsoleException(e);
}
DebugLevel = oldLevel;
return;
}
// test BFH fetch
Match testFH = Regex.Match(cmd, @"^test fh ([^\s]+)", RegexOptions.IgnoreCase);
if (testFH.Success) {
int oldLevel = DebugLevel;
DebugLevel = 7;
try {
ConsoleDump("Testing BFH Clantag fetch:");
String tn = testFH.Groups[1].Value;
PlayerModel dummy = GetPlayer(tn);
if (dummy == null) {
ConsoleDump("Player ^b" + tn + "^n seems to have left the server");
dummy = new PlayerModel(tn, 1);
} else {
ConsoleDump("Player ^b" + tn + "^n, TagVerified: " + dummy.TagVerified + ", TagFetchStatus: " + dummy.TagFetchStatus.State + ", PersonaId: " + dummy.PersonaId);
}
SendBattlelogRequestBFH(dummy.Name, "clanTag", dummy);
ConsoleDump("Status = " + dummy.TagFetchStatus.State);
dummy.TagVerified = (dummy.TagFetchStatus.State != FetchState.Failed);
} catch (Exception e) {
ConsoleException(e);
}
DebugLevel = oldLevel;
return;
}
// Undocumented command: risky (hide|show)
Match risky = Regex.Match(cmd, @"^risky (hide|show)", RegexOptions.IgnoreCase);
if (risky.Success) {
if (risky.Groups[1].Value == "show") {
fShowRiskySettings = true;
} else {
fShowRiskySettings = false;
}
if (fShowRiskySettings) {
ConsoleDump("Showing risky settings!");
} else {
ConsoleDump("Hiding risky settings!");
}
return;
}
// Undocumented command: test scrambler
if (Regex.Match(cmd, @"^test scrambler", RegexOptions.IgnoreCase).Success) {
ConsoleDump("Testing scrambler:");
ScrambleByCommand(1, true); // log only, winner is always team 1
return;
}
// Undocumented command: test @mb ...
if (Regex.Match(cmd, @"^test @mb", RegexOptions.IgnoreCase).Success) {
ConsoleDump("Testing chat command:");
String tmp = cmd.Replace("test ", String.Empty);
try {
fTestMBCommand = true;
OnGlobalChat("[Plugin]", tmp);
} catch (Exception) {
// Do nothing
} finally {
fTestMBCommand = false;
}
return;
}
// Undocumented command: test fast balance
if (Regex.Match(cmd, @"^test fast", RegexOptions.IgnoreCase).Success) {
if (!EnableAdminKillForFastBalance) {
ConsoleDump("Enable Admin Kill For Fast Balance must be True to test, skipping");
return;
}
ConsoleDump("Testing fast balance:");
if (fTestFastBalance) {
fTestFastBalance = false;
ConsoleDump("Deactivated fast balance test");
} else {
fTestFastBalance = true;
FastBalance("Test: ");
}
return;
}
// Undocumented command: test clan dispersal
if (Regex.Match(cmd, @"^test clan", RegexOptions.IgnoreCase).Success) {
PerModeSettings perMode = GetPerModeSettings();
if (perMode.DisperseEvenlyByClanPlayers == 0) {
ConsoleDump("per-mode Disperse Evenly By Clan Players must be more than 0 to test, skipping");
return;
}
ConsoleDump("Testing clan dispersal:");
if (fTestClanDispersal) {
fTestClanDispersal = false;
ConsoleDump("Deactivated clan dispersal testing");
} else {
fTestClanDispersal = true;
ConsoleDump("Activated clan dispersal testing");
}
return;
}
// Undocumented command: generate VBCode from HTML
if (Regex.Match(cmd, @"^vbcode", RegexOptions.IgnoreCase).Success) {
String vbCode = MULTIbalancerUtils.ConvertHTMLToVBCode(MULTIbalancerUtils.HTML_DOC);
ConsoleDump("Converted " + MULTIbalancerUtils.HTML_DOC.Length + " chars of HTML to " + vbCode.Length + " chars of VBCode!");
try {
String path = Path.Combine(Directory.GetParent(Application.ExecutablePath).FullName, "vbcode.txt");
using (FileStream fs = File.Open(path, FileMode.Create)) {
Byte[] buffer = new UTF8Encoding(true).GetBytes(vbCode);
fs.Write(buffer, 0, buffer.Length);
ConsoleDump("Successfully wrote " + path);
}
} catch (Exception e) {
ConsoleException(e);
}
return;
}
if (Regex.Match(cmd, @"^\s*help", RegexOptions.IgnoreCase).Success || !String.IsNullOrEmpty(cmd)) {
ConsoleDump("^1^bbad tags^n^0: Examine list of players whose clan tag fetch failed");
ConsoleDump("^1^bbad stats^n^0: Examine list of players whose stats fetch failed");
ConsoleDump("^1^bdelay^n^0: Examine recommended scrambler delay time");
ConsoleDump("^1^bgen^n ^imode^n^0: Generate settings listing for ^imode^n (one of: cs, cl, ctf, gm, r, sqdm, sr, s, tdm, dom, ob, def, crl, crs, bm, hs, hot, bh, u)");
ConsoleDump("^1^bgen^n ^isection^n^0: Generate settings listing for ^isection^n (1-6,9)");
ConsoleDump("^1^bhistogram^n^0: Examine a histogram graph of ticket loss ratios");
ConsoleDump("^1^blists^n^0: Examine all settings that are lists");
ConsoleDump("^1^bmodes^n^0: Examine the known game modes");
ConsoleDump("^1^bmoved^n^0: Examine which players were moved, how many times total and how long ago");
ConsoleDump("^1^brage^n^0: Examine rage quit statistics");
ConsoleDump("^1^brefetch^n^0: Refetch Battlelog info for all active players");
ConsoleDump("^1^brefresh^n^0: Force refresh of player list");
ConsoleDump("^1^breset settings^n^0: Reset all plugin settings to default, except for ^bWhitelist^n and ^bDisperse Evenly List^n");
ConsoleDump("^1^bscrambled^n^0: Examine list of players before and after last successful scramble");
ConsoleDump("^1^bsizes^n^0: Examine the sizes of various data structures");
ConsoleDump("^1^bsort^n ^iteam^n ^itype^n^0: Examine sorted ^iteam^n (1-4) by ^itype^n (one of: score, spm, kills, kdr, rank, kpm, bspm, bkdr, bkpm)");
ConsoleDump("^1^bstatus^n^0: Examine full status log, as if Debug Level were 7");
ConsoleDump("^1^bsubscribed^n^0: Examine all players who are subscribed to balancer chat messages");
ConsoleDump("^1^btags^n^0: Examine list of players sorted by clan tags");
ConsoleDump("^1^btest f3^n ^iname^n^0: Test BF3 tag fetch");
ConsoleDump("^1^btest f4^n ^iname^n^0: Test BF4 tag fetch");
ConsoleDump("^1^bwhitelist^n^0: Examine whitelist combined with reserved slots, by option codes");
return;
}
} catch (Exception e) {
ConsoleException(e);
}
}