diff --git a/Commands/building/CmdUndo.cs b/Commands/building/CmdUndo.cs index 4f29ec9ca..23cf70897 100644 --- a/Commands/building/CmdUndo.cs +++ b/Commands/building/CmdUndo.cs @@ -18,6 +18,8 @@ using System; using System.Globalization; using System.IO; +using MCGalaxy.Util; + namespace MCGalaxy.Commands { public sealed class CmdUndo : Command @@ -36,29 +38,21 @@ namespace MCGalaxy.Commands byte b; long seconds = -2; Player who = null; Player.UndoPos Pos; int CurrentPos = 0; bool undoPhysics = false; string whoName = String.Empty; if (p != null) p.RedoBuffer.Clear(); - - if (message == "") - { - if (p == null) - { - Player.SendMessage(null, "Console doesn't have an undo buffer."); - return; + if (message == "") { + if (p == null) { + Player.SendMessage(null, "Console doesn't have an undo buffer."); return; } message = p.name.ToLower() + " 30"; } + string[] parts = message.Split(' '); - try - { - if (message.Split(' ').Length > 1) - { - whoName = message.Split(' ')[0]; - who = message.Split(' ')[0].ToLower() == "physics" ? null : Player.Find(message.Split(' ')[0]); - undoPhysics = message.Split(' ')[0].ToLower() == "physics"; - message = message.Split(' ')[1].ToLower(); - - } - else - { + try { + if (parts.Length > 1) { + whoName = parts[0]; + who = parts[0].ToLower() == "physics" ? null : Player.Find(parts[0]); + undoPhysics = parts[0].ToLower() == "physics"; + message = parts[1].ToLower(); + } else { who = (p == null || message.ToLower() == "physics") ? null : p; undoPhysics = message.ToLower() == "physics"; } @@ -68,9 +62,12 @@ namespace MCGalaxy.Commands seconds = ((message.ToLower() != "all") ? long.Parse(message) : int.MaxValue); else seconds = getAllowed(p, message.ToLower()); - } - catch - { + } catch { + if (parts.Length > 1 && parts[1].ToLower() == "update") { + UndoFile.UpgradePlayerUndoFiles(whoName); + Player.SendMessage(p, "Updated undo files for " + whoName + " to the new binary format."); + return; + } Player.SendMessage(p, "Invalid seconds, or you're unable to use /xundo. Using 30 seconds."); //only run if seconds is an invalid number seconds = 30; } @@ -202,10 +199,9 @@ namespace MCGalaxy.Commands } bool FoundUser = false; - try { - undoOfflineHelper(p, whoName, seconds, ref FoundUser); + UndoFile.UndoPlayer(p, whoName.ToLower(), seconds, ref FoundUser); if (FoundUser) { @@ -241,85 +237,9 @@ namespace MCGalaxy.Commands } return secs; } - - //Fixed by QuantumHive - public bool undoOffline(string[] fileContent, long seconds, Player p) - { - - Player.UndoPos Pos; - - //-1 because the last element in the array is an empty string "" go check Player.SaveUndo() if you wanna know why - for (int i = (fileContent.Length - 1) / 7; i >= 0; i--) - { - try - { - string datetime = fileContent[(i * 7) - 3]; - datetime = datetime.Replace('&', ' '); - DateTime time = DateTime.Parse(datetime, CultureInfo.InvariantCulture); - time = time.AddSeconds(seconds); - if (time < DateTime.Now) - //if (Convert.ToDateTime(fileContent[(i * 7) - 3].Replace('&', ' ')).AddSeconds(seconds) < DateTime.Now) - return false; - - Level foundLevel = Level.FindExact(fileContent[(i * 7) - 7]); - if (foundLevel != null) - { - Pos.mapName = foundLevel.name; - Pos.x = Convert.ToUInt16(fileContent[(i * 7) - 6]); - Pos.y = Convert.ToUInt16(fileContent[(i * 7) - 5]); - Pos.z = Convert.ToUInt16(fileContent[(i * 7) - 4]); - - Pos.type = foundLevel.GetTile(Pos.x, Pos.y, Pos.z); - - if (Pos.type == Convert.ToByte(fileContent[(i * 7) - 1]) || - Block.Convert(Pos.type) == Block.water || Block.Convert(Pos.type) == Block.lava || - Pos.type == Block.grass) - { - Pos.newtype = Convert.ToByte(fileContent[(i * 7) - 2]); - Pos.timePlaced = DateTime.Now; - - foundLevel.Blockchange(Pos.x, Pos.y, Pos.z, Pos.newtype, true); - if (p != null) - p.RedoBuffer.Add(Pos); - } - } - } - catch (Exception e) { } - } - - return true; - } - private void undoOfflineHelper(Player p, string whoName, long seconds, ref bool FoundUser) - { - DirectoryInfo di; - string[] fileContent; - - if (p != null) - p.RedoBuffer.Clear(); - - if (Directory.Exists("extra/undo/" + whoName.ToLower())) - { - di = new DirectoryInfo("extra/undo/" + whoName.ToLower()); - - for (int i = di.GetFiles("*.undo").Length - 1; i >= 0; i--) - { - fileContent = File.ReadAllText("extra/undo/" + whoName.ToLower() + "/" + i + ".undo").Split(); - if (!undoOffline(fileContent, seconds, p)) break; - } - FoundUser = true; - } - - if (Directory.Exists("extra/undoPrevious/" + whoName.ToLower())) - { - di = new DirectoryInfo("extra/undoPrevious/" + whoName.ToLower()); - - for (int i = di.GetFiles("*.undo").Length - 1; i >= 0; i--) - { - fileContent = File.ReadAllText("extra/undoPrevious/" + whoName.ToLower() + "/" + i + ".undo").Split(); - if (!undoOffline(fileContent, seconds, p)) break; - } - FoundUser = true; - } + + private void undoOfflineHelper(Player p, string whoName, long seconds, ref bool FoundUser) { + UndoFile.UndoPlayer(p, whoName.ToLower(), seconds, ref FoundUser); } public override void Help(Player p) diff --git a/MCGalaxy_.csproj b/MCGalaxy_.csproj index 115328a66..127d45fe4 100644 --- a/MCGalaxy_.csproj +++ b/MCGalaxy_.csproj @@ -443,8 +443,11 @@ - + + + + @@ -727,6 +730,7 @@ + diff --git a/Player/Player.cs b/Player/Player.cs index a75bea55e..935409342 100644 --- a/Player/Player.cs +++ b/Player/Player.cs @@ -26,6 +26,7 @@ using System.Text.RegularExpressions; using System.Threading; using MCGalaxy.Drawing; using MCGalaxy.SQL; +using MCGalaxy.Util; namespace MCGalaxy { public sealed partial class Player : IDisposable { @@ -2752,35 +2753,14 @@ level.Unload(); } } - public void SaveUndo() { - SaveUndo(this); - } + public void SaveUndo() { SaveUndo(this); } + public static void SaveUndo(Player p) { - if ( p == null || p.UndoBuffer == null || p.UndoBuffer.Count < 1 ) return; try { - if ( !Directory.Exists("extra/undo") ) Directory.CreateDirectory("extra/undo"); - if ( !Directory.Exists("extra/undoPrevious") ) Directory.CreateDirectory("extra/undoPrevious"); - DirectoryInfo di = new DirectoryInfo("extra/undo"); - if ( di.GetDirectories("*").Length >= Server.totalUndo ) { - Directory.Delete("extra/undoPrevious", true); - Directory.Move("extra/undo", "extra/undoPrevious"); - Directory.CreateDirectory("extra/undo"); - } - - if ( !Directory.Exists("extra/undo/" + p.name.ToLower()) ) Directory.CreateDirectory("extra/undo/" + p.name.ToLower()); - di = new DirectoryInfo("extra/undo/" + p.name.ToLower()); - int number = di.GetFiles("*.undo").Length; - File.Create("extra/undo/" + p.name.ToLower() + "/" + number + ".undo").Dispose(); - using ( StreamWriter w = File.CreateText("extra/undo/" + p.name.ToLower() + "/" + number + ".undo") ) { - foreach ( UndoPos uP in p.UndoBuffer.ToList() ) { - w.Write(uP.mapName + " " + - uP.x + " " + uP.y + " " + uP.z + " " + - uP.timePlaced.ToString(CultureInfo.InvariantCulture).Replace(' ', '&') + " " + - uP.type + " " + uP.newtype + " "); - } - } + UndoFile.SaveUndo(p); + } catch (Exception e) { + Server.s.Log("Error saving undo data for " + p.name + "!"); Server.ErrorLog(e); } - catch ( Exception e ) { Server.s.Log("Error saving undo data for " + p.name + "!"); Server.ErrorLog(e); } } public void Dispose() { diff --git a/Player/Undo/UndoFile.cs b/Player/Undo/UndoFile.cs new file mode 100644 index 000000000..ea56c1324 --- /dev/null +++ b/Player/Undo/UndoFile.cs @@ -0,0 +1,142 @@ +/* + 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; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace MCGalaxy.Util { + + public abstract class UndoFile { + + protected const string undoDir = "extra/undo", prevUndoDir = "extra/undoPrevious"; + 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); + + protected abstract string Extension { get; } + + public static void SaveUndo(Player p) { + if( p == null || p.UndoBuffer == null || p.UndoBuffer.Count < 1) return; + + CreateDefaultDirectories(); + if (Directory.GetDirectories(undoDir).Length >= Server.totalUndo) { + Directory.Delete(prevUndoDir, true); + Directory.Move(undoDir, prevUndoDir); + Directory.CreateDirectory(undoDir); + } + + string playerDir = Path.Combine(undoDir, p.name.ToLower()); + if (!Directory.Exists(playerDir)) + Directory.CreateDirectory(playerDir); + + 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) { + if (p != null) + p.RedoBuffer.Clear(); + FilterEntries(p, undoDir, targetName, seconds, false, ref FoundUser); + FilterEntries(p, prevUndoDir, targetName, seconds, 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); + } + + static void FilterEntries(Player p, string dir, string name, long seconds, bool highlight, ref bool FoundUser) { + string path = Path.Combine(dir, name); + if (!Directory.Exists(path)) + return; + string[] files = Directory.GetFiles(path); + Array.Sort(files, CompareFiles); + + 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 (!format.HighlightEntry(p, path, seconds)) break; + } else { + 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 new file mode 100644 index 000000000..5d1b2278f --- /dev/null +++ b/Player/Undo/UndoFileBin.cs @@ -0,0 +1,218 @@ +/* + 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; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace MCGalaxy.Util { + + public sealed class UndoFileBin : UndoFile { + + protected override string Extension { get { return ".unbin"; } } + + protected override void SaveUndoData(List buffer, string path) { + using (FileStream fs = File.Create(path)) { + BinaryWriter w = new BinaryWriter(fs); + long entriesPos = 0; + ChunkHeader lastChunk = default(ChunkHeader); + + foreach (Player.UndoPos uP in buffer) { + int timeDiff = (int)(uP.timePlaced.ToUniversalTime() - lastChunk.BaseTime).TotalSeconds; + if (lastChunk.LevelName != uP.mapName || timeDiff > 65535 || lastChunk.Entries == ushort.MaxValue) { + WriteChunkEntries(w, lastChunk.Entries, entriesPos); + lastChunk = WriteEmptyChunk(w, uP, ref entriesPos); + } + + w.Write((ushort)timeDiff); + w.Write(uP.x); w.Write(uP.y); w.Write(uP.z); + w.Write(uP.type); + w.Write((byte)0); // block definitions placeholder + w.Write(uP.newtype); + w.Write((byte)0); // block definitions placeholder + lastChunk.Entries++; + } + if (lastChunk.Entries > 0) + WriteChunkEntries(w, lastChunk.Entries, entriesPos); + } + } + + 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; + Player.UndoPos Pos; + + using (Stream fs = File.OpenRead(path)) + using (BinaryReader r = new BinaryReader(fs)) + { + ReadHeaders(list, r); + for (int i = list.Count - 1; i >= 0; i--) { + ChunkHeader chunk = list[i]; + Level lvl; + if (!CheckChunk(chunk, now, seconds, p, out lvl)) + 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.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 + || Block.Convert(Pos.type) == Block.lava || Pos.type == Block.grass) { + + Pos.newtype = oldType; + Pos.timePlaced = now; + lvl.Blockchange(Pos.x, Pos.y, Pos.z, Pos.newtype, true); + if (p != null) + p.RedoBuffer.Add(Pos); + } + } + } + } + return true; + } + + protected override bool HighlightEntry(Player p, string path, long seconds) { + List list = new List(); + DateTime now = DateTime.Now; + + using (Stream fs = File.OpenRead(path)) + using (BinaryReader r = new BinaryReader(fs)) + { + ReadHeaders(list, r); + for (int i = list.Count - 1; i >= 0; i--) { + ChunkHeader chunk = list[i]; + Level lvl; + 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.AddSeconds(seconds) < now) return false; + ushort x = r.ReadUInt16(), y = r.ReadUInt16(), z = r.ReadUInt16(); + + byte lvlTile = lvl.GetTile(x, y, z); + byte oldType = r.ReadByte(); r.ReadByte(); // block definitions placeholder + byte newType = r.ReadByte(); r.ReadByte(); // block definitions placeholder + + if (lvlTile == newType || Block.Convert(lvlTile) == Block.water || Block.Convert(lvlTile) == Block.lava) { + + byte block = (lvlTile == Block.air || Block.Convert(lvlTile) == Block.water + || Block.Convert(lvlTile) == Block.lava) ? Block.red : Block.green; + p.SendBlockchange(x, y, z, block); + } + } + } + } + return true; + } + + 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) + return false; // we can safely discard the entire chunk + + lvl = Level.FindExact(chunk.LevelName); + return true; + } + + struct ChunkHeader { + public string LevelName; + public DateTime BaseTime; + public ushort Entries; + public long DataPosition; + } + + static void ReadHeaders(List list, BinaryReader r) { + Stream s = r.BaseStream; + long len = s.Length; + while (s.Position < len) { + 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); + + w.Write(entries); + w.BaseStream.Seek(curPos, SeekOrigin.Begin); + } + + static ChunkHeader WriteEmptyChunk(BinaryWriter w, Player.UndoPos uP, ref long entriesPos) { + byte[] mapBytes = Encoding.UTF8.GetBytes(uP.mapName); + w.Write((ushort)mapBytes.Length); + w.Write(mapBytes); + w.Write(uP.timePlaced.ToLocalTime().Ticks); + + entriesPos = w.BaseStream.Position; + w.Write((ushort)0); + ChunkHeader header = default(ChunkHeader); + header.LevelName = uP.mapName; header.BaseTime = uP.timePlaced; + return header; + } + } +} diff --git a/Player/Undo/UndoFileText.cs b/Player/Undo/UndoFileText.cs new file mode 100644 index 000000000..ef931598b --- /dev/null +++ b/Player/Undo/UndoFileText.cs @@ -0,0 +1,134 @@ +/* + 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; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; + +namespace MCGalaxy.Util { + + public sealed class UndoFileText : UndoFile { + + protected override string Extension { get { return ".undo"; } } + + protected override void SaveUndoData(List buffer, string path) { + using (StreamWriter w = File.CreateText(path)) { + foreach (Player.UndoPos uP in buffer) { + w.Write( + uP.mapName + " " + uP.x + " " + uP.y + " " + uP.z + " " + + uP.timePlaced.ToString(CultureInfo.InvariantCulture).Replace(' ', '&') + " " + + uP.type + " " + uP.newtype + " "); + } + } + } + + 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]); + + string time = lines[i + 4].Replace('&', ' '); + Pos.timePlaced = DateTime.Parse(time, 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(' '); + // because we have space to end of each entry, need to subtract one otherwise we'll start at a "". + 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; + Level foundLevel = Level.FindExact(lines[(i * 7) - 7]); + if (foundLevel == null) continue; + + Pos.mapName = foundLevel.name; + Pos.x = Convert.ToUInt16(lines[(i * 7) - 6]); + Pos.y = Convert.ToUInt16(lines[(i * 7) - 5]); + Pos.z = Convert.ToUInt16(lines[(i * 7) - 4]); + Pos.type = foundLevel.GetTile(Pos.x, Pos.y, Pos.z); + + if (Pos.type == Convert.ToByte(lines[(i * 7) - 1]) || + Block.Convert(Pos.type) == Block.water || Block.Convert(Pos.type) == Block.lava || + Pos.type == Block.grass) { + + Pos.newtype = Convert.ToByte(lines[(i * 7) - 2]); + Pos.timePlaced = DateTime.Now; + + foundLevel.Blockchange(Pos.x, Pos.y, Pos.z, Pos.newtype, true); + if (p != null) + p.RedoBuffer.Add(Pos); + } + } catch { + } + } + return true; + } + + protected override bool HighlightEntry(Player p, string path, long seconds) { + Player.UndoPos Pos; + string[] lines = File.ReadAllText(path).Split(' '); + // because we have space to end of each entry, need to subtract one otherwise we'll start at a "". + 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; + Level foundLevel = Level.FindExact(lines[(i * 7) - 7]); + if (foundLevel == null || foundLevel != p.level) continue; + + Pos.mapName = foundLevel.name; + Pos.x = Convert.ToUInt16(lines[(i * 7) - 6]); + Pos.y = Convert.ToUInt16(lines[(i * 7) - 5]); + Pos.z = Convert.ToUInt16(lines[(i * 7) - 4]); + Pos.type = foundLevel.GetTile(Pos.x, Pos.y, Pos.z); + + if (Pos.type == Convert.ToByte(lines[(i * 7) - 1]) || + Block.Convert(Pos.type) == Block.water || Block.Convert(Pos.type) == Block.lava) { + + if (Pos.type == Block.air || Block.Convert(Pos.type) == Block.water || Block.Convert(Pos.type) == Block.lava) + p.SendBlockchange(Pos.x, Pos.y, Pos.z, Block.red); + else + p.SendBlockchange(Pos.x, Pos.y, Pos.z, Block.green); + } + } catch { } + } + return true; + } + + static bool InTime(string line, long seconds) { + line = line.Replace('&', ' '); + DateTime time = DateTime.Parse(line, CultureInfo.InvariantCulture) + .AddSeconds(seconds); + return time >= DateTime.Now; + } + } +} 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