Stats plugin modifications
This commit is contained in:
parent
7ae6c7e07f
commit
c23e578319
@ -149,14 +149,25 @@ namespace IW4MAdmin
|
||||
|
||||
public override void Execute(Event E)
|
||||
{
|
||||
StringBuilder playerList = new StringBuilder();
|
||||
lock (E.Owner.getPlayers())
|
||||
{
|
||||
int count = 0;
|
||||
foreach (Player P in E.Owner.getPlayers())
|
||||
{
|
||||
if (P == null)
|
||||
continue;
|
||||
|
||||
E.Origin.Tell(String.Format("[^3{0}^7]{3}[^3{1}^7] {2}", SharedLibrary.Utilities.levelToColor(P.Level), P.clientID, P.Name, SharedLibrary.Utilities.getSpaces(Player.Permission.SeniorAdmin.ToString().Length - P.Level.ToString().Length)));
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", SharedLibrary.Utilities.levelToColor(P.Level), P.clientID, P.Name, SharedLibrary.Utilities.getSpaces(Player.Permission.SeniorAdmin.ToString().Length - P.Level.ToString().Length));
|
||||
if (count == 2)
|
||||
{
|
||||
E.Origin.Tell(playerList.ToString());
|
||||
count = 0;
|
||||
playerList = new StringBuilder();
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -519,6 +530,13 @@ namespace IW4MAdmin
|
||||
|
||||
public override void Execute(Event E)
|
||||
{
|
||||
if (E.Data != null && E.Data.ToLower().Contains("clear"))
|
||||
{
|
||||
E.Owner.Reports = new List<Report>();
|
||||
E.Origin.Tell("Reports successfully cleared!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (E.Owner.Reports.Count < 1)
|
||||
{
|
||||
E.Origin.Tell("No players reported yet.");
|
||||
@ -659,5 +677,19 @@ namespace IW4MAdmin
|
||||
E.Origin.Tell("Successfuly sent RCON command!");
|
||||
}
|
||||
}
|
||||
|
||||
class Plugins : Command
|
||||
{
|
||||
public Plugins(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.Origin.Tell("^5Loaded Plugins:");
|
||||
foreach (Plugin P in PluginImporter.potentialPlugins)
|
||||
{
|
||||
E.Origin.Tell(String.Format("^3{0} ^7[^3{1}^7] by ^5{2}^7", P.Name, P.Version, P.Author));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace IW4MAdmin
|
||||
Console.WriteLine(" Version " + Version + " (unable to retrieve latest)");
|
||||
Console.WriteLine("=====================================================");
|
||||
|
||||
serverManager = new IW4MAdmin.Manager();
|
||||
serverManager = new Manager();
|
||||
|
||||
Thread serverMGRThread = new Thread(serverManager.Init);
|
||||
serverMGRThread.Name = "Server Manager thread";
|
||||
@ -42,11 +42,11 @@ namespace IW4MAdmin
|
||||
}
|
||||
|
||||
if (serverManager.getServers() != null)
|
||||
Program.getManager().mainLog.Write("IW4M Now Initialized!", Log.Level.Production);
|
||||
getManager().mainLog.Write("IW4M Now Initialized!", Log.Level.Production);
|
||||
|
||||
String userInput;
|
||||
Server serverToExecuteOn = serverManager.getServers()[0];
|
||||
Player Origin = new Player("IW4MAdmin Console", "", -1, Player.Permission.Console, -1, "", 0, "");
|
||||
Player Origin = new Player("IW4MAdmin", "", -1, Player.Permission.Console, -1, "", 0, "");
|
||||
|
||||
do
|
||||
{
|
||||
@ -68,7 +68,7 @@ namespace IW4MAdmin
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (Server S in IW4MAdmin.Program.getServers())
|
||||
foreach (Server S in getServers())
|
||||
{
|
||||
if (S == null)
|
||||
continue;
|
||||
@ -77,12 +77,12 @@ namespace IW4MAdmin
|
||||
S.isRunning = false;
|
||||
|
||||
if (Utilities.shutdownInterface(S.pID()))
|
||||
Program.getManager().mainLog.Write("Successfully removed IW4MAdmin from server with PID " + S.pID(), Log.Level.Debug);
|
||||
getManager().mainLog.Write("Successfully removed IW4MAdmin from server with PID " + S.pID(), Log.Level.Debug);
|
||||
else
|
||||
Program.getManager().mainLog.Write("Could not remove IW4MAdmin from server with PID " + S.pID(), Log.Level.Debug);
|
||||
getManager().mainLog.Write("Could not remove IW4MAdmin from server with PID " + S.pID(), Log.Level.Debug);
|
||||
}
|
||||
|
||||
Program.getManager().shutDown();
|
||||
getManager().shutDown();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -589,6 +589,12 @@ namespace IW4MAdmin
|
||||
// basic info dvars
|
||||
hostname = SharedLibrary.Utilities.stripColors(getDvar("sv_hostname").current);
|
||||
mapname = getDvar("mapname").current;
|
||||
Map localizedMapName = maps.Find(x => x.Name.Equals(mapname));
|
||||
|
||||
if (localizedMapName != null)
|
||||
mapname = localizedMapName.Alias;
|
||||
|
||||
|
||||
IW_Ver = getDvar("shortversion").current;
|
||||
maxClients = -1;
|
||||
Int32.TryParse(getDvar("party_maxplayers").current, out maxClients);
|
||||
@ -910,6 +916,9 @@ namespace IW4MAdmin
|
||||
if(owner == null)
|
||||
commands.Add(new Owner("owner", "claim ownership of the server", "owner", Player.Permission.User, 0, false));
|
||||
|
||||
foreach (Command C in PluginImporter.potentialCommands)
|
||||
commands.Add(C);
|
||||
|
||||
commands.Add(new Kick("kick", "kick a player by name. syntax: !kick <player> <reason>.", "k", Player.Permission.Moderator, 2, true));
|
||||
commands.Add(new Say("say", "broadcast message to all players. syntax: !say <message>.", "s", Player.Permission.Moderator, 1, false));
|
||||
commands.Add(new TempBan("tempban", "temporarily ban a player for 1 hour. syntax: !tempban <player> <reason>.", "tb", Player.Permission.Moderator, 2, true));
|
||||
@ -942,10 +951,7 @@ namespace IW4MAdmin
|
||||
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));
|
||||
commands.Add(new FindAll("findall", "find a player by their aliase(s). syntax: !findall <player>", "fa", Player.Permission.Moderator, 1, false));
|
||||
|
||||
foreach (Command C in PluginImporter.potentialCommands)
|
||||
commands.Add(C);
|
||||
|
||||
commands.Add(new Plugins("plugins", "view all loaded plugins. syntax: !plugins", "p", Player.Permission.Administrator, 0, false));
|
||||
}
|
||||
|
||||
//Objects
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1,9 +1,4 @@
|
||||
<div id="footer">IW4M Admin v{{VERSION}} — <a href="http://raidmax.org/IW4MAdmin">RaidMax.org</a></div>
|
||||
<script>
|
||||
$('a').each(function () {
|
||||
this.href = this.href.replace('userip', userip);
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
$(function () {
|
||||
$("#history_dialog").dialog({
|
||||
|
@ -317,8 +317,8 @@
|
||||
margin-top: 10px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border: 1px solid #171717;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(23, 23, 23, 0.49);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#player_search input[type="text"] {
|
||||
|
Binary file not shown.
@ -80,14 +80,53 @@ namespace SamplePlugin
|
||||
public class Stats : Plugin
|
||||
{
|
||||
public static StatsDB playerStats { get; private set; }
|
||||
private DateTime[] lastKill = new DateTime[18];
|
||||
private DateTime[] connectionTime = new DateTime[18];
|
||||
private int[] inactiveMinutes = new int[18];
|
||||
private int[] Kills = new int[18];
|
||||
private int[] deathStreaks = new int[18];
|
||||
private int[] killStreaks = new int[18];
|
||||
|
||||
public override void onEvent(Event E)
|
||||
{
|
||||
playerStats = new StatsDB("stats_" + E.Owner.getPort() + ".rm");
|
||||
|
||||
if (E.Type == Event.GType.Connect)
|
||||
{
|
||||
resetCounters(E.Origin.clientID);
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.MapEnd)
|
||||
{
|
||||
foreach (Player P in E.Owner.getPlayers())
|
||||
{
|
||||
calculateAndSaveSkill(P);
|
||||
resetCounters(P.clientID);
|
||||
|
||||
E.Owner.Log.Write("Updated skill for client #" + P.databaseID, Log.Level.Debug);
|
||||
//E.Owner.Log.Write(String.Format("\r\nJoin: {0}\r\nInactive Minutes: {1}\r\nnewPlayTime: {2}\r\nnewSPM: {3}\r\nkdrWeight: {4}\r\nMultiplier: {5}\r\nscoreWeight: {6}\r\nnewSkillFactor: {7}\r\nprojectedNewSkill: {8}\r\nKills: {9}\r\nDeaths: {10}", connectionTime[P.clientID].ToShortTimeString(), inactiveMinutes[P.clientID], newPlayTime, newSPM, kdrWeight, Multiplier, scoreWeight, newSkillFactor, disconnectStats.Skill, disconnectStats.Kills, disconnectStats.Deaths));
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Disconnect)
|
||||
{
|
||||
calculateAndSaveSkill(E.Origin);
|
||||
E.Owner.Log.Write("Updated skill for disconnecting client #" + E.Origin.databaseID, Log.Level.Debug);
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Kill)
|
||||
{
|
||||
Player Killer = E.Origin;
|
||||
PlayerStats killerStats = playerStats.getStats(Killer);
|
||||
|
||||
lastKill[E.Origin.clientID] = DateTime.Now;
|
||||
Kills[E.Origin.clientID]++;
|
||||
|
||||
if ((lastKill[E.Origin.clientID] - DateTime.Now).TotalSeconds > 60)
|
||||
{
|
||||
inactiveMinutes[E.Origin.clientID]++;
|
||||
}
|
||||
|
||||
if (Killer != E.Target)
|
||||
{
|
||||
killerStats.Kills++;
|
||||
@ -95,15 +134,15 @@ namespace SamplePlugin
|
||||
if (killerStats.Deaths == 0)
|
||||
killerStats.KDR = killerStats.Kills;
|
||||
else
|
||||
killerStats.KDR = killerStats.Kills / killerStats.Deaths;
|
||||
killerStats.KDR = (double)killerStats.Kills / (double)killerStats.Deaths;
|
||||
|
||||
playerStats.updateStats(Killer, killerStats);
|
||||
|
||||
killerStats.killStreak++;
|
||||
killerStats.deathStreak = 0;
|
||||
killStreaks[E.Origin.clientID]++;
|
||||
deathStreaks[E.Origin.clientID] = 0;
|
||||
}
|
||||
|
||||
Killer.Tell(messageOnStreak(killerStats.killStreak, killerStats.deathStreak));
|
||||
Killer.Tell(messageOnStreak(killStreaks[E.Origin.clientID], deathStreaks[E.Origin.clientID]));
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Death)
|
||||
@ -112,20 +151,71 @@ namespace SamplePlugin
|
||||
PlayerStats victimStats = playerStats.getStats(Victim);
|
||||
|
||||
victimStats.Deaths++;
|
||||
victimStats.KDR = victimStats.Kills / victimStats.Deaths;
|
||||
victimStats.KDR = (double)victimStats.Kills / (double)victimStats.Deaths;
|
||||
|
||||
playerStats.updateStats(Victim, victimStats);
|
||||
|
||||
victimStats.deathStreak++;
|
||||
victimStats.killStreak = 0;
|
||||
deathStreaks[E.Origin.clientID]++;
|
||||
killStreaks[E.Origin.clientID] = 0;
|
||||
|
||||
Victim.Tell(messageOnStreak(victimStats.killStreak, victimStats.deathStreak));
|
||||
Victim.Tell(messageOnStreak(killStreaks[E.Origin.clientID], deathStreaks[E.Origin.clientID]));
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateAndSaveSkill(Player P)
|
||||
{
|
||||
PlayerStats disconnectStats = playerStats.getStats(P);
|
||||
if (Kills[P.clientID] == 0)
|
||||
return;
|
||||
|
||||
else if (lastKill[P.clientID] > connectionTime[P.clientID])
|
||||
inactiveMinutes[P.clientID] += (int)(DateTime.Now - lastKill[P.clientID]).TotalMinutes;
|
||||
|
||||
int newPlayTime = (int)(DateTime.Now - connectionTime[P.clientID]).TotalMinutes - inactiveMinutes[P.clientID];
|
||||
double newSPM = Kills[P.clientID] * 50 / Math.Max(newPlayTime, 1);
|
||||
|
||||
double kdrWeight = Math.Round(Math.Pow(disconnectStats.KDR, 2 / Math.E), 3);
|
||||
double Multiplier;
|
||||
|
||||
if (disconnectStats.scorePerMinute == 1)
|
||||
Multiplier = 1;
|
||||
else
|
||||
Multiplier = newSPM / disconnectStats.scorePerMinute;
|
||||
|
||||
double scoreWeight = (newSPM * (newPlayTime / disconnectStats.playTime));
|
||||
double newSkillFactor = Multiplier * scoreWeight;
|
||||
|
||||
if (Multiplier >= 1)
|
||||
disconnectStats.scorePerMinute += newSkillFactor;
|
||||
else
|
||||
disconnectStats.scorePerMinute -= (scoreWeight - newSkillFactor);
|
||||
|
||||
disconnectStats.Skill = disconnectStats.scorePerMinute * kdrWeight / 10;
|
||||
disconnectStats.playTime += newPlayTime;
|
||||
|
||||
playerStats.updateStats(P, disconnectStats);
|
||||
}
|
||||
|
||||
private void resetCounters(int cID)
|
||||
{
|
||||
Kills[cID] = 0;
|
||||
connectionTime[cID] = DateTime.Now;
|
||||
inactiveMinutes[cID] = 0;
|
||||
deathStreaks[cID] = 0;
|
||||
killStreaks[cID] = 0;
|
||||
}
|
||||
|
||||
|
||||
public override void onLoad()
|
||||
{
|
||||
playerStats = new StatsDB("stats.rm");
|
||||
for (int i = 0; i < 18; i++)
|
||||
{
|
||||
Kills[i] = 0;
|
||||
connectionTime[i] = DateTime.Now;
|
||||
inactiveMinutes[i] = 0;
|
||||
deathStreaks[i] = 0;
|
||||
killStreaks[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override void onUnload()
|
||||
@ -142,17 +232,17 @@ namespace SamplePlugin
|
||||
Message = "Great job! You're on a ^55 killstreak!";
|
||||
break;
|
||||
case 10:
|
||||
Message = "Amazing! ^510 ^7kills without dying!";
|
||||
Message = "Amazing! ^510 kills ^7without dying!";
|
||||
break;
|
||||
}
|
||||
|
||||
switch (deathStreak)
|
||||
{
|
||||
case 5:
|
||||
Message = "Pick it up soldier, you've died 5 times in a row...";
|
||||
Message = "Pick it up soldier, you've died ^55 times ^7in a row...";
|
||||
break;
|
||||
case 10:
|
||||
Message = "Seriously? ^510 ^7deaths without getting a kill?";
|
||||
Message = "Seriously? ^510 deaths ^7without getting a kill?";
|
||||
break;
|
||||
}
|
||||
|
||||
@ -171,7 +261,7 @@ namespace SamplePlugin
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0.1f;
|
||||
return 0.2f;
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,7 +282,7 @@ namespace SamplePlugin
|
||||
{
|
||||
if (!File.Exists(FileName))
|
||||
{
|
||||
String Create = "CREATE TABLE [STATS] ( [npID] TEXT, [KILLS] INTEGER DEFAULT 0, [DEATHS] INTEGER DEFAULT 0, [KDR] REAL DEFAULT 0, [SKILL] REAL DEFAULT 0, [MEAN] REAL DEFAULT 0, [DEV] REAL DEFAULT 0 );";
|
||||
String Create = "CREATE TABLE [STATS] ( [npID] TEXT, [KILLS] INTEGER DEFAULT 0, [DEATHS] INTEGER DEFAULT 0, [KDR] TEXT DEFAULT 0, [SKILL] TEXT DEFAULT 0, [MEAN] REAL DEFAULT 0, [DEV] REAL DEFAULT 0, [SPM] TEXT DEFAULT 0, [PLAYTIME] INTEGER DEFAULT 0);";
|
||||
ExecuteNonQuery(Create);
|
||||
}
|
||||
}
|
||||
@ -204,8 +294,10 @@ namespace SamplePlugin
|
||||
newPlayer.Add("npID", P.npID);
|
||||
newPlayer.Add("KILLS", 0);
|
||||
newPlayer.Add("DEATHS", 0);
|
||||
newPlayer.Add("KDR", 0);
|
||||
newPlayer.Add("SKILL", 0);
|
||||
newPlayer.Add("KDR", 0.0);
|
||||
newPlayer.Add("SKILL", 1.0);
|
||||
newPlayer.Add("SPM", 1.0);
|
||||
newPlayer.Add("PLAYTIME", 1.0);
|
||||
|
||||
Insert("STATS", newPlayer);
|
||||
}
|
||||
@ -222,7 +314,9 @@ namespace SamplePlugin
|
||||
Convert.ToInt32(ResponseRow["KILLS"]),
|
||||
Convert.ToInt32(ResponseRow["DEATHS"]),
|
||||
Convert.ToDouble(ResponseRow["KDR"]),
|
||||
Convert.ToDouble(ResponseRow["SKILL"])
|
||||
Convert.ToDouble(ResponseRow["SKILL"]),
|
||||
Convert.ToDouble(ResponseRow["SPM"]),
|
||||
Convert.ToInt32(ResponseRow["PLAYTIME"])
|
||||
);
|
||||
}
|
||||
|
||||
@ -240,7 +334,9 @@ namespace SamplePlugin
|
||||
updatedPlayer.Add("KILLS", S.Kills);
|
||||
updatedPlayer.Add("DEATHS", S.Deaths);
|
||||
updatedPlayer.Add("KDR", Math.Round(S.KDR, 2));
|
||||
updatedPlayer.Add("SKILL", S.Skill);
|
||||
updatedPlayer.Add("SKILL", Math.Round(S.Skill, 1));
|
||||
updatedPlayer.Add("SPM", Math.Round(S.scorePerMinute, 0));
|
||||
updatedPlayer.Add("PLAYTIME", S.playTime);
|
||||
|
||||
Update("STATS", updatedPlayer, String.Format("npID = '{0}'", P.npID));
|
||||
}
|
||||
@ -248,21 +344,21 @@ namespace SamplePlugin
|
||||
|
||||
public struct PlayerStats
|
||||
{
|
||||
public PlayerStats(int K, int D, double DR, double S)
|
||||
public PlayerStats(int K, int D, double DR, double S, double sc, int P)
|
||||
{
|
||||
Kills = K;
|
||||
Deaths = D;
|
||||
KDR = DR;
|
||||
Skill = S;
|
||||
deathStreak = 0;
|
||||
killStreak = 0;
|
||||
scorePerMinute = sc;
|
||||
playTime = P;
|
||||
}
|
||||
|
||||
public int Kills;
|
||||
public int Deaths;
|
||||
public double KDR;
|
||||
public double Skill;
|
||||
public int deathStreak;
|
||||
public int killStreak;
|
||||
public double scorePerMinute;
|
||||
public int playTime;
|
||||
}
|
||||
}
|
@ -100,12 +100,15 @@ namespace Webfront_Plugin
|
||||
private String processReplacements(String Input, String Macro, int curPage, int ID, String Query, params Server[] Servers)
|
||||
{
|
||||
bool Authenticated = false;
|
||||
bool UserPrivelege = false;
|
||||
|
||||
if (Servers[0] != null && Manager.lastIP != null)
|
||||
{
|
||||
Player User = Servers[0].clientDB.getPlayer(Manager.lastIP.ToString());
|
||||
if (User != null && User.Level > Player.Permission.Flagged)
|
||||
Authenticated = true;
|
||||
if (User != null && User.Level == Player.Permission.User)
|
||||
UserPrivelege = true;
|
||||
}
|
||||
|
||||
if (Macro.Length < 5)
|
||||
@ -174,11 +177,13 @@ namespace Webfront_Plugin
|
||||
{5}",
|
||||
|
||||
S.getName(), S.getMap(), S.getClientNum() + "/" + S.getMaxClients(), SharedLibrary.Utilities.gametypeLocalized(S.getGametype()), S.pID(), players.ToString());
|
||||
|
||||
|
||||
if (S.getClientNum() > 0)
|
||||
{
|
||||
buffer.AppendFormat("<div class='chatHistory' id='chatHistory_{0}'></div><script type='text/javascript'>$( document ).ready(function() {{ setInterval({1}loadChatMessages({0}, '#chatHistory_{0}'){1}, 2500); }});</script><div class='null' style='clear:both;'></div>", S.pID(), '\"');
|
||||
//if (S.getClientNum() > 0)
|
||||
// buffer.AppendFormat("<form class='chatOutFormat' action={1}javascript:chatRequest({0}, 'chatEntry_{0}'){1}><input class='chatFormat_text' type='text' placeholder='Enter a message...' id='chatEntry_{0}'/><input class='chatFormat_submit' type='submit'/></form>", S.pID(), '\"');
|
||||
if (UserPrivelege || Authenticated)
|
||||
buffer.AppendFormat("<form class='chatOutFormat' action={1}javascript:chatRequest({0}, 'chatEntry_{0}'){1}><input class='chatFormat_text' type='text' placeholder='Enter a message...' id='chatEntry_{0}'/><input class='chatFormat_submit' type='submit'/></form>", S.pID(), '\"');
|
||||
}
|
||||
buffer.Append("<hr/>");
|
||||
}
|
||||
return Input.Replace(Macro, buffer.ToString());
|
||||
@ -352,7 +357,7 @@ namespace Webfront_Plugin
|
||||
Player P = bannedPlayers[i];
|
||||
Player B;
|
||||
|
||||
if (Bans[i].bannedByID == Bans[i].npID)
|
||||
if (P.npID == Bans[i].bannedByID)
|
||||
B = new Player("IW4MAdmin", "", 0, SharedLibrary.Player.Permission.Banned, 0, "", 0, "");
|
||||
|
||||
else
|
||||
@ -441,9 +446,6 @@ namespace Webfront_Plugin
|
||||
case "graph":
|
||||
requestedPage = new graph();
|
||||
return processTemplate(requestedPage.Load(), request.QueryString);
|
||||
case "stats":
|
||||
requestedPage = new stats();
|
||||
break;
|
||||
case "chat":
|
||||
requestedPage = new chat();
|
||||
return processTemplate(requestedPage.Load(), request.QueryString);
|
||||
@ -552,19 +554,6 @@ namespace Webfront_Plugin
|
||||
}
|
||||
}
|
||||
|
||||
class stats : Page
|
||||
{
|
||||
public override String Name
|
||||
{
|
||||
get { return "stats"; }
|
||||
}
|
||||
|
||||
public override String Load()
|
||||
{
|
||||
return loadHTML();
|
||||
}
|
||||
}
|
||||
|
||||
class graph : Page
|
||||
{
|
||||
public override String Name
|
||||
|
Loading…
Reference in New Issue
Block a user