diff --git a/Admin/Command.cs b/Admin/Command.cs index 15674f2db..9fac290ee 100644 --- a/Admin/Command.cs +++ b/Admin/Command.cs @@ -167,7 +167,11 @@ namespace IW4MAdmin { E.Target.LastOffense = Utilities.removeWords(E.Data, 1); E.Target.lastEvent = E; // needs to be fixed +#if DEBUG String Message = "^1Player Banned: ^5" + E.Target.LastOffense + "^7 (appeal at nbsclan.org)"; +#else + String Message = "^1Player Banned: ^5" + E.Target.LastOffense; +#endif if (E.Origin.getLevel() > E.Target.getLevel()) { E.Target.Ban(Message, E.Origin); diff --git a/Admin/Connection.cs b/Admin/Connection.cs index 3c5a3dacd..f970f446c 100644 --- a/Admin/Connection.cs +++ b/Admin/Connection.cs @@ -35,6 +35,20 @@ namespace IW4MAdmin } } + public void Request(String data) + { + try + { + WebResponse Resp = WebRequest.Create(data).GetResponse(); + Resp.Close(); + } + + catch (System.Net.WebException E) + { + return; + } + } + private String Location; private WebRequest ConnectionHandle; } diff --git a/Admin/Database.cs b/Admin/Database.cs index 0d78b3e22..e1c0cd6c8 100644 --- a/Admin/Database.cs +++ b/Admin/Database.cs @@ -187,7 +187,7 @@ namespace IW4MAdmin { updatedPlayer.Add("KILLS", P.stats.Kills); updatedPlayer.Add("DEATHS", P.stats.Deaths); - updatedPlayer.Add("KDR", P.stats.KDR); + updatedPlayer.Add("KDR", Math.Round(P.stats.KDR, 2)); updatedPlayer.Add("SKILL", P.stats.Skill); Update("STATS", updatedPlayer, String.Format("Number = '{0}'", P.getDBID())); diff --git a/Admin/Event.cs b/Admin/Event.cs index 35fdef7ab..c6a92255b 100644 --- a/Admin/Event.cs +++ b/Admin/Event.cs @@ -84,11 +84,11 @@ namespace IW4MAdmin return new Event(GType.Say, Utilities.removeNastyChars(message), SV.clientFromLine(line, 3, false), null, null); } - if (eventType == "d" || eventType == ":") + if (eventType == ":") return new Event(GType.MapEnd, null, null, null, null); if (line[0].Length > 400) // blaze it - return new Event(GType.MapChange, null, null, null, null); + return new Event(GType.MapChange, line[0], null, null, null); return null; diff --git a/Admin/File.cs b/Admin/File.cs index fe953005b..c4cd735cb 100644 --- a/Admin/File.cs +++ b/Admin/File.cs @@ -64,6 +64,14 @@ namespace IW4MAdmin return 0; } + public void Close() + { + if(Handle != null) + Handle.Close(); + if (writeHandle != null) + writeHandle.Close(); + } + //FROM http://stackoverflow.com/questions/398378/get-last-10-lines-of-very-large-text-file-10gb-c-sharp public string ReadEndTokens() { @@ -105,7 +113,7 @@ namespace IW4MAdmin } public String[] readAll() { - return Handle.ReadToEnd().Split('\n'); + return Handle.ReadToEnd().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); } public String[] end(int neededLines) diff --git a/Admin/Heartbeat.cs b/Admin/Heartbeat.cs new file mode 100644 index 000000000..4f94ca0bc --- /dev/null +++ b/Admin/Heartbeat.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IW4MAdmin +{ + class Heartbeat + { + public Heartbeat(Server I) + { + Handle = new Connection("http://raidmax.org/IW4M/Admin"); + Instance = I; + } + + 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()); + Handle.Request(URI); + } + + private Connection Handle; + private Server Instance; + } +} diff --git a/Admin/IW4M ADMIN.csproj b/Admin/IW4M ADMIN.csproj index 4878af297..4a817a6ed 100644 --- a/Admin/IW4M ADMIN.csproj +++ b/Admin/IW4M ADMIN.csproj @@ -94,6 +94,7 @@ + diff --git a/Admin/Main.cs b/Admin/Main.cs index b1c8f3be4..b9394866a 100644 --- a/Admin/Main.cs +++ b/Admin/Main.cs @@ -24,57 +24,40 @@ namespace IW4MAdmin else Console.WriteLine(" Version " + Version + " (unable to retrieve latest)"); Console.WriteLine("====================================================="); + + + foreach (Server IW4M in checkConfig()) + { + //Threading seems best here + Thread monitorThread = new Thread(new ThreadStart(IW4M.Monitor)); + monitorThread.Start(); + Utilities.Wait(0.3); + Console.WriteLine("Now monitoring " + IW4M.getName()); + } + + Utilities.Wait(5); //Give them time to read an error before exiting - - file Config = new file("config\\servers.cfg"); - String[] SV_CONF = Config.readAll(); - - if (SV_CONF == null || SV_CONF.Length < 1 || SV_CONF[0] == String.Empty) - { - setupConfig(); - SV_CONF = new file("config\\servers.cfg").readAll(); - } - - - if (Config.getSize() > 0 && SV_CONF != null) - { - foreach (String S in SV_CONF) - { - if (S.Length < 1) - continue; - - String[] sv = S.Split(':'); - - Console.WriteLine("Starting admin on port " + sv[1]); - - Server IW4M; - IW4M = new Server(sv[0], Convert.ToInt32(sv[1]), sv[2]); - - //Threading seems best here - Thread monitorThread = new Thread(new ThreadStart(IW4M.Monitor)); - monitorThread.Start(); - } - - } - else - { - Console.WriteLine("[FATAL] CONFIG FILE DOES NOT EXIST OR IS INCORRECT!"); - Utilities.Wait(5); - } - } static void setupConfig() { + bool validPort = false; Console.WriteLine("Hey there, it looks like you haven't set up a server yet. Let's get started!"); + Console.Write("Please enter the IP: "); IP = Console.ReadLine(); - Console.Write("Please enter the Port: "); - Port = Convert.ToInt32(Console.ReadLine()); + + while (!validPort) + { + Console.Write("Please enter the Port: "); + int.TryParse(Console.ReadLine(), out Port); + if (Port != 0) + validPort = true; + } + Console.Write("Please enter the RCON password: "); RCON = Console.ReadLine(); file Config = new file("config\\servers.cfg", true); - Config.Write(IP + ":" + Port + ":" + RCON); Console.WriteLine("Great! Let's go ahead and start 'er up."); } @@ -83,5 +66,38 @@ namespace IW4MAdmin Connection Ver = new Connection("http://raidmax.org/IW4M/Admin/version.php"); return Ver.Read(); } + + static List checkConfig() + { + + file Config = new file("config\\servers.cfg"); + String[] SV_CONF = Config.readAll(); + List Servers = new List(); + + + if (SV_CONF == null || SV_CONF.Length < 1 || SV_CONF[0] == String.Empty) + { + setupConfig(); // get our first time server + Config.Write(IP + ':' + Port + ':' + RCON); + Servers.Add(new Server(IP, Port, RCON)); + } + + else + { + foreach (String L in SV_CONF) + { + String[] server_line = L.Split(':'); + int newPort; + if (server_line.Length < 3 || !int.TryParse(server_line[1], out newPort)) + { + Console.WriteLine("You have an error in your server.cfg"); + continue; + } + Servers.Add(new Server(server_line[0], newPort, server_line[2])); + } + } + + return Servers; + } } } diff --git a/Admin/Player.cs b/Admin/Player.cs index 5b90389de..e5c8f76d6 100644 --- a/Admin/Player.cs +++ b/Admin/Player.cs @@ -10,7 +10,7 @@ namespace IW4MAdmin { Kills = K; Deaths = D; - KDR = kdr; + KDR = Math.Round(kdr,2); Skill = Math.Round(skill,2); } diff --git a/Admin/RCON.cs b/Admin/RCON.cs index d62eaf178..b9cb6c0df 100644 --- a/Admin/RCON.cs +++ b/Admin/RCON.cs @@ -67,7 +67,7 @@ namespace IW4MAdmin { String STR_REQUEST; if (message != "getstatus") - STR_REQUEST = String.Format("ÿÿÿÿrcon {0} {1}", Instance.getPassword(), message); + STR_REQUEST = String.Format("ÿÿÿÿrcon {0} {1}", Instance.getPassword().Replace("\r", String.Empty), message); else STR_REQUEST = String.Format("ÿÿÿÿ getstatus"); @@ -93,7 +93,15 @@ namespace IW4MAdmin Byte[] receive = sv_connection.Receive(ref endPoint); int num = int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier); - return System.Text.Encoding.UTF8.GetString(receive).Split((char)num); + String[] response = System.Text.Encoding.UTF8.GetString(receive).Split((char)num); + + if(response[1] == "Invalid password.") + { + Instance.Log.Write("Invalid RCON password specified", Log.Level.Debug); + return null; + } + + return response; } catch (SocketException) diff --git a/Admin/Server.cs b/Admin/Server.cs index d2a8f6871..d61e0ddfb 100644 --- a/Admin/Server.cs +++ b/Admin/Server.cs @@ -30,6 +30,7 @@ namespace IW4MAdmin rules = new List(); messages = new List(); events = new Queue(); + HB = new Heartbeat(this); nextMessage = 0; initCommands(); initMessages(); @@ -43,6 +44,11 @@ namespace IW4MAdmin return hostname; } + public String getMap() + { + return mapname; + } + //Returns current server IP set by `net_ip` -- *STRING* public String getIP() { @@ -73,6 +79,11 @@ namespace IW4MAdmin return players; } + public int getClientNum() + { + return clientnum; + } + //Returns list of all active bans (loaded at runtime) public List getBans() { @@ -282,8 +293,8 @@ namespace IW4MAdmin String[] oldLines = new String[8]; DateTime start = DateTime.Now; - Utilities.Wait(1); - Broadcast("IW4M Admin is now ^2ONLINE"); + //Utilities.Wait(1); + //Broadcast("IW4M Admin is now ^2ONLINE"); while (errors <=5) { @@ -292,14 +303,13 @@ namespace IW4MAdmin lastMessage = DateTime.Now - start; if(lastMessage.TotalSeconds > messageTime && messages.Count > 0) { - if (Program.Version != Program.latestVersion && Program.latestVersion != 0) - Broadcast("^5IW4M Admin ^7is outdated. Please ^5update ^7to version " + Program.latestVersion); Broadcast(messages[nextMessage]); if (nextMessage == (messages.Count - 1)) nextMessage = 0; else nextMessage++; start = DateTime.Now; + HB.Send(); } if (l_size != logFile.getSize()) @@ -367,8 +377,40 @@ namespace IW4MAdmin { try { + //get sv_hostname + String[] p = RCON.responseSendRCON("sv_hostname"); + + if (p == null) + { + Log.Write("Could not obtain server name!", Log.Level.All); + return false; + } + + p = p[1].Split('"'); + hostname = Utilities.stripColors(p[3].Substring(0, p[3].Length - 2).Trim()); + p = null; + //END + + Thread.Sleep(FLOOD_TIMEOUT); + + //get mapname + p = RCON.responseSendRCON("mapname"); + + if (p == null) + { + Log.Write("Could not obtain map name!", Log.Level.All); + return false; + } + + p = p[1].Split('"'); + mapname = Utilities.stripColors(p[3].Substring(0, p[3].Length - 2).Trim()); + p = null; + //END + + Thread.Sleep(FLOOD_TIMEOUT); + //GET fs_basepath - String[] p = RCON.responseSendRCON("fs_basepath"); + p = RCON.responseSendRCON("fs_basepath"); if (p == null) { @@ -459,20 +501,6 @@ namespace IW4MAdmin Thread.Sleep(FLOOD_TIMEOUT); - //get sv_hostname - p = RCON.responseSendRCON("sv_hostname"); - - if (p == null) - { - Log.Write("Could not obtain server name!", Log.Level.All); - return false; - } - - p = p[1].Split('"'); - hostname = p[3].Substring(0, p[3].Length - 2).Trim(); - p = null; - //END - if (Mod == String.Empty || onelog == "1") logPath = Basepath + '\\' + "m2demo" + '\\' + log; else @@ -501,13 +529,13 @@ namespace IW4MAdmin { if (E.Type == Event.GType.Connect) { - this.addPlayer(E.Origin); + addPlayer(E.Origin); return true; } if (E.Type == Event.GType.Disconnect) { - if (getNumPlayers() > 0) + if (getNumPlayers() > 0 && E.Origin != null) { DB.updatePlayer(E.Origin); stats.updatePlayer(E.Origin); @@ -518,7 +546,7 @@ namespace IW4MAdmin if (E.Type == Event.GType.Kill) { - if (E.Origin != null && E.Target != null) + if (E.Origin != null && E.Target != null && E.Origin.stats != null) { E.Origin.stats.Kills++; E.Origin.stats.Update(); @@ -527,7 +555,7 @@ namespace IW4MAdmin } } - if (E.Type == Event.GType.Say) + if (E.Type == Event.GType.Say && E.Origin != null) { if (E.Data.Length < 2) return false; @@ -561,17 +589,18 @@ namespace IW4MAdmin if (E.Type == Event.GType.MapChange) { - Log.Write("Map change detected..", Log.Level.Production); - return true; - //TODO here + Log.Write("New map loaded", Log.Level.Debug); + String[] statusResponse = E.Data.Split('\\'); + if (statusResponse.Length >= 15 && statusResponse[13] == "mapname") + mapname = maps.Find(m => m.Name.Equals(statusResponse[14])).Alias; //update map for heartbeat } if (E.Type == Event.GType.MapEnd) { - Log.Write("Game ending...", Log.Level.Production); + Log.Write("Game ending...", Log.Level.Debug); foreach (Player P in players) { - if (P == null) + if (P == null || P.stats == null) continue; stats.updatePlayer(P); Log.Write("Updated stats for client " + P.getDBID(), Log.Level.Debug); @@ -707,6 +736,8 @@ namespace IW4MAdmin if (lines[0] != l && l.Length > 1) messages.Add(l); } + if (Program.Version != Program.latestVersion && Program.latestVersion != 0) + messages.Add("^5IW4M Admin ^7is outdated. Please ^5update ^7to version " + Program.latestVersion); } private void initRules() @@ -740,11 +771,11 @@ namespace IW4MAdmin commands.Add(new SBan("ban", "permanently ban a player from the server. syntax: !ban ", "b", Player.Permission.SeniorAdmin, 2, true)); commands.Add(new WhoAmI("whoami", "give information about yourself. syntax: !whoami.", "who", Player.Permission.User, 0, false)); commands.Add(new List("list", "list active clients syntax: !list.", "l", Player.Permission.Moderator, 0, false)); - commands.Add(new Help("help", "list all available commands. syntax: !help.", "l", Player.Permission.User, 0, false)); + commands.Add(new Help("help", "list all available commands. syntax: !help.", "h", Player.Permission.User, 0, false)); commands.Add(new FastRestart("fastrestart", "fast restart current map. syntax: !fastrestart.", "fr", Player.Permission.Moderator, 0, false)); commands.Add(new MapRotate("maprotate", "cycle to the next map in rotation. syntax: !maprotate.", "mr", Player.Permission.Administrator, 0, false)); commands.Add(new SetLevel("setlevel", "set player to specified administration level. syntax: !setlevel .", "sl", Player.Permission.Owner, 2, true)); - commands.Add(new Usage("usage", "get current application memory usage. syntax: !usage.", "u", Player.Permission.Moderator, 0, false)); + commands.Add(new Usage("usage", "get current application memory usage. syntax: !usage.", "us", Player.Permission.Moderator, 0, false)); 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)); @@ -774,11 +805,13 @@ namespace IW4MAdmin public List rules; public Queue events; public Database stats; + public Heartbeat HB; //Info private String IP; private int Port; private String hostname; + private String mapname; private int clientnum; private string rcon_pass; private List players; @@ -788,6 +821,7 @@ namespace IW4MAdmin private TimeSpan lastMessage; private int nextMessage; private int errors = 0; + private Connection Heartbeat; //Log stuff private String Basepath; diff --git a/Admin/Utilities.cs b/Admin/Utilities.cs index 3c4aecb82..301ac3d54 100644 --- a/Admin/Utilities.cs +++ b/Admin/Utilities.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using System.Threading; +using System.Text.RegularExpressions; namespace IW4MAdmin { @@ -73,5 +74,10 @@ namespace IW4MAdmin } return lineNumber; } + + public static String stripColors(String str) + { + return Regex.Replace(str, @"\^[0-9]", ""); + } } }