From 73dfb9a61258ba3fa326ca7565d3515f8bb7eda4 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Fri, 13 Mar 2015 18:40:16 -0500 Subject: [PATCH] -close config files after reading oops -added reload command -added macros! (Denoted by {{MACRO}} in server config right now only {{WISDOM}} and {{TOTALPLAYERS}}) -added IP's (tracks and rebans new accounts on same banned ip)! -aliases -reworked database classes -heartbeat gives running version -player banned in find gives last ban reason -reworked rcon yet again --- Admin/Bans.cs | 16 +- Admin/Command.cs | 55 +- Admin/Database.cs | 553 +++++++++++------- Admin/Heartbeat.cs | 2 +- Admin/IW4M ADMIN.csproj | 2 +- Admin/Main.cs | 2 +- Admin/Player.cs | 69 ++- Admin/RCON.cs | 102 ++-- Admin/Server.cs | 348 +++++++---- Admin/Utilities.cs | 53 +- Admin/config/messages.cfg | 2 +- Admin/version.txt | 12 +- GAME/Game_TEST/Game_TEST.sln | 22 - GAME/Game_TEST/Game_TEST/Game1.cs | 91 --- GAME/Game_TEST/Game_TEST/Game_TEST.csproj | 63 -- GAME/Game_TEST/Game_TEST/Icon.ico | Bin 32038 -> 0 bytes GAME/Game_TEST/Game_TEST/Program.cs | 26 - .../Game_TEST/Properties/AssemblyInfo.cs | 36 -- 18 files changed, 812 insertions(+), 642 deletions(-) delete mode 100644 GAME/Game_TEST/Game_TEST.sln delete mode 100644 GAME/Game_TEST/Game_TEST/Game1.cs delete mode 100644 GAME/Game_TEST/Game_TEST/Game_TEST.csproj delete mode 100644 GAME/Game_TEST/Game_TEST/Icon.ico delete mode 100644 GAME/Game_TEST/Game_TEST/Program.cs delete mode 100644 GAME/Game_TEST/Game_TEST/Properties/AssemblyInfo.cs diff --git a/Admin/Bans.cs b/Admin/Bans.cs index 3c2e4d98a..c2dc9c62f 100644 --- a/Admin/Bans.cs +++ b/Admin/Bans.cs @@ -6,12 +6,13 @@ namespace IW4MAdmin { class Ban { - public Ban(String Reas, String TargID, String From) + public Ban(String Reas, String TargID, String From, DateTime time, String ip) { Reason = Reas; npID = TargID; bannedByID = From; - When = DateTime.Now; + When = time; + IP = ip; } public String getReason() @@ -28,11 +29,22 @@ namespace IW4MAdmin { return bannedByID; } + + public String getIP() + { + return IP; + } + + public String getWhen() + { + return When.ToString("yyyy-MM-dd HH:mm:ss"); ; + } private String Reason; private String npID; private String bannedByID; private DateTime When; + private String IP; } diff --git a/Admin/Command.cs b/Admin/Command.cs index 661a36594..2c831c5bf 100644 --- a/Admin/Command.cs +++ b/Admin/Command.cs @@ -70,7 +70,7 @@ namespace IW4MAdmin E.Origin.setLevel(Player.Permission.Owner); E.Origin.Tell("Congratulations, you have claimed ownership of this server!"); E.Owner.owner = E.Origin; - E.Owner.DB.updatePlayer(E.Origin); + E.Owner.clientDB.updatePlayer(E.Origin); } else E.Origin.Tell("This server already has an owner!"); @@ -168,15 +168,10 @@ namespace IW4MAdmin E.Target.LastOffense = Utilities.removeWords(E.Data, 1); E.Target.lastEvent = E; // needs to be fixed String Message; -#if DEBUG - Message = "^1Player Banned: ^5" + E.Target.LastOffense + "^7 (appeal at nbsclan.org)"; -#else if (E.Owner.Website == null) Message = "^1Player Banned: ^5" + E.Target.LastOffense; else Message = "^1Player Banned: ^5" + E.Target.LastOffense + "^7 (appeal at " + E.Owner.Website + ")"; - -#endif if (E.Origin.getLevel() > E.Target.getLevel()) { E.Target.Ban(Message, E.Origin); @@ -324,7 +319,7 @@ namespace IW4MAdmin E.Target.Tell("Congratulations! You have been promoted to ^3" + newPerm); E.Origin.Tell(E.Target.getName() + " was successfully promoted!"); //NEEED TO MOVE - E.Owner.DB.updatePlayer(E.Target); + E.Owner.clientDB.updatePlayer(E.Target); } else @@ -379,8 +374,7 @@ namespace IW4MAdmin public override void Execute(Event E) { - String Quote = new Connection("http://www.iheartquotes.com/api/v1/random?max_lines=1&max_characters=200").Read(); - E.Owner.Broadcast(Utilities.removeNastyChars(Quote)); + E.Owner.Broadcast(E.Owner.Wisdom()); } } @@ -417,16 +411,32 @@ namespace IW4MAdmin public override void Execute(Event E) { - var db_players = E.Owner.DB.findPlayers(E.Data.Trim()); + var db_players = E.Owner.clientDB.findPlayers(E.Data.Trim()); if (db_players == null) { E.Origin.Tell("No players found"); return; } + + foreach (Player P in db_players) - { - String mesg = String.Format("[^3{0}^7] [^3@{1}^7] - {2} [{3}^7]", P.getName(), P.getDBID(), P.getID(), Utilities.levelToColor(P.getLevel())); + { + String mesg; + var db_aliases = E.Owner.aliasDB.getPlayer(P.getDBID()); + + if (P.getLevel() == Player.Permission.Banned) + mesg = String.Format("[^3{0}^7] [^3@{1}^7] - {2} [{3}^7] - {4}", P.getName(), P.getDBID(), P.getID(), Utilities.levelToColor(P.getLevel()), P.getLastO()); + else + mesg = String.Format("[^3{0}^7] [^3@{1}^7] - {2} [{3}^7]", P.getName(), P.getDBID(), P.getID(), Utilities.levelToColor(P.getLevel())); + E.Origin.Tell(mesg); + if (db_aliases != null) + { + mesg = "Aliases: "; + foreach (String S in db_aliases.getNames()) + mesg += S + ','; + } + E.Origin.Tell(mesg); } @@ -458,7 +468,7 @@ namespace IW4MAdmin public override void Execute(Event E) { E.Target.Tell("^1" + E.Origin.getName() + " ^3[PM]^7 - " + E.Data); - E.Origin.Tell("Sucessfully sent message"); + E.Origin.Tell(String.Format("To ^3{0} ^7-> {1}", E.Target.getName(), E.Data)); } } @@ -473,7 +483,7 @@ namespace IW4MAdmin else { if (E.Target.stats == null) - E.Target.stats = E.Owner.stats.getStats(E.Target.getDBID()); + E.Target.stats = E.Owner.statDB.getStats(E.Target.getDBID()); E.Origin.Tell(String.Format("[^3{4}^7] ^5{0} ^7KILLS | ^5{1} ^7DEATHS | ^5{2} ^7KDR | ^5{3} ^7SKILL", E.Target.stats.Kills, E.Target.stats.Deaths, E.Target.stats.KDR, E.Target.stats.Skill, E.Target.getName())); } } @@ -485,12 +495,12 @@ namespace IW4MAdmin public override void Execute(Event E) { - List Top = E.Owner.stats.topStats(); + List Top = E.Owner.statDB.topStats(); List TopP = new List(); foreach (Stats S in Top) { - Player P = E.Owner.DB.findPlayers(S.Kills); // BAD + Player P = E.Owner.clientDB.getPlayer(S.Kills); // BAD if (P != null && P.getLevel() != Player.Permission.Banned) { P.stats = S; @@ -512,5 +522,18 @@ namespace IW4MAdmin } } + class Reload : Command + { + public Reload(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { } + + public override void Execute(Event E) + { + if (E.Owner.Reload()) + E.Origin.Tell("Sucessfully reloaded configs!"); + else + E.Origin.Tell("Unable to reload configs :("); + } + } + } diff --git a/Admin/Database.cs b/Admin/Database.cs index 0da1d3707..3dfb6c05a 100644 --- a/Admin/Database.cs +++ b/Admin/Database.cs @@ -8,226 +8,20 @@ using System.Collections; namespace IW4MAdmin { - class Database + abstract class Database { - public enum Type - { - Clients, - Stats - } - - public Database(String FN, Type T) + public Database(String FN) { FileName = FN; DBCon = String.Format("Data Source={0}", FN); Con = new SQLiteConnection(DBCon); - DBType = T; - Init(T); - } - - private void Init(Type T) - { - if(!File.Exists(FileName)) - { - switch (T) - { - case Type.Clients: - String query = "CREATE TABLE [CLIENTS] ( [Name] TEXT NULL, [npID] TEXT NULL, [Number] INTEGER PRIMARY KEY AUTOINCREMENT, [Level] INT DEFAULT 0 NULL, [LastOffense] TEXT NULL, [Connections] INT DEFAULT 1 NULL);"; - ExecuteNonQuery(query); - query = "CREATE TABLE [BANS] ( [Reason] TEXT NULL, [npID] TEXT NULL, [bannedByID] Text NULL);"; - ExecuteNonQuery(query); - break; - case Type.Stats: - String query_stats = "CREATE TABLE [STATS] ( [Number] INTEGER, [KILLS] INTEGER DEFAULT 0, [DEATHS] INTEGER DEFAULT 0, [KDR] REAL DEFAULT 0, [SKILL] REAL DEFAULT 0 );"; - ExecuteNonQuery(query_stats); - break; - } - - } - } - - public Player getPlayer(String ID, int cNum) - { - String Query = String.Format("SELECT * FROM CLIENTS WHERE npID = '{0}' LIMIT 1", ID); - DataTable Result = GetDataTable(Query); - - if (Result != null && Result.Rows.Count > 0) - { - DataRow ResponseRow = Result.Rows[0]; - return new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), cNum, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), ResponseRow["LastOffense"].ToString(), (int)ResponseRow["Connections"]); - } - - else - return null; - } - - public List findPlayers(String name) - { - String Query = String.Format("SELECT * FROM CLIENTS WHERE Name LIKE '%{0}%' LIMIT 10", name); - DataTable Result = GetDataTable(Query); - - List Players = new List(); - - if (Result != null && Result.Rows.Count > 0) - { - foreach (DataRow p in Result.Rows) - { - Players.Add(new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), ((int)p["Connections"]))); - } - return Players; - } - - else - return null; - } - - public Player findPlayers(int dbIndex) - { - String Query = String.Format("SELECT * FROM CLIENTS WHERE Number = '{0}' LIMIT 1", dbIndex); - DataTable Result = GetDataTable(Query); - - if (Result != null && Result.Rows.Count > 0) - { - foreach (DataRow p in Result.Rows) - return new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), ((int)p["Connections"])); - } - - return null; - } - - public Player getOwner() - { - String Query = String.Format("SELECT * FROM CLIENTS WHERE Level = '{0}'", 4); - DataTable Result = GetDataTable(Query); - - if (Result != null && Result.Rows.Count > 0) - { - DataRow ResponseRow = Result.Rows[0]; - return new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), -1, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), null, 0); - } - - else - return null; - } - - public List getBans() - { - List Bans = new List(); - DataTable Result = GetDataTable("SELECT * FROM BANS"); - - foreach (DataRow Row in Result.Rows) - Bans.Add(new Ban(Row["Reason"].ToString(), Row["npID"].ToString(), Row["bannedByID"].ToString())); - - return Bans; - } - - public Stats getStats(int DBID) - { - String Query = String.Format("SELECT * FROM STATS WHERE Number = '{0}'", DBID); - DataTable Result = GetDataTable(Query); - - if (Result != null && Result.Rows.Count > 0) - { - DataRow ResponseRow = Result.Rows[0]; - return new Stats(Convert.ToInt32(ResponseRow["KILLS"]), Convert.ToInt32(ResponseRow["DEATHS"]), Convert.ToDouble(ResponseRow["KDR"]), Convert.ToDouble(ResponseRow["SKILL"])); - } - - else - return null; - } - - public void removeBan(String GUID) - { - String Query = String.Format("DELETE FROM BANS WHERE npID = '{0}'", GUID); - ExecuteNonQuery(Query); - } - - public void addPlayer(Player P) - { - Dictionary newPlayer = new Dictionary(); - - if (DBType == Type.Clients) - { - newPlayer.Add("Name", Utilities.removeNastyChars(P.getName())); - newPlayer.Add("npID", P.getID()); - newPlayer.Add("Level", (int)P.getLevel()); - newPlayer.Add("LastOffense", ""); - newPlayer.Add("Connections", 1); - - Insert("CLIENTS", newPlayer); - } - - if (DBType == Type.Stats) - { - newPlayer.Add("Number", P.getDBID()); - newPlayer.Add("KILLS", 0); - newPlayer.Add("DEATHS", 0); - newPlayer.Add("KDR", 0); - newPlayer.Add("SKILL", 0); - Insert("STATS", newPlayer); - } - } - - public List topStats() - { - String Query = String.Format("SELECT * FROM STATS WHERE SKILL > '{0}' ORDER BY SKILL DESC LIMIT 4", 20); - DataTable Result = GetDataTable(Query); - - List Top = new List(); - - if (Result != null && Result.Rows.Count > 0) - { - foreach (DataRow D in Result.Rows) - { - Stats S = new Stats(Convert.ToInt32(D["Number"]), Convert.ToInt32(D["DEATHS"]), Convert.ToDouble(D["KDR"]), Convert.ToDouble(D["SKILL"])); - if (S.Skill > 20) - Top.Add(S); - } - } - - return Top; - } - - public void updatePlayer(Player P) - { - Dictionary updatedPlayer = new Dictionary(); - - if (DBType == Type.Clients) - { - updatedPlayer.Add("Name", P.getName()); - updatedPlayer.Add("npID", P.getID()); - updatedPlayer.Add("Level", (int)P.getLevel()); - updatedPlayer.Add("LastOffense", P.getLastO()); - updatedPlayer.Add("Connections", P.getConnections()); - - Update("CLIENTS", updatedPlayer, String.Format("npID = '{0}'", P.getID())); - } - - if (DBType == Type.Stats) - { - updatedPlayer.Add("KILLS", P.stats.Kills); - updatedPlayer.Add("DEATHS", P.stats.Deaths); - updatedPlayer.Add("KDR", Math.Round(P.stats.KDR, 2)); - updatedPlayer.Add("SKILL", P.stats.Skill); - - Update("STATS", updatedPlayer, String.Format("Number = '{0}'", P.getDBID())); - } - } - - public void addBan(Ban B) - { - Dictionary newBan = new Dictionary(); - - newBan.Add("Reason", B.getReason()); - newBan.Add("npID", B.getID()); - newBan.Add("bannedByID", B.getBanner()); - - Insert("BANS", newBan); + Init(); } + abstract public void Init(); + //HELPERS - - public bool Insert(String tableName, Dictionary data) + protected bool Insert(String tableName, Dictionary data) { String columns = ""; String values = ""; @@ -251,7 +45,7 @@ namespace IW4MAdmin return returnCode; } - public bool Update(String tableName, Dictionary data, String where) + protected bool Update(String tableName, Dictionary data, String where) { String vals = ""; Boolean returnCode = true; @@ -275,13 +69,13 @@ namespace IW4MAdmin return returnCode; } - public DataRow getDataRow(String Q) + protected DataRow getDataRow(String Q) { DataRow Result = GetDataTable(Q).Rows[0]; return Result; } - private int ExecuteNonQuery(String Request) + protected int ExecuteNonQuery(String Request) { Con.Open(); SQLiteCommand CMD = new SQLiteCommand(Con); @@ -291,7 +85,7 @@ namespace IW4MAdmin return rowsUpdated; } - public DataTable GetDataTable(String sql) + protected DataTable GetDataTable(String sql) { DataTable dt = new DataTable(); try @@ -313,9 +107,328 @@ namespace IW4MAdmin } //END - private String FileName; - private String DBCon; - private SQLiteConnection Con; - private Type DBType; + protected String FileName; + protected String DBCon; + protected SQLiteConnection Con; + } + + class ClientsDB : Database + { + public ClientsDB(String FN) : base(FN) { } + + public override void Init() + { + if(!File.Exists(FileName)) + { + String Create = "CREATE TABLE [CLIENTS] ( [Name] TEXT NULL, [npID] TEXT NULL, [Number] INTEGER PRIMARY KEY AUTOINCREMENT, [Level] INT DEFAULT 0 NULL, [LastOffense] TEXT NULL, [Connections] INT DEFAULT 1 NULL, [IP] TEXT NULL);"; + ExecuteNonQuery(Create); + Create = "CREATE TABLE [BANS] ( [Reason] TEXT NULL, [npID] TEXT NULL, [bannedByID] TEXT NULL, [IP] TEXT NULL, [TIME] TEXT NULL);"; + ExecuteNonQuery(Create); + } + } + + //Returns a single player object with matching GUID, false if no matches + public Player getPlayer(String ID, int cNum) + { + String Query = String.Format("SELECT * FROM CLIENTS WHERE npID = '{0}' LIMIT 1", ID); + DataTable Result = GetDataTable(Query); + + if (Result != null && Result.Rows.Count > 0) + { + DataRow ResponseRow = Result.Rows[0]; + + + if (ResponseRow["IP"].ToString().Length < 2) + ResponseRow["IP"] = DateTime.Now.ToString(); // because aliases and backwards compatibility + + return new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), cNum, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), ResponseRow["LastOffense"].ToString(), (int)ResponseRow["Connections"], ResponseRow["IP"].ToString()); + } + + else + return null; + } + + //Overloaded method for getPlayer, returns Client with matching DBIndex, null if none found + public Player getPlayer(int dbIndex) + { + String Query = String.Format("SELECT * FROM CLIENTS WHERE Number = '{0}' LIMIT 1", dbIndex); + DataTable Result = GetDataTable(Query); + + if (Result != null && Result.Rows.Count > 0) + { + DataRow p = Result.Rows[0]; + + if (p["IP"].ToString().Length < 2) + p["IP"] = DateTime.Now.ToString(); // because aliases and backwards compatibility + + return new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), Convert.ToInt32(p["Connections"]), p["IP"].ToString()); + } + + else + return null; + } + + //Returns a list of players matching name parameter, null if no players found matching + public List findPlayers(String name) + { + String Query = String.Format("SELECT * FROM CLIENTS WHERE Name LIKE '%{0}%' LIMIT 8", name); + DataTable Result = GetDataTable(Query); + + List Players = new List(); + + if (Result != null && Result.Rows.Count > 0) + { + foreach (DataRow p in Result.Rows) + { + Players.Add(new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), Convert.ToInt32(p["Connections"]), p["IP"].ToString())); + } + return Players; + } + + else + return null; + } + + //Returns any player with level 4 permissions, null if no owner found + public Player getOwner() + { + String Query = String.Format("SELECT * FROM CLIENTS WHERE Level = '{0}'", 4); + DataTable Result = GetDataTable(Query); + + if (Result != null && Result.Rows.Count > 0) + { + DataRow ResponseRow = Result.Rows[0]; + if (ResponseRow["IP"].ToString().Length < 6) + ResponseRow["IP"] = "0"; + return new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), -1, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), null, 0, ResponseRow["IP"].ToString()); + } + + else + return null; + } + + //Returns list of bans in database + public List getBans() + { + List Bans = new List(); + DataTable Result = GetDataTable("SELECT * FROM BANS"); + + foreach (DataRow Row in Result.Rows) + { + if (Row["TIME"].ToString().Length < 2) //compatibility with my old database + Row["TIME"] = DateTime.Now.ToString(); + if (Row["IP"].ToString().Length < 2) + Row["IP"] = DateTime.Now.ToString(); //because we don't have old ip's and don't want a messy alias + + Bans.Add(new Ban(Row["Reason"].ToString(), Row["npID"].ToString(), Row["bannedByID"].ToString(), DateTime.Parse(Row["TIME"].ToString()), Row["IP"].ToString())); + } + + return Bans; + } + + //Returns total number of player entries in database + public int totalPlayers() + { + DataTable Result = GetDataTable("SELECT * from CLIENTS ORDER BY Number DESC LIMIT 1"); + if (Result.Rows.Count > 0) + return Convert.ToInt32(Result.Rows[0]["Number"]); + else + return 0; + } + + //Add specified player to database + public void addPlayer(Player P) + { + Dictionary newPlayer = new Dictionary(); + + newPlayer.Add("Name", Utilities.removeNastyChars(P.getName())); + newPlayer.Add("npID", P.getID()); + newPlayer.Add("Level", (int)P.getLevel()); + newPlayer.Add("LastOffense", ""); + newPlayer.Add("Connections", 1); + newPlayer.Add("IP", P.getIP()); + + Insert("CLIENTS", newPlayer); + } + + ///Update information of specified player + public void updatePlayer(Player P) + { + Dictionary updatedPlayer = new Dictionary(); + + updatedPlayer.Add("Name", P.getName()); + updatedPlayer.Add("npID", P.getID()); + updatedPlayer.Add("Level", (int)P.getLevel()); + updatedPlayer.Add("LastOffense", P.getLastO()); + updatedPlayer.Add("Connections", P.getConnections()); + updatedPlayer.Add("IP", P.getIP()); + + Update("CLIENTS", updatedPlayer, String.Format("npID = '{0}'", P.getID())); + } + + + //Add specified ban to database + public void addBan(Ban B) + { + Dictionary newBan = new Dictionary(); + + newBan.Add("Reason", B.getReason()); + newBan.Add("npID", B.getID()); + newBan.Add("bannedByID", B.getBanner()); + newBan.Add("IP", B.getIP()); + newBan.Add("TIME", Utilities.DateTimeSQLite(DateTime.Now)); + + Insert("BANS", newBan); + } + + + //Deletes ban with matching GUID + public void removeBan(String GUID) + { + String Query = String.Format("DELETE FROM BANS WHERE npID = '{0}'", GUID); + ExecuteNonQuery(Query); + } + + + } + + class StatsDB : Database + { + public StatsDB(String FN) : base(FN) { } + + public override void Init() + { + if (!File.Exists(FileName)) + { + String Create = "CREATE TABLE [STATS] ( [Number] INTEGER, [KILLS] INTEGER DEFAULT 0, [DEATHS] INTEGER DEFAULT 0, [KDR] REAL DEFAULT 0, [SKILL] REAL DEFAULT 0 );"; + ExecuteNonQuery(Create); + } + } + + // Return stats for player specified by Database ID, null if no matches + public Stats getStats(int DBID) + { + String Query = String.Format("SELECT * FROM STATS WHERE Number = '{0}'", DBID); + DataTable Result = GetDataTable(Query); + + if (Result != null && Result.Rows.Count > 0) + { + DataRow ResponseRow = Result.Rows[0]; + return new Stats(Convert.ToInt32(ResponseRow["KILLS"]), Convert.ToInt32(ResponseRow["DEATHS"]), Convert.ToDouble(ResponseRow["KDR"]), Convert.ToDouble(ResponseRow["SKILL"])); + } + + else + return null; + } + + public void addPlayer(Player P) + { + Dictionary newPlayer = new Dictionary(); + + newPlayer.Add("Number", P.getDBID()); + newPlayer.Add("KILLS", 0); + newPlayer.Add("DEATHS", 0); + newPlayer.Add("KDR", 0); + newPlayer.Add("SKILL", 1); + + Insert("STATS", newPlayer); + } + + //Update stat information of specified player + public void updatePlayer(Player P) + { + Dictionary updatedPlayer = new Dictionary(); + + updatedPlayer.Add("KILLS", P.stats.Kills); + updatedPlayer.Add("DEATHS", P.stats.Deaths); + updatedPlayer.Add("KDR", Math.Round(P.stats.KDR, 2)); + updatedPlayer.Add("SKILL", P.stats.Skill); + + Update("STATS", updatedPlayer, String.Format("Number = '{0}'", P.getDBID())); + } + + //Returns top 8 players (we filter through them later) + public List topStats() + { + String Query = String.Format("SELECT * FROM STATS WHERE SKILL > '{0}' ORDER BY SKILL DESC LIMIT 8", 20); + DataTable Result = GetDataTable(Query); + + List Top = new List(); + + if (Result != null && Result.Rows.Count > 0) + { + foreach (DataRow D in Result.Rows) + { + Stats S = new Stats(Convert.ToInt32(D["Number"]), Convert.ToInt32(D["DEATHS"]), Convert.ToDouble(D["KDR"]), Convert.ToDouble(D["SKILL"])); + if (S.Skill > 20) + Top.Add(S); + } + } + + return Top; + } + + public void clearSkill() + { + String Query = "SELECT * FROM STATS"; + DataTable Result = GetDataTable(Query); + + if (Result != null && Result.Rows.Count > 0) + { + foreach (DataRow D in Result.Rows) + Update("STATS", new Dictionary () { {"SKILL", 1} }, String.Format("Number = '{0}'", D["Number"])); + } + } + } + + class AliasesDB : Database + { + public AliasesDB(String FN) : base(FN) { } + + public override void Init() + { + if (!File.Exists(FileName)) + { + String Create = "CREATE TABLE [ALIASES] ( [Number] INTEGER, [NAMES] TEXT NULL, [IPS] TEXTNULL );"; + ExecuteNonQuery(Create); + } + } + + public Aliases getPlayer(int dbIndex) + { + String Query = String.Format("SELECT * FROM ALIASES WHERE Number = '{0}' LIMIT 1", dbIndex); + DataTable Result = GetDataTable(Query); + + if (Result != null && Result.Rows.Count > 0) + { + DataRow p = Result.Rows[0]; + return new Aliases(Convert.ToInt32(p["Number"]), p["NAMES"].ToString(), p["IPS"].ToString()); + } + + else + return null; + } + + public void addPlayer(Aliases Alias) + { + Dictionary newPlayer = new Dictionary(); + + newPlayer.Add("Number", Alias.getNumber()); + newPlayer.Add("NAMES", Alias.getNamesDB()); + newPlayer.Add("IPS", Alias.getIPSDB()); + + Insert("ALIASES", newPlayer); + } + + public void updatePlayer(Aliases Alias) + { + Dictionary updatedPlayer = new Dictionary(); + + updatedPlayer.Add("Number", Alias.getNumber()); + updatedPlayer.Add("NAMES", Alias.getNamesDB()); + updatedPlayer.Add("IPS", Alias.getIPSDB()); + + Update("ALIASES", updatedPlayer, String.Format("Number = '{0}'", Alias.getNumber())); + } } } diff --git a/Admin/Heartbeat.cs b/Admin/Heartbeat.cs index b89fc8703..8fd6e601c 100644 --- a/Admin/Heartbeat.cs +++ b/Admin/Heartbeat.cs @@ -14,7 +14,7 @@ namespace IW4MAdmin public void Send() { - String URI = String.Format("http://raidmax.org/IW4M/Admin/heartbeat.php?address={0}&name={1}&map={2}&players={3}", Instance.getPort().ToString(), Instance.getName(), Instance.getMap(), Instance.getClientNum().ToString() + '/' + Instance.getMaxClients().ToString()); + String URI = String.Format("http://raidmax.org/IW4M/Admin/heartbeat.php?address={0}&name={1}&map={2}&players={3}&version={4}", Instance.getPort().ToString(), Instance.getName(), Instance.getMap(), Instance.getClientNum().ToString() + '/' + Instance.getMaxClients().ToString(), IW4MAdmin.Program.Version.ToString()); Handle.Request(URI); } diff --git a/Admin/IW4M ADMIN.csproj b/Admin/IW4M ADMIN.csproj index c56609be0..d75e9be0a 100644 --- a/Admin/IW4M ADMIN.csproj +++ b/Admin/IW4M ADMIN.csproj @@ -28,7 +28,7 @@ RaidMax LLC true publish.htm - 5 + 6 0.4.0.%2a false true diff --git a/Admin/Main.cs b/Admin/Main.cs index cd014c09e..74a5e4fc6 100644 --- a/Admin/Main.cs +++ b/Admin/Main.cs @@ -10,7 +10,7 @@ namespace IW4MAdmin static String IP; static int Port; static String RCON; - static public double Version = 0.4; + static public double Version = 0.5; static public double latestVersion; static void Main(string[] args) diff --git a/Admin/Player.cs b/Admin/Player.cs index 4f310ef43..3b27d8df7 100644 --- a/Admin/Player.cs +++ b/Admin/Player.cs @@ -6,7 +6,7 @@ namespace IW4MAdmin { class Stats { - public Stats(int K, int D, double kdr, double skill) + public Stats(int K, int D, double kdr, double skill) { Kills = K; Deaths = D; @@ -21,8 +21,7 @@ namespace IW4MAdmin public void updateSkill(double enemySkill) { - Skill = (Math.Round((double)Kills * (((double)Kills / (double)Deaths) / 10), 2)); - Skill = Math.Round(Math.Log(Skill) * (enemySkill / 2) + (Math.Log(Deaths + 1) * 0.3) * 12, 2); + Skill = Math.Round(Math.Log(KDR + 1) * ((enemySkill / 2) + 1) + (Math.Log(Deaths) * 0.3) * 12, 2); } public int Kills; @@ -31,6 +30,55 @@ namespace IW4MAdmin public double Skill; } + class Aliases + { + public Aliases(int Num, String N, String I) + { + Number = Num; + Names = N; + IPS = I; + } + + public List getNames() + { + return new List(Names.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); + } + + public List getIPS() + { + return new List(IPS.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); + } + + public String getIPSDB() + { + return IPS; + } + + public String getNamesDB() + { + return Names; + } + + public int getNumber() + { + return Number; + } + + public void addName(String Name) + { + Names += Names + ';'; + } + + public void addIP(String IP) + { + IPS += IP + ';'; + } + + private String Names; + private String IPS; + private int Number; + } + class Player { public enum Permission @@ -55,7 +103,7 @@ namespace IW4MAdmin Warnings = 0; } - public Player(string n, string id, int num, Player.Permission l, int cind, String lo, int con) + public Player(string n, string id, int num, Player.Permission l, int cind, String lo, int con, String IP2) { Name = n; npID = id; @@ -64,6 +112,7 @@ namespace IW4MAdmin dbID = cind; LastOffense = lo; Connections = con + 1; + IP = IP2; Warnings = 0; } @@ -102,11 +151,21 @@ namespace IW4MAdmin return LastOffense; } + public String getIP() + { + return IP; + } + public void updateName(String n) { Name = n; } + public void updateIP(String I) + { + IP = I; + } + // BECAUSE IT NEEDS TO BE CHANGED! public void setLevel(Player.Permission Perm) { @@ -158,10 +217,12 @@ namespace IW4MAdmin private Player.Permission Level; private int dbID; private int Connections; + private String IP; public Event lastEvent; public String LastOffense; public int Warnings; public Stats stats; + public Aliases Alias; } } diff --git a/Admin/RCON.cs b/Admin/RCON.cs index a0ae6edc1..701a2e98b 100644 --- a/Admin/RCON.cs +++ b/Admin/RCON.cs @@ -22,52 +22,17 @@ namespace IW4MAdmin sv_connection.Client.SendTimeout = 1000; sv_connection.Client.ReceiveTimeout = 1000; Instance = I; - toSend = new Queue(); + toSend = new Queue(); } - //When we don't care about a response - public bool sendRCON(String message) - { - try - { - String STR_REQUEST = String.Format("ÿÿÿÿrcon {0} {1}", Instance.getPassword(), message); - - Byte[] Request_ = Encoding.Unicode.GetBytes(STR_REQUEST); - Byte[] Request = new Byte[Request_.Length / 2]; - - int count = 0; //This is kinda hacky but Unicode -> ASCII doesn't seem to be working correctly for this. - foreach (Byte b in Request_) - { - if (b != 0) - Request[count / 2] = b; - count++; - } - - - System.Net.IPAddress IP = System.Net.IPAddress.Parse(Instance.getIP()); - IPEndPoint endPoint = new IPEndPoint(IP, Instance.getPort()); - - sv_connection.Connect(endPoint); - sv_connection.Send(Request, Request.Length); - } - - catch (SocketException) - { - Instance.Log.Write("Unable to reach server for sending RCON", Log.Level.Debug); - sv_connection.Close(); - return false; - } - - return true; - } //We want to read the reponse public String[] responseSendRCON(String message) { try { - String STR_REQUEST; + String STR_REQUEST = String.Empty; if (message != "getstatus") - STR_REQUEST = String.Format("ÿÿÿÿrcon {0} {1}", Instance.getPassword().Replace("\r", String.Empty), message); + STR_REQUEST = String.Format("ÿÿÿÿrcon {0} {1}", Instance.getPassword(), message); else STR_REQUEST = String.Format("ÿÿÿÿ getstatus"); @@ -88,33 +53,50 @@ namespace IW4MAdmin sv_connection.Connect(endPoint); sv_connection.Send(Request, Request.Length); - - Byte[] receive = sv_connection.Receive(ref endPoint); - int num = int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier); + String incoming = String.Empty; + byte[] bufferRecv = new byte[65536]; + do + { + // loop on receiving the bytes + bufferRecv = sv_connection.Receive(ref endPoint); - String[] response = System.Text.Encoding.UTF8.GetString(receive).Split((char)num); + // only decode the bytes received + incoming += (Encoding.ASCII.GetString(bufferRecv, 0, bufferRecv.Length)); + } while (sv_connection.Available > 0); + + int num = int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier); + + String[] response = incoming.Split(new char[] {(char)num} , StringSplitOptions.RemoveEmptyEntries); if(response[1] == "Invalid password.") { Instance.Log.Write("Invalid RCON password specified", Log.Level.Debug); return null; } - return response; } catch (SocketException) { Instance.Log.Write("Unable to reach server for sending RCON", Log.Level.Debug); + return null; + } + + catch (System.InvalidOperationException) + { + Instance.Log.Write("RCON Connection terminated by server. Uh-OH", Log.Level.Debug); sv_connection.Close(); + sv_connection = new UdpClient(); return null; } } - public void addRCON(String Message, int delay) + public String[] addRCON(String Message) { - toSend.Enqueue(Message); + RCON_Request newReq = new RCON_Request(Message); + toSend.Enqueue(newReq); + return newReq.waitForResponse(); } public void ManageRCONQueue() @@ -123,7 +105,8 @@ namespace IW4MAdmin { if (toSend.Count > 0) { - sendRCON(toSend.Peek()); + RCON_Request Current = toSend.Peek(); + Current.Response = responseSendRCON(Current.Request); toSend.Dequeue(); Utilities.Wait(0.567); } @@ -134,6 +117,31 @@ namespace IW4MAdmin private UdpClient sv_connection; private Server Instance; - private Queue toSend; + private Queue toSend; } + + class RCON_Request + { + public RCON_Request(String IN) + { + Request = IN; + Response = null; + } + + public String[] waitForResponse() + { + DateTime Start = DateTime.Now; + DateTime Current = DateTime.Now; + while (Response == null && (Current-Start).TotalMilliseconds < 1000) + { + Thread.Sleep(1); + Current = DateTime.Now; + } + return Response; + } + + public String Request; + public String[] Response; + } + } diff --git a/Admin/Server.cs b/Admin/Server.cs index 070066f80..399b6904f 100644 --- a/Admin/Server.cs +++ b/Admin/Server.cs @@ -1,6 +1,7 @@  using System; using System.Collections.Generic; +using System.Collections; using System.Text; using System.Threading; //SLEEP using System.IO; @@ -20,19 +21,23 @@ namespace IW4MAdmin clientnum = 0; RCON = new RCON(this); logFile = new file("admin_" + port + ".log", true); +#if DEBUG + Log = new Log(logFile, Log.Level.Debug); +#else Log = new Log(logFile, Log.Level.Production); +#endif players = new List(new Player[18]); - DB = new Database("clients.rm", Database.Type.Clients); - stats = new Database("stats_" + port + ".rm", Database.Type.Stats); - Bans = DB.getBans(); - owner = DB.getOwner(); - maps = new List(); - rules = new List(); - messages = new List(); + clientDB = new ClientsDB("clients.rm"); + statDB = new StatsDB("stats_" + port + ".rm"); + aliasDB = new AliasesDB("aliases.rm"); + Bans = clientDB.getBans(); + owner = clientDB.getOwner(); events = new Queue(); HB = new Heartbeat(this); + Macros = new Dictionary(); nextMessage = 0; initCommands(); + initMacros(); initMessages(); initMaps(); initRules(); @@ -100,41 +105,72 @@ namespace IW4MAdmin { try { - if (DB.getPlayer(P.getID(), P.getClientNum()) == null) - DB.addPlayer(P); - else + if (clientDB.getPlayer(P.getID(), P.getClientNum()) == null) { - //messy way to prevent loss of last event - Player A; - A = DB.getPlayer(P.getID(), P.getClientNum()); - if (A.getName() != P.getName()) - A.updateName(P.getName()); - A.lastEvent = P.lastEvent; - P = A; + clientDB.addPlayer(P); + Player New = clientDB.getPlayer(P.getID(), P.getClientNum()); + statDB.addPlayer(New); + aliasDB.addPlayer(new Aliases(New.getDBID(), New.getName(), New.getIP())); } - P.stats = stats.getStats(P.getDBID()); - if (P.stats == null) - { - stats.addPlayer(P); - P.stats = new Stats(0, 0, 0, 1); - } - if(players[P.getClientNum()] == null) + //messy way to prevent loss of last event + Player NewPlayer = clientDB.getPlayer(P.getID(), P.getClientNum()); + NewPlayer.stats = statDB.getStats(NewPlayer.getDBID()); + if (NewPlayer.stats == null) + { + statDB.addPlayer(NewPlayer); + NewPlayer.stats = statDB.getStats(NewPlayer.getDBID()); + } + NewPlayer.Alias = aliasDB.getPlayer(NewPlayer.getDBID()); + NewPlayer.lastEvent = P.lastEvent; + + if (players[NewPlayer.getClientNum()] == null) + { + try + { + P.updateIP(IPS[P.getID()]); + } + + catch + { + Log.Write("Looks like the connecting player doesn't have an IP location assigned yet.", Log.Level.Debug); + P.updateIP(getPlayerIP(P.getID())); + } + + if (P.getName() != NewPlayer.getName()) + { + NewPlayer.updateName(P.getName()); + NewPlayer.Alias.addName(P.getName()); + } + + if (P.getIP() != NewPlayer.getIP()) + { + NewPlayer.updateIP(P.getIP()); + NewPlayer.Alias.addIP(P.getIP()); + } + clientnum++; + } - players[P.getClientNum()] = null; - players[P.getClientNum()] = P; + NewPlayer.lastEvent = P.lastEvent; - if (P.getLevel() == Player.Permission.Banned) + players[NewPlayer.getClientNum()] = null; + players[NewPlayer.getClientNum()] = NewPlayer; + + Ban B = isBanned(NewPlayer); + if (NewPlayer.getLevel() == Player.Permission.Banned || B != null) { Log.Write("Banned client " + P.getName() + " trying to connect...", Log.Level.Debug); - String Message = "^1Player Kicked: ^7Previously Banned for ^5" + isBanned(P).getReason(); + String Message = "^1Player Kicked: ^7Previously Banned for ^5" + B.getReason(); P.Kick(Message); } else - Log.Write("Client " + P.getName() + " connecting...", Log.Level.Debug); + Log.Write("Client " + NewPlayer.getName() + " connecting...", Log.Level.Debug); + + clientDB.updatePlayer(NewPlayer); + aliasDB.updatePlayer(NewPlayer.Alias); return true; } @@ -148,6 +184,8 @@ namespace IW4MAdmin //Remove player by CLIENT NUMBER public bool removePlayer(int cNum) { + Log.Write("Updating stats for " + players[cNum].getName(), Log.Level.Debug); + statDB.updatePlayer(players[cNum]); Log.Write("Client at " + cNum + " disconnecting...", Log.Level.Debug); players[cNum] = null; clientnum--; @@ -197,18 +235,38 @@ namespace IW4MAdmin public Ban isBanned(Player C) { - if (C.getLevel() == Player.Permission.Banned) - { foreach (Ban B in Bans) { + if (B.getID() == C.getID()) return B; + + if (B.getIP() == null) + continue; + + if (C.Alias.getIPS().Find(f => f.Contains(B.getIP())) != null) + return B; + + if (C.getIP() == B.getIP()) + return B; } - } return null; } + public String getPlayerIP(String GUID) + { + Dictionary dict; + int count = 0; + do + { + //because rcon can be weird + dict = Utilities.IPFromStatus(RCON.addRCON("status")); + count++; + } while(dict.Count < clientnum || count < 5); + return dict[GUID]; + } + public Command processCommand(Event E, Command C) { E.Data = Utilities.removeWords(E.Data, 1); @@ -238,7 +296,7 @@ namespace IW4MAdmin { int dbID = -1; int.TryParse(Args[0].Substring(1, Args[0].Length-1), out dbID); - Player found = E.Owner.DB.findPlayers(dbID); + Player found = E.Owner.clientDB.getPlayer(dbID); if (found != null) E.Target = found; } @@ -282,22 +340,23 @@ namespace IW4MAdmin //Starts the monitoring process public void Monitor() { - if (!intializeBasics()) - { - Log.Write("Stopping " + Port + " due to uncorrectable errors (check log)" + logPath, Log.Level.Production); - Utilities.Wait(10); - return; - } //Handles new rcon requests in a fashionable manner Thread RCONQueue = new Thread(new ThreadStart(RCON.ManageRCONQueue)); RCONQueue.Start(); + if (!intializeBasics()) + { + Log.Write("Stopping " + Port + " due to uncorrectable errors (check log)" + logPath, Log.Level.Production); + Utilities.Wait(10); + return; + } + //Handles new events in a fashionable manner Thread eventQueue = new Thread(new ThreadStart(manageEventQueue)); eventQueue.Start(); - int timeFailed = 0; + int timesFailed = 0; long l_size = -1; String[] lines = new String[8]; String[] oldLines = new String[8]; @@ -313,13 +372,24 @@ namespace IW4MAdmin lastMessage = DateTime.Now - start; if(lastMessage.TotalSeconds > messageTime && messages.Count > 0) { - Broadcast(messages[nextMessage]); + + if (RCON.responseSendRCON("sv_online") == null) + { + timesFailed++; + Log.Write("Server appears to be offline - " + timesFailed, Log.Level.Debug); + } + else + timesFailed = 0; + Thread.Sleep(300); + initMacros(); + Broadcast(Utilities.processMacro(Macros, messages[nextMessage])); if (nextMessage == (messages.Count - 1)) nextMessage = 0; else nextMessage++; start = DateTime.Now; - HB.Send(); + if (timesFailed <= 3) + HB.Send(); } if (l_size != logFile.getSize()) @@ -385,10 +455,10 @@ namespace IW4MAdmin //Vital RCON commands to establish log file and server name. May need to cleanup in the future private bool intializeBasics() { - try + try { - String[] infoResponse = RCON.responseSendRCON("getstatus"); - + String[] infoResponse = RCON.addRCON("getstatus"); + if (infoResponse == null || infoResponse.Length < 2) { Log.Write("Could not get server status!", Log.Level.All); @@ -396,32 +466,40 @@ namespace IW4MAdmin } infoResponse = infoResponse[1].Split('\\'); - mapname = infoResponse[20]; - mapname = maps.Find(m => m.Name.Equals(mapname)).Alias; - hostname = Utilities.stripColors(infoResponse[32]); - IW_Ver = infoResponse[2]; - maxClients = Convert.ToInt32(infoResponse[6]); - Gametype = infoResponse[8]; + Dictionary infoResponseDict = new Dictionary(); - //get _Website - String[] p = RCON.responseSendRCON("_Website"); - - if (p == null) + for (int i = 0; i < infoResponse.Length; i++) { - Log.Write("Could not website name!", Log.Level.All); - return false; + if (i%2 == 0 || infoResponse[i] == String.Empty) + continue; + infoResponseDict.Add(infoResponse[i], infoResponse[i+1]); } - p = p[1].Split('"'); - if (p[0].Trim() != "Unknown command") - Website = (p[3].Substring(0, p[3].Length - 2).Trim()); - p = null; - //END + mapname = infoResponseDict["mapname"]; + try + { + mapname = maps.Find(m => m.Name.Equals(mapname)).Alias; + } - Thread.Sleep(FLOOD_TIMEOUT); + catch(Exception) + { + Log.Write(mapname + " doesn't appear to be in the maps.cfg", Log.Level.Debug); + } - //GET fs_basepath - p = RCON.responseSendRCON("fs_basepath"); + hostname = Utilities.stripColors(infoResponseDict["sv_hostname"]); + IW_Ver = infoResponseDict["shortversion"]; + maxClients = Convert.ToInt32(infoResponseDict["sv_maxclients"]); + Gametype = infoResponseDict["g_gametype"]; + try + { + Website = infoResponseDict["_Website"]; + } + catch (Exception E) + { + Log.Write("Seems not to have website specified", Log.Level.Debug); + } + + String[] p =RCON.addRCON("fs_basepath"); if (p == null) { @@ -432,12 +510,10 @@ namespace IW4MAdmin p = p[1].Split('"'); Basepath = p[3].Substring(0, p[3].Length - 2).Trim(); p = null; - - Thread.Sleep(FLOOD_TIMEOUT); //END //get fs_game - p = RCON.responseSendRCON("fs_game"); + p =RCON.addRCON("fs_game"); if (p == null) { @@ -449,11 +525,10 @@ namespace IW4MAdmin Mod = p[3].Substring(0, p[3].Length - 2).Trim().Replace('/', '\\'); p = null; - Thread.Sleep(FLOOD_TIMEOUT); //END //get g_log - p = RCON.responseSendRCON("g_log"); + p = RCON.addRCON("g_log"); if (p == null) { @@ -472,11 +547,10 @@ namespace IW4MAdmin string log = p[3].Substring(0, p[3].Length - 2).Trim(); p = null; - Thread.Sleep(FLOOD_TIMEOUT); //END //get g_logsync - p = RCON.responseSendRCON("g_logsync"); + p =RCON.addRCON("g_logsync"); if (p == null) { @@ -489,15 +563,12 @@ namespace IW4MAdmin int logsync = Convert.ToInt32(p[3].Substring(0, p[3].Length - 2).Trim()); p = null; - Thread.Sleep(FLOOD_TIMEOUT); if (logsync != 1) - RCON.sendRCON("g_logsync 1"); - - Thread.Sleep(FLOOD_TIMEOUT); + RCON.addRCON("g_logsync 1"); //END //get iw4m_onelog - p = RCON.responseSendRCON("iw4m_onelog"); + p =RCON.addRCON("iw4m_onelog"); if (p[0] == String.Empty || p[1].Length < 15) { @@ -510,8 +581,6 @@ namespace IW4MAdmin p = null; //END - Thread.Sleep(FLOOD_TIMEOUT); - if (Mod == String.Empty || onelog == "1") logPath = Basepath + '\\' + "m2demo" + '\\' + log; else @@ -526,6 +595,24 @@ namespace IW4MAdmin logFile = new file(logPath); Log.Write("Log file is " + logPath, Log.Level.Debug); + //get players ip's + p =RCON.addRCON("status"); + if (p == null) + { + Log.Write("Unable to get initial player list!", Log.Level.Debug); + return false; + } + + IPS = Utilities.IPFromStatus(p); + +#if DEBUG + /* System.Net.FtpWebRequest tmp = (System.Net.FtpWebRequest)System.Net.FtpWebRequest.Create("ftp://raidmax.org/logs/games_old.log"); + tmp.Credentials = new System.Net.NetworkCredential("*", "*"); + System.IO.Stream ftpStream = tmp.GetResponse().GetResponseStream(); + String ftpLog = new StreamReader(ftpStream).ReadToEnd();*/ + logPath = "games_old.log"; +#endif + return true; } catch (Exception E) @@ -544,12 +631,11 @@ namespace IW4MAdmin return true; } - if (E.Type == Event.GType.Disconnect) + if (E.Type == Event.GType.Disconnect && E.Origin.getClientNum() > 0) { - if (getNumPlayers() > 0 && E.Origin != null) + if (getNumPlayers() > 0 && E.Origin != null && players[E.Origin.getClientNum()] != null) { - DB.updatePlayer(E.Origin); - stats.updatePlayer(E.Origin); + clientDB.updatePlayer(E.Origin); removePlayer(E.Origin.getClientNum()); } return true; @@ -565,7 +651,7 @@ namespace IW4MAdmin E.Target.stats.Deaths++; E.Target.stats.updateKDR(); - E.Target.stats.updateSkill(E.Origin.stats.Skill); + //E.Target.stats.updateSkill(E.Origin.stats.Skill); } } @@ -616,7 +702,7 @@ namespace IW4MAdmin { if (P == null || P.stats == null) continue; - stats.updatePlayer(P); + statDB.updatePlayer(P); Log.Write("Updated stats for client " + P.getDBID(), Log.Level.Debug); } return true; @@ -625,32 +711,54 @@ namespace IW4MAdmin return false; } + public bool Reload() + { + try + { + messages = null; + maps = null; + rules = null; + initMaps(); + initMessages(); + initRules(); + return true; + } + catch (Exception E) + { + Log.Write("Unable to reload configs! - " + E.Message, Log.Level.Debug); + messages = new List(); + maps = new List(); + rules = new List(); + return false; + } + } + //THESE MAY NEED TO BE MOVED public void Broadcast(String Message) { - RCON.addRCON("sayraw " + Message, 0); + RCON.addRCON("sayraw " + Message); } public void Tell(String Message, Player Target) { - RCON.addRCON("tell " + Target.getClientNum() + " " + Message + "^7", 0); + RCON.addRCON("tell " + Target.getClientNum() + " " + Message + "^7"); } public void Kick(String Message, Player Target) { - RCON.addRCON("clientkick " + Target.getClientNum() + " \"" + Message + "^7\"", 0); + RCON.addRCON("clientkick " + Target.getClientNum() + " \"" + Message + "^7\""); } public void Ban(String Message, Player Target, Player Origin) { - RCON.addRCON("tempbanclient " + Target.getClientNum() + " \"" + Message + "^7\"", 0); + RCON.addRCON("tempbanclient " + Target.getClientNum() + " \"" + Message + "^7\""); if (Origin != null) { Target.setLevel(Player.Permission.Banned); - Ban newBan = new Ban(Target.getLastO(), Target.getID(), Origin.getID()); + Ban newBan = new Ban(Target.getLastO(), Target.getID(), Origin.getID(), DateTime.Now, Target.getIP()); Bans.Add(newBan); - DB.addBan(newBan); - DB.updatePlayer(Target); + clientDB.addBan(newBan); + clientDB.updatePlayer(Target); } } @@ -660,11 +768,11 @@ namespace IW4MAdmin { if (B.getID() == Target.getID()) { - DB.removeBan(GUID); + clientDB.removeBan(GUID); Bans.Remove(B); - Player P = DB.getPlayer(Target.getID(), 0); + Player P = clientDB.getPlayer(Target.getID(), 0); P.setLevel(Player.Permission.User); - DB.updatePlayer(P); + clientDB.updatePlayer(P); return true; } } @@ -675,29 +783,36 @@ namespace IW4MAdmin public void fastRestart(int delay) { Utilities.Wait(delay); - RCON.addRCON("fast_restart", 0); + RCON.addRCON("fast_restart"); } public void mapRotate(int delay) { Utilities.Wait(delay); - RCON.addRCON("map_rotate", 0); + RCON.addRCON("map_rotate"); } public void tempBan(String Message, Player Target) { - RCON.addRCON("tempbanclient " + Target.getClientNum() + " \"" + Message + "\"", 0); + RCON.addRCON("tempbanclient " + Target.getClientNum() + " \"" + Message + "\""); } public void mapRotate() { - RCON.addRCON("map_rotate", 0); + RCON.addRCON("map_rotate"); } public void Map(String map) { - RCON.addRCON("map " + map, 0); + RCON.addRCON("map " + map); } + + public String Wisdom() + { + String Quote = new Connection("http://www.iheartquotes.com/api/v1/random?max_lines=1&max_characters=200").Read(); + return Utilities.removeNastyChars(Quote); + } + //END //THIS IS BAD BECAUSE WE DON"T WANT EVERYONE TO HAVE ACCESS :/ @@ -706,10 +821,20 @@ namespace IW4MAdmin return rcon_pass; } + private void initMacros() + { + Macros = new Dictionary(); + Macros.Add("WISDOM", Wisdom()); + Macros.Add("TOTALPLAYERS", clientDB.totalPlayers()); + } + private void initMaps() { + maps = new List(); + file mapfile = new file("config\\maps.cfg"); String[] _maps = mapfile.readAll(); + mapfile.Close(); if (_maps.Length > 2) // readAll returns minimum one empty string { foreach (String m in _maps) @@ -728,8 +853,11 @@ namespace IW4MAdmin private void initMessages() { + messages = new List(); + file messageCFG = new file("config\\messages.cfg"); String[] lines = messageCFG.readAll(); + messageCFG.Close(); if (lines.Length < 2) //readAll returns minimum one empty string { @@ -756,8 +884,11 @@ namespace IW4MAdmin private void initRules() { + rules = new List(); + file ruleFile = new file("config\\rules.cfg"); String[] _rules = ruleFile.readAll(); + ruleFile.Close(); if (_rules.Length > 2) // readAll returns minimum one empty string { foreach (String r in _rules) @@ -793,15 +924,16 @@ namespace IW4MAdmin commands.Add(new Uptime("uptime", "get current application running time. syntax: !uptime.", "up", Player.Permission.Moderator, 0, false)); commands.Add(new Warn("warn", "warn player for infringing rules syntax: !warn .", "w", Player.Permission.Moderator, 2, true)); commands.Add(new WarnClear("warnclear", "remove all warning for a player syntax: !warnclear .", "wc", Player.Permission.Administrator, 1, true)); - commands.Add(new Unban("unban", "unban player by guid. syntax: !unban .", "ub", Player.Permission.Administrator, 1, true)); + commands.Add(new Unban("unban", "unban player by guid. syntax: !unban .", "ub", Player.Permission.SeniorAdmin, 1, true)); commands.Add(new Admins("admins", "list currently connected admins. syntax: !admins.", "a", Player.Permission.User, 0, false)); commands.Add(new Wisdom("wisdom", "get a random wisdom quote. syntax: !wisdom", "w", Player.Permission.Administrator, 0, false)); commands.Add(new MapCMD("map", "change to specified map. syntax: !map", "m", Player.Permission.Administrator, 1, false)); - commands.Add(new Find("find", "find player in database. syntax: !find ", "f", Player.Permission.Administrator, 1, false)); + commands.Add(new Find("find", "find player in database. syntax: !find ", "f", Player.Permission.SeniorAdmin, 1, false)); commands.Add(new Rules("rules", "list server rules. syntax: !rules", "r", Player.Permission.User, 0, false)); commands.Add(new PrivateMessage("privatemessage", "send message to other player. syntax: !pm ", "pm", Player.Permission.User, 2, true)); commands.Add(new _Stats("stats", "view your stats or another player's. syntax: !stats", "xlrstats", Player.Permission.User, 0, true)); - commands.Add(new TopStats("topstats", "view the top 4 players on this server. syntax !topstats", "xlrtopstats", Player.Permission.User, 0, false)); + commands.Add(new TopStats("topstats", "view the top 4 players on this server. syntax: !topstats", "xlrtopstats", Player.Permission.User, 0, false)); + commands.Add(new Reload("reload", "reload configurations. syntax: !reload", "reload", Player.Permission.Owner, 0, false)); /* commands.Add(new commands { command = "stats", desc = "view your server stats.", requiredPer = 0 }); commands.Add(new commands { command = "speed", desc = "change player speed. syntax: !speed ", requiredPer = 3 }); @@ -813,13 +945,14 @@ namespace IW4MAdmin //Objects public Log Log; public RCON RCON; - public Database DB; + public ClientsDB clientDB; + public AliasesDB aliasDB; + public StatsDB statDB; public List Bans; public Player owner; public List maps; public List rules; public Queue events; - public Database stats; public Heartbeat HB; public String Website; public String Gametype; @@ -840,6 +973,11 @@ namespace IW4MAdmin private int errors = 0; private String IW_Ver; private int maxClients; + private Dictionary Macros; + + + //Will probably move this later + private Dictionary IPS; //Log stuff diff --git a/Admin/Utilities.cs b/Admin/Utilities.cs index 52f9f7037..b954826b7 100644 --- a/Admin/Utilities.cs +++ b/Admin/Utilities.cs @@ -57,7 +57,7 @@ namespace IW4MAdmin public static String removeNastyChars(String str) { - return str.Replace("`", "").Replace("'", "").Replace("\\", "").Replace("\"", "").Replace("^", "").Replace(""", "''"); + return str.Replace("`", "").Replace("\\", "").Replace("\"", "").Replace("^", "").Replace(""", "''").Replace("&", "&").Replace("\"", "''"); } public static int GetLineNumber(Exception ex) @@ -86,17 +86,60 @@ namespace IW4MAdmin { case Player.Permission.Banned: return "^1" + Player.Permission.Banned; - break; case Player.Permission.Owner: return "^5" + Player.Permission.Owner; - break; case Player.Permission.User: return "^3" + Player.Permission.User; - break; default: return "^2" + level; - break; } } + + public static String processMacro(Dictionary Dict, String str) + { + MatchCollection Found = Regex.Matches(str, @"\{\{[A-Z]+\}\}", RegexOptions.IgnoreCase); + foreach (Match M in Found) + { + String Match = M.Value; + String Identifier = M.Value.Substring(2, M.Length - 4); + String Replacement = Dict[Identifier].ToString(); + str = str.Replace(Match, Replacement); + } + + return str; + } + + public static Dictionary IPFromStatus(String[] players) + { + Dictionary Dict = new Dictionary(); + + foreach (String S in players) + { + String S2 = S.Trim(); + if (S.Length < 50) + continue; + if (Regex.Matches(S2, @"\d+$", RegexOptions.IgnoreCase).Count > 0) + { + String[] eachPlayer = S2.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 3; i < eachPlayer.Length; i++ ) + { + if (eachPlayer[i].Split(':').Length > 1) + { + Dict.Add(eachPlayer[3], eachPlayer[i].Split(':')[0]); + break; + } + } + + } + } + return Dict; + + } + + public static String DateTimeSQLite(DateTime datetime) + { + string dateTimeFormat = "{0}-{1}-{2} {3}:{4}:{5}.{6}"; + return string.Format(dateTimeFormat, datetime.Year, datetime.Month, datetime.Day, datetime.Hour, datetime.Minute, datetime.Second, datetime.Millisecond); + } } } diff --git a/Admin/config/messages.cfg b/Admin/config/messages.cfg index c7a6c5b60..19085cbb8 100644 --- a/Admin/config/messages.cfg +++ b/Admin/config/messages.cfg @@ -1,5 +1,5 @@ 60 -This server uses ^5IW4M Admin v0.2 ^7get it at ^5raidmax.org +This server uses ^5IW4M Admin v0.5 ^7get it at ^5raidmax.org ^5IW4M Admin ^7sees ^5YOU! Cheaters are ^1unwelcome ^7 on this server Did you know 8/10 people agree with unverified statistics? \ No newline at end of file diff --git a/Admin/version.txt b/Admin/version.txt index 5cce9b370..7d837fb93 100644 --- a/Admin/version.txt +++ b/Admin/version.txt @@ -1 +1,11 @@ -0.4 \ No newline at end of file +VERSION: 0.5 +CHANGELOG: +-close config files after reading oops +-added reload command +-added macros! (Denoted by {{MACRO}} in server config right now only {{WISDOM}} and {{TOTALPLAYERS}}) +-added IP's (tracks and rebans new accounts on same banned ip)! +-aliases +-reworked database classes +-heartbeat gives running version +-player banned in find gives last ban reason +-reworked rcon yet again diff --git a/GAME/Game_TEST/Game_TEST.sln b/GAME/Game_TEST/Game_TEST.sln deleted file mode 100644 index f2a42128f..000000000 --- a/GAME/Game_TEST/Game_TEST.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Game_TEST", "Game_TEST\Game_TEST.csproj", "{119DA221-3EA7-432C-AAFC-782D94EA1ECF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x86 = Debug|x86 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {119DA221-3EA7-432C-AAFC-782D94EA1ECF}.Debug|x86.ActiveCfg = Debug|x86 - {119DA221-3EA7-432C-AAFC-782D94EA1ECF}.Debug|x86.Build.0 = Debug|x86 - {119DA221-3EA7-432C-AAFC-782D94EA1ECF}.Release|x86.ActiveCfg = Release|x86 - {119DA221-3EA7-432C-AAFC-782D94EA1ECF}.Release|x86.Build.0 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/GAME/Game_TEST/Game_TEST/Game1.cs b/GAME/Game_TEST/Game_TEST/Game1.cs deleted file mode 100644 index 6d6fc5aca..000000000 --- a/GAME/Game_TEST/Game_TEST/Game1.cs +++ /dev/null @@ -1,91 +0,0 @@ -#region Using Statements -using System; -using System.Collections.Generic; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Content; -using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; -using Microsoft.Xna.Framework.Storage; -using Microsoft.Xna.Framework.GamerServices; -#endregion - -namespace Game_TEST -{ - /// - /// This is the main type for your game - /// - public class Game1 : Game - { - GraphicsDeviceManager graphics; - SpriteBatch spriteBatch; - - public Game1() - : base() - { - graphics = new GraphicsDeviceManager(this); - Content.RootDirectory = "Content"; - } - - /// - /// Allows the game to perform any initialization it needs to before starting to run. - /// This is where it can query for any required services and load any non-graphic - /// related content. Calling base.Initialize will enumerate through any components - /// and initialize them as well. - /// - protected override void Initialize() - { - // TODO: Add your initialization logic here - - base.Initialize(); - } - - /// - /// LoadContent will be called once per game and is the place to load - /// all of your content. - /// - protected override void LoadContent() - { - // Create a new SpriteBatch, which can be used to draw textures. - spriteBatch = new SpriteBatch(GraphicsDevice); - - // TODO: use this.Content to load your game content here - } - - /// - /// UnloadContent will be called once per game and is the place to unload - /// all content. - /// - protected override void UnloadContent() - { - // TODO: Unload any non ContentManager content here - } - - /// - /// Allows the game to run logic such as updating the world, - /// checking for collisions, gathering input, and playing audio. - /// - /// Provides a snapshot of timing values. - protected override void Update(GameTime gameTime) - { - if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) - Exit(); - - // TODO: Add your update logic here - - base.Update(gameTime); - } - - /// - /// This is called when the game should draw itself. - /// - /// Provides a snapshot of timing values. - protected override void Draw(GameTime gameTime) - { - GraphicsDevice.Clear(Color.CornflowerBlue); - - // TODO: Add your drawing code here - - base.Draw(gameTime); - } - } -} diff --git a/GAME/Game_TEST/Game_TEST/Game_TEST.csproj b/GAME/Game_TEST/Game_TEST/Game_TEST.csproj deleted file mode 100644 index 7ce64010f..000000000 --- a/GAME/Game_TEST/Game_TEST/Game_TEST.csproj +++ /dev/null @@ -1,63 +0,0 @@ - - - - Debug - x86 - 8.0.30703 - 2.0 - {119DA221-3EA7-432C-AAFC-782D94EA1ECF} - WinExe - Properties - Game_TEST - Game_TEST - 512 - - - x86 - true - full - false - bin\Windows\Debug\ - DEBUG;TRACE;WINDOWS - prompt - 4 - - - x86 - pdbonly - true - bin\Windows\Release\ - TRACE;WINDOWS - prompt - 4 - - - Icon.ico - - - - - - - - - $(MSBuildExtensionsPath)\..\MonoGame\v3.0\Assemblies\Windows\MonoGame.Framework.dll - - - - - - - - - - - - - \ No newline at end of file diff --git a/GAME/Game_TEST/Game_TEST/Icon.ico b/GAME/Game_TEST/Game_TEST/Icon.ico deleted file mode 100644 index 13be62a66e7cdf22ce7df765b577a5b914392e28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32038 zcmeI433!#&wfFa#ax#*M5CVh{LVysFNd!b@C^Cu2Bm#yR5riTlPKXpKDn+HrrPNxa zsEAAgG7|zs#sFa+MC4MWTx$`LQcB}teZTeQyx}Ay0j=M?x6f^!=bt^CJ*>6%wD#J2 zzs5L>+eAef{;HTq-NyV(eO2TmxVm-y;Z$RK#TnDGWq!D3Yh$k8V@yqgT0|A# zu>O;Vd`16UE?1b-=}ZD~BH~aO6Xr1{BGL{Qb&Vf+-907w@FO=b}^wnmKzfpYl7-O zZlaz#X5v;7zgtY)8ZpkKuKxU;sDBpuyBJ4gl1cr|HAnhJG4=LJP^}NdQRVrortVHL z^>c3^<#%GDJHD;?$*cTN18q#~J>$c>?U9V%E|d0SUX;P_t(W?bK}(mzH_&pqm=3$d z-0}P_fBoIaJDd9Fn!2MFHkx@t>STQ*^%mwzk4^t6jb1(?X3%jzj)@s?DBs_BQjWj= zCgjbgj8~kEpI*~<^Le>H>#RJy_NX*|X|KeMqwbNP-HiGp7WnH=C;ywEze(#izs#UL zQv0cWa>s~Gk}`6ulo^sMj)Bxa@E9)j4?HTS`+DTq_#J=!U8s8!Xk;ATcoR{5u&Mdj zLQ{YGRY#jorQCx%r1iK=x%Z9Z(*E5qq|>q;i?+rVmnEz>w+ z8knl{^Gxl1;shOkdrU$${8hr2oRQ8uFH6{+JE(uuNDBTU3;KEe{b?n2lMUxRvC__o2D( zzfDn)_8p*Y`a14Z+zq&U!Catew%}UrXVXkv4S)PAxSjYt6}J^g0Zo9)sVVRRjZd=k z+AeY07R^Ax__Tj3$u&mgpMlOZRX|H%&oeJta~BZ-e4OxtVthn7@UU_R>f zGM_eOUZ!u~aTYqd2mWsRse2;z>R8pjq=>bDQtq&}Ssbgm7-?*&#rf$vBYQun;6#oU&@_hwXni}I4rn6d+}nbZ|xY88_vFkjN| z#dvDcc3~+u_%mAF`_z2}oWg$+bk+W49AzFislO7_WWTs;zb}q-*0{Ujqe-r~?wBKG z@0u&_^jX&Lnx<4#c}`5Vm&K&M>O+U@e;2iKp!z z>G0@e=``q3sao$I35lyN_YMBJOn85#w8?l@Ty3bkJ?m$&U`@J%dK-O^|NA{lX}759 zf#b*FaD-6jc%bvV&f_lQt(IUace~=~aa7WWZ;{ET^W?Ej$K{or(=z;{EO}Mq-L{^Sa(&0jqdUHoYV&r<@Ue{^$t3wiS-=!T1eJ zDyW}2J=CXb(lq3tI?oQmlZ>a#P)GaUO1073r1!zUOH}rC>Adc9X}H%XH8TG!&+a)V z9k*YQQH#;122uC06U6}iqEI~Ho~xM)%D!&PUtQ|dwN>jK4!*)a!URQ+bv?M)H}Jh& zX|U(Il*zg(qc?vh4OaiBwBPWBJo@`BGHCvOxqn8sxE@0fx-CFs=XZR@6*|(Ey}$o8 zq~6oON8Onq&ICn`adw<56W1M-zDK^7)_eXg&#uUpkxO^WOIZgbX83M#k2pcylv$h} zR#M#EKlhoS*d~GXyWQ?c>P`Ww^Cc1vfyVx()oT(mXus5XDqF^Vd{~-1xn4Ryy+j&} zSS3LN4~xr^t;+Z|FqAYy=qDALo-x4{3-oWLgF00&xf+1p#vRwtr1s7;J-3Ua|1k-A znELu15f5(q@XgYC>|$v-ZmHB7xk)1W?h)t1$HYZC(+@oo*Xf7)O<%^x{j__dm&7D> z%`@&Y(*pTN-jk?%9nkh43R3guA8&8t44rS{?m1)9M&+62@1bw!h@%^{blxeBc3G0x zYpXPQbd~(<@sFhW$fZ&ike++Klen#U68!O>q|E$7Qen+sq}t}oGXCh_B&q4<{5-W5 zvVKPumA?nRb=_|d8i7LVzui{GQFf-O{E?XItTO=!s;Eby>$c-5`(dxhP5V_euCCUrY45Z>7SZXJlCJC8^(Ky*RD(=m9}3&!Z0&>}i?v zsM6BEj#01f7cLUcujnK8yjETj<;O%2f+1cn_>x`+y{v9|XHt^eg<<*$*4AQ0> z6Bkk(Pg*|pM}yhat82hmTwRk?C!Yb9;Gcq<;7_Y_rLLE%CyoRofTcg+CbD#Q!W}~z zEkn!d0939+fv)e9i7&Q)Yx(~9AdPhA!D*nnx%&5lXz&iWL>UW#x*FdU_ZqIQBd-Hp z$5od*0klskonM2;!F=Kd7nN&ZnBTQMR3AtO&A>e3e7OB_58*EI)2Z=x{dvSUBCK*b z0knSA<)#DW`yyBcGQrG}+QKhmmHAoHg#eZ17ThhkeSoeN%J%}|)Xl|J{(WE_I7Rp( z?tGwg+9bj{uWS8U)*&!4kl&(y(rFthKkamU;`Rj@#Cd_X!9<`kQhBOulYsV-{$K`Z z0S1zGDsCKQcyR{=QtQ{cpfege&jFn)ZRFwq z3M7?8Pd@EY*qtJBj7dX>2;G9;S`SOgQ(Am}dF5G;V;i6o%wU~9MtKX-FG^e&wLIvL z06T!rhdCe%x)m$&7t)#S`u()GpdRePBhXW@F+1*uhiLSJ@M85mJ8w}pzpR94=Bn-^`-CCV7;H4&^FjTL~-!8g)He1-;90)gDLNiv03|RWC({GYRMT zy|&hC=M8jK#?XENSJ(9spsyc2iMy%)L0dd^?lRs-eI}B9MgR(KpZ+Vvs>=p9!7eGi5mYh@^~y^0Zva_wi_7(?TxGIHB*Vcl7!7HiTz(8 zXdH+h4G-Z5thRDgVqS_E3*UQ9P`xWAdV()MKfD*pTeK_V&vrY<&zf<8xHY_4VYn(`BUrsh5|Y3$n+HT|yb*KkehjeH2XMZ!bG zSrgtS&m{hb&s5LOPj96O$ZLlSmEr$AWewxuZecE+QUd)6(5iam3EY9W-4yt{Kz~91 zH;zi(Ofd6gUHWK!=;w^j*?@h&`s?{&o%?ZS)d1V3?x*7)g~z4*jx6=JdbbJt{4jZz zn2472v7zk8%H|qJ+)Z;SbZY-U1$6Ge=7;trT@$P|$?Ct3&<&<4vcDJmLem2hRDTis z*I}HQtrbTj5R366@8;7To}N}U$cK4Z9ydgE%2uC?6~B+EwqK#8qbTVI!288 zvBQ|Qv|D5Y)`Upbh{E*iSkO4evF;5cp*d3h_876~ca7aDm_La2V`JGIGhx9&cB={lhbv>~Y-U^*L z%eaR=-wS>N(`vXH_6bk9o6cFFQ}Dgx3|g@$Nwg28S5k{13RbM z97_XsOw)0hKMnL2OeQDI78ee+&U(*cD!*0Uoa1E7ti2h(r)59 z8F2i2sl4O5gl7F+dac+m-45nS+?;Glc^lrgd?xJ=Vo#sDO~xO-BsB&@|4{77gW;hx zSa${Bv7|Jqy;uk0hTDC@?)T7H59r#9?xgy#Wp8?0<3M{8T{q1{R=eyRoGTF-J7qlf z_NK^QZT~TAFUaUEpG&j-K8fG%lZKyuBQI?~AtO&-x9%mpwBwXi8?s)4M_`K|4i7&H z@I8$7sMrkpqq{4OHV^8D?iA=$yNLGvX86_KasX&QYGk7CY-r-@T=tAOD$OUXmeHT( zNj3USIP_Ot{<%D}?F*^5@^h*7=@sd=`x|*~*;bjh?UYR3a#DV^^{B)R+9W}0x3|s` z;Gw{^{ra2jSGTC2@KEN{=+vvm6N$clgMQ@$8g2bk<$VEYKgt8!oKAPFiLTwqQLgS~ zcgAk{<%az-;>2}H#Oa)}$hOUez~DP@Q4 z5XWQmiILDy=AfB|_ltru4ohtB zEU7VMwdIfMyGuGgvqFabdYRN8wMruU?-Va*I?h2S#Lax-)U!P6J`g-v_k(WblAgTQ zU8XLJIa^6!?G2g1`Rz^iDrBv4KMS<~3tv_Ku;_P=! zyukE5Dh?3bhqa;mHmN^+h4gst4>EAtAEfR0#gaCN`KQNjaj^!u`@%c(x~A8Bc3v-W zPAjbCfnMl)?H8d_y}~&$dPIX?pbIt3Gmg+_?0#X(A9}6vi>r3Q1whv_h1yuHeneNQ z4G-@aN61MNQt6sWdeCPYzv(j_X_E)xr#rN87VPeRSUf#wqx)5F$&{)+*Gtn8%g{02 z1M{WD=*3dKZ>B_a%8{UMN5sWBw70`FBebRaNchZ0FxAM{%pS-wozl7cMsW=MqQ%=%I#z5Rndl=}}{;%tm z_J13?|J8LMDX^`I`t$iHQ;)sHX_NdadOGV&BXnKMRewGlxG}Z2$Pql+17Cz5_ma_4s#Ev*{WMx(69(8F#ZkifV`aWBO>F zfplmXI-5YR>T6p9P@?_E&TpKt?B~PhnzDm^rZ)RM?*A1BFY)y|tdi%BT$FmduSw|E z>*Cl9HvL0fZ)}qAb(bZ4^FJhd!)3V#-DJw)Zzc6^=C-=XLd&eRh4b~Q>|ctvqbLvb zBh^EbK??o|;tKkiO>a@xIMd>cque1=@jWpq%o!yhGMH*mE~`uwWYcyF(Gm!G%j z*E3OP*7xFCa7sMWmP-=t((8y%#x2|@p_Q`4QH?UIA(sm52^_I|jU(x%{iD)^?Bl^K zXjePRDg5(5a#4DVhK(bQJ6sjHEBhgJu!pRGO`#aLD&|N+Eyn7G&!pR-e@KOSd&Ikd zJ1(1j;@x;vJi}j>=m&;M=R-c3xci*cp}pXg)ivzhSHDY8R*u8Q0s`a(_jgqnPWkuQ>+c&O!HD&Yd*)aPJg% z$W953+9Ht&tEEDvrc5N~+4IK7$V=Ps}7Df&BhMHBlqWlv)$7TfQYHf%g8 z(5d?WY5GeOP!InBkgxyWf_~)*mRYC6=3pbs#jcx4_`=*Le( z+tcC-8Yw}c1H=>jkk8@59%&l#Tg)}iv`=D(<}5aLDE=hmUT7UHDnsa0{cR1d>ikNx zLj9^kTX%Abwn5QwJ`Y~nE|Kw&h#QWpVRy7Km0Ms#ew=%rGr@fL#HO!Mf6y#!y%UW| z8i;+f4QKc%j8WP%hP*D9)>B+vMR|aJZ3CVAE&B26{pEU^jyP+NhO2AyR4|U;8qrRL zu&*36CYLdwFYK!msW%Kz}ioK8s&m0vL1n&c-@0v}&|MzUtEB(rk(v%Id0b~&}rrGo{8mEIE|1^*VH>FCSG1ImqR^yAm` z+XCs=Fo*zM`Q4H~errF1t6`Oy>NN`#q#2>*7e{Z=IMV96a29C2norxKJJ7zN<3#1F zZL4j)99QMJ3?$)i0WRS$B!9xkKr(m+X#SHR6=)x|9Jq94Z&x;~lcY zxT-U%oRmLJtL-x$zxI7idz1a7INCLiKb6B?pmeA%Vd-=Dwfy#=DX_*lu9xV+xLV&K zFdJx_sN8DfwgK7~<`GtT?!vFXYZ>Fg4&qkfTI)Toj_C=wn(heDa=PH&R^L(j{qk1c zRi-Kn-A}dvj{}w8MKI3h-=AK`hu&>r{x;0x7OXkk?`K`G`k{^`i+2t4r+vPVeqg7g zym-`T`wGx+Z6V$?%f+zlFxz zp+oyWh|spww!NJ^|4vi-T@~cWpT3FuxBEXK2fe4&61)iPds>D!twsO--H%J}$N!h) zL*KC1!<*b6BL^){^V#l?DdUIa38eprERMtuvo zhrr!8*F5S=q`ufUvGd$=+-ceH*9e@2SKY6aY7Ysd%l7Bf7u^lHUofr?+#Jl-~H{Zef}%_eX$LsgESk&m8#RzuI%qZ4;WV?p9$xzs|@t$J02Sm z_=7ozG3Bnp{|v@xp*@u?N88QkIf_1>$X=~NEAq^tU!9_V@gA-x_bPwmGZEaYwCX8R zm-T)K`8#SNgQ)rWWtBW{`S~9R?^l41eE~Z6solUvvEx6#>`>Y>inrU?L!+>Xm*uRW zEHvE&;`N=iDGOpACoglw4Z8Bn@~^>;#3W=joc7M7pJP9+1J9h3MStSUFP}4no5~NU zGmi6-ka5%#$@nN=Bd{J@#>l?J9i;npeytYeY4XIo;|d6JbgSejXR~2;CU}+cUMeMbI#XZ=APgh|Cvd^*-0_y zF$L!;N=Mm+*w~Yq3oDP{oz*SZEqK>HpzVDGEC*_HvT@TJoP65jn$h-WOqt<6Q-wZW zg?g(X`(nXw$^2Fs83gNms5bO)+NLu15Go>zkhz44 z?4os&Hi@|~a%_5X0T_mV7VaWuK|VTlYITm$gt*-rI?%jOv^#h3UPpbaaqN=G>r+ zTkt;;zOTZ+&U>eDw}4FiiYzeA?lTtePWr5gdB=p+5mT4B*8jFvT~~u$;%vw_YwqU0 zFfcZ(G|+!nj(Ff> zy)^F9Q&Q^REoEZMh&RL|y~aig~r z6^$e8oJk+g9j)Wm_XWJ24@rYTi)7x}%QEZS_wvf&ujJAHcwb&vv|i@_@v=(Yz+;{(GYxqUY;*#6tMg`A^Rvbk4K(ajvs837ms}-jTdvr?VgU3`0!{My>hn<;f{COvXfGC z(Ghw4^E?^&7Iz#T;JXmqH8gr}@rU4N3%J8ayIXnSwNPGvxWiJP2~+-4isj!w_PfBN zwf~3r)8KuOO`PtDR1Qnv-(x~kL%6Rv+mven@B75f-OrF-yJh(AH^Wz+q;vl`_S5U) z&AcMR7H^Z`NB=I>mY$T*c6}su<|=u7^*$N&*>$P0_?T3f_>PP|l_wc9*NM0HA#vUI zJyK+(cTM?@hz0+5>2AJ<>QC44Lv(>kmrUr*{a@RZKM$A)^j!2J&^@EB2Nl6G;&nfC z62x=oH`2I5mYL+P++pSZ;*b-Pnz2G&*ngh8`PZc)cdzyScp2``J)Lz#dK~ph^7em7 z?2121owa|IC)VtdQJ-IzX1U)>qpUy6n4JHz_z&idflI%+QULDA>fQe0FvOp32>e;U zmypj&_Zjz9f6tm2f055Pp{p03|NQ)W;cce!3BS2O_vh?utbOrVjR)r$BPsA7Z`|Qo zrv7N3`_bbPoRK5_W@X91)aiu)-C_MjUpu7<$#Wa5j+X%LR@RYRT_-BsS zww?ly+Rrb6c7#o2az%oE?r2do#}U;0>r zX?N?}K-L=q+UzHBk>$wG(J$FI8|O(=CMCWo|Mt2Ee;t4|{^3{kK;=6ZSo0viEo3k5 zGEub?;XlXK>_wk@*fH+Cu8?V`uX4}zx>SOHeftr;_RsR<+FW^wG0}SdR=&Y>Mrur+ zE6<(aTQ@JfE479!l6sHvU80do`0ma}5;1s_I7ZPJ0rbQ)Y9QxyFbU67twd3E^8%p?CqKts?S=sb{Q^KPYyt`p7tztYs`W4%4{4mu!Z zAKoObo?0rSmh6@3toQGn&yyEd`AK%Diy$CODAV=pn!l*fKE0G*9B z$UE#a@jh}yLVIS)T|<}3n0GeFU5smuOYFB^hRys~M$TEy`;oQc?Xz2g9^oC#(328O zK9{ahx{g_{`hNoGJ2%#4^9cGu`6l%JxaGzh&sjm?wJ%T(v~?G77F-0n1_VOm7FzA^ z;0+~iXpcP--0zST7tC)V5AK$RLs!V)*FKg( zugsUu6F!iX!KhUhQd(J4+pepK4fe3ULF6s?peTZ?f)$qxFUPg`24Kgb&;-oThP8zimwN@+c6sr3290vR!5o(z0x zp0s*=kyPuu*7|00NS9m*Wsm3T$@lU=P-oto@SaD%Q|IP4r@q_mevo%T+r-fs{<|^X z_oN^6qAk=VkLgBT(RW}2@V;q0`W=tL<3Pjko&s`!>bZ?TY#?-g2<$J$&0XT)6xPCo zro8tV!}lloc0$-`_Jvo_5%Nssp0vSRKGO>ORwu?GZ==oq$l<a4&SrD4rn}Y8h3?4{M-Ejz6Zmv z+W)eF&K0`e1>z>%LG#*u!g8JE6HG{r_QqXq3UAuh(J#*OekjkxHpdp-A3MyGyf=9n zJJ))2a_DLgotDequf58EKGK2rYP{FKx5K+KE$fiHcKYw~i_gB3G1;HXunk9L!pUng zfj#zfXRgch=+~ncXUSVf{v^{@?~*#bmWf+!M}@v?O1RZl?CEKom+ASiev|!1`>S91 zgy(Vauj{?aU>v-3_>u=Iz-}iD4-xO`myQ;R? ze?h0dAj8>DzVYSvGU?;J5_vaoAqtgGJq=g#je@F-|0?|h`>^d-y7}t{bnj>De(DC| zw$A_U{*W)%m~`ypIVS8LpQ*EgZ$QFlV{E*Q8KcF($v1Jmsr#hI6AR?|oO9B42XC!* z@_u^bB?8LptCG0oZ<4y?l%(>##wN&PDE5|_M=nU+<{wLVV|cdf zX@b8x-`8-*aW0$O)NUi2cbi`LR{eJtJge@11!zCey}#`id;S+#PN3i7-w{6FxD)bB z+9v_u$6zk9zo}8gb@L64(B$?BHLm_czhl5b6)cZ=gf=J;J-Y%x@$# z+b0g*g6VrO!@D-;>WdPT`FHVd`n!Z6nyB@Ldr(=v{hU6TJ{HQwd)9 zg?N;29S0k)iRXip;;7z0%5)vd_ZGgF5!jJm+;C8m>!5QdYF%27)`vfaHP{4w&E244 z$G%NBG^@R1GSE3sbs*bS{{iGs`&RM`D2p|vY6^4TWfS&1@1vn3j`PeC0j+U>^L%$g zj>J~_P@ei|w>)vmC+%|oA@R@sPNLu3B;_~%&9bj0XI_*F8RMnp%472M7kM&q?E${? zJzs(S0lZ#P zew%OG)pUS$R_{bKqgkgce1=u+a$K)e0iGnY|_aq(l_^8X|wJN zX}$NFwBrq~u8EHw`>VWo^nwh1W4)x*T_E0ae0MqOMmg5^wnBah@3EYHmLI6iYtt9# z`gw;%SK#_Tf&QKd&uze7l?hPYIQoalAhI`S?-z}SGeYZL4t8qmjt=b;LVGLpO=Czl z;0!gGbpkj-Hj6i6mqb?oV*GT#>sE5wyLz>FnN?fuDcv`>zAt^XPept`nB-hyNLW{5kf* z$v=$$2+oN0`(|<6Uzx%hypnrKAA;GuW8}Qs#a$TZXzp&YPjSv>pY{>IAqVboIo#VN zi1(V_uq_f(afOsiS}4_Pejw$me8~7f?~UTjC~}*)LP+c3%*)Lgkc;~#&SA896%fXl zkA9POPqBF|&JEyyI=pH-pQY`s`!f6%LflM0|7YP}Z6U?>qms%dIpdD+YnXDh1$z)v znRW>Qj?457otKE~&K&(F_y};n$ANzA;LO-;61SIctVCpqCoGe9plf|Wp(}izun&FS z@L9aAoJbnI@5lMJse#{nje9jm81L|%(2jb%w?nVa?fmrzm*H2(x}N=y1hatJ!gNl^ zBweAsLvcJ8jnn$+O7th)dsZ0BSnQ81pe-&PH;Lb-K=(HK3HO0wxT{nK9-`nS38eW^ z1$jjAa4CZCOhmDlkGV*iv+#M0{Mpt$1nV8A&hZJc)M4>`JG?8snhySwKrXy0|JIzR zd}|o!zF?$XUOrDZ_XGR);$?6nJ?X5mb;GTMe5yBu_HOi*$H64V?JMXkGxDMSu*tON zIC#xqF7ISaY7_FLAt(A*L;=4xZGp4|`is(LhcyjY7b>foVdaJ6mbq6|!Bi|eH52P#54;^a%)bsyw_Zv>8wYlL;&XxtTe*0G{}oS-WioC0=wdu(anQfT~KKmFtJ>+e@7YlOzp z-r63zAJcTpf%UE!cOB4r&w_f?rR}bBwWiZH*#VXjr~GT&DZqHSbwAL01EG1~Nz0!M zv}~nOViPQYL-i#%`-pi=%&#&?8h-ZDX=-Hy_z**1$bOk!MDE}&JodZr1KBI9! z%Y4R`|LyQ?r}5XQC}GDB%oo1_W!=1E5GSFW2c|1 zbrClnXgZD00?L=>x6^%%U-N5QsI2pV@>kCfi&x6X1xX}?7L+7?Qy(xL63?PiyyaW?-G{AsLo__g2L z`Ly4i1j>iz*EIUu4v?gMh2N&&*M6pTWPv&OwQV%rGWaN1CyV2=q&VfDvYJqaP0wZg z4Tvvz%R*Sw)P@dS!wcf^>rbtK(xdab;%lH|S?6Z$v$M5s{9`l>^mnCQdAovNu>mv! zDqn4TrR6l}s&x2ij3caLUCXuPjH|RR0O^{hBpyqP_m>?_8QDPPE|j|p)X;qVd5G%< z&I9d(+WrOK1N7(D^`jnW0g{MQoh{xEt1sc#_Oata@P|-N4brFLx9*?e5@mhUNZ)5z z`HIpGJqhG#s&(PNo@^1Il9B8}w-XOFq{R*wa z_S@mWxIn+1&Zf%_2j;VXD=lAOnjh--*QqklHQJVe%E5*;p7~wpy=FhO{C|b?Dg$`a zKG_ae*Y#uItRF{+*D+g*}sAeltzDF(D9rMRQFOO5??Uy{j2cuW9jtEpkO>} Z_{Wy@?|g;#8~>g1pxyrc_kUOd{|_kcJuCnK diff --git a/GAME/Game_TEST/Game_TEST/Program.cs b/GAME/Game_TEST/Game_TEST/Program.cs deleted file mode 100644 index b1572f623..000000000 --- a/GAME/Game_TEST/Game_TEST/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -#region Using Statements -using System; -using System.Collections.Generic; -using System.Linq; -#endregion - -namespace Game_TEST -{ -#if WINDOWS || LINUX - /// - /// The main class. - /// - public static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - using (var game = new Game1()) - game.Run(); - } - } -#endif -} diff --git a/GAME/Game_TEST/Game_TEST/Properties/AssemblyInfo.cs b/GAME/Game_TEST/Game_TEST/Properties/AssemblyInfo.cs deleted file mode 100644 index c1a40cb9c..000000000 --- a/GAME/Game_TEST/Game_TEST/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Game_TEST")] -[assembly: AssemblyProduct("Game_TEST")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("5c4ee4ad-a4b0-483e-a8e8-4cd17cfa4c2d")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")]