From ee720499b40ad080e3bc1b84a89c996b38a6478e Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sun, 3 Jan 2016 23:34:13 +1100 Subject: [PATCH] Fix bugs in UndoFileBin, also add new 'ReadUndoData' method to allow for converting between the formats. --- Player/Undo/UndoFile.cs | 75 +++++++++++++++----- Player/Undo/UndoFileBin.cs | 57 +++++++++++---- Player/Undo/UndoFileText.cs | 21 ++++++ Starter.csproj | 134 ++++++++++++++++++------------------ 4 files changed, 190 insertions(+), 97 deletions(-) diff --git a/Player/Undo/UndoFile.cs b/Player/Undo/UndoFile.cs index 42ce00c28..ea56c1324 100644 --- a/Player/Undo/UndoFile.cs +++ b/Player/Undo/UndoFile.cs @@ -25,10 +25,13 @@ namespace MCGalaxy.Util { public abstract class UndoFile { protected const string undoDir = "extra/undo", prevUndoDir = "extra/undoPrevious"; - public static UndoFile Instance = new UndoFileText(); + public static UndoFile OldFormat = new UndoFileText(); + public static UndoFile NewFormat = new UndoFileBin(); protected abstract void SaveUndoData(List buffer, string path); + protected abstract void ReadUndoData(List buffer, string path); + protected abstract bool UndoEntry(Player p, string path, long seconds); protected abstract bool HighlightEntry(Player p, string path, long seconds); @@ -39,9 +42,7 @@ namespace MCGalaxy.Util { if( p == null || p.UndoBuffer == null || p.UndoBuffer.Count < 1) return; CreateDefaultDirectories(); - - DirectoryInfo di = new DirectoryInfo(undoDir); - if (di.GetDirectories("*").Length >= Server.totalUndo) { + if (Directory.GetDirectories(undoDir).Length >= Server.totalUndo) { Directory.Delete(prevUndoDir, true); Directory.Move(undoDir, prevUndoDir); Directory.CreateDirectory(undoDir); @@ -51,11 +52,9 @@ namespace MCGalaxy.Util { if (!Directory.Exists(playerDir)) Directory.CreateDirectory(playerDir); - di = new DirectoryInfo(playerDir); - string ext = Instance.Extension; - int numFiles = di.GetFiles("*" + ext).Length; - string path = Path.Combine(playerDir, numFiles + ext); - Instance.SaveUndoData(p.UndoBuffer, path); + int numFiles = Directory.GetFiles(playerDir).Length; + string path = Path.Combine(playerDir, numFiles + NewFormat.Extension); + NewFormat.SaveUndoData(p.UndoBuffer, path); } public static void UndoPlayer(Player p, string targetName, long seconds, ref bool FoundUser) { @@ -74,26 +73,70 @@ namespace MCGalaxy.Util { string path = Path.Combine(dir, name); if (!Directory.Exists(path)) return; - DirectoryInfo di = new DirectoryInfo(path); - string ext = Instance.Extension; - int numFiles = di.GetFiles("*" + ext).Length; + string[] files = Directory.GetFiles(path); + Array.Sort(files, CompareFiles); - for (int i = numFiles - 1; i >= 0; i--) { - string undoPath = Path.Combine(path, i + ext); + for (int i = files.Length - 1; i >= 0; i--) { + path = files[i]; + string file = Path.GetFileName(path); + if (file.Length == 0 || file[0] < '0' || file[0] > '9') + continue; + + UndoFile format = null; + if (path.EndsWith(OldFormat.Extension)) format = OldFormat; + if (path.EndsWith(NewFormat.Extension)) format = NewFormat; + if (format == null) continue; + if (highlight) { - if (!Instance.HighlightEntry(p, undoPath, seconds)) break; + if (!format.HighlightEntry(p, path, seconds)) break; } else { - if (!Instance.UndoEntry(p, undoPath, seconds)) break; + if (!format.UndoEntry(p, path, seconds)) break; } } FoundUser = true; } + static int CompareFiles(string a, string b) { + int aNumEnd = a.IndexOf('.'), bNumEnd = b.IndexOf('.'); + if (aNumEnd < 0 || bNumEnd < 0) return a.CompareTo(b); + + int aNum, bNum; + if (!int.TryParse(a.Substring(0, aNumEnd), out aNum) || + !int.TryParse(b.Substring(0, bNumEnd), out bNum)) + return a.CompareTo(b); + return aNum.CompareTo(bNum); + } + public static void CreateDefaultDirectories() { if (!Directory.Exists(undoDir)) Directory.CreateDirectory(undoDir); if (!Directory.Exists(prevUndoDir)) Directory.CreateDirectory(prevUndoDir); } + + public static void UpgradePlayerUndoFiles(string name) { + UpgradeFiles(undoDir, name); + UpgradeFiles(prevUndoDir, name); + } + + static void UpgradeFiles(string dir, string name) { + string path = Path.Combine(dir, name); + if (!Directory.Exists(path)) + return; + string[] files = Directory.GetFiles(path); + List buffer = new List(); + + for (int i = 0; i < files.Length; i++) { + path = files[i]; + if (!path.EndsWith(OldFormat.Extension)) + continue; + buffer.Clear(); + OldFormat.ReadUndoData(buffer, path); + + string newPath = Path.ChangeExtension(path, NewFormat.Extension); + NewFormat.SaveUndoData(buffer, newPath); + File.Delete(path); + } + } } } \ No newline at end of file diff --git a/Player/Undo/UndoFileBin.cs b/Player/Undo/UndoFileBin.cs index bca90b39e..5d1b2278f 100644 --- a/Player/Undo/UndoFileBin.cs +++ b/Player/Undo/UndoFileBin.cs @@ -52,8 +52,31 @@ namespace MCGalaxy.Util { WriteChunkEntries(w, lastChunk.Entries, entriesPos); } } - -// TODO: untested + + protected override void ReadUndoData(List buffer, string path) { + DateTime now = DateTime.Now; + Player.UndoPos Pos; + using (Stream fs = File.OpenRead(path)) + using (BinaryReader r = new BinaryReader(fs)) + { + int approxEntries = (int)(fs.Length / 12); + if (buffer.Capacity < approxEntries) + buffer.Capacity = approxEntries; + while (fs.Position < fs.Length) { + ChunkHeader chunk = ReadHeader(fs, r); + Pos.mapName = chunk.LevelName; + + for (int j = 0; j < chunk.Entries; j++ ) { + Pos.timePlaced = chunk.BaseTime.AddSeconds(r.ReadUInt16()); + Pos.x = r.ReadUInt16(); Pos.y = r.ReadUInt16(); Pos.z = r.ReadUInt16(); + Pos.type = r.ReadByte(); r.ReadByte(); // block definitions placeholder + Pos.newtype = r.ReadByte(); r.ReadByte(); // block definitions placeholder + buffer.Add(Pos); + } + } + } + } + protected override bool UndoEntry(Player p, string path, long seconds) { List list = new List(); DateTime now = DateTime.Now; @@ -70,17 +93,18 @@ namespace MCGalaxy.Util { return false; if (lvl == null || lvl != p.level) continue; Pos.mapName = chunk.LevelName; + fs.Seek(chunk.DataPosition, SeekOrigin.Begin); for (int j = 0; j < chunk.Entries; j++ ) { DateTime time = chunk.BaseTime.AddSeconds(r.ReadUInt16()); - if (time >= now) return false; + if (time.AddSeconds(seconds) < now) return false; Pos.x = r.ReadUInt16(); Pos.y = r.ReadUInt16(); Pos.z = r.ReadUInt16(); Pos.type = lvl.GetTile(Pos.x, Pos.y, Pos.z); byte oldType = r.ReadByte(); r.ReadByte(); // block definitions placeholder byte newType = r.ReadByte(); r.ReadByte(); // block definitions placeholder - if (Pos.type == newType || Block.Convert(Pos.type) == Block.water + if (Pos.type == newType || Block.Convert(Pos.type) == Block.water || Block.Convert(Pos.type) == Block.lava || Pos.type == Block.grass) { Pos.newtype = oldType; @@ -95,7 +119,6 @@ namespace MCGalaxy.Util { return true; } -// TODO: untested protected override bool HighlightEntry(Player p, string path, long seconds) { List list = new List(); DateTime now = DateTime.Now; @@ -110,10 +133,11 @@ namespace MCGalaxy.Util { if (!CheckChunk(chunk, now, seconds, p, out lvl)) return false; if (lvl == null || lvl != p.level) continue; + fs.Seek(chunk.DataPosition, SeekOrigin.Begin); for (int j = 0; j < chunk.Entries; j++ ) { DateTime time = chunk.BaseTime.AddSeconds(r.ReadUInt16()); - if (time >= now) return false; + if (time.AddSeconds(seconds) < now) return false; ushort x = r.ReadUInt16(), y = r.ReadUInt16(), z = r.ReadUInt16(); byte lvlTile = lvl.GetTile(x, y, z); @@ -135,7 +159,7 @@ namespace MCGalaxy.Util { static bool CheckChunk(ChunkHeader chunk, DateTime now, long seconds, Player p, out Level lvl) { DateTime time = chunk.BaseTime; lvl = null; - if (time.AddSeconds(65536).AddSeconds(seconds) >= now) + if (time.AddSeconds(65536).AddSeconds(seconds) < now) return false; // we can safely discard the entire chunk lvl = Level.FindExact(chunk.LevelName); @@ -153,18 +177,23 @@ namespace MCGalaxy.Util { Stream s = r.BaseStream; long len = s.Length; while (s.Position < len) { - ChunkHeader header = default(ChunkHeader); - byte[] mapNameData = r.ReadBytes(r.ReadUInt16()); - header.LevelName = Encoding.UTF8.GetString(mapNameData); - - header.BaseTime = new DateTime(r.ReadInt64(), DateTimeKind.Local); - header.Entries = r.ReadUInt16(); - header.DataPosition = s.Position; + ChunkHeader header = ReadHeader(s, r); s.Seek(header.Entries * 12, SeekOrigin.Current); list.Add(header); } } + static ChunkHeader ReadHeader(Stream s, BinaryReader r) { + ChunkHeader header = default(ChunkHeader); + byte[] mapNameData = r.ReadBytes(r.ReadUInt16()); + header.LevelName = Encoding.UTF8.GetString(mapNameData); + + header.BaseTime = new DateTime(r.ReadInt64(), DateTimeKind.Local); + header.Entries = r.ReadUInt16(); + header.DataPosition = s.Position; + return header; + } + static void WriteChunkEntries(BinaryWriter w, ushort entries, long entriesPos) { long curPos = w.BaseStream.Position; w.BaseStream.Seek(entriesPos, SeekOrigin.Begin); diff --git a/Player/Undo/UndoFileText.cs b/Player/Undo/UndoFileText.cs index a104321a2..62c8ef1ed 100644 --- a/Player/Undo/UndoFileText.cs +++ b/Player/Undo/UndoFileText.cs @@ -38,6 +38,27 @@ namespace MCGalaxy.Util { } } + protected override void ReadUndoData(List buffer, string path) { + Player.UndoPos Pos; + string[] lines = File.ReadAllText(path).Split(' '); + int approxEntries = (int)(lines.Length / 7); + if (buffer.Capacity < approxEntries) + buffer.Capacity = approxEntries; + + for (int i = 0; i < lines.Length; i += 7) { + if (lines[i].Length == 0) continue; + Pos.mapName = lines[i]; + Pos.x = ushort.Parse(lines[i + 1]); + Pos.y = ushort.Parse(lines[i + 2]); + Pos.z = ushort.Parse(lines[i + 3]); + + Pos.timePlaced = DateTime.Parse(lines[i + 4], CultureInfo.InvariantCulture); + Pos.type = byte.Parse(lines[i + 5]); + Pos.newtype = byte.Parse(lines[i + 6]); + buffer.Add(Pos); + } + } + protected override bool UndoEntry(Player p, string path, long seconds) { Player.UndoPos Pos; string[] lines = File.ReadAllText(path).Split(' '); diff --git a/Starter.csproj b/Starter.csproj index 9400ca397..fb37cd206 100644 --- a/Starter.csproj +++ b/Starter.csproj @@ -1,74 +1,74 @@ - - - - Debug - x86 - 8.0.30703 - 2.0 - {63DCBB31-92CD-4464-A86C-A7E51A5FE9FE} - Exe - Properties - Starter - MCGalaxy - v4.0 - - - 512 - - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - false - - - Starter.Program - - - Galaxy.ico - - - - - - - - - - - - - - - {12597DB0-7C34-4DE1-88EA-9250FF3372EB} - MCGalaxy_ - - - - - - + + + + Debug + x86 + 8.0.30703 + 2.0 + {63DCBB31-92CD-4464-A86C-A7E51A5FE9FE} + Exe + Properties + Starter + MCGalaxy + v4.0 + + + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + false + + + Starter.Program + + + Galaxy.ico + + + + + + + + + + + + + + + {12597DB0-7C34-4DE1-88EA-9250FF3372EB} + MCGalaxy_ + + + + + + + --> \ No newline at end of file