added preliminary heartbeat system

modified the deployment of outdated message
inform user of invalid rcon password
reworked layout of first time server setup
fixed KDR not being properly truncated in some cases
fixed duplicate game-end event
more crash fixes
This commit is contained in:
RaidMax 2015-03-10 15:45:20 -05:00
parent b745ce386c
commit 57ddbebd8c
12 changed files with 192 additions and 77 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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()));

View File

@ -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;

View File

@ -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)

24
Admin/Heartbeat.cs Normal file
View File

@ -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;
}
}

View File

@ -94,6 +94,7 @@
<Compile Include="Database.cs" />
<Compile Include="Event.cs" />
<Compile Include="File.cs" />
<Compile Include="Heartbeat.cs" />
<Compile Include="Log.cs" />
<Compile Include="Main.cs" />
<Compile Include="Maps.cs" />

View File

@ -26,55 +26,38 @@ namespace IW4MAdmin
Console.WriteLine("=====================================================");
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)
foreach (Server IW4M in checkConfig())
{
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();
Utilities.Wait(0.3);
Console.WriteLine("Now monitoring " + IW4M.getName());
}
}
else
{
Console.WriteLine("[FATAL] CONFIG FILE DOES NOT EXIST OR IS INCORRECT!");
Utilities.Wait(5);
}
Utilities.Wait(5); //Give them time to read an error before exiting
}
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();
while (!validPort)
{
Console.Write("Please enter the Port: ");
Port = Convert.ToInt32(Console.ReadLine());
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<Server> checkConfig()
{
file Config = new file("config\\servers.cfg");
String[] SV_CONF = Config.readAll();
List<Server> Servers = new List<Server>();
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;
}
}
}

View File

@ -10,7 +10,7 @@ namespace IW4MAdmin
{
Kills = K;
Deaths = D;
KDR = kdr;
KDR = Math.Round(kdr,2);
Skill = Math.Round(skill,2);
}

View File

@ -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)

View File

@ -30,6 +30,7 @@ namespace IW4MAdmin
rules = new List<String>();
messages = new List<String>();
events = new Queue<Event>();
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<Ban> 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 <player> <reason>", "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 <player> <level>.", "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 <player> <reason>.", "w", Player.Permission.Moderator, 2, true));
commands.Add(new WarnClear("warnclear", "remove all warning for a player syntax: !warnclear <player>.", "wc", Player.Permission.Administrator, 1, true));
@ -774,11 +805,13 @@ namespace IW4MAdmin
public List<String> rules;
public Queue<Event> 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<Player> 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;

View File

@ -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]", "");
}
}
}