diff --git a/Commands/Moderation/CmdHighlight.cs b/Commands/Moderation/CmdHighlight.cs index 3f572604d..ae0ad06bb 100644 --- a/Commands/Moderation/CmdHighlight.cs +++ b/Commands/Moderation/CmdHighlight.cs @@ -56,7 +56,8 @@ namespace MCGalaxy.Commands { PerformHighlight(p, seconds, who.UndoBuffer); } - UndoFile.HighlightPlayer(p, name.ToLower(), seconds, ref FoundUser); + DateTime start = DateTime.UtcNow.AddTicks(-seconds * TimeSpan.TicksPerSecond); + UndoFile.HighlightPlayer(p, name.ToLower(), start, ref FoundUser); if (FoundUser) { Player.SendMessage(p, "Now highlighting &b" + seconds + " %Sseconds for " + Server.FindColor(name) + name); Player.SendMessage(p, "&cUse /reload to un-highlight"); diff --git a/Commands/building/CmdUndo.cs b/Commands/building/CmdUndo.cs index da5d01cb6..5b55d6184 100644 --- a/Commands/building/CmdUndo.cs +++ b/Commands/building/CmdUndo.cs @@ -94,7 +94,7 @@ namespace MCGalaxy.Commands } UndoOnlineDrawOp op = new UndoOnlineDrawOp(); - op.seconds = seconds; + op.Start = DateTime.UtcNow.AddTicks(-seconds * TimeSpan.TicksPerSecond); op.who = who; op.Perform(null, p, null, null); @@ -114,7 +114,7 @@ namespace MCGalaxy.Commands } UndoOfflineDrawOp op = new UndoOfflineDrawOp(); - op.seconds = seconds; + op.Start = DateTime.UtcNow.AddTicks(-seconds * TimeSpan.TicksPerSecond); op.whoName = whoName; op.Perform(null, p, null, null); diff --git a/Drawing/DrawOps/DrawOp.cs b/Drawing/DrawOps/DrawOp.cs index c1981da33..98ff42bc2 100644 --- a/Drawing/DrawOps/DrawOp.cs +++ b/Drawing/DrawOps/DrawOp.cs @@ -160,20 +160,18 @@ namespace MCGalaxy.Drawing.Ops { UndoDrawOpEntry entry = new UndoDrawOpEntry(); entry.DrawOpName = op.Name; entry.LevelName = p.level.name; - entry.SetStart(p); - DateTime start = DateTime.UtcNow; + entry.Start = DateTime.UtcNow; bool needReveal = op.DetermineDrawOpMethod(p.level, affected); op.Perform(marks, p, p.level, brush); - entry.SetEnd(p); + entry.End = DateTime.UtcNow; - if (start > p.UndoBuffer.LastClear) { + if (entry.Start > p.UndoBuffer.LastClear) { UndoDrawOpEntry[] items = p.UndoDrawOps.Items; if (items.Length == 20) p.UndoDrawOps.Remove(items[0]); } else { // UndoBuffer has been cleared during the draw op. - entry.StartNode = p.UndoBuffer.Head; - entry.StartIndex = 0; + entry.Start = p.UndoBuffer.LastClear; p.RemoveInvalidUndos(); } p.UndoDrawOps.Add(entry); diff --git a/Drawing/DrawOps/UndoDrawOp.cs b/Drawing/DrawOps/UndoDrawOp.cs index 126c00067..6d63da806 100644 --- a/Drawing/DrawOps/UndoDrawOp.cs +++ b/Drawing/DrawOps/UndoDrawOp.cs @@ -26,7 +26,12 @@ namespace MCGalaxy.Drawing.Ops { public override string Name { get { return "UndoOnline"; } } - internal long seconds; + /// Point in time that the /undo should go backwards up to. + public DateTime Start = DateTime.MinValue; + + /// Point in time that the /undo should start updating blocks. + public DateTime End = DateTime.MaxValue; + internal Player who; internal Level saveLevel = null; @@ -35,7 +40,7 @@ namespace MCGalaxy.Drawing.Ops { public override void Perform(Vector3U16[] marks, Player p, Level lvl, Brush brush) { PerformUndo(p, ref saveLevel); bool foundUser = false; - UndoFile.UndoPlayer(p, who.name.ToLower(), seconds, ref foundUser); + UndoFile.UndoPlayer(p, who.name.ToLower(), Start, ref foundUser); } void PerformUndo(Player p, ref Level saveLvl) { @@ -54,8 +59,9 @@ namespace MCGalaxy.Drawing.Ops { UndoCacheItem item = items[i]; ushort x, y, z; node.Unpack(item.Index, out x, out y, out z); - DateTime time = node.BaseTime.AddTicks((item.TimeDelta + seconds) * TimeSpan.TicksPerSecond); - if (time < DateTime.UtcNow) { buffer.CheckIfSend(true); return; } + DateTime time = node.BaseTime.AddTicks(item.TimeDelta * TimeSpan.TicksPerSecond); + if (time > End) continue; + if (time < Start) { buffer.CheckIfSend(true); return; } byte b = lvl.GetTile(x, y, z); byte newTile = 0, newExtTile = 0; @@ -91,14 +97,16 @@ namespace MCGalaxy.Drawing.Ops { public override string Name { get { return "UndoOffline"; } } - internal long seconds; + /// Point in time that the /undo should go backwards up to. + public DateTime Start = DateTime.MinValue; + internal string whoName; internal bool foundUser = false; public override int GetBlocksAffected(Level lvl, Vector3U16[] marks) { return 0; } public override void Perform(Vector3U16[] marks, Player p, Level lvl, Brush brush) { - UndoFile.UndoPlayer(p, whoName.ToLower(), seconds, ref foundUser); + UndoFile.UndoPlayer(p, whoName.ToLower(), Start, ref foundUser); } } diff --git a/MCGalaxy_.csproj b/MCGalaxy_.csproj index 6056a955c..80ea4f160 100644 --- a/MCGalaxy_.csproj +++ b/MCGalaxy_.csproj @@ -480,7 +480,6 @@ - diff --git a/Player/Player.cs b/Player/Player.cs index b9ed6ec7a..51a114e71 100644 --- a/Player/Player.cs +++ b/Player/Player.cs @@ -951,7 +951,7 @@ Next: continue; internal void RemoveInvalidUndos() { UndoDrawOpEntry[] items = UndoDrawOps.Items; for (int i = 0; i < items.Length; i++) { - if (!items[i].IsValid(this)) + if (items[i].End < UndoBuffer.LastClear) UndoDrawOps.Remove(items[i]); } } diff --git a/Player/Undo/UndoCache.cs b/Player/Undo/UndoCache.cs index dff418c04..6488aadde 100644 --- a/Player/Undo/UndoCache.cs +++ b/Player/Undo/UndoCache.cs @@ -19,7 +19,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; -using System.Threading; namespace MCGalaxy.Util { @@ -101,7 +100,7 @@ namespace MCGalaxy.Util { [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct UndoCacheItem { public int Index; - public byte Type, NewType; + public byte Type, NewType; public ushort Flags; // upper 2 bits for 'ext' or 'physics' type, lower 14 bits for time delta. public short TimeDelta { @@ -141,7 +140,7 @@ namespace MCGalaxy.Util { item.Flags |= (ushort)(1 << 14); } else { item.Type = pos.type; - } + } if (pos.newtype == Block.custom_block) { item.NewType = pos.newExtType; item.Flags |= (ushort)(1 << 15); @@ -151,4 +150,10 @@ namespace MCGalaxy.Util { return item; } } + + public sealed class UndoDrawOpEntry { + public string DrawOpName; + public string LevelName; + public DateTime Start, End; + } } diff --git a/Player/Undo/UndoDrawOpEntry.cs b/Player/Undo/UndoDrawOpEntry.cs deleted file mode 100644 index de0082c3f..000000000 --- a/Player/Undo/UndoDrawOpEntry.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - Copyright 2015 MCGalaxy - - Dual-licensed under the Educational Community License, Version 2.0 and - the GNU General Public License, Version 3 (the "Licenses"); you may - not use this file except in compliance with the Licenses. You may - obtain a copy of the Licenses at - - http://www.opensource.org/licenses/ecl2.php - http://www.gnu.org/licenses/gpl-3.0.html - - Unless required by applicable law or agreed to in writing, - software distributed under the Licenses are distributed on an "AS IS" - BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - or implied. See the Licenses for the specific language governing - permissions and limitations under the Licenses. - */ -using System; - -namespace MCGalaxy.Util { - - public class UndoDrawOpEntry { - - public string DrawOpName; - public string LevelName; - public UndoCacheNode StartNode; - public int StartIndex; - public UndoCacheNode EndNode; - public int EndIndex; - - public void SetStart(Player p) { - StartNode = p.UndoBuffer.Tail; - if (StartNode != null) - StartIndex = StartNode.Items.Count; - } - - public void SetEnd(Player p) { - EndNode = p.UndoBuffer.Tail; - if (EndNode != null) - EndIndex = EndNode.Items.Count; - } - - public bool IsValid(Player p) { - UndoCache buffer = p.UndoBuffer; - if (p.UndoBuffer.Tail == null) return false; - - UndoCacheNode node = p.UndoBuffer.Tail; - while ((node = node.Prev) != null) { - if (node == EndNode) return true; - } - return false; - } - } -} diff --git a/Player/Undo/UndoFile.cs b/Player/Undo/UndoFile.cs index d926dfc7f..40792ffeb 100644 --- a/Player/Undo/UndoFile.cs +++ b/Player/Undo/UndoFile.cs @@ -34,9 +34,9 @@ namespace MCGalaxy.Util { protected abstract void ReadUndoData(List buffer, string path); - protected abstract bool UndoEntry(Player p, string path, ref byte[] temp, long seconds); + protected abstract bool UndoEntry(Player p, string path, ref byte[] temp, DateTime start); - protected abstract bool HighlightEntry(Player p, string path, ref byte[] temp, long seconds); + protected abstract bool HighlightEntry(Player p, string path, ref byte[] temp, DateTime start); protected abstract string Extension { get; } @@ -59,17 +59,17 @@ namespace MCGalaxy.Util { NewFormat.SaveUndoData(p.UndoBuffer, path); } - public static void UndoPlayer(Player p, string targetName, long seconds, ref bool FoundUser) { - FilterEntries(p, undoDir, targetName, seconds, false, ref FoundUser); - FilterEntries(p, prevUndoDir, targetName, seconds, false, ref FoundUser); + public static void UndoPlayer(Player p, string targetName, DateTime start, ref bool FoundUser) { + FilterEntries(p, undoDir, targetName, start, false, ref FoundUser); + FilterEntries(p, prevUndoDir, targetName, start, false, ref FoundUser); } - public static void HighlightPlayer(Player p, string targetName, long seconds, ref bool FoundUser) { - FilterEntries(p, undoDir, targetName, seconds, true, ref FoundUser); - FilterEntries(p, prevUndoDir, targetName, seconds, true, ref FoundUser); + public static void HighlightPlayer(Player p, string targetName, DateTime start, ref bool FoundUser) { + FilterEntries(p, undoDir, targetName, start, true, ref FoundUser); + FilterEntries(p, prevUndoDir, targetName, start, true, ref FoundUser); } - static void FilterEntries(Player p, string dir, string name, long seconds, bool highlight, ref bool FoundUser) { + static void FilterEntries(Player p, string dir, string name, DateTime start, bool highlight, ref bool FoundUser) { string path = Path.Combine(dir, name); if (!Directory.Exists(path)) return; @@ -89,9 +89,9 @@ namespace MCGalaxy.Util { if (format == null) continue; if (highlight) { - if (!format.HighlightEntry(p, path, ref temp, seconds)) break; + if (!format.HighlightEntry(p, path, ref temp, start)) break; } else { - if (!format.UndoEntry(p, path, ref temp, seconds)) break; + if (!format.UndoEntry(p, path, ref temp, start)) break; } } FoundUser = true; diff --git a/Player/Undo/UndoFileBin.cs b/Player/Undo/UndoFileBin.cs index e4e836f94..904f50a86 100644 --- a/Player/Undo/UndoFileBin.cs +++ b/Player/Undo/UndoFileBin.cs @@ -116,13 +116,11 @@ namespace MCGalaxy.Util { } } - protected override bool UndoEntry(Player p, string path, ref byte[] temp, long seconds) { + protected override bool UndoEntry(Player p, string path, ref byte[] temp, DateTime start) { List list = new List(); - DateTime now = DateTime.UtcNow; int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds; Player.UndoPos Pos; bool isPlayer = p != null && p.group.Permission < LevelPermission.Nobody; - seconds *= TimeSpan.TicksPerSecond; using (Stream fs = File.OpenRead(path)) using (BinaryReader r = new BinaryReader(fs)) @@ -131,7 +129,7 @@ namespace MCGalaxy.Util { for (int i = list.Count - 1; i >= 0; i--) { ChunkHeader chunk = list[i]; Level lvl; - if (!CheckChunk(chunk, now, seconds, p, out lvl)) + if (!CheckChunk(chunk, start, p, out lvl)) return false; if (lvl == null || (isPlayer && lvl != p.level)) continue; BufferedBlockSender buffer = new BufferedBlockSender(lvl); @@ -144,7 +142,7 @@ namespace MCGalaxy.Util { for (int j = chunk.Entries - 1; j >= 0; j-- ) { int offset = j * entrySize; DateTime time = chunk.BaseTime.AddTicks(U16(temp, offset + 0) * TimeSpan.TicksPerSecond); - if (time.AddTicks(seconds) < now) { buffer.CheckIfSend(true); return false; } + if (time < start) { buffer.CheckIfSend(true); return false; } Pos.x = U16(temp, offset + 2); Pos.y = U16(temp, offset + 4); Pos.z = U16(temp, offset + 6); Pos.type = lvl.GetTile(Pos.x, Pos.y, Pos.z); @@ -169,10 +167,8 @@ namespace MCGalaxy.Util { return true; } - protected override bool HighlightEntry(Player p, string path, ref byte[] temp, long seconds) { + protected override bool HighlightEntry(Player p, string path, ref byte[] temp, DateTime start) { List list = new List(); - DateTime now = DateTime.UtcNow; - seconds *= TimeSpan.TicksPerSecond; using (Stream fs = File.OpenRead(path)) using (BinaryReader r = new BinaryReader(fs)) @@ -181,7 +177,7 @@ namespace MCGalaxy.Util { for (int i = list.Count - 1; i >= 0; i--) { ChunkHeader chunk = list[i]; Level lvl; - if (!CheckChunk(chunk, now, seconds, p, out lvl)) + if (!CheckChunk(chunk, start, p, out lvl)) return false; if (lvl == null || lvl != p.level) continue; @@ -192,7 +188,7 @@ namespace MCGalaxy.Util { for (int j = chunk.Entries - 1; j >= 0; j-- ) { int offset = j * entrySize; DateTime time = chunk.BaseTime.AddTicks(U16(temp, offset + 0) * TimeSpan.TicksPerSecond); - if (time.AddTicks(seconds) < now) return false; + if (time < start) return false; ushort x = U16(temp, offset + 2), y = U16(temp, offset + 4), z = U16(temp, offset + 6); byte lvlTile = lvl.GetTile(x, y, z); @@ -215,12 +211,11 @@ namespace MCGalaxy.Util { return (ushort)(buffer[offset + 0] | buffer[offset + 1] << 8); } - static bool CheckChunk(ChunkHeader chunk, DateTime now, long ticks, Player p, out Level lvl) { + static bool CheckChunk(ChunkHeader chunk, DateTime start, Player p, out Level lvl) { DateTime time = chunk.BaseTime; lvl = null; - if (time.AddTicks(65536 * TimeSpan.TicksPerSecond + ticks) < now) - return false; // we can safely discard the entire chunk - + if (time.AddTicks(65536 * TimeSpan.TicksPerSecond) < start) + return false; // we can safely discard the entire chunk lvl = LevelInfo.FindExact(chunk.LevelName); return true; } diff --git a/Player/Undo/UndoFileText.cs b/Player/Undo/UndoFileText.cs index a96d2245f..5c3a8d48f 100644 --- a/Player/Undo/UndoFileText.cs +++ b/Player/Undo/UndoFileText.cs @@ -67,7 +67,7 @@ namespace MCGalaxy.Util { } } - protected override bool UndoEntry(Player p, string path, ref byte[] temp, long seconds) { + protected override bool UndoEntry(Player p, string path, ref byte[] temp, DateTime start) { Player.UndoPos Pos; int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds; Pos.extType = 0; Pos.newExtType = 0; @@ -77,7 +77,7 @@ namespace MCGalaxy.Util { for (int i = (lines.Length - 1) / 7; i >= 0; i--) { try { // line format: mapName x y z date oldblock newblock - if (!InTime(lines[(i * 7) - 3], seconds)) return false; + if (!InTime(lines[(i * 7) - 3], start)) return false; Level lvl = LevelInfo.FindExact(lines[(i * 7) - 7]); if (lvl == null) continue; @@ -102,7 +102,7 @@ namespace MCGalaxy.Util { return true; } - protected override bool HighlightEntry(Player p, string path, ref byte[] temp, long seconds) { + protected override bool HighlightEntry(Player p, string path, ref byte[] temp, DateTime start) { Player.UndoPos Pos; Pos.extType = 0; Pos.newExtType = 0; string[] lines = File.ReadAllText(path).Split(' '); @@ -110,7 +110,7 @@ namespace MCGalaxy.Util { for (int i = (lines.Length - 1) / 7; i >= 0; i--) { try { // line format: mapName x y z date oldblock newblock - if (!InTime(lines[(i * 7) - 3], seconds)) return false; + if (!InTime(lines[(i * 7) - 3], start)) return false; Level foundLevel = LevelInfo.FindExact(lines[(i * 7) - 7]); if (foundLevel == null || foundLevel != p.level) continue; @@ -133,11 +133,10 @@ namespace MCGalaxy.Util { return true; } - static bool InTime(string line, long seconds) { + static bool InTime(string line, DateTime start) { line = line.Replace('&', ' '); - DateTime time = DateTime.Parse(line, CultureInfo.InvariantCulture) - .AddSeconds(seconds); - return time >= DateTime.Now; + DateTime time = DateTime.Parse(line, CultureInfo.InvariantCulture); + return time >= start; } } }