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.Collections.Generic;
using System.Data;
using MCGalaxy.Maths;
using MCGalaxy.SQL;
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) {
if (!p.level.hasMessageBlocks) return false;
try {
DataTable Messages = Database.Backend.GetRows("Messages" + p.level.name, "*",
"WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z);
int last = Messages.Rows.Count - 1;
if (last == -1) { Messages.Dispose(); return false; }
string message = Get(p.level.MapName, x, y, z);
if (message == null) return false;
message = message.Replace("@p", p.name);
string message = Messages.Rows[last]["Message"].ToString().Trim();
message = message.Replace("\\'", "\'");
message = message.Cp437ToUnicode();
message = message.Replace("@p", p.name);
if (message != p.prevMsg || (alwaysRepeat || ServerConfig.RepeatMBs)) {
Execute(p, message);
}
return true;
} catch {
return false;
if (message != p.prevMsg || (alwaysRepeat || ServerConfig.RepeatMBs)) {
Execute(p, message);
}
return true;
}
public static void Execute(Player p, string message) {
@ -75,13 +66,13 @@ namespace MCGalaxy.Blocks.Extended {
Command.Search(ref cmdName, ref cmdArgs);
Command cmd = Command.Find(cmdName);
if (cmd == null) return true;
if (cmd == null) return true;
bool mbUseable = !cmd.MessageBlockRestricted && !cmd.type.CaselessContains("mod");
if (p.group.CanExecute(cmd) && (allCmds || mbUseable)) return true;
Player.Message(p, "You cannot use %T/{0} %Sin a messageblock.", cmd.name);
return false;
}
}
static string[] sep = new string[] { " |/" };
const StringSplitOptions opts = StringSplitOptions.RemoveEmptyEntries;
@ -106,5 +97,38 @@ namespace MCGalaxy.Blocks.Extended {
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 {
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) {
if (!p.level.hasPortals) return false;
try {
DataTable Portals = Database.Backend.GetRows("Portals" + p.level.name, "*",
"WHERE EntryX=@0 AND EntryY=@1 AND EntryZ=@2", x, y, z);
int last = Portals.Rows.Count - 1;
if (last == -1) { Portals.Dispose(); return false; }
Orientation rot = p.Rot;
PortalExitData exit = Get(p.level.MapName, x, y, z);
if (exit == null) return false;
Orientation rot = p.Rot;
if (p.level.name != exit.Map) {
Level curLevel = p.level;
p.summonedMap = exit.Map;
bool changedMap = false;
DataRow row = Portals.Rows[last];
string map = row["ExitMap"].ToString();
map = map.Cp437ToUnicode();
if (p.level.name != map) {
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);
try {
changedMap = PlayerActions.ChangeMap(p, exit.Map);
} catch (Exception ex) {
Logger.LogError(ex);
changedMap = false;
}
x = ushort.Parse(row["ExitX"].ToString());
y = ushort.Parse(row["ExitY"].ToString());
z = ushort.Parse(row["ExitZ"].ToString());
Position pos = Position.FromFeetBlockCoords(x, y, z);
p.SendPos(Entities.SelfID, pos, rot);
Portals.Dispose();
return true;
} catch {
return false;
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);
}
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.
*/
using System;
using System.Collections.Generic;
using System.Data;
using MCGalaxy.SQL;
@ -25,19 +26,27 @@ namespace MCGalaxy.Commands.Chatting {
public override string type { get { return CommandTypes.Chat; } }
public override bool SuperUseable { get { return false; } }
public override void Use(Player p, string message) {
if (!Database.TableExists("Inbox" + p.name)) {
class MailEntry { public string Contents, Timestamp, From; }
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;
}
string[] args = message.SplitSpaces(2);
if (message.Length == 0) {
using (DataTable Inbox = Database.Backend.GetRows("Inbox" + p.name, "*", "ORDER BY TimeSent")) {
if (Inbox.Rows.Count == 0) { Player.Message(p, "No messages found."); return; }
foreach (DataRow row in Inbox.Rows) {
OutputMessage(p, row);
}
}
foreach (MailEntry entry in entries) { Output(p, entry); }
} else if (IsDeleteCommand(args[0])) {
if (args.Length == 1) {
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);
Player.Message(p, "Deleted all messages.");
} else {
DeleteMessageByID(p, args[1]);
DeleteByID(p, args[1], entries);
}
} else {
OutputMessageByID(p, message);
OutputByID(p, message, entries);
}
}
static void DeleteMessageByID(Player p, string value) {
int num = 0;
if (!CommandParser.GetInt(p, value, "Message number", ref num, 0)) return;
static void DeleteByID(Player p, string value, List<MailEntry> entries) {
int num = 1;
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 >= Inbox.Rows.Count) {
Player.Message(p, "Message #{0} does not exist.", num); return;
}
DataRow row = Inbox.Rows[num];
string time = Convert.ToDateTime(row["TimeSent"]).ToString("yyyy-MM-dd HH:mm:ss");
if (num > entries.Count) {
Player.Message(p, "Message #{0} does not exist.", num);
} else {
MailEntry entry = entries[num - 1];
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);
}
}
static void OutputMessageByID(Player p, string value) {
int num = 0;
if (!CommandParser.GetInt(p, value, "Message number", ref num, 0)) return;
using (DataTable Inbox = Database.Backend.GetRows("Inbox" + p.name, "*", "ORDER BY TimeSent")) {
if (num >= Inbox.Rows.Count) {
Player.Message(p, "Message #{0} does not exist.", num); return;
}
OutputMessage(p, Inbox.Rows[num]);
static void OutputByID(Player p, string value, List<MailEntry> entries) {
int num = 1;
if (!CommandParser.GetInt(p, value, "Message number", ref num, 1)) return;
if (num > entries.Count) {
Player.Message(p, "Message #{0} does not exist.", num);
} else {
Output(p, entries[num - 1]);
}
}
static void OutputMessage(Player p, DataRow row) {
DateTime time = Convert.ToDateTime(row["TimeSent"]);
TimeSpan delta = DateTime.Now - time;
string sender = PlayerInfo.GetColoredName(p, row["PlayerFrom"].ToString());
static void Output(Player p, MailEntry entry) {
TimeSpan delta = DateTime.Now - Convert.ToDateTime(entry.Timestamp);
string sender = PlayerInfo.GetColoredName(p, entry.From);
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) {

View File

@ -50,9 +50,7 @@ namespace MCGalaxy.Commands {
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) {
foreach (CommandExtraPerms perms in list) {
if (perms.CmdName.CaselessEq(cmd) && perms.Number == num) return perms;
@ -60,18 +58,13 @@ namespace MCGalaxy.Commands {
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) {
CommandExtraPerms perms = Find(cmd, num);
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; }
/// <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) {
List<CommandExtraPerms> allPerms = new List<CommandExtraPerms>();
foreach (CommandExtraPerms perms in list) {

View File

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

View File

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

View File

@ -35,13 +35,14 @@ namespace MCGalaxy.Commands.Info {
static PerformanceCounter allPCounter = null;
static PerformanceCounter cpuPCounter = null;
public override void Use(Player p, string message) {
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, "&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.",
LevelInfo.Loaded.Count, ServerConfig.Currency);
@ -59,13 +60,7 @@ namespace MCGalaxy.Commands.Info {
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) {
Process proc = Process.GetCurrentProcess();
if (allPCounter == null) {

View File

@ -178,32 +178,27 @@ namespace MCGalaxy.Commands.Maintenance {
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
static void UpdateDBLo(string name, string value, string column) {
long loValue = long.Parse(value);
// OR with existing high bits of value in DB
using (DataTable results = Database.Backend.GetRows("Players", column, "WHERE Name = @0", name)) {
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);
long packed = GetLong(name, column) & ~PlayerData.LowerBitsMask; // hi value only
packed |= long.Parse(value);
UpdateDB(name, packed.ToString(), column);
}
static void UpdateDBHi(string name, string value, string column) {
long hiValue = long.Parse(value) << PlayerData.LowerBits;
// OR with existing low bits of value in DB
using (DataTable results = Database.Backend.GetRows("Players", column, "WHERE Name = @0", name)) {
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);
long packed = GetLong(name, column) & PlayerData.LowerBitsMask; // lo value only
packed |= long.Parse(value) << PlayerData.LowerBits;
UpdateDB(name, packed.ToString(), column);
}
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 = args.Message.UnicodeToCp437();
string lvlName = p.level.name;
object locker = ThreadSafeCache.DBCache.GetLocker(lvlName);
string map = p.level.name;
object locker = ThreadSafeCache.DBCache.GetLocker(map);
lock (locker) {
Database.Backend.CreateTable("Messages" + lvlName, LevelDB.createMessages);
Database.Backend.CreateTable("Messages" + map, LevelDB.createMessages);
p.level.hasMessageBlocks = true;
int count = 0;
using (DataTable Messages = Database.Backend.GetRows("Messages" + lvlName, "*",
"WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z)) {
count = Messages.Rows.Count;
}
int count = Database.CountRows("Messages" + map,
"WHERE X=@0 AND Y=@1 AND Z=@2", x, y, z);
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 {
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);
}
}
@ -130,32 +126,18 @@ namespace MCGalaxy.Commands.Building {
void ShowMessageBlocks(Player p) {
p.showMBs = !p.showMBs;
int count = 0;
List<Vec3U16> coords = MessageBlock.GetAll(p.level.MapName);
if (p.level.hasMessageBlocks) {
using (DataTable table = Database.Backend.GetRows("Messages" + p.level.name, "*")) {
count = table.Rows.Count;
if (p.showMBs) { ShowMessageBlocks(p, table); }
else { HideMessageBlocks(p, table); }
foreach (Vec3U16 pos in coords) {
if (p.showMBs) {
p.SendBlockchange(pos.X, pos.Y, pos.Z, Block.Green);
} else {
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) {
if (!props[block].IsMessageBlock) return null;

View File

@ -116,15 +116,11 @@ namespace MCGalaxy.Commands.Building {
lock (locker) {
Database.Backend.CreateTable("Portals" + lvlName, LevelDB.createPortals);
Level map = LevelInfo.FindExact(P.Map);
if (map != null) map.hasPortals = true;
Level lvl = LevelInfo.FindExact(P.Map);
if (lvl != null) lvl.hasPortals = true;
int count = 0;
using (DataTable portals = Database.Backend.GetRows("Portals" + lvlName, "*",
"WHERE EntryX=@0 AND EntryY=@1 AND EntryZ=@2", P.x, P.y, P.z)) {
count = portals.Rows.Count;
}
int count = Database.CountRows("Portals" + lvlName,
"WHERE EntryX=@0 AND EntryY=@1 AND EntryZ=@2", P.x, P.y, P.z);
if (count == 0) {
Database.Backend.AddRow("Portals" + lvlName, "EntryX, EntryY, EntryZ, ExitX, ExitY, ExitZ, ExitMap",
P.x, P.y, P.z, x, y, z, dstMap);
@ -158,7 +154,7 @@ namespace MCGalaxy.Commands.Building {
if (p.showPortals) { ShowPortals(p, table); }
else { HidePortals(p, table); }
}
}
}
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() {
ParameterisedQuery query = GetStaticParameterised();
Database.Execute(query, "CREATE DATABASE if not exists `" + ServerConfig.MySQLDatabaseName + "`", true);
string syntax = "CREATE DATABASE if not exists `" + ServerConfig.MySQLDatabaseName + "`";
Database.Do(syntax, true, null, null, null);
}
public override BulkTransaction CreateBulk() {
@ -58,8 +58,8 @@ namespace MCGalaxy.SQL {
protected internal override ParameterisedQuery GetStaticParameterised() {
return queryInstance;
}
}
public override string FastGetDateTime(IDataRecord record, int col) {
DateTime date = record.GetDateTime(col);
return date.ToString("yyyy-MM-dd HH:mm:ss");
@ -86,34 +86,21 @@ namespace MCGalaxy.SQL {
cmd = cmd.Insert(cmd.LastIndexOf(")"), ", PRIMARY KEY (`" + name + "`)");
}
static object IterateExists(IDataRecord record, object arg) { return ""; }
public override bool TableExists(string table) {
ValidateTable(table);
const string syntax = "SELECT * FROM information_schema.tables WHERE table_name = @0 AND table_schema = @1";
using (DataTable results = Database.Fill(syntax, table, ServerConfig.MySQLDatabaseName)) {
return results.Rows.Count > 0;
}
return Database.Iterate("SHOW TABLES LIKE '" + table + "'",
null, IterateExists) != null;
}
public override List<string> AllTables() {
using (DataTable results = Database.Fill("SHOW TABLES")) {
List<string> tables = new List<string>(results.Rows.Count);
foreach (DataRow row in results.Rows) {
tables.Add(row[0].ToString());
}
return tables;
}
return Database.GetStrings("SHOW TABLES");
}
public override List<string> ColumnNames(string table) {
ValidateTable(table);
using (DataTable results = Database.Fill("DESCRIBE `" + table + "`")) {
List<string> columns = new List<string>(results.Rows.Count);
foreach (DataRow row in results.Rows) {
columns.Add(row["Field"].ToString());
}
return columns;
}
return Database.GetStrings("DESCRIBE `" + table + "`");
}
public override void RenameTable(string srcTable, string dstTable) {
@ -127,7 +114,7 @@ namespace MCGalaxy.SQL {
ValidateTable(table);
string syntax = "TRUNCATE TABLE `" + table + "`";
Database.Execute(syntax);
}
}
protected override void CreateTableColumns(StringBuilder sql, ColumnDesc[] columns) {
string priKey = null;
@ -146,36 +133,41 @@ namespace MCGalaxy.SQL {
}
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) {
string pri = "";
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[] data = new string[schema.Columns.Count];
foreach (DataRow row in schema.Rows) {
for (int col = 0; col < schema.Columns.Count; col++) {
data[col] = row[col].ToString();
}
if (data[3].CaselessEq("pri")) pri = data[0];
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 pri = "";
for (int i = 0; i < fields.Count; i++) {
string[] field = fields[i];
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 (field[5].Length > 0) value += " " + field[5];
string suffix = pri.Length == 0 && row == schema.Rows[schema.Rows.Count - 1] ? "" : ",";
w.WriteLine("`{0}` {1} {2}{3}", row[0], row[1], value, suffix);
}
string suffix = pri.Length == 0 && (i == fields.Count - 1) ? "" : ",";
w.WriteLine("`{0}` {1} {2}{3}", field[0], field[1], value, suffix);
}
if (pri.Length > 0) w.Write("PRIMARY KEY (`{0}`)", pri);
w.WriteLine(");");
}
public override void AddColumn(string table, ColumnDesc col, string colAfter) {
ValidateTable(table);
string syntax = "ALTER TABLE `" + table + "` ADD COLUMN "
string syntax = "ALTER TABLE `" + table + "` ADD COLUMN "
+ col.Column + " " + col.FormatType();
if (colAfter.Length > 0) syntax += " AFTER " + colAfter;
Database.Execute(syntax);
@ -227,5 +219,5 @@ namespace MCGalaxy.SQL {
protected override IDbDataParameter CreateParameter() {
return new MySqlParameter();
}
}
}
}

View File

@ -30,7 +30,7 @@ namespace MCGalaxy.SQL {
public static IDatabaseBackend Instance = new SQLiteBackend();
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 {
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
}
public override bool TableExists(string table) {
ValidateTable(table);
using (DataTable results = GetRows("sqlite_master", "name",
"WHERE type='table' AND name=@0", table)) {
return results.Rows.Count > 0;
}
return Database.CountRows("sqlite_master",
"WHERE type='table' AND name=@0", table) > 0;
}
public override List<string> AllTables() {
using (DataTable results = GetRows("sqlite_master", "name", "WHERE type='table'")) {
List<string> tables = new List<string>(results.Rows.Count);
foreach (DataRow row in results.Rows) {
if (row["name"].ToString().StartsWith("sqlite_")) continue;
tables.Add(row["name"].ToString());
}
return tables;
const string syntax = "SELECT name from sqlite_master WHERE type='table'";
List<string> tables = Database.GetStrings(syntax);
// exclude sqlite built-in database tables
for (int i = tables.Count - 1; i >= 0; i--) {
if (tables[i].StartsWith("sqlite_")) tables.RemoveAt(i);
}
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) {
ValidateTable(table);
using (DataTable results = Database.Fill("PRAGMA table_info(`" + table + "`)")) {
List<string> columns = new List<string>(results.Rows.Count);
foreach (DataRow row in results.Rows) {
columns.Add(row["name"].ToString());
}
return columns;
}
List<string> columns = new List<string>();
Database.Iterate("PRAGMA table_info(`" + table + "`)",
columns, IterateColumnNames);
return columns;
}
public override void RenameTable(string srcTable, string dstTable) {
@ -105,7 +106,7 @@ namespace MCGalaxy.SQL {
}
protected override void CreateTableColumns(StringBuilder sql, ColumnDesc[] columns) {
string priKey = null;
string priKey = null;
for (int i = 0; i < columns.Length; i++) {
ColumnDesc col = columns[i];
sql.Append(col.Column).Append(' ');
@ -135,21 +136,20 @@ namespace MCGalaxy.SQL {
}
public override void PrintSchema(string table, TextWriter w) {
const string syntax = "SELECT sql FROM sqlite_master WHERE tbl_name = @0 AND type = 'table'";
using (DataTable schema = Database.Fill(syntax + CaselessWhereSuffix, table)) {
foreach (DataRow row in schema.Rows) {
string sql = row[0].ToString();
sql = sql.Replace(" " + table, " `" + table + "`");
sql = sql.Replace("CREATE TABLE `" + table + "`", "CREATE TABLE IF NOT EXISTS `" + table + "`");
w.WriteLine(sql + ";");
}
const string syntax = "SELECT sql from sqlite_master WHERE tbl_name = @0 AND type = 'table'";
List<string> all = Database.GetStrings(syntax + CaselessWhereSuffix, table);
for (int i = 0; i < all.Count; i++) {
string sql = all[i];
sql = sql.Replace(" " + table, " `" + table + "`");
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) {
ValidateTable(table);
string syntax = "ALTER TABLE `" + table + "` ADD COLUMN "
+ col.Column + " " + col.FormatType();
string syntax = "ALTER TABLE `" + table + "` ADD COLUMN " + col.Column + " " + col.FormatType();
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.Open();
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 IDbConnection CreateConnection(string connString) {

View File

@ -17,6 +17,7 @@
*/
using System;
using System.Data;
using MCGalaxy.Blocks.Extended;
using MCGalaxy.SQL;
using BlockID = System.UInt16;
@ -26,7 +27,7 @@ namespace MCGalaxy.DB {
public static class BlockDBChange {
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);
TimeSpan delta = DateTime.UtcNow.Subtract(time);
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) {
if (!p.level.Props[block].IsMessageBlock) return;
try {
if (!Database.TableExists("Messages" + p.level.name)) return;
DataTable messages = Database.Backend.GetRows("Messages" + p.level.name, "*",
"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 {
}
string message = MessageBlock.Get(p.level.MapName, x, y, z);
if (message == null) return;
Player.Message(p, "Message Block contents: {0}", message);
}
public static void OutputPortal(Player p, BlockID block, ushort x, ushort y, ushort z) {
if (!p.level.Props[block].IsPortal) return;
try {
if (!Database.TableExists("Portals" + p.level.name)) return;
DataTable portals = Database.Backend.GetRows("Portals" + p.level.name, "*",
"WHERE EntryX=@0 AND EntryY=@1 AND EntryZ=@2", x, y, z);
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 {
}
Portal.PortalExitData exit = Portal.Get(p.level.MapName, x, y, z);
if (exit == null) return;
Player.Message(p, "Portal destination: ({0}, {1}, {2}) in {3}",
exit.X, exit.Y, exit.Z, exit.Map);
}
static ushort U16(object x) { return ushort.Parse(x.ToString()); }
static string FormatReason(ushort flags) {
if ((flags & BlockDBFlags.Painted) != 0) return " (Painted)";
if ((flags & BlockDBFlags.Drawn) != 0) return " (Drawn)";

View File

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

View File

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

View File

@ -16,62 +16,69 @@
permissions and limitations under the Licenses.
*/
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
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 IDatabaseBackend Backend;
/// <summary> Returns whether the given table exists in the database. </summary>
public static bool TableExists(string table) { return Backend.TableExists(table); }
/// <summary> Executes an SQL command that does not return any results. </summary>
public static void Execute(string sql) {
ParameterisedQuery query = Backend.GetStaticParameterised();
Execute(query, sql, false, null);
static object IterateCount(IDataRecord record, object arg) { return record.GetInt32(0); }
public static int CountRows(string table, string modifier = "", params object[] args) {
return (int)Backend.IterateRows(table, "COUNT(*)", 0,
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>
public static void Execute(string sql, params object[] args) {
ParameterisedQuery query = Backend.CreateParameterised();
Execute(query, sql, false, args);
Do(sql, false, null, null, null, args);
}
/// <summary> Executes an SQL query, invoking callback function on each returned row. </summary>
public static void Iterate(string sql, ReaderCallback callback, params object[] args) {
ParameterisedQuery query = Backend.CreateParameterised();
DoDatabaseCall(query, sql, false, null, callback, args);
public static object Iterate(string sql, object arg, ReaderCallback callback, params object[] args) {
return Do(sql, false, arg, null, callback, args);
}
/// <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) {
ParameterisedQuery query = Backend.CreateParameterised();
return Fill(query, sql, 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);
using (DataTable results = new DataTable("toReturn")) {
Do(sql, false, null, results, null, args);
return results;
}
}
static void DoDatabaseCall(ParameterisedQuery query, string sql, bool createDB,
DataTable results, ReaderCallback callback, params object[] args) {
internal static object Do(string sql, bool createDB, object arg,
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;
string connString = Backend.ConnectionString;
Exception e = null;
@ -79,7 +86,7 @@ namespace MCGalaxy.SQL {
for (int i = 0; i < 10; i++) {
try {
if (callback != null) {
query.Iterate(sql, connString, callback);
arg = query.Iterate(sql, connString, arg, callback);
} else if (results == null) {
query.Execute(sql, connString, createDB);
} else {
@ -87,7 +94,7 @@ namespace MCGalaxy.SQL {
}
query.parameters = null;
return;
return arg;
} catch (Exception ex) {
e = ex; // try yet again
}
@ -96,6 +103,7 @@ namespace MCGalaxy.SQL {
query.parameters = null;
File.AppendAllText("MySQL_error.log", DateTime.Now + " " + sql + "\r\n");
Logger.LogError(e);
return arg;
}
@ -110,5 +118,22 @@ namespace MCGalaxy.SQL {
}
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>
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) { }
@ -129,6 +129,17 @@ namespace MCGalaxy.SQL {
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>
/// <remarks> modifier is optional SQL which can be used to update only certain rows.</remarks>
public virtual void UpdateRows(string table, string columns,

View File

@ -21,9 +21,6 @@ using System.Data;
using System.Data.Common;
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>
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>
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)) {
conn.Open();
if (MultipleSchema)
@ -78,11 +75,12 @@ namespace MCGalaxy.SQL {
using (IDbCommand cmd = CreateCommand(query, conn)) {
FillParams(cmd);
using (IDataReader reader = cmd.ExecuteReader()) {
while (reader.Read() && callback(reader)) { }
while (reader.Read()) { arg = callback(reader, arg); }
}
}
conn.Close();
}
return arg;
}
void FillParams(IDbCommand cmd) {

View File

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

View File

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

View File

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

View File

@ -101,30 +101,26 @@ namespace MCGalaxy.Games {
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) {
DataTable table = Database.Backend.GetRows("ZombieStats", "*", "WHERE Name=@0", name);
ZombieStats stats = default(ZombieStats);
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;
ZombieStats stats = default(ZombieStats);
return (ZombieStats)Database.Backend.IterateRows("ZombieStats", "*", stats,
IterateStats, "WHERE Name=@0", name);
}
static void SaveStats(Player p) {
ZSData data = TryGet(p);
if (data == null || data.TotalRoundsSurvived == 0 && data.TotalInfected == 0) return;
int count = 0;
using (DataTable table = Database.Backend.GetRows("ZombieStats", "*", "WHERE Name=@0", p.name)) {
count = table.Rows.Count;
}
int count = Database.CountRows("ZombieStats", "WHERE Name=@0", p.name);
if (count == 0) {
Database.Backend.AddRow("ZombieStats", "TotalRounds, MaxRounds, TotalInfected, MaxInfected, Name",
data.TotalRoundsSurvived, data.MaxRoundsSurvived,

View File

@ -18,6 +18,8 @@
using System;
using System.Collections.Generic;
using System.Data;
using MCGalaxy.Blocks.Extended;
using MCGalaxy.Maths;
using MCGalaxy.SQL;
using MCGalaxy.Util;
using BlockID = System.UInt16;
@ -64,7 +66,7 @@ namespace MCGalaxy {
continue;
} else {
z.Access.Whitelisted.Add(owner);
z.Access.Min = LevelPermission.Admin;
z.Access.Min = LevelPermission.Admin;
}
z.Config.Name = "Zone" + id;
@ -99,19 +101,13 @@ namespace MCGalaxy {
internal static void LoadMessages(Level level, string 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 (DataRow row in table.Rows) {
ushort x = ushort.Parse(row["X"].ToString());
ushort y = ushort.Parse(row["Y"].ToString());
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);
}
foreach (Vec3U16 p in coords) {
BlockID block = level.GetBlock(p.X, p.Y, p.Z);
if (level.Props[block].IsMessageBlock) continue;
MessageBlock.Delete(name, p.X, p.Y, p.Z);
}
}

View File

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

View File

@ -138,14 +138,11 @@ namespace MCGalaxy {
SendMessage("&cPlease complete admin verification with %T/Pass [Password]!");
}
try {
if (group.CanExecute("Inbox") && Database.TableExists("Inbox" + name) ) {
using (DataTable table = Database.Backend.GetRows("Inbox" + name, "*")) {
if (table.Rows.Count > 0)
SendMessage("You have &a" + table.Rows.Count + " %Smessages in %T/Inbox");
}
if (group.CanExecute("Inbox") && Database.TableExists("Inbox" + name)) {
int count = Database.CountRows("Inbox" + name);
if (count > 0) {
SendMessage("You have &a" + count + " %Smessages in %T/Inbox");
}
} catch {
}
if (ServerConfig.PositionUpdateInterval > 1000)
@ -216,16 +213,17 @@ namespace MCGalaxy {
}
void GetPlayerStats() {
DataTable data = Database.Backend.GetRows("Players", "*", "WHERE Name=@0", name);
if (data.Rows.Count == 0) {
object raw = Database.Backend.IterateRows("Players", "*",
null, PlayerData.IteratePlayerData,
"WHERE Name=@0", name);
if (raw == null) {
PlayerData.Create(this);
Chat.MessageFrom(this, "λNICK %Shas connected for the first time!");
SendMessage("Welcome " + ColoredName + "%S! This is your first visit.");
} else {
PlayerData.Load(data, this);
PlayerData.Apply((PlayerData)raw, this);
SendMessage("Welcome back " + FullName + "%S! You've been here " + TimesVisited + " times!");
}
data.Dispose();
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
permissions and limitations under the Licenses.
*/
using System;
using System.Collections.Generic;
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>
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);
return target != null ? target.group : Group.GroupIn(name);
}
@ -34,10 +34,10 @@ namespace MCGalaxy {
public static string GetColoredName(Player p, string name) {
Player target = FindExact(name);
// 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();
}
public static int NonHiddenCount() {
Player[] players = Online.Items;
int count = 0;
@ -49,7 +49,7 @@ namespace MCGalaxy {
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) {
matches = 0;
if (!Formatter.ValidName(pl, name, "player")) return null;
@ -65,7 +65,7 @@ namespace MCGalaxy {
Player target = FindMatches(p, name, out matches);
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);
return FindOfflineNameMatches(p, name);
}
@ -80,36 +80,24 @@ namespace MCGalaxy {
}
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) {
using (DataTable results = Query(name, "*")) {
if (results.Rows.Count == 0) return null;
return PlayerData.Fill(results.Rows[0]);
}
}
/// <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();
}
string suffix = Database.Backend.CaselessWhereSuffix;
object raw = Database.Backend.IterateRows("Players", "*",
null, PlayerData.IteratePlayerData,
"WHERE Name=@0" + suffix, name);
return (PlayerData)raw;
}
/// <summary> Retrieves from the database the last IP address for the
/// player whose name caselessly exactly matches the given name. </summary>
/// <returns> Last IP address if found, null if not. </returns>
public static string FindName(string name) {
string suffix = Database.Backend.CaselessWhereSuffix;
return Database.GetString("Players", "Name", "WHERE Name=@0" + suffix, name);
}
public static string FindIP(string name) {
using (DataTable results = Query(name, "IP")) {
if (results.Rows.Count == 0) return null;
return results.Rows[0]["IP"].ToString().Trim();
}
string suffix = Database.Backend.CaselessWhereSuffix;
return Database.GetString("Players", "IP", "WHERE Name=@0" + suffix, name);
}
@ -135,39 +123,33 @@ namespace MCGalaxy {
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>
/// <remarks> This is current IP for online players, last IP for offline players from the database. </remarks>
public static List<string> FindAccounts(string ip) {
DataTable clones = Database.Backend.GetRows("Players", "Name", "WHERE IP=@0", ip);
List<string> accounts = new List<string>();
foreach (DataRow row in clones.Rows) {
string name = row["Name"].ToString();
if (!accounts.CaselessContains(name))
accounts.Add(name);
}
List<string> names = new List<string>();
Database.Backend.IterateRows("Players", "Name", names, IterateAccounts,
"WHERE IP=@0", ip);
// TODO: should we instead do save() when the player logs in
// by checking online players we avoid a DB write though
Player[] players = PlayerInfo.Online.Items;
foreach (Player p in players) {
if (p.ip != ip) continue;
if (!accounts.CaselessContains(p.name))
accounts.Add(p.name);
if (!names.CaselessContains(p.name)) names.Add(p.name);
}
clones.Dispose();
return accounts;
return names;
}
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) {
static DataRow QueryMulti(Player p, string name, string columns) {
string suffix = Database.Backend.CaselessLikeSuffix;
using (DataTable results = Database.Backend.GetRows("Players", columns,
"WHERE Name LIKE @0 ESCAPE '#' LIMIT 21" + suffix,

View File

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