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:
parent
ed4883d675
commit
c3bf5bf33a
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
</startup>
|
||||
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
|
||||
</configuration>
|
||||
|
@ -37,7 +37,12 @@ namespace IW4MAdmin
|
||||
|
||||
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;
|
||||
|
189
Admin/Command.cs
189
Admin/Command.cs
@ -154,7 +154,6 @@ namespace IW4MAdmin
|
||||
E.Target.tempBan(Message);
|
||||
else
|
||||
E.Origin.Tell("You cannot temp ban " + E.Target.getName());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -171,7 +170,7 @@ namespace IW4MAdmin
|
||||
if (E.Owner.Website == null)
|
||||
Message = "^1Player Banned: ^5" + E.Target.LastOffense;
|
||||
else
|
||||
Message = "^1Player Banned: ^5" + E.Target.LastOffense + "^7 (appeal at " + E.Owner.Website + ")";
|
||||
Message = "^1Player Banned: ^5" + E.Target.LastOffense + "^7 (appeal at nbsclan.org)";
|
||||
if (E.Origin.getLevel() > E.Target.getLevel())
|
||||
{
|
||||
E.Target.Ban(Message, E.Origin);
|
||||
@ -203,7 +202,7 @@ namespace IW4MAdmin
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -260,7 +259,7 @@ namespace IW4MAdmin
|
||||
if (E.Origin.getLevel() >= C.getNeededPerm())
|
||||
{
|
||||
_commands = _commands + " [^3" + C.getName() + "^7] ";
|
||||
if (count >= 3)
|
||||
if (count >= 4)
|
||||
{
|
||||
E.Origin.Tell(_commands);
|
||||
_commands = String.Empty;
|
||||
@ -359,7 +358,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
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()));
|
||||
}
|
||||
@ -421,46 +420,10 @@ namespace IW4MAdmin
|
||||
|
||||
foreach (Player P in db_players)
|
||||
{
|
||||
String mesg;
|
||||
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()));
|
||||
|
||||
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());
|
||||
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
|
||||
@ -477,7 +440,6 @@ namespace IW4MAdmin
|
||||
E.Origin.Tell("- " + r);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PrivateMessage : Command
|
||||
@ -487,6 +449,7 @@ namespace IW4MAdmin
|
||||
public override void Execute(Event E)
|
||||
{
|
||||
E.Data = Utilities.removeWords(E.Data, 1);
|
||||
E.Target.Alert();
|
||||
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));
|
||||
}
|
||||
@ -499,12 +462,20 @@ namespace IW4MAdmin
|
||||
public override void Execute(Event E)
|
||||
{
|
||||
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)
|
||||
E.Target.stats = E.Owner.statDB.getStats(E.Target.getDBID());
|
||||
E.Origin.Tell(String.Format("[^3{4}^7] ^5{0} ^7KILLS | ^5{1} ^7DEATHS | ^5{2} ^7KDR | ^5{3} ^7SKILL", E.Target.stats.Kills, E.Target.stats.Deaths, E.Target.stats.KDR, E.Target.stats.Skill, E.Target.getName()));
|
||||
E.Origin.Tell("You do not have any stats!");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (TopP.Count > 0)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
E.Origin.Tell("There are no top players yet!");
|
||||
}
|
||||
@ -608,6 +581,12 @@ namespace IW4MAdmin
|
||||
|
||||
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())
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,11 +79,17 @@ namespace IW4MAdmin
|
||||
protected int ExecuteNonQuery(String Request)
|
||||
{
|
||||
waitForClose();
|
||||
Con.Open();
|
||||
SQLiteCommand CMD = new SQLiteCommand(Con);
|
||||
CMD.CommandText = Request;
|
||||
int rowsUpdated = CMD.ExecuteNonQuery();
|
||||
Con.Close();
|
||||
int rowsUpdated = 0;
|
||||
|
||||
lock (Con)
|
||||
{
|
||||
Con.Open();
|
||||
SQLiteCommand CMD = new SQLiteCommand(Con);
|
||||
CMD.CommandText = Request;
|
||||
rowsUpdated = CMD.ExecuteNonQuery();
|
||||
Con.Close();
|
||||
}
|
||||
|
||||
return rowsUpdated;
|
||||
}
|
||||
|
||||
@ -93,13 +99,17 @@ namespace IW4MAdmin
|
||||
try
|
||||
{
|
||||
waitForClose();
|
||||
Con.Open();
|
||||
SQLiteCommand mycommand = new SQLiteCommand(Con);
|
||||
mycommand.CommandText = sql;
|
||||
SQLiteDataReader reader = mycommand.ExecuteReader();
|
||||
dt.Load(reader);
|
||||
reader.Close();
|
||||
Con.Close();
|
||||
lock (Con)
|
||||
{
|
||||
Con.Open();
|
||||
SQLiteCommand mycommand = new SQLiteCommand(Con);
|
||||
mycommand.CommandText = sql;
|
||||
SQLiteDataReader reader = mycommand.ExecuteReader();
|
||||
dt.Load(reader);
|
||||
reader.Close();
|
||||
Con.Close();
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -134,7 +144,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
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);
|
||||
Create = "CREATE TABLE [BANS] ( [Reason] TEXT NULL, [npID] TEXT NULL, [bannedByID] TEXT NULL, [IP] TEXT NULL, [TIME] TEXT NULL);";
|
||||
ExecuteNonQuery(Create);
|
||||
@ -150,12 +160,18 @@ namespace IW4MAdmin
|
||||
if (Result != null && Result.Rows.Count > 0)
|
||||
{
|
||||
DataRow ResponseRow = Result.Rows[0];
|
||||
|
||||
|
||||
// if (ResponseRow["IP"].ToString().Length < 2)
|
||||
// ResponseRow["IP"] = DateTime.Now.ToString(); // because aliases and backwards compatibility
|
||||
DateTime LC;
|
||||
|
||||
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
|
||||
@ -171,11 +187,17 @@ namespace IW4MAdmin
|
||||
if (Result != null && Result.Rows.Count > 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)
|
||||
// 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());
|
||||
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);
|
||||
}
|
||||
|
||||
else
|
||||
@ -194,7 +216,17 @@ namespace IW4MAdmin
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -206,7 +238,7 @@ namespace IW4MAdmin
|
||||
//Returns any player with level 4 permissions, null if no owner found
|
||||
public Player getOwner()
|
||||
{
|
||||
String Query = String.Format("SELECT * FROM CLIENTS WHERE Level = '{0}'", 4);
|
||||
String Query = String.Format("SELECT * FROM CLIENTS WHERE Level >= '{0}'", 4);
|
||||
DataTable Result = GetDataTable(Query);
|
||||
|
||||
if (Result != null && Result.Rows.Count > 0)
|
||||
@ -225,14 +257,12 @@ namespace IW4MAdmin
|
||||
public List<Ban> getBans()
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (Row["TIME"].ToString().Length < 2) //compatibility with my old database
|
||||
Row["TIME"] = DateTime.Now.ToString();
|
||||
// if (Row["IP"].ToString().Length < 2)
|
||||
// Row["IP"] = DateTime.Now.ToString(); //because we don't have old ip's and don't want a messy alias
|
||||
|
||||
Bans.Add(new Ban(Row["Reason"].ToString(), Row["npID"].ToString(), Row["bannedByID"].ToString(), DateTime.Parse(Row["TIME"].ToString()), Row["IP"].ToString()));
|
||||
}
|
||||
@ -261,6 +291,7 @@ namespace IW4MAdmin
|
||||
newPlayer.Add("LastOffense", "");
|
||||
newPlayer.Add("Connections", 1);
|
||||
newPlayer.Add("IP", P.getIP());
|
||||
newPlayer.Add("LastConnection", Utilities.DateTimeSQLite(DateTime.Now));
|
||||
|
||||
Insert("CLIENTS", newPlayer);
|
||||
}
|
||||
@ -276,6 +307,7 @@ namespace IW4MAdmin
|
||||
updatedPlayer.Add("LastOffense", P.getLastO());
|
||||
updatedPlayer.Add("Connections", P.getConnections());
|
||||
updatedPlayer.Add("IP", P.getIP());
|
||||
updatedPlayer.Add("LastConnection", Utilities.DateTimeSQLite(DateTime.Now));
|
||||
|
||||
Update("CLIENTS", updatedPlayer, String.Format("npID = '{0}'", P.getID()));
|
||||
}
|
||||
@ -314,7 +346,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -328,11 +360,18 @@ namespace IW4MAdmin
|
||||
if (Result != null && Result.Rows.Count > 0)
|
||||
{
|
||||
DataRow ResponseRow = Result.Rows[0];
|
||||
return new Stats(Convert.ToInt32(ResponseRow["KILLS"]), Convert.ToInt32(ResponseRow["DEATHS"]), Convert.ToDouble(ResponseRow["KDR"]), Convert.ToDouble(ResponseRow["SKILL"]));
|
||||
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
|
||||
return null;
|
||||
return new Stats(0, 0, 0, 0, 0, 25, 8.3333);
|
||||
}
|
||||
|
||||
public void addPlayer(Player P)
|
||||
@ -343,7 +382,9 @@ namespace IW4MAdmin
|
||||
newPlayer.Add("KILLS", 0);
|
||||
newPlayer.Add("DEATHS", 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);
|
||||
}
|
||||
@ -357,6 +398,8 @@ namespace IW4MAdmin
|
||||
updatedPlayer.Add("DEATHS", P.stats.Deaths);
|
||||
updatedPlayer.Add("KDR", Math.Round(P.stats.KDR, 2));
|
||||
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()));
|
||||
}
|
||||
@ -364,7 +407,7 @@ namespace IW4MAdmin
|
||||
//Returns top 8 players (we filter through them later)
|
||||
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);
|
||||
|
||||
List<Stats> Top = new List<Stats>();
|
||||
@ -373,8 +416,16 @@ namespace IW4MAdmin
|
||||
{
|
||||
foreach (DataRow D in Result.Rows)
|
||||
{
|
||||
Stats S = new Stats(Convert.ToInt32(D["Number"]), Convert.ToInt32(D["DEATHS"]), Convert.ToDouble(D["KDR"]), Convert.ToDouble(D["SKILL"]));
|
||||
if (S.Skill > 10)
|
||||
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"]));
|
||||
if (S.Skill > 230)
|
||||
Top.Add(S);
|
||||
}
|
||||
}
|
||||
@ -382,6 +433,42 @@ namespace IW4MAdmin
|
||||
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()
|
||||
{
|
||||
String Query = "SELECT * FROM STATS";
|
||||
@ -423,6 +510,21 @@ namespace IW4MAdmin
|
||||
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)
|
||||
{
|
||||
Dictionary<String, object> newPlayer = new Dictionary<String, object>();
|
||||
|
@ -66,24 +66,19 @@ namespace IW4MAdmin
|
||||
eventType = eventType.Trim();
|
||||
|
||||
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")
|
||||
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")
|
||||
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.Length < 4)
|
||||
{
|
||||
Console.WriteLine("SAY FUCKED UP BIG-TIME");
|
||||
return null;
|
||||
}
|
||||
Regex rgx = new Regex("[^a-zA-Z0-9 -! -_]");
|
||||
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 == ":")
|
||||
|
@ -112,11 +112,17 @@ namespace IW4MAdmin
|
||||
return encoding.GetString(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public String[] readAll()
|
||||
{
|
||||
return Handle.ReadToEnd().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
public String getLines()
|
||||
{
|
||||
return Handle.ReadToEnd();
|
||||
}
|
||||
|
||||
public String[] end(int neededLines)
|
||||
{
|
||||
var lines = new List<String>();
|
||||
|
@ -14,7 +14,7 @@ namespace IW4MAdmin
|
||||
|
||||
public void Send()
|
||||
{
|
||||
String URI = String.Format("http://raidmax.org/IW4M/Admin/heartbeat.php?address={0}&name={1}&map={2}&players={3}&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);
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>IW4MAdmin</RootNamespace>
|
||||
<AssemblyName>IW4MAdmin</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
<IsWebBootstrapper>true</IsWebBootstrapper>
|
||||
@ -78,11 +78,18 @@
|
||||
<StartupObject>IW4MAdmin.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<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.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>
|
||||
<HintPath>bin\Release\System.Data.SQLite.dll</HintPath>
|
||||
<HintPath>libs\System.Data.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml" />
|
||||
@ -110,9 +117,31 @@
|
||||
<Compile Include="Server.cs" />
|
||||
<Compile Include="TrueSkill.cs" />
|
||||
<Compile Include="Utilities.cs" />
|
||||
<Compile Include="WebFront.cs" />
|
||||
</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" />
|
||||
<Content Include="config\maps.cfg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
@ -127,6 +156,7 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="IW4M ADMIN_TemporaryKey.pfx" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\app.manifest" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
@ -151,7 +181,8 @@
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<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>
|
||||
<!-- 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.
|
||||
|
@ -10,8 +10,9 @@ namespace IW4MAdmin
|
||||
static String IP;
|
||||
static int Port;
|
||||
static String RCON;
|
||||
static public double Version = 0.6;
|
||||
static public double Version = 0.7;
|
||||
static public double latestVersion;
|
||||
static public List<Server> Servers;
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
@ -24,9 +25,9 @@ namespace IW4MAdmin
|
||||
else
|
||||
Console.WriteLine(" Version " + Version + " (unable to retrieve latest)");
|
||||
Console.WriteLine("=====================================================");
|
||||
|
||||
|
||||
foreach (Server IW4M in checkConfig())
|
||||
Servers = checkConfig();
|
||||
foreach (Server IW4M in Servers)
|
||||
{
|
||||
//Threading seems best here
|
||||
Server SV = IW4M;
|
||||
@ -36,6 +37,9 @@ namespace IW4MAdmin
|
||||
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
|
||||
|
||||
}
|
||||
|
@ -6,34 +6,33 @@ namespace IW4MAdmin
|
||||
{
|
||||
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;
|
||||
Deaths = D;
|
||||
KDR = Math.Round(kdr,2);
|
||||
Skill = Math.Round(skill,2);
|
||||
|
||||
lastSigma = lastMew/3;
|
||||
lastMew = 25;
|
||||
Rating = new Moserware.Skills.Rating(mean, dev);
|
||||
Skill = Math.Round(Rating.ConservativeRating, 3)*10;
|
||||
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Skill = TrueSkill.Gaussian(lastMew, lastSigma);
|
||||
KDR = Math.Round((double)((double)Kills / (double)tempDeaths), 2);
|
||||
}
|
||||
|
||||
public int Kills;
|
||||
public int Deaths;
|
||||
public double KDR;
|
||||
public double Skill;
|
||||
|
||||
public double lastSigma;
|
||||
public double lastMew;
|
||||
public int statIndex;
|
||||
public Moserware.Skills.Rating Rating;
|
||||
}
|
||||
|
||||
class Aliases
|
||||
@ -73,7 +72,7 @@ namespace IW4MAdmin
|
||||
public void addName(String Name)
|
||||
{
|
||||
if (Name.Trim() != String.Empty && Name != null)
|
||||
Names += ';' + Names;
|
||||
Names += ';' + Name;
|
||||
}
|
||||
|
||||
public void addIP(String IP)
|
||||
@ -112,7 +111,18 @@ namespace IW4MAdmin
|
||||
IP = "";
|
||||
Warnings = 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)
|
||||
@ -129,6 +139,26 @@ namespace IW4MAdmin
|
||||
Connections = con;
|
||||
IP = IP2;
|
||||
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()
|
||||
@ -171,9 +201,24 @@ namespace IW4MAdmin
|
||||
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)
|
||||
{
|
||||
Name = n;
|
||||
if (n.Trim() != String.Empty)
|
||||
Name = n;
|
||||
}
|
||||
|
||||
public void updateIP(String I)
|
||||
@ -209,7 +254,12 @@ namespace IW4MAdmin
|
||||
|
||||
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
|
||||
@ -233,11 +283,13 @@ namespace IW4MAdmin
|
||||
private int dbID;
|
||||
public int Connections;
|
||||
private String IP;
|
||||
private DateTime LastConnection;
|
||||
|
||||
public Event lastEvent;
|
||||
public String LastOffense;
|
||||
public int Warnings;
|
||||
public Stats stats;
|
||||
public Aliases Alias;
|
||||
public bool Masked;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
509
Admin/Server.cs
509
Admin/Server.cs
@ -36,6 +36,8 @@ namespace IW4MAdmin
|
||||
HB = new Heartbeat(this);
|
||||
Macros = new Dictionary<String, Object>();
|
||||
Reports = new List<Report>();
|
||||
Skills = new Moserware.TrueSkill();
|
||||
statusPlayers = new Dictionary<string, Player>();
|
||||
nextMessage = 0;
|
||||
initCommands();
|
||||
initMacros();
|
||||
@ -55,6 +57,11 @@ namespace IW4MAdmin
|
||||
return mapname;
|
||||
}
|
||||
|
||||
public String getGametype()
|
||||
{
|
||||
return Gametype;
|
||||
}
|
||||
|
||||
//Returns current server IP set by `net_ip` -- *STRING*
|
||||
public String getIP()
|
||||
{
|
||||
@ -101,111 +108,178 @@ namespace IW4MAdmin
|
||||
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
|
||||
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
|
||||
try
|
||||
#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());
|
||||
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);
|
||||
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
|
||||
else
|
||||
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)
|
||||
{
|
||||
Log.Write("Connecting player has new alias -- " + P.getName() + " previous was: " + NewPlayer.getName(), Log.Level.Debug);
|
||||
NewPlayer.updateName(P.getName());
|
||||
NewPlayer.Alias.addName(P.getName());
|
||||
aliasDB.updatePlayer(NewPlayer.Alias);
|
||||
}
|
||||
// lets check aliases
|
||||
if ((NewPlayer.Alias.getNames().Find(m => m.Equals(P.getName()))) == null || NewPlayer.getName() == null || NewPlayer.getName() == String.Empty)
|
||||
{
|
||||
NewPlayer.updateName(P.getName().Trim());
|
||||
NewPlayer.Alias.addName(NewPlayer.getName());
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
aliasDB.updatePlayer(NewPlayer.Alias);
|
||||
clientDB.updatePlayer(NewPlayer);
|
||||
|
||||
clientDB.updatePlayer(NewPlayer);
|
||||
NewPlayer.lastEvent.Owner = this; // cuz crashes
|
||||
List<Player> newPlayerAliases = new List<Player>();
|
||||
getAliases(newPlayerAliases, NewPlayer);
|
||||
|
||||
Ban B = isBanned(NewPlayer);
|
||||
if (B != null || NewPlayer.getLevel() == Player.Permission.Banned)
|
||||
foreach (Player aP in newPlayerAliases)
|
||||
{
|
||||
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);
|
||||
string Reason = String.Empty;
|
||||
|
||||
if (B != null)
|
||||
Reason = B.getReason();
|
||||
if (aP.getLastO() != null)
|
||||
Message = "^7Player Kicked: Previously banned for ^5" + aP.getLastO() + " ^7(appeal at " + Website+ ")";
|
||||
else
|
||||
Reason = P.LastOffense;
|
||||
Message = "Player Kicked: Previous Ban";
|
||||
|
||||
String Message = "^1Player Kicked: ^7Previously Banned for ^5" + Reason;
|
||||
NewPlayer.Kick(Message);
|
||||
|
||||
if (players[P.getClientNum()] != null)
|
||||
players[P.getClientNum()] = null;
|
||||
if (players[NewPlayer.getClientNum()] != null)
|
||||
{
|
||||
lock (players)
|
||||
{
|
||||
players[NewPlayer.getClientNum()] = null;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
#if DEBUG == FALSE
|
||||
NewPlayer.Tell("Welcome ^5" + NewPlayer.getName() + " ^7this is your ^5" + Utilities.timesConnected(NewPlayer.getConnections()) + " ^7time connecting!");
|
||||
#endif
|
||||
Log.Write("Client " + NewPlayer.getName() + " connecting...", Log.Level.Debug);
|
||||
clientnum++;
|
||||
Log.Write("Client " + NewPlayer.getName() + " connecting...", Log.Level.Debug);
|
||||
|
||||
if (NewPlayer.getLevel() == Player.Permission.Flagged)
|
||||
ToAdmins("^1NOTICE: ^7Flagged player ^5" + NewPlayer.getName() + "^7 has joined!");
|
||||
}
|
||||
if (NewPlayer.getLevel() == Player.Permission.Flagged)
|
||||
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;
|
||||
}
|
||||
@ -221,115 +295,78 @@ namespace IW4MAdmin
|
||||
//Remove player by CLIENT NUMBER
|
||||
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);
|
||||
statDB.updatePlayer(players[cNum]);
|
||||
Player Leaving = players[cNum];
|
||||
Leaving.Connections++;
|
||||
clientDB.updatePlayer(Leaving);
|
||||
statDB.updatePlayer(Leaving);
|
||||
|
||||
Log.Write("Client at " + cNum + " disconnecting...", Log.Level.Debug);
|
||||
players[cNum] = null;
|
||||
clientnum--;
|
||||
lock (players)
|
||||
{
|
||||
players[cNum] = null;
|
||||
}
|
||||
clientnum = statusPlayers.Count;
|
||||
return true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Log.Write("Client disconnecting has an invalid client index!", Log.Level.Debug);
|
||||
clientnum = statusPlayers.Count;
|
||||
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
|
||||
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);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (kill)
|
||||
int pID = -1;
|
||||
int.TryParse(L[cIDPos].Trim(), out pID);
|
||||
|
||||
if (pID < 0 || pID > 17)
|
||||
{
|
||||
foreach (Player P in players)
|
||||
{
|
||||
if (P == 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()];
|
||||
Log.Write("Error event player index " + pID + " is out of bounds!", Log.Level.Debug);
|
||||
Log.Write(String.Join(";", L), Log.Level.Debug);
|
||||
return null;
|
||||
}
|
||||
|
||||
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)
|
||||
continue;
|
||||
if (P.getName().ToLower().Contains(L[4].Trim()))
|
||||
if (P != null && P.getName().ToLower().Contains(pName.ToLower()))
|
||||
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
|
||||
@ -349,9 +386,6 @@ namespace IW4MAdmin
|
||||
if (B.getIP() == null || C.getIP() == null)
|
||||
continue;
|
||||
|
||||
if (C.Alias.getIPS().Find(f => f.Equals(B.getIP())) != null)
|
||||
return B;
|
||||
|
||||
if (C.getIP() == B.getIP())
|
||||
return B;
|
||||
}
|
||||
@ -409,7 +443,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
|
||||
else
|
||||
E.Target = clientFromLine(Args[0]);
|
||||
E.Target = clientFromName(Args[0]);
|
||||
|
||||
if (E.Target == null)
|
||||
{
|
||||
@ -528,7 +562,7 @@ namespace IW4MAdmin
|
||||
if (lines[count] == oldLines[oldLines.Length - 1])
|
||||
continue;
|
||||
|
||||
if (lines[count].Length < 10) //Not a needed line
|
||||
if (lines[count].Length < 10) // its not a needed line
|
||||
continue;
|
||||
|
||||
else
|
||||
@ -574,8 +608,55 @@ namespace IW4MAdmin
|
||||
int timesFailed = 0;
|
||||
while (isRunning)
|
||||
{
|
||||
IPS = Utilities.IPFromStatus(RCON.addRCON("status"));
|
||||
while (IPS == null)
|
||||
lock (statusPlayers)
|
||||
{
|
||||
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++;
|
||||
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);
|
||||
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(20); // don't want to overload the server
|
||||
Utilities.Wait(15);
|
||||
}
|
||||
}
|
||||
|
||||
@ -654,7 +713,7 @@ namespace IW4MAdmin
|
||||
|
||||
try
|
||||
{
|
||||
Website = infoResponseDict["_Website"];
|
||||
Website = infoResponseDict["_website"];
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
@ -747,7 +806,7 @@ namespace IW4MAdmin
|
||||
logPath = Basepath + '\\' + "m2demo" + '\\' + log;
|
||||
else
|
||||
logPath = Basepath + '\\' + Mod + '\\' + log;
|
||||
|
||||
//#if DEBUG == FALSE && System.Environment.GetEnvironmentVariable("COMPUTERNAME") != "michael-surface"
|
||||
if (!File.Exists(logPath))
|
||||
{
|
||||
Log.Write("Gamelog does not exist!", Log.Level.All);
|
||||
@ -755,6 +814,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
|
||||
logFile = new file(logPath);
|
||||
//#endif
|
||||
Log.Write("Log file is " + logPath, Log.Level.Debug);
|
||||
|
||||
//get players ip's
|
||||
@ -765,7 +825,6 @@ namespace IW4MAdmin
|
||||
return false;
|
||||
}
|
||||
|
||||
IPS = Utilities.IPFromStatus(p);
|
||||
lastPoll = DateTime.Now;
|
||||
|
||||
#if DEBUG
|
||||
@ -774,6 +833,7 @@ namespace IW4MAdmin
|
||||
System.IO.Stream ftpStream = tmp.GetResponse().GetResponseStream();
|
||||
String ftpLog = new StreamReader(ftpStream).ReadToEnd();*/
|
||||
//logPath = "games_old.log";
|
||||
//logFile = new file("C:\\Users\\Michael\\text.txt");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@ -787,14 +847,13 @@ namespace IW4MAdmin
|
||||
//Process any server event
|
||||
public bool processEvent(Event E)
|
||||
{
|
||||
//
|
||||
if (E.Type == Event.GType.Connect)
|
||||
/*if (E.Type == Event.GType.Connect)
|
||||
{
|
||||
if (E.Origin == null)
|
||||
Log.Write("Connect event triggered, but no client is detected!", Log.Level.Debug);
|
||||
addPlayer(E.Origin);
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
|
||||
if (E.Type == Event.GType.Disconnect)
|
||||
{
|
||||
@ -804,10 +863,7 @@ namespace IW4MAdmin
|
||||
return false;
|
||||
}
|
||||
|
||||
E.Origin.Connections++;
|
||||
clientDB.updatePlayer(E.Origin);
|
||||
removePlayer(E.Origin.getClientNum());
|
||||
|
||||
removePlayer(E.Origin.getClientNum());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -837,30 +893,27 @@ namespace IW4MAdmin
|
||||
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)
|
||||
{
|
||||
E.Origin.stats.Kills++;
|
||||
E.Origin.stats.Kills += 1;
|
||||
E.Origin.stats.updateKDR();
|
||||
|
||||
E.Origin.stats.lastMew = TrueSkill.calculateWinnerMu(E.Origin.stats, E.Target.stats);
|
||||
E.Origin.stats.lastSigma = TrueSkill.calculateWinnerSigma(E.Origin.stats, E.Target.stats);
|
||||
E.Origin.stats.updateSkill();
|
||||
|
||||
E.Target.stats.Deaths++;
|
||||
E.Target.stats.Deaths += 1;
|
||||
E.Target.stats.updateKDR();
|
||||
|
||||
E.Target.stats.lastMew = TrueSkill.calculateLoserMu(E.Target.stats, E.Origin.stats);
|
||||
E.Target.stats.lastSigma = TrueSkill.calculateLoserSigma(E.Target.stats, E.Origin.stats);
|
||||
Skills.updateNewSkill(E.Origin, E.Target);
|
||||
E.Owner.statDB.updatePlayer(E.Origin);
|
||||
E.Owner.statDB.updatePlayer(E.Target);
|
||||
|
||||
totalKills++;
|
||||
Log.Write(E.Origin.getName() + " killed " + E.Target.getName() + " with a " + E.Data, Log.Level.Debug);
|
||||
}
|
||||
|
||||
else //Suicide
|
||||
{
|
||||
E.Origin.stats.Deaths++;
|
||||
E.Origin.stats.updateKDR();
|
||||
Log.Write(E.Origin.getName() + " suicided...", Log.Level.Debug);
|
||||
}
|
||||
}
|
||||
|
||||
@ -902,6 +955,7 @@ namespace IW4MAdmin
|
||||
C.Execute(E);
|
||||
return true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
Log.Write("New map loaded", Log.Level.Debug);
|
||||
Log.Write("New map loaded - " + clientnum + " active players", Log.Level.Debug);
|
||||
String[] statusResponse = E.Data.Split('\\');
|
||||
if (statusResponse.Length >= 15 && statusResponse[13] == "mapname")
|
||||
mapname = maps.Find(m => m.Name.Equals(statusResponse[14])).Alias; //update map for heartbeat
|
||||
@ -969,17 +1023,21 @@ namespace IW4MAdmin
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
RCON.addRCON("tempbanclient " + Target.getClientNum() + " \"" + Message + "^7\"");
|
||||
if (Target.getClientNum() > -1)
|
||||
RCON.addRCON("tempbanclient " + Target.getClientNum() + " \"" + Message + "^7\"");
|
||||
|
||||
if (Origin != null)
|
||||
{
|
||||
Target.setLevel(Player.Permission.Banned);
|
||||
@ -987,6 +1045,21 @@ namespace IW4MAdmin
|
||||
Bans.Add(newBan);
|
||||
clientDB.addBan(newBan);
|
||||
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)
|
||||
{
|
||||
List<Player> admins = players;
|
||||
foreach (Player P in admins)
|
||||
lock (players) // threading can modify list while we do this
|
||||
{
|
||||
if (P == null)
|
||||
continue;
|
||||
|
||||
if (P.getLevel() > Player.Permission.User)
|
||||
foreach (Player P in players)
|
||||
{
|
||||
RCON.addRCON("admin_lastevent alert;" + P.getID() + ";0;mp_killstreak_nuclearstrike");
|
||||
P.Tell(message);
|
||||
if (P == null)
|
||||
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
|
||||
|
||||
//THIS IS BAD BECAUSE WE DON"T WANT EVERYONE TO HAVE ACCESS :/
|
||||
@ -1068,6 +1148,7 @@ namespace IW4MAdmin
|
||||
private void initMacros()
|
||||
{
|
||||
Macros = new Dictionary<String, Object>();
|
||||
Macros.Add("WEBSITE", "nbsclan.org");
|
||||
Macros.Add("WISDOM", Wisdom());
|
||||
Macros.Add("TOTALPLAYERS", clientDB.totalPlayers());
|
||||
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 _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 _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
|
||||
@ -1221,10 +1306,12 @@ namespace IW4MAdmin
|
||||
private String IW_Ver;
|
||||
private int maxClients;
|
||||
private Dictionary<String, Object> Macros;
|
||||
private Moserware.TrueSkill Skills;
|
||||
|
||||
|
||||
//Will probably move this later
|
||||
private Dictionary<String, String> IPS;
|
||||
//public Dictionary<String, String> IPS;
|
||||
public Dictionary<String, Player> statusPlayers;
|
||||
public bool isRunning;
|
||||
private DateTime lastPoll;
|
||||
|
||||
|
@ -1,55 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Moserware.Skills.TrueSkill;
|
||||
using IW4MAdmin;
|
||||
|
||||
namespace IW4MAdmin
|
||||
|
||||
namespace Moserware
|
||||
{
|
||||
class TrueSkill
|
||||
{
|
||||
public static double calculateWinnerMu(Stats originStats, Stats targetStats)
|
||||
{
|
||||
double Beta = originStats.lastMew / 6;
|
||||
double lastSkill = Gaussian(originStats.lastMew, originStats.lastSigma);
|
||||
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 TrueSkill()
|
||||
{
|
||||
calculator = new TwoPlayerTrueSkillCalculator();
|
||||
gInfo = Skills.GameInfo.DefaultGameInfo;
|
||||
}
|
||||
|
||||
public static double calculateLoserMu(Stats originStats, Stats targetStats)
|
||||
{
|
||||
double Beta = originStats.lastMew / 6;
|
||||
double lastSkill = Gaussian(originStats.lastMew, originStats.lastSigma);
|
||||
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 void updateNewSkill(Player P1, Player P2)
|
||||
{
|
||||
var player1 = new Skills.Player(P1.getDBID());
|
||||
var player2 = new Skills.Player(P2.getDBID());
|
||||
|
||||
public static double calculateLoserSigma(Stats originStats, Stats targetStats)
|
||||
{
|
||||
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;
|
||||
}
|
||||
var team1 = new Skills.Team(player1, P1.stats.Rating);
|
||||
var team2 = new Skills.Team(player2, P2.stats.Rating);
|
||||
|
||||
public static double calculateWinnerSigma(Stats originStats, Stats targetStats)
|
||||
{
|
||||
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;
|
||||
}
|
||||
var newRatings = calculator.CalculateNewRatings(gInfo, Skills.Teams.Concat(team1, team2), 1, 2);
|
||||
|
||||
P1.stats.Rating = newRatings[player1];
|
||||
P2.stats.Rating = newRatings[player2];
|
||||
|
||||
|
||||
//https://gist.github.com/tansey/1444070
|
||||
public static double Gaussian( double mean, double stddev)
|
||||
{
|
||||
|
||||
double y1 = Math.Sqrt(-2.0 * Math.Log(.5)) * Math.Cos(2.0 * Math.PI * .5);
|
||||
return y1 * stddev + mean;
|
||||
}
|
||||
P1.stats.Skill = Math.Round(P1.stats.Rating.ConservativeRating, 3)*10;
|
||||
P2.stats.Skill = Math.Round(P2.stats.Rating.ConservativeRating, 3)*10;
|
||||
}
|
||||
|
||||
private Skills.SkillCalculator calculator;
|
||||
public Skills.GameInfo gInfo;
|
||||
}
|
||||
}
|
||||
}
|
@ -94,9 +94,9 @@ namespace IW4MAdmin
|
||||
case Player.Permission.Owner:
|
||||
return "^5" + Player.Permission.Owner;
|
||||
case Player.Permission.User:
|
||||
return "^3" + Player.Permission.User;
|
||||
return "^2" + Player.Permission.User;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
String Prefix = String.Empty;
|
||||
@ -168,8 +291,7 @@ namespace IW4MAdmin
|
||||
case 3:
|
||||
Prefix = "rd";
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
switch (connection)
|
||||
|
394
Admin/WebFront.cs
Normal file
394
Admin/WebFront.cs
Normal 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" + " — " + 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
Admin/libs/Moserware.Skills.dll
Normal file
BIN
Admin/libs/Moserware.Skills.dll
Normal file
Binary file not shown.
BIN
Admin/libs/SQLite.Interop.dll
Normal file
BIN
Admin/libs/SQLite.Interop.dll
Normal file
Binary file not shown.
BIN
Admin/libs/System.Data.SQLite.dll
Normal file
BIN
Admin/libs/System.Data.SQLite.dll
Normal file
Binary file not shown.
4
Admin/packages.config
Normal file
4
Admin/packages.config
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Kayak" version="0.7.2" targetFramework="net40" />
|
||||
</packages>
|
@ -1,4 +1,21 @@
|
||||
VERSION: 0.7
|
||||
VERSION: 0.8
|
||||
CHANGELOG:
|
||||
-rcon tweaks
|
||||
-so much stuff cant remember
|
||||
-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
|
11
Admin/webfront/bans.html
Normal file
11
Admin/webfront/bans.html
Normal 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 — <a href="http://raidmax.org/IW4MAdmin">RaidMax.org</a></div>
|
||||
|
||||
</body>
|
||||
</html>
|
277
Admin/webfront/header.html
Normal file
277
Admin/webfront/header.html
Normal 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
12
Admin/webfront/main.html
Normal 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 — <a href="http://raidmax.org/IW4MAdmin">RaidMax.org</a></div>
|
||||
</body>
|
||||
</html>
|
10
Admin/webfront/stats.html
Normal file
10
Admin/webfront/stats.html
Normal 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 — <a href="http://raidmax.org/IW4MAdmin">RaidMax.org</a></div>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user