Hadlock

InsaneBalancer 0.0.0.6-patch-5

Dec 30th, 2011
1,825
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 264.59 KB | None | 0 0
  1.  
  2. /*
  3.  * Copyright 2011 Miguel Mendoza - miguel@micovery.com
  4.  *
  5.  * Insane Balancer is free software: you can redistribute it and/or modify it under the terms of the
  6.  * GNU General Public License as published by the Free Software Foundation, either version 3 of the License,
  7.  * or (at your option) any later version. Insane Balancer is distributed in the hope that it will be useful,
  8.  * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9.  * See the GNU General Public License for more details. You should have received a copy of the
  10.  * GNU General Public License along with Insane Balancer. If not, see http://www.gnu.org/licenses/.
  11.  *
  12.  */
  13.  
  14.  
  15. using System;
  16. using System.IO;
  17. using System.Text;
  18. using System.Reflection;
  19. using System.Collections.Generic;
  20. using System.Collections;
  21. using System.Net;
  22. using System.Threading;
  23. using System.Diagnostics;
  24.  
  25.  
  26. using System.Web;
  27. using System.Data;
  28. using System.Text.RegularExpressions;
  29.  
  30.  
  31. using PRoCon.Core;
  32. using PRoCon.Core.Plugin;
  33. using PRoCon.Core.Plugin.Commands;
  34. using PRoCon.Core.Players;
  35. using PRoCon.Core.Players.Items;
  36. using PRoCon.Core.Battlemap;
  37. using PRoCon.Core.Maps;
  38.  
  39.  
  40.  
  41.  
  42.  
  43. namespace PRoConEvents
  44. {
  45.     public class InsaneBalancer : PRoConPluginAPI, IPRoConPluginInterface
  46.     {
  47.  
  48.         int check_state_phase = 0;
  49.         int login_state = 0;
  50.         bool delayed_connect = false;
  51.         int win_teamId, lose_teamId;
  52.         public bool round_balancer = false;
  53.         public bool live_balancer = false;
  54.         public bool level_started = false;
  55.         public bool wait_state = false;
  56.         bool sleep = false;
  57.         bool virtual_mode = false;
  58.         int max_player_count = 0;
  59.         public BattleLog blog = null;
  60.         public int attempts = 0;
  61.         int min_tickets = 0;
  62.  
  63.  
  64.  
  65.  
  66.  
  67.         public class BattleLog
  68.         {
  69.  
  70.             private String gate = "https://battlelog.battlefield.com/bf3/gate";
  71.             private HttpWebRequest req = null;
  72.             private CookieContainer cookies = null;
  73.             private InsaneBalancer plugin = null;
  74.             WebClient client = null;
  75.  
  76.  
  77.             public BattleLog(InsaneBalancer plugin)
  78.             {
  79.                 this.plugin = plugin;
  80.             }
  81.  
  82.            
  83.  
  84.  
  85.             private String fetchWebPage(ref String html_data, String url)
  86.             {
  87.                 try
  88.                 {
  89.                     if (client == null)
  90.                     client = new WebClient();
  91.  
  92.                     html_data = client.DownloadString(url);
  93.                     return html_data;
  94.  
  95.  
  96.                 }
  97.                 catch (WebException e)
  98.                 {
  99.                     if (e.Status.Equals(WebExceptionStatus.Timeout))
  100.                         throw new StatsException("^1^bERROR^n^0: HTTP request timed-out");
  101.                     else
  102.                         throw;
  103.                        
  104.                 }
  105.  
  106.                 return html_data;
  107.  
  108.  
  109.             }
  110.  
  111.             public class StatsException : Exception
  112.             {
  113.                 public StatsException(String message)
  114.                     : base(message)
  115.                 {
  116.                 }
  117.             }
  118.  
  119.             public void extractClanTag(ref String result, PlayerStats stats, String name)
  120.             {
  121.                 /* Extract the player tag */
  122.                 Match tag = Regex.Match(result, @"\[\s*([a-zA-Z0-9]+)\s*\]\s*" + name, RegexOptions.IgnoreCase | RegexOptions.Singleline);
  123.                 if (tag.Success)
  124.                     stats.tag = tag.Groups[1].Value;
  125.             }
  126.  
  127.             public PlayerStats getPlayerStats(String player)
  128.             {
  129.                 try
  130.                 {
  131.                     String result = "";
  132.  
  133.                     /* First fetch the player's main page to get the persona id */
  134.                     fetchWebPage(ref result, "http://battlelog.battlefield.com/bf3/user/" + player);
  135.                  
  136.                     /* Extract the persona id */
  137.                     Match pid = Regex.Match(result, @"bf3/soldier/" + player + @"/stats/(\d+)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
  138.  
  139.                     if (!pid.Success)
  140.                         throw new StatsException("^1^bERROR^n^0: could not find persona-id for ^b" + player);
  141.  
  142.                     String personaId = pid.Groups[1].Value.Trim();
  143.  
  144.                     // extract the player clan-tag
  145.                     PlayerStats ps = new PlayerStats();
  146.                     extractClanTag(ref result, ps, player);
  147.  
  148.                     // follow link to the player's detailed stats
  149.                     fetchWebPage(ref result, "http://battlelog.battlefield.com/bf3/overviewPopulateStats/" + personaId + "/bf3-us-engineer/1/");
  150.  
  151.                     Hashtable json = (Hashtable)JSON.JsonDecode(result);
  152.  
  153.                     if (json == null)
  154.                         throw new StatsException("^1^bERROR^0^n: could not parse JSON profile data for ^b"+player+"^n");
  155.  
  156.                     // check we got a valid response
  157.  
  158.                     if (!(json.ContainsKey("type") && json.ContainsKey("message")))
  159.                         throw new StatsException("^1^bERROR^0^n: JSON response does not contain \"type\" or \"message\" fields");
  160.  
  161.                     String type = (String)json["type"];
  162.                     String message = (String)json["message"];
  163.  
  164.  
  165.                     if (!(type.StartsWith("success") && message.StartsWith("OK")))
  166.                         throw new StatsException("^1^bERROR^0^n: JSON response was ^btype^n=^b" + type + "^b, ^bmessage^n=^b" + message);
  167.  
  168.  
  169.                     Hashtable data = null;
  170.                     if (!json.ContainsKey("data") || (data = (Hashtable)json["data"]) == null)
  171.                         throw new StatsException("^1^bERROR^0^n: JSON response was does not contain a ^bdata^n field");
  172.  
  173.                     Hashtable stats = null;
  174.                     if (!data.ContainsKey("overviewStats") || (stats = (Hashtable)data["overviewStats"]) == null)
  175.                         throw new StatsException("^1^bERROR^0^n: JSON response ^bdata^n does not contain ^boverviewStats^n");
  176.  
  177.  
  178.                     // get the data fields
  179.                     if (stats.ContainsKey("kills"))
  180.                         Double.TryParse(stats["kills"].ToString(), out ps.kills);
  181.  
  182.                     if (stats.ContainsKey("elo"))
  183.                         Double.TryParse(stats["elo"].ToString(), out ps.skill);
  184.  
  185.                     if (stats.ContainsKey("deaths"))
  186.                         Double.TryParse(stats["deaths"].ToString(), out ps.deaths);
  187.  
  188.                     if (stats.ContainsKey("kdRatio"))
  189.                         Double.TryParse(stats["kdRatio"].ToString(), out ps.kdr);
  190.  
  191.                     if (stats.ContainsKey("rank"))
  192.                         Double.TryParse(stats["rank"].ToString(), out ps.rank);
  193.  
  194.                     if (stats.ContainsKey("scorePerMinute"))
  195.                         Double.TryParse(stats["scorePerMinute"].ToString(), out ps.spm);
  196.  
  197.                     if (stats.ContainsKey("quitPercentage"))
  198.                         Double.TryParse(stats["quitPercentage"].ToString(), out ps.quits);
  199.  
  200.                     if (stats.ContainsKey("totalScore"))
  201.                         Double.TryParse(stats["totalScore"].ToString(), out ps.score);
  202.  
  203.                     if (stats.ContainsKey("accuracy"))
  204.                         Double.TryParse(stats["accuracy"].ToString(), out ps.accuracy);
  205.  
  206.                     if (stats.ContainsKey("timePlayed"))
  207.                         Double.TryParse(stats["timePlayed"].ToString(), out ps.secs);
  208.  
  209.                     return ps;
  210.  
  211.  
  212.                 }
  213.                 catch (StatsException e)
  214.                 {
  215.                     plugin.ConsoleWrite(e.Message);
  216.                 }
  217.                 catch (Exception e)
  218.                 {
  219.                     plugin.dump_exception(e);
  220.                 }
  221.  
  222.                 return new PlayerStats();
  223.  
  224.             }
  225.         }
  226.  
  227.  
  228.         private class PlayerSquad
  229.         {
  230.             public List<PlayerProfile> members;
  231.             int squadId = 0;
  232.             int teamId = 0;
  233.             int random_value = -1;
  234.  
  235.             public PlayerSquad(int tid, int sid)
  236.             {
  237.                 members = new List<PlayerProfile>();
  238.                 teamId = tid;
  239.                 squadId = sid;
  240.             }
  241.  
  242.             public PlayerSquad(PlayerSquad squad)
  243.             {
  244.                 squadId = squad.squadId;
  245.                 teamId = squad.squadId;
  246.                 members = squad.members;
  247.             }
  248.  
  249.             private bool playerBelongs(PlayerProfile player)
  250.             {
  251.                 return getTeamId() == player.getTeamId() && getSquadId() == player.getSquadId();
  252.             }
  253.  
  254.             public bool hasPlayer(PlayerProfile player)
  255.             {
  256.                 return members.Contains(player);
  257.             }
  258.  
  259.             public int getFreeSlots()
  260.             {
  261.                 if (getSquadId() == 0)
  262.                     return 32;
  263.  
  264.                 return 4 - getCount();
  265.             }
  266.  
  267.             public bool addPlayer(PlayerProfile player)
  268.             {
  269.                 if (!playerBelongs(player) || hasPlayer(player))
  270.                     return false;
  271.  
  272.                 members.Add(player);
  273.                 return true;
  274.             }
  275.  
  276.             public virtual int getTeamId()
  277.             {
  278.                 return teamId;
  279.             }
  280.  
  281.             public virtual int getSquadId()
  282.             {
  283.                 return squadId;
  284.             }
  285.  
  286.  
  287.             public bool dropPlayer(PlayerProfile player)
  288.             {
  289.                 if (!members.Contains(player))
  290.                     return false;
  291.  
  292.                 members.Remove(player);
  293.                 return true;
  294.             }
  295.  
  296.  
  297.  
  298.             public virtual PlayerProfile getRandomPlayer()
  299.             {
  300.                 int pcount = getCount();
  301.                 if (pcount == 0)
  302.                     return null;
  303.  
  304.                 return getMembers()[(new Random()).Next(pcount)];
  305.             }
  306.  
  307.             public virtual int getRandomValue()
  308.             {
  309.                 if (random_value == -1)
  310.                     random_value = (new Random()).Next(0, int.MaxValue);
  311.                 return random_value;
  312.             }
  313.  
  314.             public PlayerProfile removeRandomPlayer()
  315.             {
  316.                 PlayerProfile player = getRandomPlayer();
  317.                 if (player == null)
  318.                     return null;
  319.  
  320.                 dropPlayer(player);
  321.                 return player;
  322.             }
  323.  
  324.             public void sortMembers(player_sort_method sort_method)
  325.             {
  326.                 this.members.Sort(new Comparison<PlayerProfile>(sort_method));
  327.  
  328.             }
  329.  
  330.             public int getCount()
  331.             {
  332.                 return members.Count;
  333.             }
  334.  
  335.             public List<PlayerProfile> getMembers()
  336.             {
  337.                 return members;
  338.             }
  339.  
  340.             /* Round Level statistics */
  341.  
  342.             public double getRoundSpm()
  343.             {
  344.                 int count = getCount();
  345.                 if (count == 0)
  346.                     return 0;
  347.  
  348.                 double spm = 0;
  349.                 foreach (PlayerProfile player in getMembers())
  350.                 {
  351.                     spm += player.getRoundSpm();
  352.                 }
  353.  
  354.                 return (spm / count);
  355.             }
  356.  
  357.             public double getRoundKpm()
  358.             {
  359.                 int count = getCount();
  360.                 if (count == 0)
  361.                     return 0;
  362.  
  363.                 double kpm = 0;
  364.                 foreach (PlayerProfile player in getMembers())
  365.                 {
  366.                     kpm += player.getRoundKpm();
  367.                 }
  368.  
  369.                 return (kpm / count);
  370.             }
  371.  
  372.  
  373.             public double getRoundKills()
  374.             {
  375.                 int count = getCount();
  376.                 if (count == 0)
  377.                     return 0;
  378.  
  379.                 double kills = 0;
  380.                 foreach (PlayerProfile player in getMembers())
  381.                 {
  382.                     kills += player.getRoundKills();
  383.                 }
  384.  
  385.                 return (kills / count);
  386.             }
  387.  
  388.             public double getRoundDeaths()
  389.             {
  390.                 int count = getCount();
  391.                 if (count == 0)
  392.                     return 0;
  393.  
  394.                 double deaths = 0;
  395.                 foreach (PlayerProfile player in getMembers())
  396.                 {
  397.                     deaths += player.getRoundDeaths();
  398.                 }
  399.  
  400.                 return (deaths / count);
  401.             }
  402.  
  403.             public double getRoundScore()
  404.             {
  405.                 int count = getCount();
  406.                 if (count == 0)
  407.                     return 0;
  408.  
  409.                 double score = 0;
  410.                 foreach (PlayerProfile player in getMembers())
  411.                 {
  412.                     score += player.getRoundScore();
  413.                 }
  414.  
  415.                 return (score / count);
  416.             }
  417.  
  418.  
  419.             public double getRoundKdr()
  420.             {
  421.                 return (getRoundKills() + 1) / (getRoundDeaths() + 1);
  422.             }
  423.  
  424.             public DateTime getRoundTime()
  425.             {
  426.                 int count = getCount();
  427.                 if (count == 0)
  428.                     return DateTime.Now;
  429.  
  430.                 long total_ticks = 0;
  431.                 foreach (PlayerProfile player in getMembers())
  432.                 {
  433.                     total_ticks += player.getRoundTime().Ticks;
  434.                 }
  435.  
  436.                 long avg = total_ticks / count;
  437.                 return new DateTime(avg);
  438.             }
  439.  
  440.             /* Online statistics */
  441.  
  442.  
  443.             public double getOnlineSkill()
  444.             {
  445.                 double count = getCount();
  446.                 if (count == 0)
  447.                     return 0;
  448.  
  449.                 double skill = 0;
  450.                 foreach (PlayerProfile player in getMembers())
  451.                     skill += player.getOnlineSkill();
  452.  
  453.                 return (skill / count);
  454.             }
  455.  
  456.             public double getOnlineSpm()
  457.             {
  458.                 double count = getCount();
  459.                 if (count == 0)
  460.                     return 0;
  461.  
  462.                 double spm = 0;
  463.                 foreach (PlayerProfile player in getMembers())
  464.                     spm += player.getOnlineSpm();
  465.  
  466.                 return (spm / count);
  467.             }
  468.  
  469.             public double getOnlineKpm()
  470.             {
  471.                 double count = getCount();
  472.                 if (count == 0)
  473.                     return 0;
  474.  
  475.                 double kpm = 0;
  476.                 foreach (PlayerProfile player in getMembers())
  477.                     kpm += player.getOnlineKpm();
  478.  
  479.                 return (kpm / count);
  480.             }
  481.  
  482.  
  483.             public double getOnlineKills()
  484.             {
  485.                 double count = getCount();
  486.                 if (count == 0)
  487.                     return 0;
  488.  
  489.                 double kills = 0;
  490.                 foreach (PlayerProfile player in getMembers())
  491.                     kills += player.getOnlineKills();
  492.  
  493.                 return (kills / count);
  494.             }
  495.  
  496.             public double getOnlineDeaths()
  497.             {
  498.                 double count = getCount();
  499.                 if (count == 0)
  500.                     return 0;
  501.  
  502.                 double deaths = 0;
  503.                 foreach (PlayerProfile player in getMembers())
  504.                     deaths += player.getOnlineDeaths();
  505.  
  506.                 return (deaths / count);
  507.             }
  508.  
  509.             public double getOnlineScore()
  510.             {
  511.                 double count = getCount();
  512.                 if (count == 0)
  513.                     return 0;
  514.  
  515.                 double score = 0;
  516.                 foreach (PlayerProfile player in getMembers())
  517.                     score += player.getOnlineScore();
  518.  
  519.                 return (score / count);
  520.             }
  521.  
  522.  
  523.             public double getOnlineKdr()
  524.             {
  525.                 double count = getCount();
  526.                 if (count == 0)
  527.                     return 0;
  528.  
  529.                 double kdr = 0;
  530.                 foreach (PlayerProfile player in getMembers())
  531.                     kdr += player.getOnlineKdr();
  532.  
  533.                 return (kdr / count);
  534.             }
  535.  
  536.             public double getOnlineRank()
  537.             {
  538.                 double count = getCount();
  539.                 if (count == 0)
  540.                     return 0;
  541.  
  542.                 double rank = 0;
  543.                 foreach (PlayerProfile player in getMembers())
  544.                     rank += player.getOnlineRank();
  545.  
  546.                 return (rank / count);
  547.             }
  548.  
  549.             public double getOnlineQuits()
  550.             {
  551.                 double count = getCount();
  552.                 if (count == 0)
  553.                     return 0;
  554.  
  555.                 double quits = 0;
  556.                 foreach (PlayerProfile player in getMembers())
  557.                     quits += player.getOnlineQuits();
  558.  
  559.                 return (quits / count);
  560.             }
  561.  
  562.             public double getOnlineAccuracy()
  563.             {
  564.                 double count = getCount();
  565.                 if (count == 0)
  566.                     return 0;
  567.  
  568.                 double accuracy = 0;
  569.                 foreach (PlayerProfile player in getMembers())
  570.                     accuracy += player.getOnlineAccuracy();
  571.  
  572.                 return (accuracy / count);
  573.             }
  574.  
  575.  
  576.             public double getOnlineTime()
  577.             {
  578.                 double count = getCount();
  579.                 if (count == 0)
  580.                     return 0;
  581.  
  582.                 double total_ticks = 0;
  583.                 foreach (PlayerProfile player in getMembers())
  584.                     total_ticks += (double)player.getOnlineTime();
  585.  
  586.                 return total_ticks / count;
  587.  
  588.             }
  589.  
  590.  
  591.             public override String ToString()
  592.             {
  593.                 return "Team(" + TN(getTeamId()) + ").Squad(" + SQN(getSquadId()) + "): " + getMembersListStr();
  594.             }
  595.  
  596.             public string getMembersListStr()
  597.             {
  598.                 List<string> names = new List<string>();
  599.                 foreach (PlayerProfile player in members)
  600.                     names.Add(player.ToString());
  601.  
  602.                 return String.Join(", ", names.ToArray());
  603.             }
  604.  
  605.  
  606.             public string getClanTag()
  607.             {
  608.                 if (getCount() == 0)
  609.                     return "";
  610.  
  611.                 return getRandomPlayer().getClanTag();
  612.             }
  613.  
  614.             public string getMajorityClanTag()
  615.             {
  616.  
  617.                 if (getCount() == 0)
  618.                     return "";
  619.  
  620.                 Dictionary<string, int> tagCount = new Dictionary<string, int>();
  621.                 string tag = "";
  622.  
  623.                 /* count how many times each tag repeats */
  624.                 foreach (PlayerProfile player in getMembers())
  625.                 {
  626.                     tag = player.getClanTag();
  627.                     if (tag == null || tag.Length == 0)
  628.                         continue;
  629.  
  630.                     if (!tagCount.ContainsKey(tag))
  631.                         tagCount[tag] = 0;
  632.  
  633.                     tagCount[tag]++;
  634.                 }
  635.  
  636.                 if (tagCount.Count == 0)
  637.                     return "";
  638.  
  639.                 /* sort by ascending tag count */
  640.                 List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(tagCount);
  641.                 list.Sort(delegate(KeyValuePair<string, int> left, KeyValuePair<string, int> right) { return left.Value.CompareTo(right.Value) * (-1); });
  642.  
  643.                 return list[0].Key;
  644.             }
  645.  
  646.             public void setSquadId(int sid)
  647.             {
  648.                 squadId = sid;
  649.             }
  650.  
  651.             public void setTeamId(int tid)
  652.             {
  653.                 teamId = tid;
  654.             }
  655.         }
  656.  
  657.         private class PlayerMessage
  658.         {
  659.             public MessageType type;
  660.             public string text;
  661.             public int time;
  662.  
  663.             public PlayerMessage(string tx)
  664.             {
  665.                 text = tx;
  666.                 type = MessageType.say;
  667.             }
  668.         }
  669.  
  670.         public enum PluginState { stop, wait, check, warn, balance };
  671.  
  672.         //variables to keep track of the start time for each state
  673.         DateTime startStopTime;
  674.         DateTime startWaitTime;
  675.         DateTime startCheckTime;
  676.         DateTime startWarnTime;
  677.         DateTime startBalanceTime;
  678.         DateTime startRoundTime;
  679.         DateTime utc; //universal time;
  680.  
  681.         PluginState pluginState;
  682.         CServerInfo serverInfo = null;
  683.  
  684.         bool plugin_enabled = false;
  685.  
  686.         public Dictionary<String, String> maps;
  687.         public Dictionary<String, String> modes;
  688.         public Dictionary<String, List<String>> settings_group;
  689.         public Dictionary<String, int> settings_group_order;
  690.         public Dictionary<string, bool> booleanVariables;
  691.         public Dictionary<string, int> integerVariables;
  692.         public Dictionary<string, float> floatVariables;
  693.         public Dictionary<string, string> stringListVariables;
  694.         public Dictionary<string, string> stringVariables;
  695.         public List<string> hiddenVariables;
  696.  
  697.  
  698.         public delegate bool integerVariableValidator(string var, int value);
  699.         public delegate bool booleanVariableValidator(string var, bool value);
  700.         private delegate int player_sort_method(PlayerProfile left, PlayerProfile right);
  701.         private delegate int squad_sort_method(PlayerSquad left, PlayerSquad right);
  702.         public delegate bool stringVariableValidator(string var, string value);
  703.         public Dictionary<string, integerVariableValidator> integerVarValidators;
  704.         public Dictionary<string, booleanVariableValidator> booleanVarValidators;
  705.         public Dictionary<string, stringVariableValidator> stringVarValidators;
  706.  
  707.  
  708.         private Dictionary<string, PlayerProfile> players;
  709.         Dictionary<String, CPunkbusterInfo> new_player_queue;
  710.         EventWaitHandle wake_handle;
  711.  
  712.         Thread stats_fetching_thread;
  713.  
  714.  
  715.         public enum PlayerState { dead, alive, left, kicked, limbo };
  716.         public enum MessageType { say, invalid };
  717.  
  718.  
  719.         public class PlayerStats
  720.         {
  721.             public double rank;
  722.             public double kills;
  723.             public double deaths;
  724.             public double score;
  725.             public double skill;
  726.             public double accuracy;
  727.             public double quits;
  728.             public double kdr;
  729.             public double spm;
  730.             public string tag = "";
  731.             public double secs;
  732.  
  733.             public void reset()
  734.             {
  735.                 rank = 0;
  736.                 kills = 0;
  737.                 deaths = 0;
  738.                 score = 0;
  739.                 skill = 0;
  740.                 accuracy = 0;
  741.                 quits = 0;
  742.                 kdr = 0;
  743.                 tag = "";
  744.                 secs = 0;
  745.             }
  746.  
  747.         }
  748.  
  749.         private class PlayerProfile
  750.         {
  751.             private InsaneBalancer plugin;
  752.             public string name;
  753.             public string tag = "";
  754.             public PlayerStats stats;
  755.             public PlayerStats round_stats;
  756.  
  757.             public PlayerState state;
  758.             public CPlayerInfo info;
  759.             public CPunkbusterInfo pbinfo;
  760.             public Queue<PlayerMessage> qmsg;          //queued messages
  761.             public DateTime time = DateTime.Now;
  762.             public DateTime last_kill = DateTime.Now;
  763.             public DateTime last_death = DateTime.Now;
  764.             public DateTime last_spawn = DateTime.Now;
  765.             public DateTime last_chat = DateTime.Now;
  766.             public DateTime last_score = DateTime.Now;
  767.  
  768.  
  769.             public int savedTeamId = -1;
  770.             public int savedSquadId = -1;
  771.  
  772.             public int targetTeamId = -1;
  773.             public int targetSquadId = -1;
  774.  
  775.             public int delayedTeamId = -1;
  776.             public int delayedSquadId = -1;
  777.  
  778.             public int random_value = -1;
  779.  
  780.  
  781.             public bool isInGame()
  782.             {
  783.                 return (info.TeamID >= 0);
  784.             }
  785.  
  786.             public PlayerProfile(PlayerProfile player)
  787.             {
  788.                 /* shallow copy */
  789.                 updateInfo(player.info);
  790.                 pbinfo = player.pbinfo;
  791.                 name = player.name;
  792.                 plugin = player.plugin;
  793.                 stats = player.stats;
  794.                 state = player.state;
  795.                 qmsg = player.qmsg;
  796.                 tag = player.tag;
  797.                 time = player.time;
  798.                 random_value = player.random_value;
  799.  
  800.                 last_kill = player.last_kill;
  801.                 last_death = player.last_death;
  802.                 last_spawn = player.last_spawn;
  803.                 last_chat = player.last_chat;
  804.                 last_score = player.last_score;
  805.  
  806.                 savedTeamId = player.savedTeamId;
  807.                 savedSquadId = player.savedSquadId;
  808.  
  809.                 targetTeamId = player.targetTeamId;
  810.                 targetSquadId = player.targetSquadId;
  811.  
  812.                 delayedTeamId = player.delayedTeamId;
  813.                 delayedSquadId = player.delayedSquadId;
  814.             }
  815.  
  816.             public PlayerProfile(InsaneBalancer plg, CPunkbusterInfo inf)
  817.             {
  818.  
  819.                 try
  820.                 {
  821.                     plugin = plg;
  822.                     info = new CPlayerInfo();
  823.                     pbinfo = inf;
  824.                     name = pbinfo.SoldierName;
  825.  
  826.                     time = DateTime.Now;
  827.                     round_stats = new PlayerStats();
  828.                     stats = new PlayerStats();
  829.                     resetStats();
  830.  
  831.                     fetchStats();
  832.                 }
  833.                 catch (Exception e)
  834.                 {
  835.                     plugin.dump_exception(e);
  836.                 }
  837.             }
  838.  
  839.             public void fetchStats()
  840.             {
  841.                 stats = plugin.blog.getPlayerStats(name);
  842.                 plugin.ConsoleWrite(this.ToString() + ", fetched, " + getOnlineStatistics());
  843.             }
  844.  
  845.  
  846.             public void updateInfo(CPlayerInfo inf)
  847.             {
  848.                 /* don't update the information while round is begining */
  849.                 if (plugin.round_balancer)
  850.                     return;
  851.  
  852.                 if (plugin.live_balancer)
  853.                     return;
  854.  
  855.  
  856.                 if (info.Score != inf.Score)
  857.                     updateLastScore();
  858.  
  859.                 info = inf;
  860.  
  861.                 round_stats.kills = info.Kills;
  862.                 round_stats.deaths = info.Deaths;
  863.                 round_stats.score = info.Score;
  864.             }
  865.  
  866.  
  867.             public void resetStats()
  868.             {
  869.                 //queued messages
  870.                 qmsg = new Queue<PlayerMessage>();
  871.  
  872.                 //other
  873.                 state = PlayerState.limbo;
  874.  
  875.                 round_stats.reset();
  876.  
  877.                 savedSquadId = -1;
  878.                 savedTeamId = -1;
  879.                 targetSquadId = -1;
  880.                 targetTeamId = -1;
  881.                 delayedTeamId = -1;
  882.                 delayedSquadId = -1;
  883.                 random_value = -1;
  884.  
  885.                 last_kill = DateTime.Now;
  886.                 last_chat = DateTime.Now;
  887.                 last_death = DateTime.Now;
  888.                 last_spawn = DateTime.Now;
  889.                 last_score = DateTime.Now;
  890.             }
  891.  
  892.  
  893.             /* Player Messages */
  894.             public void dequeueMessages()
  895.             {
  896.                 while (this.qmsg.Count > 0)
  897.                 {
  898.                     PlayerMessage msg = this.qmsg.Dequeue();
  899.                     if (msg.type.Equals(MessageType.say))
  900.                     {
  901.                         this.plugin.SendPlayerMessage(name, msg.text);
  902.                     }
  903.                 }
  904.             }
  905.  
  906.  
  907.             public void enqueueMessage(PlayerMessage msg)
  908.             {
  909.                 this.qmsg.Enqueue(msg);
  910.             }
  911.  
  912.  
  913.             public bool willMoveAcrossTeams()
  914.             {
  915.                 return willMoveTeams();
  916.             }
  917.  
  918.  
  919.             public bool willMoveAcrossSquad()
  920.             {
  921.                 return !willMoveAcrossTeams() && willMoveSquads();
  922.             }
  923.  
  924.             public bool willMoveTeams()
  925.             {
  926.                 return getSavedSquadId() != getTargetTeamId();
  927.             }
  928.  
  929.             public bool willMoveSquads()
  930.             {
  931.                 return getSavedSquadId() != getTargetSquadId();
  932.             }
  933.  
  934.  
  935.             /* Round Level Statistics */
  936.             public double getRoundKpm()
  937.             {
  938.                 double minutes = plugin.getRoundMinutes();
  939.                 double kills = getRoundKills();
  940.  
  941.                 return kills / minutes;
  942.             }
  943.  
  944.             public double getRoundSpm()
  945.             {
  946.                 double minutes = plugin.getRoundMinutes();
  947.                 double score = getRoundScore();
  948.                 return score / minutes;
  949.             }
  950.  
  951.             public double getRoundKills()
  952.             {
  953.                 return round_stats.kills;
  954.             }
  955.  
  956.             public double getRoundScore()
  957.             {
  958.                 return round_stats.score;
  959.             }
  960.  
  961.             public double getRoundDeaths()
  962.             {
  963.                 return round_stats.deaths;
  964.             }
  965.  
  966.             public double getRoundKdr()
  967.             {
  968.                 return (getRoundKills() + 1) / (getRoundDeaths() + 1);
  969.  
  970.             }
  971.  
  972.  
  973.  
  974.  
  975.  
  976.             /* Online  Statistics */
  977.             public double getOnlineKpm()
  978.             {
  979.                 double minutes = getOnlineTime();
  980.                 double kills = getOnlineKills();
  981.  
  982.                 if (kills == 0 || minutes == 0)
  983.                     return 0;
  984.  
  985.                 return kills / minutes;
  986.             }
  987.  
  988.             public double getOnlineSpm()
  989.             {
  990.                 double minutes = getOnlineTime();
  991.                 double score = getOnlineScore();
  992.  
  993.                 if (score == 0 || minutes == 0)
  994.                     return 0;
  995.  
  996.                 return score / minutes;
  997.             }
  998.  
  999.             public double getOnlineKills()
  1000.             {
  1001.                 return stats.kills;
  1002.             }
  1003.  
  1004.             public double getOnlineScore()
  1005.             {
  1006.                 return stats.score;
  1007.             }
  1008.  
  1009.             public double getOnlineDeaths()
  1010.             {
  1011.                 return stats.deaths;
  1012.             }
  1013.  
  1014.             public double getOnlineKdr()
  1015.             {
  1016.                 return stats.kdr;
  1017.             }
  1018.  
  1019.             public double getOnlineTime()
  1020.             {
  1021.                 return stats.secs / 60D;
  1022.             }
  1023.  
  1024.             public double getOnlineRank()
  1025.             {
  1026.                 return stats.rank;
  1027.             }
  1028.  
  1029.             public double getOnlineQuits()
  1030.             {
  1031.                 return stats.quits;
  1032.             }
  1033.  
  1034.             public double getOnlineAccuracy()
  1035.             {
  1036.                 return stats.accuracy;
  1037.             }
  1038.  
  1039.             public double getOnlineSkill()
  1040.             {
  1041.                 return stats.skill;
  1042.             }
  1043.  
  1044.  
  1045.             public DateTime getRoundTime()
  1046.             {
  1047.                 return time;
  1048.             }
  1049.  
  1050.             public virtual int getRandomValue()
  1051.             {
  1052.                 if (random_value == -1)
  1053.                     random_value = (new Random()).Next(0, int.MaxValue);
  1054.                 return random_value;
  1055.             }
  1056.  
  1057.             public override string ToString()
  1058.             {
  1059.                 string t = getClanTag();
  1060.                 if (t.Length > 0)
  1061.                     return "[" + t + "]" + name;
  1062.                 return name;
  1063.             }
  1064.  
  1065.  
  1066.             /* Player State and Information */
  1067.             public bool isAlive()
  1068.             {
  1069.                 return state.Equals(PlayerState.alive);
  1070.             }
  1071.  
  1072.             public bool isDead()
  1073.             {
  1074.                 return state.Equals(PlayerState.dead);
  1075.             }
  1076.  
  1077.             public bool wasKicked()
  1078.             {
  1079.                 return state.Equals(PlayerState.kicked);
  1080.             }
  1081.  
  1082.             public bool leftGame()
  1083.             {
  1084.                 return state.Equals(PlayerState.left);
  1085.             }
  1086.  
  1087.  
  1088.             public string getClanTag()
  1089.             {
  1090.                 return stats.tag;
  1091.             }
  1092.  
  1093.             public bool isInClan()
  1094.             {
  1095.                 return getClanTag().Length > 0;
  1096.             }
  1097.  
  1098.             public int getLastScore()
  1099.             {
  1100.                 return (int)DateTime.Now.Subtract(last_score).TotalSeconds;
  1101.             }
  1102.  
  1103.             public int getLastChat()
  1104.             {
  1105.                 return (int)DateTime.Now.Subtract(last_chat).TotalSeconds;
  1106.             }
  1107.  
  1108.             public void updateLastScore()
  1109.             {
  1110.                 last_score = DateTime.Now;
  1111.             }
  1112.  
  1113.             public void updateLastChat()
  1114.             {
  1115.                 last_chat = DateTime.Now;
  1116.             }
  1117.  
  1118.             public int getLastKill()
  1119.             {
  1120.                 return (int)DateTime.Now.Subtract(last_kill).TotalSeconds;
  1121.             }
  1122.  
  1123.             public void updateLastKill()
  1124.             {
  1125.                 last_kill = DateTime.Now;
  1126.             }
  1127.  
  1128.             public int getLastDeath()
  1129.             {
  1130.                 return (int)DateTime.Now.Subtract(last_death).TotalSeconds;
  1131.             }
  1132.  
  1133.             public void updateLastDeath()
  1134.             {
  1135.                 last_death = DateTime.Now;
  1136.             }
  1137.  
  1138.  
  1139.             public int getLastSpawn()
  1140.             {
  1141.                 return (int)DateTime.Now.Subtract(last_spawn).TotalSeconds;
  1142.             }
  1143.  
  1144.  
  1145.  
  1146.  
  1147.             public void updateLastSpawn()
  1148.             {
  1149.                 last_spawn = DateTime.Now;
  1150.             }
  1151.  
  1152.  
  1153.  
  1154.             public virtual void setSquadId(int sid)
  1155.             {
  1156.                 info.SquadID = sid;
  1157.             }
  1158.  
  1159.             public virtual void setTeamId(int tid)
  1160.             {
  1161.                 info.TeamID = tid;
  1162.             }
  1163.  
  1164.             public virtual int getSquadId()
  1165.             {
  1166.                 return info.SquadID;
  1167.             }
  1168.  
  1169.             public virtual int getTeamId()
  1170.             {
  1171.                 return info.TeamID;
  1172.             }
  1173.  
  1174.             public void saveTeamSquad()
  1175.             {
  1176.  
  1177.                 if (savedTeamId == -1)
  1178.                     savedTeamId = getTeamId();
  1179.  
  1180.                 if (savedSquadId == -1)
  1181.                     savedSquadId = getSquadId();
  1182.             }
  1183.  
  1184.             public void saveTargetTeamSquad()
  1185.             {
  1186.  
  1187.                 if (targetTeamId == -1)
  1188.                     targetTeamId = getTeamId();
  1189.  
  1190.                 if (targetSquadId == -1)
  1191.                     targetSquadId = getSquadId();
  1192.             }
  1193.  
  1194.             public void saveDelayedTeamSquad()
  1195.             {
  1196.                 if (delayedTeamId == -1)
  1197.                     delayedTeamId = getTeamId();
  1198.  
  1199.                 if (delayedSquadId == -1)
  1200.                     delayedSquadId = getSquadId();
  1201.             }
  1202.  
  1203.             public void resetDelayedTeamSquad()
  1204.             {
  1205.                 delayedTeamId = -1;
  1206.                 delayedSquadId = -1;
  1207.             }
  1208.  
  1209.             public void resetSavedTeamSquad()
  1210.             {
  1211.                 savedTeamId = -1;
  1212.                 savedSquadId = -1;
  1213.             }
  1214.  
  1215.             public int getSavedSquadId()
  1216.             {
  1217.                 return savedSquadId;
  1218.             }
  1219.  
  1220.             public int getSavedTeamId()
  1221.             {
  1222.                 return savedTeamId;
  1223.             }
  1224.  
  1225.             public int getTargetSquadId()
  1226.             {
  1227.                 return targetSquadId;
  1228.             }
  1229.  
  1230.             public int getTargetTeamId()
  1231.             {
  1232.                 return targetTeamId;
  1233.             }
  1234.  
  1235.             public int getDelayedTeamId()
  1236.             {
  1237.                 return delayedTeamId;
  1238.             }
  1239.  
  1240.  
  1241.             public int getDelayedSquadId()
  1242.             {
  1243.                 return delayedSquadId;
  1244.             }
  1245.  
  1246.  
  1247.             public void resetTeamSquad()
  1248.             {
  1249.                 if (savedTeamId > 0)
  1250.                     setTeamId(savedTeamId);
  1251.  
  1252.                 if (savedSquadId > 0)
  1253.                     setSquadId(savedSquadId);
  1254.             }
  1255.  
  1256.  
  1257.             public string getRoundStatistics()
  1258.             {
  1259.                 return String.Format("score({0}), kills({1}), deaths({2}) kdr({3}), spm({4}), kpm({5}), time({6})", getRoundScore(), getRoundKills(), getRoundDeaths(), Math.Round(getRoundKdr(), 2), Math.Round(getRoundSpm(), 2), Math.Round(getRoundKpm(), 2), getRoundTime());
  1260.             }
  1261.  
  1262.             public string getOnlineStatistics()
  1263.             {
  1264.                 return String.Format("score({0}), rank({1}), skill({2}), kills({3}), deaths({4}) kdr({5}), spm({6}), kpm({7}), quits({8}), acc({9})", getOnlineScore(), getOnlineRank(), Math.Round(getOnlineSkill(), 2), getOnlineKills(), getOnlineDeaths(), Math.Round(getOnlineKdr(), 2), Math.Round(getOnlineSpm(), 2), Math.Round(getOnlineKpm(), 2), Math.Round(getOnlineQuits(), 2), Math.Round(getOnlineAccuracy(), 2));
  1265.             }
  1266.  
  1267.  
  1268.             public string getIdleStatistics()
  1269.             {
  1270.                 return String.Format("last_kill({0}), last_death({1}), last_spawn({2}) last_chat({3}) last_score({4})", getLastKill(), getLastDeath(), getLastSpawn(), getLastChat(), getLastScore());
  1271.             }
  1272.  
  1273.         }
  1274.  
  1275.  
  1276.         public InsaneBalancer()
  1277.         {
  1278.             utc = DateTime.Now;
  1279.             startRoundTime = utc;
  1280.             blog = new BattleLog(this);
  1281.  
  1282.             this.maps = new Dictionary<string, string>();
  1283.             maps.Add("mp_001", "grand_bazaar");
  1284.             maps.Add("mp_003", "teheran_highway");
  1285.             maps.Add("mp_007", "caspian_border");
  1286.             maps.Add("mp_011", "seine_crossing");
  1287.             maps.Add("mp_012", "operation_firestorm");
  1288.             maps.Add("mp_013", "damavand_peak");
  1289.             maps.Add("mp_017", "noshahar_canals");
  1290.             maps.Add("mp_018", "kharg_island");
  1291.             maps.Add("mp_subway", "operation_metro");
  1292.  
  1293.             maps.Add("xp1_001", "strike_karkand");
  1294.             maps.Add("xp1_002", "gulf_oman");
  1295.             maps.Add("xp1_003", "sharqi_peninsula");
  1296.             maps.Add("xp1_004", "wake_island");
  1297.  
  1298.             this.modes = new Dictionary<string, string>();
  1299.             modes.Add("conquestlarge0", "cl");
  1300.             modes.Add("conquestsmall0", "cs");
  1301.             modes.Add("conquestsmall1", "csa");
  1302.             modes.Add("rushlarge0", "rl");
  1303.             modes.Add("teamdeathmatch0", "td");
  1304.             modes.Add("squadrush0", "sr");
  1305.  
  1306.  
  1307.  
  1308.  
  1309.             this.players = new Dictionary<string, PlayerProfile>();
  1310.             this.new_player_queue = new Dictionary<string, CPunkbusterInfo>();
  1311.            
  1312.            
  1313.  
  1314.  
  1315.             this.booleanVariables = new Dictionary<string, bool>();
  1316.             this.booleanVariables.Add("auto_start", true);
  1317.             this.booleanVariables.Add("keep_squads_live", true);
  1318.             this.booleanVariables.Add("keep_squads_round", true);
  1319.             this.booleanVariables.Add("keep_clans_live", false);
  1320.             this.booleanVariables.Add("keep_clans_round", false);
  1321.             this.booleanVariables.Add("use_white_list", false);
  1322.             this.booleanVariables.Add("use_extra_white_lists", false);
  1323.             this.booleanVariables.Add("virtual_mode", false);
  1324.             this.booleanVariables.Add("warn_say", true);
  1325.             this.booleanVariables.Add("balance_round", true);
  1326.             this.booleanVariables.Add("balance_live", true);
  1327.             this.booleanVariables.Add("kick_idle", true);
  1328.             this.booleanVariables.Add("wait_death", false);
  1329.  
  1330.  
  1331.             this.booleanVariables.Add("quiet_mode", false);
  1332.             this.booleanVariables.Add("advanced_mode", false);
  1333.  
  1334.  
  1335.  
  1336.             this.integerVariables = new Dictionary<string, int>();
  1337.             this.integerVariables.Add("wait_death_count", 6);
  1338.             this.integerVariables.Add("balance_threshold", 1);
  1339.             this.integerVariables.Add("debug_level", 3);
  1340.             this.integerVariables.Add("live_interval_time", 15);
  1341.             this.integerVariables.Add("round_interval", 1);
  1342.             this.integerVariables.Add("round_wait_time", 3);
  1343.             this.integerVariables.Add("warn_msg_interval_time", 15);
  1344.             this.integerVariables.Add("warn_msg_total_time", 15);
  1345.             this.integerVariables.Add("warn_msg_countdown_time", 3);
  1346.             this.integerVariables.Add("warn_msg_display_time", 5);
  1347.  
  1348.             this.integerVariables.Add("last_kill_time", 300);
  1349.             this.integerVariables.Add("last_death_time", 300);
  1350.             this.integerVariables.Add("last_spawn_time", 300);
  1351.             this.integerVariables.Add("last_chat_time", 300);
  1352.             this.integerVariables.Add("last_score_time", 300);
  1353.             this.integerVariables.Add("ticket_threshold", 0);
  1354.  
  1355.  
  1356.             this.integerVarValidators = new Dictionary<string, integerVariableValidator>();
  1357.             this.integerVarValidators.Add("warn_msg_interval_time", integerValidator);
  1358.             this.integerVarValidators.Add("warn_msg_total_time", integerValidator);
  1359.             this.integerVarValidators.Add("warn_msg_display_time", integerValidator);
  1360.             this.integerVarValidators.Add("warn_msg_countdown_time", integerValidator);
  1361.             this.integerVarValidators.Add("balance_threshold", integerValidator);
  1362.             this.integerVarValidators.Add("round_interval", integerValidator);
  1363.             this.integerVarValidators.Add("live_interval_time", integerValidator);
  1364.             this.integerVarValidators.Add("debug_level", integerValidator);
  1365.             this.integerVarValidators.Add("last_kill_time", integerValidator);
  1366.             this.integerVarValidators.Add("last_death_time", integerValidator);
  1367.             this.integerVarValidators.Add("last_spawn_time", integerValidator);
  1368.             this.integerVarValidators.Add("last_chat_time", integerValidator);
  1369.             this.integerVarValidators.Add("last_score_time", integerValidator);
  1370.             this.integerVarValidators.Add("ticket_threshold", integerValidator);
  1371.             this.integerVarValidators.Add("wait_death_count", integerValidator);
  1372.  
  1373.             /* set up per-map intervals */
  1374.             List<String> map_interval = new List<string>();
  1375.             foreach (KeyValuePair<String, String> mode_pair in modes)
  1376.                 foreach (KeyValuePair<String, String> map_pair in maps)
  1377.                     // skip Wake Island for ConquestSmall1
  1378.                     // skip all non Expansion Pack 1 maps from ConquestSmall1
  1379.                     if (!(mode_pair.Value.Equals("csa") &&
  1380.                         (map_pair.Value.Equals("wake_island") || !map_pair.Key.Contains("xp1"))))
  1381.                         map_interval.Add(mode_pair.Value + "_" + map_pair.Value);
  1382.  
  1383.             foreach (String name in map_interval)
  1384.             {
  1385.                 this.integerVariables.Add(name, 0);
  1386.                 this.integerVarValidators.Add(name, integerValidator);
  1387.             }
  1388.  
  1389.  
  1390.             this.booleanVarValidators = new Dictionary<string, booleanVariableValidator>();
  1391.             this.booleanVarValidators.Add("keep_squads_live", booleanValidator);
  1392.             this.booleanVarValidators.Add("keep_squads_round", booleanValidator);
  1393.             this.booleanVarValidators.Add("keep_clan_live", booleanValidator);
  1394.             this.booleanVarValidators.Add("keep_clans_round", booleanValidator);
  1395.             this.booleanVarValidators.Add("use_white_list", booleanValidator);
  1396.             this.booleanVarValidators.Add("use_extra_white_lists", booleanValidator);
  1397.             this.booleanVarValidators.Add("virtual_mode", booleanValidator);
  1398.             this.booleanVarValidators.Add("kick_idle", booleanValidator);
  1399.             this.booleanVarValidators.Add("wait_death", booleanValidator);
  1400.  
  1401.             this.stringVarValidators = new Dictionary<string, stringVariableValidator>();
  1402.             this.stringVarValidators.Add("round_sort", stringValidator);
  1403.             this.stringVarValidators.Add("live_sort", stringValidator);
  1404.             this.stringVarValidators.Add("console", commandValidator);
  1405.  
  1406.  
  1407.             this.floatVariables = new Dictionary<string, float>();
  1408.             this.stringListVariables = new Dictionary<string, string>();
  1409.             this.stringListVariables.Add("admin_list", @"micovery, admin2, admin3");
  1410.  
  1411.             this.stringListVariables.Add("player_kick_wlist", "list of players that should not kicked when idle");
  1412.             this.stringListVariables.Add("player_move_wlist", "list of players that should not be moved");
  1413.             this.stringListVariables.Add("player_safe_wlist", "list of players that should not be kicked or moved ");
  1414.             this.stringListVariables.Add("clan_kick_wlist", "list of clans that should not be kicked when idle");
  1415.             this.stringListVariables.Add("clan_move_wlist", "list of clans that should not be moved");
  1416.             this.stringListVariables.Add("clan_safe_wlist", "list of clans that should not be kicked or moved");
  1417.  
  1418.  
  1419.             this.stringVariables = new Dictionary<string, string>();
  1420.  
  1421.             this.stringVariables.Add("round_sort", "spm_desc_round");
  1422.             this.stringVariables.Add("live_sort", "time_desc_round");
  1423.             this.stringVariables.Add("console", "Type a command here to test");
  1424.  
  1425.             this.hiddenVariables = new List<string>();
  1426.             this.hiddenVariables.Add("advanced_mode");
  1427.             this.hiddenVariables.Add("warn_msg_total_time");
  1428.             this.hiddenVariables.Add("warn_msg_countdown_time");
  1429.             this.hiddenVariables.Add("warn_msg_interval_time");
  1430.             this.hiddenVariables.Add("warn_msg_display_time");
  1431.             this.hiddenVariables.Add("quiet_mode");
  1432.             this.hiddenVariables.Add("auto_start");
  1433.             this.hiddenVariables.Add("virtual_mode");
  1434.  
  1435.             /* Grouping settings */
  1436.  
  1437.             this.settings_group = new Dictionary<string, List<string>>();
  1438.  
  1439.  
  1440.             List<String> whitelist_group = new List<string>();
  1441.             whitelist_group.Add("use_extra_white_lists");
  1442.             whitelist_group.Add("player_move_wlist");
  1443.             whitelist_group.Add("player_kick_wlist");
  1444.             whitelist_group.Add("player_safe_wlist");
  1445.  
  1446.             whitelist_group.Add("clan_move_wlist");
  1447.             whitelist_group.Add("clan_kick_wlist");
  1448.             whitelist_group.Add("clan_safe_wlist");
  1449.  
  1450.             List<String> round_balancer_group = new List<string>();
  1451.             round_balancer_group.Add("keep_clans_round");
  1452.             round_balancer_group.Add("keep_squads_round");
  1453.             round_balancer_group.Add("round_interval");
  1454.             round_balancer_group.Add("round_wait_time");
  1455.             round_balancer_group.Add("round_sort");
  1456.             round_balancer_group.Add("kick_idle");
  1457.  
  1458.             List<String> idle_watch_group = new List<string>();
  1459.             idle_watch_group.Add("last_kill_time");
  1460.             idle_watch_group.Add("last_death_time");
  1461.             idle_watch_group.Add("last_spawn_time");
  1462.             idle_watch_group.Add("last_chat_time");
  1463.             idle_watch_group.Add("last_score_time");
  1464.  
  1465.             List<String> live_balancer_group = new List<string>();
  1466.             live_balancer_group.Add("keep_clans_live");
  1467.             live_balancer_group.Add("keep_squads_live");
  1468.             live_balancer_group.Add("live_sort");
  1469.             live_balancer_group.Add("live_interval_time");
  1470.             live_balancer_group.Add("warn_say");
  1471.             live_balancer_group.Add("wait_death");
  1472.             live_balancer_group.Add("wait_death_count");
  1473.             live_balancer_group.Add("balance_threshold");
  1474.             live_balancer_group.Add("ticket_threshold");
  1475.  
  1476.             settings_group.Add("Round Interval", map_interval);
  1477.             settings_group.Add("Whitelist", whitelist_group);
  1478.             settings_group.Add("Live Balancer", live_balancer_group);
  1479.             settings_group.Add("Round Balancer", round_balancer_group);
  1480.             settings_group.Add("Idle Watch", idle_watch_group);
  1481.  
  1482.             settings_group_order = new Dictionary<string, int>();
  1483.             settings_group_order.Add("Settings", 1);
  1484.             settings_group_order.Add("Live Balancer", 2);
  1485.             settings_group_order.Add("Round Balancer", 3);
  1486.             settings_group_order.Add("Whitelist", 4);
  1487.             settings_group_order.Add("Idle Watch", 5);
  1488.             settings_group_order.Add("Round Interval", 6);
  1489.  
  1490.         }
  1491.  
  1492.  
  1493.  
  1494.         public void loadSettings()
  1495.         {
  1496.             attempts = 0;
  1497.         }
  1498.  
  1499.  
  1500.         private player_sort_method getPlayerSort(string phase)
  1501.         {
  1502.             string sort_method = getStringVarValue(phase);
  1503.  
  1504.  
  1505.             if (sort_method.CompareTo("kdr_asc_round") == 0)
  1506.                 return player_kdr_asc_round_cmp;
  1507.             else if (sort_method.CompareTo("kdr_desc_round") == 0)
  1508.                 return player_kdr_desc_round_cmp;
  1509.             else if (sort_method.CompareTo("score_asc_round") == 0)
  1510.                 return player_score_asc_round_cmp;
  1511.             else if (sort_method.CompareTo("score_desc_round") == 0)
  1512.                 return player_score_desc_round_cmp;
  1513.             else if (sort_method.CompareTo("spm_asc_round") == 0)
  1514.                 return player_spm_asc_round_cmp;
  1515.             else if (sort_method.CompareTo("spm_desc_round") == 0)
  1516.                 return player_spm_desc_round_cmp;
  1517.             else if (sort_method.CompareTo("kpm_asc_round") == 0)
  1518.                 return player_kpm_asc_round_cmp;
  1519.             else if (sort_method.CompareTo("kpm_desc_round") == 0)
  1520.                 return player_kpm_desc_round_cmp;
  1521.             else if (sort_method.CompareTo("time_asc_round") == 0)
  1522.                 return player_time_asc_round_cmp;
  1523.             else if (sort_method.CompareTo("time_desc_round") == 0)
  1524.                 return player_time_desc_round_cmp;
  1525.             else if (sort_method.CompareTo("kdr_asc_online") == 0)
  1526.                 return player_kdr_asc_online_cmp;
  1527.             else if (sort_method.CompareTo("kdr_desc_online") == 0)
  1528.                 return player_kdr_desc_online_cmp;
  1529.             else if (sort_method.CompareTo("kpm_asc_online") == 0)
  1530.                 return player_kpm_asc_online_cmp;
  1531.             else if (sort_method.CompareTo("kpm_desc_online") == 0)
  1532.                 return player_kpm_desc_online_cmp;
  1533.             else if (sort_method.CompareTo("spm_asc_online") == 0)
  1534.                 return player_spm_asc_online_cmp;
  1535.             else if (sort_method.CompareTo("spm_desc_online") == 0)
  1536.                 return player_spm_desc_online_cmp;
  1537.             else if (sort_method.CompareTo("kills_asc_online") == 0)
  1538.                 return player_kills_asc_online_cmp;
  1539.             else if (sort_method.CompareTo("kills_desc_online") == 0)
  1540.                 return player_kills_desc_online_cmp;
  1541.             else if (sort_method.CompareTo("deaths_asc_online") == 0)
  1542.                 return player_deaths_asc_online_cmp;
  1543.             else if (sort_method.CompareTo("deaths_desc_online") == 0)
  1544.                 return player_deaths_desc_online_cmp;
  1545.             else if (sort_method.CompareTo("skill_asc_online") == 0)
  1546.                 return player_skill_asc_online_cmp;
  1547.             else if (sort_method.CompareTo("skill_desc_online") == 0)
  1548.                 return player_skill_desc_online_cmp;
  1549.             else if (sort_method.CompareTo("quits_asc_online") == 0)
  1550.                 return player_quits_asc_online_cmp;
  1551.             else if (sort_method.CompareTo("quits_desc_online") == 0)
  1552.                 return player_quits_desc_online_cmp;
  1553.             else if (sort_method.CompareTo("accuracy_asc_online") == 0)
  1554.                 return player_accuracy_asc_online_cmp;
  1555.             else if (sort_method.CompareTo("accuracy_desc_online") == 0)
  1556.                 return player_accuracy_desc_online_cmp;
  1557.             else if (sort_method.CompareTo("score_asc_online") == 0)
  1558.                 return player_score_asc_online_cmp;
  1559.             else if (sort_method.CompareTo("score_desc_online") == 0)
  1560.                 return player_score_desc_online_cmp;
  1561.             else if (sort_method.CompareTo("rank_asc_online") == 0)
  1562.                 return player_rank_asc_online_cmp;
  1563.             else if (sort_method.CompareTo("rank_desc_online") == 0)
  1564.                 return player_rank_desc_online_cmp;
  1565.             else if (sort_method.CompareTo("random_value") == 0)
  1566.                 return player_random_value_cmp;
  1567.  
  1568.             ConsoleWrite("cannot find player sort method for ^b" + sort_method + "^0 during ^b" + phase + "^n, using default sort");
  1569.             return player_spm_asc_round_cmp;
  1570.         }
  1571.  
  1572.  
  1573.         private squad_sort_method getSquadSort(string phase)
  1574.         {
  1575.             string sort_method = getStringVarValue(phase);
  1576.  
  1577.             if (sort_method.CompareTo("kdr_asc_round") == 0)
  1578.                 return squad_kdr_asc_round_cmp;
  1579.             else if (sort_method.CompareTo("kdr_desc_round") == 0)
  1580.                 return squad_kdr_desc_round_cmp;
  1581.             else if (sort_method.CompareTo("score_asc_round") == 0)
  1582.                 return squad_score_asc_round_cmp;
  1583.             else if (sort_method.CompareTo("score_desc_round") == 0)
  1584.                 return squad_score_desc_round_cmp;
  1585.             else if (sort_method.CompareTo("spm_asc_round") == 0)
  1586.                 return squad_spm_asc_round_cmp;
  1587.             else if (sort_method.CompareTo("spm_desc_round") == 0)
  1588.                 return squad_spm_desc_round_cmp;
  1589.             else if (sort_method.CompareTo("kpm_asc_round") == 0)
  1590.                 return squad_kpm_asc_round_cmp;
  1591.             else if (sort_method.CompareTo("kpm_desc_round") == 0)
  1592.                 return squad_kpm_desc_round_cmp;
  1593.             else if (sort_method.CompareTo("time_asc_round") == 0)
  1594.                 return squad_time_asc_round_cmp;
  1595.             else if (sort_method.CompareTo("time_desc_round") == 0)
  1596.                 return squad_time_desc_round_cmp;
  1597.  
  1598.  
  1599.             else if (sort_method.CompareTo("kdr_asc_online") == 0)
  1600.                 return squad_kdr_asc_online_cmp;
  1601.             else if (sort_method.CompareTo("kdr_desc_online") == 0)
  1602.                 return squad_kdr_desc_online_cmp;
  1603.             else if (sort_method.CompareTo("kpm_asc_online") == 0)
  1604.                 return squad_kpm_asc_online_cmp;
  1605.             else if (sort_method.CompareTo("kpm_desc_online") == 0)
  1606.                 return squad_kpm_desc_online_cmp;
  1607.             else if (sort_method.CompareTo("spm_asc_online") == 0)
  1608.                 return squad_spm_asc_online_cmp;
  1609.             else if (sort_method.CompareTo("spm_desc_online") == 0)
  1610.                 return squad_spm_desc_online_cmp;
  1611.             else if (sort_method.CompareTo("kills_asc_online") == 0)
  1612.                 return squad_kills_asc_online_cmp;
  1613.             else if (sort_method.CompareTo("kills_desc_online") == 0)
  1614.                 return squad_kills_desc_online_cmp;
  1615.             else if (sort_method.CompareTo("deaths_asc_online") == 0)
  1616.                 return squad_deaths_asc_online_cmp;
  1617.             else if (sort_method.CompareTo("deaths_desc_online") == 0)
  1618.                 return squad_deaths_desc_online_cmp;
  1619.             else if (sort_method.CompareTo("skill_asc_online") == 0)
  1620.                 return squad_skill_asc_online_cmp;
  1621.             else if (sort_method.CompareTo("skill_desc_online") == 0)
  1622.                 return squad_skill_desc_online_cmp;
  1623.             else if (sort_method.CompareTo("quits_asc_online") == 0)
  1624.                 return squad_quits_asc_online_cmp;
  1625.             else if (sort_method.CompareTo("quits_desc_online") == 0)
  1626.                 return squad_quits_desc_online_cmp;
  1627.             else if (sort_method.CompareTo("accuracy_asc_online") == 0)
  1628.                 return squad_accuracy_asc_online_cmp;
  1629.             else if (sort_method.CompareTo("accuracy_desc_online") == 0)
  1630.                 return squad_accuracy_desc_online_cmp;
  1631.             else if (sort_method.CompareTo("score_asc_online") == 0)
  1632.                 return squad_score_asc_online_cmp;
  1633.             else if (sort_method.CompareTo("score_desc_online") == 0)
  1634.                 return squad_score_desc_online_cmp;
  1635.             else if (sort_method.CompareTo("rank_asc_online") == 0)
  1636.                 return squad_rank_asc_online_cmp;
  1637.             else if (sort_method.CompareTo("rank_desc_online") == 0)
  1638.                 return squad_rank_desc_online_cmp;
  1639.             else if (sort_method.CompareTo("random_value") == 0)
  1640.                 return squad_random_value_cmp;
  1641.  
  1642.             ConsoleWrite("cannot find squad sort method for ^b" + sort_method + "^0 during ^b" + phase + "^n, using default sort");
  1643.             return squad_kpm_desc_round_cmp;
  1644.         }
  1645.  
  1646.  
  1647.         /* squad comparison methods */
  1648.  
  1649.         private int squad_count_asc_cmp(PlayerSquad left, PlayerSquad right)
  1650.         {
  1651.             int lval = left.getCount();
  1652.             int rval = right.getCount();
  1653.  
  1654.             return lval.CompareTo(rval);
  1655.         }
  1656.  
  1657.         private int squad_count_desc_cmp(PlayerSquad left, PlayerSquad right)
  1658.         {
  1659.             return squad_count_asc_cmp(left, right) * (-1);
  1660.         }
  1661.  
  1662.  
  1663.         private int squad_kdr_asc_round_cmp(PlayerSquad left, PlayerSquad right)
  1664.         {
  1665.             double lval = left.getRoundKdr();
  1666.             double rval = right.getRoundKdr();
  1667.             return lval.CompareTo(rval);
  1668.         }
  1669.  
  1670.         private int squad_kdr_desc_round_cmp(PlayerSquad left, PlayerSquad right)
  1671.         {
  1672.             return squad_kdr_asc_round_cmp(left, right) * (-1);
  1673.         }
  1674.  
  1675.         private int squad_spm_asc_round_cmp(PlayerSquad left, PlayerSquad right)
  1676.         {
  1677.             double lval = left.getRoundSpm();
  1678.             double rval = right.getRoundSpm();
  1679.             return lval.CompareTo(rval);
  1680.         }
  1681.  
  1682.         private int squad_spm_desc_round_cmp(PlayerSquad left, PlayerSquad right)
  1683.         {
  1684.             return squad_spm_asc_round_cmp(left, right) * (-1);
  1685.         }
  1686.  
  1687.  
  1688.         private int squad_score_asc_round_cmp(PlayerSquad left, PlayerSquad right)
  1689.         {
  1690.             double lval = left.getRoundScore();
  1691.             double rval = right.getRoundScore();
  1692.             return lval.CompareTo(rval);
  1693.         }
  1694.  
  1695.         private int squad_score_desc_round_cmp(PlayerSquad left, PlayerSquad right)
  1696.         {
  1697.             return squad_score_asc_round_cmp(left, right) * (-1);
  1698.         }
  1699.  
  1700.         private int squad_kpm_asc_round_cmp(PlayerSquad left, PlayerSquad right)
  1701.         {
  1702.             double lval = left.getRoundKpm();
  1703.             double rval = right.getRoundKpm();
  1704.             return lval.CompareTo(rval);
  1705.         }
  1706.  
  1707.         private int squad_kpm_desc_round_cmp(PlayerSquad left, PlayerSquad right)
  1708.         {
  1709.             return squad_kpm_asc_round_cmp(left, right) * (-1);
  1710.         }
  1711.  
  1712.  
  1713.         private int squad_time_asc_round_cmp(PlayerSquad left, PlayerSquad right)
  1714.         {
  1715.             DateTime lval = left.getRoundTime();
  1716.             DateTime rval = right.getRoundTime();
  1717.             return lval.CompareTo(rval);
  1718.         }
  1719.  
  1720.         private int squad_time_desc_round_cmp(PlayerSquad left, PlayerSquad right)
  1721.         {
  1722.             return squad_time_asc_round_cmp(left, right) * (-1);
  1723.         }
  1724.  
  1725.  
  1726.         /* Squad sorting methods based on online stats */
  1727.  
  1728.         private int squad_kdr_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1729.         {
  1730.             double lval = left.getOnlineKdr();
  1731.             double rval = right.getOnlineKdr();
  1732.             return lval.CompareTo(rval);
  1733.         }
  1734.  
  1735.         private int squad_kdr_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1736.         {
  1737.             return squad_kdr_asc_online_cmp(left, right) * (-1);
  1738.         }
  1739.  
  1740.         private int squad_kpm_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1741.         {
  1742.             double lval = left.getOnlineKpm();
  1743.             double rval = right.getOnlineKpm();
  1744.             return lval.CompareTo(rval);
  1745.         }
  1746.  
  1747.         private int squad_kpm_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1748.         {
  1749.             return squad_kpm_asc_online_cmp(left, right) * (-1);
  1750.         }
  1751.  
  1752.  
  1753.         private int squad_spm_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1754.         {
  1755.             double lval = left.getOnlineSpm();
  1756.             double rval = right.getOnlineSpm();
  1757.             return lval.CompareTo(rval);
  1758.         }
  1759.  
  1760.         private int squad_spm_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1761.         {
  1762.             return squad_spm_asc_online_cmp(left, right) * (-1);
  1763.         }
  1764.  
  1765.         private int squad_kills_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1766.         {
  1767.             double lval = left.getOnlineKills();
  1768.             double rval = right.getOnlineKills();
  1769.             return lval.CompareTo(rval);
  1770.         }
  1771.  
  1772.         private int squad_kills_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1773.         {
  1774.             return squad_kills_asc_online_cmp(left, right) * (-1);
  1775.         }
  1776.  
  1777.         private int squad_deaths_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1778.         {
  1779.             double lval = left.getOnlineDeaths();
  1780.             double rval = right.getOnlineDeaths();
  1781.             return lval.CompareTo(rval);
  1782.         }
  1783.  
  1784.         private int squad_deaths_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1785.         {
  1786.             return squad_deaths_asc_online_cmp(left, right) * (-1);
  1787.         }
  1788.  
  1789.  
  1790.         private int squad_skill_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1791.         {
  1792.             double lval = left.getOnlineSkill();
  1793.             double rval = right.getOnlineSkill();
  1794.             return lval.CompareTo(rval);
  1795.         }
  1796.  
  1797.         private int squad_skill_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1798.         {
  1799.             return squad_skill_asc_online_cmp(left, right) * (-1);
  1800.         }
  1801.  
  1802.  
  1803.         private int squad_quits_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1804.         {
  1805.             double lval = left.getOnlineQuits();
  1806.             double rval = right.getOnlineQuits();
  1807.             return lval.CompareTo(rval);
  1808.         }
  1809.  
  1810.         private int squad_quits_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1811.         {
  1812.             return squad_quits_asc_online_cmp(left, right) * (-1);
  1813.         }
  1814.  
  1815.  
  1816.         private int squad_accuracy_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1817.         {
  1818.             double lval = left.getOnlineAccuracy();
  1819.             double rval = right.getOnlineAccuracy();
  1820.             return lval.CompareTo(rval);
  1821.         }
  1822.  
  1823.         private int squad_accuracy_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1824.         {
  1825.             return squad_accuracy_asc_online_cmp(left, right) * (-1);
  1826.         }
  1827.  
  1828.         private int squad_score_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1829.         {
  1830.             double lval = left.getOnlineScore();
  1831.             double rval = right.getOnlineScore();
  1832.             return lval.CompareTo(rval);
  1833.         }
  1834.  
  1835.         private int squad_score_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1836.         {
  1837.             return squad_score_asc_online_cmp(left, right) * (-1);
  1838.         }
  1839.  
  1840.         private int squad_rank_asc_online_cmp(PlayerSquad left, PlayerSquad right)
  1841.         {
  1842.             double lval = left.getOnlineRank();
  1843.             double rval = right.getOnlineRank();
  1844.             return lval.CompareTo(rval);
  1845.         }
  1846.  
  1847.         private int squad_rank_desc_online_cmp(PlayerSquad left, PlayerSquad right)
  1848.         {
  1849.             return squad_rank_asc_online_cmp(left, right) * (-1);
  1850.         }
  1851.  
  1852.  
  1853.         private int squad_random_value_cmp(PlayerSquad left, PlayerSquad right)
  1854.         {
  1855.             int lval = left.getRandomValue();
  1856.             int rval = right.getRandomValue();
  1857.             return lval.CompareTo(rval);
  1858.         }
  1859.  
  1860.  
  1861.  
  1862.  
  1863.         /* player comparison methods */
  1864.  
  1865.  
  1866.         private int player_kdr_asc_round_cmp(PlayerProfile left, PlayerProfile right)
  1867.         {
  1868.             double lval = left.getRoundKdr();
  1869.             double rval = right.getRoundKdr();
  1870.  
  1871.             return lval.CompareTo(rval);
  1872.         }
  1873.  
  1874.         private int player_kdr_desc_round_cmp(PlayerProfile left, PlayerProfile right)
  1875.         {
  1876.             return player_kdr_asc_round_cmp(left, right) * (-1);
  1877.         }
  1878.  
  1879.         private int player_spm_asc_round_cmp(PlayerProfile left, PlayerProfile right)
  1880.         {
  1881.             double lval = left.getRoundSpm();
  1882.             double rval = right.getRoundSpm();
  1883.             return lval.CompareTo(rval);
  1884.         }
  1885.  
  1886.         private int player_spm_desc_round_cmp(PlayerProfile left, PlayerProfile right)
  1887.         {
  1888.             return player_spm_asc_round_cmp(left, right) * (-1);
  1889.         }
  1890.  
  1891.  
  1892.         private int player_score_asc_round_cmp(PlayerProfile left, PlayerProfile right)
  1893.         {
  1894.             double lval = left.getRoundScore();
  1895.             double rval = right.getRoundScore();
  1896.             return lval.CompareTo(rval);
  1897.         }
  1898.  
  1899.         private int player_score_desc_round_cmp(PlayerProfile left, PlayerProfile right)
  1900.         {
  1901.             return player_score_asc_round_cmp(left, right) * (-1);
  1902.         }
  1903.  
  1904.         private int player_kpm_asc_round_cmp(PlayerProfile left, PlayerProfile right)
  1905.         {
  1906.             double lval = left.getRoundKpm();
  1907.             double rval = right.getRoundKpm();
  1908.             return lval.CompareTo(rval);
  1909.         }
  1910.  
  1911.         private int player_kpm_desc_round_cmp(PlayerProfile left, PlayerProfile right)
  1912.         {
  1913.             return player_kpm_asc_round_cmp(left, right) * (-1);
  1914.         }
  1915.  
  1916.  
  1917.         private int player_time_asc_round_cmp(PlayerProfile left, PlayerProfile right)
  1918.         {
  1919.             DateTime lval = left.getRoundTime();
  1920.             DateTime rval = right.getRoundTime();
  1921.             return lval.CompareTo(rval);
  1922.         }
  1923.  
  1924.         private int player_time_desc_round_cmp(PlayerProfile left, PlayerProfile right)
  1925.         {
  1926.             return player_time_asc_round_cmp(left, right) * (-1);
  1927.         }
  1928.  
  1929.  
  1930.         /* Player sort methodsd based on online stats */
  1931.         private int player_kdr_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  1932.         {
  1933.             double lval = left.getOnlineKdr();
  1934.             double rval = right.getOnlineKdr();
  1935.             return lval.CompareTo(rval);
  1936.         }
  1937.  
  1938.         private int player_kdr_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  1939.         {
  1940.             return player_kdr_asc_online_cmp(left, right) * (-1);
  1941.         }
  1942.  
  1943.         private int player_kpm_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  1944.         {
  1945.             double lval = left.getOnlineKpm();
  1946.             double rval = right.getOnlineKpm();
  1947.             return lval.CompareTo(rval);
  1948.         }
  1949.  
  1950.         private int player_kpm_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  1951.         {
  1952.             return player_kpm_asc_online_cmp(left, right) * (-1);
  1953.         }
  1954.  
  1955.  
  1956.         private int player_spm_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  1957.         {
  1958.             double lval = left.getOnlineSpm();
  1959.             double rval = right.getOnlineSpm();
  1960.             return lval.CompareTo(rval);
  1961.         }
  1962.  
  1963.         private int player_spm_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  1964.         {
  1965.             return player_spm_asc_online_cmp(left, right) * (-1);
  1966.         }
  1967.  
  1968.         private int player_kills_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  1969.         {
  1970.             double lval = left.getOnlineKills();
  1971.             double rval = right.getOnlineKills();
  1972.             return lval.CompareTo(rval);
  1973.         }
  1974.  
  1975.         private int player_kills_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  1976.         {
  1977.             return player_kills_asc_online_cmp(left, right) * (-1);
  1978.         }
  1979.  
  1980.         private int player_deaths_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  1981.         {
  1982.             double lval = left.getOnlineDeaths();
  1983.             double rval = right.getOnlineDeaths();
  1984.             return lval.CompareTo(rval);
  1985.         }
  1986.  
  1987.         private int player_deaths_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  1988.         {
  1989.             return player_deaths_asc_online_cmp(left, right) * (-1);
  1990.         }
  1991.  
  1992.         private int player_skill_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  1993.         {
  1994.             double lval = left.getOnlineSkill();
  1995.             double rval = right.getOnlineSkill();
  1996.             return lval.CompareTo(rval);
  1997.         }
  1998.  
  1999.         private int player_skill_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  2000.         {
  2001.             return player_skill_asc_online_cmp(left, right) * (-1);
  2002.         }
  2003.  
  2004.  
  2005.         private int player_quits_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  2006.         {
  2007.             double lval = left.getOnlineQuits();
  2008.             double rval = right.getOnlineQuits();
  2009.             return lval.CompareTo(rval);
  2010.         }
  2011.  
  2012.         private int player_quits_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  2013.         {
  2014.             return player_quits_asc_online_cmp(left, right) * (-1);
  2015.         }
  2016.  
  2017.  
  2018.         private int player_accuracy_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  2019.         {
  2020.             double lval = left.getOnlineAccuracy();
  2021.             double rval = right.getOnlineAccuracy();
  2022.             return lval.CompareTo(rval);
  2023.         }
  2024.  
  2025.         private int player_accuracy_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  2026.         {
  2027.             return player_accuracy_asc_online_cmp(left, right) * (-1);
  2028.         }
  2029.  
  2030.         private int player_score_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  2031.         {
  2032.             double lval = left.getOnlineScore();
  2033.             double rval = right.getOnlineScore();
  2034.             return lval.CompareTo(rval);
  2035.         }
  2036.  
  2037.         private int player_score_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  2038.         {
  2039.             return player_score_asc_online_cmp(left, right) * (-1);
  2040.         }
  2041.  
  2042.         private int player_rank_asc_online_cmp(PlayerProfile left, PlayerProfile right)
  2043.         {
  2044.             double lval = left.getOnlineRank();
  2045.             double rval = right.getOnlineRank();
  2046.             return lval.CompareTo(rval);
  2047.         }
  2048.  
  2049.         private int player_rank_desc_online_cmp(PlayerProfile left, PlayerProfile right)
  2050.         {
  2051.             return player_rank_asc_online_cmp(left, right) * (-1);
  2052.         }
  2053.  
  2054.  
  2055.         private int player_random_value_cmp(PlayerProfile left, PlayerProfile right)
  2056.         {
  2057.             int lval = left.getRandomValue();
  2058.             int rval = right.getRandomValue();
  2059.             return lval.CompareTo(rval);
  2060.         }
  2061.  
  2062.  
  2063.         public void unloadSettings()
  2064.         {
  2065.             this.players.Clear();
  2066.             removeTask("InsaneBalancer");
  2067.             attempts = 0;
  2068.         }
  2069.  
  2070.  
  2071.  
  2072.         public string GetPluginName()
  2073.         {
  2074.             return "Insane Balancer";
  2075.         }
  2076.  
  2077.         public string GetPluginVersion()
  2078.         {
  2079.             return "0.0.0.6-patch-4";
  2080.         }
  2081.  
  2082.         public string GetPluginAuthor()
  2083.         {
  2084.             return "micovery";
  2085.         }
  2086.  
  2087.         public string GetPluginWebsite()
  2088.         {
  2089.             return "www.insanegamersasylum.com";
  2090.         }
  2091.  
  2092.  
  2093.         public string GetPluginDescription()
  2094.         {
  2095.             return @"
  2096.        <h2>Description</h2>
  2097.        <p> This is the draft impelementation for a flexible team balancer, which can balance teams by skill, rank, score, kdr and other rules.
  2098.            All of it, while doing best effort to maintain squads together, and clans on the same team.
  2099.        </p>
  2100.  
  2101.        <h2>Sort Methods</h2>
  2102.        <p> A sort method is a rule used for sorting a list of players or squads. The following balancing methods are supported:
  2103.        </p>
  2104.        <ul>
  2105.            
  2106.            <li><b>kpm_asc_round</b> , <b>kpm_desc_round</b> <br />
  2107.             Sorting based on the soldier round kills per minute
  2108.            </li>
  2109.            <li><b>spm_asc_round</b> , <b>spm_desc_round</b> <br />
  2110.             Sorting based on the soldier round score per minute
  2111.            </li>
  2112.            <li><b>kdr_asc_round</b> , <b>kdr_desc_round</b> <br />
  2113.             Sorting based on the soldier round kill to death ratio  
  2114.            </li>
  2115.          
  2116.            <li><b>score_asc_round</b> , <b>score_desc_round</b> <br />
  2117.             Sorting based on the soldier round score  
  2118.            </li>
  2119.            <li><b>time_asc_round</b> , <b>time_desc_round</b> <br />
  2120.             Sorting based on the time the player joined the server.
  2121.            </li>
  2122.  
  2123.            <li><b>kdr_asc_online</b> , <b>kdr_desc_online</b> <br />
  2124.             Sorting based on the soldier online kill to death ratio
  2125.            </li>
  2126.            <li><b>kpm_asc_online</b> , <b>kpm_asc_online</b> <br />
  2127.             Sorting based on the soldier online kills per minute
  2128.            </li>
  2129.            <li><b>spm_asc_online</b> , <b>spm_desc_online</b> <br />
  2130.             Sorting based on the soldier online score per minute  
  2131.            </li>
  2132.            <li><b>kills_asc_online</b> , <b>kills_desc_online</b> <br />
  2133.             Sorting based on the soldier online kills  
  2134.            </li>
  2135.            <li><b>deaths_asc_online</b> , <b>deaths_desc_online</b> <br />
  2136.             Sorting based on the soldier online deaths
  2137.            </li>
  2138.            <li><b>skill_asc_online</b> , <b>skill_desc_online</b> <br />
  2139.             Sorting based on the soldier online skill statistic
  2140.            </li>
  2141.            <li><b>quits_asc_online</b> , <b>quits_desc_online</b> <br />
  2142.             Sorting based on the soldier online quit percentage
  2143.             (a round not completed is counted as a quit)
  2144.            </li>
  2145.            <li><b>accuracy_asc_online</b> , <b>accuracy_desc_online</b> <br />
  2146.             Sorting based on the soldier online accuracy
  2147.            </li>
  2148.            <li><b>score_asc_online</b> , <b>score_desc_online</b> <br />
  2149.             Sorting based on the soldier online total score
  2150.            </li>
  2151.            </li>
  2152.            <li><b>rank_asc_online</b> , <b>rank_desc_online</b> <br />
  2153.             Sorting based on the soldier online rank
  2154.            </li>
  2155.            <li><b>random_value</b><br />
  2156.             Sorting is based on random values assigned to the players.<br />
  2157.             <br />
  2158.             Each player gets assigned a random value. Then, the list of players is sorted/ordered by those random values.<br />
  2159.             That way you end up with a random permutation of the players list for shuffling.
  2160.            </li>
  2161.  
  2162.        </ul>
  2163.  
  2164.            All the data for sorting rules ending in <b>_round</b> is obtained from the previous or current round statistics.<br />
  2165.            <br />
  2166.            All the data for sorting rules ending in <b>_online</b> is obtained from from the battlelog.battlefield.com website.<br />
  2167.            <br />
  2168.            <br />
  2169.            The substrings <b>asc</b>, and <b>desc</b> mean ascending, and descending respectively. This is used for the sorting order.
  2170.      
  2171.        <h2>Live Balancing Logic</h2>
  2172.                          
  2173.        <blockquote>
  2174.        Insane Balancer tries to be as un-intrusive as posible while balancing a game that is in progress.
  2175.        If the teams become un-balanced while the game is in progess it will create two pools of players and sort them.
  2176.        (players chosen from the bigger team) One pool for players who are not in any squad, and another pool for squads.
  2177.        First it will chose the player at the top of the no-squad pool and move it to the other team until teams are balanced.
  2178.        If the no-squad pool becomes empty (and teams are still unbalanced) then squad at the top of the squad pool is moved
  2179.        to the other team if the number of players needed is greater than or equal to the size of the squad. If the number of players
  2180.        needed is less than the size of the top squad, a random random player is chosen from the squad and moved to the opposite team
  2181.        until teams are balanced. (players that were on the same squad are kept together)
  2182.        </blockquote>
  2183.  
  2184.        
  2185.        <h2>Round Re-balancing Logic</h2>
  2186.                          
  2187.        <blockquote>
  2188.        If end of round balancing is enabled, Insane Balancer will completely re-sort teams, even if they are already balanced.
  2189.        The logic for the re-sort is as follows. Create two pools of players and sort them (choosing players from all teams).
  2190.        One pool for players who are not in squads, and another for players who are in squads. Then, move all all players and squads
  2191.        to the neutral team, in order to end up with two empty non-neutral teams. Then, pick the squad at the top of the squad pool,
  2192.        and move it to the losing team. Then pick the next squad on top of the squad pool, and move it to the team with the least players, and so on.
  2193.        Once the squad pool is empty, pick the player on top of the no-squad pool, and move it to the team with least players, and so on.
  2194.        If teams are still unbalanced after the no-squad pool is empty, then the live balancing logic is applied.<br />
  2195.        <br />
  2196.        This is a high-level explanation explanation of the round-balancing logic algorithm. It does not mean that players are actually moved to
  2197.        the neutral team. This is not possible anymore with Battlefield 3. Instead, it's done virtually with 3 arrays of players to represent
  2198.        the three teams: Neutral, US, and RU. When all the calculations are done, and the plugin has determined the target teams and squad for each
  2199.        player ... it will then swap players between RU, and US one at a time.<br />
  2200.        <br />
  2201.        Note that if the server is full, round-balacing logic will not be applied. It's not possible to move players when both teams are full.
  2202.        Also note that, it can happen that the server becomes full while the round-balancing is being applied. When this happens, the admin.movePlayer
  2203.        commands may fail. This was not a problem in Battlefield Bad Company 2, because there was a spare/neutral team that players could be moved to
  2204.        when balancing a full server.
  2205.      
  2206.        </blockquote>
  2207.  
  2208.        
  2209.        <h2>Balanced Team Determination </h2>
  2210.        
  2211.        <blockquote>
  2212.        Teams are determined to be balanced if the difference in number of players between teams is less than or equal to the <b>balance_threshold</b>.
  2213.        The <b>balance_threshold</b> has to be a number greater than 0. If the total number of players in the server is less than or equal to the
  2214.        <b>balance_threshold</b>, then the user set threshold is ignored, and a value of 1 is used instead. Technically, no team should ever be bigger
  2215.        than the other by more than the value of <b>balance_threshold</b>.
  2216.        </blockquote>
  2217.  
  2218.        <h2>Keeping Squads Together </h2>
  2219.  
  2220.        <blockquote>
  2221.        Insane Balancer is coded to keep squads together by default. However, you can set <b>keep_squads</b> to false and both round-balancing and
  2222.        live-balancing logics are changed a bit. What happens, is that all squads in the squad pool are broken, and players put into the no-squad pool,
  2223.        before balancing starts.<br />
  2224.        <br />
  2225.        Note that for live-balancing, players are not actually moved out of the squad. (it would kill all players if you do that).
  2226.        They are just treated as if they were not in a squad. Also, If <b>keep_squads</b> is enabled, clan-squads will not be broken.
  2227.        </blockquote>
  2228.  
  2229.        <h2> Keeping Clans On Same Team (requires Battlelog credentials) </h2>  
  2230.  
  2231.        <blockquote>                                                                        
  2232.        During end-of round re-balancing, if <b>keep_clans</b> is enabled, players with the same clan tag are be removed from their current squad,
  2233.        and put into exclusive squads. These special clan squads are given priority over non-clan squads, so that clan-squads end up in the same team.
  2234.        Note that when <b>keep_clans</b> is enabled, teams may end up unbalanced in number, so the live-balancing logic may still need to be applied.<br />
  2235.        <br />
  2236.        During live-balancing, if <b>keep_clans</b> is enabled, players with clan tags are given priority, as long as there is at least two members of
  2237.        the same clan in the server. When picking players to move to the other team, if a player has a clan tag, the player will be automatically skipped,
  2238.        if the majority of the clan is in the same team (otherwise the player is moved to the other team to join his clan buddies).
  2239.        If at the end of live-balancing phase, teams are still unbalanced, then <b>keep_clans</b> is disabled temporarily, and the live-balancer logic is applied again.
  2240.        </blockquote>        
  2241.  
  2242.        <h2>Settings</h2>
  2243.        <ol>
  2244.          <li><blockquote><b>balance_threshold</b><br />
  2245.                <i>(integer > 0)</i> -  maximum difference in team sizes before teams are considered unbalanced <br />
  2246.                Technically, no team will ever be bigger by more than the <b>balance_threshold</b>
  2247.                </blockquote>
  2248.          </li>
  2249.          <li><blockquote><b>live_interval_time</b><br />
  2250.                <i>(integer > 0)</i> - interval number of seconds at which team balance is checked during game  <br />
  2251.                </blockquote>
  2252.          </li>
  2253.          <li><blockquote><b>round_interval</b><br />
  2254.                <i>(integer > 0)</i> - interval number number of rounds at which the round balancer is applied  <br />
  2255.                For example, if map Atacama has 6 rounds, and the value of <b>round_interval</b> is 2, then the round
  2256.                balancer is run at the end of rounds 2, 4, and 6. <br />
  2257.                <br />
  2258.                This value is used for all maps, unless you provide a per-map interval value. <br />
  2259.                Per-map round interval values can be set in the ""Round Interval"" section of the plugin.<br />
  2260.                There you will see a list of maps, and game modes supported by BF3. <br />
  2261.  
  2262.                <br />
  2263.                The following prefixes are used to identify game modes:<br />
  2264.                <br />
  2265.                <ul>
  2266.                   <li><i>cl</i> - conquest large </li>
  2267.                   <li><i>cs</i> - conquest small </li>
  2268.                   <li><i>csa</i> - conquest small assault (only for BF3 Back to Karkand Maps, except Wake Island) </li>
  2269.                   <li><i>rl</i> - rush large </li>
  2270.                   <li><i>sr</i> - squad rush </li>
  2271.                   <li><i>td</i> - team death-match </li>
  2272.                </ul>
  2273.                </blockquote>
  2274.          </li>
  2275.          <li><blockquote><b>round_wait_time</b><br />
  2276.                <i>(integer > 0)</i> - number of seconds to wait after round-over event, before activating the round-end balancer <br />
  2277.                </blockquote>
  2278.          </li>
  2279.          <li><blockquote><strong>keep_squads_live</strong><br />
  2280.                <i>true</i> - squads are preseved during live balancing <br />
  2281.                <i>false</i> - squads are intentionally broken up during live balancing
  2282.                </blockquote>
  2283.          </li>
  2284.          <li><blockquote><strong>keep_squads_round</strong><br />
  2285.                <i>true</i> - squads are preseved during end of round balancing <br />
  2286.                <i>false</i> - squads are intentionally broken up during end of round balancing
  2287.                </blockquote>
  2288.          </li>
  2289.          <li><blockquote><strong>keep_clans_round</strong><br />
  2290.                <i>true</i> - players with same clan tags are kept on the same team during end of round balancing <br />
  2291.                <i>false</i> - clan tags are ignored during end of round balancing
  2292.                </blockquote>
  2293.          </li>
  2294.          <li><blockquote><strong>keep_clans_live</strong><br />
  2295.                <i>true</i> - players with same clan tags are kept on the same team during live balancing <br />
  2296.                <i>false</i> - clan tags are ignored during live balancing
  2297.                </blockquote>
  2298.          </li>
  2299.          <li><blockquote><strong>warn_say</strong><br />
  2300.                <i>true</i> - send auto-balancer warning in chat <br />
  2301.                <i>false</i> - do not say the auto-balancer warning in chat
  2302.                </blockquote>
  2303.          </li>
  2304.          <li><blockquote><strong>balance_round</strong><br />
  2305.                <i>true</i> - enables the end of round balancer<br />
  2306.                <i>false</i> - disabled the end of round balancer
  2307.                </blockquote>
  2308.          </li>
  2309.          <li><blockquote><strong>balance_live</strong><br />
  2310.                <i>true</i> - enables the live balancer<br />
  2311.                <i>false</i> - disables the live balancer
  2312.                </blockquote>
  2313.          </li>
  2314.          <li><blockquote><strong>admin_list</strong><br />
  2315.                <i>(string, csv)</i> - list of players who are allow to execute admin commands      
  2316.                </blockquote>
  2317.           </li>        
  2318.           <li><blockquote><strong>round_sort</strong><br />
  2319.                <i>(string)</i> - method used for sorting players and squads during end of round balancing      
  2320.                </blockquote>
  2321.           </li>
  2322.           <li><blockquote><strong>live_sort</strong><br />
  2323.                <i>(string)</i> - method used for sorting players and squads during live balancing      
  2324.                </blockquote>
  2325.           </li>
  2326.  
  2327.           <li><blockquote><strong>player_safe_wlist</strong><br />
  2328.                <i>(string, csv)</i> - list of players that should never be moved or kicked (by the idle kicker)    
  2329.                </blockquote>
  2330.           </li>
  2331.  
  2332.           <li><blockquote><strong>clan_safe_wlist</strong><br />
  2333.                <i>(string, csv)</i> - list of clan (tags) for players that should never be moved or kicked (by the idle kicker)  
  2334.                </blockquote>
  2335.           </li>
  2336.  
  2337.           <li><blockquote><strong>use_extra_white_lists</strong><br />
  2338.                <i>true</i> - enables and shows the extra white-lists<br />
  2339.                <i>false</i> - disables and hides the extra white-lists  
  2340.                </blockquote>
  2341.           </li>
  2342.           <li><blockquote><strong>player_move_wlist</strong><br />
  2343.                <i>(string, csv)</i> - list of players that should never be moved    
  2344.                </blockquote>
  2345.           </li>
  2346.  
  2347.            <li><blockquote><strong>player_kick_wlist</strong><br />
  2348.                <i>(string, csv)</i> - list of players that should never be kicked (by the idle kicker)  
  2349.                </blockquote>
  2350.           </li>
  2351.  
  2352.           <li><blockquote><strong>clan_kick_wlist</strong><br />
  2353.                <i>(string, csv)</i> - list of clan (tags) for players that should never be kicked by the idle kicker
  2354.                </blockquote>
  2355.           </li>
  2356.           <li><blockquote><strong>clan_move_wlist</strong><br />
  2357.                <i>(string, csv)</i> - list of clan (tags) for players that should never be moved    
  2358.                </blockquote>
  2359.           </li>
  2360.  
  2361.           <li><blockquote><strong>clan_kick_wlist</strong><br />
  2362.                <i>(string, csv)</i> - list of clan (tags) for players that should never be kicked  
  2363.                </blockquote>
  2364.           </li>
  2365.          <li><blockquote><strong>kick_idle</strong><br />
  2366.                <i>true</i> - enables the idle kicking feature for the round-end balancer<br />
  2367.                <i>false</i> - disables the idle kicking feature for the round-end balancer <br />
  2368.               <br />
  2369.              If the server is full when the round ends, it is not possible to apply the round-end balancing logic.<br />
  2370.              This is where the idle kicker can be useful. If enabled, the idle-kicker will kick one idle player from each team. <br />
  2371.              This allows the round-end balancing logic to proceed. <br />
  2372.              <br />
  2373.              The way it determines if a player is idle, is by keeping track of the time since the last player activity. <br />
  2374.              There are five indicators of activity that the plugin keeps track of. These are: <b>chat</b>, <b>kills</b>, <b>deaths</b>, <b>spawn</b>, and <b>score</b>. <br />
  2375.              <br />
  2376.              For a player to be considered idle, all five activity indicators must be true. If any of the indicators is not true, then the player is not considered idle.<br />
  2377.          </blockquote>
  2378.          </li>
  2379.           <li><blockquote><strong>last_chat_time</strong><br />
  2380.                <i>(integer >= 0)</i> - This is the number of seconds for the idle-kicker chat activity indicator
  2381.                </blockquote>
  2382.           </li>
  2383.           <li><blockquote><strong>last_kill_time</strong><br />
  2384.                <i>(integer >= 0)</i> - This is the number of seconds for the idle-kicker kill activity indicator
  2385.                </blockquote>
  2386.           </li>
  2387.           <li><blockquote><strong>last_death_time</strong><br />
  2388.                <i>(integer >= 0)</i> - This is the number of seconds for the idle-kicker death activity indicator
  2389.                </blockquote>
  2390.           </li>
  2391.           <li><blockquote><strong>last_spawn_time</strong><br />
  2392.                <i>(integer >= 0)</i> - This is the number of seconds for the idle-kicker spawn activity indicator
  2393.                </blockquote>
  2394.           </li>
  2395.           <li><blockquote><strong>last_score_time</strong><br />
  2396.                <i>(integer >= 0)</i> - This is the number of seconds for the idle-kicker score activity indicator
  2397.                </blockquote>
  2398.           </li>
  2399.           <li><blockquote><strong>ticket_threshold</strong><br />
  2400.                <i>(integer >= 0)</i> - When the number of tickets in either team goes below this value, live balancer stops working.<br />
  2401.                <br />
  2402.                For Team-Deathmatch mode, the behavior is reversed. <br />
  2403.                For example, if the maximum number of tickets is 100, and the ticket threshold is 20, then<br />
  2404.                the live balancer will stop working whenever either team reaches goes over 80 tickets.<br />
  2405.                </blockquote>
  2406.           </li>
  2407.           <li><blockquote><strong>wait_death</strong><br />
  2408.                <i>true</i> - enables the wait death algorithm. Players are not killed by live balancer. They are moved when they die.<br />
  2409.                <i>false</i> - disables the wait death algorithm. Players may be killed and and moved while alive, for balancing purposes.<br />
  2410.            </blockquote>
  2411.           </li>
  2412.           <li><blockquote><strong>wait_death_count</strong><br />
  2413.                <i>(integer > 0)</i> - Specifies how big to make the candidates list for moving when <b>wait_death</b> is enabled. <br />
  2414.                <br />
  2415.                By default this is set to <b>6</b> players, but you may change it as you wish depending on your server size. <br />
  2416.                The reason this is needed is because even though only one player may be needed to achieve balance,<br />
  2417.                that player may take too long to die, and thus the teams will stay un-balanced much longer. <br />
  2418.                <br />
  2419.                With the candidate list set to <b>6</b>, it means that 6 players will be chosen from the top of the list<br />
  2420.                and marked as possible candidates to be moved.
  2421.                </blockquote>
  2422.           </li>
  2423.           <li><blockquote><strong>console</strong><br />
  2424.                <i>(string)</i> - here you can use to test the in-game commands from within procon. </br>
  2425.                For example: ""!show round stats"" will print the player statistic for the current round in the plugin console.    
  2426.                </blockquote>
  2427.           </li>
  2428.        </ol>
  2429.  
  2430.        <h2>Public In-Game Commands</h2>
  2431.        <p>
  2432.            In-game commands are messages typed into the game chat box, which have special meaning to the plugin.
  2433.            Commands must start with one of the following characters: !,@, or /. This plugin interprets the following commands:
  2434.        </p>
  2435.        <ul>
  2436.           <li><blockquote><strong>!move</strong><br />
  2437.               This command can be used by regular players to move themselves to the opposite team as long as teams are balanced.
  2438.               </blockquote>
  2439.           </li>
  2440.        </ul>
  2441.       <h2> Admin In-Game Commands</h2>
  2442.        <p>
  2443.            These are the commands that only soldiers in the ""admin_list"" are allowed to execute. Reply messages generated by admin commands
  2444.            are sent only to the admin who executed the command.
  2445.        </p>
  2446.        <ul>
  2447.           <li><blockquote><strong>!start check</strong><br />
  2448.               This command puts the live balancer in started state, so that it periodically (every <b>live_interval_time</b> seconds) checks the teams for balance. <br />
  2449.               When this command is run <b>balance_live</b> is implicitly set to true.
  2450.               </blockquote>
  2451.           </li>
  2452.           <li><blockquote><strong>!stop check</strong><br />
  2453.                This command puts the live balancer in stopped state.
  2454.                When this command is run <b>balance_live</b> is implicitly set to false.
  2455.               </blockquote>
  2456.           </li>
  2457.           <li><blockquote><strong>!show round stats [player-name]</strong><br />
  2458.                This command is used for showing the player statistics for the current round.
  2459.                The name of the player is optional. If you do not provide a player name, it will print statistics for all players.
  2460.               </blockquote>
  2461.           </li>
  2462.           <li><blockquote><strong>!show online stats [player-name]</strong><br />
  2463.                This command is used for showing the player online (battlelog) statistics.
  2464.                The name of the player is optional. If you do not provide a player name, it will print statistics for all players.
  2465.               </blockquote>
  2466.           </li>
  2467.           <li><blockquote><strong>!show idle</strong><br />
  2468.                Prints a list of the players that the plugin considers idle, based on the indicators of activity.<br />
  2469.                 <br />
  2470.                 <ul>
  2471.                 <li><b>last_chat_time</b><br /></li>
  2472.                 <li><b>last_kill_time</b><br /></li>
  2473.                 <li><b>last_death_time</b><br /></li>
  2474.                 <li><b>last_spawn_time</b><br /></li>
  2475.                 <li><b>last_score_time</b><br /></li>
  2476.                 </ul>
  2477.                 <br />
  2478.               </blockquote>
  2479.           </li>
  2480.          <li><blockquote><strong>!wlist_info {player-name}</strong><br />
  2481.                This command is used for testing/checking what white-lists a player is in, e.g.:
  2482.                 <br />
  2483.                 <br />
  2484.                 !wlist_info micovery
  2485.                 <br />
  2486.                 player_safe_wlist = False<br />
  2487.                 clan_safe_wlist = False<br />
  2488.                 player_kick_wlist = <b>True</b><br />
  2489.                 player_move_wlist = False<br />
  2490.                 clan_kick_wlist = False<br />
  2491.                 clan_move_wlist = False<br />
  2492.                 <br />
  2493.                 <br />
  2494.                 In the example above, the player is only in the ""player_kick_wlist"".
  2495.                 If the player is not in-gmae, you will not be able to see white-list information.
  2496.                
  2497.               </blockquote>
  2498.           </li>
  2499.           <li><blockquote><strong>!balance live</strong><br />
  2500.               This command forces the live balancing logic to be applied whithout any warning period or countdown.
  2501.               </blockquote>
  2502.           </li>
  2503.          <li><blockquote><strong>!balance round</strong><br />
  2504.               This command forces the round balancing logic to be applied whithout any warning period or countdown.
  2505.               </blockquote>
  2506.           </li>
  2507.           <li><blockquote>
  2508.                <strong>1. !set {variable} {to|=} {value}</strong><br />
  2509.                <strong>2. !set {variable} {value}</strong><br />      
  2510.                <strong>3. !set {variable}</strong><br />  
  2511.                This command is used for setting the value of this plugin's variables.<br />
  2512.                For the 2nd invocation syntax you cannot use ""="" or ""to"" as the variable value. <br />
  2513.                For the 3rd invocation syntax the value is assumed to be ""true"".
  2514.               </blockquote>
  2515.           </li>
  2516.           <li><blockquote>
  2517.                <strong>!get {variable} </strong><br />
  2518.                This command prints the value of the specified variable.
  2519.               </blockquote>
  2520.           </li>
  2521.         </ul>
  2522.        ";
  2523.         }
  2524.  
  2525.         public void OnPluginLoaded(string strHostName, string strPort, string strPRoConVersion)
  2526.         {
  2527.             ConsoleWrite("plugin loaded");
  2528.             this.RegisterEvents("OnPlayerJoin",
  2529.                                 "OnPlayerKilled",
  2530.                                 "OnPlayerSpawned",
  2531.                                 "OnPlayerLeft",
  2532.                                 "OnGlobalChat",
  2533.                                 "OnTeamChat",
  2534.                                 "OnSquadChat",
  2535.                                 "OnLevelStarted",
  2536.                                 "OnPunkbusterplayerStatsCmd",
  2537.                                 "OnServerInfo",
  2538.                                 "OnPlayerTeamChange",
  2539.                                 "OnPlayerMovedByAdmin",
  2540.                                 "OnPlayerKickedByAdmin",
  2541.                                 "OnPlayerSquadChange",
  2542.                                 "OnplayersStatsCmd",
  2543.                                 "OnRoundOver");
  2544.         }
  2545.  
  2546.         public void OnPluginEnable()
  2547.         {
  2548.  
  2549.  
  2550.             ConsoleWrite("^b^2Enabled!^0");
  2551.  
  2552.             plugin_enabled = true;
  2553.  
  2554.             unloadSettings();
  2555.             loadSettings();
  2556.  
  2557.  
  2558.             addPluginCallTask("InsaneBalancer", "ticks", 0, 1, -1);
  2559.             initializeBalancer();
  2560.         }
  2561.  
  2562.  
  2563.  
  2564.         public void addPluginCallTask(string task, string method, int delay, int interval, int repeat)
  2565.         {
  2566.             this.ExecuteCommand("procon.protected.tasks.add", task, delay.ToString(), interval.ToString(), repeat.ToString(), "procon.protected.plugins.call", "InsaneBalancer", method);
  2567.         }
  2568.  
  2569.         public void removeTask(string task)
  2570.         {
  2571.             this.ExecuteCommand("procon.protected.tasks.remove", task);
  2572.         }
  2573.  
  2574.         public int getElapsedTime(DateTime now, PluginState state)
  2575.         {
  2576.             DateTime startTime = getStartTime(state);
  2577.             int elapsed = (int)now.Subtract(startTime).TotalSeconds;
  2578.             return elapsed;
  2579.         }
  2580.  
  2581.         public DateTime getStartTime(PluginState state)
  2582.         {
  2583.             if (state.Equals(PluginState.wait))
  2584.                 return startWaitTime;
  2585.             else if (state.Equals(PluginState.warn))
  2586.                 return startWarnTime;
  2587.             else if (state.Equals(PluginState.check))
  2588.                 return startCheckTime;
  2589.             else if (state.Equals(PluginState.balance))
  2590.                 return startBalanceTime;
  2591.             else if (state.Equals(PluginState.stop))
  2592.                 return startStopTime;
  2593.             else
  2594.                 ConsoleWrite("^1^bWARNING^0^n: cannot find start time for ^b" + state.ToString() + "^n^0");
  2595.  
  2596.             return utc;
  2597.         }
  2598.  
  2599.         public void setStartTime(PluginState state, DateTime now)
  2600.         {
  2601.             if (state.Equals(PluginState.wait))
  2602.                 startWaitTime = now;
  2603.             else if (state.Equals(PluginState.warn))
  2604.                 startWarnTime = now;
  2605.             else if (state.Equals(PluginState.check))
  2606.                 startCheckTime = now;
  2607.             else if (state.Equals(PluginState.balance))
  2608.                 startBalanceTime = now;
  2609.             else if (state.Equals(PluginState.stop))
  2610.                 startStopTime = now;
  2611.             else
  2612.                 ConsoleWrite("^1^bWARNING^0^n: cannot set start time for ^b" + state.ToString() + "^n^0");
  2613.         }
  2614.  
  2615.  
  2616.         public int getMaxTime(PluginState state)
  2617.         {
  2618.             if (state.Equals(PluginState.wait))
  2619.                 return getIntegerVarValue("live_interval_time");
  2620.             else if (state.Equals(PluginState.warn))
  2621.                 return getIntegerVarValue("warn_msg_total_time");
  2622.             /*else
  2623.                 DebugWrite("^1Getting max time for ^b" + state.ToString() + "^n state is not valid", 6);
  2624.             */
  2625.  
  2626.             return getElapsedTime(utc, PluginState.check);
  2627.         }
  2628.  
  2629.         public int getRemainingTime(DateTime now, PluginState state)
  2630.         {
  2631.  
  2632.             int max_time = getMaxTime(state);
  2633.             int elapsed = getElapsedTime(now, state);
  2634.  
  2635.             int remain = max_time - elapsed;
  2636.             return remain;
  2637.         }
  2638.  
  2639.  
  2640.         public void ExecCommand(params string[] args)
  2641.         {
  2642.             List<string> list = new List<string>();
  2643.             list.Add("procon.protected.send");
  2644.             list.AddRange(args);
  2645.             this.ExecuteCommand(list.ToArray());
  2646.         }
  2647.  
  2648.  
  2649.  
  2650.         public void getPlayerList()
  2651.         {
  2652.             ExecCommand("admin.listPlayers", "all");
  2653.             ExecCommand("serverInfo");
  2654.             ExecCommand("punkBuster.pb_sv_command", "pb_sv_plist");
  2655.         }
  2656.  
  2657.         public void getServerInfo()
  2658.         {
  2659.             ExecCommand("serverInfo");
  2660.         }
  2661.  
  2662.  
  2663.         public void ticks()
  2664.         {
  2665.             utc = utc.AddSeconds(1);
  2666.             timer(utc);
  2667.  
  2668.         }
  2669.  
  2670.         public bool isPluginState(PluginState state)
  2671.         {
  2672.             return pluginState.Equals(state);
  2673.         }
  2674.  
  2675.         public bool isPluginWaiting()
  2676.         {
  2677.             return isPluginState(PluginState.wait);
  2678.         }
  2679.  
  2680.         public bool isPluginBalancing()
  2681.         {
  2682.             return isPluginState(PluginState.balance);
  2683.         }
  2684.  
  2685.         public bool isPluginWarning()
  2686.         {
  2687.             return isPluginState(PluginState.warn);
  2688.         }
  2689.  
  2690.         public bool isPluginStopped()
  2691.         {
  2692.             return isPluginState(PluginState.stop);
  2693.         }
  2694.  
  2695.         public bool isPluginChecking()
  2696.         {
  2697.             return isPluginState(PluginState.check);
  2698.         }
  2699.  
  2700.         public void startCheckState(DateTime now)
  2701.         {
  2702.  
  2703.             try
  2704.             {
  2705.                 if (check_state_phase == 0)
  2706.                 {
  2707.                     pluginState = PluginState.check;
  2708.                     setStartTime(pluginState, now.AddSeconds(1));
  2709.                     DebugWrite("^b" + pluginState + "^n state started " + getStartTime(pluginState).ToString() + "^n^0", 1);
  2710.  
  2711.                     DebugWrite("^b" + PluginState.check.ToString() + "^n state ^bphase-" + check_state_phase + "^n started " + getStartTime(pluginState).ToString() + "^0", 2);
  2712.                     DebugWrite("Requesting player list", 2);
  2713.  
  2714.                     check_state_phase = 1;
  2715.                     getPlayerList();
  2716.  
  2717.  
  2718.                     return;
  2719.                 }
  2720.                 else if (check_state_phase == 1)
  2721.                 {
  2722.  
  2723.                     DebugWrite("^b" + PluginState.check.ToString() + "^n state ^bphase-" + check_state_phase + "^n started " + now.ToString() + "^0", 2);
  2724.  
  2725.  
  2726.  
  2727.                     if (teamsUnbalanced())
  2728.                     {
  2729.                         DebugWrite("Teams are unbalanced, going to ^b" + PluginState.warn.ToString() + "^n state", 2);
  2730.                         startWarnState(now);
  2731.                     }
  2732.                     else
  2733.                     {
  2734.                         DebugWrite("Teams are balanced, going to ^b" + PluginState.wait.ToString() + "^n state", 2);
  2735.                         restartWaitState(now);
  2736.                     }
  2737.  
  2738.                     check_state_phase = 0;
  2739.  
  2740.                     return;
  2741.                 }
  2742.             }
  2743.             catch (Exception e)
  2744.             {
  2745.                 dump_exception(e);
  2746.             }
  2747.         }
  2748.  
  2749.         public void timer(DateTime now)
  2750.         {
  2751.  
  2752.             if (!getBooleanVarValue("balance_live"))
  2753.                 return;
  2754.  
  2755.             int remain_time = getRemainingTime(now, pluginState);
  2756.             int elapsed_time = getElapsedTime(now, pluginState);
  2757.  
  2758.  
  2759.             if (isPluginChecking() || isPluginStopped() || isPluginBalancing())
  2760.                 DebugWrite(pluginState.ToString() + "(" + elapsed_time + ")", 4);
  2761.             else
  2762.                 DebugWrite(pluginState.ToString() + "(" + remain_time + ")", 4);
  2763.  
  2764.  
  2765.  
  2766.             if (isPluginStopped())
  2767.             {
  2768.                 if (getBooleanVarValue("auto_start"))
  2769.                 {
  2770.                     DebugWrite("^bauto_start^n is enabled, going to ^b" + PluginState.wait.ToString() + "^n state^0", 2);
  2771.                     startWaitSate(now);
  2772.                 }
  2773.             }
  2774.             else if (isPluginWaiting())
  2775.             {
  2776.                 if (remain_time <= 0)
  2777.                     startCheckState(now);
  2778.             }
  2779.             else if (isPluginWarning())
  2780.             {
  2781.                 int countdown_time = getIntegerVarValue("warn_msg_countdown_time");
  2782.                 int display_time = getIntegerVarValue("warn_msg_display_time");
  2783.                 int interval_time = getIntegerVarValue("warn_msg_interval_time");
  2784.  
  2785.                 if (teamsBalanced())
  2786.                 {
  2787.                     DebugWrite("Teams are balanced, halting ^b" + PluginState.warn.ToString() + "^n state, and restarting ^b" + PluginState.wait.ToString() + "^n state^0", 4);
  2788.                     restartWaitState(now);
  2789.                     return;
  2790.                 }
  2791.                 else if (remain_time <= 0)
  2792.                 {
  2793.                     balanceLive(now);
  2794.                     restartWaitState(now);
  2795.                     return;
  2796.                 }
  2797.                 else if (remain_time >= 1 && remain_time <= countdown_time)
  2798.                 {
  2799.                     warnCountdown();
  2800.                     return;
  2801.                 }
  2802.                 else if (isTimeLeft(remain_time, display_time, interval_time, countdown_time))
  2803.                 {
  2804.                     warnAnnounce(display_time);
  2805.                     return;
  2806.                 }
  2807.             }
  2808.         }
  2809.  
  2810.  
  2811.         private bool teamsUnbalanced()
  2812.         {
  2813.             return !teamsBalanced();
  2814.         }
  2815.  
  2816.         private bool teamsBalanced()
  2817.         {
  2818.  
  2819.             //return false;
  2820.             /* initialize hash with player count for 16 teams*/
  2821.             Dictionary<int, int> player_count = getPlayerCount();
  2822.             int total = player_count[1] + player_count[2];
  2823.  
  2824.             int difference = Math.Abs(player_count[1] - player_count[2]);
  2825.             int balance_threshold = getIntegerVarValue("balance_threshold");
  2826.  
  2827.             /* assumer the minimum threshold if user total players is less than user set threshold */
  2828.             int threshold = (total <= balance_threshold) ? 1 : balance_threshold;
  2829.  
  2830.  
  2831.  
  2832.             if (difference <= balance_threshold)
  2833.                 return true;
  2834.  
  2835.             return false;
  2836.         }
  2837.  
  2838.         private int sumSquadPlayers(List<PlayerSquad> squads)
  2839.         {
  2840.             int sum = 0;
  2841.             foreach (PlayerSquad squad in squads)
  2842.                 sum += squad.getCount();
  2843.             return sum;
  2844.         }
  2845.  
  2846.         private void listPlayers()
  2847.         {
  2848.             DebugWrite("== Listing Players == ", 3);
  2849.             listPlayers(getPlayersProfile(""));
  2850.  
  2851.         }
  2852.  
  2853.         private void listPlayers(List<PlayerProfile> players_list)
  2854.         {
  2855.             int count = 1;
  2856.             foreach (PlayerProfile player in players_list)
  2857.             {
  2858.                 DebugWrite("    " + count + ". ^b" + player + "^n STeam(" + TN(player.getSavedTeamId()) + ").SSquad(" + SQN(player.getSavedSquadId()) + ") ... Team(" + TN(player.getTeamId()) + ").Squad(" + SQN(player.getSquadId()) + ")", 3);
  2859.                 count++;
  2860.             }
  2861.         }
  2862.  
  2863.  
  2864.         private List<PlayerSquad> getSquadsNotInWhiteList(List<PlayerSquad> squads)
  2865.         {
  2866.             return getSquadsByWhiteList(squads, false);
  2867.         }
  2868.  
  2869.         private List<PlayerSquad> getSquadsInWhiteList(List<PlayerSquad> squads)
  2870.         {
  2871.             return getSquadsByWhiteList(squads, true);
  2872.         }
  2873.  
  2874.         private List<PlayerSquad> getSquadsByWhiteList(List<PlayerSquad> squads, bool flag)
  2875.         {
  2876.             List<PlayerSquad> list = new List<PlayerSquad>();
  2877.  
  2878.             foreach (PlayerSquad squad in squads)
  2879.             {
  2880.                 bool in_whitelist = false;
  2881.                 foreach (PlayerProfile player in squad.getMembers())
  2882.                     if (isInMoveWhiteList(player))
  2883.                     {
  2884.                         in_whitelist = true;
  2885.                         break;
  2886.                     }
  2887.  
  2888.                 if (flag && in_whitelist)
  2889.                     list.Add(squad);
  2890.                 else if (!flag && !in_whitelist)
  2891.                     list.Add(squad);
  2892.  
  2893.             }
  2894.  
  2895.             return list;
  2896.         }
  2897.  
  2898.         private int mergePlayerWithTeam(PlayerProfile pp, int toTeamId)
  2899.         {
  2900.  
  2901.             if (pp.getTeamId() == toTeamId)
  2902.                 return 0;
  2903.  
  2904.             int players_moved = 0;
  2905.             int squad_max_sz = 4;
  2906.             int nosquadId = 0;
  2907.  
  2908.             List<PlayerSquad> squads = getAllSquads(toTeamId);
  2909.  
  2910.             /* sort the squads in increasing order of player count */
  2911.             squads.Sort(new Comparison<PlayerSquad>(squad_count_asc_cmp));
  2912.  
  2913.             DebugWrite("First looking for empty slots in squads for " + pp + " in Team(" + TN(toTeamId) + ")", 3);
  2914.             for (int i = 0; i < squads.Count; i++)
  2915.             {
  2916.                 PlayerSquad sorted_squad = squads[i];
  2917.  
  2918.                 if (sorted_squad.getCount() == squad_max_sz)
  2919.                     continue;
  2920.  
  2921.                 if (movePlayer(pp, sorted_squad.getTeamId(), sorted_squad.getSquadId()))
  2922.                 {
  2923.                     DebugWrite(pp + " moved to Team(" + TN(sorted_squad.getTeamId()) + ").Squad(" + SQN(sorted_squad.getTeamId()) + ")", 3);
  2924.                     sorted_squad.addPlayer(pp);
  2925.                     players_moved++;
  2926.                     break;
  2927.                 }
  2928.             }
  2929.  
  2930.             if (players_moved > 0)
  2931.                 return players_moved;
  2932.  
  2933.             DebugWrite("Could not find empty slots in squads for " + pp + " in Team(" + TN(toTeamId) + ")", 3);
  2934.             if (movePlayer(pp, toTeamId, nosquadId))
  2935.             {
  2936.                 DebugWrite(pp + " moved to Team(" + TN(toTeamId) + ").Squad(" + SQN(nosquadId) + ")", 3);
  2937.                 players_moved++;
  2938.             }
  2939.  
  2940.             return players_moved;
  2941.         }
  2942.  
  2943.         private int mergeSquadWithPool(PlayerSquad squad, List<PlayerSquad> squads)
  2944.         {
  2945.  
  2946.             int players_moved = 0;
  2947.             if (squad == null)
  2948.                 return 0;
  2949.  
  2950.             int squad_max_sz = 4;
  2951.  
  2952.             List<PlayerProfile> squad_players = squad.getMembers();
  2953.  
  2954.  
  2955.             /* sort the squads in increasing order of player count */
  2956.  
  2957.             squads.Sort(new Comparison<PlayerSquad>(squad_count_asc_cmp));
  2958.  
  2959.             for (int i = 0; i < squads.Count; i++)
  2960.             {
  2961.                 PlayerSquad sorted_squad = squads[i];
  2962.                 if (squad.getTeamId() == sorted_squad.getTeamId() &&
  2963.                     squad.getSquadId() == sorted_squad.getSquadId())
  2964.                     continue;
  2965.  
  2966.                 while (sorted_squad.getFreeSlots() > 0 && squad_players.Count > 0)
  2967.                 {
  2968.                     PlayerProfile squad_player = squad_players[0];
  2969.                     squad_players.RemoveAt(0);
  2970.                     if (movePlayer(squad_player, sorted_squad.getTeamId(), sorted_squad.getSquadId()))
  2971.                     {
  2972.                         DebugWrite(squad_player + " moved to Team(" + TN(squad_player.getTeamId()) + ").Squad(" + SQN(squad_player.getSquadId()) + ")", 3);
  2973.                         sorted_squad.addPlayer(squad_player);
  2974.                         players_moved++;
  2975.                     }
  2976.                 }
  2977.             }
  2978.  
  2979.             return players_moved;
  2980.         }
  2981.  
  2982.         private int kickOnePlayer(List<PlayerProfile> list)
  2983.         {
  2984.             foreach (PlayerProfile pp in list)
  2985.             {
  2986.                 if (isInKickWhiteList(pp))
  2987.                 {
  2988.                     DebugWrite("Player " + pp + " is idle, but cannot kick because he in white-list", 3);
  2989.                     continue;
  2990.                 }
  2991.                 ConsoleWrite("Kicking idle player ^bTeam(" + TN(pp.getTeamId()) + ").Squad(" + SQN(pp.getSquadId()) + ") " + pp + " from server");
  2992.                 KickPlayerWithMessage(pp, "kicked for inactivity when server was full");
  2993.                 return 1;
  2994.             }
  2995.  
  2996.             return 0;
  2997.         }
  2998.  
  2999.  
  3000.  
  3001.         private Dictionary<int, List<PlayerProfile>> getAllIdle()
  3002.         {
  3003.             List<PlayerProfile> all = getPlayersProfile("");
  3004.  
  3005.             Dictionary<int, List<PlayerProfile>> idle = new Dictionary<int, List<PlayerProfile>>();
  3006.  
  3007.             //pre initialize the idle list with 0
  3008.  
  3009.             idle.Add(0, new List<PlayerProfile>());
  3010.             idle.Add(1, new List<PlayerProfile>());
  3011.             idle.Add(2, new List<PlayerProfile>());
  3012.  
  3013.             foreach (PlayerProfile pp in all)
  3014.             {
  3015.                 if (!isPlayerIdle(pp))
  3016.                     continue;
  3017.  
  3018.                 if (idle[pp.getTeamId()].Contains(pp))
  3019.                     continue;
  3020.  
  3021.                 idle[pp.getTeamId()].Add(pp);
  3022.             }
  3023.  
  3024.             return idle;
  3025.  
  3026.         }
  3027.  
  3028.         private void balanceRound(int winTeamId)
  3029.         {
  3030.             if (winTeamId == 0)
  3031.                 winTeamId = 1;
  3032.  
  3033.             if (serverInfo == null)
  3034.             {
  3035.                 ConsoleWrite("^1^bERROR^0^n: will not run round-balancer, server size information is not available");
  3036.                 return;
  3037.             }
  3038.  
  3039.             if (!getBooleanVarValue("balance_round"))
  3040.             {
  3041.                 ConsoleWrite("Round balancer disbaled, not running");
  3042.                 return;
  3043.             }
  3044.  
  3045.             int loseTeamId = getOpposingTeamId(winTeamId);
  3046.             int neutralTeamId = 0;
  3047.             int team_sz = serverInfo.MaxPlayerCount / 2;
  3048.  
  3049.             /* find where the free slot is */
  3050.             Dictionary<int, int> pcounts = getPlayerCount();
  3051.             int total = pcounts[winTeamId] + pcounts[loseTeamId];
  3052.  
  3053.  
  3054.             if (total > serverInfo.MaxPlayerCount)
  3055.             {
  3056.                 ConsoleWrite("^1^bWARNING^n^0: detected that there are ^b" + total + "^n players, but DICE says the server size is ^b" + serverInfo.MaxPlayerCount + "^n ");
  3057.                 ConsoleWrite("^1^bWARNING^n^0: This makes no sense, DICE be trolling you! ");
  3058.                 ConsoleWrite("The highest player count I saw this round was ^b" + max_player_count + "^n, I will use that as server size instead (cross fingers)");
  3059.                 team_sz = max_player_count / 2;
  3060.             }
  3061.  
  3062.  
  3063.             Dictionary<int, int> fslots = new Dictionary<int, int>();
  3064.             fslots.Add(neutralTeamId, 0);
  3065.             fslots.Add(winTeamId, team_sz - pcounts[winTeamId]);
  3066.             fslots.Add(loseTeamId, team_sz - pcounts[loseTeamId]);
  3067.  
  3068.             if (!(fslots[winTeamId] > 0 || fslots[loseTeamId] > 0))
  3069.             {
  3070.                 if (getBooleanVarValue("kick_idle"))
  3071.                 {
  3072.                     Dictionary<int, List<PlayerProfile>> idle = getAllIdle();
  3073.                     DebugWrite("^bkick_idle^n is ^bon^n, will try to find idle players to kick on Team(" + TN(winTeamId) + ") or Team(" + TN(loseTeamId) + ")", 3);
  3074.  
  3075.  
  3076.                     if (idle[winTeamId].Count == 0 && idle[loseTeamId].Count == 0)
  3077.                     {
  3078.                         ConsoleWrite("^1^bWARNING^0^n: No free player slots in either team, and no idle players, not balancing");
  3079.                         return;
  3080.                     }
  3081.  
  3082.                     /* kick at least 1 from win team */
  3083.                     fslots[winTeamId] += kickOnePlayer(idle[winTeamId]);
  3084.  
  3085.                     /* kick at least 1 from lose team */
  3086.                     fslots[loseTeamId] += kickOnePlayer(idle[loseTeamId]);
  3087.  
  3088.                     if (!(fslots[winTeamId] > 0 || fslots[loseTeamId] > 0))
  3089.                     {
  3090.                         ConsoleWrite("^1^bWARNING^0^n: Cannot find at least one idle player that is not in white-list, not balancing");
  3091.                         return;
  3092.                     }
  3093.                 }
  3094.  
  3095.                 ConsoleWrite("^1^bWARNING^0^n: No free player slots in either team, not balancing");
  3096.                 return;
  3097.             }
  3098.  
  3099.             DebugWrite("Team(" + TN(winTeamId) + ") has " + fslots[winTeamId] + " free slots", 3);
  3100.             DebugWrite("Team(" + TN(loseTeamId) + ") has " + fslots[loseTeamId] + " free slots", 3);
  3101.  
  3102.  
  3103.             int max_squads = 16;
  3104.             int max_squad_size = 4;
  3105.             virtual_mode = true;
  3106.             DateTime now = utc;
  3107.             pluginState = PluginState.balance;
  3108.             setStartTime(pluginState, now.AddSeconds(1));
  3109.             DebugWrite("^b" + pluginState + "_clans^n state started " + getStartTime(pluginState).ToString() + "^n^0", 1);
  3110.  
  3111.  
  3112.             DebugWrite("Saving original teams state", 3);
  3113.             List<PlayerProfile> all = getPlayersProfile("");
  3114.             all.ForEach(delegate(PlayerProfile pp) { pp.saveTeamSquad(); });
  3115.             listPlayers();
  3116.  
  3117.  
  3118.  
  3119.             DebugWrite("Building no-squad pool from ^bTeam(" + TN(winTeamId) + ")^n and ^bTeam(" + TN(loseTeamId) + ")^n^0", 3);
  3120.             List<PlayerProfile> win_nosquad_pool = getNoSquadPlayers(winTeamId);
  3121.             List<PlayerProfile> lose_nosquad_pool = getNoSquadPlayers(loseTeamId);
  3122.             List<PlayerProfile> nosquad_pool = new List<PlayerProfile>();
  3123.             nosquad_pool.AddRange(win_nosquad_pool);
  3124.             nosquad_pool.AddRange(lose_nosquad_pool);
  3125.  
  3126.             DebugWrite("No-squad pool has ^b" + nosquad_pool.Count + "^n player/s^0", 2);
  3127.             DebugWrite("Building squad pool from ^bTeam(" + TN(winTeamId) + ")^n and ^bTeam(" + TN(loseTeamId) + ")^n^0", 3);
  3128.  
  3129.             List<PlayerSquad> win_squad_pool = getNonEmptySquads(winTeamId);
  3130.             List<PlayerSquad> lose_squad_pool = getNonEmptySquads(loseTeamId);
  3131.             List<PlayerSquad> squad_pool = new List<PlayerSquad>();
  3132.             squad_pool.AddRange(win_squad_pool);
  3133.             squad_pool.AddRange(lose_squad_pool);
  3134.  
  3135.  
  3136.  
  3137.             DebugWrite("Squad pool has ^b" + squad_pool.Count + "^n squads^0", 3);
  3138.             listSquads(squad_pool);
  3139.  
  3140.             if (!getBooleanVarValue("keep_squads_round"))
  3141.             {
  3142.                 foreach (PlayerSquad squad in squad_pool)
  3143.                 {
  3144.                     DebugWrite("Breaking up ^bTeam(" + TN(squad.getTeamId()) + ").Squad(" + SQN(squad.getSquadId()) + ")^n^0", 3);
  3145.                     while (squad.getCount() > 0)
  3146.                     {
  3147.                         PlayerProfile player = squad.removeRandomPlayer();
  3148.                         if (movePlayer(player, player.getTeamId(), 0, true))
  3149.                             nosquad_pool.Add(player); /* only add to no-squad list if move succeeded */
  3150.                     }
  3151.                 }
  3152.             }
  3153.  
  3154.             DebugWrite("Moving no-squad pool to neutral ^bTeam(" + TN(neutralTeamId) + ")^n^0", 3);
  3155.             List<PlayerProfile> nosquad_pool_remove = new List<PlayerProfile>();
  3156.             foreach (PlayerProfile player in nosquad_pool)
  3157.             {
  3158.                 if (!movePlayer(player, neutralTeamId, 0, true))
  3159.                     nosquad_pool_remove.Add(player); /*  move failed, remove him from no squad pool (probably in whitelist) */
  3160.  
  3161.             }
  3162.  
  3163.             /* remove the players that were marked for removing */
  3164.             foreach (PlayerProfile pp in nosquad_pool_remove)
  3165.                 nosquad_pool.Remove(pp);
  3166.  
  3167.             DebugWrite("Moving squad pool to neutral ^bTeam(" + TN(neutralTeamId) + ")^n^0", 3);
  3168.             foreach (PlayerSquad squad in squad_pool)
  3169.                 moveSquad(squad, neutralTeamId, team_sz * 2);
  3170.  
  3171.  
  3172.             /* re-build the pools */
  3173.             DebugWrite("", 3);
  3174.             DebugWrite("Rebuilding no-squad pool from ^bTeam(" + TN(winTeamId) + ")^n and ^bTeam(" + TN(loseTeamId) + ")^n^0", 3);
  3175.             nosquad_pool = getNoSquadPlayers(neutralTeamId);
  3176.             DebugWrite("No-squad pool has ^b" + nosquad_pool.Count + "^n player/s^0", 3);
  3177.  
  3178.  
  3179.             DebugWrite("", 3);
  3180.             DebugWrite("Rebuilding squad pool from ^bTeam(" + TN(winTeamId) + ")^n and ^bTeam(" + TN(loseTeamId) + ")^n^0", 3);
  3181.             squad_pool = getNonEmptySquads(neutralTeamId);
  3182.             DebugWrite("Squad pool has ^b" + squad_pool.Count + "^n squads^0", 2);
  3183.             listSquads(squad_pool);
  3184.  
  3185.  
  3186.             if (getBooleanVarValue("keep_clans_round"))
  3187.             {
  3188.                 DebugWrite("Keeping clans in same team", 3);
  3189.  
  3190.                 /* collect statistics about clans */
  3191.                 DebugWrite("Collecting clan statistics", 3);
  3192.                 Dictionary<string, int> clan_stats = new Dictionary<string, int>();
  3193.                 getClanStats(nosquad_pool, clan_stats);
  3194.                 foreach (PlayerSquad squad in squad_pool)
  3195.                     getClanStats(squad.getMembers(), clan_stats);
  3196.  
  3197.  
  3198.                 List<string> clanlist = new List<string>(clan_stats.Keys);
  3199.                 DebugWrite("^b" + clanlist.Count + "^n clans in server: [^b" + String.Join("^n], [^b", clanlist.ToArray()) + "^n]", 3);
  3200.  
  3201.  
  3202.                 int count = 1;
  3203.                 foreach (KeyValuePair<string, int> pair in clan_stats)
  3204.                 {
  3205.                     DebugWrite("    " + count + ". clan [^b" + pair.Key + "^n] has ^b" + pair.Value + "^n member/s", 3);
  3206.                     count++;
  3207.                 }
  3208.  
  3209.  
  3210.                 /* for clans with more than two players in game, create a new squad for them, and remove them from their current squads */
  3211.                 Dictionary<string, List<PlayerSquad>> clan_squads = new Dictionary<string, List<PlayerSquad>>();
  3212.                 DebugWrite("Creating clan squads from ^bno-squad^n pool", 3);
  3213.                 getClanSquads(nosquad_pool, clan_squads, clan_stats);
  3214.                 DebugWrite("Creating clan squads from ^bsquad-spool^n pool", 3);
  3215.                 foreach (PlayerSquad squad in squad_pool)
  3216.                     getClanSquads(squad.getMembers(), clan_squads, clan_stats);
  3217.  
  3218.                 /* remove the empty squads */
  3219.                 DebugWrite("Removing empty squads", 3);
  3220.                 squad_pool.RemoveAll(delegate(PlayerSquad squad) { return squad.getCount() == 0; });
  3221.                 DebugWrite("squad-pool has now " + squad_pool.Count, 3);
  3222.  
  3223.  
  3224.                 int new_squads_count = 0;
  3225.                 List<PlayerSquad> clan_squad_pool = new List<PlayerSquad>();
  3226.                 foreach (KeyValuePair<string, List<PlayerSquad>> pair in clan_squads)
  3227.                 {
  3228.                     List<PlayerSquad> csquads = pair.Value;
  3229.                     DebugWrite(csquads.Count + " squads for clan ^b[" + csquads[0].getMajorityClanTag() + "]^n", 3);
  3230.                     DebugWrite("----------------------------------------------------------", 3);
  3231.                     listSquads(csquads);
  3232.  
  3233.                     clan_squad_pool.AddRange(csquads);
  3234.                     new_squads_count += csquads.Count;
  3235.                 }
  3236.  
  3237.  
  3238.  
  3239.                 List<int> empty_squad_ids = new List<int>();
  3240.                 DebugWrite("Total of ^b" + new_squads_count + "^n new clan-squads created", 3);
  3241.                 if ((max_squads - squad_pool.Count) < new_squads_count)
  3242.                 {
  3243.                     int squads_to_remove_count = 0;
  3244.                     int squads_to_ignore_count = 0;
  3245.                     int fixed_new_count = 0;
  3246.  
  3247.                     int free_squad_slots = (max_squads - squad_pool.Count);
  3248.                     int extra = new_squads_count - free_squad_slots;
  3249.  
  3250.                     if (extra < squad_pool.Count)
  3251.                         squads_to_remove_count = extra;
  3252.                     else
  3253.                         squads_to_remove_count = squad_pool.Count;
  3254.  
  3255.  
  3256.                     squads_to_ignore_count = extra - squads_to_remove_count;
  3257.  
  3258.                     fixed_new_count = new_squads_count - squads_to_ignore_count;
  3259.  
  3260.                     if (squads_to_ignore_count > 0)
  3261.                         DebugWrite("Out of those new clan-squads,  ^b" + squads_to_ignore_count + "^n  will be ignored", 3);
  3262.  
  3263.                     DebugWrite("There are already " + squad_pool.Count + "^n non-clan squads in ^bTeam(" + TN(neutralTeamId) + ")^n", 3);
  3264.                     DebugWrite("Out of those non-clan squads, " + squads_to_remove_count + " will be removed to make room for " + fixed_new_count + " new clan-squads", 3);
  3265.  
  3266.  
  3267.                     List<PlayerSquad> squads_to_remove = new List<PlayerSquad>();
  3268.  
  3269.                     /* find squads to remove from the list of squads without players in white-list*/
  3270.                     List<PlayerSquad> no_whitelist_squads = getSquadsNotInWhiteList(squad_pool);
  3271.  
  3272.                     /* we may have not found enough squads to remove from the non-whitelist squads */
  3273.                     int extra_squads_needed = 0;
  3274.                     if (no_whitelist_squads.Count < squads_to_remove_count)
  3275.                     {
  3276.                         extra_squads_needed = squads_to_remove_count - no_whitelist_squads.Count;
  3277.                         squads_to_remove_count = no_whitelist_squads.Count;
  3278.                         DebugWrite("Only found ^b" + no_whitelist_squads.Count + " non-clan squads without players in white-list, ^b" + squads_to_remove_count + "^n are needed", 3);
  3279.                         DebugWrite("Will pick ^b" + extra_squads_needed + "^n extra squads from non-clan pool to remove regardless of white-list", 3);
  3280.                     }
  3281.  
  3282.                     squads_to_remove.AddRange(no_whitelist_squads.GetRange(0, squads_to_remove_count));
  3283.                     if (extra_squads_needed > 0)
  3284.                     {
  3285.                         List<PlayerSquad> whitelisted_squads = getSquadsInWhiteList(squad_pool);
  3286.                         squads_to_remove.AddRange(whitelisted_squads.GetRange(0, extra_squads_needed));
  3287.                     }
  3288.  
  3289.                     DebugWrite("The following squads will be removed removed from ^bTeam(" + TN(neutralTeamId) + ")", 3);
  3290.                     DebugWrite("----------------------------------------------------------------------", 3);
  3291.                     listSquads(squads_to_remove);
  3292.  
  3293.                     while (squads_to_remove.Count > 0)
  3294.                     {
  3295.                         PlayerSquad squad_to_remove = squads_to_remove[0];
  3296.                         squads_to_remove.RemoveAt(0);
  3297.                         empty_squad_ids.Add(squad_to_remove.getSquadId());
  3298.                         int squad_sz = squad_to_remove.getCount();
  3299.  
  3300.                         DebugWrite("Looking for empty slots in existing squads, for memebers of " + squad_to_remove, 3);
  3301.  
  3302.                         squad_sz -= mergeSquadWithPool(squad_to_remove, clan_squad_pool);
  3303.                         squad_sz -= mergeSquadWithPool(squad_to_remove, squad_pool);
  3304.  
  3305.  
  3306.                         if (squad_sz > 0)
  3307.                         {
  3308.                             DebugWrite("Did not find empty slots for all members of, " + squad_to_remove, 3);
  3309.                             DebugWrite("Will move them to no-squad spool, " + squad_to_remove, 3);
  3310.                             while (squad_to_remove.getCount() > 0)
  3311.                             {
  3312.                                 PlayerProfile player = squad_to_remove.removeRandomPlayer();
  3313.                                 if (movePlayer(player, player.getTeamId(), 0))
  3314.                                     nosquad_pool.Add(player);
  3315.                             }
  3316.                         }
  3317.  
  3318.                         /* remove the empty squads */
  3319.                         DebugWrite("Removing empty squads", 3);
  3320.                         squad_pool.RemoveAll(delegate(PlayerSquad squad) { return squad.getCount() == 0; });
  3321.                     }
  3322.  
  3323.  
  3324.  
  3325.                 }
  3326.  
  3327.  
  3328.                 DebugWrite("Team(" + TN(neutralTeamId) + ") squad pool before move", 3);
  3329.                 DebugWrite("=====================================================", 3);
  3330.                 List<PlayerSquad> temp_pool = getNonEmptySquads(neutralTeamId);
  3331.                 DebugWrite("Temp pool has ^b" + temp_pool.Count + "^n squads^0", 2);
  3332.                 temp_pool.Sort(new Comparison<PlayerSquad>(getSquadSort("round_sort")));
  3333.                 for (int i = 0; i < squad_pool.Count; i++)
  3334.                 {
  3335.                     DebugWrite("      " + i + ". " + temp_pool[i].ToString() + "(" + getSortFieldValueStr(temp_pool[i], "round_sort") + ")", 3);
  3336.                 }
  3337.  
  3338.  
  3339.                 /* add clan squads to the squad pool */
  3340.                 DebugWrite("Moving ^b" + clan_squad_pool.Count + "^n from clan-squad pool to Team(" + TN(neutralTeamId) + ")", 3);
  3341.                 foreach (PlayerSquad squad in clan_squad_pool)
  3342.                     moveSquad(squad, neutralTeamId, team_sz * 2);
  3343.  
  3344.             }
  3345.  
  3346.             /* re-build the pools */
  3347.  
  3348.             DebugWrite("Rebuilding no-squad pool", 3);
  3349.             nosquad_pool = getNoSquadPlayers(neutralTeamId);
  3350.             DebugWrite("No-squad pool has ^b" + nosquad_pool.Count + "^n player/s^0", 3);
  3351.             listPlayers(nosquad_pool);
  3352.  
  3353.             DebugWrite("Rebuilding squad pool", 3);
  3354.             squad_pool = getNonEmptySquads(neutralTeamId);
  3355.             DebugWrite("Squad pool has ^b" + squad_pool.Count + "^n squads^0", 2);
  3356.  
  3357.  
  3358.             if (squad_pool.Count > max_squads)
  3359.                 ConsoleWrite("^1^bWARNING^0^n: There are still more squads than allowed!");
  3360.  
  3361.             /* sort the no-squad pool */
  3362.             DebugWrite("Sorting the no-squad pool by ^b" + getStringVarValue("round_sort") + "^n^0", 3);
  3363.             nosquad_pool.Sort(new Comparison<PlayerProfile>(getPlayerSort("round_sort")));
  3364.  
  3365.             for (int i = 0; i < nosquad_pool.Count; i++)
  3366.             {
  3367.                 DebugWrite("      " + i + ". " + nosquad_pool[i] + "(" + getSortFieldValueStr(nosquad_pool[i], "round_sort") + ")", 3);
  3368.             }
  3369.  
  3370.  
  3371.             /* sort the squad pool */
  3372.             DebugWrite("Sorting the squad pool by ^b" + getStringVarValue("round_sort") + "^n^0", 3);
  3373.             squad_pool.Sort(new Comparison<PlayerSquad>(getSquadSort("round_sort")));
  3374.  
  3375.             for (int i = 0; i < squad_pool.Count; i++)
  3376.             {
  3377.                 DebugWrite("      " + i + ". " + squad_pool[i].ToString() + "(" + getSortFieldValueStr(squad_pool[i], "round_sort") + ")", 3);
  3378.             }
  3379.  
  3380.             int[] teamCount = new int[3];
  3381.             teamCount[neutralTeamId] = sumSquadPlayers(squad_pool) + nosquad_pool.Count;
  3382.             teamCount[winTeamId] = getPlayerCount()[winTeamId];
  3383.             teamCount[loseTeamId] = getPlayerCount()[loseTeamId];
  3384.  
  3385.             DebugWrite("Team counts, ^bTeam(" + TN(neutralTeamId) + ")^n: " + teamCount[neutralTeamId] + ", ^bTeam(" + TN(winTeamId) + ")^n: " + teamCount[winTeamId] + ", ^bTeam(" + TN(loseTeamId) + ")^n: " + teamCount[loseTeamId], 3);
  3386.  
  3387.             Dictionary<string, int> clanTeam = new Dictionary<string, int>();
  3388.  
  3389.             /* assume the smaller team */
  3390.             int smallTeamId = loseTeamId;
  3391.  
  3392.             DebugWrite("Moving ^b" + squad_pool.Count + "^n squads from neutral ^bTeam(" + TN(neutralTeamId) + ")^n into ^bTeam(" + TN(winTeamId) + ")^n and ^bTeam(" + TN(loseTeamId) + ")^n^0", 3);
  3393.  
  3394.             while (squad_pool.Count > 0)
  3395.             {
  3396.                 /* get the top squad */
  3397.                 PlayerSquad squad = squad_pool[0];
  3398.                 squad_pool.RemoveAt(0);
  3399.  
  3400.                 if (getBooleanVarValue("keep_clans_round"))
  3401.                 {
  3402.                     string tag = squad.getMajorityClanTag();
  3403.                     /* if squad has a clan tag, determine where most of his team is already */
  3404.                     if (!Regex.Match(tag, @"^\s*$").Success)
  3405.                     {
  3406.                         if (clanTeam.ContainsKey(tag))
  3407.                         {
  3408.                             int clan_team_id = clanTeam[tag];
  3409.                             DebugWrite("There is already a clan squad for ^b[" + tag + "]^n in ^bTeam(" + TN(clan_team_id) + ")^n^0", 3);
  3410.                             smallTeamId = clan_team_id;
  3411.                         }
  3412.                         else
  3413.                         {
  3414.                             DebugWrite("First time seeing clan ^b[" + tag + "]^n will assign to ^bTeam(" + TN(smallTeamId) + ")^n^0", 3);
  3415.                             clanTeam.Add(tag, smallTeamId);
  3416.                         }
  3417.                     }
  3418.                 }
  3419.  
  3420.                 int squad_sz = squad.getCount();
  3421.  
  3422.                 /* move top squad to the smaller team */
  3423.                 DebugWrite("Moving entire " + squad.ToString() + " to ^bTeam(" + TN(smallTeamId) + ")^n^0", 3);
  3424.                 /* squad size may change if not all players were moved, i.e. someone was in white list */
  3425.                 squad_sz = moveSquad(squad, smallTeamId, team_sz);
  3426.  
  3427.  
  3428.                 /* update the team counts */
  3429.                 teamCount[smallTeamId] += squad_sz;
  3430.                 teamCount[neutralTeamId] -= squad_sz;
  3431.  
  3432.                 /* determine the smaller team */
  3433.                 smallTeamId = getSmallTeamId(winTeamId, teamCount[winTeamId], loseTeamId, teamCount[loseTeamId]);
  3434.                 DebugWrite("Team counts, ^bTeam(" + TN(neutralTeamId) + ")^n: " + teamCount[neutralTeamId] + ", ^bTeam(" + TN(winTeamId) + ")^n: " + teamCount[winTeamId] + ", ^bTeam(" + TN(loseTeamId) + ")^n: " + teamCount[loseTeamId] + ", ^b Small Team(" + TN(smallTeamId) + ")^n: " + teamCount[smallTeamId], 3);
  3435.                 DebugWrite("------------------------------------------------------------------------------------------------", 3);
  3436.             }
  3437.  
  3438.             DebugWrite("^bTeam(" + TN(winTeamId) + ")^n has now ^b" + teamCount[winTeamId] + "^n player/s", 3);
  3439.             DebugWrite("^bTeam(" + TN(loseTeamId) + ")^n has now ^b" + teamCount[loseTeamId] + "^n player/s", 3);
  3440.  
  3441.             DebugWrite("Moving ^b" + nosquad_pool.Count + "^n player/s from neutral ^bTeam(" + TN(neutralTeamId) + ")^n into ^bTeam(" + TN(winTeamId) + ")^n and ^bTeam(" + TN(loseTeamId) + ")^n^0", 3);
  3442.  
  3443.             while (nosquad_pool.Count > 0)
  3444.             {
  3445.                 /* get the top player */
  3446.                 PlayerProfile player = nosquad_pool[0];
  3447.                 nosquad_pool.RemoveAt(0);
  3448.  
  3449.                 /* move the top player to the smaller team */
  3450.                 DebugWrite("Moving ^b" + player.ToString() + "^n to ^bTeam(^n" + TN(smallTeamId) + ")^n^0", 3);
  3451.                 int moved = 1;
  3452.                 if (!movePlayer(player, smallTeamId, 0, true))
  3453.                     moved = 0;
  3454.  
  3455.                 /* update the team counts */
  3456.                 teamCount[neutralTeamId] -= moved;
  3457.                 teamCount[smallTeamId] += moved;
  3458.  
  3459.                 /* determine the smaller team */
  3460.                 smallTeamId = getSmallTeamId(winTeamId, teamCount[winTeamId], loseTeamId, teamCount[loseTeamId]);
  3461.             }
  3462.  
  3463.  
  3464.             DebugWrite("^bTeam(" + TN(winTeamId) + ")^n has now ^b" + teamCount[winTeamId] + "^n player/s", 3);
  3465.             DebugWrite("^bTeam(" + TN(loseTeamId) + ")^n has now ^b" + teamCount[loseTeamId] + "^n player/s", 3);
  3466.  
  3467.             if (teamsUnbalanced())
  3468.             {
  3469.                 DebugWrite("Teams are still unbalanced, applying the live balancing logic", 3);
  3470.                 balanceLive(utc, true);
  3471.  
  3472.  
  3473.             }
  3474.             else
  3475.                 DebugWrite("Teams should now be balanced!", 3);
  3476.  
  3477.             DebugWrite("Doing sanity check now", 3);
  3478.             DebugWrite("========================", 3);
  3479.  
  3480.             List<PlayerProfile> moving_from_win_to_lose = getPlayersMoving(winTeamId, loseTeamId);
  3481.             List<PlayerProfile> moving_from_lose_to_win = getPlayersMoving(loseTeamId, winTeamId);
  3482.  
  3483.  
  3484.  
  3485.             DebugWrite(moving_from_win_to_lose.Count + " players will be moving from Team(" + TN(winTeamId) + ") to Team(" + TN(loseTeamId) + ")", 3);
  3486.             DebugWrite(moving_from_lose_to_win.Count + " players will be moving from Team(" + TN(loseTeamId) + ") to Team(" + TN(winTeamId) + ")", 3);
  3487.  
  3488.             int teamWithMoreMoving = 0;
  3489.             int players_needing_moving = 0;
  3490.             List<PlayerProfile> players_moving = null;
  3491.  
  3492.             if (moving_from_win_to_lose.Count > moving_from_lose_to_win.Count)
  3493.             {
  3494.                 teamWithMoreMoving = winTeamId;
  3495.                 players_moving = moving_from_win_to_lose;
  3496.                 int slots_needed_in_lose_team = moving_from_win_to_lose.Count - moving_from_lose_to_win.Count;
  3497.                 DebugWrite(slots_needed_in_lose_team + " free slots are needed in Team(" + TN(loseTeamId) + "), and there are " + fslots[loseTeamId], 3);
  3498.                 if (slots_needed_in_lose_team > fslots[loseTeamId])
  3499.                 {
  3500.                     DebugWrite("There are only " + fslots[loseTeamId] + " free slots in Team(" + TN(loseTeamId) + ")", 3);
  3501.                     players_needing_moving = slots_needed_in_lose_team - fslots[loseTeamId];
  3502.                     DebugWrite("Meaning that, " + players_needing_moving + " players will not be able to move from Team(" + TN(winTeamId) + ") to Team(" + TN(loseTeamId) + ")", 3);
  3503.                 }
  3504.             }
  3505.             else if (moving_from_lose_to_win.Count > moving_from_win_to_lose.Count)
  3506.             {
  3507.                 teamWithMoreMoving = loseTeamId;
  3508.                 players_moving = moving_from_lose_to_win;
  3509.                 int slots_needed_in_win_team = moving_from_lose_to_win.Count - moving_from_win_to_lose.Count;
  3510.                 DebugWrite(slots_needed_in_win_team + " free slots are needed in Team(" + TN(winTeamId) + "), and there are " + fslots[winTeamId], 3);
  3511.                 if (slots_needed_in_win_team > fslots[winTeamId])
  3512.                 {
  3513.                     DebugWrite("There are only " + fslots[winTeamId] + " free slots in Team(" + TN(winTeamId) + ")", 3);
  3514.                     players_needing_moving = slots_needed_in_win_team - fslots[winTeamId];
  3515.                     DebugWrite("Meaning that, " + players_needing_moving + " players will not be able to move from Team(" + TN(loseTeamId) + ") to Team(" + TN(winTeamId) + ")", 3);
  3516.                 }
  3517.             }
  3518.  
  3519.             if (players_needing_moving > 0)
  3520.             {
  3521.                 DebugWrite("I have determined that " + players_needing_moving + " players will need to stay in Team(" + TN(teamWithMoreMoving) + ") to remedy this situation", 3);
  3522.  
  3523.                 while (players_needing_moving > 0 && players_moving.Count > 0)
  3524.                 {
  3525.                     PlayerProfile pp = players_moving[0];
  3526.                     players_moving.RemoveAt(0);
  3527.  
  3528.                     /* he already stays, leave him alone */
  3529.                     if (pp.getSavedTeamId() == pp.getTeamId())
  3530.                         continue;
  3531.  
  3532.                     /* move him back to where he was */
  3533.                     players_needing_moving -= mergePlayerWithTeam(pp, pp.getSavedTeamId());
  3534.  
  3535.                 }
  3536.  
  3537.             }
  3538.  
  3539.             DebugWrite("Re-doing sanity check now", 3);
  3540.             DebugWrite("========================", 3);
  3541.  
  3542.             moving_from_win_to_lose = getPlayersMoving(winTeamId, loseTeamId);
  3543.             moving_from_lose_to_win = getPlayersMoving(loseTeamId, winTeamId);
  3544.  
  3545.             DebugWrite(moving_from_win_to_lose.Count + " players will be moving from Team(" + TN(winTeamId) + ") to Team(" + TN(loseTeamId) + ")", 3);
  3546.             DebugWrite(moving_from_lose_to_win.Count + " players will be moving from Team(" + TN(loseTeamId) + ") to Team(" + TN(winTeamId) + ")", 3);
  3547.  
  3548.             virtual_mode = false;
  3549.  
  3550.             /* swap players that are moving across teams only, and put them in no-squad */
  3551.             fixTeams(winTeamId, pcounts, fslots);
  3552.  
  3553.             /* fix the squads for players */
  3554.             sleep = true;
  3555.             fixSquads();
  3556.             sleep = false;
  3557.         }
  3558.  
  3559.  
  3560.  
  3561.         private List<PlayerProfile> getPlayersMoving(int fromTeamId, int toTeamId)
  3562.         {
  3563.             List<PlayerProfile> list = new List<PlayerProfile>();
  3564.             List<PlayerProfile> player_list = getPlayersProfile("");
  3565.  
  3566.             foreach (PlayerProfile pp in player_list)
  3567.                 if (pp.getSavedTeamId() == fromTeamId &&
  3568.                     pp.getTeamId() == toTeamId)
  3569.                     list.Add(pp);
  3570.             return list;
  3571.         }
  3572.  
  3573.  
  3574.  
  3575.  
  3576.         private void balanceLive(DateTime now)
  3577.         {
  3578.             balanceLive(now, false);
  3579.         }
  3580.  
  3581.  
  3582.         private void delayedLiveBalance(DateTime now, bool force)
  3583.         {
  3584.  
  3585.             /* save the original team and squad for each player */
  3586.             List<PlayerProfile> players = getPlayersProfile("");
  3587.             players.ForEach(delegate(PlayerProfile pp)
  3588.             {
  3589.                 if (pp.getDelayedTeamId() > 0)
  3590.                     DebugWrite("Un-flagging ^b" + pp + "^n for delayed move", 3);
  3591.                 pp.resetDelayedTeamSquad();
  3592.                 pp.saveTeamSquad();
  3593.             });
  3594.  
  3595.  
  3596.             live_balancer = true;
  3597.  
  3598.             /* for delayed live balance we want to do it all virtually, and only move players after they respawn */
  3599.             bool original_value = virtual_mode;
  3600.             if (!original_value)
  3601.                 virtual_mode = true;
  3602.  
  3603.  
  3604.  
  3605.             balanceLive(now, force, true);
  3606.             virtual_mode = original_value;
  3607.  
  3608.             ConsoleWrite("virtual live-balance done, proceeding now to flag players that will need moving");
  3609.  
  3610.             /* save the delayed team, and squad for each player */
  3611.             players = getPlayersProfile("");
  3612.             players.ForEach(delegate(PlayerProfile pp)
  3613.             {
  3614.                 if (pp.getSavedTeamId() != pp.getTeamId())
  3615.                 {
  3616.                     /* save the delayed team and squad */
  3617.                     pp.saveDelayedTeamSquad();
  3618.  
  3619.                     /* reset the original team and squad */
  3620.                     pp.setTeamId(pp.getSavedTeamId());
  3621.                     pp.setSquadId(pp.getSavedSquadId());
  3622.  
  3623.                     if (pp.isAlive())
  3624.                     {
  3625.                         DebugWrite("Player ^b" + pp + "^n is alive, flagged for delayed move from ^bTeam(" + TN(pp.getSavedTeamId()) + ").Squad(" + SQN(pp.getSavedSquadId()) + ")^n to ^bDTeam(" + TN(pp.getDelayedTeamId()) + ").DSquad(" + SQN(pp.getDelayedSquadId()) + ")^n", 3);
  3626.                     }
  3627.                     else
  3628.                     {
  3629.                         DebugWrite("Player ^b" + pp + "^n " + playerstate2stringED(pp.state) + ", flagged for immediate move from ^bTeam(" + TN(pp.getSavedTeamId()) + ").Squad(" + SQN(pp.getSavedSquadId()) + ")^n to ^bDTeam(" + TN(pp.getDelayedTeamId()) + ").DSquad(" + SQN(pp.getDelayedSquadId()) + ")^n", 3);
  3630.                         /* skip balance check, we alreay know teams are not balanced */
  3631.                         enforceImmediateMove(pp);
  3632.                     }
  3633.                 }
  3634.                 pp.resetSavedTeamSquad();
  3635.             });
  3636.  
  3637.  
  3638.             live_balancer = false;
  3639.         }
  3640.  
  3641.         private void balanceLive(DateTime now, bool force)
  3642.         {
  3643.             if (getBooleanVarValue("wait_death"))
  3644.             {
  3645.                 ConsoleWrite("^bwait_death^n is ^bon^n, will do live-balance in virtual mode");
  3646.                 delayedLiveBalance(now, force);
  3647.             }
  3648.             else
  3649.             {
  3650.                 ConsoleWrite("^bwait_death^n is ^boff^n, will do live-balance on the fly");
  3651.                 balanceLive(now, force, false);
  3652.             }
  3653.         }
  3654.  
  3655.         private bool checkTicketThreshold()
  3656.         {
  3657.             List<TeamScore> scores = null;
  3658.             lock (info_mutex)
  3659.             {
  3660.                 if (serverInfo == null)
  3661.                     return false;
  3662.  
  3663.                 scores = serverInfo.TeamScores; ;
  3664.             }
  3665.  
  3666.             // check that the scores are available
  3667.             if (scores == null || scores.Count == 0)
  3668.             {
  3669.                 ConsoleWrite("^1^bERROR^0^n: team scores not available");
  3670.                 return false;
  3671.             }
  3672.  
  3673.             int min_tickets = getIntegerVarValue("ticket_threshold");
  3674.             int min_score = int.MaxValue;
  3675.             int min_team = int.MaxValue;
  3676.  
  3677.            
  3678.             // find the team with the least tickets
  3679.             foreach (TeamScore score in scores)
  3680.             {
  3681.                 if (score == null)
  3682.                 {
  3683.                     ConsoleWrite("^1^bERROR^0^n: team score not available");
  3684.                     return false;
  3685.                 }
  3686.  
  3687.                 if (score.Score > min_score)
  3688.                     continue;
  3689.                 min_score = score.Score;
  3690.                 min_team = score.TeamID;
  3691.             }
  3692.  
  3693.             if (min_score <= min_tickets)
  3694.             {
  3695.                 ConsoleWrite("Not running live balancer, Team(" + TN(min_team) + ") has only " + min_score + " ticket" + ((min_score > 1) ? "s" : "") + " left to lose");
  3696.                 return true;
  3697.             }
  3698.      
  3699.        
  3700.             return false;
  3701.         }
  3702.  
  3703.         private void balanceLive(DateTime now, bool force, bool delayed)
  3704.         {
  3705.             try
  3706.             {
  3707.                 if (wait_state && !force)
  3708.                 {
  3709.                     ConsoleWrite("Cannot run live balancer, round-over wait state is active");
  3710.                     return;
  3711.                 }
  3712.  
  3713.                 if (round_balancer && !force)
  3714.                 {
  3715.                     ConsoleWrite("Cannot run live balancer, round-balancing is active");
  3716.                     return;
  3717.                 }
  3718.  
  3719.                 // do not balance if there min ticket have been reached
  3720.                 if (!force && checkTicketThreshold())
  3721.                     return;
  3722.  
  3723.  
  3724.                 if (balanceTeams(now) > 0 && getBooleanVarValue("keep_clans_live"))
  3725.                 {
  3726.                     ConsoleWrite("Re-running live balancer, with ^bkeep_clans_live^n disabled");
  3727.                     setBooleanVarValue("keep_clans_live", false);
  3728.                     if (balanceTeams(now) > 0 && getBooleanVarValue("use_white_list"))
  3729.                     {
  3730.                         ConsoleWrite("Re-running live balancer, with ^buse_white_list^n disabled");
  3731.                         setBooleanVarValue("use_white_list", false);
  3732.                         balanceTeams(now);
  3733.                         setBooleanVarValue("use_white_list", true);
  3734.                     }
  3735.                     setBooleanVarValue("keep_clans_live", true);
  3736.                 }
  3737.             }
  3738.             catch (Exception e)
  3739.             {
  3740.                 dump_exception(e);
  3741.             }
  3742.         }
  3743.  
  3744.  
  3745.  
  3746.  
  3747.         private void fixSquads()
  3748.         {
  3749.  
  3750.         }
  3751.  
  3752.         private void fixTeams(int winTeamId, Dictionary<int, int> pcounts, Dictionary<int, int> fslots)
  3753.         {
  3754.             int loseTeamId = getOpposingTeamId(winTeamId);
  3755.             int neutralTeamId = 0;
  3756.             int noSquadId = 0;
  3757.  
  3758.             DebugWrite("====================================================================", 3);
  3759.             DebugWrite("Virtual calculations done, now proceeding to actually move players!", 3);
  3760.             List<PlayerProfile> players_list = getPlayersProfile("");
  3761.  
  3762.             /* save everyone's target team and squad */
  3763.             players_list.ForEach(delegate(PlayerProfile p) { p.saveTargetTeamSquad(); });
  3764.  
  3765.             /* get players moving across teams */
  3766.             List<PlayerProfile> moving_from_win_to_lose = getPlayersMoving(winTeamId, loseTeamId);
  3767.             List<PlayerProfile> moving_from_lose_to_win = getPlayersMoving(loseTeamId, winTeamId);
  3768.  
  3769.             DebugWrite(moving_from_win_to_lose.Count + " players will be moving from Team(" + TN(winTeamId) + ") to Team(" + TN(loseTeamId) + ")", 3);
  3770.             DebugWrite(moving_from_lose_to_win.Count + " players will be moving from Team(" + TN(loseTeamId) + ") to Team(" + TN(winTeamId) + ")", 3);
  3771.  
  3772.             /* get aggreagte moving across teams */
  3773.             List<PlayerProfile> all_moving = new List<PlayerProfile>();
  3774.             all_moving.AddRange(moving_from_win_to_lose);
  3775.             all_moving.AddRange(moving_from_lose_to_win);
  3776.             DebugWrite("Total of " + all_moving.Count + " players will be moving across teams", 3);
  3777.  
  3778.             /* get players staying in their own team */
  3779.             List<PlayerProfile> staying_in_win = getPlayersMoving(winTeamId, winTeamId);
  3780.             List<PlayerProfile> staying_in_lose = getPlayersMoving(loseTeamId, loseTeamId);
  3781.  
  3782.             DebugWrite(staying_in_win.Count + " players will be staying in Team(" + TN(winTeamId) + ")", 3);
  3783.             DebugWrite(staying_in_lose.Count + " players will be staying in Team(" + TN(loseTeamId) + ")", 3);
  3784.  
  3785.             /* aggregate players staying in their own team */
  3786.             List<PlayerProfile> all_staying = new List<PlayerProfile>();
  3787.             all_staying.AddRange(staying_in_win);
  3788.             all_staying.AddRange(staying_in_lose);
  3789.             DebugWrite("Total of " + all_staying.Count + " players will be staying in their own team", 3);
  3790.  
  3791.  
  3792.  
  3793.             List<PlayerProfile> all_players = new List<PlayerProfile>();
  3794.             all_players.AddRange(all_moving);
  3795.             all_players.AddRange(all_staying);
  3796.             DebugWrite("Total of " + all_players.Count + " in server", 3);
  3797.  
  3798.  
  3799.             DebugWrite("Swapping players that move across Team(" + TN(winTeamId) + ") and Team(" + TN(loseTeamId) + ")", 3);
  3800.             DebugWrite("Team(" + TN(winTeamId) + ") has " + fslots[winTeamId] + " free slots", 3);
  3801.             DebugWrite("Team(" + TN(loseTeamId) + ") has " + fslots[loseTeamId] + " free slots", 3);
  3802.  
  3803.  
  3804.             int teamWithMostFreeSlots = 0;
  3805.             int teamWithLeastFreeSlots = 0;
  3806.             List<PlayerProfile> teamToChooseFromFirst = null;
  3807.             List<PlayerProfile> teamToChooseFromSecond = null;
  3808.  
  3809.             if (fslots[winTeamId] > fslots[loseTeamId])
  3810.             {
  3811.                 teamWithMostFreeSlots = winTeamId;
  3812.                 teamWithLeastFreeSlots = loseTeamId;
  3813.                 teamToChooseFromFirst = moving_from_lose_to_win;
  3814.                 teamToChooseFromSecond = moving_from_win_to_lose;
  3815.  
  3816.                 DebugWrite("Team(" + TN(teamWithMostFreeSlots) + ") has the most free slots", 3);
  3817.  
  3818.             }
  3819.             else if (fslots[loseTeamId] > fslots[winTeamId])
  3820.             {
  3821.                 teamWithMostFreeSlots = loseTeamId;
  3822.                 teamWithLeastFreeSlots = winTeamId;
  3823.                 teamToChooseFromFirst = moving_from_win_to_lose;
  3824.                 teamToChooseFromSecond = moving_from_lose_to_win;
  3825.  
  3826.                 DebugWrite("Team(" + TN(teamWithMostFreeSlots) + ") has the most free slots", 3);
  3827.             }
  3828.             else
  3829.             {
  3830.                 teamWithMostFreeSlots = winTeamId;
  3831.                 teamWithLeastFreeSlots = loseTeamId;
  3832.                 teamToChooseFromFirst = moving_from_lose_to_win;
  3833.                 teamToChooseFromSecond = moving_from_win_to_lose;
  3834.                 DebugWrite("Both Team(" + TN(winTeamId) + ") and Team(" + TN(loseTeamId) + " have same number of free slots", 3);
  3835.             }
  3836.  
  3837.  
  3838.             DebugWrite("I will start the swapping from Team(" + TN(teamWithLeastFreeSlots) + ") to Team(" + TN(teamWithMostFreeSlots) + ")", 3);
  3839.  
  3840.             while (teamToChooseFromFirst.Count > 0 && teamToChooseFromSecond.Count > 0)
  3841.             {
  3842.                 PlayerProfile player_first = teamToChooseFromFirst[0];
  3843.                 PlayerProfile player_second = teamToChooseFromSecond[0];
  3844.                 teamToChooseFromFirst.RemoveAt(0);
  3845.                 teamToChooseFromSecond.RemoveAt(0);
  3846.  
  3847.                 DebugWrite("Swapping" + player_first + " from Team(" + TN(player_first.getSavedTeamId()) + ") with " + player_second + " from Team(" + TN(player_second.getSavedTeamId()) + ")", 3);
  3848.  
  3849.                 PlayerProfile pp = player_first;
  3850.                 DebugWrite("Moving " + pp + " to from STeam(" + TN(pp.getSavedTeamId()) + ").SSquad(" + SQN(pp.getSavedSquadId()) + ") to TTeam(" + TN(pp.getTargetTeamId()) + ").TSquad(" + SQN(noSquadId) + ")", 3);
  3851.                 movePlayer(player_first, player_first.getTargetTeamId(), noSquadId, true, true);
  3852.  
  3853.                 pp = player_second;
  3854.                 DebugWrite("Moving " + pp + " to from STeam(" + TN(pp.getSavedTeamId()) + ").SSquad(" + SQN(pp.getSavedSquadId()) + ") to TTeam(" + TN(pp.getTargetTeamId()) + ").TSquad(" + SQN(noSquadId) + ")", 3);
  3855.                 movePlayer(player_second, player_second.getTargetTeamId(), noSquadId, true, true);
  3856.  
  3857.                 DebugWrite("-------------------------------------------------------------------------------------------------------------------------", 3);
  3858.             }
  3859.  
  3860.             int teamIdWithRemainingPlayer = 0;
  3861.             List<PlayerProfile> teamWithRemainingPlayer = new List<PlayerProfile>();
  3862.  
  3863.             if (teamToChooseFromFirst.Count > teamToChooseFromSecond.Count)
  3864.             {
  3865.                 teamIdWithRemainingPlayer = teamWithMostFreeSlots;
  3866.                 teamWithRemainingPlayer = teamToChooseFromFirst;
  3867.             }
  3868.             else if (teamToChooseFromSecond.Count > teamToChooseFromFirst.Count)
  3869.             {
  3870.                 teamIdWithRemainingPlayer = teamWithLeastFreeSlots;
  3871.                 teamWithRemainingPlayer = teamToChooseFromSecond;
  3872.             }
  3873.  
  3874.             if (teamWithRemainingPlayer.Count > 0)
  3875.             {
  3876.                 DebugWrite("There are still " + teamWithRemainingPlayer.Count + " in Team(" + TN(teamIdWithRemainingPlayer) + ") to be moved", 3);
  3877.  
  3878.                 foreach (PlayerProfile pp in teamWithRemainingPlayer)
  3879.                 {
  3880.                     DebugWrite("Moving " + pp + " to from STeam(" + TN(pp.getSavedTeamId()) + ").SSquad(" + SQN(pp.getSavedSquadId()) + ") to TTeam(" + TN(pp.getTargetTeamId()) + ").TSquad(" + SQN(noSquadId) + ")", 3);
  3881.                     movePlayer(pp, pp.getTargetTeamId(), noSquadId, true, true);
  3882.                     DebugWrite("-------------------------------------------------------------------------------------------------------------------------", 3);
  3883.                 }
  3884.             }
  3885.  
  3886.  
  3887.  
  3888.  
  3889.             List<PlayerProfile> move = new List<PlayerProfile>();
  3890.             /* move all players to no-squad, unless they are already in the no-squad, or they are already in their final position */
  3891.  
  3892.             DebugWrite("Fixing squad positions, will only move players that are switching squads", 3);
  3893.  
  3894.             DebugWrite("First putting players in Squad(" + SQN(noSquadId) + "), using " + all_staying.Count + " players that did not move across teams", 3);
  3895.             foreach (PlayerProfile pp in all_staying)
  3896.             {
  3897.                 /* skip players that are already in squad 0 anyways */
  3898.                 if (pp.getSavedSquadId() == 0)
  3899.                     continue;
  3900.  
  3901.                 /* skip players that are already in their final position */
  3902.                 if (pp.getSavedSquadId() == pp.getTargetSquadId())
  3903.                     continue;
  3904.  
  3905.                 DebugWrite("Moving " + pp + " to from STeam(" + TN(pp.getSavedTeamId()) + ").SSquad(" + SQN(pp.getSavedSquadId()) + ") to TTeam(" + TN(pp.getTargetTeamId()) + ").TSquad(" + SQN(noSquadId) + ")", 3);
  3906.                 if (pp.getTargetSquadId() == 0)
  3907.                 {
  3908.                     /* after this move, this player end in its final position, no need to move him anymore*/
  3909.                     movePlayer(pp, pp.getTargetTeamId(), noSquadId, true, true);
  3910.                     continue;
  3911.                 }
  3912.  
  3913.                 movePlayer(pp, pp.getTargetTeamId(), noSquadId, true, true);
  3914.                 move.Add(pp);
  3915.             }
  3916.  
  3917.             move.AddRange(all_moving);
  3918.  
  3919.             /* now put all players in their destination squad */
  3920.             DebugWrite("Now putting players in their final squads, using a set of " + move.Count + " players", 3);
  3921.             foreach (PlayerProfile pp in move)
  3922.             {
  3923.                 /* skip players that are already in their final position */
  3924.                 if (pp.getSavedSquadId() == pp.getTargetSquadId())
  3925.                     continue;
  3926.  
  3927.                 DebugWrite("Moving " + pp + " to from STeam(" + TN(pp.getTeamId()) + ").SSquad(" + SQN(pp.getSquadId()) + ") to TTeam(" + TN(pp.getTargetTeamId()) + ").TSquad(" + SQN(pp.getTargetSquadId()) + ")", 3);
  3928.                 movePlayer(pp, pp.getTargetTeamId(), pp.getTargetSquadId(), true, true);
  3929.             }
  3930.         }
  3931.  
  3932.         private void getClanSquads(List<PlayerProfile> members, Dictionary<string, List<PlayerSquad>> clan_squads, Dictionary<string, int> clan_stats)
  3933.         {
  3934.             int neutralTeamId = 0;
  3935.             int noSquadId = 0;
  3936.             int max_squad_size = 4;
  3937.             int max_squads = 16;
  3938.  
  3939.             members.RemoveAll(delegate(PlayerProfile player)
  3940.             {
  3941.                 /* if player is not in a clan, ignore it*/
  3942.                 if (!player.isInClan())
  3943.                     return false;
  3944.  
  3945.  
  3946.  
  3947.                 string tag = player.getClanTag();
  3948.  
  3949.                 /* if less than two players for the clan, ignore it */
  3950.  
  3951.                 if (clan_stats[tag] < 2)
  3952.                     return false;
  3953.  
  3954.                 /* count total number of squads */
  3955.                 int total = 0;
  3956.                 foreach (KeyValuePair<string, List<PlayerSquad>> pair in clan_squads)
  3957.                     total += pair.Value.Count;
  3958.  
  3959.                 if (!clan_squads.ContainsKey(tag))
  3960.                     clan_squads[tag] = new List<PlayerSquad>();
  3961.  
  3962.                 List<PlayerSquad> squads = clan_squads[tag];
  3963.  
  3964.                 /* if there is no squads, of they are all full, add a new at the end */
  3965.                 if (squads.Count == 0 || squads[squads.Count - 1].getCount() == max_squad_size)
  3966.                 {
  3967.                     if (total >= max_squads)
  3968.                     {
  3969.                         if (squads.Count == 0)
  3970.                             clan_squads.Remove(tag);
  3971.  
  3972.                         DebugWrite(player + " ignored, max squads count reached", 3);
  3973.                         return false;
  3974.                     }
  3975.                     squads.Add(new PlayerSquad(neutralTeamId, getNextFreeClanSquadId(clan_squads)));
  3976.                 }
  3977.  
  3978.                 /* add player to the last squad in the clan */
  3979.                 if (movePlayer(player, neutralTeamId, squads[squads.Count - 1].getSquadId(), true))
  3980.                     squads[squads.Count - 1].addPlayer(player);
  3981.  
  3982.                 return true;
  3983.             });
  3984.         }
  3985.  
  3986.         private int getNextFreeClanSquadId(Dictionary<string, List<PlayerSquad>> clan_squads)
  3987.         {
  3988.             int count = 1;
  3989.             foreach (KeyValuePair<string, List<PlayerSquad>> pair in clan_squads)
  3990.                 foreach (PlayerSquad squad in pair.Value)
  3991.                     count++;
  3992.  
  3993.             return 16 + count;
  3994.         }
  3995.  
  3996.         private void getClanStats(List<PlayerProfile> members, Dictionary<string, int> stats)
  3997.         {
  3998.             foreach (PlayerProfile player in members)
  3999.             {
  4000.                 if (!player.isInClan())
  4001.                     continue;
  4002.  
  4003.                 string tag = player.getClanTag();
  4004.  
  4005.                 if (!stats.ContainsKey(tag))
  4006.                     stats[tag] = 0;
  4007.  
  4008.                 stats[tag]++;
  4009.             }
  4010.         }
  4011.  
  4012.         private void getClanStatsByTeam(List<PlayerProfile> members, Dictionary<string, int>[] stats)
  4013.         {
  4014.             for (int i = 0; i < members.Count; i++)
  4015.             {
  4016.                 PlayerProfile player = members[i];
  4017.                 if (!player.isInClan())
  4018.                     continue;
  4019.  
  4020.                 string tag = player.getClanTag();
  4021.                 int teamId = player.getTeamId();
  4022.                 int oppositeTeamId = getOpposingTeamId(teamId);
  4023.  
  4024.                 if (!stats[teamId].ContainsKey(tag))
  4025.                     stats[teamId].Add(tag, 0);
  4026.  
  4027.                 if (!stats[oppositeTeamId].ContainsKey(tag))
  4028.                     stats[oppositeTeamId].Add(tag, 0);
  4029.  
  4030.  
  4031.                 stats[teamId][tag]++;
  4032.             }
  4033.         }
  4034.  
  4035.  
  4036.         private int getSmallTeamId(int team1Id, int team1Count, int team2Id, int team2Count)
  4037.         {
  4038.             return (team1Count < team2Count) ? team1Id : team2Id;
  4039.         }
  4040.  
  4041.         private int getBigTeamId(int team1Id, int team1Count, int team2Id, int team2Count)
  4042.         {
  4043.             return (team1Count > team2Count) ? team1Id : team2Id;
  4044.         }
  4045.  
  4046.         private int getOpposingTeamId(int teamId)
  4047.         {
  4048.             return (teamId == 0) ? teamId : (teamId == 1) ? 2 : 1;
  4049.         }
  4050.  
  4051.  
  4052.  
  4053.  
  4054.  
  4055.         private int balanceTeams(DateTime now)
  4056.         {
  4057.  
  4058.  
  4059.             pluginState = PluginState.balance;
  4060.             setStartTime(pluginState, now.AddSeconds(1));
  4061.             DebugWrite("^b" + pluginState + "_live^n state started " + getStartTime(pluginState).ToString() + "^n^0", 1);
  4062.  
  4063.             bool keep_clans = getBooleanVarValue("keep_clans_live");
  4064.             Dictionary<int, int> player_count = getPlayerCount();
  4065.  
  4066.             if (player_count[1] == player_count[2])
  4067.                 return 0;
  4068.  
  4069.             int team_sz = serverInfo.MaxPlayerCount / 2;
  4070.             int neutral_team = 0;
  4071.             int bigger_team = (player_count[1] > player_count[2]) ? 1 : 2;
  4072.             int smaller_team = (player_count[1] > player_count[2]) ? 2 : 1;
  4073.             int total = player_count[1] + player_count[2];
  4074.             int difference = Math.Abs(player_count[1] - player_count[2]);
  4075.             int needed = difference / 2;
  4076.  
  4077.  
  4078.  
  4079.  
  4080.             DebugWrite("Total of ^b" + total + "^n player/s in server^0", 3);
  4081.             for (int i = 1; i < 3; i++)
  4082.             {
  4083.                 DebugWrite("^bTeam(" + TN(i) + ")^n has ^b" + player_count[i] + "^n player/s^0", 3);
  4084.             }
  4085.  
  4086.             DebugWrite("Teams differ by ^b" + difference + "^n player/s,  ^b" + needed + "^n player/s are needed on ^bTeam(" + TN(smaller_team) + ")^n^0", 3);
  4087.  
  4088.             int candidates = needed;
  4089.             if (getBooleanVarValue("wait_death"))
  4090.             {
  4091.                 candidates = getIntegerVarValue("wait_death_count");
  4092.  
  4093.                 int tsz = player_count[smaller_team];
  4094.  
  4095.                 // check that the candidate list does not exceed the team size
  4096.                 if (candidates > tsz)
  4097.                 {
  4098.                     DebugWrite("cannot use candidate list size of ^b" + candidates + "^n, Team(" + TN(smaller_team) + ") has only ^b" + tsz + "^n player" + ((tsz > 1) ? "s" : ""), 3);
  4099.                     candidates = tsz;
  4100.                 }
  4101.                
  4102.                 // check that the candidates list size is bigger than
  4103.                 if (candidates > needed)
  4104.                 {
  4105.                     needed = candidates;
  4106.                     DebugWrite("^bwait_death^n is on, will flag " + needed + " candidate" + ((needed > 1) ? "s" : "") + " for moving", 3);
  4107.                 }
  4108.             }
  4109.  
  4110.  
  4111.             DebugWrite("Building no-squad pool from ^bTeam(" + TN(bigger_team) + ")^n^0", 3);
  4112.             List<PlayerProfile> nosquad_pool = getNoSquadPlayers(bigger_team);
  4113.             DebugWrite("No-squad pool has ^b" + nosquad_pool.Count + "^n player/s^0", 3);
  4114.  
  4115.             DebugWrite("Building squad pool from ^bTeam(" + TN(bigger_team) + ")^n^0", 3);
  4116.             List<PlayerSquad> squad_pool = getNonEmptySquads(bigger_team);
  4117.             DebugWrite("Squad pool has ^b" + squad_pool.Count + "^n squads^0", 3);
  4118.  
  4119.  
  4120.             Dictionary<string, int>[] clan_stats = new Dictionary<string, int>[3];
  4121.             clan_stats[smaller_team] = new Dictionary<string, int>();
  4122.             clan_stats[bigger_team] = new Dictionary<string, int>();
  4123.             clan_stats[neutral_team] = new Dictionary<string, int>();
  4124.  
  4125.             if (keep_clans)
  4126.             {
  4127.                 DebugWrite("Keeping clans in same team", 3);
  4128.  
  4129.                 List<PlayerProfile> players_list = getPlayersProfile("");
  4130.                 /* collect statistics about clans */
  4131.                 DebugWrite("Collecting clan statistics", 3);
  4132.                 getClanStatsByTeam(players_list, clan_stats);
  4133.  
  4134.  
  4135.                 List<string> clanlist = new List<string>();
  4136.                 clanlist.AddRange(clan_stats[1].Keys);
  4137.  
  4138.                 DebugWrite("^b" + clanlist.Count + "^n clans in server: [^b" + String.Join("^n], [^b", clanlist.ToArray()) + "^n]", 3);
  4139.             }
  4140.  
  4141.             if (!getBooleanVarValue("keep_squads_live"))
  4142.             {
  4143.                 DebugWrite("^bkeep_squads_live^n is off, moving players to no-squad pool before balancing", 3);
  4144.                 foreach (PlayerSquad squad in squad_pool)
  4145.                     foreach (PlayerProfile player in squad.getMembers())
  4146.                         nosquad_pool.Add(player);
  4147.  
  4148.                 squad_pool.Clear();
  4149.             }
  4150.  
  4151.             /* sort the no-squad pool */
  4152.             DebugWrite("Sorting the no-squad pool by ^b" + getStringVarValue("live_sort") + "^n^0", 3);
  4153.             nosquad_pool.Sort(new Comparison<PlayerProfile>(getPlayerSort("live_sort")));
  4154.  
  4155.             for (int i = 0; i < nosquad_pool.Count; i++)
  4156.                 DebugWrite("      " + i + ". " + nosquad_pool[i] + "(" + getSortFieldValueStr(nosquad_pool[i], "live_sort") + ")", 3);
  4157.  
  4158.  
  4159.             DebugWrite("Moving ^b" + needed + "^n players from sorted no-squad pool to ^bTeam(" + TN(smaller_team) + ")^n^0", 3);
  4160.             while (needed > 0 && nosquad_pool.Count > 0)
  4161.             {
  4162.                 PlayerProfile player = nosquad_pool[0];
  4163.                 nosquad_pool.RemoveAt(0);
  4164.                 string tag = player.getClanTag();
  4165.  
  4166.                 /* if keeping clans together, and there are more than two players in the clan in the sever */
  4167.                 if (keep_clans && shouldSkipClanPlayer(player, smaller_team, bigger_team, clan_stats))
  4168.                     continue;
  4169.  
  4170.                 DebugWrite("Moving ^b" + player.ToString() + "^n to ^bTeam(^n" + TN(smaller_team) + ")^n^0", 3);
  4171.                 if (movePlayer(player, smaller_team, 0, true))
  4172.                     needed--;
  4173.             }
  4174.  
  4175.             /* if teams are balanced, we are done */
  4176.             if (needed == 0)
  4177.             {
  4178.                 DebugWrite("Teams should now be balanced!", 3);
  4179.                 return needed;
  4180.             }
  4181.  
  4182.             /* teams are not balanced, proceed on squad balancing */
  4183.  
  4184.             DebugWrite("Teams are still unbalanced, " + needed + " more player/s needed", 3);
  4185.  
  4186.             /* sort the squad pool */
  4187.             DebugWrite("Sorting the squad pool by ^b" + getStringVarValue("live_sort") + "^n^0", 3);
  4188.             squad_pool.Sort(new Comparison<PlayerSquad>(getSquadSort("live_sort")));
  4189.  
  4190.             for (int i = 0; i < squad_pool.Count; i++)
  4191.             {
  4192.                 DebugWrite("      " + i + ". " + squad_pool[i].ToString() + "(" + getSortFieldValueStr(squad_pool[i], "live_sort") + ")", 3);
  4193.             }
  4194.  
  4195.             DebugWrite("Moving squads from sorted squad pool to ^bTeam(" + TN(smaller_team) + ")^n^0", 3);
  4196.             while (needed > 0 && squad_pool.Count > 0)
  4197.             {
  4198.                 PlayerSquad squad = squad_pool[0];
  4199.                 squad_pool.RemoveAt(0);
  4200.  
  4201.  
  4202.                 int squad_sz = squad.getCount();
  4203.                 string squad_uid = squad.ToString();
  4204.                 string smaller_team_uid = "^bTeam(" + TN(smaller_team) + ")^n";
  4205.  
  4206.                 DebugWrite("^b" + needed + "^n players are needed on " + smaller_team_uid + "^0", 3);
  4207.                 DebugWrite(squad_uid + " has ^b" + squad_sz + "^n player/s^0", 2);
  4208.  
  4209.                 if (needed >= squad_sz)
  4210.                 {
  4211.                     if (keep_clans && shouldSkipClanSquad(squad, smaller_team, bigger_team, clan_stats))
  4212.                         continue;
  4213.  
  4214.  
  4215.                     /* we can move the entrie squad */
  4216.                     DebugWrite("Moving entire " + squad_uid + " to " + smaller_team_uid + "^0", 3);
  4217.                     squad_sz = moveSquad(squad, smaller_team, team_sz);
  4218.                     needed -= squad_sz;
  4219.                 }
  4220.                 else
  4221.                 {
  4222.                     /* we have to break up a squad */
  4223.                     PlayerSquad temp_squad = new PlayerSquad(squad.getTeamId(), squad.getSquadId());
  4224.  
  4225.                     DebugWrite("Breaking up " + squad_uid + " to get ^b" + needed + "^n player/s^0", 3);
  4226.                     DebugWrite("But, first I will sort the members of " + squad_uid, 3);
  4227.                     squad.sortMembers(getPlayerSort("live_sort"));
  4228.                     for (int i = 0; i < squad.getCount(); i++)
  4229.                         DebugWrite("      " + i + ". " + squad.getMembers()[i] + "(" + getSortFieldValueStr(squad.getMembers()[i], "live_sort") + ")", 3);
  4230.  
  4231.                     /* get as many players as needed */
  4232.                     while (needed > 0 && squad.getCount() > 0)
  4233.                     {
  4234.                         PlayerProfile player = squad.getMembers()[0];
  4235.                         squad.dropPlayer(player);
  4236.  
  4237.                         if (keep_clans && shouldSkipClanPlayer(player, smaller_team, bigger_team, clan_stats))
  4238.                             continue;
  4239.  
  4240.                         if (isInMoveWhiteList(player))
  4241.                             continue;
  4242.  
  4243.                         temp_squad.addPlayer(player);
  4244.                         DebugWrite("Player " + player + " selected to move to " + smaller_team_uid + "^0", 3);
  4245.                         needed--;
  4246.                     }
  4247.  
  4248.                     /* move the temporary squad */
  4249.                     moveSquad(temp_squad, smaller_team, team_sz);
  4250.                 }
  4251.             }
  4252.  
  4253.  
  4254.             if (needed == 0)
  4255.                 DebugWrite("Teams should now be balanced!", 3);
  4256.             else
  4257.                 DebugWrite("Teams are still ubalanced!", 3);
  4258.  
  4259.             return needed;
  4260.         }
  4261.  
  4262.         private bool shouldSkipClanSquad(PlayerSquad squad, int smaller_team, int bigger_team, Dictionary<string, int>[] clan_stats)
  4263.         {
  4264.             int squad_sz = squad.getCount();
  4265.             string tag = squad.getMajorityClanTag();
  4266.  
  4267.             if (tag.Length > 0 && (clan_stats[bigger_team][tag] + clan_stats[smaller_team][tag]) > 1)
  4268.             {
  4269.                 if (clan_stats[bigger_team][tag] >= clan_stats[smaller_team][tag])
  4270.                 {
  4271.                     DebugWrite("Skipping clan-squad " + squad.ToString() + " because majority of clan is in same team", 3);
  4272.                     return true;
  4273.                 }
  4274.  
  4275.                 /* update clan stats */
  4276.                 clan_stats[bigger_team][tag] -= squad_sz;
  4277.                 clan_stats[smaller_team][tag] += squad_sz;
  4278.             }
  4279.  
  4280.             return false;
  4281.         }
  4282.  
  4283.         private bool shouldSkipClanPlayer(PlayerProfile player, int smaller_team, int bigger_team, Dictionary<string, int>[] clan_stats)
  4284.         {
  4285.             string tag = player.getClanTag();
  4286.  
  4287.             if (!clan_stats[bigger_team].ContainsKey(tag))
  4288.                 clan_stats[bigger_team].Add(tag, 0);
  4289.  
  4290.             if (!clan_stats[smaller_team].ContainsKey(tag))
  4291.                 clan_stats[smaller_team].Add(tag, 0);
  4292.  
  4293.             if (player.isInClan() && (clan_stats[bigger_team][tag] + clan_stats[smaller_team][tag]) > 1)
  4294.             {
  4295.                 /* if the majority of the players in the clan are in this team, skip this player */
  4296.                 if (clan_stats[bigger_team][tag] >= clan_stats[smaller_team][tag])
  4297.                 {
  4298.                     DebugWrite("Skipping clan-player ^b" + player + "^n because majority of clan is in his team", 3);
  4299.                     return true;
  4300.                 }
  4301.  
  4302.                 /* update the clan stats */
  4303.                 clan_stats[bigger_team][tag]--;
  4304.                 clan_stats[smaller_team][tag]++;
  4305.             }
  4306.             return false;
  4307.         }
  4308.  
  4309.         private string getSortFieldValueStr(PlayerProfile player, string phase)
  4310.         {
  4311.             string sort_method = getStringVarValue(phase);
  4312.  
  4313.             if (sort_method.CompareTo("kdr_asc_round") == 0 || sort_method.CompareTo("kdr_desc_round") == 0)
  4314.                 return "kdr_round: " + Math.Round(player.getRoundKdr(), 2);
  4315.             else if (sort_method.CompareTo("score_asc_round") == 0 || sort_method.CompareTo("score_desc_round") == 0)
  4316.                 return "score_round: " + Math.Round(player.getRoundScore(), 2);
  4317.             else if (sort_method.CompareTo("spm_asc_round") == 0 || sort_method.CompareTo("spm_desc_round") == 0)
  4318.                 return "spm_round: " + Math.Round(player.getRoundSpm(), 2);
  4319.             else if (sort_method.CompareTo("kpm_asc_round") == 0 || sort_method.CompareTo("kpm_desc_round") == 0)
  4320.                 return "kpm_round: " + Math.Round(player.getRoundKpm(), 2);
  4321.             else if (sort_method.CompareTo("time_asc_round") == 0 || sort_method.CompareTo("time_desc_round") == 0)
  4322.                 return "time_round: " + player.getRoundTime();
  4323.             else if (sort_method.CompareTo("random_value") == 0 || sort_method.CompareTo("random_value") == 0)
  4324.                 return "random_value: " + player.getRandomValue();
  4325.             else if (sort_method.CompareTo("kdr_asc_online") == 0 || sort_method.CompareTo("kdr_desc_online") == 0)
  4326.                 return "kdr_online: " + Math.Round(player.getOnlineKdr(), 2);
  4327.             else if (sort_method.CompareTo("kpm_asc_online") == 0 || sort_method.CompareTo("kpm_desc_online") == 0)
  4328.                 return "kpm_online: " + Math.Round(player.getOnlineKpm(), 2);
  4329.             else if (sort_method.CompareTo("spm_asc_online") == 0 || sort_method.CompareTo("spm_desc_online") == 0)
  4330.                 return "spm_online: " + Math.Round(player.getOnlineSpm(), 2);
  4331.             else if (sort_method.CompareTo("kills_asc_online") == 0 || sort_method.CompareTo("kills_desc_online") == 0)
  4332.                 return "kills_online: " + player.getOnlineKills();
  4333.             else if (sort_method.CompareTo("deaths_asc_online") == 0 || sort_method.CompareTo("deaths_desc_online") == 0)
  4334.                 return "deaths_online: " + player.getOnlineDeaths();
  4335.             else if (sort_method.CompareTo("skill_asc_online") == 0 || sort_method.CompareTo("skill_desc_online") == 0)
  4336.                 return "skill_online: " + Math.Round(player.getOnlineSkill(), 2);
  4337.             else if (sort_method.CompareTo("quits_asc_online") == 0 || sort_method.CompareTo("quits_desc_online") == 0)
  4338.                 return "quits_online: " + Math.Round(player.getOnlineQuits(), 2);
  4339.             else if (sort_method.CompareTo("accuracy_asc_online") == 0 || sort_method.CompareTo("accuracy_desc_online") == 0)
  4340.                 return "accuracy_online: " + Math.Round(player.getOnlineAccuracy(), 2);
  4341.             else if (sort_method.CompareTo("score_asc_online") == 0 || sort_method.CompareTo("score_desc_online") == 0)
  4342.                 return "score_online: " + player.getOnlineScore();
  4343.             else if (sort_method.CompareTo("rank_asc_online") == 0 || sort_method.CompareTo("rank_desc_online") == 0)
  4344.                 return "rank_online: " + player.getOnlineRank();
  4345.  
  4346.             ConsoleWrite("^1^bWARNING^0^n: cannot find player sort method for ^b" + sort_method + "^0");
  4347.             return "";
  4348.         }
  4349.  
  4350.         private string getSortFieldValueStr(PlayerSquad squad, string phase)
  4351.         {
  4352.             string sort_method = getStringVarValue(phase);
  4353.  
  4354.             if (sort_method.CompareTo("kdr_asc_round") == 0 || sort_method.CompareTo("kdr_desc_round") == 0)
  4355.                 return "kdr_round: " + Math.Round(squad.getRoundKdr(), 2);
  4356.             else if (sort_method.CompareTo("score_asc_round") == 0 || sort_method.CompareTo("score_desc_round") == 0)
  4357.                 return "score_round: " + Math.Round(squad.getRoundScore(), 2);
  4358.             else if (sort_method.CompareTo("spm_asc_round") == 0 || sort_method.CompareTo("spm_desc_round") == 0)
  4359.                 return "spm_round: " + Math.Round(squad.getRoundSpm(), 2);
  4360.             else if (sort_method.CompareTo("kpm_asc_round") == 0 || sort_method.CompareTo("kpm_desc_round") == 0)
  4361.                 return "kpm_round: " + Math.Round(squad.getRoundKpm(), 2);
  4362.             else if (sort_method.CompareTo("time_asc_round") == 0 || sort_method.CompareTo("time_desc_round") == 0)
  4363.                 return "time_round: " + squad.getRoundTime();
  4364.             else if (sort_method.CompareTo("random_value") == 0 || sort_method.CompareTo("random_value") == 0)
  4365.                 return "random_value: " + squad.getRandomValue();
  4366.             else if (sort_method.CompareTo("kdr_asc_online") == 0 || sort_method.CompareTo("kdr_desc_online") == 0)
  4367.                 return "kdr_online: " + Math.Round(squad.getOnlineKdr(), 2);
  4368.             else if (sort_method.CompareTo("kpm_asc_online") == 0 || sort_method.CompareTo("kpm_desc_online") == 0)
  4369.                 return "kpm_online: " + Math.Round(squad.getOnlineKpm(), 2);
  4370.             else if (sort_method.CompareTo("spm_asc_online") == 0 || sort_method.CompareTo("spm_desc_online") == 0)
  4371.                 return "spm_online: " + Math.Round(squad.getOnlineSpm(), 2);
  4372.             else if (sort_method.CompareTo("kills_asc_online") == 0 || sort_method.CompareTo("kills_desc_online") == 0)
  4373.                 return "kills_online: " + Math.Round(squad.getOnlineKills(), 2);
  4374.             else if (sort_method.CompareTo("deaths_asc_online") == 0 || sort_method.CompareTo("deaths_desc_online") == 0)
  4375.                 return "deaths_online: " + Math.Round(squad.getOnlineDeaths(), 2);
  4376.             else if (sort_method.CompareTo("skill_asc_online") == 0 || sort_method.CompareTo("skill_desc_online") == 0)
  4377.                 return "skill_online: " + Math.Round(squad.getOnlineSkill(), 2);
  4378.             else if (sort_method.CompareTo("quits_asc_online") == 0 || sort_method.CompareTo("quits_desc_online") == 0)
  4379.                 return "quits_online: " + Math.Round(squad.getOnlineQuits(), 2);
  4380.             else if (sort_method.CompareTo("accuracy_asc_online") == 0 || sort_method.CompareTo("accuracy_desc_online") == 0)
  4381.                 return "accuracy_online: " + Math.Round(squad.getOnlineAccuracy(), 2);
  4382.             else if (sort_method.CompareTo("score_asc_online") == 0 || sort_method.CompareTo("score_desc_online") == 0)
  4383.                 return "score_online: " + Math.Round(squad.getOnlineScore(), 2);
  4384.             else if (sort_method.CompareTo("rank_asc_online") == 0 || sort_method.CompareTo("rank_desc_online") == 0)
  4385.                 return "rank_online: " + Math.Round(squad.getOnlineRank(), 2);
  4386.  
  4387.             ConsoleWrite("^1^bWARNING^0^n: cannot find squad sort method for ^b" + sort_method + "^0");
  4388.             return "";
  4389.         }
  4390.  
  4391.         private bool movePlayer(PlayerProfile player, int teamId, int squadId, bool force)
  4392.         {
  4393.             return movePlayer(player, teamId, squadId, force, false);
  4394.         }
  4395.  
  4396.         private bool movePlayer(PlayerProfile player, int teamId, int squadId)
  4397.         {
  4398.             return movePlayer(player, teamId, squadId, false, false);
  4399.         }
  4400.  
  4401.         private bool isInMoveWhiteList(PlayerProfile player)
  4402.         {
  4403.             bool result = isPlayerInWhiteList(player, "player_safe_wlist") || isPlayerInWhiteList(player, "clan_safe_wlist");
  4404.  
  4405.             if (getBooleanVarValue("use_extra_white_lists"))
  4406.                 result |= isPlayerInWhiteList(player, "player_move_wlist") || isPlayerInWhiteList(player, "clan_move_wlist");
  4407.  
  4408.             return result;
  4409.         }
  4410.  
  4411.         private bool isInKickWhiteList(PlayerProfile player)
  4412.         {
  4413.             bool result = isPlayerInWhiteList(player, "player_safe_wlist") || isPlayerInWhiteList(player, "clan_safe_wlist");
  4414.  
  4415.             if (getBooleanVarValue("use_extra_white_lists"))
  4416.                 result |= isPlayerInWhiteList(player, "player_kick_wlist") || isPlayerInWhiteList(player, "clan_kick_wlist");
  4417.  
  4418.             return result;
  4419.         }
  4420.  
  4421.  
  4422.         private bool isPlayerInWhiteList(PlayerProfile player, String list_name)
  4423.         {
  4424.             if (!getBooleanVarValue("use_white_list"))
  4425.                 return false;
  4426.  
  4427.             if (!getPluginVars().Contains(list_name))
  4428.             {
  4429.                 ConsoleWrite("^1^bWARNING: ^n^0 unknown white list ^b" + list_name + "^n");
  4430.                 return false;
  4431.             }
  4432.  
  4433.             List<String> whitelist = getStringListVarValue(list_name);
  4434.             if (whitelist.Count == 0)
  4435.                 return false;
  4436.  
  4437.             String field = "";
  4438.             if (Regex.Match(list_name, @"clan").Success)
  4439.                 field = player.getClanTag();
  4440.             else if (Regex.Match(list_name, @"player").Success)
  4441.                 field = player.name;
  4442.             else
  4443.             {
  4444.                 ConsoleWrite("^1^bWARNING:^0^n white list ^b" + list_name + "^n does not contain 'player' or 'clan' sub-string");
  4445.                 return false;
  4446.             }
  4447.  
  4448.             if (Regex.Match(field, @"^\s*$").Success)
  4449.                 return false;
  4450.  
  4451.             return whitelist.Contains(field);
  4452.         }
  4453.  
  4454.  
  4455.         private bool movePlayer(PlayerProfile player, int teamId, int squadId, bool force, bool ignore_white_list)
  4456.         {
  4457.             if (player == null)
  4458.                 return false;
  4459.  
  4460.             if (!force && player.getTeamId() == teamId && player.getSquadId() == squadId)
  4461.             {
  4462.                 ConsoleWrite("^1^bWARNING^0^n: not moving ^b" + player + "^n to same Team(" + TN(teamId) + ").Squad(" + SQN(squadId) + ")");
  4463.                 return false;
  4464.             }
  4465.             else if (!ignore_white_list && isInMoveWhiteList(player))
  4466.             {
  4467.                 ConsoleWrite("^b" + player.ToString() + "^n in white-list, will not move to Team(" + TN(teamId) + ").Squad(" + SQN(squadId) + ")");
  4468.                 return false;
  4469.             }
  4470.  
  4471.  
  4472.             /* firt move player to the no-squad, to guarantee a spot (unless he is already goin to the no-squad, or stays in same team) */
  4473.             if ((squadId != 0 || player.getTeamId() != teamId) && !(virtual_mode || getBooleanVarValue("virtual_mode")))
  4474.             {
  4475.                 if (sleep)
  4476.                     Thread.Sleep(100);
  4477.                 ExecCommand("admin.movePlayer", player.name, teamId.ToString(), "0", "true");
  4478.             }
  4479.  
  4480.             /* in virtual mode, don't actually do the move */
  4481.             if (!(virtual_mode || getBooleanVarValue("virtual_mode")))
  4482.             {
  4483.                 if (sleep)
  4484.                     Thread.Sleep(100);
  4485.                 ExecCommand("admin.movePlayer", player.name, teamId.ToString(), squadId.ToString(), "true");
  4486.             }
  4487.             player.setTeamId(teamId);
  4488.             player.setSquadId(squadId);
  4489.             return true;
  4490.         }
  4491.  
  4492.  
  4493.         /* best effort to move an entire squad into another team withouth breaking up */
  4494.  
  4495.         private int moveSquad(PlayerSquad squad, int teamId, int team_sz)
  4496.         {
  4497.             int players_moved = 0;
  4498.             if (squad == null)
  4499.                 return 0;
  4500.  
  4501.             /* first move all players to the opposite team without squad (to guarantee a spot)*/
  4502.             int squadId = 0;
  4503.             int noSquadId = 0;
  4504.  
  4505.  
  4506.             List<PlayerProfile> squad_players = squad.getMembers();
  4507.  
  4508.             /* find a squad on teamId with enough space */
  4509.             List<PlayerSquad> squads = getAllSquads(teamId);
  4510.  
  4511.  
  4512.             /* find first empty squad */
  4513.  
  4514.             foreach (PlayerSquad sq in squads)
  4515.             {
  4516.                 if (sq.getCount() == 0)
  4517.                 {
  4518.                     DebugWrite("Found empty squad " + sq + ", for " + squad, 3);
  4519.                     while (squad.getCount() > 0)
  4520.                     {
  4521.                         PlayerProfile pp = squad.removeRandomPlayer();
  4522.                         DebugWrite("Moving ^b" + pp + "^n to Team(" + TN(teamId) + ").Squad(" + SQN(sq.getSquadId()) + ")", 3);
  4523.                         if (movePlayer(pp, teamId, sq.getSquadId()))
  4524.                             players_moved++;
  4525.  
  4526.                     }
  4527.                     break;
  4528.                 }
  4529.             }
  4530.  
  4531.             if (squad.getCount() == 0)
  4532.                 return players_moved;
  4533.  
  4534.             ConsoleWrite("^1^bWARNING^0^n: Could not find an empty squad on ^bTeam(" + TN(teamId) + ")^n for " + squad.ToString());
  4535.             ConsoleWrite("Looking now for squads that are not full^n");
  4536.  
  4537.             /* sort the squads in increasing order of player count */
  4538.  
  4539.             squads.Sort(new Comparison<PlayerSquad>(squad_count_asc_cmp));
  4540.  
  4541.             for (int i = 0; i < squads.Count; i++)
  4542.             {
  4543.                 PlayerSquad sorted_squad = squads[i];
  4544.                 if (sorted_squad.getSquadId() > 8)
  4545.                     continue;
  4546.  
  4547.                 if (sorted_squad.getFreeSlots() > 0 && squad_players.Count > 0)
  4548.                     DebugWrite("Found " + sorted_squad.getFreeSlots() + " free slots on " + sorted_squad, 3);
  4549.  
  4550.                 while (sorted_squad.getFreeSlots() > 0 && squad_players.Count > 0)
  4551.                 {
  4552.                     PlayerProfile squad_player = squad_players[0];
  4553.                     squad_players.RemoveAt(0);
  4554.                     DebugWrite("Moving ^b" + squad_player + "^n to Team(" + TN(teamId) + ").Squad(" + SQN(sorted_squad.getSquadId()) + ")", 3);
  4555.                     if (movePlayer(squad_player, teamId, sorted_squad.getSquadId()))
  4556.                         players_moved++;
  4557.                 }
  4558.             }
  4559.  
  4560.             foreach (PlayerProfile pp in squad_players)
  4561.             {
  4562.                 ConsoleWrite("^1^bWARNING^0^n: could not find squad on ^bTeam(" + TN(teamId) + ")^n for ^b" + pp + "^n^0");
  4563.                 DebugWrite("Moving ^b" + pp + "^n to Team(" + TN(teamId) + ").Squad(" + SQN(noSquadId) + ")", 3);
  4564.                 if (movePlayer(pp, teamId, noSquadId))
  4565.                     players_moved++;
  4566.             }
  4567.  
  4568.             return players_moved;
  4569.  
  4570.         }
  4571.  
  4572.         private List<PlayerProfile> removePlayers(List<PlayerProfile> player_list, int max_size)
  4573.         {
  4574.             if (players == null || players.Count == 0)
  4575.             {
  4576.                 ConsoleWrite("^1^bWARNING^0^n: cannot make a squad without any players");
  4577.                 return null;
  4578.             }
  4579.  
  4580.             List<PlayerProfile> removed = new List<PlayerProfile>();
  4581.             while (player_list.Count > 0 && removed.Count <= max_size)
  4582.             {
  4583.                 removed.Add(player_list[0]);
  4584.                 player_list.RemoveAt(0);
  4585.             }
  4586.  
  4587.             return removed;
  4588.         }
  4589.  
  4590.  
  4591.         public Dictionary<int, int> getPlayerCount()
  4592.         {
  4593.  
  4594.             /* initialize hash with player count for 16 teams*/
  4595.             Dictionary<int, int> player_count = new Dictionary<int, int>();
  4596.             for (int i = 0; i < 16; i++)
  4597.                 player_count[i] = 0;
  4598.  
  4599.             List<PlayerProfile> player_list = getPlayersProfile("");
  4600.  
  4601.             foreach (PlayerProfile player in player_list)
  4602.                 player_count[player.getTeamId()]++;
  4603.  
  4604.  
  4605.             return player_count;
  4606.         }
  4607.  
  4608.  
  4609.  
  4610.         private List<PlayerProfile> getNoSquadPlayers(int teamId)
  4611.         {
  4612.             Dictionary<int, PlayerSquad> squads = getSquads(teamId);
  4613.             /* return the members of the no-squad */
  4614.             return squads[0].getMembers();
  4615.         }
  4616.  
  4617.  
  4618.         private List<PlayerSquad> getAllSquads(int teamId)
  4619.         {
  4620.             Dictionary<int, PlayerSquad> squads = getSquads(teamId);
  4621.  
  4622.             /* remove the no-squad */
  4623.             squads.Remove(0);
  4624.  
  4625.             List<PlayerSquad> list = new List<PlayerSquad>();
  4626.             foreach (KeyValuePair<int, PlayerSquad> pair in squads)
  4627.                 list.Add(pair.Value);
  4628.  
  4629.             return list;
  4630.         }
  4631.  
  4632.  
  4633.  
  4634.         private List<PlayerSquad> getNonEmptySquads(int teamId)
  4635.         {
  4636.             Dictionary<int, PlayerSquad> squads = getSquads(teamId);
  4637.  
  4638.             /* remove the no-squad */
  4639.             squads.Remove(0);
  4640.  
  4641.             /* get only the non-empty squads */
  4642.             List<PlayerSquad> list = new List<PlayerSquad>();
  4643.             foreach (KeyValuePair<int, PlayerSquad> pair in squads)
  4644.                 if (pair.Value.getCount() > 0)
  4645.                     list.Add(pair.Value);
  4646.  
  4647.             return list;
  4648.         }
  4649.  
  4650.         private void listSquad(PlayerSquad sq)
  4651.         {
  4652.             List<PlayerProfile> members = sq.getMembers();
  4653.  
  4654.             DebugWrite("Team(^b" + TN(sq.getTeamId()) + "^n).Squad(^b" + SQN(sq.getSquadId()) + "^n): " + sq.getCount() + " players", 3);
  4655.             int count = 1;
  4656.             foreach (PlayerProfile pp in members)
  4657.                 DebugWrite("    " + count++ + ".  ^b" + pp + "^n", 3);
  4658.         }
  4659.  
  4660.         private void listSquads(List<PlayerSquad> sqs)
  4661.         {
  4662.             foreach (PlayerSquad sq in sqs)
  4663.                 listSquad(sq);
  4664.         }
  4665.  
  4666.         private Dictionary<int, PlayerSquad> getSquads(int teamId)
  4667.         {
  4668.             int num_squads = 8;
  4669.             if (teamId == 0)
  4670.                 num_squads = 16;
  4671.  
  4672.             List<PlayerProfile> player_list = getPlayersProfile("");
  4673.  
  4674.             Dictionary<int, PlayerSquad> squads = new Dictionary<int, PlayerSquad>();
  4675.             for (int i = 0; i <= num_squads; i++)
  4676.                 squads[i] = new PlayerSquad(teamId, i);
  4677.  
  4678.             foreach (PlayerProfile player in player_list)
  4679.             {
  4680.                 if (player.getTeamId() == teamId && squads.ContainsKey(player.getSquadId()))
  4681.                     squads[player.getSquadId()].addPlayer(player);
  4682.             }
  4683.  
  4684.             return squads;
  4685.         }
  4686.  
  4687.         private bool isTimeLeft(int remain_time, int msg_display_time, int msg_interval_time, int countdown_time)
  4688.         {
  4689.             return (remain_time % msg_interval_time) == 0 && (remain_time - msg_display_time) >= countdown_time;
  4690.         }
  4691.  
  4692.         public bool isServerEmpty()
  4693.         {
  4694.             return players.Count == 0;
  4695.         }
  4696.  
  4697.         public void forwardTicks(int count)
  4698.         {
  4699.             utc = utc.AddSeconds(count);
  4700.         }
  4701.  
  4702.  
  4703.         public void ConsoleWrite(string msg)
  4704.         {
  4705.             string prefix = "[^b" + GetPluginName() + "^n] ";
  4706.             this.ExecuteCommand("procon.protected.pluginconsole.write", prefix + msg);
  4707.         }
  4708.  
  4709.         public void DebugWrite(string msg, int level)
  4710.         {
  4711.             if (getIntegerVarValue("debug_level") >= level)
  4712.                 ConsoleWrite(msg);
  4713.         }
  4714.  
  4715.         public void OnPluginDisable()
  4716.         {
  4717.            
  4718.             plugin_enabled = false;
  4719.  
  4720.             unloadSettings();
  4721.  
  4722.             ConsoleWrite("signaling stats fetching thread to stop");
  4723.             wake_handle.Set();
  4724.             scratch_handle.Set();
  4725.             ConsoleWrite("^b^1Disabled =(^0");
  4726.  
  4727.         }
  4728.  
  4729.         public String getPluginVariableGroup(String name)
  4730.         {
  4731.             foreach (KeyValuePair<String, List<String>> group_pair in settings_group)
  4732.                 if (group_pair.Value.Contains(name))
  4733.                     return group_pair.Key;
  4734.  
  4735.             return "Settings";
  4736.         }
  4737.  
  4738.         public int getGroupOrder(String name)
  4739.         {
  4740.             //if (settings_group_order.ContainsKey(name))
  4741.             //    return settings_group_order[name];
  4742.  
  4743.  
  4744.             Dictionary<int, String> reverse = new Dictionary<int, string>();
  4745.             foreach (KeyValuePair<String, int> pair in settings_group_order)
  4746.                 reverse.Add(pair.Value, pair.Key);
  4747.  
  4748.             int offset = 0;
  4749.             for (int i = 0; i <= reverse.Count; i++)
  4750.                 if (!reverse.ContainsKey(i))
  4751.                     continue;
  4752.                 else
  4753.                 {
  4754.                     if (shouldSkipGroup(reverse[i]))
  4755.                         continue;
  4756.                     offset++;
  4757.                     if (name.Equals(reverse[i]))
  4758.                         return offset;
  4759.                 }
  4760.  
  4761.  
  4762.             return offset;
  4763.         }
  4764.  
  4765.         public bool shouldSkipGroup(String name)
  4766.         {
  4767.  
  4768.             if ((name.Equals("Round Balancer") || name.Equals("Round Interval")) && !getBooleanVarValue("balance_round"))
  4769.                 return true;
  4770.  
  4771.             if (name.Equals("Live Balancer") && !getBooleanVarValue("balance_live"))
  4772.                 return true;
  4773.  
  4774.             if (name.Equals("Whitelist") && !getBooleanVarValue("use_white_list"))
  4775.                 return true;
  4776.  
  4777.             return false;
  4778.  
  4779.         }
  4780.  
  4781.         public bool shouldSkipVariable(String name)
  4782.         {
  4783.  
  4784.             if (Regex.Match(name, @"^(?:player|clan)_(?:move|kick)_wlist").Success && !getBooleanVarValue("use_extra_white_lists"))
  4785.                 return true;
  4786.  
  4787.             if (name.Equals("wait_death_count") && !getBooleanVarValue("wait_death"))
  4788.                 return true;
  4789.  
  4790.             return false;
  4791.  
  4792.         }
  4793.  
  4794.         public List<CPluginVariable> GetDisplayPluginVariables()
  4795.         {
  4796.             List<CPluginVariable> lstReturn = new List<CPluginVariable>();
  4797.  
  4798.  
  4799.  
  4800.             //lstReturn.Add(new CPluginVariable("Settings|Refresh", typeof(string), "| edit this field to refresh settings |"));
  4801.  
  4802.             List<string> vars = getPluginVars(true);
  4803.  
  4804.  
  4805.  
  4806.  
  4807.  
  4808.             foreach (string var_name in vars)
  4809.             {
  4810.                 String group_name = getPluginVariableGroup(var_name);
  4811.                 String var_type = "string";
  4812.                 String var_value = getPluginVarValue(var_name);
  4813.                 int group_order = getGroupOrder(group_name);
  4814.  
  4815.                 if (shouldSkipGroup(group_name))
  4816.                     continue;
  4817.  
  4818.                 if (shouldSkipVariable(var_name))
  4819.                     continue;
  4820.  
  4821.                 if (var_name.Equals("live_sort") || var_name.Equals("round_sort"))
  4822.                     var_type = "enum." + var_name + "(" + String.Join("|", getAllowedSorts().ToArray()) + ")";
  4823.                 else if (var_name.Equals("pass"))
  4824.                     var_value = Regex.Replace(getPluginVarValue(var_name), @".", "*");
  4825.  
  4826.                 lstReturn.Add(new CPluginVariable(group_order + ". " + group_name + "|" + var_name, var_type, var_value));
  4827.             }
  4828.  
  4829.             return lstReturn;
  4830.         }
  4831.  
  4832.         //Lists all of the plugin variables.
  4833.         public List<CPluginVariable> GetPluginVariables()
  4834.         {
  4835.             List<CPluginVariable> lstReturn = new List<CPluginVariable>();
  4836.  
  4837.             List<string> vars = getPluginVars();
  4838.             foreach (string var in vars)
  4839.                 lstReturn.Add(new CPluginVariable(var, typeof(string), getPluginVarValue(var)));
  4840.  
  4841.             return lstReturn;
  4842.         }
  4843.  
  4844.  
  4845.         public void SetPluginVariable(string var, string val)
  4846.         {
  4847.             //ConsoleWrite("setting " + var + " to " + val);
  4848.             if (var.ToLower().Contains("refresh"))
  4849.                 return;
  4850.  
  4851.  
  4852.  
  4853.  
  4854.  
  4855.             setPluginVarValue(var, val);
  4856.  
  4857.  
  4858.         }
  4859.  
  4860.  
  4861.         public void OnPlayerJoin(string strSoldierName)
  4862.         {
  4863.             battleLogConnect();
  4864.         }
  4865.  
  4866.         public void OnPlayerKilled(Kill killInfo)
  4867.         {
  4868.  
  4869.             if (killInfo == null)
  4870.                 return;
  4871.  
  4872.             CPlayerInfo killer = killInfo.Killer;
  4873.             CPlayerInfo victim = killInfo.Victim;
  4874.  
  4875.             PlayerProfile vp = getPlayerProfile(victim.SoldierName);
  4876.             PlayerProfile kp = getPlayerProfile(killer.SoldierName);
  4877.  
  4878.             if (vp != null)
  4879.             {
  4880.                 vp.state = PlayerState.dead;
  4881.                 vp.updateInfo(victim);
  4882.                 vp.updateLastDeath();
  4883.  
  4884.                 if (vp.getDelayedTeamId() > 0)
  4885.                 {
  4886.                     DebugWrite("Player " + vp + " has died, he was flagged to be moved to ^bDTeam(" + TN(vp.getDelayedTeamId()) + ").DSquad(" + SQN(vp.getDelayedSquadId()) + ")^n", 3);
  4887.                     /* do not skip the balance check, this is delayed move, teams may already be balanced */
  4888.                     enforceDelayedMove(vp);
  4889.                 }
  4890.             }
  4891.  
  4892.             if (kp != null)
  4893.             {
  4894.                 kp.updateInfo(killer);
  4895.                 kp.updateLastKill();
  4896.             }
  4897.         }
  4898.  
  4899.         private void enforceDelayedMove(PlayerProfile vp)
  4900.         {
  4901.  
  4902.             int dtid = vp.getDelayedTeamId();
  4903.             int dsid = vp.getDelayedSquadId();
  4904.  
  4905.             vp.resetDelayedTeamSquad();
  4906.  
  4907.             /* if player is already in the delayed team, ignore him */
  4908.             if (dtid == vp.getTeamId())
  4909.             {
  4910.                 DebugWrite("Player " + vp + " is already in to ^bDTeam(" + TN(dtid) + ")^n, will skip", 3);
  4911.                 return;
  4912.             }
  4913.  
  4914.  
  4915.             /* if teams are already balanced, ignore this player */
  4916.             DebugWrite("I will now re-check if teams are balanced", 3);
  4917.             if (teamsBalanced())
  4918.             {
  4919.                 DebugWrite("Teams are balanced, will not move player " + vp, 3);
  4920.                 return;
  4921.             }
  4922.  
  4923.             DebugWrite("Moving player " + vp + " from ^bTeam(" + TN(vp.getTeamId()) + ").Squad(" + SQN(vp.getSquadId()) + ")^n to ^bDTeam(" + TN(dtid) + ").DSquad(" + SQN(dsid) + ")^n", 3);
  4924.             movePlayer(vp, dtid, dsid);
  4925.  
  4926.         }
  4927.  
  4928.         private void enforceImmediateMove(PlayerProfile vp)
  4929.         {
  4930.  
  4931.             int dtid = vp.getDelayedTeamId();
  4932.             int dsid = vp.getDelayedSquadId();
  4933.  
  4934.             vp.resetDelayedTeamSquad();
  4935.  
  4936.             DebugWrite("Moving player " + vp + " from ^bTeam(" + TN(vp.getSavedTeamId()) + ").Squad(" + SQN(vp.getSavedSquadId()) + ")^n to ^bDTeam(" + TN(dtid) + ").DSquad(" + SQN(dsid) + ")^n", 3);
  4937.             movePlayer(vp, dtid, dsid);
  4938.  
  4939.         }
  4940.  
  4941.  
  4942.  
  4943.  
  4944.         public void battleLogConnect()
  4945.         {
  4946.             /*
  4947.             if (!blog.isReady())
  4948.             {
  4949.                 if (attempts < 2)
  4950.                 {
  4951.                     ConsoleWrite("Attempting to connect to battlelog.battlefield.com");
  4952.                     attempts++;
  4953.  
  4954.                     blog.connect();
  4955.  
  4956.                 }
  4957.             }*/
  4958.         }
  4959.  
  4960.  
  4961.         public void OnPlayerLeft(string strSoldierName)
  4962.         {
  4963.             PlayerProfile player = getPlayerProfile(strSoldierName);
  4964.             if (player != null)
  4965.                 player.state = PlayerState.left;
  4966.  
  4967.             if (this.players.ContainsKey(strSoldierName))
  4968.                 this.players.Remove(strSoldierName);
  4969.  
  4970.         }
  4971.  
  4972.         public virtual void OnPlayerKickedByAdmin(string soldierName, string reason)
  4973.         {
  4974.             PlayerProfile player = getPlayerProfile(soldierName);
  4975.             if (player != null)
  4976.             {
  4977.                 player.state = PlayerState.kicked;
  4978.                 this.players.Remove(player.name);
  4979.             }
  4980.         }
  4981.  
  4982.         public virtual void OnPlayerMovedByAdmin(string soldierName, int destinationTeamId, int destinationSquadId, bool forceKilled)
  4983.         {
  4984.             PlayerProfile player = getPlayerProfile(soldierName);
  4985.             if (player == null)
  4986.                 return;
  4987.  
  4988.             player.state = PlayerState.dead;
  4989.  
  4990.         }
  4991.  
  4992.  
  4993.  
  4994.         public void OnGlobalChat(string strSpeaker, string strMessage)
  4995.         {
  4996.             if (isInGameCommand(strMessage))
  4997.                 inGameCommand(strSpeaker, strMessage);
  4998.  
  4999.             PlayerProfile player = getPlayerProfile(strSpeaker);
  5000.             if (player != null)
  5001.                 player.updateLastChat();
  5002.         }
  5003.  
  5004.  
  5005.         public void OnTeamChat(string strSpeaker, string strMessage, int iTeamID)
  5006.         {
  5007.             if (isInGameCommand(strMessage))
  5008.                 inGameCommand(strSpeaker, strMessage);
  5009.  
  5010.             PlayerProfile player = getPlayerProfile(strSpeaker);
  5011.             if (player != null)
  5012.                 player.updateLastChat();
  5013.         }
  5014.  
  5015.  
  5016.         public void OnSquadChat(string strSpeaker, string strMessage, int iTeamID, int iSquadID)
  5017.         {
  5018.             if (isInGameCommand(strMessage))
  5019.                 inGameCommand(strSpeaker, strMessage);
  5020.  
  5021.             PlayerProfile player = getPlayerProfile(strSpeaker);
  5022.             if (player != null)
  5023.                 player.updateLastChat();
  5024.         }
  5025.  
  5026.  
  5027.         private bool isInGameCommand(string str)
  5028.         {
  5029.             if (Regex.Match(str, @"^\s*[@/!]").Success)
  5030.                 return true;
  5031.  
  5032.             return false;
  5033.         }
  5034.  
  5035.         public void OnLevelStarted()
  5036.         {
  5037.             DebugWrite("Level starting!", 3);
  5038.             level_started = true;
  5039.         }
  5040.  
  5041.  
  5042.         public void OnPunkbusterplayerStatsCmd(CPunkbusterInfo cpbiPlayer)
  5043.         {
  5044.         }
  5045.  
  5046.  
  5047.         public void processNewPlayer(CPunkbusterInfo cpbiPlayer)
  5048.         {
  5049.             if (this.players.ContainsKey(cpbiPlayer.SoldierName))
  5050.                 this.players[cpbiPlayer.SoldierName].pbinfo = cpbiPlayer;
  5051.             else
  5052.             {
  5053.                 lock (mutex)
  5054.                 {
  5055.  
  5056.                     // add new player to the queue, and wake the stats fetching loop
  5057.                     if (!(new_player_queue.ContainsKey(cpbiPlayer.SoldierName) ||
  5058.                           players.ContainsKey(cpbiPlayer.SoldierName) ||
  5059.                           new_players_batch.ContainsKey(cpbiPlayer.SoldierName)))
  5060.                     {
  5061.                         ConsoleWrite("Queueing ^b" + cpbiPlayer.SoldierName + "^n for stats fetching");
  5062.                         new_player_queue.Add(cpbiPlayer.SoldierName, cpbiPlayer);
  5063.                         wake_handle.Set();
  5064.                     }
  5065.  
  5066.                 }
  5067.             }
  5068.         }
  5069.  
  5070.  
  5071.         Dictionary<String, PlayerProfile> new_players_batch = new Dictionary<string, PlayerProfile>();
  5072.  
  5073.         Object mutex = new Object();
  5074.         EventWaitHandle scratch_handle = new EventWaitHandle(false, EventResetMode.ManualReset);
  5075.         public void stats_fetching_loop()
  5076.         {
  5077.             ConsoleWrite("Starting stats fetching thread");
  5078.             getPlayerList();
  5079.             while (true)
  5080.             {
  5081.                 if (new_player_queue.Count == 0)
  5082.                 {
  5083.                     // if there are no more players, put yourself to sleep
  5084.                     ConsoleWrite("No new players, stats fetching thread going to sleep");
  5085.                     wake_handle.Reset();
  5086.                     wake_handle.WaitOne();
  5087.                     ConsoleWrite("Stats fetching thread is now awake!");
  5088.                 }
  5089.  
  5090.  
  5091.                 InsaneBalancer plugin = this;
  5092.  
  5093.                 while (new_player_queue.Count > 0)
  5094.                 {
  5095.                     if (!plugin_enabled)
  5096.                         break;
  5097.  
  5098.                     List<String> keys = new List<string>(new_player_queue.Keys);
  5099.  
  5100.                     String name = keys[keys.Count - 1];
  5101.  
  5102.                     CPunkbusterInfo info = null;
  5103.                     new_player_queue.TryGetValue(name, out info);
  5104.  
  5105.                     if (info == null)
  5106.                         continue;
  5107.  
  5108.                     // make sure I am the only one modifying these dictionarie at this time
  5109.                     lock (mutex)
  5110.                     {
  5111.                         if (new_player_queue.ContainsKey(name))
  5112.                             new_player_queue.Remove(name);
  5113.  
  5114.                         if (!new_players_batch.ContainsKey(name))
  5115.                             new_players_batch.Add(name, null);
  5116.                     }
  5117.  
  5118.                     String msg = new_player_queue.Count + " more player" + ((new_player_queue.Count > 1) ? "s" : "") + " in queue";
  5119.                     if (new_player_queue.Count == 0)
  5120.                         msg = "no more players in queue";
  5121.  
  5122.                     plugin.ConsoleWrite("Getting battlelog stats for ^b" + name + "^n, " + msg);
  5123.                     if (new_players_batch.ContainsKey(info.SoldierName))
  5124.                         new_players_batch[name] = new PlayerProfile(plugin, info);
  5125.                 }
  5126.  
  5127.                 // abort the thread if the plugin was disabled
  5128.                 if (!plugin_enabled)
  5129.                 {
  5130.                     plugin.ConsoleWrite("detected that plugin was disabled, aborting stats fetching thread");
  5131.                     lock (mutex)
  5132.                     {
  5133.                         new_player_queue.Clear();
  5134.                         new_players_batch.Clear();
  5135.                         scratch_list.Clear();
  5136.                     }
  5137.                     return;
  5138.                 }
  5139.  
  5140.                 ConsoleWrite("Done fetching stats, " + new_players_batch.Count + " player" + ((new_players_batch.Count > 1) ? "s" : "") + " in new batch, waiting for players list now");
  5141.                 scratch_handle.Reset();
  5142.                 getPlayerList();
  5143.                 scratch_handle.WaitOne();
  5144.                 scratch_handle.Reset();
  5145.                 lock (mutex)
  5146.                 {
  5147.                     // remove the nulls, and the ones that left
  5148.                     List<String> players_to_remove = new List<string>();
  5149.                     foreach (KeyValuePair<String, PlayerProfile> pair in new_players_batch)
  5150.                         if (pair.Value == null || !scratch_list.Contains(pair.Key))
  5151.                             if (!players_to_remove.Contains(pair.Key))
  5152.                             {
  5153.                                 DebugWrite("Looks like ^b" + pair.Key + "^n left, removing him from new batch", 3);
  5154.                                 players_to_remove.Add(pair.Key);
  5155.                             }
  5156.                        
  5157.  
  5158.                     // now remove them
  5159.                     foreach (String pname in players_to_remove)
  5160.                         if (new_players_batch.ContainsKey(pname))
  5161.                             new_players_batch.Remove(pname);
  5162.  
  5163.                     if (new_players_batch.Count > 0)
  5164.                         ConsoleWrite("Queue exhausted, will insert now a batch of " + new_players_batch.Count + " player" + ((new_players_batch.Count>1) ? "s" : ""));
  5165.                     foreach (KeyValuePair<String, PlayerProfile> pair in new_players_batch)
  5166.                         if (pair.Value != null && scratch_list.Contains(pair.Key))
  5167.                             plugin.players.Add(pair.Key, pair.Value);
  5168.  
  5169.                     new_players_batch.Clear();
  5170.                 }
  5171.  
  5172.             }
  5173.  
  5174.         }
  5175.  
  5176.         Object info_mutex = new Object();
  5177.         public void OnServerInfo(CServerInfo csiServerInfo)
  5178.         {
  5179.             lock (info_mutex)
  5180.             {
  5181.                 this.serverInfo = csiServerInfo;
  5182.             }
  5183.         }
  5184.  
  5185.  
  5186.  
  5187.         public void OnPlayerTeamChange(string soldierName, int teamId, int squadId)
  5188.         {
  5189.             PlayerProfile player = getPlayerProfile(soldierName);
  5190.             if (player == null)
  5191.                 return;
  5192.  
  5193.             player.state = PlayerState.dead;
  5194.             player.setTeamId(teamId);
  5195.             player.setSquadId(squadId);
  5196.  
  5197.         }
  5198.  
  5199.  
  5200.         public void OnPlayerSquadChange(string strSoldierName, int iTeamID, int iSquadID)
  5201.         {
  5202.             PlayerProfile player = getPlayerProfile(strSoldierName);
  5203.             if (player == null)
  5204.                 return;
  5205.  
  5206.             player.setSquadId(iSquadID);
  5207.             player.setTeamId(iTeamID);
  5208.         }
  5209.  
  5210.         public void OnplayersStatsCmd(List<CPlayerInfo> lstPlayers, CPlayerSubset cpsSubset)
  5211.         {
  5212.  
  5213.             if (cpsSubset.Subset == CPlayerSubset.PlayerSubsetType.All)
  5214.                 foreach (CPlayerInfo cpiPlayer in lstPlayers)
  5215.                     if (this.players.ContainsKey(cpiPlayer.SoldierName))
  5216.                         this.players[cpiPlayer.SoldierName].updateInfo(cpiPlayer);
  5217.  
  5218.             /* fail safe to get the maximum number of players in server */
  5219.             if (lstPlayers.Count > max_player_count)
  5220.                 max_player_count = lstPlayers.Count;
  5221.         }
  5222.  
  5223.  
  5224.         public int getPerMapInterval()
  5225.         {
  5226.  
  5227.             String key = getPerMapKey();
  5228.  
  5229.             if (key.Length > 0)
  5230.                 return getIntegerVarValue(key);
  5231.  
  5232.             return 0;
  5233.         }
  5234.  
  5235.         public String getPerMapKey()
  5236.         {
  5237.             string mode = serverInfo.GameMode.ToLower().Trim();
  5238.             string map = serverInfo.Map.ToLower().Trim();
  5239.  
  5240.             if (maps.ContainsKey(map) && modes.ContainsKey(mode))
  5241.                 return modes[mode] + "_" + maps[map];
  5242.  
  5243.             return "";
  5244.         }
  5245.  
  5246.         public bool checkRoundBalance()
  5247.         {
  5248.             int round_interval = this.getIntegerVarValue("round_interval");
  5249.             int round_total = serverInfo.TotalRounds;
  5250.             int round_current = serverInfo.CurrentRound + 1;
  5251.             string map = serverInfo.Map.ToLower();
  5252.  
  5253.             int per_map_interval = getPerMapInterval();
  5254.  
  5255.             /* if user set a value per-map, use that instead */
  5256.             if (per_map_interval > 0)
  5257.             {
  5258.                 ConsoleWrite("Using round_interval value of " + per_map_interval + " for map " + getPerMapKey());
  5259.                 round_interval = per_map_interval;
  5260.             }
  5261.  
  5262.             if (round_interval > round_total)
  5263.             {
  5264.                 ConsoleWrite("^1^bWARNING^0^n: ^bround_interval(" + round_interval + ")^n is greater than total ^brounds(" + round_total + ")^n for ^bmap(" + map + ")^n^0");
  5265.                 ConsoleWrite("setting ^bround_interval^n to ^b" + round_total + "^n internally for ^bmap(" + map + ")^n^0");
  5266.                 round_interval = round_total;
  5267.             }
  5268.  
  5269.  
  5270.             ConsoleWrite("End of round detected");
  5271.             ConsoleWrite("Current round is ^b" + round_current + "^n/^b" + round_total + "^n,");
  5272.             ConsoleWrite("Round balance interval is ^b" + round_interval + "^n^0");
  5273.  
  5274.             if (!getBooleanVarValue("balance_round"))
  5275.                 return false;
  5276.  
  5277.             if (round_current % round_interval == 0)
  5278.                 return true;
  5279.  
  5280.             return false;
  5281.         }
  5282.  
  5283.  
  5284.         public static string TN(int teamNo)
  5285.         {
  5286.  
  5287.             switch (teamNo)
  5288.             {
  5289.                 case 0:
  5290.                     return "Neutral";
  5291.                 case 1:
  5292.                     return "US";
  5293.                 case 2:
  5294.                     return "RU";
  5295.                 default:
  5296.                     return "Unknown";
  5297.             }
  5298.         }
  5299.  
  5300.  
  5301.         public static string SQN(int squadNo)
  5302.         {
  5303.  
  5304.             switch (squadNo)
  5305.             {
  5306.                 case 0:
  5307.                     return "Neutral";
  5308.                 case 1:
  5309.                     return "Alpha";
  5310.                 case 2:
  5311.                     return "Bravo";
  5312.                 case 3:
  5313.                     return "Charlie";
  5314.                 case 4:
  5315.                     return "Delta";
  5316.                 case 5:
  5317.                     return "Echo";
  5318.                 case 6:
  5319.                     return "Foxtrot";
  5320.                 case 7:
  5321.                     return "Golf";
  5322.                 case 8:
  5323.                     return "Hotel";
  5324.                 case 9:
  5325.                     return "India";
  5326.                 case 10:
  5327.                     return "Juliet";
  5328.                 case 11:
  5329.                     return "Kilo";
  5330.                 case 12:
  5331.                     return "Lima";
  5332.                 case 13:
  5333.                     return "Mike";
  5334.                 case 14:
  5335.                     return "November";
  5336.                 case 15:
  5337.                     return "Oscar";
  5338.                 case 16:
  5339.                     return "Papa";
  5340.                 case 17:
  5341.                     return "X-Alpha";
  5342.                 case 18:
  5343.                     return "X-Bravo";
  5344.                 case 19:
  5345.                     return "X-Charlie";
  5346.                 case 20:
  5347.                     return "X-Delta";
  5348.                 case 21:
  5349.                     return "X-Echo";
  5350.                 case 22:
  5351.                     return "X-Foxtrot";
  5352.                 case 23:
  5353.                     return "X-Golf";
  5354.                 case 24:
  5355.                     return "X-Hotel";
  5356.                 case 25:
  5357.                     return "X-India";
  5358.                 case 26:
  5359.                     return "X-Juliet";
  5360.                 case 27:
  5361.                     return "X-Kilo";
  5362.                 case 28:
  5363.                     return "X-Lima";
  5364.                 case 29:
  5365.                     return "X-Mike";
  5366.                 case 30:
  5367.                     return "X-November";
  5368.                 case 31:
  5369.                     return "X-Oscar";
  5370.                 case 32:
  5371.                     return "X-Papa";
  5372.  
  5373.                 default:
  5374.                     if (squadNo > 16 && squadNo <= 32)
  5375.                         return "S-" + squadNo;
  5376.                     else
  5377.                         return "Unknown";
  5378.             }
  5379.         }
  5380.  
  5381.         public double getRoundMinutes()
  5382.         {
  5383.             return utc.Subtract(startRoundTime).TotalMinutes;
  5384.         }
  5385.  
  5386.  
  5387.         public void delayedRoundBalance()
  5388.         {
  5389.  
  5390.             bool original_state = getBooleanVarValue("balance_live");
  5391.  
  5392.             if (original_state)
  5393.             {
  5394.                 ConsoleWrite("Temporarily disabling live balancer, for round-over");
  5395.                 setBooleanVarValue("balance_live", false);
  5396.             }
  5397.  
  5398.             try
  5399.             {
  5400.                 wait_state = true;
  5401.                 Thread.Sleep(getIntegerVarValue("round_wait_time") * 1000);
  5402.                 wait_state = false;
  5403.  
  5404.                 ConsoleWrite("round-over, ^b" + getIntegerVarValue("round_wait_time") + "^n seconds wait time expired");
  5405.                 if (round_balancer)
  5406.                     balanceRound(win_teamId);
  5407.                 restartWaitState(utc);
  5408.                 resetPlayerStats();
  5409.                 round_balancer = false;
  5410.  
  5411.             }
  5412.             catch (Exception e)
  5413.             {
  5414.                 dump_exception(e);
  5415.             }
  5416.  
  5417.  
  5418.             if (original_state)
  5419.             {
  5420.                 ConsoleWrite("Re-enabling live balancing");
  5421.                 setBooleanVarValue("balance_live", true);
  5422.             }
  5423.         }
  5424.  
  5425.         public void OnRoundOver(int iWinningTeamID)
  5426.         {
  5427.             round_balancer = checkRoundBalance();
  5428.             win_teamId = iWinningTeamID;
  5429.             lose_teamId = getOpposingTeamId(win_teamId);
  5430.             level_started = true;
  5431.  
  5432.             if (!getBooleanVarValue("balance_round"))
  5433.             {
  5434.                 ConsoleWrite("round-over, but ^bbalance_round^n is not enabled");
  5435.                 round_balancer = false;
  5436.                 return;
  5437.             }
  5438.  
  5439.  
  5440.             ConsoleWrite("round-over, waiting for ^b" + getIntegerVarValue("round_wait_time") + "^n seconds");
  5441.             Thread sleeper = new Thread(new ThreadStart(delayedRoundBalance));
  5442.             sleeper.Start();
  5443.  
  5444.         }
  5445.  
  5446.         public override void OnPlayerSpawned(string soldierName, Inventory spawnedInventory)
  5447.         {
  5448.             PlayerProfile player = getPlayerProfile(soldierName);
  5449.             if (player != null)
  5450.             {
  5451.                 player.updateLastSpawn();
  5452.                 player.state = PlayerState.alive;
  5453.             }
  5454.  
  5455.         }
  5456.  
  5457.  
  5458.         private void resetPlayerStats()
  5459.         {
  5460.  
  5461.             List<PlayerProfile> players_list = getPlayersProfile("");
  5462.             foreach (PlayerProfile player in players_list)
  5463.             {
  5464.                 player.resetStats();
  5465.             }
  5466.  
  5467.             /* reset the fail-safe counter */
  5468.             max_player_count = 0;
  5469.  
  5470.         }
  5471.  
  5472.  
  5473.  
  5474.         private void SendPlayerMessage(string soldierName, string message)
  5475.         {
  5476.             if (getBooleanVarValue("quiet_mode") && !isAdmin(soldierName))
  5477.                 return;
  5478.  
  5479.             if (soldierName == null)
  5480.                 return;
  5481.  
  5482.             /* Temporarily disable player messages until DICE
  5483.              * enables individual player messages
  5484.              */
  5485.  
  5486.             //ExecCommand("admin.say", message, "player", soldierName);
  5487.         }
  5488.  
  5489.  
  5490.         private void SendGlobalMessage(string message)
  5491.         {
  5492.             if (getBooleanVarValue("quiet_mode"))
  5493.                 SendConsoleMessage(message);
  5494.             else
  5495.                 ExecCommand("admin.say", message, "all");
  5496.  
  5497.         }
  5498.  
  5499.         private void SendConsoleMessage(string name, string msg)
  5500.         {
  5501.             List<string> admin_list = getAdminList();
  5502.  
  5503.             ConsoleWrite(msg);
  5504.             msg = Regex.Replace(msg, @"\^[0-9a-zA-Z]", "");
  5505.  
  5506.             if (name != null)
  5507.                 SendPlayerMessage(name, msg);
  5508.  
  5509.  
  5510.         }
  5511.  
  5512.         private void SendConsoleMessage(string msg)
  5513.         {
  5514.             List<string> admin_list = getAdminList();
  5515.             ConsoleWrite(msg);
  5516.  
  5517.             msg = Regex.Replace(msg, @"\^[0-9a-zA-Z]", "");
  5518.  
  5519.  
  5520.  
  5521.             foreach (string name in admin_list)
  5522.             {
  5523.                 PlayerProfile pp = this.getPlayerProfile(name);
  5524.                 if (pp != null)
  5525.                 {
  5526.                     SendPlayerMessage(pp.name, msg);
  5527.                 }
  5528.             }
  5529.  
  5530.         }
  5531.  
  5532.  
  5533.         private void KickPlayerWithMessage(PlayerProfile player, string message)
  5534.         {
  5535.             if (player == null)
  5536.                 return;
  5537.  
  5538.             player.state = PlayerState.kicked;
  5539.             this.ExecuteCommand("procon.protected.send", "admin.kickPlayer", player.name, message);
  5540.             if (players.ContainsKey(player.name))
  5541.                 players.Remove(player.name);
  5542.         }
  5543.  
  5544.         private void inGameCommand(string cmd)
  5545.         {
  5546.             inGameCommand(getAdminList()[0], cmd);
  5547.         }
  5548.  
  5549.         private void inGameCommand(string sender, string cmd)
  5550.         {
  5551.  
  5552.             try
  5553.             {
  5554.  
  5555.                 //Player commands
  5556.                 Match adminMovePlayerMatch = Regex.Match(cmd, @"\s*[!@/]\s*move\s+([^ ]+)", RegexOptions.IgnoreCase);
  5557.                 Match movePlayerMatch = Regex.Match(cmd, @"\s*[!@/]\s*move", RegexOptions.IgnoreCase);
  5558.  
  5559.  
  5560.  
  5561.                 Match showPlayerRoundStatsMatch = Regex.Match(cmd, @"\s*[!@/]\s*show\s+round\s+stats\s+([^ ]+)", RegexOptions.IgnoreCase);
  5562.                 Match showRoundStatsMatch = Regex.Match(cmd, @"\s*[!@/]\s*show\s+round\s+stats", RegexOptions.IgnoreCase);
  5563.  
  5564.                 Match showPlayerOnlineStatsMatch = Regex.Match(cmd, @"\s*[!@/]\s*show\s+online\s+stats\s+([^ ]+)", RegexOptions.IgnoreCase);
  5565.                 Match showOnlineStatsMatch = Regex.Match(cmd, @"\s*[!@/]\s*show\s+online\s+stats", RegexOptions.IgnoreCase);
  5566.  
  5567.                 Match showIdlePlayersMatch = Regex.Match(cmd, @"\s*[!@/]\s*show\s+idle", RegexOptions.IgnoreCase);
  5568.                 Match wlistInfoPlayerMatch = Regex.Match(cmd, @"\s*[!@/]\s*wlist_info\s+([^ ]+)", RegexOptions.IgnoreCase);
  5569.  
  5570.                 Match stopBalancerMatch = Regex.Match(cmd, @"\s*[!@/]\s*stop\s+check", RegexOptions.IgnoreCase);
  5571.                 Match startBalancerMatch = Regex.Match(cmd, @"\s*[!@/]\s*start\s+check", RegexOptions.IgnoreCase);
  5572.                 Match balanceLiveMatch = Regex.Match(cmd, @"\s*[!@/]\s*balance\s+live", RegexOptions.IgnoreCase);
  5573.                 Match balanceRoundMatch = Regex.Match(cmd, @"\s*[!@/]\s*balance\s+round", RegexOptions.IgnoreCase);
  5574.  
  5575.  
  5576.                 //Setting/Getting variables
  5577.                 Match setVarValueMatch = Regex.Match(cmd, @"\s*[!@/]\s*set\s+([^ ]+)\s+(.+)", RegexOptions.IgnoreCase);
  5578.                 Match setVarValueEqMatch = Regex.Match(cmd, @"\s*[!@/]\s*set\s+([^ ]+)\s*=\s*(.+)", RegexOptions.IgnoreCase);
  5579.                 Match setVarValueToMatch = Regex.Match(cmd, @"\s*[!@/]\s*set\s+([^ ]+)\s+to\s+(.+)", RegexOptions.IgnoreCase);
  5580.                 Match setVarTrueMatch = Regex.Match(cmd, @"\s*[!@/]\s*set\s+([^ ]+)", RegexOptions.IgnoreCase);
  5581.                 Match getVarValueMatch = Regex.Match(cmd, @"\s*[!@/]\s*get\s+([^ ]+)", RegexOptions.IgnoreCase);
  5582.                 Match enableMatch = Regex.Match(cmd, @"\s*[!@/]\s*enable\s+(.+)", RegexOptions.IgnoreCase);
  5583.                 Match disableMatch = Regex.Match(cmd, @"\s*[!@/]\s*disable\s+(.+)", RegexOptions.IgnoreCase);
  5584.  
  5585.                 //ConsoleWrite("Command run " + cmd + ", Matched: " + enableMatch.Success);
  5586.                 //Information
  5587.                 Match pluginSettingsMatch = Regex.Match(cmd, @"\s*[!@/]\s*settings", RegexOptions.IgnoreCase);
  5588.  
  5589.  
  5590.                 bool senderIsAdmin = isAdmin(sender);
  5591.  
  5592.                 DateTime now = utc;
  5593.                 if (showIdlePlayersMatch.Success && senderIsAdmin)
  5594.                     showIdlePlayers(sender);
  5595.                 if (wlistInfoPlayerMatch.Success && senderIsAdmin)
  5596.                     wlistInfoPlayer(sender, wlistInfoPlayerMatch.Groups[1].Value);
  5597.                 else if (startBalancerMatch.Success && senderIsAdmin)
  5598.                     startBalancerCmd(sender, now);
  5599.                 else if (stopBalancerMatch.Success && senderIsAdmin)
  5600.                     stopBalancerCmd(sender, now);
  5601.                 else if (showPlayerRoundStatsMatch.Success && senderIsAdmin)
  5602.                     showPlayerRoundStatsCmd(sender, showPlayerRoundStatsMatch.Groups[1].Value);
  5603.                 else if (showRoundStatsMatch.Success && senderIsAdmin)
  5604.                     showPlayerRoundStatsCmd(sender, null);
  5605.  
  5606.                 else if (showPlayerOnlineStatsMatch.Success && senderIsAdmin)
  5607.                     showPlayerOnlineStatsCmd(sender, showPlayerOnlineStatsMatch.Groups[1].Value);
  5608.                 else if (showOnlineStatsMatch.Success && senderIsAdmin)
  5609.                     showPlayerOnlineStatsCmd(sender, null);
  5610.  
  5611.                 else if (balanceLiveMatch.Success && senderIsAdmin)
  5612.                     balanceLiveCmd(sender, now);
  5613.                 else if (balanceRoundMatch.Success && senderIsAdmin)
  5614.                     balanceRoundCmd(sender, now);
  5615.                 else if (adminMovePlayerMatch.Success && senderIsAdmin)
  5616.                     movePlayerCmd(sender, adminMovePlayerMatch.Groups[1].Value);
  5617.                 else if (movePlayerMatch.Success)
  5618.                     movePlayerCmd(sender);
  5619.                 else if (setVarValueEqMatch.Success && senderIsAdmin)
  5620.                     setVariableCmd(sender, setVarValueEqMatch.Groups[1].Value, setVarValueEqMatch.Groups[2].Value);
  5621.                 else if (setVarValueToMatch.Success && senderIsAdmin)
  5622.                     setVariableCmd(sender, setVarValueToMatch.Groups[1].Value, setVarValueToMatch.Groups[2].Value);
  5623.                 else if (setVarValueMatch.Success && senderIsAdmin)
  5624.                     setVariableCmd(sender, setVarValueMatch.Groups[1].Value, setVarValueMatch.Groups[2].Value);
  5625.                 else if (setVarTrueMatch.Success && senderIsAdmin)
  5626.                     setVariableCmd(sender, setVarTrueMatch.Groups[1].Value, "1");
  5627.                 else if (getVarValueMatch.Success && senderIsAdmin)
  5628.                     getVariableCmd(sender, getVarValueMatch.Groups[1].Value);
  5629.                 else if (enableMatch.Success && senderIsAdmin)
  5630.                     enableVarGroupCmd(sender, enableMatch.Groups[1].Value);
  5631.                 else if (disableMatch.Success && senderIsAdmin)
  5632.                     disableVarGroupCmd(sender, disableMatch.Groups[1].Value);
  5633.                 else if (pluginSettingsMatch.Success && senderIsAdmin)
  5634.                     pluginSettingsCmd(sender);
  5635.             }
  5636.             catch (Exception e)
  5637.             {
  5638.                 dump_exception(e);
  5639.             }
  5640.         }
  5641.  
  5642.  
  5643.  
  5644.  
  5645.  
  5646.         private string state2strWHILE(PluginState state)
  5647.         {
  5648.             if (state.Equals(PluginState.balance))
  5649.             {
  5650.                 return "balancing";
  5651.             }
  5652.             else if (state.Equals(PluginState.check))
  5653.             {
  5654.                 return "checking";
  5655.             }
  5656.             else if (state.Equals(PluginState.warn))
  5657.             {
  5658.                 return "warning";
  5659.             }
  5660.             else if (state.Equals(PluginState.stop))
  5661.             {
  5662.                 return "stopped";
  5663.             }
  5664.             else if (state.Equals(PluginState.wait))
  5665.             {
  5666.                 return "waiting";
  5667.             }
  5668.  
  5669.             return "unknown state";
  5670.         }
  5671.  
  5672.  
  5673.  
  5674.         private void initializeBalancer()
  5675.         {
  5676.             getServerInfo();
  5677.             startStopState(utc);
  5678.  
  5679.             // initialize the stats fetching thread
  5680.             this.wake_handle = new EventWaitHandle(false, EventResetMode.ManualReset);
  5681.             this.stats_fetching_thread = new Thread(new ThreadStart(stats_fetching_loop));
  5682.             stats_fetching_thread.Start();
  5683.  
  5684.         }
  5685.  
  5686.  
  5687.         private void movePlayerCmd(string sender)
  5688.         {
  5689.             if (teamsUnbalanced())
  5690.             {
  5691.                 SendConsoleMessage(sender, "Teams are un-balanced, cannot move to other team");
  5692.                 return;
  5693.             }
  5694.  
  5695.             movePlayerCmd(sender, null);
  5696.         }
  5697.  
  5698.         private void movePlayerCmd(string sender, string player)
  5699.         {
  5700.             /* player is moving himself */
  5701.             if (player == null)
  5702.                 player = sender;
  5703.  
  5704.             SendConsoleMessage(sender, "moving player " + player);
  5705.  
  5706.             PlayerProfile profile = getPlayerProfile(player);
  5707.  
  5708.             if (profile == null)
  5709.             {
  5710.                 SendConsoleMessage(sender, "cannot find profile for " + player);
  5711.                 return;
  5712.             }
  5713.  
  5714.             if (profile.getTeamId() == 0)
  5715.             {
  5716.                 SendConsoleMessage(sender, "cannot move " + player + " from neutral team");
  5717.                 return;
  5718.             }
  5719.  
  5720.             int opposite = (profile.getTeamId() == 1) ? 2 : 1;
  5721.             movePlayer(profile, opposite, 0, false, true);
  5722.         }
  5723.  
  5724.  
  5725.         private void startWaitSate(DateTime now)
  5726.         {
  5727.             startWaitSate(null, now);
  5728.         }
  5729.  
  5730.  
  5731.  
  5732.         private void startWarnState(DateTime now)
  5733.         {
  5734.             startWarnState(null, now);
  5735.         }
  5736.  
  5737.         private void startWarnState(string sender, DateTime now)
  5738.         {
  5739.             if (!isPluginChecking())
  5740.             {
  5741.                 SendConsoleMessage(sender, "plugin is in " + pluginState.ToString() + " state");
  5742.                 return;
  5743.             }
  5744.  
  5745.             pluginState = PluginState.warn;
  5746.             setStartTime(pluginState, now.AddSeconds(1));
  5747.  
  5748.             DebugWrite("^b" + pluginState + "^n state started " + getStartTime(pluginState).ToString(), 1);
  5749.         }
  5750.  
  5751.         private void startWaitSate(string sender, DateTime now)
  5752.         {
  5753.  
  5754.             if (!isPluginStopped())
  5755.             {
  5756.                 SendConsoleMessage(sender, "cannot start while balancer is in " + pluginState.ToString() + " state");
  5757.                 return;
  5758.             }
  5759.  
  5760.             if (sender != null)
  5761.                 setPluginVarValue("auto_start", "true");
  5762.  
  5763.  
  5764.             pluginState = PluginState.wait;
  5765.             setStartTime(pluginState, now.AddSeconds(1));
  5766.  
  5767.  
  5768.             SendConsoleMessage(sender, "^b" + pluginState + "^n state started " + getStartTime(pluginState).ToString());
  5769.         }
  5770.  
  5771.         private void startStopState(DateTime now)
  5772.         {
  5773.             startStopState(null, now);
  5774.         }
  5775.  
  5776.         private void startStopState(string sender, DateTime now)
  5777.         {
  5778.             virtual_mode = false;
  5779.             round_balancer = false;
  5780.             level_started = false;
  5781.             check_state_phase = 0;
  5782.  
  5783.             if (sender != null)
  5784.                 setPluginVarValue("auto_start", "false");
  5785.  
  5786.             pluginState = PluginState.stop;
  5787.             setStartTime(pluginState, now.AddSeconds(1));
  5788.  
  5789.             SendConsoleMessage(sender, "^b" + pluginState + "^n state started " + getStartTime(pluginState).ToString());
  5790.         }
  5791.  
  5792.  
  5793.         public void balanceLiveCmd(string sender, DateTime now)
  5794.         {
  5795.  
  5796.             balanceLive(now);
  5797.             restartWaitState(now);
  5798.         }
  5799.  
  5800.         public void balanceRoundCmd(string sender, DateTime now)
  5801.         {
  5802.             try
  5803.             {
  5804.                 balanceRound(1);
  5805.                 restartWaitState(utc);
  5806.                 resetPlayerStats();
  5807.             }
  5808.             catch (Exception e)
  5809.             {
  5810.                 dump_exception(e);
  5811.             }
  5812.  
  5813.         }
  5814.  
  5815.         private bool isPlayerIdle(PlayerProfile player)
  5816.         {
  5817.             int last_kill_time = getIntegerVarValue("last_kill_time");
  5818.             int last_death_time = getIntegerVarValue("last_death_time");
  5819.             int last_chat_time = getIntegerVarValue("last_chat_time");
  5820.             int last_spawn_time = getIntegerVarValue("last_spawn_time");
  5821.             int last_score_time = getIntegerVarValue("last_score_time");
  5822.  
  5823.  
  5824.             if (player.getLastKill() > last_kill_time &&
  5825.                 player.getLastDeath() > last_death_time &&
  5826.                 player.getLastChat() > last_chat_time &&
  5827.                 player.getLastSpawn() > last_spawn_time &&
  5828.                 player.getLastScore() > last_score_time)
  5829.                 return true;
  5830.  
  5831.             return false;
  5832.  
  5833.         }
  5834.  
  5835.         public void showIdlePlayers(string sender)
  5836.         {
  5837.             List<PlayerProfile> players_list = getPlayersProfile("");
  5838.  
  5839.             List<PlayerProfile> list = new List<PlayerProfile>();
  5840.  
  5841.             foreach (PlayerProfile player in players_list)
  5842.                 if (isPlayerIdle(player))
  5843.                     list.Add(player);
  5844.  
  5845.  
  5846.             SendConsoleMessage(sender, " == " + list.Count + " idle players (watching for last " + Math.Round(getRoundMinutes(), 2) + " minutes)  ==");
  5847.             foreach (PlayerProfile player in list)
  5848.                 SendConsoleMessage(sender, player + ": " + player.getIdleStatistics());
  5849.         }
  5850.  
  5851.  
  5852.         public void wlistInfoPlayer(string sender, string pname)
  5853.         {
  5854.  
  5855.             PlayerProfile player = getPlayerProfile(pname);
  5856.             if (player == null)
  5857.             {
  5858.                 SendConsoleMessage(sender, "^1^bWARNING^n^0: could not find ^b" + pname + "^n in game");
  5859.                 return;
  5860.             }
  5861.  
  5862.             SendConsoleMessage(sender, " == White List Info for " + pname + "  ==");
  5863.  
  5864.             List<String> list_names = new List<string>();
  5865.             list_names.Add("player_move_wlist");
  5866.             list_names.Add("clan_move_wlist");
  5867.             list_names.Add("player_safe_wlist");
  5868.             list_names.Add("clan_move_wlist");
  5869.             list_names.Add("player_safe_wlist");
  5870.             list_names.Add("clan_safe_wlist");
  5871.  
  5872.             foreach (String list in list_names)
  5873.             {
  5874.                 Boolean inlist = isPlayerInWhiteList(player, list);
  5875.                 String inlist_str = (inlist) ? "^b" + inlist.ToString() + "^n" : inlist.ToString();
  5876.                 SendConsoleMessage(sender, list + " = " + inlist_str);
  5877.             }
  5878.  
  5879.         }
  5880.  
  5881.  
  5882.         public void showPlayerRoundStatsCmd(string sender, string player_name)
  5883.         {
  5884.             List<PlayerProfile> players_list;
  5885.             if (player_name == null)
  5886.                 players_list = getPlayersProfile("");
  5887.             else
  5888.                 players_list = getPlayersProfile(player_name);
  5889.  
  5890.  
  5891.             if (players_list.Count == 0)
  5892.                 return;
  5893.  
  5894.             int i = 1;
  5895.             SendConsoleMessage(sender, " == Round Statistics ( " + Math.Round(getRoundMinutes(), 2) + " minutes) ==");
  5896.             foreach (PlayerProfile player in players_list)
  5897.             {
  5898.                 SendConsoleMessage(sender, i + ". " + player + ": " + player.getRoundStatistics());
  5899.                 i++;
  5900.             }
  5901.         }
  5902.  
  5903.         public void showPlayerOnlineStatsCmd(string sender, string player_name)
  5904.         {
  5905.             List<PlayerProfile> players_list;
  5906.             if (player_name == null)
  5907.                 players_list = getPlayersProfile("");
  5908.             else
  5909.                 players_list = getPlayersProfile(player_name);
  5910.  
  5911.  
  5912.             if (players_list.Count == 0)
  5913.                 return;
  5914.  
  5915.             int i = 1;
  5916.  
  5917.             SendConsoleMessage(sender, " == Online Statistics ==");
  5918.             foreach (PlayerProfile player in players_list)
  5919.             {
  5920.                 SendConsoleMessage(sender, i + ". " + player + ": " + player.getOnlineStatistics());
  5921.                 i++;
  5922.             }
  5923.         }
  5924.  
  5925.         private void startBalancerCmd(string sender, DateTime now)
  5926.         {
  5927.             setBooleanVarValue("balance_live", true);
  5928.             startWaitSate(now);
  5929.         }
  5930.  
  5931.         private void stopBalancerCmd(string sender, DateTime now)
  5932.         {
  5933.             setBooleanVarValue("balance_live", false);
  5934.             startStopState(now);
  5935.         }
  5936.  
  5937.         private void restartWaitState(DateTime now)
  5938.         {
  5939.             pluginState = PluginState.wait;
  5940.             setStartTime(pluginState, now.AddSeconds(1));
  5941.             DebugWrite("^b" + pluginState.ToString() + "^n state re-started " + getStartTime(pluginState).ToString() + "^0", 1);
  5942.         }
  5943.  
  5944.  
  5945.  
  5946.         private void genericSayAnnounce(List<string> messages)
  5947.         {
  5948.             if (messages.Count == 0)
  5949.                 return;
  5950.  
  5951.             int remain_time = getRemainingTime(utc, pluginState);
  5952.  
  5953.             for (int i = 0; i < messages.Count; i++)
  5954.             {
  5955.                 string msg = messages[i];
  5956.                 msg = msg.Replace("%time%", remain_time.ToString());
  5957.                 SendGlobalMessage(msg);
  5958.             }
  5959.  
  5960.         }
  5961.  
  5962.  
  5963.  
  5964.         private void warnAnnounce(int display_time)
  5965.         {
  5966.             if (!isPluginWarning())
  5967.                 return;
  5968.  
  5969.             DebugWrite("sending ^b" + pluginState.ToString() + "^n announcement", 1);
  5970.  
  5971.             List<string> msg = new List<string>();
  5972.             msg.Add("Teams are unbalanced");
  5973.             msg.Add("Autobalancer starts in %time% secs");
  5974.  
  5975.             if (getBooleanVarValue("warn_say"))
  5976.                 genericSayAnnounce(msg);
  5977.         }
  5978.  
  5979.  
  5980.         private void warnCountdown()
  5981.         {
  5982.             if (!isPluginWarning())
  5983.                 return;
  5984.  
  5985.             DebugWrite("sending ^b" + pluginState.ToString() + "^n countdown", 1);
  5986.  
  5987.             int remain_time = getRemainingTime(utc, PluginState.warn);
  5988.             string msg = "Autobalancer starts in " + remain_time.ToString() + "!";
  5989.  
  5990.             if (getBooleanVarValue("warn_say"))
  5991.                 SendGlobalMessage(msg);
  5992.         }
  5993.  
  5994.         private void enableVarGroupCmd(string sender, string group)
  5995.         {
  5996.             if (group.CompareTo("plugin") == 0)
  5997.             {
  5998.                 ConsoleWrite("Disabling plugin");
  5999.                 this.ExecuteCommand("procon.plugin.enable", "InsaneBalancer", "false");
  6000.             }
  6001.             enablePluginVarGroup(sender, group);
  6002.         }
  6003.  
  6004.         private void disableVarGroupCmd(string sender, string group)
  6005.         {
  6006.             if (group.CompareTo("plugin") == 0)
  6007.             {
  6008.                 ConsoleWrite("Enabling plugin");
  6009.                 this.ExecuteCommand("procon.plugin.enable", "InsaneBalancer", "true");
  6010.             }
  6011.  
  6012.             disablePluginVarGroup(sender, group);
  6013.         }
  6014.  
  6015.         private bool setPluginVarGroup(string sender, string group, string val)
  6016.         {
  6017.             String msg = "";
  6018.             if (group == null)
  6019.             {
  6020.                 msg = "no variables to enable";
  6021.                 ConsoleWrite(msg);
  6022.                 SendConsoleMessage(sender, msg);
  6023.                 return false;
  6024.             }
  6025.  
  6026.  
  6027.             group = group.Replace(";", ",");
  6028.             List<string> vars = new List<string>(Regex.Split(group, @"\s*,\s*", RegexOptions.IgnoreCase));
  6029.             foreach (string var in vars)
  6030.             {
  6031.                 if (setPluginVarValue(sender, var, val))
  6032.                 {
  6033.                     msg = var + " set to \"" + val + "\"";
  6034.                     SendConsoleMessage(sender, msg);
  6035.                 }
  6036.  
  6037.             }
  6038.             return true;
  6039.         }
  6040.  
  6041.         private bool enablePluginVarGroup(string sender, string group)
  6042.         {
  6043.             //search for all variables matching
  6044.             List<string> vars = getVariableNames(group);
  6045.             String msg = "";
  6046.             if (vars.Count == 0)
  6047.             {
  6048.                 msg = "no variables match \"" + group + "\"";
  6049.                 //ConsoleWrite(msg);
  6050.                 SendConsoleMessage(sender, msg);
  6051.                 return false;
  6052.             }
  6053.  
  6054.             return setPluginVarGroup(sender, String.Join(",", vars.ToArray()), "true");
  6055.         }
  6056.  
  6057.         private List<string> getVariableNames(string group)
  6058.         {
  6059.             List<string> names = new List<string>();
  6060.             List<string> list = new List<string>(Regex.Split(group, @"\s*,\s*"));
  6061.             List<string> vars = getPluginVars();
  6062.             foreach (string search in list)
  6063.             {
  6064.                 foreach (string var in vars)
  6065.                 {
  6066.                     if (var.Contains(search))
  6067.                         if (!names.Contains(var))
  6068.                             names.Add(var);
  6069.                 }
  6070.             }
  6071.  
  6072.             return names;
  6073.         }
  6074.  
  6075.         private bool disablePluginVarGroup(string sender, string group)
  6076.         {
  6077.             //search for all variables matching
  6078.             List<string> vars = getVariableNames(group);
  6079.  
  6080.             if (vars.Count == 0)
  6081.             {
  6082.                 SendConsoleMessage(sender, "no variables match \"" + group + "\"");
  6083.                 return false;
  6084.             }
  6085.             return setPluginVarGroup(sender, String.Join(",", vars.ToArray()), "false");
  6086.         }
  6087.  
  6088.         private void getVariableCmd(string sender, string var)
  6089.         {
  6090.             string val = getPluginVarValue(sender, var);
  6091.  
  6092.             if (var.Equals("pass"))
  6093.                 val = Regex.Replace(val, @".", "*");
  6094.  
  6095.             String msg = var + " = " + val;
  6096.  
  6097.             //ConsoleWrite(msg);
  6098.             SendConsoleMessage(sender, msg);
  6099.         }
  6100.  
  6101.  
  6102.  
  6103.  
  6104.         private void setVariableCmd(string sender, string var, string val)
  6105.         {
  6106.  
  6107.             if (setPluginVarValue(sender, var, val))
  6108.             {
  6109.                 SendConsoleMessage(sender, var + " set to \"" + val + "\"");
  6110.             }
  6111.         }
  6112.  
  6113.         private void pluginSettingsCmd(string sender)
  6114.         {
  6115.             SendConsoleMessage(sender, " == Insane Balancer Settings == ");
  6116.             foreach (string var in getPluginVars())
  6117.             {
  6118.                 SendConsoleMessage(sender, var + " = " + getPluginVarValue(sender, var));
  6119.             }
  6120.         }
  6121.  
  6122.  
  6123.         public bool stringValidator(string var, string value)
  6124.         {
  6125.             if (var.CompareTo("round_sort") == 0)
  6126.             {
  6127.                 if (!strAssertSort(value))
  6128.                     return false;
  6129.             }
  6130.             if (var.CompareTo("live_sort") == 0)
  6131.             {
  6132.                 if (!strAssertSort(value))
  6133.                     return false;
  6134.             }
  6135.             return true;
  6136.         }
  6137.  
  6138.         public bool commandValidator(string var, string value)
  6139.         {
  6140.  
  6141.             try
  6142.             {
  6143.                 inGameCommand(value);
  6144.             }
  6145.             catch (Exception e)
  6146.             {
  6147.                 dump_exception(e);
  6148.             }
  6149.             return false;
  6150.         }
  6151.  
  6152.  
  6153.         public List<String> getAllowedSorts()
  6154.         {
  6155.             List<string> sort_methods = new List<string>();
  6156.  
  6157.  
  6158.             sort_methods.Add("kdr_asc_round");
  6159.             sort_methods.Add("kdr_desc_round");
  6160.             sort_methods.Add("spm_asc_round");
  6161.             sort_methods.Add("spm_desc_round");
  6162.             sort_methods.Add("kpm_asc_round");
  6163.             sort_methods.Add("kpm_desc_round");
  6164.             sort_methods.Add("score_asc_round");
  6165.             sort_methods.Add("score_desc_round");
  6166.             sort_methods.Add("time_asc_round");
  6167.             sort_methods.Add("time_desc_round");
  6168.  
  6169.  
  6170.             sort_methods.Add("kdr_asc_online");
  6171.             sort_methods.Add("kdr_desc_online");
  6172.             sort_methods.Add("kpm_asc_online");
  6173.             sort_methods.Add("kpm_desc_online");
  6174.             sort_methods.Add("spm_asc_online");
  6175.             sort_methods.Add("spm_desc_online");
  6176.             sort_methods.Add("kills_asc_online");
  6177.             sort_methods.Add("kills_desc_online");
  6178.             sort_methods.Add("deaths_asc_online");
  6179.             sort_methods.Add("deaths_desc_online");
  6180.             sort_methods.Add("skill_asc_online");
  6181.             sort_methods.Add("skill_desc_online");
  6182.             sort_methods.Add("quits_asc_online");
  6183.             sort_methods.Add("quits_desc_online");
  6184.             sort_methods.Add("accuracy_asc_online");
  6185.             sort_methods.Add("accuracy_desc_online");
  6186.             sort_methods.Add("score_asc_online");
  6187.             sort_methods.Add("score_desc_online");
  6188.             sort_methods.Add("rank_asc_online");
  6189.             sort_methods.Add("rank_desc_online");
  6190.  
  6191.             sort_methods.Add("random_value");
  6192.  
  6193.             return sort_methods;
  6194.         }
  6195.  
  6196.  
  6197.         public bool strAssertSort(string value)
  6198.         {
  6199.             if (value == null)
  6200.                 return false;
  6201.  
  6202.  
  6203.             List<String> sort_methods = getAllowedSorts();
  6204.  
  6205.             if (!sort_methods.Contains(value))
  6206.             {
  6207.                 SendConsoleMessage("^1^bERROR^0^n: ^b" + value + "^n is not a valid sort method ^0");
  6208.                 SendConsoleMessage("valid sort methods are: ^b" + String.Join("^0,^b ", sort_methods.ToArray()) + "^0");
  6209.                 return false;
  6210.             }
  6211.             return true;
  6212.         }
  6213.  
  6214.         public bool booleanValidator(string var, bool value)
  6215.         {
  6216.             return true;
  6217.         }
  6218.  
  6219.  
  6220.         bool boolAssertNE(string var, bool value, string cmp)
  6221.         {
  6222.             bool cmp_value = getBooleanVarValue(cmp);
  6223.  
  6224.             if (!(value != cmp_value))
  6225.             {
  6226.                 ConsoleWrite("^1^bERROR^0^n:  cannot set ^b" + var + "^n to ^b" + value.ToString() + "^n while ^b" + cmp + "^n is set to ^b" + cmp_value.ToString() + "^n^0");
  6227.                 return false;
  6228.             }
  6229.             return true;
  6230.         }
  6231.  
  6232.         public bool integerValidator(string var, int value)
  6233.         {
  6234.  
  6235.             if (var.CompareTo("warn_msg_interval_time") == 0)
  6236.             {
  6237.                 if (!intAssertGTE(var, value, 0) ||
  6238.                     !intAssertLTE(var, value, "warn_msg_total_time"))
  6239.                     return false;
  6240.             }
  6241.  
  6242.             if (var.CompareTo("warn_msg_countdown_time") == 0)
  6243.             {
  6244.                 if (!intAssertGTE(var, value, 0) ||
  6245.                     !intAssertLTE(var, value, "warn_msg_total_time"))
  6246.                     return false;
  6247.             }
  6248.  
  6249.             if (var.CompareTo("warn_msg_total_time") == 0)
  6250.             {
  6251.                 if (!intAssertGTE(var, value, 0) ||
  6252.                     !intAssertGTE(var, value, "warn_msg_interval_time") ||
  6253.                     !intAssertGTE(var, value, "warn_msg_countdown_time"))
  6254.                     return false;
  6255.             }
  6256.  
  6257.             if (var.CompareTo("warn_msg_display_time") == 0)
  6258.             {
  6259.                 if (!intAssertGTE(var, value, 0) ||
  6260.                     !intAssertLTE(var, value, "warn_msg_total_time"))
  6261.                     return false;
  6262.             }
  6263.  
  6264.             if (var.CompareTo("balance_threshold") == 0 ||
  6265.                 var.CompareTo("wait_death_count") == 0)
  6266.             {
  6267.                 if (!intAssertGT(var, value, 0))
  6268.                     return false;
  6269.             }
  6270.  
  6271.             if (var.CompareTo("round_interval") == 0)
  6272.             {
  6273.  
  6274.                 if (!intAssertGT(var, value, 0))
  6275.                     return false;
  6276.  
  6277.                 if (serverInfo == null)
  6278.                     return true;
  6279.  
  6280.                 if (value > serverInfo.TotalRounds)
  6281.                 {
  6282.                     SendConsoleMessage("^1^bERROR^0^n: ^b" + var + "(" + value + ")^n must be less than or equal than the total number of ^brounds(" + serverInfo.TotalRounds + ")^n per ^bmap(" + serverInfo.Map.ToLower() + ")^n^0");
  6283.                     return false;
  6284.                 }
  6285.             }
  6286.  
  6287.             if (var.CompareTo("live_interval_time") == 0)
  6288.             {
  6289.                 if (!intAssertGT(var, value, 0))
  6290.                     return false;
  6291.             }
  6292.  
  6293.             if (var.CompareTo("debug_level") == 0)
  6294.             {
  6295.                 if (!intAssertGTE(var, value, 0))
  6296.                     return false;
  6297.             }
  6298.  
  6299.             return true;
  6300.         }
  6301.  
  6302.  
  6303.         private bool intAssertLT(string var, int value, int max_value)
  6304.         {
  6305.             if (!(value < max_value))
  6306.             {
  6307.                 SendConsoleMessage("^1^bERROR^0^n: b" + var + "(" + value + ")^n must be less than  ^b" + max_value + "^n^0");
  6308.                 return false;
  6309.             }
  6310.  
  6311.             return true;
  6312.         }
  6313.  
  6314.  
  6315.         private bool intAssertLTE(string var, int value, int max_value)
  6316.         {
  6317.             if (!(value <= max_value))
  6318.             {
  6319.                 SendConsoleMessage("^1^bERROR^0^n: ^b" + var + "(" + value + ")^n must be less than or equal to ^b" + max_value + "^n^0");
  6320.                 return false;
  6321.             }
  6322.  
  6323.             return true;
  6324.         }
  6325.  
  6326.  
  6327.         private bool intAssertGT(string var, int value, int min_value)
  6328.         {
  6329.             if (!(value > min_value))
  6330.             {
  6331.                 SendConsoleMessage("^1^bERROR^0^n: ^b" + var + "(" + value + ")^n must be greater than  ^b" + min_value + "^n^0");
  6332.                 return false;
  6333.             }
  6334.  
  6335.             return true;
  6336.         }
  6337.  
  6338.  
  6339.         private bool intAssertGTE(string var, int value, int min_value)
  6340.         {
  6341.             if (!(value >= min_value))
  6342.             {
  6343.                 SendConsoleMessage("^1^bERROR^0^n: ^b" + var + "(" + value + ")^n must be greater than or equal to ^b" + min_value + "^n^0");
  6344.                 return false;
  6345.             }
  6346.  
  6347.             return true;
  6348.         }
  6349.  
  6350.         private bool intAssertGTE(string var1, int var1_value, string var2)
  6351.         {
  6352.             int var2_value = getIntegerVarValue(var2);
  6353.  
  6354.             if (!(var1_value >= var2_value))
  6355.             {
  6356.  
  6357.                 SendConsoleMessage("^1^bERROR^0^n: ^b" + var1 + "(" + var1_value + ")^n must be greater than or equal to the value of ^b" + var2 + "(" + var2_value + ")^n");
  6358.  
  6359.                 return false;
  6360.             }
  6361.  
  6362.             return true;
  6363.         }
  6364.  
  6365.  
  6366.         private bool intAssertLTE(string var1, int var1_value, string var2)
  6367.         {
  6368.             int var2_value = getIntegerVarValue(var2);
  6369.  
  6370.  
  6371.             if (!(var1_value <= var2_value))
  6372.             {
  6373.                 SendConsoleMessage("^1^bERROR^0^n: ^b" + var1 + "(" + var1_value + ")^n must be less than or equal to the value of ^b" + var2 + "(" + var2_value + ")^n");
  6374.                 return false;
  6375.             }
  6376.  
  6377.             return true;
  6378.         }
  6379.  
  6380.  
  6381.         private bool setPluginVarValue(string var, string val)
  6382.         {
  6383.             return setPluginVarValue(null, var, val);
  6384.         }
  6385.  
  6386.         private bool setPluginVarValue(string sender, string var, string val)
  6387.         {
  6388.             if (var == null || val == null)
  6389.                 return false;
  6390.  
  6391.             if (!getPluginVars().Contains(var))
  6392.             {
  6393.                 SendConsoleMessage(sender, "Insane Balancer: unknown variable \"" + var + "\"");
  6394.                 return false;
  6395.             }
  6396.  
  6397.             /* Parse Boolean Values */
  6398.             bool booleanValue = false;
  6399.             bool isBooleanValue = true;
  6400.             if (Regex.Match(val, @"\s*(1|true|yes)\s*", RegexOptions.IgnoreCase).Success)
  6401.                 booleanValue = true;
  6402.             else if (Regex.Match(val, @"\s*(0|false|no)\s*", RegexOptions.IgnoreCase).Success)
  6403.                 booleanValue = false;
  6404.             else
  6405.                 isBooleanValue = false;
  6406.  
  6407.  
  6408.             /* Parse Integer Values */
  6409.             int integerValue = 0;
  6410.             //bool isIntegerValue = int.TryParse(val, out integerValue) && integerValue >= 0;
  6411.             bool isIntegerValue = int.TryParse(val, out integerValue);
  6412.  
  6413.             /* Parse Float Values */
  6414.             float floatValue = 0F;
  6415.             bool isFloatValue = float.TryParse(val, out floatValue) && floatValue >= 0F;
  6416.  
  6417.             /* Parse String List */
  6418.             List<string> stringListValue = new List<string>(Regex.Split(val.Replace(";", ",").Replace("|", ","), @"\s*,\s*"));
  6419.             bool isStringList = true;
  6420.  
  6421.             /* Parse String var */
  6422.             string stringValue = val;
  6423.             bool isStringValue = (val != null);
  6424.  
  6425.  
  6426.             if (isBooleanVar(var))
  6427.             {
  6428.                 if (!isBooleanValue)
  6429.                 {
  6430.                     SendConsoleMessage(sender, "\"" + val + "\" is invalid for " + var);
  6431.                     return false;
  6432.                 }
  6433.                 setBooleanVarValue(var, booleanValue);
  6434.                 return true;
  6435.             }
  6436.             else if (isIntegerVar(var))
  6437.             {
  6438.                 if (!isIntegerValue)
  6439.                 {
  6440.                     SendConsoleMessage(sender, "\"" + val + "\" is invalid for " + var);
  6441.                     return false;
  6442.                 }
  6443.  
  6444.                 setIntegerVarValue(var, integerValue);
  6445.                 return true;
  6446.             }
  6447.             else if (isFloatVar(var))
  6448.             {
  6449.                 if (!isFloatValue)
  6450.                 {
  6451.                     SendConsoleMessage(sender, "\"" + val + "\" is invalid for " + var);
  6452.                     return false;
  6453.                 }
  6454.  
  6455.                 setFloatVarValue(var, floatValue);
  6456.                 return true;
  6457.             }
  6458.             else if (isStringListVar(var))
  6459.             {
  6460.                 if (!isStringList)
  6461.                 {
  6462.                     SendConsoleMessage(sender, "\"" + val + "\"  is invalid for " + var);
  6463.                     return false;
  6464.                 }
  6465.  
  6466.                 setStringListVarValue(var, stringListValue);
  6467.                 return true;
  6468.             }
  6469.             else if (isStringVar(var))
  6470.             {
  6471.                 if (!isStringValue)
  6472.                 {
  6473.                     SendConsoleMessage(sender, "invalid value for " + var);
  6474.                     return false;
  6475.                 }
  6476.  
  6477.                 setStringVarValue(var, stringValue);
  6478.                 return true;
  6479.             }
  6480.             else
  6481.             {
  6482.                 SendConsoleMessage(sender, "Insane Balancer: unknown variable \"" + var + "\"");
  6483.                 return false;
  6484.             }
  6485.  
  6486.         }
  6487.  
  6488.         private bool isIntegerVar(string var)
  6489.         {
  6490.             return this.integerVariables.ContainsKey(var);
  6491.         }
  6492.  
  6493.         private int getIntegerVarValue(string var)
  6494.         {
  6495.             if (!isIntegerVar(var))
  6496.             {
  6497.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6498.                 return -1;
  6499.             }
  6500.  
  6501.             return this.integerVariables[var];
  6502.         }
  6503.  
  6504.         private bool setIntegerVarValue(string var, int val)
  6505.         {
  6506.             if (!isIntegerVar(var))
  6507.             {
  6508.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6509.                 return false;
  6510.             }
  6511.  
  6512.             if (hasIntegerValidator(var))
  6513.             {
  6514.                 integerVariableValidator validator = integerVarValidators[var];
  6515.                 if (validator(var, val) == false)
  6516.                     return false;
  6517.             }
  6518.  
  6519.             this.integerVariables[var] = val;
  6520.             return true;
  6521.         }
  6522.  
  6523.         private bool hasBooleanValidator(string var)
  6524.         {
  6525.             return booleanVarValidators.ContainsKey(var);
  6526.         }
  6527.  
  6528.         private bool hasIntegerValidator(string var)
  6529.         {
  6530.             return integerVarValidators.ContainsKey(var);
  6531.         }
  6532.  
  6533.         private bool hasStringValidator(string var)
  6534.         {
  6535.             return stringVarValidators.ContainsKey(var);
  6536.         }
  6537.  
  6538.         private bool isStringVar(string var)
  6539.         {
  6540.             return this.stringVariables.ContainsKey(var);
  6541.         }
  6542.  
  6543.  
  6544.         private string getStringVarValue(string var)
  6545.         {
  6546.             if (!isStringVar(var))
  6547.             {
  6548.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6549.                 return "";
  6550.             }
  6551.  
  6552.             return this.stringVariables[var];
  6553.         }
  6554.  
  6555.         private bool setStringVarValue(string var, string val)
  6556.         {
  6557.             if (!isStringVar(var))
  6558.             {
  6559.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6560.                 return false;
  6561.             }
  6562.  
  6563.  
  6564.             if (hasStringValidator(var))
  6565.             {
  6566.                 stringVariableValidator validator = stringVarValidators[var];
  6567.                 if (validator(var, val) == false)
  6568.                     return false;
  6569.             }
  6570.  
  6571.  
  6572.             string oldval = this.stringVariables[var];
  6573.             this.stringVariables[var] = val;
  6574.  
  6575.             return true;
  6576.         }
  6577.  
  6578.  
  6579.  
  6580.  
  6581.         private bool isStringListVar(string var)
  6582.         {
  6583.             return this.stringListVariables.ContainsKey(var);
  6584.         }
  6585.  
  6586.         private List<string> getStringListVarValue(string var)
  6587.         {
  6588.             if (!isStringListVar(var))
  6589.             {
  6590.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6591.                 return new List<string>();
  6592.             }
  6593.  
  6594.             string[] out_list = Regex.Split(this.stringListVariables[var].Replace(";", ",").Replace("|", ","), @"\s*,\s*");
  6595.             return new List<string>(out_list);
  6596.         }
  6597.  
  6598.         private bool setStringListVarValue(string var, List<string> val)
  6599.         {
  6600.             if (!isStringListVar(var))
  6601.             {
  6602.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6603.                 return false;
  6604.             }
  6605.  
  6606.             List<string> cleanList = new List<string>();
  6607.             foreach (string item in val)
  6608.                 if (Regex.Match(item, @"^\s*$").Success)
  6609.                     continue;
  6610.                 else
  6611.                     cleanList.Add(item);
  6612.  
  6613.             //this.stringListVariables[var] = val;
  6614.             this.stringListVariables[var] = String.Join("|", cleanList.ToArray());
  6615.             return true;
  6616.         }
  6617.  
  6618.  
  6619.         private bool isFloatVar(string var)
  6620.         {
  6621.             return this.floatVariables.ContainsKey(var);
  6622.         }
  6623.  
  6624.         private float getFloatVarValue(string var)
  6625.         {
  6626.             if (!isFloatVar(var))
  6627.             {
  6628.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6629.                 return -1F;
  6630.             }
  6631.  
  6632.             return this.floatVariables[var];
  6633.         }
  6634.  
  6635.         private bool setFloatVarValue(string var, float val)
  6636.         {
  6637.             if (!isFloatVar(var))
  6638.             {
  6639.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6640.                 return false;
  6641.             }
  6642.  
  6643.             this.floatVariables[var] = val;
  6644.             return true;
  6645.         }
  6646.  
  6647.  
  6648.         private bool isBooleanVar(string var)
  6649.         {
  6650.             return this.booleanVariables.ContainsKey(var);
  6651.         }
  6652.  
  6653.         private bool getBooleanVarValue(string var)
  6654.         {
  6655.             if (!isBooleanVar(var))
  6656.             {
  6657.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6658.                 return false;
  6659.             }
  6660.  
  6661.             return this.booleanVariables[var];
  6662.         }
  6663.  
  6664.         private bool setBooleanVarValue(string var, bool val)
  6665.         {
  6666.             if (!isBooleanVar(var))
  6667.             {
  6668.                 SendConsoleMessage("unknown variable \"" + var + "\"");
  6669.                 return false;
  6670.             }
  6671.  
  6672.             if (hasBooleanValidator(var))
  6673.             {
  6674.                 booleanVariableValidator validator = booleanVarValidators[var];
  6675.                 if (validator(var, val) == false)
  6676.                     return false;
  6677.             }
  6678.  
  6679.             this.booleanVariables[var] = val;
  6680.             return true;
  6681.         }
  6682.  
  6683.  
  6684.         private string getPluginVarValue(string var)
  6685.         {
  6686.             return getPluginVarValue(null, var);
  6687.         }
  6688.  
  6689.         private string getPluginVarValue(string sender, string var)
  6690.         {
  6691.             if (!getPluginVars().Contains(var))
  6692.             {
  6693.                 SendConsoleMessage(sender, "Insane Balancer: unknown variable \"" + var + "\"");
  6694.                 return "";
  6695.             }
  6696.  
  6697.             if (isBooleanVar(var))
  6698.             {
  6699.                 return getBooleanVarValue(var).ToString();
  6700.             }
  6701.             else if (isIntegerVar(var))
  6702.             {
  6703.                 return getIntegerVarValue(var).ToString();
  6704.             }
  6705.             else if (isFloatVar(var))
  6706.             {
  6707.                 return getFloatVarValue(var).ToString();
  6708.             }
  6709.             else if (isStringListVar(var))
  6710.             {
  6711.                 string lst = list2string(getStringListVarValue(var), "");
  6712.                 return lst;
  6713.             }
  6714.             else if (isStringVar(var))
  6715.             {
  6716.                 return getStringVarValue(var);
  6717.             }
  6718.             else
  6719.             {
  6720.                 SendConsoleMessage(sender, "Insane Balancer: unknown variable \"" + var + "\"");
  6721.                 return "";
  6722.             }
  6723.         }
  6724.  
  6725.         private List<string> getPluginVars()
  6726.         {
  6727.             return getPluginVars(false);
  6728.         }
  6729.  
  6730.         private List<string> getPluginVars(bool hide)
  6731.         {
  6732.             List<string> vars = new List<string>();
  6733.  
  6734.  
  6735.             vars.AddRange(getBooleanPluginVars());
  6736.             vars.AddRange(getIntegerPluginVars());
  6737.             vars.AddRange(getStringListPluginVars());
  6738.             vars.AddRange(getFloatPluginVars());
  6739.             vars.AddRange(getStringPluginVars());
  6740.  
  6741.             if (hide && !getBooleanVarValue("advanced_mode"))
  6742.             {
  6743.                 foreach (string hidden_var in hiddenVariables)
  6744.                     vars.Remove(hidden_var);
  6745.             }
  6746.  
  6747.             return vars;
  6748.         }
  6749.  
  6750.  
  6751.         private List<string> getStringPluginVars()
  6752.         {
  6753.             return new List<string>(this.stringVariables.Keys);
  6754.         }
  6755.  
  6756.  
  6757.         private List<string> getStringListPluginVars()
  6758.         {
  6759.             return new List<string>(this.stringListVariables.Keys);
  6760.         }
  6761.  
  6762.  
  6763.         private List<string> getIntegerPluginVars()
  6764.         {
  6765.             return new List<string>(this.integerVariables.Keys);
  6766.         }
  6767.  
  6768.         private List<string> getFloatPluginVars()
  6769.         {
  6770.             return new List<string>(this.floatVariables.Keys);
  6771.         }
  6772.  
  6773.         private List<string> getBooleanPluginVars()
  6774.         {
  6775.             return new List<string>(this.booleanVariables.Keys);
  6776.         }
  6777.  
  6778.         public string playerstate2stringED(PlayerState state)
  6779.         {
  6780.             switch (state)
  6781.             {
  6782.                 case PlayerState.alive:
  6783.                     return "is alive";
  6784.                 case PlayerState.dead:
  6785.                     return "is dead";
  6786.                 case PlayerState.kicked:
  6787.                     return "was kicked";
  6788.                 case PlayerState.left:
  6789.                     return "left the game";
  6790.                 case PlayerState.limbo:
  6791.                     return "is in limbo";
  6792.                 default:
  6793.                     return "(%player_state%)";
  6794.             }
  6795.  
  6796.         }
  6797.  
  6798.  
  6799.         public string list2string(List<string> list, string glue)
  6800.         {
  6801.  
  6802.             if (list == null || list.Count == 0)
  6803.                 return "";
  6804.             else if (list.Count == 1)
  6805.                 return list[0];
  6806.  
  6807.             string last = list[list.Count - 1];
  6808.             list.RemoveAt(list.Count - 1);
  6809.  
  6810.             string str = "";
  6811.             foreach (string item in list)
  6812.                 str += item + ", ";
  6813.  
  6814.             return str + glue + last;
  6815.         }
  6816.  
  6817.         public string list2string(List<string> list)
  6818.         {
  6819.             return list2string(list, "and ");
  6820.         }
  6821.  
  6822.  
  6823.         private List<string> getAdminList()
  6824.         {
  6825.             return getStringListVarValue("admin_list");
  6826.         }
  6827.  
  6828.         private bool isAdmin(string soldier)
  6829.         {
  6830.             List<string> admin_list = getAdminList();
  6831.             return admin_list.Contains(soldier);
  6832.         }
  6833.  
  6834.  
  6835.         private PlayerProfile getPlayerProfile(CPlayerInfo info)
  6836.         {
  6837.             return getPlayerProfile(info.SoldierName);
  6838.         }
  6839.  
  6840.  
  6841.         private List<PlayerProfile> getPlayersProfile(string name)
  6842.         {
  6843.  
  6844.             List<PlayerProfile> profiles = new List<PlayerProfile>();
  6845.             foreach (KeyValuePair<string, PlayerProfile> pair in this.players)
  6846.             {
  6847.  
  6848.                 if (pair.Value.info == null || pair.Value.info.TeamID < 0 ||
  6849.                     pair.Value.wasKicked() || pair.Value.leftGame())
  6850.                     continue;
  6851.  
  6852.                 if (name.Equals(""))
  6853.                     profiles.Add(pair.Value);
  6854.                 else if (pair.Key.ToLower().Contains(name.ToLower()))
  6855.                     profiles.Add(pair.Value);
  6856.             }
  6857.  
  6858.  
  6859.             return profiles;
  6860.         }
  6861.  
  6862.         private PlayerProfile getPlayerProfile(string name)
  6863.         {
  6864.             PlayerProfile pp;
  6865.             this.players.TryGetValue(name, out pp);
  6866.             return pp;
  6867.         }
  6868.  
  6869.  
  6870.         public override void OnPunkbusterPlayerInfo(CPunkbusterInfo cpbiPlayer)
  6871.         {
  6872.  
  6873.             if (cpbiPlayer == null)
  6874.                 return;
  6875.  
  6876.             processNewPlayer(cpbiPlayer);
  6877.         }
  6878.  
  6879.         public void dump_exception(Exception e)
  6880.         {
  6881.             if (e.GetType().Equals(typeof(ThreadAbortException)))
  6882.             {
  6883.                 Thread.ResetAbort();
  6884.                 return;
  6885.             }
  6886.            
  6887.  
  6888.             ConsoleWrite("^1^bEXCEPTION^0^n: " + e.GetType() + ": " + e.Message);
  6889.             try
  6890.             {
  6891.                 string class_name = "InsaneBalancer";
  6892.                 // Create a temporary file
  6893.                 string path = class_name+".dump";
  6894.  
  6895.  
  6896.                 ConsoleWrite("^1Extra information dumped in file " + path);
  6897.                 using (FileStream fs = File.Open(path, FileMode.Append))
  6898.                 {
  6899.                     String version = GetPluginVersion();
  6900.                     String trace_str = "\n-----------------------------------------------\n";
  6901.                     trace_str += "Version: " + class_name +" "+ version + "\n";
  6902.                     trace_str += "Date: "+DateTime.Now.ToString()+"\n";
  6903.                     trace_str += e.GetType() + ": " + e.Message + "\n\n";
  6904.                     trace_str += "Stack Trace: \n"+ e.StackTrace + "\n\n";
  6905.                     trace_str += "MSIL Stack Trace:\n";
  6906.  
  6907.                     StackTrace trace = new StackTrace(e);
  6908.                     StackFrame[] frames = trace.GetFrames();
  6909.                     foreach (StackFrame frame in frames)
  6910.                         trace_str += "    "+frame.GetMethod() +", IL: " + String.Format("0x{0:X}",frame.GetILOffset())+"\n";
  6911.                        
  6912.                    
  6913.                     Byte[] info = new UTF8Encoding(true).GetBytes(trace_str);
  6914.                     fs.Write(info, 0, info.Length);
  6915.                 }
  6916.                
  6917.  
  6918.             }
  6919.             catch (Exception ex)
  6920.             {
  6921.                 ConsoleWrite("^1^bWARNING^0^n: Unable to dump extra exception information.");
  6922.                 ConsoleWrite("^1^bEXCEPTION^0^n:  " + ex.TargetSite + ": " + ex.Message);
  6923.  
  6924.             }
  6925.         }
  6926.  
  6927.         public void dump_data(string s)
  6928.         {
  6929.  
  6930.             try
  6931.             {
  6932.                 // Create a temporary file
  6933.                 string path = Path.GetRandomFileName() + ".dump";
  6934.  
  6935.                 ConsoleWrite("^1Dumping information in file " + path);
  6936.                 using (FileStream fs = File.Open(path, FileMode.OpenOrCreate))
  6937.                 {
  6938.                     Byte[] info = new UTF8Encoding(true).GetBytes(s);
  6939.                     fs.Write(info, 0, info.Length);
  6940.                 }
  6941.  
  6942.             }
  6943.             catch (Exception ex)
  6944.             {
  6945.                 ConsoleWrite("^1^bWARNING^0^n: Unable to dump extra exception information.");
  6946.                 ConsoleWrite("^1^bEXCEPTION^0^n:  " + ex.TargetSite + ": " + ex.Message);
  6947.  
  6948.             }
  6949.         }
  6950.  
  6951.         List<String> scratch_list = new List<string>();
  6952.  
  6953.         public void updateQueues(List<CPlayerInfo> lstPlayers)
  6954.         {
  6955.             lock (mutex)
  6956.             {
  6957.                 scratch_handle.Reset();
  6958.                 // update the scratch list
  6959.                 scratch_list.Clear();
  6960.                 foreach (CPlayerInfo info in lstPlayers)
  6961.                     if (!scratch_list.Contains(info.SoldierName))
  6962.                         scratch_list.Add(info.SoldierName);
  6963.  
  6964.                 scratch_handle.Set();
  6965.  
  6966.                 // make a list of players to drop from the stats queue
  6967.                 List<String> players_to_remove = new List<string>();
  6968.                 foreach (KeyValuePair<String, CPunkbusterInfo> pair in new_player_queue)
  6969.                     if (!scratch_list.Contains(pair.Key) && !players_to_remove.Contains(pair.Key))
  6970.                         players_to_remove.Add(pair.Key);
  6971.  
  6972.                 // now actually drop them from the new players queue
  6973.                 foreach (String name in players_to_remove)
  6974.                     if (new_player_queue.ContainsKey(name))
  6975.                     {
  6976.                         ConsoleWrite("Looks like ^b" + name + "^n left the server, removing him from stats queue");
  6977.                         new_player_queue.Remove(name);
  6978.                     }
  6979.  
  6980.                 // make a list of players to drop from the new players batch
  6981.                 players_to_remove.Clear();
  6982.                 foreach (KeyValuePair<String, PlayerProfile> pair in new_players_batch)
  6983.                     if (!scratch_list.Contains(pair.Key) && !players_to_remove.Contains(pair.Key))
  6984.                         players_to_remove.Add(pair.Key);
  6985.  
  6986.                 // now actually drop them from the new players batch
  6987.                 foreach (String name in players_to_remove)
  6988.                     if (new_players_batch.ContainsKey(name))
  6989.                         new_players_batch.Remove(name);
  6990.             }
  6991.         }
  6992.  
  6993.  
  6994.         public void syncPlayersList(List<CPlayerInfo> lstPlayers)
  6995.         {
  6996.  
  6997.             lock (mutex)
  6998.             {
  6999.                 // first update the information taht players that still are in list
  7000.                 foreach (CPlayerInfo cpiPlayer in lstPlayers)
  7001.                     if (this.players.ContainsKey(cpiPlayer.SoldierName))
  7002.                         this.players[cpiPlayer.SoldierName].updateInfo(cpiPlayer);
  7003.  
  7004.                 //build a lookup table
  7005.                 Dictionary<String, bool> player_lookup = new Dictionary<string, bool>();
  7006.                 foreach (CPlayerInfo pinfo in lstPlayers)
  7007.                     if (!player_lookup.ContainsKey(pinfo.SoldierName))
  7008.                         player_lookup.Add(pinfo.SoldierName, true);
  7009.  
  7010.  
  7011.                 List<String> players_to_remove = new List<string>();
  7012.  
  7013.                 // now make a list of players that will need to be removed
  7014.                 foreach (KeyValuePair<String, PlayerProfile> pair in players)
  7015.                     if (!player_lookup.ContainsKey(pair.Key) && !players_to_remove.Contains(pair.Key))
  7016.                         players_to_remove.Add(pair.Key);
  7017.  
  7018.                 // now actually remove them
  7019.                 foreach (String pname in players_to_remove)
  7020.                     if (players.ContainsKey(pname))
  7021.                         players.Remove(pname);
  7022.             }
  7023.         }
  7024.  
  7025.  
  7026.         public override void OnListPlayers(List<CPlayerInfo> lstPlayers, CPlayerSubset cpsSubset)
  7027.         {
  7028.             if (cpsSubset.Subset != CPlayerSubset.PlayerSubsetType.All)
  7029.                 return;
  7030.  
  7031.  
  7032.             updateQueues(lstPlayers);
  7033.             syncPlayersList(lstPlayers);
  7034.  
  7035.             /* fail safe to get the maximum number of players in server */
  7036.             if (lstPlayers.Count > max_player_count)
  7037.                 max_player_count = lstPlayers.Count;
  7038.  
  7039.  
  7040.             if (check_state_phase == 1)
  7041.                 startCheckState(utc);
  7042.         }
  7043.  
  7044.     }
  7045. }
Add Comment
Please, Sign In to add comment