diff --git a/Commands/building/CmdUndo.cs b/Commands/building/CmdUndo.cs index 4d588b4ce..39e6b9da8 100644 --- a/Commands/building/CmdUndo.cs +++ b/Commands/building/CmdUndo.cs @@ -23,128 +23,154 @@ using MCGalaxy.Util; namespace MCGalaxy.Commands { - public sealed class CmdUndo : Command - { - public override string name { get { return "undo"; } } - public override string shortcut { get { return "u"; } } - public override string type { get { return CommandTypes.Building; } } - public override bool museumUsable { get { return true; } } - public override LevelPermission defaultRank { get { return LevelPermission.Guest; } } - public override CommandPerm[] AdditionalPerms { - get { return new[] { - new CommandPerm(LevelPermission.Operator, "The lowest rank to undo other players actions", 1), - new CommandPerm(LevelPermission.AdvBuilder, "The lowest rank to be able to undo physics", 2), - }; } - } + public sealed class CmdUndo : Command + { + public override string name { get { return "undo"; } } + public override string shortcut { get { return "u"; } } + public override string type { get { return CommandTypes.Building; } } + public override bool museumUsable { get { return true; } } + public override LevelPermission defaultRank { get { return LevelPermission.Guest; } } + public override CommandPerm[] AdditionalPerms { + get { return new[] { + new CommandPerm(LevelPermission.Operator, "The lowest rank to undo other players actions", 1), + new CommandPerm(LevelPermission.AdvBuilder, "The lowest rank to be able to undo physics", 2), + }; } + } - public override void Use(Player p, string message) { - if (p != null) p.RedoBuffer.Clear(); - int ignored = 0; - if (message == "") { - if (p == null) { Player.SendMessage(null, "Console doesn't have an undo buffer."); return; } - message = p.name.ToLower() + " 30"; - } else if (p != null && int.TryParse(message, out ignored)) { - message = p.name.ToLower() + " " + message; - } - - string[] parts = message.Split(' '); - bool undoPhysics = parts[0].CaselessEq("physics"); - Player who = undoPhysics ? null : PlayerInfo.Find(parts[0]); - long seconds = GetSeconds(p, who, parts.Length > 1 ? parts[1] : "30"); - - if (parts.Length > 1 && parts[1].CaselessEq("update")) { - UndoFile.UpgradePlayerUndoFiles(parts[0]); - Player.SendMessage(p, "Updated undo files for " + parts[0] + " to the new binary format."); - return; - } + public override void Use(Player p, string message) { + if (p != null) p.RedoBuffer.Clear(); + int ignored = 0; + if (message == "") { + if (p == null) { Player.SendMessage(null, "Console doesn't have an undo buffer."); return; } + UndoSelf(p); return; + } else if (p != null && int.TryParse(message, out ignored)) { + message = p.name.ToLower() + " " + message; + } + + string[] parts = message.Split(' '); + bool undoPhysics = parts[0].CaselessEq("physics"); + Player who = undoPhysics ? null : PlayerInfo.Find(parts[0]); + long seconds = GetSeconds(p, who, parts.Length > 1 ? parts[1] : "30"); + + if (parts.Length > 1 && parts[1].CaselessEq("update")) { + UndoFile.UpgradePlayerUndoFiles(parts[0]); + Player.SendMessage(p, "Updated undo files for " + parts[0] + " to the new binary format."); + return; + } - if (who != null) - UndoOnlinePlayer(p, seconds, who); - else if (undoPhysics) - UndoLevelPhysics(p, seconds); - else - UndoOfflinePlayer(p, seconds, parts[0]); - } + if (who != null) + UndoOnlinePlayer(p, seconds, who); + else if (undoPhysics) + UndoLevelPhysics(p, seconds); + else + UndoOfflinePlayer(p, seconds, parts[0]); + } - const int undoMax = -1; // allows everything to be undone. - internal static long GetSeconds(Player p, Player who, string param) { - long secs; - if (param.CaselessEq("all")) { - secs = (p.group.maxUndo == undoMax || p == who) ? int.MaxValue : p.group.maxUndo; - } else if (!long.TryParse(param, out secs)) { - Player.SendMessage(p, "Invalid seconds, using 30 seconds."); - return 30; - } + const int undoMax = -1; // allows everything to be undone. + internal static long GetSeconds(Player p, Player who, string param) { + long secs; + if (param.CaselessEq("all")) { + secs = (p.group.maxUndo == undoMax || p == who) ? int.MaxValue : p.group.maxUndo; + } else if (!long.TryParse(param, out secs)) { + Player.SendMessage(p, "Invalid seconds, using 30 seconds."); + return 30; + } - if (secs == 0) secs = 5400; - if (p != null && p != who && p.group.maxUndo != undoMax && secs > p.group.maxUndo) { - Player.SendMessage(p, p.group.name + "s may only undo up to " + p.group.maxUndo + " seconds."); - return p.group.maxUndo; - } - return secs; - } - - void UndoOnlinePlayer(Player p, long seconds, Player who) { - if (p != null && p != who) { - if (who.group.Permission > p.group.Permission) { - MessageTooHighRank(p, "undo", true); return; - } - if (!CheckAdditionalPerm(p)) { MessageNeedPerms(p, "can undo other players."); return; } - } - - UndoOnlineDrawOp op = new UndoOnlineDrawOp(); - op.Start = DateTime.UtcNow.AddTicks(-seconds * TimeSpan.TicksPerSecond); - op.who = who; - DrawOp.DoDrawOp(op, null, p, new [] { Vec3U16.MaxVal, Vec3U16.MaxVal } ); - - Level saveLevel = op.saveLevel; - if (p == who) { - Player.SendMessage(p, "Undid your actions for the past &b" + seconds + " %Sseconds."); - } else { - Player.SendChatFrom(who, who.color + who.DisplayName + "%S's actions for the past &b" + seconds + " seconds were undone.", false); - } - Server.s.Log(who.name + "'s actions for the past " + seconds + " seconds were undone."); - if (saveLevel != null) saveLevel.Save(true); - } - - void UndoOfflinePlayer(Player p, long seconds, string whoName) { - if (!CheckAdditionalPerm(p)) { MessageNeedPerms(p, "can undo other players."); return; } - UndoOfflineDrawOp op = new UndoOfflineDrawOp(); - op.Start = DateTime.UtcNow.AddTicks(-seconds * TimeSpan.TicksPerSecond); - op.whoName = whoName; - DrawOp.DoDrawOp(op, null, p, new [] { Vec3U16.MaxVal, Vec3U16.MaxVal } ); + if (secs == 0) secs = 5400; + if (p != null && p != who && p.group.maxUndo != undoMax && secs > p.group.maxUndo) { + Player.SendMessage(p, p.group.name + "s may only undo up to " + p.group.maxUndo + " seconds."); + return p.group.maxUndo; + } + return secs; + } + + void UndoSelf(Player p) { + UndoDrawOpEntry[] entries = p.UndoDrawOps.Items; + if (entries.Length == 0) { + Player.SendMessage(p, "You have no draw operations to undo."); + Player.SendMessage(p, "Try using %T/undo %Sinstead."); + return; + } + + for (int i = entries.Length - 1; i >= 0; i--) { + UndoDrawOpEntry entry = entries[i]; + if (entry.DrawOpName == "UndoSelf") continue; + + UndoSelfDrawOp op = new UndoSelfDrawOp(); + op.who = p; + op.Start = entry.Start; op.End = entry.End; + DrawOp.DoDrawOp(op, null, p, new [] { Vec3U16.MaxVal, Vec3U16.MaxVal } ); + + entry.UndoDrawOpName = entry.DrawOpName; + entry.DrawOpName = "UndoSelf"; + return; + } + + Player.SendMessage(p, "Max number of draw operations that can be undone reached."); + Player.SendMessage(p, "Try using %T/undo %Sinstead."); + } + + void UndoOnlinePlayer(Player p, long seconds, Player who) { + if (p != null && p != who) { + if (who.group.Permission > p.group.Permission) { + MessageTooHighRank(p, "undo", true); return; + } + if (!CheckAdditionalPerm(p)) { MessageNeedPerms(p, "can undo other players."); return; } + } + + UndoOnlineDrawOp op = new UndoOnlineDrawOp(); + op.Start = DateTime.UtcNow.AddTicks(-seconds * TimeSpan.TicksPerSecond); + op.who = who; + DrawOp.DoDrawOp(op, null, p, new [] { Vec3U16.MaxVal, Vec3U16.MaxVal } ); + + Level saveLevel = op.saveLevel; + if (p == who) { + Player.SendMessage(p, "Undid your actions for the past &b" + seconds + " %Sseconds."); + } else { + Player.SendChatFrom(who, who.color + who.DisplayName + "%S's actions for the past &b" + seconds + " seconds were undone.", false); + } + Server.s.Log(who.name + "'s actions for the past " + seconds + " seconds were undone."); + if (saveLevel != null) saveLevel.Save(true); + } + + void UndoOfflinePlayer(Player p, long seconds, string whoName) { + if (!CheckAdditionalPerm(p)) { MessageNeedPerms(p, "can undo other players."); return; } + UndoOfflineDrawOp op = new UndoOfflineDrawOp(); + op.Start = DateTime.UtcNow.AddTicks(-seconds * TimeSpan.TicksPerSecond); + op.whoName = whoName; + DrawOp.DoDrawOp(op, null, p, new [] { Vec3U16.MaxVal, Vec3U16.MaxVal } ); - if (op.foundUser) { - Player.GlobalMessage(Server.FindColor(whoName) + whoName + "%S's actions for the past &b" + seconds + " %Sseconds were undone."); - Server.s.Log(whoName + "'s actions for the past " + seconds + " seconds were undone."); - p.level.Save(true); - } else { - Player.SendMessage(p, "Could not find player specified."); - } - } - - void UndoLevelPhysics(Player p, long seconds) { - if (!CheckAdditionalPerm(p, 2)) { MessageNeedPerms(p, "can undo physics.", 2); return; } - if (p != null && !p.group.CanExecute("physics")) { - Player.SendMessage(p, "You can only undo physics if you can use /physics."); return; - } - Command.all.Find("physics").Use(p, "0"); - UndoPhysicsDrawOp op = new UndoPhysicsDrawOp(); - op.seconds = seconds; - DrawOp.DoDrawOp(op, null, p, new [] { Vec3U16.MaxVal, Vec3U16.MaxVal } ); - - Player.GlobalMessage("Physics were undone &b" + seconds + " %Sseconds"); - Server.s.Log( "Physics were undone &b" + seconds + " %Sseconds"); - p.level.Save(true); - } + if (op.foundUser) { + Player.GlobalMessage(Server.FindColor(whoName) + whoName + "%S's actions for the past &b" + seconds + " %Sseconds were undone."); + Server.s.Log(whoName + "'s actions for the past " + seconds + " seconds were undone."); + p.level.Save(true); + } else { + Player.SendMessage(p, "Could not find player specified."); + } + } + + void UndoLevelPhysics(Player p, long seconds) { + if (!CheckAdditionalPerm(p, 2)) { MessageNeedPerms(p, "can undo physics.", 2); return; } + if (p != null && !p.group.CanExecute("physics")) { + Player.SendMessage(p, "You can only undo physics if you can use /physics."); return; + } + Command.all.Find("physics").Use(p, "0"); + UndoPhysicsDrawOp op = new UndoPhysicsDrawOp(); + op.seconds = seconds; + DrawOp.DoDrawOp(op, null, p, new [] { Vec3U16.MaxVal, Vec3U16.MaxVal } ); + + Player.GlobalMessage("Physics were undone &b" + seconds + " %Sseconds"); + Server.s.Log( "Physics were undone &b" + seconds + " %Sseconds"); + p.level.Save(true); + } - public override void Help(Player p) { - Player.SendMessage(p, "/undo [player] [seconds] - Undoes the blockchanges made by [player] in the previous [seconds]."); - if (p == null || (p.group.maxUndo <= 500000 || p.group.maxUndo == 0)) - Player.SendMessage(p, "/undo [player] all - &cWill undo 68 years, 18 days, 15 hours, 28 minutes, 31 seconds for [player]"); - if (p == null || (p.group.maxUndo <= 1800 || p.group.maxUndo == 0)) - Player.SendMessage(p, "/undo [player] - &cWill undo 30 minutes"); - Player.SendMessage(p, "/undo physics [seconds] - Undoes the physics for the current map"); - } - } + public override void Help(Player p) { + Player.SendMessage(p, "/undo [player] [seconds] - Undoes the blockchanges made by [player] in the previous [seconds]."); + if (p == null || (p.group.maxUndo <= 500000 || p.group.maxUndo == 0)) + Player.SendMessage(p, "/undo [player] all - &cWill undo 68 years, 18 days, 15 hours, 28 minutes, 31 seconds for [player]"); + if (p == null || (p.group.maxUndo <= 1800 || p.group.maxUndo == 0)) + Player.SendMessage(p, "/undo [player] - &cWill undo 30 minutes"); + Player.SendMessage(p, "/undo physics [seconds] - Undoes the physics for the current map"); + } + } } diff --git a/Drawing/DrawOps/DrawOp.cs b/Drawing/DrawOps/DrawOp.cs index f95bdf6e8..fd454b3db 100644 --- a/Drawing/DrawOps/DrawOp.cs +++ b/Drawing/DrawOps/DrawOp.cs @@ -188,20 +188,17 @@ namespace MCGalaxy.Drawing.Ops { entry.DrawOpName = op.Name; entry.LevelName = p.level.name; entry.Start = DateTime.UtcNow; + // Use same time method as DoBlockchange writing to undo buffer + int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds; + entry.Start = Server.StartTime.AddTicks(timeDelta * TimeSpan.TicksPerSecond); bool needReveal = op.DetermineDrawOpMethod(p.level, affected); op.Perform(marks, p, p.level, brush); - entry.End = DateTime.UtcNow; + timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds; + entry.End = Server.StartTime.AddTicks(timeDelta * TimeSpan.TicksPerSecond); - if (entry.Start > p.UndoBuffer.LastClear) { - UndoDrawOpEntry[] items = p.UndoDrawOps.Items; - if (items.Length == 25) - p.UndoDrawOps.Remove(items[0]); - } else { // UndoBuffer has been cleared during the draw op. - entry.Start = p.UndoBuffer.LastClear; - p.RemoveInvalidUndos(); - } - p.UndoDrawOps.Add(entry); + if (op.Name != "UndoSelf") + p.UndoDrawOps.Add(entry); DoReload(p, needReveal); return true; } diff --git a/Drawing/DrawOps/UndoDrawOp.cs b/Drawing/DrawOps/UndoDrawOp.cs index b7fa9dd74..70410e204 100644 --- a/Drawing/DrawOps/UndoDrawOp.cs +++ b/Drawing/DrawOps/UndoDrawOp.cs @@ -23,6 +23,10 @@ using MCGalaxy.Util; namespace MCGalaxy.Drawing.Ops { + public class UndoSelfDrawOp : UndoOnlineDrawOp { + public override string Name { get { return "UndoSelf"; } } + } + public class UndoOnlineDrawOp : DrawOp { public override string Name { get { return "UndoOnline"; } } diff --git a/Player/Undo/UndoCache.cs b/Player/Undo/UndoCache.cs index 34a5dc252..7935fd137 100644 --- a/Player/Undo/UndoCache.cs +++ b/Player/Undo/UndoCache.cs @@ -151,7 +151,7 @@ namespace MCGalaxy.Util { } public sealed class UndoDrawOpEntry { - public string DrawOpName; + public string DrawOpName, UndoDrawOpName; public string LevelName; public DateTime Start, End; }