Been a while -- unstable 0.75

-added mask command
-added baninfo command
-added alias command and removed redundant output from `find`
-added rcon command
-added webfront (http://127.0.0.1:1624)
-true skill is officially implemented
-find now shows last connect time
-noise on pm (if gsc_enabled)
-force 8 line chat height (if gsc_enabled)
-tell admins the number of reports on join
-enhanced ban tracking
-ip wait timeout added
-remove report on ban
-can't report yourself
-remove reported players when banned
-fixed rare crash with toadmins backend
-fixed crash when finding player stats that don't exist
-fixed a bug that caused owner command to reactivate only `creator` rank player existed
-fixed a bug that caused certain notifications to be sent to all players
This commit is contained in:
RaidMax 2015-04-09 23:02:12 -05:00
parent ed4883d675
commit c3bf5bf33a
24 changed files with 1585 additions and 612 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v2.0.50727"/>
</startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
</configuration> </configuration>

View File

@ -37,7 +37,12 @@ namespace IW4MAdmin
public String getWhen() public String getWhen()
{ {
return When.ToString("yyyy-MM-dd HH:mm:ss"); ; return When.ToString("MM/dd/yy HH:mm:ss"); ;
}
public DateTime getTime()
{
return When;
} }
private String Reason; private String Reason;

View File

@ -154,7 +154,6 @@ namespace IW4MAdmin
E.Target.tempBan(Message); E.Target.tempBan(Message);
else else
E.Origin.Tell("You cannot temp ban " + E.Target.getName()); E.Origin.Tell("You cannot temp ban " + E.Target.getName());
} }
} }
@ -171,7 +170,7 @@ namespace IW4MAdmin
if (E.Owner.Website == null) if (E.Owner.Website == null)
Message = "^1Player Banned: ^5" + E.Target.LastOffense; Message = "^1Player Banned: ^5" + E.Target.LastOffense;
else else
Message = "^1Player Banned: ^5" + E.Target.LastOffense + "^7 (appeal at " + E.Owner.Website + ")"; Message = "^1Player Banned: ^5" + E.Target.LastOffense + "^7 (appeal at nbsclan.org)";
if (E.Origin.getLevel() > E.Target.getLevel()) if (E.Origin.getLevel() > E.Target.getLevel())
{ {
E.Target.Ban(Message, E.Origin); E.Target.Ban(Message, E.Origin);
@ -203,7 +202,7 @@ namespace IW4MAdmin
public override void Execute(Event E) public override void Execute(Event E)
{ {
String You = String.Format("{0} [^3{1}^7] {{2}} {{3}} [{4}^7] IP: {5}", E.Origin.getName(), E.Origin.getClientNum(), E.Origin.getID(), Utilities.levelToColor(E.Origin.getLevel()), E.Origin.getDBID(), E.Origin.getIP()); String You = String.Format("{0} [^3#{1}^7] {2} [^3@{3}^7] [{4}^7] IP: {5}", E.Origin.getName(), E.Origin.getClientNum(), E.Origin.getID(), E.Origin.getDBID(), Utilities.levelToColor(E.Origin.getLevel()), E.Origin.getIP());
E.Origin.Tell(You); E.Origin.Tell(You);
} }
@ -260,7 +259,7 @@ namespace IW4MAdmin
if (E.Origin.getLevel() >= C.getNeededPerm()) if (E.Origin.getLevel() >= C.getNeededPerm())
{ {
_commands = _commands + " [^3" + C.getName() + "^7] "; _commands = _commands + " [^3" + C.getName() + "^7] ";
if (count >= 3) if (count >= 4)
{ {
E.Origin.Tell(_commands); E.Origin.Tell(_commands);
_commands = String.Empty; _commands = String.Empty;
@ -359,7 +358,7 @@ namespace IW4MAdmin
{ {
foreach (Player P in E.Owner.getPlayers()) foreach (Player P in E.Owner.getPlayers())
{ {
if (P != null && P.getLevel() > Player.Permission.User) if (P != null && P.getLevel() > Player.Permission.User && !P.Masked)
{ {
E.Origin.Tell(String.Format("[^3{0}^7] {1}", Utilities.levelToColor(P.getLevel()), P.getName())); E.Origin.Tell(String.Format("[^3{0}^7] {1}", Utilities.levelToColor(P.getLevel()), P.getName()));
} }
@ -421,46 +420,10 @@ namespace IW4MAdmin
foreach (Player P in db_players) foreach (Player P in db_players)
{ {
String mesg; String mesg = String.Format("[^3{0}^7] [^3@{1}^7] - [{2}^7] - {3} | last seen {4} ago", P.getName(), P.getDBID(), Utilities.levelToColor(P.getLevel()), P.getIP(), P.getLastConnection());
P.Alias = 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); E.Origin.Tell(mesg);
if (P.Alias == null)
continue;
if (P.Alias.getNames() != null)
{
mesg = "Aliases: ";
foreach (String S in P.Alias.getNames())
{
if (S != String.Empty)
mesg += S + " | ";
}
E.Origin.Tell(mesg);
}
if (P.Alias.getIPS() != null)
{
mesg = "IPs: ";
foreach (String IP in P.Alias.getIPS())
{
if (IP.Split('.').Length > 3 && IP != String.Empty)
mesg += IP + " | ";
}
E.Origin.Tell(mesg);
}
} }
} }
} }
class Rules : Command class Rules : Command
@ -477,7 +440,6 @@ namespace IW4MAdmin
E.Origin.Tell("- " + r); E.Origin.Tell("- " + r);
} }
} }
} }
class PrivateMessage : Command class PrivateMessage : Command
@ -487,6 +449,7 @@ namespace IW4MAdmin
public override void Execute(Event E) public override void Execute(Event E)
{ {
E.Data = Utilities.removeWords(E.Data, 1); E.Data = Utilities.removeWords(E.Data, 1);
E.Target.Alert();
E.Target.Tell("^1" + E.Origin.getName() + " ^3[PM]^7 - " + E.Data); E.Target.Tell("^1" + E.Origin.getName() + " ^3[PM]^7 - " + E.Data);
E.Origin.Tell(String.Format("To ^3{0} ^7-> {1}", E.Target.getName(), E.Data)); E.Origin.Tell(String.Format("To ^3{0} ^7-> {1}", E.Target.getName(), E.Data));
} }
@ -499,12 +462,20 @@ namespace IW4MAdmin
public override void Execute(Event E) public override void Execute(Event E)
{ {
if (E.Target == null) if (E.Target == null)
E.Origin.Tell(String.Format("^5{0} ^7KILLS | ^5{1} ^7DEATHS | ^5{2} ^7KDR | ^5{3} ^7SKILL", E.Origin.stats.Kills, E.Origin.stats.Deaths, E.Origin.stats.KDR, E.Origin.stats.Skill));
else
{ {
if (E.Target.stats == null) if (E.Target.stats == null)
E.Target.stats = E.Owner.statDB.getStats(E.Target.getDBID()); E.Origin.Tell("You do not have any stats!");
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())); else
E.Origin.Tell(String.Format("^5{0} ^7KILLS | ^5{1} ^7DEATHS | ^5{2} ^7KDR | ^5{3} ^7SKILL", E.Origin.stats.Kills, E.Origin.stats.Deaths, E.Origin.stats.KDR, E.Origin.stats.Skill));
}
else
{
E.Target.stats = E.Owner.statDB.getStats(E.Target.getDBID());
if (E.Target.stats == null)
E.Origin.Tell("That person does not have any stats at this time!");
else
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()));
} }
} }
} }
@ -527,6 +498,7 @@ namespace IW4MAdmin
TopP.Add(P); TopP.Add(P);
} }
} }
if (TopP.Count > 0) if (TopP.Count > 0)
{ {
E.Origin.Tell("^1TOP PLAYERS"); E.Origin.Tell("^1TOP PLAYERS");
@ -536,6 +508,7 @@ namespace IW4MAdmin
E.Origin.Tell(String.Format("^3{0}^7 - ^5{1} ^7KDR | ^5{2} ^7SKILL", P.getName(), P.stats.KDR, P.stats.Skill)); E.Origin.Tell(String.Format("^3{0}^7 - ^5{1} ^7KDR | ^5{2} ^7SKILL", P.getName(), P.stats.KDR, P.stats.Skill));
} }
} }
else else
E.Origin.Tell("There are no top players yet!"); E.Origin.Tell("There are no top players yet!");
} }
@ -608,6 +581,12 @@ namespace IW4MAdmin
public override void Execute(Event E) public override void Execute(Event E)
{ {
if (E.Target == E.Origin)
{
E.Origin.Tell("You cannot report yourself, silly.");
return;
}
if (E.Target.getLevel() > E.Origin.getLevel()) if (E.Target.getLevel() > E.Origin.getLevel())
{ {
E.Origin.Tell("You cannot report " + E.Target.getName()); E.Origin.Tell("You cannot report " + E.Target.getName());
@ -655,5 +634,121 @@ namespace IW4MAdmin
E.Owner.RCON.addRCON(String.Format("admin_lastevent tell;{0};{1};{2}", E.Origin.getID(), E.Target.getID(), E.Data)); E.Owner.RCON.addRCON(String.Format("admin_lastevent tell;{0};{1};{2}", E.Origin.getID(), E.Target.getID(), E.Data));
} }
} }
class Mask : Command
{
public Mask(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.Origin.Masked)
{
E.Origin.Masked = false;
E.Origin.Tell("You are now unmasked");
}
else
{
E.Origin.Masked = true;
E.Origin.Tell("You are now masked");
}
}
}
class BanInfo : Command
{
public BanInfo(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.Target == null)
{
E.Origin.Tell("No bans for that player.");
return;
}
Ban B = E.Owner.Bans.Find(b => b.getID().Equals(E.Target.getID()));
if (B == null)
{
E.Origin.Tell("No active ban was found for that player.");
return;
}
Player Banner = E.Owner.clientDB.getPlayer(B.getBanner(), -1);
if (Banner == null)
{
E.Origin.Tell("Ban was found for the player, but origin of the ban is unavailable.");
return;
}
E.Origin.Tell(String.Format("^1{0} ^7was banned by ^5{1} ^7for: {2}", E.Target.getName(), Banner.getName(), B.getReason()));
}
}
class Alias : Command
{
public Alias(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)
{
E.Target.Alias = E.Owner.aliasDB.getPlayer(E.Target.getDBID());
if (E.Target.Alias == null)
{
E.Target.Tell("Could not find alias info for that player.");
return;
}
E.Target.Tell("[^3" + E.Target.getName() + "^7]");
StringBuilder message = new StringBuilder();
List<Player> playerAliases = new List<Player>();
E.Owner.getAliases(playerAliases, E.Target);
// if (E.Target.Alias.getNames() != null)
{
message.Append("Aliases: ");
foreach (Player P in playerAliases)
{
foreach (String S in P.Alias.getNames())
{
if (S != String.Empty && S != E.Target.getName())
message.Append(S + " | ");
}
}
E.Origin.Tell(message.ToString());
}
if (E.Target.Alias.getIPS() != null)
{
message.Append("IPS: ");
foreach (Player P2 in playerAliases)
{
foreach (String IP in P2.Alias.getIPS())
{
if (IP.Split('.').Length > 3 && IP != String.Empty && !message.ToString().Contains(IP))
message.Append (IP + " | ");
}
}
E.Origin.Tell(message.ToString());
}
}
}
class _RCON : Command
{
public _RCON(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)
{
String[] Response = E.Owner.RCON.addRCON(E.Data.Trim());
if (Response.Length > 0)
E.Origin.Tell("Successfuly sent RCON command!");
}
}
} }

View File

