Initial WIP on database rewrite

This commit is contained in:
UnknownShadow200 2018-06-14 15:51:44 +10:00
parent 5857321031
commit fde77c5542
28 changed files with 571 additions and 515 deletions

View File

@ -18,32 +18,23 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using MCGalaxy.Maths;
using MCGalaxy.SQL; using MCGalaxy.SQL;
namespace MCGalaxy.Blocks.Extended { namespace MCGalaxy.Blocks.Extended {
public static class MessageBlock { public static class MessageBlock {
public static bool Handle(Player p, ushort x, ushort y, ushort z, bool alwaysRepeat) { public static bool Handle(Player p, ushort x, ushort y, ushort z, bool alwaysRepeat) {
if (!p.level.hasMessageBlocks) return false; if (!p.level.hasMessageBlocks) return false;
try { string message = Get(p.level.MapName, x, y, z);
DataTable Messages = Database.Backend.GetRows("Messages" + p.level.name, "*", if (message == null) return false;
"WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z); message = message.Replace("@p", p.name);
int last = Messages.Rows.Count - 1;
if (last == -1) { Messages.Dispose(); return false; }
string message = Messages.Rows[last]["Message"].ToString().Trim(); if (message != p.prevMsg || (alwaysRepeat || ServerConfig.RepeatMBs)) {
message = message.Replace("\\'", "\'"); Execute(p, message);
message = message.Cp437ToUnicode();
message = message.Replace("@p", p.name);
if (message != p.prevMsg || (alwaysRepeat || ServerConfig.RepeatMBs)) {
Execute(p, message);
}
return true;
} catch {
return false;
} }
return true;
} }
public static void Execute(Player p, string message) { public static void Execute(Player p, string message) {
@ -75,13 +66,13 @@ namespace MCGalaxy.Blocks.Extended {
Command.Search(ref cmdName, ref cmdArgs); Command.Search(ref cmdName, ref cmdArgs);
Command cmd = Command.Find(cmdName); Command cmd = Command.Find(cmdName);
if (cmd == null) return true; if (cmd == null) return true;
bool mbUseable = !cmd.MessageBlockRestricted && !cmd.type.CaselessContains("mod"); bool mbUseable = !cmd.MessageBlockRestricted && !cmd.type.CaselessContains("mod");
if (p.group.CanExecute(cmd) && (allCmds || mbUseable)) return true; if (p.group.CanExecute(cmd) && (allCmds || mbUseable)) return true;
Player.Message(p, "You cannot use %T/{0} %Sin a messageblock.", cmd.name); Player.Message(p, "You cannot use %T/{0} %Sin a messageblock.", cmd.name);
return false; return false;
} }
static string[] sep = new string[] { " |/" }; static string[] sep = new string[] { " |/" };
const StringSplitOptions opts = StringSplitOptions.RemoveEmptyEntries; const StringSplitOptions opts = StringSplitOptions.RemoveEmptyEntries;
@ -106,5 +97,38 @@ namespace MCGalaxy.Blocks.Extended {
text = message; return empty; text = message; return empty;
} }
} }
static object IterateAll(IDataRecord record, object arg) {
Vec3U16 pos;
pos.X = (ushort)record.GetInt32(0);
pos.Y = (ushort)record.GetInt32(1);
pos.Z = (ushort)record.GetInt32(2);
((List<Vec3U16>)arg).Add(pos);
return arg;
}
internal static List<Vec3U16> GetAll(string map) {
List<Vec3U16> coords = new List<Vec3U16>();
Database.Backend.IterateRows("Messages" + map, "X,Y,Z",
coords, IterateAll);
return coords;
}
internal static string Get(string map, ushort x, ushort y, ushort z) {
string msg = Database.GetString("Messages" + map, "Message",
"WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z);
if (msg == null) return null;
msg = msg.Trim().Replace("\\'", "\'");
msg = msg.Cp437ToUnicode();
return msg;
}
internal static void Delete(string map, ushort x, ushort y, ushort z) {
Database.Backend.DeleteRows("Messages" + map,
"WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z);
}
} }
} }

View File

@ -22,41 +22,55 @@ using MCGalaxy.SQL;
namespace MCGalaxy.Blocks.Extended { namespace MCGalaxy.Blocks.Extended {
public static class Portal { public static class Portal {
public class PortalExitData {
public string Map;
public int X, Y, Z;
}
static object IteratePortalExit(IDataRecord record, object arg) {
PortalExitData data = new PortalExitData();
data.Map = record.GetString("ExitMap");
data.X = record.GetInt32("ExitX");
data.Y = record.GetInt32("ExitY");
data.Z = record.GetInt32("ExitZ");
data.Map = data.Map.Cp437ToUnicode();
return data;
}
public static PortalExitData Get(string map, ushort x, ushort y, ushort z) {
object raw = Database.Backend.IterateRows("Portals" + map, "*", null, IteratePortalExit,
"WHERE EntryX=@0 AND EntryY=@1 AND EntryZ=@2", x, y, z);
return (PortalExitData)raw;
}
public static bool Handle(Player p, ushort x, ushort y, ushort z) { public static bool Handle(Player p, ushort x, ushort y, ushort z) {
if (!p.level.hasPortals) return false; if (!p.level.hasPortals) return false;
try { PortalExitData exit = Get(p.level.MapName, x, y, z);
DataTable Portals = Database.Backend.GetRows("Portals" + p.level.name, "*", if (exit == null) return false;
"WHERE EntryX=@0 AND EntryY=@1 AND EntryZ=@2", x, y, z); Orientation rot = p.Rot;
int last = Portals.Rows.Count - 1;
if (last == -1) { Portals.Dispose(); return false; } if (p.level.name != exit.Map) {
Orientation rot = p.Rot; Level curLevel = p.level;
p.summonedMap = exit.Map;
bool changedMap = false;
DataRow row = Portals.Rows[last]; try {
string map = row["ExitMap"].ToString(); changedMap = PlayerActions.ChangeMap(p, exit.Map);
map = map.Cp437ToUnicode(); } catch (Exception ex) {
Logger.LogError(ex);
if (p.level.name != map) { changedMap = false;
Level curLevel = p.level;
p.summonedMap = map;
bool changedMap = PlayerActions.ChangeMap(p, map);
p.summonedMap = null;
if (!changedMap) { Player.Message(p, "Unable to use this portal, as this portal goes to that map."); return true; }
p.BlockUntilLoad(10);
} }
x = ushort.Parse(row["ExitX"].ToString()); p.summonedMap = null;
y = ushort.Parse(row["ExitY"].ToString()); if (!changedMap) { Player.Message(p, "Unable to use this portal, as this portal goes to that map."); return true; }
z = ushort.Parse(row["ExitZ"].ToString()); p.BlockUntilLoad(10);
Position pos = Position.FromFeetBlockCoords(x, y, z);
p.SendPos(Entities.SelfID, pos, rot);
Portals.Dispose();
return true;
} catch {
return false;
} }
Position pos = Position.FromFeetBlockCoords(exit.X, exit.Y, exit.Z);
p.SendPos(Entities.SelfID, pos, rot);
return true;
} }
} }
} }

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses. permissions and limitations under the Licenses.
*/ */
using System; using System;
using System.Collections.Generic;
using System.Data; using System.Data;
using MCGalaxy.SQL; using MCGalaxy.SQL;
@ -25,19 +26,27 @@ namespace MCGalaxy.Commands.Chatting {
public override string type { get { return CommandTypes.Chat; } } public override string type { get { return CommandTypes.Chat; } }
public override bool SuperUseable { get { return false; } } public override bool SuperUseable { get { return false; } }
public override void Use(Player p, string message) { class MailEntry { public string Contents, Timestamp, From; }
if (!Database.TableExists("Inbox" + p.name)) { static object IterateInbox(IDataRecord record, object arg) {
MailEntry e = new MailEntry();
e.Contents = record.GetString("Contents");
e.Timestamp = record.GetString("TimeSent");
e.From = record.GetString("PlayerFrom");
((List<MailEntry>)arg).Add(e); return arg;
}
public override void Use(Player p, string message) {
List<MailEntry> entries = new List<MailEntry>();
Database.Backend.IterateRows("Inbox" + p.name, "*",
entries, IterateInbox, "ORDER BY TimeSent");
if (entries.Count == 0) {
Player.Message(p, "Your inbox is empty."); return; Player.Message(p, "Your inbox is empty."); return;
} }
string[] args = message.SplitSpaces(2); string[] args = message.SplitSpaces(2);
if (message.Length == 0) { if (message.Length == 0) {
using (DataTable Inbox = Database.Backend.GetRows("Inbox" + p.name, "*", "ORDER BY TimeSent")) { foreach (MailEntry entry in entries) { Output(p, entry); }
if (Inbox.Rows.Count == 0) { Player.Message(p, "No messages found."); return; }
foreach (DataRow row in Inbox.Rows) {
OutputMessage(p, row);
}
}
} else if (IsDeleteCommand(args[0])) { } else if (IsDeleteCommand(args[0])) {
if (args.Length == 1) { if (args.Length == 1) {
Player.Message(p, "You need to provide either \"all\" or a number."); return; Player.Message(p, "You need to provide either \"all\" or a number."); return;
@ -45,50 +54,44 @@ namespace MCGalaxy.Commands.Chatting {
Database.Backend.ClearTable("Inbox" + p.name); Database.Backend.ClearTable("Inbox" + p.name);
Player.Message(p, "Deleted all messages."); Player.Message(p, "Deleted all messages.");
} else { } else {
DeleteMessageByID(p, args[1]); DeleteByID(p, args[1], entries);
} }
} else { } else {
OutputMessageByID(p, message); OutputByID(p, message, entries);
} }
} }
static void DeleteMessageByID(Player p, string value) { static void DeleteByID(Player p, string value, List<MailEntry> entries) {
int num = 0; int num = 1;
if (!CommandParser.GetInt(p, value, "Message number", ref num, 0)) return; if (!CommandParser.GetInt(p, value, "Message number", ref num, 1)) return;
using (DataTable Inbox = Database.Backend.GetRows("Inbox" + p.name, "*", "ORDER BY TimeSent")) { if (num > entries.Count) {
if (num >= Inbox.Rows.Count) { Player.Message(p, "Message #{0} does not exist.", num);
Player.Message(p, "Message #{0} does not exist.", num); return; } else {
} MailEntry entry = entries[num - 1];
DataRow row = Inbox.Rows[num];
string time = Convert.ToDateTime(row["TimeSent"]).ToString("yyyy-MM-dd HH:mm:ss");
Database.Backend.DeleteRows("Inbox" + p.name, Database.Backend.DeleteRows("Inbox" + p.name,
"WHERE PlayerFrom=@0 AND TimeSent=@1", row["PlayerFrom"], time); "WHERE PlayerFrom=@0 AND TimeSent=@1", entry.From, entry.Timestamp);
Player.Message(p, "Deleted message #{0}", num); Player.Message(p, "Deleted message #{0}", num);
} }
} }
static void OutputMessageByID(Player p, string value) { static void OutputByID(Player p, string value, List<MailEntry> entries) {
int num = 0; int num = 1;
if (!CommandParser.GetInt(p, value, "Message number", ref num, 0)) return; if (!CommandParser.GetInt(p, value, "Message number", ref num, 1)) return;
using (DataTable Inbox = Database.Backend.GetRows("Inbox" + p.name, "*", "ORDER BY TimeSent")) { if (num > entries.Count) {
if (num >= Inbox.Rows.Count) { Player.Message(p, "Message #{0} does not exist.", num);
Player.Message(p, "Message #{0} does not exist.", num); return; } else {
} Output(p, entries[num - 1]);
OutputMessage(p, Inbox.Rows[num]);
} }
} }
static void OutputMessage(Player p, DataRow row) { static void Output(Player p, MailEntry entry) {
DateTime time = Convert.ToDateTime(row["TimeSent"]); TimeSpan delta = DateTime.Now - Convert.ToDateTime(entry.Timestamp);
TimeSpan delta = DateTime.Now - time; string sender = PlayerInfo.GetColoredName(p, entry.From);
string sender = PlayerInfo.GetColoredName(p, row["PlayerFrom"].ToString());
Player.Message(p, "From {0} &a{1} ago:", sender, delta.Shorten()); Player.Message(p, "From {0} &a{1} ago:", sender, delta.Shorten());
Player.Message(p, row["Contents"].ToString()); Player.Message(p, entry.Contents);
} }
public override void Help(Player p) { public override void Help(Player p) {

View File

@ -50,9 +50,7 @@ namespace MCGalaxy.Commands {
static List<CommandExtraPerms> list = new List<CommandExtraPerms>(); static List<CommandExtraPerms> list = new List<CommandExtraPerms>();
/// <summary> Finds the nth extra permission for a given command. </summary>
/// <returns> null if the nth extra permission does not exist. </returns>
public static CommandExtraPerms Find(string cmd, int num = 1) { public static CommandExtraPerms Find(string cmd, int num = 1) {
foreach (CommandExtraPerms perms in list) { foreach (CommandExtraPerms perms in list) {
if (perms.CmdName.CaselessEq(cmd) && perms.Number == num) return perms; if (perms.CmdName.CaselessEq(cmd) && perms.Number == num) return perms;
@ -60,18 +58,13 @@ namespace MCGalaxy.Commands {
return null; return null;
} }
/// <summary> Returns the lowest rank that has the nth extra permission for a given command. </summary>
/// <returns> defPerm if the nth extra permission does not exist, otherwise the lowest rank. </returns>
public static LevelPermission MinPerm(string cmd, LevelPermission defPerm, int num = 1) { public static LevelPermission MinPerm(string cmd, LevelPermission defPerm, int num = 1) {
CommandExtraPerms perms = Find(cmd, num); CommandExtraPerms perms = Find(cmd, num);
return perms == null ? defPerm : perms.MinRank; return perms == null ? defPerm : perms.MinRank;
} }
/// <summary> Returns the lowest rank that has the nth extra permission for a given command. </summary>
public static LevelPermission MinPerm(string cmd, int num = 1) { return Find(cmd, num).MinRank; } public static LevelPermission MinPerm(string cmd, int num = 1) { return Find(cmd, num).MinRank; }
/// <summary> Finds all extra permissions for a given command. </summary>
/// <remarks> list is empty when no extra permissions are found, not null. </remarks>
public static List<CommandExtraPerms> FindAll(string cmd) { public static List<CommandExtraPerms> FindAll(string cmd) {
List<CommandExtraPerms> allPerms = new List<CommandExtraPerms>(); List<CommandExtraPerms> allPerms = new List<CommandExtraPerms>();
foreach (CommandExtraPerms perms in list) { foreach (CommandExtraPerms perms in list) {

View File

@ -130,7 +130,7 @@ namespace MCGalaxy.Commands.Info {
Player.Message(p, "Block \"{0}\" appears as &b{1}", Player.Message(p, "Block \"{0}\" appears as &b{1}",
message, Block.GetName(p, Block.Convert(b))); message, Block.GetName(p, Block.Convert(b)));
BlockPerms.List[b].MessageCannotUse(p, "use"); BlockPerms.List[b].MessageCannotUse(p, "use");
DescribePhysics(p, message, b); DescribePhysics(p, message, b);
return true; return true;
} }

View File

@ -64,23 +64,23 @@ namespace MCGalaxy.Commands.Info {
Player.Message(p, "OpStats for {0} %Ssince {1}", Player.Message(p, "OpStats for {0} %Ssince {1}",
PlayerInfo.GetColoredName(p, name), start); PlayerInfo.GetColoredName(p, name), start);
int reviews = Query(start, end, name, "review", "LIKE 'next'"); int reviews = Count(start, end, name, "review", "LIKE 'next'");
int ranks = Query(start, end, name, "setrank", "!=''"); int ranks = Count(start, end, name, "setrank", "!=''");
int promotes = Query(start, end, name, "setrank", "LIKE '+up%'"); int promotes = Count(start, end, name, "setrank", "LIKE '+up%'");
int demotes = Query(start, end, name, "setrank", "LIKE '-down%'"); int demotes = Count(start, end, name, "setrank", "LIKE '-down%'");
int promotesOld = Query(start, end, name, "promote"); int promotesOld = Count(start, end, name, "promote");
int demotesOld = Query(start, end, name, "demote"); int demotesOld = Count(start, end, name, "demote");
int mutes = Query(start, end, name, "mute"); int mutes = Count(start, end, name, "mute");
int freezes = Query(start, end, name, "freeze"); int freezes = Count(start, end, name, "freeze");
int warns = Query(start, end, name, "warn"); int warns = Count(start, end, name, "warn");
int kicks = Query(start, end, name, "kick"); int kicks = Count(start, end, name, "kick");
int bans = Query(start, end, name, "ban"); int bans = Count(start, end, name, "ban");
int kickbans = Query(start, end, name, "kickban"); int kickbans = Count(start, end, name, "kickban");
int ipbans = Query(start, end, name, "banip"); int ipbans = Count(start, end, name, "banip");
int xbans = Query(start, end, name, "xban"); int xbans = Count(start, end, name, "xban");
int tempbans = Query(start, end, name, "tempban"); int tempbans = Count(start, end, name, "tempban");
Player.Message(p, " &a{0}%S bans, &a{1}%S IP-bans, &a{2}%S tempbans", Player.Message(p, " &a{0}%S bans, &a{1}%S IP-bans, &a{2}%S tempbans",
bans + kickbans + xbans, ipbans + xbans, tempbans); bans + kickbans + xbans, ipbans + xbans, tempbans);
@ -94,14 +94,10 @@ namespace MCGalaxy.Commands.Info {
static bool ValidTimespan(string value) { static bool ValidTimespan(string value) {
return value == "today" || value == "yesterday" || value == "thismonth" || value == "lastmonth" || value == "all"; return value == "today" || value == "yesterday" || value == "thismonth" || value == "lastmonth" || value == "all";
} }
static int Query(string start, string end, string name, string cmd, string msg = "!=''") { static int Count(string start, string end, string name, string cmd, string msg = "!=''") {
using (DataTable table = Database.Backend.GetRows( const string whereSQL = "WHERE Time >= @0 AND Time < @1 AND Name LIKE @2 AND Cmd LIKE @3 AND Cmdmsg ";
"Opstats", "COUNT(ID)" ,"WHERE Time >= @0 AND Time < @1 AND " + return Database.CountRows("Opstats", whereSQL + msg, start, end, name, cmd);
"Name LIKE @2 AND Cmd LIKE @3 AND Cmdmsg " + msg, start, end, name, cmd)) {
string count = table.Rows[0]["COUNT(id)"].ToString();
return PlayerData.ParseInt(count);
}
} }
public override void Help(Player p) { public override void Help(Player p) {

View File

@ -35,13 +35,14 @@ namespace MCGalaxy.Commands.Info {
static PerformanceCounter allPCounter = null; static PerformanceCounter allPCounter = null;
static PerformanceCounter cpuPCounter = null; static PerformanceCounter cpuPCounter = null;
public override void Use(Player p, string message) { public override void Use(Player p, string message) {
if (message.Length > 0) { Help(p); return; } if (message.Length > 0) { Help(p); return; }
int count = Database.CountRows("Players");
Player.Message(p, "Server's name: &b{0}%S", ServerConfig.Name); Player.Message(p, "Server's name: &b{0}%S", ServerConfig.Name);
Player.Message(p, "&a{0} %Splayers total. (&a{1} %Sonline, &8{2} banned%S)", Player.Message(p, "&a{0} %Splayers total. (&a{1} %Sonline, &8{2} banned%S)",
GetPlayerCount(), PlayerInfo.Online.Count, Group.BannedRank.Players.Count); count, PlayerInfo.Online.Count, Group.BannedRank.Players.Count);
Player.Message(p, "&a{0} %Slevels currently loaded. Currency is &3{1}%S.", Player.Message(p, "&a{0} %Slevels currently loaded. Currency is &3{1}%S.",
LevelInfo.Loaded.Count, ServerConfig.Currency); LevelInfo.Loaded.Count, ServerConfig.Currency);
@ -59,13 +60,7 @@ namespace MCGalaxy.Commands.Info {
if (HasExtraPerm(p, 1)) ShowServerStats(p); if (HasExtraPerm(p, 1)) ShowServerStats(p);
} }
static int GetPlayerCount() {
using (DataTable table = Database.Backend.GetRows("Players", "COUNT(id)")) {
return int.Parse(table.Rows[0]["COUNT(id)"].ToString());
}
}
void ShowServerStats(Player p) { void ShowServerStats(Player p) {
Process proc = Process.GetCurrentProcess(); Process proc = Process.GetCurrentProcess();
if (allPCounter == null) { if (allPCounter == null) {

View File

@ -178,32 +178,27 @@ namespace MCGalaxy.Commands.Maintenance {
static void UpdateDB(string name, string value, string column) { static void UpdateDB(string name, string value, string column) {
Database.Backend.UpdateRows("Players", column + " = @1", "WHERE Name = @0", name, value.UnicodeToCp437()); Database.Backend.UpdateRows("Players", column + " = @1",
"WHERE Name = @0", name, value.UnicodeToCp437());
}
static object IterateSetLong(IDataRecord record, object arg) { return record.GetInt64(0); }
static long GetLong(string name, string column) {
return (long)Database.Backend.IterateRows("Players", column, null, IterateSetLong,
"WHERE Name = @0", name);
} }
// special case handling for packed forms of totalBlocks and totalCuboided // special case handling for packed forms of totalBlocks and totalCuboided
static void UpdateDBLo(string name, string value, string column) { static void UpdateDBLo(string name, string value, string column) {
long loValue = long.Parse(value); long packed = GetLong(name, column) & ~PlayerData.LowerBitsMask; // hi value only
// OR with existing high bits of value in DB packed |= long.Parse(value);
using (DataTable results = Database.Backend.GetRows("Players", column, "WHERE Name = @0", name)) { UpdateDB(name, packed.ToString(), column);
if (results.Rows.Count > 0) {
long curValue = PlayerData.ParseLong(results.Rows[0][column].ToString());
loValue |= (curValue & ~PlayerData.LowerBitsMask);
}
}
Database.Backend.UpdateRows("Players", column + " = @1", "WHERE Name = @0", name, loValue);
} }
static void UpdateDBHi(string name, string value, string column) { static void UpdateDBHi(string name, string value, string column) {
long hiValue = long.Parse(value) << PlayerData.LowerBits; long packed = GetLong(name, column) & PlayerData.LowerBitsMask; // lo value only
// OR with existing low bits of value in DB packed |= long.Parse(value) << PlayerData.LowerBits;
using (DataTable results = Database.Backend.GetRows("Players", column, "WHERE Name = @0", name)) { UpdateDB(name, packed.ToString(), column);
if (results.Rows.Count > 0) {
long curValue = PlayerData.ParseLong(results.Rows[0][column].ToString());
hiValue |= (curValue & PlayerData.LowerBitsMask);
}
}
Database.Backend.UpdateRows("Players", column + " = @1", "WHERE Name = @0", name, hiValue);
} }
static void MessageDataChanged(Player p, string name, string type, string value) { static void MessageDataChanged(Player p, string name, string type, string value) {

View File

@ -103,23 +103,19 @@ namespace MCGalaxy.Commands.Building {
args.Message = Colors.Escape(args.Message); args.Message = Colors.Escape(args.Message);
args.Message = args.Message.UnicodeToCp437(); args.Message = args.Message.UnicodeToCp437();
string lvlName = p.level.name; string map = p.level.name;
object locker = ThreadSafeCache.DBCache.GetLocker(lvlName); object locker = ThreadSafeCache.DBCache.GetLocker(map);
lock (locker) { lock (locker) {
Database.Backend.CreateTable("Messages" + lvlName, LevelDB.createMessages); Database.Backend.CreateTable("Messages" + map, LevelDB.createMessages);
p.level.hasMessageBlocks = true; p.level.hasMessageBlocks = true;
int count = 0; int count = Database.CountRows("Messages" + map,
using (DataTable Messages = Database.Backend.GetRows("Messages" + lvlName, "*", "WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z);
"WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z)) {
count = Messages.Rows.Count;
}
if (count == 0) { if (count == 0) {
Database.Backend.AddRow("Messages" + lvlName, "X, Y, Z, Message", x, y, z, args.Message); Database.Backend.AddRow("Messages" + map, "X, Y, Z, Message", x, y, z, args.Message);
} else { } else {
Database.Backend.UpdateRows("Messages" + lvlName, "Message=@3", Database.Backend.UpdateRows("Messages" + map, "Message=@3",
"WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z, args.Message); "WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z, args.Message);
} }
} }
@ -130,32 +126,18 @@ namespace MCGalaxy.Commands.Building {
void ShowMessageBlocks(Player p) { void ShowMessageBlocks(Player p) {
p.showMBs = !p.showMBs; p.showMBs = !p.showMBs;
int count = 0; List<Vec3U16> coords = MessageBlock.GetAll(p.level.MapName);
if (p.level.hasMessageBlocks) { foreach (Vec3U16 pos in coords) {
using (DataTable table = Database.Backend.GetRows("Messages" + p.level.name, "*")) { if (p.showMBs) {
count = table.Rows.Count; p.SendBlockchange(pos.X, pos.Y, pos.Z, Block.Green);
if (p.showMBs) { ShowMessageBlocks(p, table); } } else {
else { HideMessageBlocks(p, table); } p.RevertBlock(pos.X, pos.Y, pos.Z);
} }
} }
Player.Message(p, "Now {0} %SMBs.", p.showMBs ? "showing &a" + count : "hiding");
}
static void ShowMessageBlocks(Player p, DataTable table) {
foreach (DataRow row in table.Rows) {
p.SendBlockchange(U16(row["X"]), U16(row["Y"]), U16(row["Z"]), Block.Green);
}
}
static void HideMessageBlocks(Player p, DataTable table) {
foreach (DataRow row in table.Rows) {
p.RevertBlock(U16(row["X"]), U16(row["Y"]), U16(row["Z"]));
}
}
static ushort U16(object x) { return Convert.ToUInt16(x); }
Player.Message(p, "Now {0} %SMBs.", p.showMBs ? "showing &a" + coords.Count : "hiding");
}
static string Format(BlockID block, Player p, BlockProps[] props) { static string Format(BlockID block, Player p, BlockProps[] props) {
if (!props[block].IsMessageBlock) return null; if (!props[block].IsMessageBlock) return null;

View File

@ -116,15 +116,11 @@ namespace MCGalaxy.Commands.Building {
lock (locker) { lock (locker) {
Database.Backend.CreateTable("Portals" + lvlName, LevelDB.createPortals); Database.Backend.CreateTable("Portals" + lvlName, LevelDB.createPortals);
Level map = LevelInfo.FindExact(P.Map); Level lvl = LevelInfo.FindExact(P.Map);
if (map != null) map.hasPortals = true; if (lvl != null) lvl.hasPortals = true;
int count = 0; int count = Database.CountRows("Portals" + lvlName,
using (DataTable portals = Database.Backend.GetRows("Portals" + lvlName, "*", "WHERE EntryX=@0 AND EntryY=@1 AND EntryZ=@2", P.x, P.y, P.z);
"WHERE EntryX=@0 AND EntryY=@1 AND EntryZ=@2", P.x, P.y, P.z)) {
count = portals.Rows.Count;
}
if (count == 0) { if (count == 0) {
Database.Backend.AddRow("Portals" + lvlName, "EntryX, EntryY, EntryZ, ExitX, ExitY, ExitZ, ExitMap", Database.Backend.AddRow("Portals" + lvlName, "EntryX, EntryY, EntryZ, ExitX, ExitY, ExitZ, ExitMap",
P.x, P.y, P.z, x, y, z, dstMap); P.x, P.y, P.z, x, y, z, dstMap);
@ -158,7 +154,7 @@ namespace MCGalaxy.Commands.Building {
if (p.showPortals) { ShowPortals(p, table); } if (p.showPortals) { ShowPortals(p, table); }
else { HidePortals(p, table); } else { HidePortals(p, table); }
} }
} }
Player.Message(p, "Now {0} %Sportals.", p.showPortals ? "showing &a" + count : "hiding"); Player.Message(p, "Now {0} %Sportals.", p.showPortals ? "showing &a" + count : "hiding");
} }

View File

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using MCGalaxy.Commands.Building;
using MCGalaxy.Drawing.Ops;
using MCGalaxy.Drawing.Brushes;
using MCGalaxy.Maths;
using BlockID = System.UInt16;
namespace MCGalaxy.Commands {
public class Write2DrawOp : DrawOp {
public override string Name { get { return "Write2"; } }
public string Text;
public override long BlocksAffected(Level lvl, Vec3S32[] marks) { return Text.Length; }
Vec3S32 dir, pos;
public override void Perform(Vec3S32[] marks, Brush brush, DrawOpOutput output) {
Vec3S32 p1 = marks[0], p2 = marks[1];
if (Math.Abs(p2.X - p1.X) > Math.Abs(p2.Z - p1.Z)) {
dir.X = p2.X > p1.X ? 1 : -1;
} else {
dir.Z = p2.Z > p1.Z ? 1 : -1;
}
pos = p1;
foreach (char c in Text) { DrawLetter(Player, c, output); }
}
void DrawLetter(Player p, char c, DrawOpOutput output) {
if ((int)c >= 256 || letters[c] == 0) {
if (c != ' ') Player.Message(p, "\"{0}\" is not currently supported, replacing with space", c);
} else {
BlockID block = Block.FromRaw(letters[c]);
output(Place((ushort)pos.X, (ushort)pos.Y, (ushort)pos.Z, block));
}
pos += dir;
}
static BlockID[] letters;
static Write2DrawOp() {
letters = new BlockID[256];
letters['.'] = 520;
letters['!'] = 521;
letters['/'] = 522;
letters['?'] = 523;
for (int i = '0'; i <= '9'; i++) {
letters[i] = (BlockID)(484 + (i - '0'));
}
for (int i = 'A'; i <= 'Z'; i++) {
letters[i] = (BlockID)(494 + (i - 'A'));
}
}
}
public class CmdWrite2 : DrawCmd {
public override string name { get { return "Write2"; } }
public override LevelPermission defaultRank { get { return LevelPermission.AdvBuilder; } }
protected override string SelectionType { get { return "direction"; } }
protected override string PlaceMessage { get { return "Place or break two blocks to determine direction."; } }
protected override DrawOp GetDrawOp(DrawArgs dArgs) {
Player p = dArgs.Player;
if (dArgs.Message.Length == 0) { Help(p); return null; }
Write2DrawOp op = new Write2DrawOp();
op.Text = dArgs.Message.ToUpper();
return op;
}
protected override void GetMarks(DrawArgs dArgs, ref Vec3S32[] m) {
if (m[0].X != m[1].X || m[0].Z != m[1].Z) return;
Player.Message(dArgs.Player, "No direction was selected");
m = null;
}
public override void Help(Player p) {
Player.Message(p, "%T/Write2 [message]");
Player.Message(p, "%HWrites [message] in letter blocks");
}
}
}

View File

@ -44,8 +44,8 @@ namespace MCGalaxy.SQL {
public override void CreateDatabase() { public override void CreateDatabase() {
ParameterisedQuery query = GetStaticParameterised(); string syntax = "CREATE DATABASE if not exists `" + ServerConfig.MySQLDatabaseName + "`";
Database.Execute(query, "CREATE DATABASE if not exists `" + ServerConfig.MySQLDatabaseName + "`", true); Database.Do(syntax, true, null, null, null);
} }
public override BulkTransaction CreateBulk() { public override BulkTransaction CreateBulk() {
@ -58,8 +58,8 @@ namespace MCGalaxy.SQL {
protected internal override ParameterisedQuery GetStaticParameterised() { protected internal override ParameterisedQuery GetStaticParameterised() {
return queryInstance; return queryInstance;
} }
public override string FastGetDateTime(IDataRecord record, int col) { public override string FastGetDateTime(IDataRecord record, int col) {
DateTime date = record.GetDateTime(col); DateTime date = record.GetDateTime(col);
return date.ToString("yyyy-MM-dd HH:mm:ss"); return date.ToString("yyyy-MM-dd HH:mm:ss");
@ -86,34 +86,21 @@ namespace MCGalaxy.SQL {
cmd = cmd.Insert(cmd.LastIndexOf(")"), ", PRIMARY KEY (`" + name + "`)"); cmd = cmd.Insert(cmd.LastIndexOf(")"), ", PRIMARY KEY (`" + name + "`)");
} }
static object IterateExists(IDataRecord record, object arg) { return ""; }
public override bool TableExists(string table) { public override bool TableExists(string table) {
ValidateTable(table); ValidateTable(table);
const string syntax = "SELECT * FROM information_schema.tables WHERE table_name = @0 AND table_schema = @1"; return Database.Iterate("SHOW TABLES LIKE '" + table + "'",
using (DataTable results = Database.Fill(syntax, table, ServerConfig.MySQLDatabaseName)) { null, IterateExists) != null;
return results.Rows.Count > 0;
}
} }
public override List<string> AllTables() { public override List<string> AllTables() {
using (DataTable results = Database.Fill("SHOW TABLES")) { return Database.GetStrings("SHOW TABLES");
List<string> tables = new List<string>(results.Rows.Count);
foreach (DataRow row in results.Rows) {
tables.Add(row[0].ToString());
}
return tables;
}
} }
public override List<string> ColumnNames(string table) { public override List<string> ColumnNames(string table) {
ValidateTable(table); ValidateTable(table);
using (DataTable results = Database.Fill("DESCRIBE `" + table + "`")) { return Database.GetStrings("DESCRIBE `" + table + "`");
List<string> columns = new List<string>(results.Rows.Count);
foreach (DataRow row in results.Rows) {
columns.Add(row["Field"].ToString());
}
return columns;
}
} }
public override void RenameTable(string srcTable, string dstTable) { public override void RenameTable(string srcTable, string dstTable) {
@ -127,7 +114,7 @@ namespace MCGalaxy.SQL {
ValidateTable(table); ValidateTable(table);
string syntax = "TRUNCATE TABLE `" + table + "`"; string syntax = "TRUNCATE TABLE `" + table + "`";
Database.Execute(syntax); Database.Execute(syntax);
} }
protected override void CreateTableColumns(StringBuilder sql, ColumnDesc[] columns) { protected override void CreateTableColumns(StringBuilder sql, ColumnDesc[] columns) {
string priKey = null; string priKey = null;
@ -146,36 +133,41 @@ namespace MCGalaxy.SQL {
} }
sql.AppendLine(); sql.AppendLine();
} }
} }
static object IterateFields(IDataRecord record, object arg) {
string[] field = new string[record.FieldCount];
for (int i = 0; i < field.Length; i++) { field[i] = record.GetString(i); }
((List<string[]>)arg).Add(field);
return arg;
}
public override void PrintSchema(string table, TextWriter w) { public override void PrintSchema(string table, TextWriter w) {
string pri = "";
w.WriteLine("CREATE TABLE IF NOT EXISTS `{0}` (", table); w.WriteLine("CREATE TABLE IF NOT EXISTS `{0}` (", table);
List<string[]> fields = new List<string[]>();
Database.Iterate("DESCRIBE `" + table + "`", fields, IterateFields);
using (DataTable schema = Database.Fill("DESCRIBE `" + table + "`")) { string pri = "";
string[] data = new string[schema.Columns.Count]; for (int i = 0; i < fields.Count; i++) {
foreach (DataRow row in schema.Rows) { string[] field = fields[i];
for (int col = 0; col < schema.Columns.Count; col++) {
data[col] = row[col].ToString(); if (field[3].CaselessEq("pri")) pri = field[0];
} string value = field[2].CaselessEq("no") ? "NOT NULL" : "DEFAULT NULL";
if (field[4].Length > 0) value += " DEFAULT '" + field[4] + "'";
if (data[3].CaselessEq("pri")) pri = data[0]; if (field[5].Length > 0) value += " " + field[5];
string value = data[2].CaselessEq("no") ? "NOT NULL" : "DEFAULT NULL";
if (data[4].Length > 0) value += " DEFAULT '" + data[4] + "'";
if (data[5].Length > 0) value += " " + data[5];
string suffix = pri.Length == 0 && row == schema.Rows[schema.Rows.Count - 1] ? "" : ","; string suffix = pri.Length == 0 && (i == fields.Count - 1) ? "" : ",";
w.WriteLine("`{0}` {1} {2}{3}", row[0], row[1], value, suffix); w.WriteLine("`{0}` {1} {2}{3}", field[0], field[1], value, suffix);
}
} }
if (pri.Length > 0) w.Write("PRIMARY KEY (`{0}`)", pri); if (pri.Length > 0) w.Write("PRIMARY KEY (`{0}`)", pri);
w.WriteLine(");"); w.WriteLine(");");
} }
public override void AddColumn(string table, ColumnDesc col, string colAfter) { public override void AddColumn(string table, ColumnDesc col, string colAfter) {
ValidateTable(table); ValidateTable(table);
string syntax = "ALTER TABLE `" + table + "` ADD COLUMN " string syntax = "ALTER TABLE `" + table + "` ADD COLUMN "
+ col.Column + " " + col.FormatType(); + col.Column + " " + col.FormatType();
if (colAfter.Length > 0) syntax += " AFTER " + colAfter; if (colAfter.Length > 0) syntax += " AFTER " + colAfter;
Database.Execute(syntax); Database.Execute(syntax);
@ -227,5 +219,5 @@ namespace MCGalaxy.SQL {
protected override IDbDataParameter CreateParameter() { protected override IDbDataParameter CreateParameter() {
return new MySqlParameter(); return new MySqlParameter();
} }
} }
} }

View File

@ -30,7 +30,7 @@ namespace MCGalaxy.SQL {
public static IDatabaseBackend Instance = new SQLiteBackend(); public static IDatabaseBackend Instance = new SQLiteBackend();
static ParameterisedQuery queryInstance = new SQLiteParameterisedQuery(); static ParameterisedQuery queryInstance = new SQLiteParameterisedQuery();
static string connFormat = "Data Source =" + Utils.FolderPath + "/MCGalaxy.db; Version =3; Pooling ={0}; Max Pool Size =300;"; static string connFormat = "Data Source =" + Utils.FolderPath + "/MCGalaxy.db; Version =3; Pooling ={0}; Max Pool Size =300;";
public override string ConnectionString { public override string ConnectionString {
get { return string.Format(connFormat, ServerConfig.DatabasePooling); } get { return string.Format(connFormat, ServerConfig.DatabasePooling); }
} }
@ -60,35 +60,36 @@ namespace MCGalaxy.SQL {
return record.GetString(col); // reader.GetDateTime is extremely slow so avoid it return record.GetString(col); // reader.GetDateTime is extremely slow so avoid it
} }
public override bool TableExists(string table) { public override bool TableExists(string table) {
ValidateTable(table); ValidateTable(table);
using (DataTable results = GetRows("sqlite_master", "name", return Database.CountRows("sqlite_master",
"WHERE type='table' AND name=@0", table)) { "WHERE type='table' AND name=@0", table) > 0;
return results.Rows.Count > 0;
}
} }
public override List<string> AllTables() { public override List<string> AllTables() {
using (DataTable results = GetRows("sqlite_master", "name", "WHERE type='table'")) { const string syntax = "SELECT name from sqlite_master WHERE type='table'";
List<string> tables = new List<string>(results.Rows.Count); List<string> tables = Database.GetStrings(syntax);
foreach (DataRow row in results.Rows) {
if (row["name"].ToString().StartsWith("sqlite_")) continue; // exclude sqlite built-in database tables
tables.Add(row["name"].ToString()); for (int i = tables.Count - 1; i >= 0; i--) {
} if (tables[i].StartsWith("sqlite_")) tables.RemoveAt(i);
return tables;
} }
return tables;
}
static object IterateColumnNames(IDataRecord record, object arg) {
List<string> columns = (List<string>)arg;
columns.Add(record.GetString("name"));
return arg;
} }
public override List<string> ColumnNames(string table) { public override List<string> ColumnNames(string table) {
ValidateTable(table); ValidateTable(table);
using (DataTable results = Database.Fill("PRAGMA table_info(`" + table + "`)")) { List<string> columns = new List<string>();
List<string> columns = new List<string>(results.Rows.Count);
foreach (DataRow row in results.Rows) { Database.Iterate("PRAGMA table_info(`" + table + "`)",
columns.Add(row["name"].ToString()); columns, IterateColumnNames);
} return columns;
return columns;
}
} }
public override void RenameTable(string srcTable, string dstTable) { public override void RenameTable(string srcTable, string dstTable) {
@ -105,7 +106,7 @@ namespace MCGalaxy.SQL {
} }
protected override void CreateTableColumns(StringBuilder sql, ColumnDesc[] columns) { protected override void CreateTableColumns(StringBuilder sql, ColumnDesc[] columns) {
string priKey = null; string priKey = null;
for (int i = 0; i < columns.Length; i++) { for (int i = 0; i < columns.Length; i++) {
ColumnDesc col = columns[i]; ColumnDesc col = columns[i];
sql.Append(col.Column).Append(' '); sql.Append(col.Column).Append(' ');
@ -135,21 +136,20 @@ namespace MCGalaxy.SQL {
} }
public override void PrintSchema(string table, TextWriter w) { public override void PrintSchema(string table, TextWriter w) {
const string syntax = "SELECT sql FROM sqlite_master WHERE tbl_name = @0 AND type = 'table'"; const string syntax = "SELECT sql from sqlite_master WHERE tbl_name = @0 AND type = 'table'";
using (DataTable schema = Database.Fill(syntax + CaselessWhereSuffix, table)) { List<string> all = Database.GetStrings(syntax + CaselessWhereSuffix, table);
foreach (DataRow row in schema.Rows) {
string sql = row[0].ToString(); for (int i = 0; i < all.Count; i++) {
sql = sql.Replace(" " + table, " `" + table + "`"); string sql = all[i];
sql = sql.Replace("CREATE TABLE `" + table + "`", "CREATE TABLE IF NOT EXISTS `" + table + "`"); sql = sql.Replace(" " + table, " `" + table + "`");
w.WriteLine(sql + ";"); sql = sql.Replace("CREATE TABLE `" + table + "`", "CREATE TABLE IF NOT EXISTS `" + table + "`");
} w.WriteLine(sql + ";");
} }
} }
public override void AddColumn(string table, ColumnDesc col, string colAfter) { public override void AddColumn(string table, ColumnDesc col, string colAfter) {
ValidateTable(table); ValidateTable(table);
string syntax = "ALTER TABLE `" + table + "` ADD COLUMN " string syntax = "ALTER TABLE `" + table + "` ADD COLUMN " + col.Column + " " + col.FormatType();
+ col.Column + " " + col.FormatType();
Database.Execute(syntax); Database.Execute(syntax);
} }
@ -160,9 +160,9 @@ namespace MCGalaxy.SQL {
} }
public sealed class SQLiteBulkTransaction : BulkTransaction { public sealed class SQLiteBulkTransaction : BulkTransaction {
public SQLiteBulkTransaction(string connString) { public SQLiteBulkTransaction(string connString) {
connection = new SQLiteConnection(connString); connection = new SQLiteConnection(connString);
connection.Open(); connection.Open();
transaction = connection.BeginTransaction(); transaction = connection.BeginTransaction();
@ -177,7 +177,7 @@ namespace MCGalaxy.SQL {
} }
} }
public sealed class SQLiteParameterisedQuery : ParameterisedQuery { public sealed class SQLiteParameterisedQuery : ParameterisedQuery {
protected override bool MultipleSchema { get { return false; } } protected override bool MultipleSchema { get { return false; } }
protected override IDbConnection CreateConnection(string connString) { protected override IDbConnection CreateConnection(string connString) {

View File

@ -17,6 +17,7 @@
*/ */
using System; using System;
using System.Data; using System.Data;
using MCGalaxy.Blocks.Extended;
using MCGalaxy.SQL; using MCGalaxy.SQL;
using BlockID = System.UInt16; using BlockID = System.UInt16;
@ -26,7 +27,7 @@ namespace MCGalaxy.DB {
public static class BlockDBChange { public static class BlockDBChange {
public static void Output(Player p, string name, BlockDBEntry e) { public static void Output(Player p, string name, BlockDBEntry e) {
BlockID oldBlock = e.OldBlock, newBlock = e.NewBlock; BlockID oldBlock = e.OldBlock, newBlock = e.NewBlock;
DateTime time = BlockDB.Epoch.AddSeconds(e.TimeDelta); DateTime time = BlockDB.Epoch.AddSeconds(e.TimeDelta);
TimeSpan delta = DateTime.UtcNow.Subtract(time); TimeSpan delta = DateTime.UtcNow.Subtract(time);
name = PlayerInfo.GetColoredName(p, name); name = PlayerInfo.GetColoredName(p, name);
@ -46,43 +47,21 @@ namespace MCGalaxy.DB {
public static void OutputMessageBlock(Player p, BlockID block, ushort x, ushort y, ushort z) { public static void OutputMessageBlock(Player p, BlockID block, ushort x, ushort y, ushort z) {
if (!p.level.Props[block].IsMessageBlock) return; if (!p.level.Props[block].IsMessageBlock) return;
string message = MessageBlock.Get(p.level.MapName, x, y, z);
try {
if (!Database.TableExists("Messages" + p.level.name)) return; if (message == null) return;
DataTable messages = Database.Backend.GetRows("Messages" + p.level.name, "*", Player.Message(p, "Message Block contents: {0}", message);
"WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z);
int last = messages.Rows.Count - 1;
if (last == -1) { messages.Dispose(); return; }
string message = messages.Rows[last]["Message"].ToString().Trim();
message = message.Replace("\\'", "\'");
Player.Message(p, "Message Block contents: {0}", message);
} catch {
}
} }
public static void OutputPortal(Player p, BlockID block, ushort x, ushort y, ushort z) { public static void OutputPortal(Player p, BlockID block, ushort x, ushort y, ushort z) {
if (!p.level.Props[block].IsPortal) return; if (!p.level.Props[block].IsPortal) return;
Portal.PortalExitData exit = Portal.Get(p.level.MapName, x, y, z);
try {
if (!Database.TableExists("Portals" + p.level.name)) return; if (exit == null) return;
DataTable portals = Database.Backend.GetRows("Portals" + p.level.name, "*", Player.Message(p, "Portal destination: ({0}, {1}, {2}) in {3}",
"WHERE EntryX=@0 AND EntryY=@1 AND EntryZ=@2", x, y, z); exit.X, exit.Y, exit.Z, exit.Map);
int last = portals.Rows.Count - 1;
if (last == -1) { portals.Dispose(); return; }
string exitMap = portals.Rows[last]["ExitMap"].ToString().Trim();
ushort exitX = U16(portals.Rows[last]["ExitX"]);
ushort exitY = U16(portals.Rows[last]["ExitY"]);
ushort exitZ = U16(portals.Rows[last]["ExitZ"]);
Player.Message(p, "Portal destination: ({0}, {1}, {2}) in {3}",
exitX, exitY, exitZ, exitMap);
} catch {
}
} }
static ushort U16(object x) { return ushort.Parse(x.ToString()); }
static string FormatReason(ushort flags) { static string FormatReason(ushort flags) {
if ((flags & BlockDBFlags.Painted) != 0) return " (Painted)"; if ((flags & BlockDBFlags.Painted) != 0) return " (Painted)";
if ((flags & BlockDBFlags.Drawn) != 0) return " (Drawn)"; if ((flags & BlockDBFlags.Drawn) != 0) return " (Drawn)";

View File

@ -45,7 +45,7 @@ namespace MCGalaxy.DB {
mapName = table.Substring("Block".Length); mapName = table.Substring("Block".Length);
try { try {
Database.Iterate("SELECT * FROM `" + table + "`", DumpRow); Database.Backend.IterateRows(table, "*", null, DumpRow);
WriteBuffer(true); WriteBuffer(true);
AppendCbdbFile(); AppendCbdbFile();
SaveCbdbFile(); SaveCbdbFile();
@ -58,8 +58,8 @@ namespace MCGalaxy.DB {
Database.Backend.DeleteTable(table); Database.Backend.DeleteTable(table);
} }
bool DumpRow(IDataRecord record) { object DumpRow(IDataRecord record, object arg) {
if (errorOccurred) return false; if (errorOccurred) return arg;
try { try {
if (stream == null) { if (stream == null) {
@ -87,7 +87,7 @@ namespace MCGalaxy.DB {
Logger.LogError(ex); Logger.LogError(ex);
errorOccurred = true; errorOccurred = true;
} }
return true; return arg;
} }
void WriteBuffer(bool force) { void WriteBuffer(bool force) {

View File

@ -33,25 +33,25 @@ namespace MCGalaxy.DB {
if (id > MaxPlayerID - invalid.Count) if (id > MaxPlayerID - invalid.Count)
return invalid[MaxPlayerID - id]; return invalid[MaxPlayerID - id];
using (DataTable ids = Database.Backend.GetRows("Players", "Name", "WHERE ID=@0", id)) { string name = Database.GetString("Players", "Name", "WHERE ID=@0", id);
if (ids.Rows.Count == 0) return "ID#" + id; return name != null ? name : "ID#" + id;
return ids.Rows[0]["Name"].ToString(); }
}
static object IterateFindIds(IDataRecord record, object arg) {
List<int> ids = (List<int>)arg;
ids.Add(record.GetInt32(0));
return arg;
} }
public static int[] FindIds(string name) { public static int[] FindIds(string name) {
List<string> invalid = Server.invalidIds.All(); List<string> invalid = Server.invalidIds.All();
List<int> ids = new List<int>(); List<int> ids = new List<int>();
int index = invalid.CaselessIndexOf(name); int i = invalid.CaselessIndexOf(name);
if (index >= 0) ids.Add(MaxPlayerID - index); if (i >= 0) ids.Add(MaxPlayerID - i);
using (DataTable names = Database.Backend.GetRows("Players", "ID", "WHERE Name=@0", name)) { Database.Backend.IterateRows("Players", "ID", ids, IterateFindIds,
foreach (DataRow row in names.Rows) { "WHERE Name=@0", name);
string raw = row["ID"].ToString();
ids.Add(PlayerData.ParseInt(raw));
}
}
return ids.ToArray(); return ids.ToArray();
} }

View File

@ -16,62 +16,69 @@
permissions and limitations under the Licenses. permissions and limitations under the Licenses.
*/ */
using System; using System;
using System.Collections.Generic;
using System.Data; using System.Data;
using System.IO; using System.IO;
namespace MCGalaxy.SQL { namespace MCGalaxy.SQL {
/// <summary> Callback function invoked on a row returned from an SQL query. </summary>
public delegate object ReaderCallback(IDataRecord record, object arg);
public static class Database { public static class Database {
public static IDatabaseBackend Backend; public static IDatabaseBackend Backend;
/// <summary> Returns whether the given table exists in the database. </summary>
public static bool TableExists(string table) { return Backend.TableExists(table); } public static bool TableExists(string table) { return Backend.TableExists(table); }
/// <summary> Executes an SQL command that does not return any results. </summary> static object IterateCount(IDataRecord record, object arg) { return record.GetInt32(0); }
public static void Execute(string sql) { public static int CountRows(string table, string modifier = "", params object[] args) {
ParameterisedQuery query = Backend.GetStaticParameterised(); return (int)Backend.IterateRows(table, "COUNT(*)", 0,
Execute(query, sql, false, null); IterateCount, modifier, args);
} }
static object IterateString(IDataRecord record, object arg) { return record.GetString(0); }
public static string GetString(string table, string column,
string modifier = "", params object[] args) {
return (string)Backend.IterateRows(table, column, null,
IterateString, modifier, args);
}
static object IterateList(IDataRecord record, object arg) {
((List<string>)arg).Add(record.GetString(0)); return arg;
}
internal static List<string> GetStrings(string sql, params object[] args) {
List<string> values = new List<string>();
Iterate(sql, values, IterateList, args);
return values;
}
/// <summary> Executes an SQL command that does not return any results. </summary> /// <summary> Executes an SQL command that does not return any results. </summary>
public static void Execute(string sql, params object[] args) { public static void Execute(string sql, params object[] args) {
ParameterisedQuery query = Backend.CreateParameterised(); Do(sql, false, null, null, null, args);
Execute(query, sql, false, args);
} }
/// <summary> Executes an SQL query, invoking callback function on each returned row. </summary> /// <summary> Executes an SQL query, invoking callback function on each returned row. </summary>
public static void Iterate(string sql, ReaderCallback callback, params object[] args) { public static object Iterate(string sql, object arg, ReaderCallback callback, params object[] args) {
ParameterisedQuery query = Backend.CreateParameterised(); return Do(sql, false, arg, null, callback, args);
DoDatabaseCall(query, sql, false, null, callback, args);
} }
/// <summary> Executes an SQL query, returning all read rows into a DataTable. </summary> /// <summary> Executes an SQL query, returning all read rows into a DataTable. </summary>
public static DataTable Fill(string sql) {
ParameterisedQuery query = Backend.GetStaticParameterised();
return Fill(query, sql, null);
}
/// <summary> Executes an SQL query, returning all read rows into a DataTable. </summary>
public static DataTable Fill(string sql, params object[] args) { public static DataTable Fill(string sql, params object[] args) {
ParameterisedQuery query = Backend.CreateParameterised(); using (DataTable results = new DataTable("toReturn")) {
return Fill(query, sql, args); Do(sql, false, null, results, null, args);
}
internal static void Execute(ParameterisedQuery query, string sql, bool createDB, params object[] args) {
DoDatabaseCall(query, sql, createDB, null, null, args);
}
internal static DataTable Fill(ParameterisedQuery query, string sql, params object[] args) {
using (DataTable results = new DataTable("toReturn")) {
DoDatabaseCall(query, sql, false, results, null, args);
return results; return results;
} }
} }
static void DoDatabaseCall(ParameterisedQuery query, string sql, bool createDB, internal static object Do(string sql, bool createDB, object arg,
DataTable results, ReaderCallback callback, params object[] args) { DataTable results, ReaderCallback callback, params object[] args) {
ParameterisedQuery query;
if (args == null || args.Length == 0) {
query = Backend.GetStaticParameterised();
} else {
query = Backend.CreateParameterised();
}
query.parameters = args; query.parameters = args;
string connString = Backend.ConnectionString; string connString = Backend.ConnectionString;
Exception e = null; Exception e = null;
@ -79,7 +86,7 @@ namespace MCGalaxy.SQL {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
try { try {
if (callback != null) { if (callback != null) {
query.Iterate(sql, connString, callback); arg = query.Iterate(sql, connString, arg, callback);
} else if (results == null) { } else if (results == null) {
query.Execute(sql, connString, createDB); query.Execute(sql, connString, createDB);
} else { } else {
@ -87,7 +94,7 @@ namespace MCGalaxy.SQL {
} }
query.parameters = null; query.parameters = null;
return; return arg;
} catch (Exception ex) { } catch (Exception ex) {
e = ex; // try yet again e = ex; // try yet again
} }
@ -96,6 +103,7 @@ namespace MCGalaxy.SQL {
query.parameters = null; query.parameters = null;
File.AppendAllText("MySQL_error.log", DateTime.Now + " " + sql + "\r\n"); File.AppendAllText("MySQL_error.log", DateTime.Now + " " + sql + "\r\n");
Logger.LogError(e); Logger.LogError(e);
return arg;
} }
@ -110,5 +118,22 @@ namespace MCGalaxy.SQL {
} }
return names; return names;
} }
internal static string GetString(this IDataRecord record, string name) {
return record.GetString(record.GetOrdinal(name));
}
internal static int GetInt32(this IDataRecord record, string name) {
return record.GetInt32(record.GetOrdinal(name));
}
internal static long GetInt64(this IDataRecord record, string name) {
return record.GetInt64(record.GetOrdinal(name));
}
internal static DateTime GetDateTime(this IDataRecord record, string name) {
string raw = Database.Backend.FastGetDateTime(record, record.GetOrdinal(name));
return DateTime.ParseExact(raw, "yyyy-MM-dd HH:mm:ss", null);
}
} }
} }

View File

@ -55,7 +55,7 @@ namespace MCGalaxy.SQL {
/// for sql queries with no parameters. </summary> /// for sql queries with no parameters. </summary>
protected internal abstract ParameterisedQuery GetStaticParameterised(); protected internal abstract ParameterisedQuery GetStaticParameterised();
public abstract string FastGetDateTime(IDataRecord reader, int col); public abstract string FastGetDateTime(IDataRecord record, int col);
protected internal virtual void ParseCreate(ref string cmd) { } protected internal virtual void ParseCreate(ref string cmd) { }
@ -129,6 +129,17 @@ namespace MCGalaxy.SQL {
return Database.Fill(syntax, args); return Database.Fill(syntax, args);
} }
/// <summary> Iterates over read rows for the given table. </summary>
/// <remarks> modifier is optional SQL which can be used to retrieve only certain rows,
/// return rows in a certain order, etc.</remarks>
public virtual object IterateRows(string table, string columns, object arg,
ReaderCallback callback, string modifier = "", params object[] args) {
ValidateTable(table);
string syntax = "SELECT " + columns + " FROM `" + table + "`";
if (modifier.Length > 0) syntax += " " + modifier;
return Database.Iterate(syntax, arg, callback, args);
}
/// <summary> Updates rows for the given table. </summary> /// <summary> Updates rows for the given table. </summary>
/// <remarks> modifier is optional SQL which can be used to update only certain rows.</remarks> /// <remarks> modifier is optional SQL which can be used to update only certain rows.</remarks>
public virtual void UpdateRows(string table, string columns, public virtual void UpdateRows(string table, string columns,

View File

@ -21,9 +21,6 @@ using System.Data;
using System.Data.Common; using System.Data.Common;
namespace MCGalaxy.SQL { namespace MCGalaxy.SQL {
/// <summary> Callback function invoked on a row returned from an SQL query. </summary>
public delegate bool ReaderCallback(IDataRecord record);
/// <summary> Represents an SQL command or query, that takes named parameters/arguments. </summary> /// <summary> Represents an SQL command or query, that takes named parameters/arguments. </summary>
public abstract class ParameterisedQuery { public abstract class ParameterisedQuery {
@ -69,7 +66,7 @@ namespace MCGalaxy.SQL {
} }
/// <summary> Excecutes an SQL query, invoking a callback on the returned rows one by one. </summary> /// <summary> Excecutes an SQL query, invoking a callback on the returned rows one by one. </summary>
public void Iterate(string query, string connString, ReaderCallback callback) { public object Iterate(string query, string connString, object arg, ReaderCallback callback) {
using (IDbConnection conn = CreateConnection(connString)) { using (IDbConnection conn = CreateConnection(connString)) {
conn.Open(); conn.Open();
if (MultipleSchema) if (MultipleSchema)
@ -78,11 +75,12 @@ namespace MCGalaxy.SQL {
using (IDbCommand cmd = CreateCommand(query, conn)) { using (IDbCommand cmd = CreateCommand(query, conn)) {
FillParams(cmd); FillParams(cmd);
using (IDataReader reader = cmd.ExecuteReader()) { using (IDataReader reader = cmd.ExecuteReader()) {
while (reader.Read() && callback(reader)) { } while (reader.Read()) { arg = callback(reader, arg); }
} }
} }
conn.Close(); conn.Close();
} }
return arg;
} }
void FillParams(IDbCommand cmd) { void FillParams(IDbCommand cmd) {

View File

@ -23,8 +23,7 @@ namespace MCGalaxy.DB {
/// <summary> Retrieves or sets player stats in the database. </summary> /// <summary> Retrieves or sets player stats in the database. </summary>
public class PlayerData { public class PlayerData {
public const string DBTable = "Players";
public const string ColumnDeaths = "totalDeaths"; public const string ColumnDeaths = "totalDeaths";
public const string ColumnLogins = "totalLogin"; public const string ColumnLogins = "totalLogin";
public const string ColumnMoney = "Money"; public const string ColumnMoney = "Money";
@ -50,16 +49,16 @@ namespace MCGalaxy.DB {
internal static void Create(Player p) { internal static void Create(Player p) {
p.prefix = ""; p.prefix = "";
p.color = p.group.Color; p.color = p.group.Color;
p.FirstLogin = DateTime.Now; p.FirstLogin = DateTime.Now;
p.TimesVisited = 1; p.TimesVisited = 1;
string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
Database.Backend.AddRow(DBTable, "Name, IP, FirstLogin, LastLogin, totalLogin, Title, " + Database.Backend.AddRow("Players", "Name, IP, FirstLogin, LastLogin, totalLogin, Title, " +
"totalDeaths, Money, totalBlocks, totalKicked, Messages, TimeSpent", "totalDeaths, Money, totalBlocks, totalKicked, Messages, TimeSpent",
p.name, p.ip, now, now, 1, "", 0, 0, 0, 0, 0, (long)p.TotalTime.TotalSeconds); p.name, p.ip, now, now, 1, "", 0, 0, 0, 0, 0, (long)p.TotalTime.TotalSeconds);
using (DataTable ids = Database.Backend.GetRows(DBTable, using (DataTable ids = Database.Backend.GetRows("Players",
"ID", "WHERE Name = @0", p.name)) { "ID", "WHERE Name = @0", p.name)) {
if (ids.Rows.Count > 0) { if (ids.Rows.Count > 0) {
string id = ids.Rows[0]["ID"].ToString(); string id = ids.Rows[0]["ID"].ToString();
@ -70,8 +69,7 @@ namespace MCGalaxy.DB {
} }
} }
internal static void Load(DataTable playerDb, Player p) { internal static void Apply(PlayerData data, Player p) {
PlayerData data = PlayerData.Fill(playerDb.Rows[0]);
p.TimesVisited = data.Logins + 1; p.TimesVisited = data.Logins + 1;
p.TotalTime = data.TotalTime; p.TotalTime = data.TotalTime;
p.DatabaseID = data.DatabaseID; p.DatabaseID = data.DatabaseID;
@ -81,7 +79,7 @@ namespace MCGalaxy.DB {
p.titlecolor = data.TitleColor; p.titlecolor = data.TitleColor;
p.color = data.Color; p.color = data.Color;
if (p.color.Length == 0) p.color = p.group.Color; if (p.color.Length == 0) p.color = p.group.Color;
p.TotalModified = data.TotalModified; p.TotalModified = data.TotalModified;
p.TotalDrawn = data.TotalDrawn; p.TotalDrawn = data.TotalDrawn;
p.TotalPlaced = data.TotalPlaced; p.TotalPlaced = data.TotalPlaced;
@ -93,35 +91,37 @@ namespace MCGalaxy.DB {
p.TimesBeenKicked = data.Kicks; p.TimesBeenKicked = data.Kicks;
} }
public static PlayerData Fill(DataRow row) { internal static object IteratePlayerData(IDataRecord record, object arg) {
PlayerData data = new PlayerData(); PlayerData data = new PlayerData();
data.Name = row["Name"].ToString().Trim(); data.Name = record.GetString("Name");
data.IP = row["IP"].ToString().Trim(); data.IP = record.GetString("IP");
data.DatabaseID = ParseInt(row["ID"].ToString()); data.DatabaseID = record.GetInt32("ID");
// Backwards compatibility with old format
string rawTime = record.GetString(ColumnTimeSpent);
try { try {
long secs = PlayerData.ParseLong(row[ColumnTimeSpent].ToString()); long secs = long.Parse(rawTime);
data.TotalTime = TimeSpan.FromSeconds(secs); data.TotalTime = TimeSpan.FromSeconds(secs);
} catch { } catch {
data.TotalTime = row[ColumnTimeSpent].ToString().ParseDBTime(); data.TotalTime = rawTime.ParseDBTime();
} }
data.FirstLogin = ParseDate(row[ColumnFirstLogin]); data.FirstLogin = record.GetDateTime(ColumnFirstLogin);
data.LastLogin = ParseDate(row[ColumnLastLogin]); data.LastLogin = record.GetDateTime(ColumnLastLogin);
data.Title = row[ColumnTitle].ToString().Trim(); data.Title = record.GetString(ColumnTitle);
data.Title = data.Title.Cp437ToUnicode(); data.Title = data.Title.Cp437ToUnicode();
data.TitleColor = ParseColor(row[ColumnTColor]); data.TitleColor = ParseCol(record.GetString(ColumnTColor));
data.Color = ParseColor(row[ColumnColor]); data.Color = ParseCol(record.GetString(ColumnColor));
data.Money = ParseInt(row[ColumnMoney].ToString()); data.Money = record.GetInt32(ColumnMoney);
data.Deaths = ParseInt(row[ColumnDeaths].ToString()); data.Deaths = record.GetInt32(ColumnDeaths);
data.Logins = ParseInt(row[ColumnLogins].ToString()); data.Logins = record.GetInt32(ColumnLogins);
data.Kicks = ParseInt(row[ColumnKicked].ToString()); data.Kicks = record.GetInt32(ColumnKicked);
data.Messages = ParseInt(row[ColumnMessages].ToString()); data.Messages = record.GetInt32(ColumnMessages);
long blocks = ParseLong(row[ColumnTotalBlocks].ToString()); long blocks = record.GetInt64(ColumnTotalBlocks);
long cuboided = ParseLong(row[ColumnTotalCuboided].ToString()); long cuboided = record.GetInt64(ColumnTotalCuboided);
data.TotalModified = blocks & LowerBitsMask; data.TotalModified = blocks & LowerBitsMask;
data.TotalPlaced = blocks >> LowerBits; data.TotalPlaced = blocks >> LowerBits;
data.TotalDrawn = cuboided & LowerBitsMask; data.TotalDrawn = cuboided & LowerBitsMask;
@ -142,14 +142,13 @@ namespace MCGalaxy.DB {
return (value.Length == 0 || value.CaselessEq("null")) ? 0 : int.Parse(value); return (value.Length == 0 || value.CaselessEq("null")) ? 0 : int.Parse(value);
} }
static string ParseColor(object value) { static string ParseCol(string raw) {
string col = value.ToString().Trim(); if (raw.Length == 0) return raw;
if (col.Length == 0) return col;
// Try parse color name, then color code // Try parse color name, then color code
string parsed = Colors.Parse(col); string col = Colors.Parse(raw);
if (parsed.Length > 0) return parsed; if (col.Length > 0) return col;
return Colors.Name(col).Length == 0 ? "" : col; return Colors.Name(raw).Length == 0 ? "" : raw;
} }
@ -165,15 +164,15 @@ namespace MCGalaxy.DB {
public const long LowerBitsMask = (1L << LowerBits) - 1; public const long LowerBitsMask = (1L << LowerBits) - 1;
static bool IterateFindCol(IDataRecord record, object arg) {
arg = record.GetString(0);
return true;
}
public static string FindDBColor(Player p) { public static string FindDBColor(Player p) {
using (DataTable colors = Database.Backend.GetRows(DBTable, string raw = Database.GetString("Players", "Color", "WHERE ID=@0", p.DatabaseID);
"Color", "WHERE ID = @0", p.DatabaseID)) { if (raw == null) return "";
if (colors.Rows.Count > 0) { return ParseCol(raw);
string col = ParseColor(colors.Rows[0]["Color"]);
if (col.Length > 0) return col;
}
return "";
}
} }
} }
} }

View File

@ -51,38 +51,38 @@ namespace MCGalaxy.DB {
/// <summary> List of stats that can be ordered. </summary> /// <summary> List of stats that can be ordered. </summary>
public static List<TopStat> Stats = new List<TopStat>() { public static List<TopStat> Stats = new List<TopStat>() {
new TopStat("Logins", PlayerData.DBTable, new TopStat("Logins", "Players",
PlayerData.ColumnLogins, MostLogins, FormatInteger), PlayerData.ColumnLogins, MostLogins, FormatInteger),
new TopStat("Deaths", PlayerData.DBTable, new TopStat("Deaths", "Players",
PlayerData.ColumnDeaths, MostDeaths, FormatInteger), PlayerData.ColumnDeaths, MostDeaths, FormatInteger),
new TopStat("Money", PlayerData.DBTable, new TopStat("Money", "Players",
PlayerData.ColumnMoney, MostMoney, FormatInteger), PlayerData.ColumnMoney, MostMoney, FormatInteger),
new TopStat("Oldest", PlayerData.DBTable, new TopStat("Oldest", "Players",
PlayerData.ColumnFirstLogin, MostOldest, FormatDate, true), PlayerData.ColumnFirstLogin, MostOldest, FormatDate, true),
new TopStat("Newest", PlayerData.DBTable, new TopStat("Newest", "Players",
PlayerData.ColumnFirstLogin, MostNewest, FormatDate), PlayerData.ColumnFirstLogin, MostNewest, FormatDate),
new TopStat("Recent", PlayerData.DBTable, new TopStat("Recent", "Players",
PlayerData.ColumnLastLogin, MostRecent, FormatDate), PlayerData.ColumnLastLogin, MostRecent, FormatDate),
new TopStat("Least-Recent", PlayerData.DBTable, new TopStat("Least-Recent", "Players",
PlayerData.ColumnLastLogin, MostNotRecent, FormatDate, true), PlayerData.ColumnLastLogin, MostNotRecent, FormatDate, true),
new TopStat("Kicked", PlayerData.DBTable, new TopStat("Kicked", "Players",
PlayerData.ColumnKicked, MostKicked, FormatInteger), PlayerData.ColumnKicked, MostKicked, FormatInteger),
new TopStat("Modified", PlayerData.DBTable, new TopStat("Modified", "Players",
PlayerData.ColumnTotalBlocks + " & " + PlayerData.LowerBitsMask, PlayerData.ColumnTotalBlocks + " & " + PlayerData.LowerBitsMask,
MostModified, FormatInteger), MostModified, FormatInteger),
new TopStat("Drawn", PlayerData.DBTable, new TopStat("Drawn", "Players",
PlayerData.ColumnTotalCuboided + " & " + PlayerData.LowerBitsMask, PlayerData.ColumnTotalCuboided + " & " + PlayerData.LowerBitsMask,
MostDrawn, FormatInteger), MostDrawn, FormatInteger),
new TopStat("Placed", PlayerData.DBTable, new TopStat("Placed", "Players",
PlayerData.ColumnTotalBlocks + " >> " + PlayerData.LowerBits, PlayerData.ColumnTotalBlocks + " >> " + PlayerData.LowerBits,
MostPlaced, FormatInteger), MostPlaced, FormatInteger),
new TopStat("Deleted", PlayerData.DBTable, new TopStat("Deleted", "Players",
PlayerData.ColumnTotalCuboided + " >> " + PlayerData.LowerBits, PlayerData.ColumnTotalCuboided + " >> " + PlayerData.LowerBits,
MostDeleted, FormatInteger), MostDeleted, FormatInteger),
new TopStat("TimeSpent", PlayerData.DBTable, new TopStat("TimeSpent", "Players",
PlayerData.ColumnTimeSpent, MostTime, FormatTimespan, PlayerData.ColumnTimeSpent, MostTime, FormatTimespan,
false, " CAST(TimeSpent as unsigned) "), false, " CAST(TimeSpent as unsigned) "),
new TopStat("Messages", PlayerData.DBTable, new TopStat("Messages", "Players",
PlayerData.ColumnMessages, MostMessages, FormatInteger), PlayerData.ColumnMessages, MostMessages, FormatInteger),
}; };

View File

@ -31,7 +31,7 @@ namespace MCGalaxy.SQL {
gottenRows = false; gottenRows = false;
this.sql = sql; this.sql = sql;
this.table = table; this.table = table;
Database.Iterate("SELECT * FROM `" + table + "`", DumpRow); Database.Backend.IterateRows(table, "*", null, DumpRow);
if (!gottenRows) { if (!gottenRows) {
sql.WriteLine("-- No data in table `{0}`!", table); sql.WriteLine("-- No data in table `{0}`!", table);
@ -55,7 +55,7 @@ namespace MCGalaxy.SQL {
gottenRows = true; gottenRows = true;
} }
bool DumpRow(IDataRecord record) { object DumpRow(IDataRecord record, object arg) {
if (!gottenRows) MakeInsertFormat(record); if (!gottenRows) MakeInsertFormat(record);
sql.WriteLine(insertCols); sql.WriteLine(insertCols);
@ -79,7 +79,7 @@ namespace MCGalaxy.SQL {
} }
sql.WriteLine(); sql.WriteLine();
return true; return arg;
} }
static string FormatInsertColumns(string[] cols, string name) { static string FormatInsertColumns(string[] cols, string name) {

View File

@ -101,30 +101,26 @@ namespace MCGalaxy.Games {
new ColumnDesc("Additional1", ColumnType.Int32), new ColumnDesc("Additional1", ColumnType.Int32),
}; };
static object IterateStats(IDataRecord record, object arg) {
ZombieStats stats;
stats.TotalRounds = record.GetInt32("TotalRounds");
stats.MaxRounds = record.GetInt32("MaxRounds");
stats.TotalInfected = record.GetInt32("TotalInfected");
stats.MaxInfected = record.GetInt32("MaxInfected");
return stats;
}
static ZombieStats LoadStats(string name) { static ZombieStats LoadStats(string name) {
DataTable table = Database.Backend.GetRows("ZombieStats", "*", "WHERE Name=@0", name); ZombieStats stats = default(ZombieStats);
ZombieStats stats = default(ZombieStats); return (ZombieStats)Database.Backend.IterateRows("ZombieStats", "*", stats,
IterateStats, "WHERE Name=@0", name);
if (table.Rows.Count > 0) {
DataRow row = table.Rows[0];
stats.TotalRounds = PlayerData.ParseInt(row["TotalRounds"].ToString());
stats.MaxRounds = PlayerData.ParseInt(row["MaxRounds"].ToString());
stats.TotalInfected = PlayerData.ParseInt(row["TotalInfected"].ToString());
stats.MaxInfected = PlayerData.ParseInt(row["MaxInfected"].ToString());
}
table.Dispose();
return stats;
} }
static void SaveStats(Player p) { static void SaveStats(Player p) {
ZSData data = TryGet(p); ZSData data = TryGet(p);
if (data == null || data.TotalRoundsSurvived == 0 && data.TotalInfected == 0) return; if (data == null || data.TotalRoundsSurvived == 0 && data.TotalInfected == 0) return;
int count = 0; int count = Database.CountRows("ZombieStats", "WHERE Name=@0", p.name);
using (DataTable table = Database.Backend.GetRows("ZombieStats", "*", "WHERE Name=@0", p.name)) {
count = table.Rows.Count;
}
if (count == 0) { if (count == 0) {
Database.Backend.AddRow("ZombieStats", "TotalRounds, MaxRounds, TotalInfected, MaxInfected, Name", Database.Backend.AddRow("ZombieStats", "TotalRounds, MaxRounds, TotalInfected, MaxInfected, Name",
data.TotalRoundsSurvived, data.MaxRoundsSurvived, data.TotalRoundsSurvived, data.MaxRoundsSurvived,

View File

@ -18,6 +18,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using MCGalaxy.Blocks.Extended;
using MCGalaxy.Maths;
using MCGalaxy.SQL; using MCGalaxy.SQL;
using MCGalaxy.Util; using MCGalaxy.Util;
using BlockID = System.UInt16; using BlockID = System.UInt16;
@ -64,7 +66,7 @@ namespace MCGalaxy {
continue; continue;
} else { } else {
z.Access.Whitelisted.Add(owner); z.Access.Whitelisted.Add(owner);
z.Access.Min = LevelPermission.Admin; z.Access.Min = LevelPermission.Admin;
} }
z.Config.Name = "Zone" + id; z.Config.Name = "Zone" + id;
@ -99,19 +101,13 @@ namespace MCGalaxy {
internal static void LoadMessages(Level level, string name) { internal static void LoadMessages(Level level, string name) {
level.hasMessageBlocks = Database.TableExists("Messages" + name); level.hasMessageBlocks = Database.TableExists("Messages" + name);
if (!level.hasMessageBlocks) return; if (!level.hasMessageBlocks) return;
List<Vec3U16> coords = MessageBlock.GetAll(name);
using (DataTable table = Database.Backend.GetRows("Messages" + name, "*")) { foreach (Vec3U16 p in coords) {
foreach (DataRow row in table.Rows) { BlockID block = level.GetBlock(p.X, p.Y, p.Z);
ushort x = ushort.Parse(row["X"].ToString()); if (level.Props[block].IsMessageBlock) continue;
ushort y = ushort.Parse(row["Y"].ToString()); MessageBlock.Delete(name, p.X, p.Y, p.Z);
ushort z = ushort.Parse(row["Z"].ToString());
BlockID block = level.GetBlock(x, y, z);
if (level.Props[block].IsMessageBlock) continue;
Database.Backend.DeleteRows("Messages" + name, "WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z);
}
} }
} }

View File

@ -157,6 +157,7 @@
<Compile Include="Commands\building\CmdSphere.cs" /> <Compile Include="Commands\building\CmdSphere.cs" />
<Compile Include="Commands\building\CmdTransform.cs" /> <Compile Include="Commands\building\CmdTransform.cs" />
<Compile Include="Commands\building\CmdTriangle.cs" /> <Compile Include="Commands\building\CmdTriangle.cs" />
<Compile Include="Commands\building\CmdWrite2.cs" />
<Compile Include="Commands\building\ReplaceCmds.cs" /> <Compile Include="Commands\building\ReplaceCmds.cs" />
<Compile Include="Commands\building\CmdRestartPhysics.cs" /> <Compile Include="Commands\building\CmdRestartPhysics.cs" />
<Compile Include="Commands\building\CmdSpheroid.cs" /> <Compile Include="Commands\building\CmdSpheroid.cs" />

View File

@ -138,14 +138,11 @@ namespace MCGalaxy {
SendMessage("&cPlease complete admin verification with %T/Pass [Password]!"); SendMessage("&cPlease complete admin verification with %T/Pass [Password]!");
} }
try { if (group.CanExecute("Inbox") && Database.TableExists("Inbox" + name)) {
if (group.CanExecute("Inbox") && Database.TableExists("Inbox" + name) ) { int count = Database.CountRows("Inbox" + name);
using (DataTable table = Database.Backend.GetRows("Inbox" + name, "*")) { if (count > 0) {
if (table.Rows.Count > 0) SendMessage("You have &a" + count + " %Smessages in %T/Inbox");
SendMessage("You have &a" + table.Rows.Count + " %Smessages in %T/Inbox");
}
} }
} catch {
} }
if (ServerConfig.PositionUpdateInterval > 1000) if (ServerConfig.PositionUpdateInterval > 1000)
@ -216,16 +213,17 @@ namespace MCGalaxy {
} }
void GetPlayerStats() { void GetPlayerStats() {
DataTable data = Database.Backend.GetRows("Players", "*", "WHERE Name=@0", name); object raw = Database.Backend.IterateRows("Players", "*",
if (data.Rows.Count == 0) { null, PlayerData.IteratePlayerData,
"WHERE Name=@0", name);
if (raw == null) {
PlayerData.Create(this); PlayerData.Create(this);
Chat.MessageFrom(this, "λNICK %Shas connected for the first time!"); Chat.MessageFrom(this, "λNICK %Shas connected for the first time!");
SendMessage("Welcome " + ColoredName + "%S! This is your first visit."); SendMessage("Welcome " + ColoredName + "%S! This is your first visit.");
} else { } else {
PlayerData.Load(data, this); PlayerData.Apply((PlayerData)raw, this);
SendMessage("Welcome back " + FullName + "%S! You've been here " + TimesVisited + " times!"); SendMessage("Welcome back " + FullName + "%S! You've been here " + TimesVisited + " times!");
} }
data.Dispose();
gotSQLData = true; gotSQLData = true;
} }

View File

@ -12,7 +12,7 @@ BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the Licenses for the specific language governing or implied. See the Licenses for the specific language governing
permissions and limitations under the Licenses. permissions and limitations under the Licenses.
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
@ -26,7 +26,7 @@ namespace MCGalaxy {
/// <remarks> Note this field is highly volatile, you should cache references to the items array. </remarks> /// <remarks> Note this field is highly volatile, you should cache references to the items array. </remarks>
public static VolatileArray<Player> Online = new VolatileArray<Player>(true); public static VolatileArray<Player> Online = new VolatileArray<Player>(true);
public static Group GetGroup(string name) { public static Group GetGroup(string name) {
Player target = FindExact(name); Player target = FindExact(name);
return target != null ? target.group : Group.GroupIn(name); return target != null ? target.group : Group.GroupIn(name);
} }
@ -34,10 +34,10 @@ namespace MCGalaxy {
public static string GetColoredName(Player p, string name) { public static string GetColoredName(Player p, string name) {
Player target = FindExact(name); Player target = FindExact(name);
// TODO: select color from database? // TODO: select color from database?
return target != null && Entities.CanSee(p, target) ? target.ColoredName return target != null && Entities.CanSee(p, target) ? target.ColoredName
: Group.GroupIn(name).Color + name.RemoveLastPlus(); : Group.GroupIn(name).Color + name.RemoveLastPlus();
} }
public static int NonHiddenCount() { public static int NonHiddenCount() {
Player[] players = Online.Items; Player[] players = Online.Items;
int count = 0; int count = 0;
@ -49,7 +49,7 @@ namespace MCGalaxy {
int matches = 0; return FindMatches(pl, name, out matches, onlyCanSee); int matches = 0; return FindMatches(pl, name, out matches, onlyCanSee);
} }
public static Player FindMatches(Player pl, string name, public static Player FindMatches(Player pl, string name,
out int matches, bool onlyCanSee = true) { out int matches, bool onlyCanSee = true) {
matches = 0; matches = 0;
if (!Formatter.ValidName(pl, name, "player")) return null; if (!Formatter.ValidName(pl, name, "player")) return null;
@ -65,7 +65,7 @@ namespace MCGalaxy {
Player target = FindMatches(p, name, out matches); Player target = FindMatches(p, name, out matches);
if (matches > 1) return null; if (matches > 1) return null;
if (target != null) return target.name; if (target != null) return target.name;
Player.Message(p, "Searching PlayerDB for \"{0}\"..", name); Player.Message(p, "Searching PlayerDB for \"{0}\"..", name);
return FindOfflineNameMatches(p, name); return FindOfflineNameMatches(p, name);
} }
@ -80,36 +80,24 @@ namespace MCGalaxy {
} }
return null; return null;
} }
/// <summary> Retrieves from the database the player data for the player
/// whose name caselessly exactly matches the given name. </summary>
/// <returns> PlayerData instance if found, null if not. </returns>
public static PlayerData FindData(string name) { public static PlayerData FindData(string name) {
using (DataTable results = Query(name, "*")) { string suffix = Database.Backend.CaselessWhereSuffix;
if (results.Rows.Count == 0) return null; object raw = Database.Backend.IterateRows("Players", "*",
return PlayerData.Fill(results.Rows[0]); null, PlayerData.IteratePlayerData,
} "WHERE Name=@0" + suffix, name);
} return (PlayerData)raw;
/// <summary> Retrieves from the database the actual name for the player
/// whose name caselessly exactly matches the given name. </summary>
/// <returns> Correctly cased name if found, null if not. </returns>
public static string FindName(string name) {
using (DataTable playerDB = Query(name, "Name")) {
if (playerDB.Rows.Count == 0) return null;
return playerDB.Rows[0]["Name"].ToString().Trim();
}
} }
/// <summary> Retrieves from the database the last IP address for the public static string FindName(string name) {
/// player whose name caselessly exactly matches the given name. </summary> string suffix = Database.Backend.CaselessWhereSuffix;
/// <returns> Last IP address if found, null if not. </returns> return Database.GetString("Players", "Name", "WHERE Name=@0" + suffix, name);
}
public static string FindIP(string name) { public static string FindIP(string name) {
using (DataTable results = Query(name, "IP")) { string suffix = Database.Backend.CaselessWhereSuffix;
if (results.Rows.Count == 0) return null; return Database.GetString("Players", "IP", "WHERE Name=@0" + suffix, name);
return results.Rows[0]["IP"].ToString().Trim();
}
} }
@ -135,39 +123,33 @@ namespace MCGalaxy {
return row == null ? null : row["Name"].ToString(); return row == null ? null : row["Name"].ToString();
} }
static object IterateAccounts(IDataRecord record, object arg) {
List<string> names = (List<string>)arg;
string name = record.GetString(0);
if (!names.CaselessContains(name)) names.Add(name);
return arg;
}
/// <summary> Retrieves names of all players whose IP address matches the given IP address. </summary> /// <summary> Retrieves names of all players whose IP address matches the given IP address. </summary>
/// <remarks> This is current IP for online players, last IP for offline players from the database. </remarks> /// <remarks> This is current IP for online players, last IP for offline players from the database. </remarks>
public static List<string> FindAccounts(string ip) { public static List<string> FindAccounts(string ip) {
DataTable clones = Database.Backend.GetRows("Players", "Name", "WHERE IP=@0", ip); List<string> names = new List<string>();
List<string> accounts = new List<string>(); Database.Backend.IterateRows("Players", "Name", names, IterateAccounts,
"WHERE IP=@0", ip);
foreach (DataRow row in clones.Rows) {
string name = row["Name"].ToString();
if (!accounts.CaselessContains(name))
accounts.Add(name);
}
// TODO: should we instead do save() when the player logs in // TODO: should we instead do save() when the player logs in
// by checking online players we avoid a DB write though // by checking online players we avoid a DB write though
Player[] players = PlayerInfo.Online.Items; Player[] players = PlayerInfo.Online.Items;
foreach (Player p in players) { foreach (Player p in players) {
if (p.ip != ip) continue; if (p.ip != ip) continue;
if (!accounts.CaselessContains(p.name)) if (!names.CaselessContains(p.name)) names.Add(p.name);
accounts.Add(p.name);
} }
return names;
clones.Dispose();
return accounts;
} }
static DataRow QueryMulti(Player p, string name, string columns) {
internal static DataTable Query(string name, string columns) {
string suffix = Database.Backend.CaselessWhereSuffix;
return Database.Backend.GetRows("Players", columns,
"WHERE Name=@0" + suffix, name);
}
internal static DataRow QueryMulti(Player p, string name, string columns) {
string suffix = Database.Backend.CaselessLikeSuffix; string suffix = Database.Backend.CaselessLikeSuffix;
using (DataTable results = Database.Backend.GetRows("Players", columns, using (DataTable results = Database.Backend.GetRows("Players", columns,
"WHERE Name LIKE @0 ESCAPE '#' LIMIT 21" + suffix, "WHERE Name LIKE @0 ESCAPE '#' LIMIT 21" + suffix,

View File

@ -212,10 +212,8 @@ namespace MCGalaxy.Tasks {
internal static void UpgradeDBTimeSpent(SchedulerTask task) { internal static void UpgradeDBTimeSpent(SchedulerTask task) {
DataTable table = Database.Backend.GetRows(PlayerData.DBTable, "TimeSpent", "LIMIT 1"); string time = Database.GetString("Players", "TimeSpent", "LIMIT 1");
if (table.Rows.Count == 0) return; // no players if (time == null) return; // no players at all in DB
string time = table.Rows[0]["TimeSpent"].ToString();
if (time.IndexOf(' ') == -1) return; // already upgraded if (time.IndexOf(' ') == -1) return; // already upgraded
Logger.Log(LogType.SystemActivity, "Upgrading TimeSpent column in database to new format.."); Logger.Log(LogType.SystemActivity, "Upgrading TimeSpent column in database to new format..");
@ -232,10 +230,10 @@ namespace MCGalaxy.Tasks {
static void DumpPlayerTimeSpents() { static void DumpPlayerTimeSpents() {
playerIds = new List<int>(); playerIds = new List<int>();
playerSeconds = new List<long>(); playerSeconds = new List<long>();
Database.Iterate("SELECT ID, TimeSpent FROM Players", AddPlayerTimeSpent); Database.Backend.IterateRows("Players", "ID,TimeSpent", null, IterateTimeSpent);
} }
static bool AddPlayerTimeSpent(IDataRecord record) { static object IterateTimeSpent(IDataRecord record, object arg) {
playerCount++; playerCount++;
try { try {
int id = record.GetInt32(0); int id = record.GetInt32(0);
@ -246,10 +244,10 @@ namespace MCGalaxy.Tasks {
} catch { } catch {
playerFailed++; playerFailed++;
} }
return true; return arg;
} }
static void UpgradePlayerTimeSpents() { static void UpgradePlayerTimeSpents() {
using (BulkTransaction bulk = Database.Backend.CreateBulk()) { using (BulkTransaction bulk = Database.Backend.CreateBulk()) {
IDataParameter idParam = bulk.CreateParam("@0", DbType.Int32); IDataParameter idParam = bulk.CreateParam("@0", DbType.Int32);
IDataParameter secsParam = bulk.CreateParam("@1", DbType.Int64); IDataParameter secsParam = bulk.CreateParam("@1", DbType.Int64);