Guest User

Insane Balancer 0.0.0.5 (BF3)

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