@ -79,11 +79,17 @@ namespace IW4MAdmin
protected int ExecuteNonQuery(String Request) protected int ExecuteNonQuery(String Request)
{ {
waitForClose(); waitForClose();
Con.Open(); int rowsUpdated = 0;
SQLiteCommand CMD = new SQLiteCommand(Con);
CMD.CommandText = Request; lock (Con)
int rowsUpdated = CMD.ExecuteNonQuery(); {
Con.Close(); Con.Open();
SQLiteCommand CMD = new SQLiteCommand(Con);
CMD.CommandText = Request;
rowsUpdated = CMD.ExecuteNonQuery();
Con.Close();
}
return rowsUpdated; return rowsUpdated;
} }
@ -93,13 +99,17 @@ namespace IW4MAdmin
try try
{ {
waitForClose(); waitForClose();
Con.Open(); lock (Con)
SQLiteCommand mycommand = new SQLiteCommand(Con); {
mycommand.CommandText = sql; Con.Open();
SQLiteDataReader reader = mycommand.ExecuteReader(); SQLiteCommand mycommand = new SQLiteCommand(Con);
dt.Load(reader); mycommand.CommandText = sql;
reader.Close(); SQLiteDataReader reader = mycommand.ExecuteReader();
Con.Close(); dt.Load(reader);
reader.Close();
Con.Close();
}
} }
catch (Exception e) catch (Exception e)
{ {
@ -134,7 +144,7 @@ namespace IW4MAdmin
{ {
if(!File.Exists(FileName)) 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);"; 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, [LastConnection] TEXT NULL);";
ExecuteNonQuery(Create); ExecuteNonQuery(Create);
Create = "CREATE TABLE [BANS] ( [Reason] TEXT NULL, [npID] TEXT NULL, [bannedByID] TEXT NULL, [IP] TEXT NULL, [TIME] TEXT NULL);"; Create = "CREATE TABLE [BANS] ( [Reason] TEXT NULL, [npID] TEXT NULL, [bannedByID] TEXT NULL, [IP] TEXT NULL, [TIME] TEXT NULL);";
ExecuteNonQuery(Create); ExecuteNonQuery(Create);
@ -150,12 +160,18 @@ namespace IW4MAdmin
if (Result != null && Result.Rows.Count > 0) if (Result != null && Result.Rows.Count > 0)
{ {
DataRow ResponseRow = Result.Rows[0]; DataRow ResponseRow = Result.Rows[0];
DateTime LC;
// 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()); try
{
LC = DateTime.Parse(ResponseRow["LastConnection"].ToString());
}
catch (Exception)
{
LC = DateTime.Now;
}
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(), LC);
} }
else else
@ -171,11 +187,17 @@ namespace IW4MAdmin
if (Result != null && Result.Rows.Count > 0) if (Result != null && Result.Rows.Count > 0)
{ {
DataRow p = Result.Rows[0]; DataRow p = Result.Rows[0];
DateTime LC;
try
{
LC = DateTime.Parse(p["LastConnection"].ToString());
}
catch (Exception)
{
LC = DateTime.Now;
}
// if (p["IP"].ToString().Length < 2) 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(), LC);
// 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 else
@ -194,7 +216,17 @@ namespace IW4MAdmin
{ {
foreach (DataRow p in Result.Rows) 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())); DateTime LC;
try
{
LC = DateTime.Parse(p["LastConnection"].ToString());
}
catch (Exception)
{
LC = DateTime.Now;
}
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(), LC));
} }
return Players; return Players;
} }
@ -206,7 +238,7 @@ namespace IW4MAdmin
//Returns any player with level 4 permissions, null if no owner found //Returns any player with level 4 permissions, null if no owner found
public Player getOwner() public Player getOwner()
{ {
String Query = String.Format("SELECT * FROM CLIENTS WHERE Level = '{0}'", 4); String Query = String.Format("SELECT * FROM CLIENTS WHERE Level >= '{0}'", 4);
DataTable Result = GetDataTable(Query); DataTable Result = GetDataTable(Query);
if (Result != null && Result.Rows.Count > 0) if (Result != null && Result.Rows.Count > 0)
@ -225,14 +257,12 @@ namespace IW4MAdmin
public List<Ban> getBans() public List<Ban> getBans()
{ {
List<Ban> Bans = new List<Ban>(); List<Ban> Bans = new List<Ban>();
DataTable Result = GetDataTable("SELECT * FROM BANS"); DataTable Result = GetDataTable("SELECT * FROM BANS ORDER BY TIME DESC");
foreach (DataRow Row in Result.Rows) foreach (DataRow Row in Result.Rows)
{ {
if (Row["TIME"].ToString().Length < 2) //compatibility with my old database if (Row["TIME"].ToString().Length < 2) //compatibility with my old database
Row["TIME"] = DateTime.Now.ToString(); 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())); Bans.Add(new Ban(Row["Reason"].ToString(), Row["npID"].ToString(), Row["bannedByID"].ToString(), DateTime.Parse(Row["TIME"].ToString()), Row["IP"].ToString()));
} }
@ -261,6 +291,7 @@ namespace IW4MAdmin
newPlayer.Add("LastOffense", ""); newPlayer.Add("LastOffense", "");
newPlayer.Add("Connections", 1); newPlayer.Add("Connections", 1);
newPlayer.Add("IP", P.getIP()); newPlayer.Add("IP", P.getIP());
newPlayer.Add("LastConnection", Utilities.DateTimeSQLite(DateTime.Now));
Insert("CLIENTS", newPlayer); Insert("CLIENTS", newPlayer);
} }
@ -276,6 +307,7 @@ namespace IW4MAdmin
updatedPlayer.Add("LastOffense", P.getLastO()); updatedPlayer.Add("LastOffense", P.getLastO());
updatedPlayer.Add("Connections", P.getConnections()); updatedPlayer.Add("Connections", P.getConnections());
updatedPlayer.Add("IP", P.getIP()); updatedPlayer.Add("IP", P.getIP());
updatedPlayer.Add("LastConnection", Utilities.DateTimeSQLite(DateTime.Now));
Update("CLIENTS", updatedPlayer, String.Format("npID = '{0}'", P.getID())); Update("CLIENTS", updatedPlayer, String.Format("npID = '{0}'", P.getID()));
} }
@ -314,7 +346,7 @@ namespace IW4MAdmin
{ {
if (!File.Exists(FileName)) 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 );"; String Create = "CREATE TABLE [STATS] ( [Number] INTEGER, [KILLS] INTEGER DEFAULT 0, [DEATHS] INTEGER DEFAULT 0, [KDR] REAL DEFAULT 0, [SKILL] REAL DEFAULT 0, [MEAN] REAL DEFAULT 0, [DEV] REAL DEFAULT 0 );";
ExecuteNonQuery(Create); ExecuteNonQuery(Create);
} }
} }
@ -328,11 +360,18 @@ namespace IW4MAdmin
if (Result != null && Result.Rows.Count > 0) if (Result != null && Result.Rows.Count > 0)
{ {
DataRow ResponseRow = Result.Rows[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"])); if (ResponseRow["MEAN"] == DBNull.Value)
ResponseRow["MEAN"] = 25;
if (ResponseRow["DEV"] == DBNull.Value)
ResponseRow["DEV"] = 8;
if (ResponseRow["SKILL"] == DBNull.Value)
ResponseRow["SKILL"] = 0;
return new Stats(Convert.ToInt32(ResponseRow["Number"]), Convert.ToInt32(ResponseRow["KILLS"]), Convert.ToInt32(ResponseRow["DEATHS"]), Convert.ToDouble(ResponseRow["KDR"]), Convert.ToDouble(ResponseRow["SKILL"]), Convert.ToDouble(ResponseRow["MEAN"]), Convert.ToDouble(ResponseRow["DEV"]));
} }
else else
return null; return new Stats(0, 0, 0, 0, 0, 25, 8.3333);
} }
public void addPlayer(Player P) public void addPlayer(Player P)
@ -343,7 +382,9 @@ namespace IW4MAdmin
newPlayer.Add("KILLS", 0); newPlayer.Add("KILLS", 0);
newPlayer.Add("DEATHS", 0); newPlayer.Add("DEATHS", 0);
newPlayer.Add("KDR", 0); newPlayer.Add("KDR", 0);
newPlayer.Add("SKILL", 1); newPlayer.Add("SKILL", Moserware.Skills.GameInfo.DefaultGameInfo.DefaultRating.ConservativeRating);
newPlayer.Add("MEAN", Moserware.Skills.GameInfo.DefaultGameInfo.DefaultRating.Mean);
newPlayer.Add("DEV", Moserware.Skills.GameInfo.DefaultGameInfo.DefaultRating.StandardDeviation);
Insert("STATS", newPlayer); Insert("STATS", newPlayer);
} }
@ -357,6 +398,8 @@ namespace IW4MAdmin
updatedPlayer.Add("DEATHS", P.stats.Deaths); updatedPlayer.Add("DEATHS", P.stats.Deaths);
updatedPlayer.Add("KDR", Math.Round(P.stats.KDR, 2)); updatedPlayer.Add("KDR", Math.Round(P.stats.KDR, 2));
updatedPlayer.Add("SKILL", P.stats.Skill); updatedPlayer.Add("SKILL", P.stats.Skill);
updatedPlayer.Add("MEAN", P.stats.Rating.Mean);
updatedPlayer.Add("DEV", P.stats.Rating.StandardDeviation);
Update("STATS", updatedPlayer, String.Format("Number = '{0}'", P.getDBID())); Update("STATS", updatedPlayer, String.Format("Number = '{0}'", P.getDBID()));
} }
@ -364,7 +407,7 @@ namespace IW4MAdmin
//Returns top 8 players (we filter through them later) //Returns top 8 players (we filter through them later)
public List<Stats> topStats() public List<Stats> topStats()
{ {
String Query = String.Format("SELECT * FROM STATS WHERE SKILL > '{0}' ORDER BY SKILL DESC LIMIT 8", 10); String Query = String.Format("SELECT * FROM STATS WHERE SKILL > '{0}' ORDER BY SKILL DESC LIMIT 5", 230);
DataTable Result = GetDataTable(Query); DataTable Result = GetDataTable(Query);
List<Stats> Top = new List<Stats>(); List<Stats> Top = new List<Stats>();
@ -373,8 +416,16 @@ namespace IW4MAdmin
{ {
foreach (DataRow D in Result.Rows) 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 (D["MEAN"] == DBNull.Value)
if (S.Skill > 10) continue;
if (D["DEV"] == DBNull.Value)
continue;
if (D["SKILL"] == DBNull.Value)
D["SKILL"] = 0;
Stats S = new Stats(Convert.ToInt32(D["Number"]), Convert.ToInt32(D["KILLS"]), Convert.ToInt32(D["DEATHS"]), Convert.ToDouble(D["KDR"]), Convert.ToDouble(D["SKILL"]), Convert.ToDouble(D["MEAN"]), Convert.ToDouble(D["DEV"]));
if (S.Skill > 230)
Top.Add(S); Top.Add(S);
} }
} }
@ -382,6 +433,42 @@ namespace IW4MAdmin
return Top; return Top;
} }
public List<Stats> getMultipleStats(int start, int length)
{
String Query = String.Format("SELECT * FROM STATS ORDER BY SKILL DESC LIMIT '{0}' OFFSET '{1}'", length, start);
DataTable Result = GetDataTable(Query);
List<Stats> Stats = new List<Stats>();
if (Result != null && Result.Rows.Count > 0)
{
foreach (DataRow D in Result.Rows)
{
if (D["MEAN"] == DBNull.Value)
continue;
if (D["DEV"] == DBNull.Value)
continue;
if (D["SKILL"] == DBNull.Value)
D["SKILL"] = 0;
Stats S = new Stats(Convert.ToInt32(D["Number"]), Convert.ToInt32(D["KILLS"]), Convert.ToInt32(D["DEATHS"]), Convert.ToDouble(D["KDR"]), Convert.ToDouble(D["SKILL"]), Convert.ToDouble(D["MEAN"]), Convert.ToDouble(D["DEV"]));
Stats.Add(S);
}
}
return Stats;
}
public int totalStats()
{
DataTable Result = GetDataTable("SELECT * from STATS ORDER BY Number DESC LIMIT 1");
if (Result.Rows.Count > 0)
return Convert.ToInt32(Result.Rows[0]["Number"]);
else
return 0;
}
public void clearSkill() public void clearSkill()
{ {
String Query = "SELECT * FROM STATS"; String Query = "SELECT * FROM STATS";
@ -423,6 +510,21 @@ namespace IW4MAdmin
return null; return null;
} }
public List<Aliases> getPlayer(String IP)
{
String Query = String.Format("SELECT * FROM ALIASES WHERE IPS LIKE '%{0}%'", IP);
DataTable Result = GetDataTable(Query);
List<Aliases> players = new List<Aliases>();
if (Result != null && Result.Rows.Count > 0)
{
foreach (DataRow p in Result.Rows)
players.Add(new Aliases(Convert.ToInt32(p["Number"]), p["NAMES"].ToString(), p["IPS"].ToString()));
}
return players;
}
public void addPlayer(Aliases Alias) public void addPlayer(Aliases Alias)
{ {
Dictionary<String, object> newPlayer = new Dictionary<String, object>(); Dictionary<String, object> newPlayer = new Dictionary<String, object>();

View File

@ -66,24 +66,19 @@ namespace IW4MAdmin
eventType = eventType.Trim(); eventType = eventType.Trim();
if (eventType == "J") if (eventType == "J")
return new Event(GType.Connect, null, SV.clientFromLine(line, 3, true), null, SV); return new Event(GType.Connect, null, SV.clientFromEventLine(line, 2), null, SV);
if (eventType == "Q") if (eventType == "Q")
return new Event(GType.Disconnect, null, SV.getPlayers()[Convert.ToInt16(line[2])], null, null); return new Event(GType.Disconnect, null, SV.clientFromEventLine(line, 2), null, SV);
if (eventType == "K") if (eventType == "K")
return new Event(GType.Kill, line[9], SV.clientFromLineArr(line, true), SV.clientFromLineArr(line, false), null); return new Event(GType.Kill, line[9], SV.clientFromEventLine(line, 6), SV.clientFromEventLine(line, 2), SV);
if (line[0].Substring(line[0].Length - 3).Trim() == "say") if (line[0].Substring(line[0].Length - 3).Trim() == "say")
{ {
if (line.Length < 4)
{
Console.WriteLine("SAY FUCKED UP BIG-TIME");
return null;
}
Regex rgx = new Regex("[^a-zA-Z0-9 -! -_]"); Regex rgx = new Regex("[^a-zA-Z0-9 -! -_]");
string message = rgx.Replace(line[4], ""); string message = rgx.Replace(line[4], "");
return new Event(GType.Say, Utilities.removeNastyChars(message), SV.clientFromLine(line, 3, false), null, null); return new Event(GType.Say, Utilities.removeNastyChars(message), SV.clientFromEventLine(line, 2), null, SV);
} }
if (eventType == ":") if (eventType == ":")

View File

@ -112,11 +112,17 @@ namespace IW4MAdmin
return encoding.GetString(buffer); return encoding.GetString(buffer);
} }
} }
public String[] readAll() public String[] readAll()
{ {
return Handle.ReadToEnd().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); return Handle.ReadToEnd().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
} }
public String getLines()
{
return Handle.ReadToEnd();
}
public String[] end(int neededLines) public String[] end(int neededLines)
{ {
var lines = new List<String>(); var lines = new List<String>();

View File

@ -14,7 +14,7 @@ namespace IW4MAdmin
public void Send() public void Send()
{ {
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()); 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.statusPlayers.Count.ToString() + '/' + Instance.getMaxClients().ToString(), IW4MAdmin.Program.Version.ToString());
Handle.Request(URI); Handle.Request(URI);
} }

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>IW4MAdmin</RootNamespace> <RootNamespace>IW4MAdmin</RootNamespace>
<AssemblyName>IW4MAdmin</AssemblyName> <AssemblyName>IW4MAdmin</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion> <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<IsWebBootstrapper>true</IsWebBootstrapper> <IsWebBootstrapper>true</IsWebBootstrapper>
@ -78,11 +78,18 @@
<StartupObject>IW4MAdmin.Program</StartupObject> <StartupObject>IW4MAdmin.Program</StartupObject>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Kayak">
<HintPath>..\packages\Kayak.0.7.2\lib\Kayak.dll</HintPath>
</Reference>
<Reference Include="Moserware.Skills, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>bin\Debug\Moserware.Skills.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Data.SQLite, Version=1.0.66.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86"> <Reference Include="System.Data.SQLite, Version=1.0.96.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>bin\Release\System.Data.SQLite.dll</HintPath> <HintPath>libs\System.Data.SQLite.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Web" /> <Reference Include="System.Web" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
@ -110,9 +117,31 @@
<Compile Include="Server.cs" /> <Compile Include="Server.cs" />
<Compile Include="TrueSkill.cs" /> <Compile Include="TrueSkill.cs" />
<Compile Include="Utilities.cs" /> <Compile Include="Utilities.cs" />
<Compile Include="WebFront.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="version.txt" /> <Content Include="libs\Moserware.Skills.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="libs\SQLite.Interop.dll" />
<Content Include="libs\System.Data.SQLite.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="version.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="webfront\bans.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\header.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\main.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\stats.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="App.config" /> <None Include="App.config" />
<Content Include="config\maps.cfg"> <Content Include="config\maps.cfg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -127,6 +156,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<None Include="IW4M ADMIN_TemporaryKey.pfx" /> <None Include="IW4M ADMIN_TemporaryKey.pfx" />
<None Include="packages.config" />
<None Include="Properties\app.manifest" /> <None Include="Properties\app.manifest" />
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>
@ -151,7 +181,8 @@
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent>copy $(TargetDir)$(TargetFileName) $(SolutionDir)OFFICIAL\NBS\$(TargetFileName)</PostBuildEvent> <PostBuildEvent>copy $(TargetDir)$(TargetFileName) $(SolutionDir)OFFICIAL\Release\$(TargetFileName)
copy $(SolutionDir)Admin\version.txt $(SolutionDir)OFFICIAL\Release\version.txt</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -10,8 +10,9 @@ namespace IW4MAdmin
static String IP; static String IP;
static int Port; static int Port;
static String RCON; static String RCON;
static public double Version = 0.6; static public double Version = 0.7;
static public double latestVersion; static public double latestVersion;
static public List<Server> Servers;
static void Main(string[] args) static void Main(string[] args)
{ {
@ -24,9 +25,9 @@ namespace IW4MAdmin
else else
Console.WriteLine(" Version " + Version + " (unable to retrieve latest)"); Console.WriteLine(" Version " + Version + " (unable to retrieve latest)");
Console.WriteLine("====================================================="); Console.WriteLine("=====================================================");
foreach (Server IW4M in checkConfig()) Servers = checkConfig();
foreach (Server IW4M in Servers)
{ {
//Threading seems best here //Threading seems best here
Server SV = IW4M; Server SV = IW4M;
@ -36,6 +37,9 @@ namespace IW4MAdmin
Console.WriteLine("Now monitoring " + SV.getName()); Console.WriteLine("Now monitoring " + SV.getName());
} }
IW4MAdmin_Web.WebFront test = new IW4MAdmin_Web.WebFront();
test.Init();
Utilities.Wait(5); //Give them time to read an error before exiting Utilities.Wait(5); //Give them time to read an error before exiting
} }

