Modularise top, partially addresses #304

This commit is contained in:
UnknownShadow200 2017-05-06 16:16:39 +10:00
parent 62d4c13ba9
commit 0082904fb5
21 changed files with 211 additions and 113 deletions

View File

@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using MCGalaxy.DB;
namespace MCGalaxy.Gui {
public sealed class PlayerProperties {

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses.
*/
using System.IO;
using MCGalaxy.DB;
namespace MCGalaxy.Commands.Chatting {
public sealed class CmdLoginMessage : EntityPropertyCmd {

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses.
*/
using System.IO;
using MCGalaxy.DB;
namespace MCGalaxy.Commands.Chatting {
public sealed class CmdLogoutMessage : EntityPropertyCmd {

View File

@ -18,6 +18,7 @@
using System;
using MCGalaxy;
using MCGalaxy.Bots;
using MCGalaxy.DB;
namespace MCGalaxy.Commands.Chatting {
public class CmdNick : EntityPropertyCmd {

View File

@ -19,6 +19,7 @@
*/
using System;
using System.Data;
using MCGalaxy.DB;
using MCGalaxy.SQL;
namespace MCGalaxy.Commands.Info {

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses.
*/
using System;
using MCGalaxy.DB;
namespace MCGalaxy.Commands.Info {
public sealed class CmdSeen : Command {

View File

@ -17,18 +17,20 @@
*/
using System;
using System.Data;
using MCGalaxy.DB;
using MCGalaxy.SQL;
namespace MCGalaxy.Commands.Info {
public sealed class CmdTop : Command {
public sealed class CmdTop : Command {
public override string name { get { return "top"; } }
public override string shortcut { get { return "most"; } }
public override string type { get { return CommandTypes.Information; } }
public override bool museumUsable { get { return true; } }
public override LevelPermission defaultRank { get { return LevelPermission.Guest; } }
public CmdTop() { }
public override CommandAlias[] Aliases {
get { return new [] { new CommandAlias("topten", null, "10"), new CommandAlias("topfive", null, "5"),
new CommandAlias("top10", null, "10"), }; }
new CommandAlias("top10", null, "10"), }; }
}
public override void Use(Player p, string message) {
@ -36,81 +38,25 @@ namespace MCGalaxy.Commands.Info {
if (args.Length < 2) { Help(p); return; }
int offset = ParseOffset(p, args);
int limit = ParseLimit(p, args);
int limit = ParseLimit(p, args);
if (limit == -1 || offset == -1) return;
string col, title;
string table = "Players", order = "desc";
switch (args[0]) {
case "1":
col = "TotalLogin";
title = "&aMost logins:";
break;
case "2":
col = "TotalDeaths";
title = "&aMost deaths:";
break;
case "3":
col = "money";
title = "&aMost " + Server.moneys + ":";
break;
case "4":
col = "firstlogin";
title = "&aOldest players:";
order = "asc";
break;
case "5":
col = "lastlogin";
title = "&aMost recent players:";
break;
case "6":
col = "TotalKicked";
title = "&aMost times kicked:";
break;
case "7":
col = "totalBlocks & " + PlayerData.LowerBitsMask;
title = "&aMost blocks modified:";
break;
case "8":
col = "totalCuboided & " + PlayerData.LowerBitsMask;
title = "&aMost blocks drawn:";
break;
case "9":
col = "totalBlocks >> " + PlayerData.LowerBits;
title = "&aMost blocks placed:";
break;
case "10":
col = "totalCuboided >> " + PlayerData.LowerBits;
title = "&aMost blocks deleted:";
break;
case "11":
col = "TotalInfected";
title = "&aMost players infected:";
table = "ZombieStats"; break;
case "12":
col = "TotalRounds";
title = "&aMost rounds survived:";
table = "ZombieStats"; break;
case "13":
col = "MaxInfected";
title = "&aMost consecutive infections:";
table = "ZombieStats"; break;
case "14":
col = "MaxRounds";
title = "&aMost consecutive rounds survived:";
table = "ZombieStats"; break;
default:
Player.Message(p, "/Top: Unrecognised type \"{0}\".", args[0]);
return;
TopStat stat = FindTopStat(args[0]);
if (stat == null) {
Player.Message(p, "/Top: Unrecognised type \"{0}\".", args[0]);
return;
}
string order = stat.Ascending ? "asc" : "desc";
string strLimit = " LIMIT " + offset + "," + limit;
DataTable db = Database.Backend.GetRows(table, "DISTINCT Name, " + col,
"ORDER BY " + col + " " + order + strLimit);
DataTable db = Database.Backend.GetRows(stat.Table, "DISTINCT Name, " + stat.Column,
"ORDER BY " + stat.Column + " " + order + strLimit);
Player.Message(p, title);
Player.Message(p, stat.Title());
for (int i = 0; i < db.Rows.Count; i++) {
Player.Message(p, "{0}) {1} - {2}", offset + (i + 1), db.Rows[i]["Name"], db.Rows[i][col]);
string player = PlayerInfo.GetColoredName(p, db.Rows[i]["Name"].ToString());
string item = db.Rows[i][stat.Column].ToString();
Player.Message(p, "{0}) {1} %S- {2}", offset + (i + 1), player, stat.Formatter(item));
}
db.Dispose();
}
@ -128,22 +74,28 @@ namespace MCGalaxy.Commands.Info {
int offset = 0;
if (!CommandParser.GetInt(p, args[1], "Offset", ref offset, 0)) return -1;
return offset;
return offset;
}
static TopStat FindTopStat(string input) {
foreach (TopStat stat in TopStat.Stats) {
if (stat.Identifier.CaselessEq(input)) return stat;
}
int number;
if (int.TryParse(input, out number)) {
// Backwards compatibility where top used to take a number
if (number >= 1 && number <= TopStat.Stats.Count)
return TopStat.Stats[number - 1];
}
return null;
}
public override void Help(Player p) {
Player.Message(p, "%T/top [stat] <offset> [number of players to show] ");
Player.Message(p, "%HPrints a list of players who have the " +
"most/top of a particular stat. Available stats:");
Player.Message(p, "1) Most logins, 2) Most deaths, 3) Money");
Player.Message(p, "4) First joined, 5) Recently joined, 6) Most kicks");
Player.Message(p, "7) Blocks modified, 8) Blocks drawn");
Player.Message(p, "9) Blocks placed, 10) Blocks deleted");
if (!Server.zombie.Running) return;
Player.Message(p, "11) Most infected, 12) Most rounds survived");
Player.Message(p, "13) Max infected, 14) Max rounds survived");
Player.Message(p, "&f" + TopStat.Stats.Join(stat => stat.Identifier));
}
}
}

View File

@ -17,6 +17,7 @@
*/
using System;
using System.Collections.Generic;
using MCGalaxy.DB;
using MCGalaxy.Games;
namespace MCGalaxy.Commands.Info {

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses.
*/
using System;
using MCGalaxy.DB;
using MCGalaxy.SQL;
namespace MCGalaxy.Commands.Maintenance {

View File

@ -17,6 +17,7 @@
*/
using System;
using System.Data;
using MCGalaxy.DB;
using MCGalaxy.SQL;
namespace MCGalaxy.Commands.Maintenance {
@ -47,21 +48,23 @@ namespace MCGalaxy.Commands.Maintenance {
switch (args[1].ToLower()) {
case "firstlogin":
SetDate(p, args, "FirstLogin", who, v => who.firstLogin = v); break;
SetDate(p, args, PlayerData.ColumnFirstLogin, who,
v => who.firstLogin = v); break;
case "lastlogin":
SetDate(p, args, "LastLogin", who, v => who.timeLogged = v); break;
SetDate(p, args, PlayerData.ColumnLastLogin, who,
v => who.timeLogged = v); break;
case "logins":
case "totallogin":
case "totallogins":
SetInteger(p, args, "totalLogin", 1000000000, who,
SetInteger(p, args, PlayerData.ColumnLogins, 1000000000, who,
v => who.totalLogins = v, UpdateDB); break;
case "deaths":
case "totaldeaths":
SetInteger(p, args, "TotalDeaths", short.MaxValue, who,
SetInteger(p, args, PlayerData.ColumnDeaths, short.MaxValue, who,
v => who.overallDeath = v, UpdateDB); break;
case "money":
SetInteger(p, args, "Money", 100000000, who,
SetInteger(p, args, PlayerData.ColumnMoney, 100000000, who,
v => who.money = v, UpdateDB); break;
case "title":
@ -75,35 +78,34 @@ namespace MCGalaxy.Commands.Maintenance {
who.title = args[2];
who.SetPrefix();
}
UpdateDB(args[0], args[2], "Title");
UpdateDB(args[0], args[2], PlayerData.ColumnTitle);
MessageDataChanged(p, args[0], args[1], args[2]); break;
case "modified":
case "totalblocks":
SetInteger(p, args, "totalBlocks", int.MaxValue, who,
SetInteger(p, args, PlayerData.ColumnTotalBlocks, int.MaxValue, who,
v => who.overallBlocks = v, UpdateDBLo); break;
case "drawn":
case "totalcuboided":
case "totalcuboid":
SetInteger(p, args, "totalCuboided", int.MaxValue, who,
SetInteger(p, args, PlayerData.ColumnTotalCuboided, int.MaxValue, who,
v => who.TotalDrawn = v, UpdateDBLo); break;
case "placed":
SetInteger(p, args, "totalBlocks", int.MaxValue, who,
SetInteger(p, args, PlayerData.ColumnTotalBlocks, int.MaxValue, who,
v => who.TotalPlaced = v, UpdateDBHi); break;
case "deleted":
SetInteger(p, args, "totalCuboided", int.MaxValue, who,
SetInteger(p, args, PlayerData.ColumnTotalCuboided, int.MaxValue, who,
v => who.TotalDeleted = v, UpdateDBHi); break;
case "totalkicked":
SetInteger(p, args, "totalKicked", 1000000000, who,
SetInteger(p, args, PlayerData.ColumnKicked, 1000000000, who,
v => who.totalKicked = v, UpdateDB); break;
case "timespent":
SetTimespan(p, args, "TimeSpent", who, v => who.time = v.ParseDBTime()); break;
SetTimespan(p, args, PlayerData.ColumnTimeSpent, who, v => who.time = v.ParseDBTime()); break;
case "color":
SetColor(p, args, "color", who, v => who.color = (v == "" ? who.group.color : v)); break;
SetColor(p, args, PlayerData.ColumnColor, who, v => who.color = (v == "" ? who.group.color : v)); break;
case "titlecolor":
SetColor(p, args, "title_color", who, v => who.titlecolor = (v == "" ? "" : v)); break;
default:
Player.Message(p, Colors.red + "Invalid type.");
MessageValidTypes(p); break;

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses.
*/
using System.IO;
using MCGalaxy.DB;
namespace MCGalaxy.Commands.Moderation {
public sealed class CmdHide : Command {

View File

@ -19,7 +19,7 @@ using System;
using System.Collections.Generic;
using System.IO;
namespace MCGalaxy {
namespace MCGalaxy.DB {
/// <summary> Stores per-player persistent data. </summary>
public static class PlayerDB {

View File

@ -17,11 +17,30 @@
*/
using System;
using System.Data;
using MCGalaxy.DB;
using MCGalaxy.SQL;
namespace MCGalaxy {
namespace MCGalaxy.DB {
/// <summary> Retrieves or sets player stats in the database. </summary>
public class PlayerData {
public const string DBTable = "Players";
public const string ColumnDeaths = "totalDeaths";
public const string ColumnLogins = "totalLogin";
public const string ColumnMoney = "Money";
public const string ColumnKicked = "totalKicked";
public const string ColumnColor = "color";
public const string ColumnTitle = "title";
public const string ColumnTColor = "title_color";
public const string ColumnFirstLogin = "FirstLogin";
public const string ColumnLastLogin = "LastLogin";
public const string ColumnTimeSpent = "TimeSpent";
public const string ColumnTotalBlocks = "totalBlocks";
public const string ColumnTotalCuboided = "totalCuboided";
public string Name, Color, Title, TitleColor, TotalTime, IP;
public DateTime FirstLogin, LastLogin;
public int UserID, Money, Deaths, Logins, Kicks;
@ -43,11 +62,11 @@ namespace MCGalaxy {
p.TotalDrawn = 0;
string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
Database.Backend.AddRow("Players", "Name, IP, FirstLogin, LastLogin, totalLogin, Title, " +
Database.Backend.AddRow(DBTable, "Name, IP, FirstLogin, LastLogin, totalLogin, Title, " +
"totalDeaths, Money, totalBlocks, totalKicked, TimeSpent",
p.name, p.ip, now, now, 1, "", 0, 0, 0, 0, p.time.ToDBTime());
using (DataTable ids = Database.Backend.GetRows("Players",
using (DataTable ids = Database.Backend.GetRows(DBTable,
"ID", "WHERE Name = @0", p.name)) {
if (ids.Rows.Count > 0) {
string id = ids.Rows[0]["ID"].ToString();
@ -87,22 +106,22 @@ namespace MCGalaxy {
data.IP = row["IP"].ToString().Trim();
data.UserID = ParseInt(row["ID"].ToString());
data.TotalTime = row["TimeSpent"].ToString();
data.FirstLogin = DateTime.Parse(row["FirstLogin"].ToString());
data.LastLogin = DateTime.Parse(row["LastLogin"].ToString());
data.TotalTime = row[ColumnTimeSpent].ToString();
data.FirstLogin = DateTime.Parse(row[ColumnFirstLogin].ToString());
data.LastLogin = DateTime.Parse(row[ColumnLastLogin].ToString());
data.Title = row["Title"].ToString().Trim();
data.Title = row[ColumnTitle].ToString().Trim();
data.Title.Cp437ToUnicodeInPlace();
data.TitleColor = ParseColor(row["title_color"]);
data.Color = ParseColor(row["color"]);
data.TitleColor = ParseColor(row[ColumnTColor]);
data.Color = ParseColor(row[ColumnColor]);
data.Money = ParseInt(row["Money"].ToString());
data.Deaths = ParseInt(row["TotalDeaths"].ToString());
data.Logins = ParseInt(row["totalLogin"].ToString());
data.Kicks = ParseInt(row["totalKicked"].ToString());
data.Money = ParseInt(row[ColumnMoney].ToString());
data.Deaths = ParseInt(row[ColumnDeaths].ToString());
data.Logins = ParseInt(row[ColumnLogins].ToString());
data.Kicks = ParseInt(row[ColumnKicked].ToString());
long blocks = ParseLong(row["totalBlocks"].ToString());
long cuboided = ParseLong(row["totalCuboided"].ToString());
long blocks = ParseLong(row[ColumnTotalBlocks].ToString());
long cuboided = ParseLong(row[ColumnTotalCuboided].ToString());
data.TotalModified = blocks & LowerBitsMask;
data.TotalPlaced = blocks >> LowerBits;
data.TotalDrawn = cuboided & LowerBitsMask;

View File

@ -0,0 +1,108 @@
/*
Copyright 2015 MCGalaxy
Dual-licensed under the Educational Community License, Version 2.0 and
the GNU General Public License, Version 3 (the "Licenses"); you may
not use this file except in compliance with the Licenses. You may
obtain a copy of the Licenses at
http://www.osedu.org/licenses/ECL-2.0
http://www.gnu.org/licenses/gpl-3.0.html
Unless required by applicable law or agreed to in writing,
software distributed under the Licenses are distributed on an "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the Licenses for the specific language governing
permissions and limitations under the Licenses.
*/
using System;
using System.Collections.Generic;
using System.IO;
namespace MCGalaxy.DB {
/// <summary> Outputs ordered stats from a column in a database table. </summary>
public sealed class TopStat {
public readonly string Identifier, Table, Column;
public readonly bool Ascending;
public readonly Func<string> Title;
public readonly Func<string, string> Formatter;
public TopStat(string identifier, string table, string col, Func<string> title,
Func<string, string> formatter, bool ascending = false) {
Identifier = identifier;
Table = table;
Column = col;
Title = title;
Formatter = formatter;
Ascending = ascending;
}
/// <summary> List of stats that can be ordered. </summary>
public static List<TopStat> Stats = new List<TopStat>() {
new TopStat("Logins", PlayerData.DBTable,
PlayerData.ColumnLogins,
() => "Most logins", FormatInteger),
new TopStat("Deaths", PlayerData.DBTable,
PlayerData.ColumnDeaths,
() => "Most deaths", FormatInteger),
new TopStat("Money", PlayerData.DBTable,
PlayerData.ColumnMoney,
() => "Most " + Server.moneys, FormatInteger),
new TopStat("Oldest", PlayerData.DBTable,
PlayerData.ColumnFirstLogin,
() => "Oldest players", FormatDate, true),
new TopStat("Newest", PlayerData.DBTable,
PlayerData.ColumnLastLogin,
() => "Most recent players", FormatDate),
new TopStat("Kicked", PlayerData.DBTable,
PlayerData.ColumnKicked,
() => "Most times kicked", FormatInteger),
new TopStat("Modified", PlayerData.DBTable,
PlayerData.ColumnTotalBlocks + " & " + PlayerData.LowerBitsMask,
() => "Most blocks modified", FormatInteger),
new TopStat("Drawn", PlayerData.DBTable,
PlayerData.ColumnTotalCuboided + " & " + PlayerData.LowerBitsMask,
() => "Most blocks drawn", FormatInteger),
new TopStat("Placed", PlayerData.DBTable,
PlayerData.ColumnTotalBlocks + " >> " + PlayerData.LowerBits,
() => "Most blocks placed", FormatInteger),
new TopStat("Deleted", PlayerData.DBTable,
PlayerData.ColumnTotalCuboided + " >> " + PlayerData.LowerBits,
() => "Most blocks deleted", FormatInteger),
// TODO: only add when ZS is running
};
public static string FormatInteger(string input) {
long value = PlayerData.ParseLong(input);
return value.ToString("N0");
}
public static string FormatDate(string input) {
DateTime time = DateTime.Parse(input);
TimeSpan delta = DateTime.Now - time;
return String.Format("{0:H:mm} on {0:d} ({1} ago)", time, delta.Shorten());
}
/*case "11":
col = "TotalInfected";
title = "&aMost players infected:";
table = "ZombieStats"; break;
case "12":
col = "TotalRounds";
title = "&aMost rounds survived:";
table = "ZombieStats"; break;
case "13":
col = "MaxInfected";
title = "&aMost consecutive infections:";
table = "ZombieStats"; break;
case "14":
col = "MaxRounds";
title = "&aMost consecutive rounds survived:";
table = "ZombieStats"; break;*/
}
}

View File

@ -17,6 +17,7 @@
*/
using System;
using System.Data;
using MCGalaxy.DB;
using MCGalaxy.SQL;
namespace MCGalaxy {

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses.
*/
using System;
using MCGalaxy.DB;
namespace MCGalaxy.Eco {

View File

@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using MCGalaxy.Commands;
using MCGalaxy.DB;
using MCGalaxy.Games;
namespace MCGalaxy.Eco {

View File

@ -411,6 +411,8 @@
<Compile Include="Database\IDatabaseBackend.cs" />
<Compile Include="Database\Backends\MySQL.cs" />
<Compile Include="Database\ParameterisedQuery.cs" />
<Compile Include="Database\PlayerData.cs" />
<Compile Include="Database\Stats\TopStat.cs" />
<Compile Include="Drawing\Brushes\Brush.cs" />
<Compile Include="Drawing\Brushes\CloudyBrush.cs" />
<Compile Include="Drawing\Brushes\PasteBrush.cs" />
@ -572,7 +574,6 @@
<Compile Include="Player\Player.Handlers.cs" />
<Compile Include="Player\Player.Login.cs" />
<Compile Include="Player\PlayerActions.cs" />
<Compile Include="Player\PlayerData.cs" />
<Compile Include="Player\PlayerInfo.cs" />
<Compile Include="Player\SpamChecker.cs" />
<Compile Include="Player\TabList.cs" />
@ -706,6 +707,7 @@
<ItemGroup>
<Folder Include="Commands\Maintenance" />
<Folder Include="Commands\Permissions" />
<Folder Include="Database\Stats" />
<Folder Include="Network\Heartbeat" />
<Folder Include="Network\IRCPlugin" />
<Folder Include="Network\Utils" />

View File

@ -17,6 +17,7 @@ using System.Collections.Generic;
using System.Data;
using System.IO;
using MCGalaxy.Commands;
using MCGalaxy.DB;
using MCGalaxy.Games;
using MCGalaxy.Network;
using MCGalaxy.SQL;

View File

@ -18,6 +18,7 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using MCGalaxy.Blocks;
using MCGalaxy.DB;
using MCGalaxy.Games;
using MCGalaxy.SQL;
using MCGalaxy.Network;

View File

@ -16,6 +16,7 @@ permissions and limitations under the Licenses.
using System;
using System.Collections.Generic;
using System.Data;
using MCGalaxy.DB;
using MCGalaxy.SQL;
namespace MCGalaxy {
@ -34,7 +35,7 @@ namespace MCGalaxy {
public static string GetColoredName(Player p, string name) {
Player target = FindExact(name);
return target != null && Entities.CanSee(p, target) ?
target.ColoredName : GetColor(name) + name; // TODO: select color from database?
target.ColoredName : GetColor(name) + name; // TODO: select color from database?
}