View File

@ -6,34 +6,33 @@ namespace IW4MAdmin
{ {
class Stats class Stats
{ {
public Stats(int K, int D, double kdr, double skill) public Stats(int n, int K, int D, double kdr, double skill, double mean, double dev)
{ {
statIndex = n;
Kills = K; Kills = K;
Deaths = D; Deaths = D;
KDR = Math.Round(kdr,2); KDR = Math.Round(kdr,2);
Skill = Math.Round(skill,2);
lastSigma = lastMew/3; Rating = new Moserware.Skills.Rating(mean, dev);
lastMew = 25; Skill = Math.Round(Rating.ConservativeRating, 3)*10;
} }
public void updateKDR() public void updateKDR()
{ {
KDR = Math.Round((double)((double)Kills / (double)Deaths), 2); int tempDeaths = Deaths; // cuz we don't want undefined!
} if (Deaths == 0)
tempDeaths = 1;
public void updateSkill() KDR = Math.Round((double)((double)Kills / (double)tempDeaths), 2);
{
Skill = TrueSkill.Gaussian(lastMew, lastSigma);
} }
public int Kills; public int Kills;
public int Deaths; public int Deaths;
public double KDR; public double KDR;
public double Skill; public double Skill;
public int statIndex;
public double lastSigma; public Moserware.Skills.Rating Rating;
public double lastMew;
} }
class Aliases class Aliases
@ -73,7 +72,7 @@ namespace IW4MAdmin
public void addName(String Name) public void addName(String Name)
{ {
if (Name.Trim() != String.Empty && Name != null) if (Name.Trim() != String.Empty && Name != null)
Names += ';' + Names; Names += ';' + Name;
} }
public void addIP(String IP) public void addIP(String IP)
@ -112,7 +111,18 @@ namespace IW4MAdmin
IP = ""; IP = "";
Warnings = 0; Warnings = 0;
Alias = new Aliases(0, "", ""); Alias = new Aliases(0, "", "");
stats = new Stats(0, 0, 0, 1); stats = new Stats(0, 0, 0, 0, Moserware.Skills.GameInfo.DefaultGameInfo.DefaultRating.ConservativeRating, Moserware.Skills.GameInfo.DefaultGameInfo.DefaultRating.Mean, Moserware.Skills.GameInfo.DefaultGameInfo.DefaultRating.StandardDeviation);
LastConnection = DateTime.Now;
}
public Player(string n, string id, int num, String I)
{
Name = n;
npID = id;
Number = num;
IP = I;
LastConnection = DateTime.Now;
} }
public Player(string n, string id, int num, Player.Permission l, int cind, String lo, int con, String IP2) public Player(string n, string id, int num, Player.Permission l, int cind, String lo, int con, String IP2)
@ -129,6 +139,26 @@ namespace IW4MAdmin
Connections = con; Connections = con;
IP = IP2; IP = IP2;
Warnings = 0; Warnings = 0;
Masked = false;
LastConnection = DateTime.Now;
}
public Player(string n, string id, int num, Player.Permission l, int cind, String lo, int con, String IP2, DateTime LC)
{
Name = n;
npID = id;
Number = num;
Level = l;
dbID = cind;
if (lo == null)
LastOffense = String.Empty;
else
LastOffense = lo;
Connections = con;
IP = IP2;
Warnings = 0;
Masked = false;
LastConnection = LC;
} }
public String getName() public String getName()
@ -171,9 +201,24 @@ namespace IW4MAdmin
return IP; return IP;
} }
public String getLastConnection()
{
TimeSpan Elapsed = DateTime.Now - LastConnection;
if (Elapsed.Minutes < 60)
return Elapsed.Minutes + " minutes";
if (Elapsed.Hours <= 24)
return Elapsed.Hours + " hours";
if (Elapsed.Days <= 365)
return Elapsed.Days + " days";
else
return "a very long time";
}
public void updateName(String n) public void updateName(String n)
{ {
Name = n; if (n.Trim() != String.Empty)
Name = n;
} }
public void updateIP(String I) public void updateIP(String I)
@ -209,7 +254,12 @@ namespace IW4MAdmin
public void Ban(String Message, Player Sender) public void Ban(String Message, Player Sender)
{ {
lastEvent.Owner.Ban(Message, this, Sender); lastEvent.Owner.Ban(Message, this, Sender);
}
public void Alert()
{
lastEvent.Owner.Alert(this);
} }
//should be moved to utils //should be moved to utils
@ -233,11 +283,13 @@ namespace IW4MAdmin
private int dbID; private int dbID;
public int Connections; public int Connections;
private String IP; private String IP;
private DateTime LastConnection;
public Event lastEvent; public Event lastEvent;
public String LastOffense; public String LastOffense;
public int Warnings; public int Warnings;
public Stats stats; public Stats stats;
public Aliases Alias; public Aliases Alias;
public bool Masked;
} }
} }

View File

@ -1,234 +0,0 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
namespace IW4MAdmin
{
public class SQLiteDatabase
{
String dbConnection;
/// <summary>
/// Default Constructor for SQLiteDatabase Class.
/// </summary>
public SQLiteDatabase(String file)
{
dbConnection = "Data Source=" + file;
}
/// <summary>
/// Single Param Constructor for specifying the DB file.
/// </summary>
/// <param name="inputFile">The File containing the DB</param>
public SQLiteDatabase(String inputFile)
{
dbConnection = String.Format("Data Source={0}", inputFile);
}
/// <summary>
/// Single Param Constructor for specifying advanced connection options.
/// </summary>
/// <param name="connectionOpts">A dictionary containing all desired options and their values</param>
public SQLiteDatabase(Dictionary<String, String> connectionOpts)
{
String str = "";
foreach (KeyValuePair<String, String> row in connectionOpts)
{
str += String.Format("{0}={1}; ", row.Key, row.Value);
}
str = str.Trim().Substring(0, str.Length - 1);
dbConnection = str;
}
/// <summary>
/// Allows the programmer to run a query against the Database.
/// </summary>
/// <param name="sql">The SQL to run</param>
/// <returns>A DataTable containing the result set.</returns>
public DataTable GetDataTable(string sql)
{
DataTable dt = new DataTable();
try
{
SQLiteConnection cnn = new SQLiteConnection(dbConnection);
cnn.Open();
SQLiteCommand mycommand = new SQLiteCommand(cnn);
mycommand.CommandText = sql;
SQLiteDataReader reader = mycommand.ExecuteReader();
dt.Load(reader);
reader.Close();
cnn.Close();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
throw new Exception(e.Message);
}
return dt;
}
/// <summary>
/// Allows the programmer to interact with the database for purposes other than a query.
/// </summary>
/// <param name="sql">The SQL to be run.</param>
/// <returns>An Integer containing the number of rows updated.</returns>
public int ExecuteNonQuery(string sql)
{
SQLiteConnection cnn = new SQLiteConnection(dbConnection);
cnn.Open();
SQLiteCommand mycommand = new SQLiteCommand(cnn);
mycommand.CommandText = sql;
int rowsUpdated = mycommand.ExecuteNonQuery();
cnn.Close();
return rowsUpdated;
}
/// <summary>
/// Allows the programmer to retrieve single items from the DB.
/// </summary>
/// <param name="sql">The query to run.</param>
/// <returns>A string.</returns>
public string ExecuteScalar(string sql)
{
SQLiteConnection cnn = new SQLiteConnection(dbConnection);
cnn.Open();
SQLiteCommand mycommand = new SQLiteCommand(cnn);
mycommand.CommandText = sql;
object value = mycommand.ExecuteScalar();
cnn.Close();
if (value != null)
{
return value.ToString();
}
return "";
}
/// <summary>
/// Allows the programmer to easily update rows in the DB.
/// </summary>
/// <param name="tableName">The table to update.</param>
/// <param name="data">A dictionary containing Column names and their new values.</param>
/// <param name="where">The where clause for the update statement.</param>
/// <returns>A boolean true or false to signify success or failure.</returns>
public bool Update(String tableName, Dictionary<String, String> data, String where)
{
String vals = "";
Boolean returnCode = true;
if (data.Count >= 1)
{
foreach (KeyValuePair<String, String> val in data)
{
vals += String.Format(" {0} = '{1}',", val.Key.ToString(), val.Value.ToString());
}
vals = vals.Substring(0, vals.Length - 1);
}
try
{
this.ExecuteNonQuery(String.Format("update {0} set {1} where {2};", tableName, vals, where));
}
catch( Exception fail)
{
Console.WriteLine(fail.Message);
returnCode = false;
}
return returnCode;
}
/// <summary>
/// Allows the programmer to easily delete rows from the DB.
/// </summary>
/// <param name="tableName">The table from which to delete.</param>
/// <param name="where">The where clause for the delete.</param>
/// <returns>A boolean true or false to signify success or failure.</returns>
public bool Delete(String tableName, String where)
{
Boolean returnCode = true;
try
{
this.ExecuteNonQuery(String.Format("delete from {0} where {1};", tableName, where));
}
catch (Exception fail)
{
Console.WriteLine(fail.Message);
returnCode = false;
}
return returnCode;
}
/// <summary>
/// Allows the programmer to easily insert into the DB
/// </summary>
/// <param name="tableName">The table into which we insert the data.</param>
/// <param name="data">A dictionary containing the column names and data for the insert.</param>
/// <returns>A boolean true or false to signify success or failure.</returns>
public bool Insert(String tableName, Dictionary<String, String> data)
{
String columns = "";
String values = "";
Boolean returnCode = true;
foreach (KeyValuePair<String, String> val in data)
{
columns += String.Format(" {0},", val.Key.ToString());
values += String.Format(" '{0}',", val.Value);
}
columns = columns.Substring(0, columns.Length - 1);
values = values.Substring(0, values.Length - 1);
try
{
this.ExecuteNonQuery(String.Format("insert into {0}({1}) values({2});", tableName, columns, values));
}
catch (Exception fail)
{
Console.WriteLine(fail.Message);
returnCode = false;
}
return returnCode;
}
/// <summary>
/// Allows the programmer to easily delete all data from the DB.
/// </summary>
/// <returns>A boolean true or false to signify success or failure.</returns>
public bool ClearDB()
{
DataTable tables;
try
{
tables = this.GetDataTable("select NAME from SQLITE_MASTER where type='table' order by NAME;");
foreach (DataRow table in tables.Rows)
{
this.ClearTable(table["NAME"].ToString());
}
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Allows the user to easily clear all data from a specific table.
/// </summary>
/// <param name="table">The name of the table to clear.</param>
/// <returns>A boolean true or false to signify success or failure.</returns>
public bool ClearTable(String table)
{
try
{
this.ExecuteNonQuery(String.Format("delete from {0};", table));
return true;
}
catch
{
return false;
}
}
}
}

View File

@ -36,6 +36,8 @@ namespace IW4MAdmin
HB = new Heartbeat(this); HB = new Heartbeat(this);
Macros = new Dictionary<String, Object>(); Macros = new Dictionary<String, Object>();
Reports = new List<Report>(); Reports = new List<Report>();
Skills = new Moserware.TrueSkill();
statusPlayers = new Dictionary<string, Player>();
nextMessage = 0; nextMessage = 0;
initCommands(); initCommands();
initMacros(); initMacros();
@ -55,6 +57,11 @@ namespace IW4MAdmin
return mapname; return mapname;
} }
public String getGametype()
{
return Gametype;
}
//Returns current server IP set by `net_ip` -- *STRING* //Returns current server IP set by `net_ip` -- *STRING*
public String getIP() public String getIP()
{ {
@ -101,111 +108,178 @@ namespace IW4MAdmin
return Bans; return Bans;
} }
public void getAliases(List<Player> returnPlayers, Player Origin)
{
if (Origin == null)
return;
List<Aliases> aliasAliases = new List<Aliases>();
Aliases currentAliases = aliasDB.getPlayer(Origin.getDBID());
if (currentAliases == null)
Log.Write("No aliases found for " + Origin.getName(), Log.Level.Debug);
foreach (String IP in currentAliases.getIPS())
{
List<Aliases> tmp = aliasDB.getPlayer(IP);
if (tmp != null)
aliasAliases = tmp;
foreach (Aliases a in aliasAliases)
{
if (a == null)
continue;
Player aliasPlayer = clientDB.getPlayer(a.getNumber());
if (aliasPlayer != null)
{
aliasPlayer.Alias = a;
if (returnPlayers.Exists(p => p.getDBID() == aliasPlayer.getDBID()) == false)
{
returnPlayers.Add(aliasPlayer);
getAliases(returnPlayers, aliasPlayer);
}
}
}
}
}
//Add player object p to `players` list //Add player object p to `players` list
public bool addPlayer(Player P) public bool addPlayer(Player P)
{ {
if (P.getClientNum() < 0 || P.getClientNum() > (players.Count-1)) // invalid index
return false;
if (players[P.getClientNum()] != null && players[P.getClientNum()].getID() == P.getID()) // if someone has left and a new person has taken their spot between polls
return true;
Log.Write("Client slot #" + P.getClientNum() + " now reserved", Log.Level.Debug);
#if DEBUG == false #if DEBUG == false
try try
#endif #endif
{ {
if (clientDB.getPlayer(P.getID(), P.getClientNum()) == null)
{
clientDB.addPlayer(P);
Player New = clientDB.getPlayer(P.getID(), P.getClientNum());
statDB.addPlayer(New);
}
//messy way to prevent loss of last event
Player NewPlayer = clientDB.getPlayer(P.getID(), P.getClientNum()); Player NewPlayer = clientDB.getPlayer(P.getID(), P.getClientNum());
NewPlayer.stats = statDB.getStats(NewPlayer.getDBID());
if (NewPlayer.stats == null) //For safety if (NewPlayer == null) // first time connecting
{ {
Log.Write("Client slot #" + P.getClientNum() + " first time connecting", Log.Level.All);
clientDB.addPlayer(P);
NewPlayer = clientDB.getPlayer(P.getID(), P.getClientNum());
aliasDB.addPlayer(new Aliases(NewPlayer.getDBID(), NewPlayer.getName(), NewPlayer.getIP()));
statDB.addPlayer(NewPlayer); statDB.addPlayer(NewPlayer);
NewPlayer.stats = statDB.getStats(NewPlayer.getDBID());
} }
if (P.lastEvent == null) NewPlayer.updateName(P.getName().Trim());
NewPlayer.stats = statDB.getStats(NewPlayer.getDBID());
NewPlayer.Alias = aliasDB.getPlayer(NewPlayer.getDBID());
if (NewPlayer.Alias == null)
{
aliasDB.addPlayer(new Aliases(NewPlayer.getDBID(), NewPlayer.getName(), NewPlayer.getIP()));
NewPlayer.Alias = aliasDB.getPlayer(NewPlayer.getDBID());
}
// try not to crash if no stats!
if (P.lastEvent == null || P.lastEvent.Owner == null)
NewPlayer.lastEvent = new Event(Event.GType.Say, null, NewPlayer, null, this); // this is messy but its throwing an error when they've started it too late NewPlayer.lastEvent = new Event(Event.GType.Say, null, NewPlayer, null, this); // this is messy but its throwing an error when they've started it too late
else else
NewPlayer.lastEvent = P.lastEvent; NewPlayer.lastEvent = P.lastEvent;
if (players[NewPlayer.getClientNum()] == null)
{
bool updated = false;
while (!updated) //Sometimes we get a issue when a player disconnects and it doesn't register
{
try
{
P.updateIP(IPS[P.getID()].Trim());
NewPlayer.updateIP(P.getIP());
Log.Write("Sucessfully updated " + NewPlayer.getName() + "'s IP to " + P.getIP(), Log.Level.Debug);
updated = true;
}
catch
{
Log.Write("Waiting for " + P.getName() + "'s IP...", Log.Level.Debug);
Utilities.Wait(2);
}
}
if (aliasDB.getPlayer(NewPlayer.getDBID()) == null)
aliasDB.addPlayer(new Aliases(NewPlayer.getDBID(), P.getName(), P.getIP()));
NewPlayer.Alias = aliasDB.getPlayer(NewPlayer.getDBID());
if ((NewPlayer.Alias.getNames().Find(m => m.Equals(P.getName()))) == null || NewPlayer.getName() == null || NewPlayer.getName() == String.Empty) // lets check aliases
{ if ((NewPlayer.Alias.getNames().Find(m => m.Equals(P.getName()))) == null || NewPlayer.getName() == null || NewPlayer.getName() == String.Empty)
Log.Write("Connecting player has new alias -- " + P.getName() + " previous was: " + NewPlayer.getName(), Log.Level.Debug); {
NewPlayer.updateName(P.getName()); NewPlayer.updateName(P.getName().Trim());
NewPlayer.Alias.addName(P.getName()); NewPlayer.Alias.addName(NewPlayer.getName());
aliasDB.updatePlayer(NewPlayer.Alias); }
}
// and ips
if (NewPlayer.Alias.getIPS().Find(i => i.Equals(P.getIP())) == null || P.getIP() == null || P.getIP() == String.Empty)
{
NewPlayer.updateIP(P.getIP());
NewPlayer.Alias.addIP(P.getIP());
}
if (NewPlayer.Alias.getIPS().Find(i => i.Equals(P.getIP())) == null || P.getIP() == null || P.getIP() == String.Empty) aliasDB.updatePlayer(NewPlayer.Alias);
{ clientDB.updatePlayer(NewPlayer);
Log.Write("Connecting player has new IP - " + P.getIP(), Log.Level.Debug);
NewPlayer.updateIP(P.getIP());
NewPlayer.Alias.addIP(P.getIP());
aliasDB.updatePlayer(NewPlayer.Alias);
}
clientDB.updatePlayer(NewPlayer); List<Player> newPlayerAliases = new List<Player>();
NewPlayer.lastEvent.Owner = this; // cuz crashes getAliases(newPlayerAliases, NewPlayer);
Ban B = isBanned(NewPlayer); foreach (Player aP in newPlayerAliases)
if (B != null || NewPlayer.getLevel() == Player.Permission.Banned) {
if (aP == null)
continue;
String Reason = String.Empty;
String Message = String.Empty;
if (NewPlayer.getLevel() == Player.Permission.Banned)
{ {
Log.Write("Banned client " + P.getName() + " trying to connect...", Log.Level.Debug); Log.Write("Banned client " + P.getName() + " trying to connect...", Log.Level.Debug);
string Reason = String.Empty;
if (B != null) if (aP.getLastO() != null)
Reason = B.getReason(); Message = "^7Player Kicked: Previously banned for ^5" + aP.getLastO() + " ^7(appeal at " + Website+ ")";
else else
Reason = P.LastOffense; Message = "Player Kicked: Previous Ban";
String Message = "^1Player Kicked: ^7Previously Banned for ^5" + Reason;
NewPlayer.Kick(Message); NewPlayer.Kick(Message);
if (players[P.getClientNum()] != null) if (players[NewPlayer.getClientNum()] != null)
players[P.getClientNum()] = null; {
lock (players)
{
players[NewPlayer.getClientNum()] = null;
}
}
return true; return true;
} }
players[NewPlayer.getClientNum()] = null; Ban B = isBanned(aP);
if (B != null && aP != null)
{
Log.Write("Banned alias " + aP.getName() + " trying to connect...", Log.Level.Debug);
aP.lastEvent = NewPlayer.lastEvent;
aP.LastOffense = "Evading";
if (B.getReason() != null)
NewPlayer.Ban("^7Previously Banned: ^5" + B.getReason() + " ^7(appeal at " + Website + ")", NewPlayer);
else
NewPlayer.Ban("^7Previous Ban", NewPlayer);
lock (players)
{
if (players[NewPlayer.getClientNum()] != null)
players[NewPlayer.getClientNum()] = null;
}
return true;
}
}
lock (players)
{
players[NewPlayer.getClientNum()] = null; // just in case we have shit in the way
players[NewPlayer.getClientNum()] = NewPlayer; players[NewPlayer.getClientNum()] = NewPlayer;
}
#if DEBUG == FALSE #if DEBUG == FALSE
NewPlayer.Tell("Welcome ^5" + NewPlayer.getName() + " ^7this is your ^5" + Utilities.timesConnected(NewPlayer.getConnections()) + " ^7time connecting!"); NewPlayer.Tell("Welcome ^5" + NewPlayer.getName() + " ^7this is your ^5" + Utilities.timesConnected(NewPlayer.getConnections()) + " ^7time connecting!");
#endif #endif
Log.Write("Client " + NewPlayer.getName() + " connecting...", Log.Level.Debug); Log.Write("Client " + NewPlayer.getName() + " connecting...", Log.Level.Debug);
clientnum++;
if (NewPlayer.getLevel() == Player.Permission.Flagged) if (NewPlayer.getLevel() == Player.Permission.Flagged)
ToAdmins("^1NOTICE: ^7Flagged player ^5" + NewPlayer.getName() + "^7 has joined!"); ToAdmins("^1NOTICE: ^7Flagged player ^5" + NewPlayer.getName() + "^7 has joined!");
}
if (NewPlayer.getLevel() > Player.Permission.Moderator)
NewPlayer.Tell("There are ^5" + Reports.Count + " ^7recent reports!");
return true; return true;
} }
@ -221,115 +295,78 @@ namespace IW4MAdmin
//Remove player by CLIENT NUMBER //Remove player by CLIENT NUMBER
public bool removePlayer(int cNum) public bool removePlayer(int cNum)
{ {
if (cNum >= 0 && cNum < 18) if (cNum >= 0 && cNum < players.Count)
{ {
Log.Write("Updating stats for " + players[cNum].getName(), Log.Level.Debug); Player Leaving = players[cNum];
statDB.updatePlayer(players[cNum]); Leaving.Connections++;
clientDB.updatePlayer(Leaving);
statDB.updatePlayer(Leaving);
Log.Write("Client at " + cNum + " disconnecting...", Log.Level.Debug); Log.Write("Client at " + cNum + " disconnecting...", Log.Level.Debug);
players[cNum] = null; lock (players)
clientnum--; {
players[cNum] = null;
}
clientnum = statusPlayers.Count;
return true; return true;
} }
else else
{ {
Log.Write("Client disconnecting has an invalid client index!", Log.Level.Debug); Log.Write("Client disconnecting has an invalid client index!", Log.Level.Debug);
clientnum = statusPlayers.Count;
return false; return false;
} }
} }
//Get a client from players list by by log line. If create = true, it will return a new player object
public Player clientFromLine(String[] line, int name_pos, bool create)
{
string Name = line[name_pos].ToString().Trim();
if (create)
{
Player C = new Player(Name, line[1].ToString(), Convert.ToInt16(line[2]), 0);
return C;
}
else
{
foreach (Player P in players)
{
if (P == null)
continue;
if (line[1].Trim() == P.getID())
return P;
}
Log.Write("Could not find player but player is in server. Lets try to manually add (looks like you didn't start me on an empty server)", Log.Level.All);
players[Convert.ToInt16(line[2])] = null;
addPlayer(new Player(Name, line[1].ToString().Trim(), Convert.ToInt16(line[2]), 0));
return players[Convert.ToInt16(line[2])];
}
}
//Should be client from Name ( returns client in players list by name )
public Player clientFromLine(String Name)
{
foreach (Player P in players)
{
if (P == null)
continue;
if (P.getName().ToLower().Contains(Name.ToLower()))
return P;
}
return null;
}
//Another version of client from line, written for the line created by a kill or death event //Another version of client from line, written for the line created by a kill or death event
public Player clientFromLineArr(String[] L, bool kill) public Player clientFromEventLine(String[] L, int cIDPos)
{ {
if (L.Length < 7) if (L.Length < cIDPos)
{ {
Log.Write("Line sent for client creation is not long enough!", Log.Level.Debug); Log.Write("Line sent for client creation is not long enough!", Log.Level.Debug);
return null; return null;
} }
if (kill) int pID = -1;
int.TryParse(L[cIDPos].Trim(), out pID);
if (pID < 0 || pID > 17)
{ {
foreach (Player P in players) Log.Write("Error event player index " + pID + " is out of bounds!", Log.Level.Debug);
{ Log.Write(String.Join(";", L), Log.Level.Debug);
if (P == null) return null;
continue;
if (P.getName().ToLower().Contains(L[8].Trim()))
return P;
}
String killerName = L[8].Trim();
String killerGUID = L[5].Trim();
int killerID = -1;
int.TryParse(L[6], out killerID);
Player newPlayer = new Player(killerName, killerGUID, killerID, 0);
addPlayer(newPlayer);
return players[newPlayer.getClientNum()];
} }
else else
{ {
foreach (Player P in players) Player P = null;
try
{
P = players[pID];
return P;
}
catch (Exception)
{
Log.Write("Client index is invalid - " + pID, Log.Level.Debug);
Log.Write(L.ToString(), Log.Level.Debug);
return null;
}
}
}
public Player clientFromName(String pName)
{
lock (players)
{
foreach (var P in players)
{ {
if (P == null) if (P != null && P.getName().ToLower().Contains(pName.ToLower()))
continue;
if (P.getName().ToLower().Contains(L[4].Trim()))
return P; return P;
} }
String victimName = L[4].Trim();
String victimGUID = L[1].Trim();
int victimID = -1;
int.TryParse(L[2].Trim(), out victimID);
Player newPlayer = new Player(victimName, victimGUID, victimID, 0);
addPlayer(newPlayer);
return players[newPlayer.getClientNum()];
} }
return null;
} }
//Check ban list for every banned player and return ban if match is found //Check ban list for every banned player and return ban if match is found
@ -349,9 +386,6 @@ namespace IW4MAdmin
if (B.getIP() == null || C.getIP() == null) if (B.getIP() == null || C.getIP() == null)
continue; continue;
if (C.Alias.getIPS().Find(f => f.Equals(B.getIP())) != null)
return B;
if (C.getIP() == B.getIP()) if (C.getIP() == B.getIP())
return B; return B;
} }
@ -409,7 +443,7 @@ namespace IW4MAdmin
} }
else else
E.Target = clientFromLine(Args[0]); E.Target = clientFromName(Args[0]);
if (E.Target == null) if (E.Target == null)
{ {
@ -528,7 +562,7 @@ namespace IW4MAdmin
if (lines[count] == oldLines[oldLines.Length - 1]) if (lines[count] == oldLines[oldLines.Length - 1])
continue; continue;
if (lines[count].Length < 10) //Not a needed line if (lines[count].Length < 10) // its not a needed line
continue; continue;
else else
@ -574,8 +608,55 @@ namespace IW4MAdmin
int timesFailed = 0; int timesFailed = 0;
while (isRunning) while (isRunning)
{ {
IPS = Utilities.IPFromStatus(RCON.addRCON("status")); lock (statusPlayers)
while (IPS == null) {
String[] Response = RCON.addRCON("status");
if (Response != null)
statusPlayers = Utilities.playersFromStatus(Response);
}
if (statusPlayers != null)
{
lastPoll = DateTime.Now;
timesFailed = 0;
if (statusPlayers.Count > clientnum)
{
lock (statusPlayers)
{
foreach (var P in statusPlayers.Values)
{
if (P == null)
continue;
if (!addPlayer(P))
Log.Write("Error adding " + P.getName() + " at client slot #" + P.getClientNum(), Log.Level.Debug);
}
}
}
else if (statusPlayers.Count < clientnum)
{
List<Player> toRemove = new List<Player>();
foreach (Player P in players)
{
if (P == null)
continue;
Player Matching;
statusPlayers.TryGetValue(P.getID(), out Matching);
if (Matching == null) // they are no longer with us
toRemove.Add(P);
}
foreach (Player Removing in toRemove) // cuz cant modify collections
removePlayer(Removing.getClientNum());
}
clientnum = statusPlayers.Count;
}
else
{ {
timesFailed++; timesFailed++;
Log.Write("Server appears to be offline - " + timesFailed, Log.Level.Debug); Log.Write("Server appears to be offline - " + timesFailed, Log.Level.Debug);
@ -584,32 +665,10 @@ namespace IW4MAdmin
{ {
Log.Write("Max offline attempts reached. Reinitializing RCON connection.", Log.Level.Debug); Log.Write("Max offline attempts reached. Reinitializing RCON connection.", Log.Level.Debug);
RCON.Reset(); RCON.Reset();
timesFailed = 0;
}
Utilities.Wait(10); // cut the time in half to make sure it isn't changing a map
IPS = Utilities.IPFromStatus(RCON.addRCON("status"));
}
Log.Write("Server responded to status query!", Log.Level.Debug);
timesFailed = 0;
foreach(Player P in players) //Ensures uniformity between server and admin players
{
if (P == null)
continue;
String IP = String.Empty;
IPS.TryGetValue(P.getID(), out IP);
if (IP == String.Empty)
{
Log.Write("Invalid player detected, quit event must have been skipped!", Log.Level.All);
removePlayer(P.getClientNum());
} }
} }
lastPoll = DateTime.Now; Utilities.Wait(15);
Utilities.Wait(20); // don't want to overload the server
} }
} }
@ -654,7 +713,7 @@ namespace IW4MAdmin
try try
{ {
Website = infoResponseDict["_Website"]; Website = infoResponseDict["_website"];
} }
catch (Exception E) catch (Exception E)
{ {
@ -747,7 +806,7 @@ namespace IW4MAdmin
logPath = Basepath + '\\' + "m2demo" + '\\' + log; logPath = Basepath + '\\' + "m2demo" + '\\' + log;
else else
logPath = Basepath + '\\' + Mod + '\\' + log; logPath = Basepath + '\\' + Mod + '\\' + log;
//#if DEBUG == FALSE && System.Environment.GetEnvironmentVariable("COMPUTERNAME") != "michael-surface"
if (!File.Exists(logPath)) if (!File.Exists(logPath))
{ {
Log.Write("Gamelog does not exist!", Log.Level.All); Log.Write("Gamelog does not exist!", Log.Level.All);
@ -755,6 +814,7 @@ namespace IW4MAdmin
} }
logFile = new file(logPath); logFile = new file(logPath);
//#endif
Log.Write("Log file is " + logPath, Log.Level.Debug); Log.Write("Log file is " + logPath, Log.Level.Debug);
//get players ip's //get players ip's
@ -765,7 +825,6 @@ namespace IW4MAdmin
return false; return false;
} }
IPS = Utilities.IPFromStatus(p);
lastPoll = DateTime.Now; lastPoll = DateTime.Now;
#if DEBUG #if DEBUG
@ -774,6 +833,7 @@ namespace IW4MAdmin
System.IO.Stream ftpStream = tmp.GetResponse().GetResponseStream(); System.IO.Stream ftpStream = tmp.GetResponse().GetResponseStream();
String ftpLog = new StreamReader(ftpStream).ReadToEnd();*/ String ftpLog = new StreamReader(ftpStream).ReadToEnd();*/
//logPath = "games_old.log"; //logPath = "games_old.log";
//logFile = new file("C:\\Users\\Michael\\text.txt");
#endif #endif
return true; return true;
} }
@ -787,14 +847,13 @@ namespace IW4MAdmin
//Process any server event //Process any server event
public bool processEvent(Event E) public bool processEvent(Event E)
{ {
// /*if (E.Type == Event.GType.Connect)
if (E.Type == Event.GType.Connect)
{ {
if (E.Origin == null) if (E.Origin == null)
Log.Write("Connect event triggered, but no client is detected!", Log.Level.Debug); Log.Write("Connect event triggered, but no client is detected!", Log.Level.Debug);
addPlayer(E.Origin); addPlayer(E.Origin);
return true; return true;
} }*/
if (E.Type == Event.GType.Disconnect) if (E.Type == Event.GType.Disconnect)
{ {
@ -804,10 +863,7 @@ namespace IW4MAdmin
return false; return false;
} }
E.Origin.Connections++; removePlayer(E.Origin.getClientNum());
clientDB.updatePlayer(E.Origin);
removePlayer(E.Origin.getClientNum());
return true; return true;
} }
@ -837,30 +893,27 @@ namespace IW4MAdmin
E.Target.stats = statDB.getStats(E.Target.getDBID()); E.Target.stats = statDB.getStats(E.Target.getDBID());
} }
Log.Write(E.Origin.getName() + " killed " + E.Target.getName() + " with a " + E.Data, Log.Level.Debug);
if (E.Origin != E.Target) if (E.Origin != E.Target)
{ {
E.Origin.stats.Kills++; E.Origin.stats.Kills += 1;
E.Origin.stats.updateKDR(); E.Origin.stats.updateKDR();
E.Origin.stats.lastMew = TrueSkill.calculateWinnerMu(E.Origin.stats, E.Target.stats); E.Target.stats.Deaths += 1;
E.Origin.stats.lastSigma = TrueSkill.calculateWinnerSigma(E.Origin.stats, E.Target.stats);
E.Origin.stats.updateSkill();
E.Target.stats.Deaths++;
E.Target.stats.updateKDR(); E.Target.stats.updateKDR();
E.Target.stats.lastMew = TrueSkill.calculateLoserMu(E.Target.stats, E.Origin.stats); Skills.updateNewSkill(E.Origin, E.Target);
E.Target.stats.lastSigma = TrueSkill.calculateLoserSigma(E.Target.stats, E.Origin.stats); E.Owner.statDB.updatePlayer(E.Origin);
E.Owner.statDB.updatePlayer(E.Target);
totalKills++; totalKills++;
Log.Write(E.Origin.getName() + " killed " + E.Target.getName() + " with a " + E.Data, Log.Level.Debug);
} }
else //Suicide else //Suicide
{ {
E.Origin.stats.Deaths++; E.Origin.stats.Deaths++;
E.Origin.stats.updateKDR(); E.Origin.stats.updateKDR();
Log.Write(E.Origin.getName() + " suicided...", Log.Level.Debug);
} }
} }
@ -902,6 +955,7 @@ namespace IW4MAdmin
C.Execute(E); C.Execute(E);
return true; return true;
} }
else else
{ {
Log.Write("Player didn't properly enter command - " + E.Origin.getName(), Log.Level.Debug); Log.Write("Player didn't properly enter command - " + E.Origin.getName(), Log.Level.Debug);
@ -917,7 +971,7 @@ namespace IW4MAdmin
if (E.Type == Event.GType.MapChange) if (E.Type == Event.GType.MapChange)
{ {
Log.Write("New map loaded", Log.Level.Debug); Log.Write("New map loaded - " + clientnum + " active players", Log.Level.Debug);
String[] statusResponse = E.Data.Split('\\'); String[] statusResponse = E.Data.Split('\\');
if (statusResponse.Length >= 15 && statusResponse[13] == "mapname") if (statusResponse.Length >= 15 && statusResponse[13] == "mapname")
mapname = maps.Find(m => m.Name.Equals(statusResponse[14])).Alias; //update map for heartbeat mapname = maps.Find(m => m.Name.Equals(statusResponse[14])).Alias; //update map for heartbeat
@ -969,17 +1023,21 @@ namespace IW4MAdmin
public void Tell(String Message, Player Target) public void Tell(String Message, Player Target)
{ {
RCON.addRCON("tell " + Target.getClientNum() + " " + Message + "^7"); if (Target.getClientNum() > -1)
RCON.addRCON("tell " + Target.getClientNum() + " " + Message + "^7");
} }
public void Kick(String Message, Player Target) public void Kick(String Message, Player Target)
{ {
RCON.addRCON("clientkick " + Target.getClientNum() + " \"" + Message + "^7\""); if (Target.getClientNum() > -1)
RCON.addRCON("clientkick " + Target.getClientNum() + " \"" + Message + "^7\"");
} }
public void Ban(String Message, Player Target, Player Origin) public void Ban(String Message, Player Target, Player Origin)
{ {
RCON.addRCON("tempbanclient " + Target.getClientNum() + " \"" + Message + "^7\""); if (Target.getClientNum() > -1)
RCON.addRCON("tempbanclient " + Target.getClientNum() + " \"" + Message + "^7\"");
if (Origin != null) if (Origin != null)
{ {
Target.setLevel(Player.Permission.Banned); Target.setLevel(Player.Permission.Banned);
@ -987,6 +1045,21 @@ namespace IW4MAdmin
Bans.Add(newBan); Bans.Add(newBan);
clientDB.addBan(newBan); clientDB.addBan(newBan);
clientDB.updatePlayer(Target); clientDB.updatePlayer(Target);
lock (Reports) // threading seems to do something weird here
{
List<Report> toRemove = new List<Report>();
foreach (Report R in Reports)
{
if (R.Target.getID() == Target.getID())
toRemove.Add(R);
}
foreach (Report R in toRemove)
{
Reports.Remove(R);
Log.Write("Removing report for banned GUID -- " + R.Origin.getID(), Log.Level.Debug);
}
}
} }
} }
@ -1043,20 +1116,27 @@ namespace IW4MAdmin
public void ToAdmins(String message) public void ToAdmins(String message)
{ {
List<Player> admins = players; lock (players) // threading can modify list while we do this
foreach (Player P in admins)
{ {
if (P == null) foreach (Player P in players)
continue;
if (P.getLevel() > Player.Permission.User)
{ {
RCON.addRCON("admin_lastevent alert;" + P.getID() + ";0;mp_killstreak_nuclearstrike"); if (P == null)
P.Tell(message); continue;
if (P.getLevel() > Player.Permission.User)
{
P.Alert();
P.Tell(message);
}
} }
} }
} }
public void Alert(Player P)
{
RCON.addRCON("admin_lastevent alert;" + P.getID() + ";0;mp_killstreak_nuclearstrike");
}
//END //END
//THIS IS BAD BECAUSE WE DON"T WANT EVERYONE TO HAVE ACCESS :/ //THIS IS BAD BECAUSE WE DON"T WANT EVERYONE TO HAVE ACCESS :/
@ -1068,6 +1148,7 @@ namespace IW4MAdmin
private void initMacros() private void initMacros()
{ {
Macros = new Dictionary<String, Object>(); Macros = new Dictionary<String, Object>();
Macros.Add("WEBSITE", "nbsclan.org");
Macros.Add("WISDOM", Wisdom()); Macros.Add("WISDOM", Wisdom());
Macros.Add("TOTALPLAYERS", clientDB.totalPlayers()); Macros.Add("TOTALPLAYERS", clientDB.totalPlayers());
Macros.Add("TOTALKILLS", totalKills); Macros.Add("TOTALKILLS", totalKills);
@ -1185,7 +1266,11 @@ namespace IW4MAdmin
commands.Add(new Flag("flag", "flag a suspicious player and announce to admins on join . syntax !flag <player>:", "flag", Player.Permission.Moderator, 1, true)); commands.Add(new Flag("flag", "flag a suspicious player and announce to admins on join . syntax !flag <player>:", "flag", Player.Permission.Moderator, 1, true));
commands.Add(new _Report("report", "report a player for suspicious behaivor. syntax !report <player> <reason>", "rep", Player.Permission.User, 2, true)); commands.Add(new _Report("report", "report a player for suspicious behaivor. syntax !report <player> <reason>", "rep", Player.Permission.User, 2, true));
commands.Add(new Reports("reports", "get most recent reports. syntax !reports", "reports", Player.Permission.Moderator, 0, false)); commands.Add(new Reports("reports", "get most recent reports. syntax !reports", "reports", Player.Permission.Moderator, 0, false));
commands.Add(new _Tell("tell", "send onscreen message to player. synrax !tell <player> <message>", "t", Player.Permission.Moderator, 2, true)); commands.Add(new _Tell("tell", "send onscreen message to player. syntax !tell <player> <message>", "t", Player.Permission.Moderator, 2, true));
commands.Add(new Mask("mask", "hide your online presence from online admin list. syntax: !mask", "mask", Player.Permission.Administrator, 0, false));
commands.Add(new BanInfo("baninfo", "get information about a ban for a player. syntax: !baninfo <player>", "bi", Player.Permission.Moderator, 1, true));
commands.Add(new Alias("alias", "get past aliases and ips of a player. syntax: !alias <player>", "known", Player.Permission.Moderator, 1, true));
commands.Add(new _RCON("rcon", "send rcon command to server. syntax: !rcon <command>", "rcon", Player.Permission.Owner, 1, false));
} }
//Objects //Objects
@ -1221,10 +1306,12 @@ namespace IW4MAdmin
private String IW_Ver; private String IW_Ver;
private int maxClients; private int maxClients;
private Dictionary<String, Object> Macros; private Dictionary<String, Object> Macros;
private Moserware.TrueSkill Skills;
//Will probably move this later //Will probably move this later
private Dictionary<String, String> IPS; //public Dictionary<String, String> IPS;
public Dictionary<String, Player> statusPlayers;
public bool isRunning; public bool isRunning;
private DateTime lastPoll; private DateTime lastPoll;

View File

@ -1,55 +1,38 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Moserware.Skills.TrueSkill;
using IW4MAdmin;
namespace IW4MAdmin
namespace Moserware
{ {
class TrueSkill class TrueSkill
{ {
public static double calculateWinnerMu(Stats originStats, Stats targetStats) public TrueSkill()
{ {
double Beta = originStats.lastMew / 6; calculator = new TwoPlayerTrueSkillCalculator();
double lastSkill = Gaussian(originStats.lastMew, originStats.lastSigma); gInfo = Skills.GameInfo.DefaultGameInfo;
double c = Math.Sqrt((2 * Beta * Beta) + (originStats.lastSigma * originStats.lastSigma) + (targetStats.lastSigma * targetStats.lastSigma)); }
double newMew = originStats.lastMew + ((originStats.lastSigma) * (originStats.lastSigma) / c) * ((originStats.lastMew - targetStats.lastMew) / c);
return newMew;
}
public static double calculateLoserMu(Stats originStats, Stats targetStats) public void updateNewSkill(Player P1, Player P2)
{ {
double Beta = originStats.lastMew / 6; var player1 = new Skills.Player(P1.getDBID());
double lastSkill = Gaussian(originStats.lastMew, originStats.lastSigma); var player2 = new Skills.Player(P2.getDBID());
double c = Math.Sqrt( (2 * Beta * Beta) + (originStats.lastSigma * originStats.lastSigma) + (targetStats.lastSigma * targetStats.lastSigma));
double newMew = originStats.lastMew - ((targetStats.lastSigma) * (targetStats.lastSigma) / c) * ((originStats.lastMew - targetStats.lastMew) / c);
return newMew;
}
public static double calculateLoserSigma(Stats originStats, Stats targetStats) var team1 = new Skills.Team(player1, P1.stats.Rating);
{ var team2 = new Skills.Team(player2, P2.stats.Rating);
double Beta = originStats.lastMew / 6;
double lastSkill = Gaussian(originStats.lastMew, originStats.lastSigma);
double c = ((2 * Beta * Beta) + originStats.lastSigma * originStats.lastSigma) + (targetStats.lastSigma * targetStats.lastSigma);
double newSigma = originStats.lastSigma * ( 1 - (targetStats.lastSigma) * (targetStats.lastSigma) / c) * ((originStats.lastMew - targetStats.lastMew) / c);
return newSigma;
}
public static double calculateWinnerSigma(Stats originStats, Stats targetStats) var newRatings = calculator.CalculateNewRatings(gInfo, Skills.Teams.Concat(team1, team2), 1, 2);
{
double Beta = originStats.lastMew / 6;
double lastSkill = Gaussian(originStats.lastMew, originStats.lastSigma);
double c = ((2 * Beta * Beta) + originStats.lastSigma * originStats.lastSigma) + (targetStats.lastSigma * targetStats.lastSigma);
double newSigma = originStats.lastSigma * (1 - (originStats.lastSigma) * (originStats.lastSigma) / c) * ((originStats.lastMew - targetStats.lastMew) / c);
return newSigma;
}
P1.stats.Rating = newRatings[player1];
P2.stats.Rating = newRatings[player2];
P1.stats.Skill = Math.Round(P1.stats.Rating.ConservativeRating, 3)*10;
//https://gist.github.com/tansey/1444070 P2.stats.Skill = Math.Round(P2.stats.Rating.ConservativeRating, 3)*10;
public static double Gaussian( double mean, double stddev) }
{
private Skills.SkillCalculator calculator;
double y1 = Math.Sqrt(-2.0 * Math.Log(.5)) * Math.Cos(2.0 * Math.PI * .5); public Skills.GameInfo gInfo;
return y1 * stddev + mean;
}
} }
} }

View File

@ -94,9 +94,9 @@ namespace IW4MAdmin
case Player.Permission.Owner: case Player.Permission.Owner:
return "^5" + Player.Permission.Owner; return "^5" + Player.Permission.Owner;
case Player.Permission.User: case Player.Permission.User:
return "^3" + Player.Permission.User; return "^2" + Player.Permission.User;
default: default:
return "^2" + level; return "^3" + level;
} }
} }
@ -144,12 +144,135 @@ namespace IW4MAdmin
} }
public static String nameHTMLFormatted(Player P)
{
switch (P.getLevel())
{
case Player.Permission.User:
return "<span style='color:rgb(121, 194, 97)'>" + P.getName() + "</span>";
case Player.Permission.Moderator:
return "<span style='color:#e7b402'>" + P.getName() + "</span>";
case Player.Permission.Administrator:
return "<span style='color:#ec82de'>" + P.getName() + "</span>";
case Player.Permission.SeniorAdmin:
return "<span style='color:#2eb6bf'>" + P.getName() + "</span>";
case Player.Permission.Owner | Player.Permission.Creator:
return "<span style='color:rgb(38,120,230)'>" + P.getName() + "</span>";
default:
return "<i>" + P.getName() + "</i>";
}
}
public static String gametypeLocalized(String input)
{
switch (input)
{
case "dm":
return "Deathmatch";
case "war":
return "Team Deathmatch";
case "koth":
return "Headquarters";
case "ctf":
return "Capture The Flag";
case "dd":
return "Demolition";
case "dom":
return "Domination";
case "sab":
return "Sabotage";
case "sd":
return "Search & Destroy";
case "vip":
return "Very Important Person";
case "gtnw":
return "Global Thermonuclear War";
case "oitc":
return "One In The Chamber";
case "arena":
return "Arena";
case "dzone":
return "Drop Zone";
case "gg":
return "Gun Game";
case "snipe":
return "Sniping";
case "ss":
return "Sharp Shooter";
case "m40a3":
return "M40A3";
case "fo":
return "Face Off";
case "dmc":
return "Deathmatch Classic";
case "killcon":
return "Kill Confirmed";
case "oneflag":
return "One Flag CTF";
default:
return "Unknown";
}
}
public static Dictionary<String, Player> playersFromStatus(String[] Status)
{
Dictionary<String, Player> playerDictionary = new Dictionary<String, Player>();
if (Status == null) // looks like we didn't get a proper response
return null;
foreach (String S in Status)
{
String responseLine = S.Trim();
if (Regex.Matches(responseLine, @"\d+$", RegexOptions.IgnoreCase).Count > 0 && responseLine.Length > 72) // its a client line!
{
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
String cName = stripColors(responseLine.Substring(46, 18)).Trim();
String npID = responseLine.Substring(29, 17).Trim(); // DONT TOUCH PLZ
int cID = Convert.ToInt32(playerInfo[0]);
String cIP = responseLine.Substring(72,20).Trim().Split(':')[0];
Player P = new Player(cName, npID, cID, cIP);
try
{
playerDictionary.Add(npID, P);
}
catch(Exception E)
{
/// need to handle eventually
Console.WriteLine("Error handling player add -- " + E.Message);
continue;
}
}
}
return playerDictionary;
}
public static String DateTimeSQLite(DateTime datetime) public static String DateTimeSQLite(DateTime datetime)
{ {
string dateTimeFormat = "{0}-{1}-{2} {3}:{4}:{5}.{6}"; 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); return string.Format(dateTimeFormat, datetime.Year, datetime.Month, datetime.Day, datetime.Hour, datetime.Minute, datetime.Second, datetime.Millisecond);
} }
public static String timePassed(DateTime start)
{
TimeSpan Elapsed = DateTime.Now - start;
if (Elapsed.Hours < 1 && Elapsed.Minutes < 60)
return Elapsed.Minutes + " minutes";
if (Elapsed.Days < 1 && Elapsed.Hours <= 24)
return Elapsed.Hours + " hours";
if (Elapsed.Days <= 365)
return Elapsed.Days + " days";
else
return "a very long time";
}
public static String timesConnected(int connection) public static String timesConnected(int connection)
{ {
String Prefix = String.Empty; String Prefix = String.Empty;
@ -168,8 +291,7 @@ namespace IW4MAdmin
case 3: case 3:
Prefix = "rd"; Prefix = "rd";
break; break;
} }
} }
switch (connection) switch (connection)

394
Admin/WebFront.cs Normal file
View File

@ -0,0 +1,394 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kayak;
using Kayak.Http;
using System.Net;
namespace IW4MAdmin_Web
{
class WebFront
{
public enum Page
{
main,
stats,
bans
}
public WebFront()
{
}
public void Init()
{
webSchedule = KayakScheduler.Factory.Create(new SchedulerDelegate());
webServer = KayakServer.Factory.CreateHttp(new RequestDelegate(), webSchedule);
using (webServer.Listen(new IPEndPoint(IPAddress.Any, 1624)))
{
// runs scheduler on calling thread. this method will block until
// someone calls Stop() on the scheduler.
webSchedule.Start();
}
}
private IScheduler webSchedule;
private IServer webServer;
}
static class Macro
{
static public String parsePagination(int server, int totalItems, int itemsPerPage, int currentPage, String Page)
{
StringBuilder output = new StringBuilder();
output.Append("<div id=pages>");
if ( currentPage > 0)
output.AppendFormat("<a href=/{0}/{1}/?{2}>PREV</a>", server, currentPage - 1, Page);
double totalPages = Math.Ceiling(((float)totalItems / itemsPerPage));
output.Append("<span id=pagination>" + (currentPage + 1) + "/" + totalPages + "</span>");
if ((currentPage + 1) < totalPages)
output.AppendFormat("<a href=/{0}/{1}/?{2}>NEXT</a>", server, currentPage + 1, Page);
output.Append("</div>");
return output.ToString();
}
static public String parseMacros(String input, WebFront.Page Page, int Pagination, int server)
{
StringBuilder buffer = new StringBuilder();
switch (input)
{
case "SERVERS":
var Servers = IW4MAdmin.Program.Servers;
for (int i = 0; i < Servers.Count; i++)
{
StringBuilder players = new StringBuilder();
if (Servers[i].getClientNum() < 1)
players.Append("<th>No Players</th>");
else
{
int count = 0;
foreach (IW4MAdmin.Player P in Servers[i].statusPlayers.Values)
{
if (count > 0 && count % 6 == 0)
players.Append("</tr><tr>");
players.AppendFormat("<td>{0}</td>", P.getName());
count++;
}
}
buffer.AppendFormat(@"<table cellpadding=0 cellspacing=0 class=server>
<tr>
<th class=server_title><span>{0}</span></th>
<th class=server_map><span>{1}</span></th>
<th class=server_players><span>{2}</span></th>
<th class=server_gametype><span>{3}</span></th>
<th><a href=/{4}/0/?stats>Stats</a></th>
<th><a href=/{4}/0/?bans>Bans</a></th>
</tr>
</table>
<table class=players>
<tr>
{5}
</tr>
</table>
<hr/>",
Servers[i].getName(), Servers[i].getMap(), Servers[i].getClientNum() + "/" + Servers[i].getMaxClients(), IW4MAdmin.Utilities.gametypeLocalized(Servers[i].getGametype()), i, players.ToString());
}
return buffer.ToString();
case "TITLE":
return "IW4M Administration";
case "BANS":
buffer.Append("<table cellspacing=0 class=bans>");
int range;
int start = Pagination*30 + 1;
if (IW4MAdmin.Program.Servers[0].Bans.Count <= 30)
range = IW4MAdmin.Program.Servers[0].Bans.Count - 1;
else if ((IW4MAdmin.Program.Servers[0].Bans.Count - start) < 30 )
range = (IW4MAdmin.Program.Servers[0].Bans.Count - start);
else
range = 30;
List<IW4MAdmin.Ban> Bans = IW4MAdmin.Program.Servers[0].Bans.GetRange(start, range);
buffer.Append("<h1 style=margin-top: 0;>{{TIME}}</h1><hr /><tr><th>Name</th><th style=text-align:left;>Offense</th><th style=text-align:left;>Banned By</th><th style='width: 175px; text-align:right;padding-right: 80px;'>Time</th></tr>");
if (Bans[0] != null)
buffer = buffer.Replace("{{TIME}}", "From " + IW4MAdmin.Utilities.timePassed(Bans[0].getTime()) + " ago" + " &mdash; " + IW4MAdmin.Program.Servers[0].Bans.Count + " total");
int cycleFix = 0;
for (int i = 0; i < Bans.Count; i++)
{
if (Bans[i] == null)
continue;
IW4MAdmin.Player P = IW4MAdmin.Program.Servers[0].clientDB.getPlayer(Bans[i].getID(), -1);
IW4MAdmin.Player B = IW4MAdmin.Program.Servers[0].clientDB.getPlayer(Bans[i].getBanner(), -1);
if (P == null)
P = new IW4MAdmin.Player("Unknown", "n/a", 0, 0, 0, "Unknown", 0, "");
if (B == null)
B = new IW4MAdmin.Player("Unknown", "n/a", 0, 0, 0, "Unknown", 0, "");
if (P.getLastO() == String.Empty)
P.LastOffense = "Evade";
if (P != null && B != null)
{
if (B.getID() == P.getID())
B.updateName("IW4MAdmin"); // shh it will all be over soon
String Prefix;
if (cycleFix % 2 == 0)
Prefix = "class=row-grey";
else
Prefix = "class=row-white";
buffer.AppendFormat("<tr {4}><td>{0}</th><td style='border-left: 3px solid #bbb; text-align:left;'>{1}</td><td style='border-left: 3px solid #bbb;text-align:left;'>{2}</td><td style='width: 175px; text-align:right;'>{3}</td></tr></div>", P.getName(), P.getLastO(), IW4MAdmin.Utilities.nameHTMLFormatted(B), Bans[i].getWhen(), Prefix);
cycleFix++;
}
}
buffer.Append("</table><hr/>");
buffer.Append(parsePagination(server, IW4MAdmin.Program.Servers[0].Bans.Count, 30, Pagination, "bans"));
return buffer.ToString();
case "PAGE":
buffer.Append("<div id=pages>");
return buffer.ToString();
case "STATS":
int totalStats = IW4MAdmin.Program.Servers[server].statDB.totalStats()-1;
buffer.Append("<h1 style='margin-top: 0;'>Starting at #{{TOP}}</h1><hr />");
buffer.Append("<table style='width:100%' cellspacing=0 class=stats>");
start = Pagination*30 + 1;
if (totalStats <= 30)
range = totalStats - 1;
else if ((totalStats - start) < 30 )
range = (totalStats - start);
else
range = 30;
List<IW4MAdmin.Stats> Stats = IW4MAdmin.Program.Servers[server].statDB.getMultipleStats(start, range);
buffer.Append("<tr><th style=text-align:left;>Name</th><th style=text-align:left;>Kills</th><th style=text-align:left;>Deaths</th><th style=text-align:left;>KDR</th><th style='width: 175px; text-align:right;'>Rating</th></tr>");
cycleFix = 0;
for (int i = 0; i < totalStats; i++)
{
if (i >= Stats.Count -1 || Stats[i] == null )
continue;
IW4MAdmin.Player P = IW4MAdmin.Program.Servers[server].clientDB.getPlayer(Stats[i].statIndex);
if (P == null)
continue;
P.stats = Stats[i];
if (P.stats != null)
{
String Prefix;
if (cycleFix % 2 == 0)
Prefix = "class=row-grey";
else
Prefix = "class=row-white";
buffer.AppendFormat("<tr {5}><td>{0}</th><td style='border-left: 3px solid #bbb; text-align:left;'>{1}</td><td style='border-left: 3px solid #bbb;text-align:left;'>{2}</td><td style='border-left: 3px solid #bbb;text-align:left;'>{3}</td><td style='width: 175px; text-align:right;'>{4}</td></tr></div>", P.getName(), P.stats.Kills, P.stats.Deaths, P.stats.KDR, P.stats.Skill, Prefix);
cycleFix++;
}
}
buffer.Append("</table><hr/>");
buffer.Append(parsePagination(server, totalStats, 30, Pagination, "stats"));
return buffer.ToString().Replace("{{TOP}}", (start).ToString());
default:
return input;
}
}
static public String findMacros(String input, int pageNumber, int server, WebFront.Page page)
{
String output = input;
switch (page)
{
case WebFront.Page.main:
output = output.Replace("{{SERVERS}}", parseMacros("SERVERS", page, pageNumber, server));
break;
case WebFront.Page.bans:
output = output.Replace("{{BANS}}", parseMacros("BANS", page, pageNumber, server));
break;
case WebFront.Page.stats:
output = output.Replace("{{STATS}}", parseMacros("STATS", page, pageNumber, server));
break;
}
//output = output.Replace("{{PAGE}}", parseMacros("PAGE", page, pageNumber, server));
//output = output.Replace("{{SERVERS}}", parseMacros("SERVERS", 0));
//output = output.Replace("{{BANS}}", parseMacros("BANS", page));
output = output.Replace("{{TITLE}}", "IW4M Administration");
//output = output.Replace("{{PAGE}}", parseMacros("PAGE", page));
//output = output.Replace("{{STATS}}", parseMacros("STATS", page));
return output;
}
}
class SchedulerDelegate : ISchedulerDelegate
{
public void OnException(IScheduler scheduler, Exception e)
{
Console.WriteLine(e.InnerException.Message);
Console.Write(e.InnerException);
e.DebugStackTrace();
}
public void OnStop(IScheduler scheduler)
{
}
}
class RequestDelegate : IHttpRequestDelegate
{
public void OnRequest(HttpRequestHead request, IDataProducer requestBody, IHttpResponseDelegate response)
{
if (request.Uri.StartsWith("/"))
{
Console.WriteLine("[WEBFRONT] Processing Request for " + request.Uri);
var body = String.Empty;
if (request.Uri.StartsWith("/"))
{
IW4MAdmin.file Header = new IW4MAdmin.file("webfront\\header.html");
var header = Header.getLines();
Header.Close();
String[] req = request.Path.Split(new char[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
int server = 0;
int page = 0;
if (req.Length > 1)
{
Int32.TryParse(req[0], out server);
Int32.TryParse(req[1], out page);
}
if (request.QueryString == "bans")
{
IW4MAdmin.file Bans = new IW4MAdmin.file("webfront\\bans.html");
var bans = Bans.getLines();
Bans.Close();
body = Macro.findMacros((header + bans), page, server, WebFront.Page.bans);
}
else if (request.QueryString == "stats")
{
IW4MAdmin.file Stats = new IW4MAdmin.file("webfront\\stats.html");
var stats = Stats.getLines();
Stats.Close();
body = Macro.findMacros(header + stats, page, server, WebFront.Page.stats);
}
else
{
IW4MAdmin.file Main = new IW4MAdmin.file("webfront\\main.html");
var main = Main.getLines();
Main.Close();
body = Macro.findMacros(header + main, 0, server, WebFront.Page.main);
}
}
/*var body = string.Format(
"Uri: {0}\r\nPath: {1}\r\nQuery:{2}\r\nFragment: {3}\r\n",
request.Uri,
request.Path,
request.QueryString,
request.Fragment);*/
var headers = new HttpResponseHead()
{
Status = "200 OK",
Headers = new Dictionary<string, string>()
{
{ "Content-Type", "text/html" },
{ "Content-Length", body.Length.ToString() },
}
};
response.OnResponse(headers, new BufferedProducer(body));
}
else
{
var responseBody = "The resource you requested ('" + request.Uri + "') could not be found.";
var headers = new HttpResponseHead()
{
Status = "404 Not Found",
Headers = new Dictionary<string, string>()
{
{ "Content-Type", "text/text" },
{ "Content-Length", responseBody.Length.ToString() }
}
};
var body = new BufferedProducer(responseBody);
response.OnResponse(headers, body);
}
}
class BufferedProducer : IDataProducer
{
ArraySegment<byte> data;
public BufferedProducer(string data) : this(data, Encoding.UTF8) { }
public BufferedProducer(string data, Encoding encoding) : this(encoding.GetBytes(data)) { }
public BufferedProducer(byte[] data) : this(new ArraySegment<byte>(data)) { }
public BufferedProducer(ArraySegment<byte> data)
{
this.data = data;
}
public IDisposable Connect(IDataConsumer channel)
{
// null continuation, consumer must swallow the data immediately.
channel.OnData(data, null);
channel.OnEnd();
return null;
}
}
class BufferedConsumer : IDataConsumer
{
List<ArraySegment<byte>> buffer = new List<ArraySegment<byte>>();
Action<string> resultCallback;
Action<Exception> errorCallback;
public BufferedConsumer(Action<string> resultCallback, Action<Exception> errorCallback)
{
this.resultCallback = resultCallback;
this.errorCallback = errorCallback;
}
public bool OnData(ArraySegment<byte> data, Action continuation)
{
// since we're just buffering, ignore the continuation.
buffer.Add(data);
return false;
}
public void OnError(Exception error)
{
errorCallback(error);
}
public void OnEnd()
{
// turn the buffer into a string.
var str = buffer
.Select(b => Encoding.UTF8.GetString(b.Array, b.Offset, b.Count))
.Aggregate((result, next) => result + next);
resultCallback(str);
}
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

4
Admin/packages.config Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Kayak" version="0.7.2" targetFramework="net40" />
</packages>

View File

@ -1,4 +1,21 @@
VERSION: 0.7 VERSION: 0.8
CHANGELOG: CHANGELOG:
-rcon tweaks -added mask command
-so much stuff cant remember -added baninfo command
-added alias command and removed redundant output from `find`
-added rcon command
-added webfront (http://127.0.0.1:1624)
-true skill is officially implemented
-find now shows last connect time
-noise on pm (if gsc_enabled)
-force 8 line chat height (if gsc_enabled)
-tell admins the number of reports on join
-enhanced ban tracking
-ip wait timeout added
-remove report on ban
-can't report yourself
-remove reported players when banned
-fixed rare crash with toadmins backend
-fixed crash when finding player stats that don't exist
-fixed a bug that caused owner command to reactivate only `creator` rank player existed
-fixed a bug that caused certain notifications to be sent to all players

11
Admin/webfront/bans.html Normal file
View File

@ -0,0 +1,11 @@
<body>
<div id="container">
<div class="h0" style="margin-top: 0; line-height:normal;">BANS<br/><a style="padding: 0; margin: 0; font-size: 24px; float: right;" href="/">Back</a></div>
<div id="logo_shit"></div>
{{BANS}}
</div>
<div id="footer">IW4M Admin &mdash; <a href="http://raidmax.org/IW4MAdmin">RaidMax.org</a></div>
</body>
</html>

277
Admin/webfront/header.html Normal file
View File

@ -0,0 +1,277 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>{{TITLE}}</title>
<style>
* {
font-family: 'Robot', sans-serif;
margin: 0;
}
html, body {
width: 100%;
height: 100%;
background-color: #171717;
}
#container {
width: 1100px;
background-color: #fff;
margin: 0 auto;
padding: 30px;
}
.h0 {
font-size: 40pt;
text-align: center;
margin-bottom: 0px;
float: right;
line-height: 100px;
}
h1 {
margin-top: 20px;
clear: both;
}
#header_img {
width: 100px;
height: 96px;
float: right;
background-image: url("http://23.251.150.125:8000/static/images/logo.png");
background-size: 100px 96px;
background-repeat: no-repeat;
}
#logo_shit
{
width: 100px;
height: 96px;
float: left;
background-image: url("http://23.251.150.125:8000/static/images/logo.png");
background-size: 100px 96px;
background-repeat: no-repeat;
}
p {
margin-top: 10px;
margin-bottom: 10px;
}
ul {
padding: 0;
margin: 0;
display: table;
width: 100%;
}
ul.tablehead li {
display: table-cell;
list-style-type: none;
font-size: 18pt;
margin: 0;
padding-top: 10px;
padding-bottom: 10px;
}
ul.row li {
overflow: hidden;
display: table-cell;
list-style-type: none;
font-size: 12pt;
}
li {
}
td{
padding: 8px;
overflow: hidden;
white-space: nowrap;
}
tr.row-white {
background-color: #fff;
}
tr.row-grey {
background-color: #eee;
}
th
{
font-size: 16pt;
}
li.row-green {
background-color: rgba(121, 194, 97, .3);
padding: 10px 0px 10px 0px;
width: 70px;
text-align: center;
}
li.row-red {
background-color: rgba(196, 22, 28, .3);
padding: 10px 0px 10px 0px;
width: 70px;
text-align: center;
}
input[type="submit"] {
border: none;
border-radius: 4px;
color: #fff;
font-size: 14pt;
width: 250px;
height: 40px;
background-color: rgb(121, 194, 97);
}
input[type="submit"]:hover {
background-color: #fff;
color: #171717;
border: 1px solid #171717;
}
.question_title {
color: #171717;
font-size: 16pt;
font-weight: bold;
margin-top: 10px;
}
.question_answer {
background-color: rgb(121, 194, 97);
color: #fff;
padding: 5px;
border-radius: 4px;
}
.question_answer a:hover {
color: cyan;
}
ol, ol li {
margin-left: 0;
padding-left: 30px;
}
a:link, a:visited {
text-decoration: none;
color: rgb(38,120,230);
}
a:hover {
color: #171717;
}
.BigList {
font-size: 12pt;
opacity: 0.5;
}
.separator {
position: absolute;
width: 3px;
height: 40px;
background-color: #ccc;
left: 25%;
right: 75%;
}
.asterik {
font-size: 11pt;
color: #171717;
font-style: italic;
}
#commands {
margin: 0 auto;
}
.block {
margin-top: 10px;
margin-bottom: 10px;
}
hr {
background-color: rgb(38,120,230);
border: none;
width: 100%;
height: 5px;
margin-bottom: 5px;
margin-top: 5px;
}
.server {
width: 100%;
text-align: left;
}
.server_title {
font-size: 24pt;
margin-bottom: 20px;
min-width: 550px;
}
.server_info {
font-size: 14pt;
}
.server_map {
min-width: 140px;
}
.server_players {
min-width: 50px;
}
.server_gametype {
min-width: 175px;
}
.players {
width: 60%;
text-align: left;
}
.bans {
text-align: left;
width: 100%;
}
.bans th
{
font-size: 20pt;
}
#pages{
font-size: 14pt;
text-align:center;
}
#pages a {
margin: 10px;
}
#pagination{
}
#footer{
background-color: #fff;
padding-top: 5px;
padding-bottom: 10px;
text-align: center;
width: 1160px;
margin: 0 auto;
border-radius: 0px 0px 11px 11px;
}
.players tbody tr td
{
padding: 1px;
}
</style>
</head>

12
Admin/webfront/main.html Normal file
View File

@ -0,0 +1,12 @@
<body>
<div id="container">
<div class="h0" style="margin-top: 0">IW4M Admin</div><div id="header_img"></div>
<h1 style="margin-top: 0;">Currently Monitoring</h1>
<hr />
{{SERVERS}}
</div>
<div id="footer">IW4M Admin &mdash; <a href="http://raidmax.org/IW4MAdmin">RaidMax.org</a></div>
</body>
</html>

10
Admin/webfront/stats.html Normal file
View File

@ -0,0 +1,10 @@
<body>
<div id="container">
<div class="h0" style="margin-top: 0; line-height:normal;">STATS<br /><a style="padding: 0; margin: 0; font-size: 24px; float: right;" href="/">Back</a></div>
<div id="logo_shit"></div>
{{STATS}}
</div>
<div id="footer">IW4M Admin &mdash; <a href="http://raidmax.org/IW4MAdmin">RaidMax.org</a></div>
</body>
</html>