[Major WIP] Rewrite UndoFile classes to be much more extensible.

This commit is contained in:
UnknownShadow200 2016-08-19 23:55:26 +10:00
parent 6b7b3cd191
commit 075e99f118
4 changed files with 112 additions and 349 deletions

View File

@ -29,17 +29,18 @@ namespace MCGalaxy.Util {
public static UndoFile BinFormat = new UndoFileBin(); public static UndoFile BinFormat = new UndoFileBin();
public static UndoFile NewFormat = new UndoFileCBin(); public static UndoFile NewFormat = new UndoFileCBin();
protected class UndoEntriesArgs {
public Player Player;
public byte[] Temp;
public bool Stop;
public DateTime StartRange;
}
protected abstract void SaveUndoData(List<Player.UndoPos> buffer, string path); protected abstract void SaveUndoData(List<Player.UndoPos> buffer, string path);
protected abstract void SaveUndoData(UndoCache buffer, string path); protected abstract void SaveUndoData(UndoCache buffer, string path);
protected abstract void ReadUndoData(List<Player.UndoPos> buffer, string path);
protected abstract bool UndoEntry(Player p, string path, Vec3S32[] marks, protected abstract IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args);
ref byte[] temp, DateTime start);
protected abstract bool HighlightEntry(Player p, string path,
ref byte[] temp, DateTime start);
protected abstract string Extension { get; } protected abstract string Extension { get; }
@ -66,8 +67,8 @@ namespace MCGalaxy.Util {
} }
using (IDisposable locker = cache.ClearLock.AccquireWriteLock()) { using (IDisposable locker = cache.ClearLock.AccquireWriteLock()) {
lock (cache.AddLock) lock (cache.AddLock)
cache.Clear(); cache.Clear();
} }
} }
@ -138,17 +139,17 @@ namespace MCGalaxy.Util {
buffer.CheckIfSend(false); buffer.CheckIfSend(false);
} }
} else { } else {
bool diffBlock = Block.Convert(lvlTile) != Block.Convert(P.newtype); bool diffBlock = Block.Convert(lvlTile) != Block.Convert(P.newtype);
if (!diffBlock && lvlTile == Block.custom_block) if (!diffBlock && lvlTile == Block.custom_block)
diffBlock = lvl.GetExtTile(P.x, P.y, P.z) != P.newExtType; diffBlock = lvl.GetExtTile(P.x, P.y, P.z) != P.newExtType;
if (diffBlock) { if (diffBlock) {
buffer.Add(lvl.PosToInt(P.x, P.y, P.z), P.newtype, P.newExtType); buffer.Add(lvl.PosToInt(P.x, P.y, P.z), P.newtype, P.newExtType);
buffer.CheckIfSend(false); buffer.CheckIfSend(false);
} }
lvl.SetTile(P.x, P.y, P.z, P.newtype); lvl.SetTile(P.x, P.y, P.z, P.newtype);
if (P.newtype == Block.custom_block) if (P.newtype == Block.custom_block)
lvl.SetExtTile(P.x, P.y, P.z, P.newExtType); lvl.SetExtTile(P.x, P.y, P.z, P.newExtType);
} }
} }
} }
@ -156,9 +157,9 @@ namespace MCGalaxy.Util {
protected static void HighlightBlock(Player p, Level lvl, byte type, byte newType, protected static void HighlightBlock(Player p, Level lvl, byte type, byte newType,
ushort x, ushort y, ushort z) { ushort x, ushort y, ushort z) {
byte block = (newType == Block.air byte block = (newType == Block.air
|| Block.Convert(type) == Block.water || type == Block.waterstill || Block.Convert(type) == Block.water || type == Block.waterstill
|| Block.Convert(type) == Block.lava || type == Block.lavastill) || Block.Convert(type) == Block.lava || type == Block.lavastill)
? Block.red : Block.green; ? Block.red : Block.green;
p.SendBlockchange(x, y, z, block); p.SendBlockchange(x, y, z, block);
} }

View File

@ -19,7 +19,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using MCGalaxy.Drawing;
namespace MCGalaxy.Util { namespace MCGalaxy.Util {
@ -36,123 +35,46 @@ namespace MCGalaxy.Util {
throw new NotSupportedException("Non-optimised binary undo files have been deprecated"); throw new NotSupportedException("Non-optimised binary undo files have been deprecated");
} }
protected override void ReadUndoData(List<Player.UndoPos> buffer, string path) { protected override IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args) {
Player.UndoPos Pos;
using (Stream fs = File.OpenRead(path))
using (BinaryReader r = new BinaryReader(fs))
{
int approxEntries = (int)(fs.Length / entrySize);
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++ ) {
DateTime rawTime = chunk.BaseTime.AddSeconds(r.ReadUInt16());
Pos.timeDelta = (int)rawTime.Subtract(Server.StartTime).TotalSeconds;
Pos.x = r.ReadUInt16(); Pos.y = r.ReadUInt16(); Pos.z = r.ReadUInt16();
Pos.type = r.ReadByte(); Pos.extType = r.ReadByte();
Pos.newtype = r.ReadByte(); Pos.newExtType = r.ReadByte();
buffer.Add(Pos);
}
}
}
}
protected override bool UndoEntry(Player p, string path, Vec3S32[] marks,
ref byte[] temp, DateTime start) {
List<ChunkHeader> list = new List<ChunkHeader>(); List<ChunkHeader> list = new List<ChunkHeader>();
int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds; Player.UndoPos pos = default(Player.UndoPos);
Player.UndoPos Pos = default(Player.UndoPos); bool super = args.Player == null || args.Player.ircNick != null;
Vec3U16 min = (Vec3U16)marks[0], max = (Vec3U16)marks[1]; DateTime start = args.StartRange;
bool undoArea = min.X != ushort.MaxValue;
using (Stream fs = File.OpenRead(path)) ReadHeaders(list, s);
using (BinaryReader r = new BinaryReader(fs)) for (int i = list.Count - 1; i >= 0; i--) {
{ ChunkHeader chunk = list[i];
ReadHeaders(list, r); // Can we safely discard the entire chunk?
for (int i = list.Count - 1; i >= 0; i--) { bool inRange = chunk.BaseTime.AddTicks(65536 * TimeSpan.TicksPerSecond) >= start;
ChunkHeader chunk = list[i]; if (!inRange) { args.Stop = true; yield break; }
Level lvl; if (!super && !args.Player.level.name.CaselessEq(chunk.LevelName)) continue;
if (!CheckChunk(chunk, start, p, out lvl)) return false;
if (lvl == null) continue; s.Seek(chunk.DataPosition, SeekOrigin.Begin);
bool super = p == null || p.ircNick != null; if (args.Temp == null)
if (!super && !p.level.name.CaselessEq(lvl.name)) continue; args.Temp = new byte[ushort.MaxValue * entrySize];
s.Read(args.Temp, 0, chunk.Entries * entrySize);
byte[] temp = args.Temp;
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 < start) { args.Stop = true; yield break; }
BufferedBlockSender buffer = new BufferedBlockSender(lvl); pos.x = U16(temp, offset + 2);
if (!undoArea) { pos.y = U16(temp, offset + 4);
min = new Vec3U16(0, 0, 0); pos.z = U16(temp, offset + 6);
max = new Vec3U16((ushort)(lvl.Width - 1), (ushort)(lvl.Height - 1), (ushort)(lvl.Length - 1));
}
Pos.mapName = chunk.LevelName; pos.type = temp[offset + 8]; pos.extType = temp[offset + 9];
fs.Seek(chunk.DataPosition, SeekOrigin.Begin); pos.newtype = temp[offset + 10]; pos.newExtType = temp[offset + 11];
if (temp == null) temp = new byte[ushort.MaxValue * entrySize]; yield return pos;
fs.Read(temp, 0, chunk.Entries * entrySize);
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 < start) { buffer.CheckIfSend(true); return false; }
Pos.x = U16(temp, offset + 2); Pos.y = U16(temp, offset + 4); Pos.z = U16(temp, offset + 6);
if (Pos.x < min.X || Pos.y < min.Y || Pos.z < min.Z ||
Pos.x > max.X || Pos.y > max.Y || Pos.z > max.Z) continue;
Pos.type = temp[offset + 8]; Pos.extType = temp[offset + 9];
Pos.newtype = temp[offset + 10]; Pos.newExtType = temp[offset + 11];
UndoBlock(p, lvl, Pos, timeDelta, buffer);
}
buffer.CheckIfSend(true);
} }
} }
return true;
}
protected override bool HighlightEntry(Player p, string path,
ref byte[] temp, DateTime start) {
List<ChunkHeader> list = new List<ChunkHeader>();
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, start, p, out lvl))
return false;
if (lvl == null || lvl != p.level) continue;
fs.Seek(chunk.DataPosition, SeekOrigin.Begin);
if (temp == null) temp = new byte[ushort.MaxValue * entrySize];
fs.Read(temp, 0, chunk.Entries * entrySize);
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 < start) return false;
ushort x = U16(temp, offset + 2), y = U16(temp, offset + 4), z = U16(temp, offset + 6);
HighlightBlock(p, lvl, temp[offset + 8], temp[offset + 10], x, y, z);
}
}
}
return true;
} }
static ushort U16(byte[] buffer, int offset) { static ushort U16(byte[] buffer, int offset) {
return (ushort)(buffer[offset + 0] | buffer[offset + 1] << 8); return (ushort)(buffer[offset + 0] | buffer[offset + 1] << 8);
} }
static bool CheckChunk(ChunkHeader chunk, DateTime start, Player p, out Level lvl) {
DateTime time = chunk.BaseTime;
lvl = null;
if (time.AddTicks(65536 * TimeSpan.TicksPerSecond) < start)
return false; // we can safely discard the entire chunk
lvl = LevelInfo.FindExact(chunk.LevelName);
return true;
}
struct ChunkHeader { struct ChunkHeader {
public string LevelName; public string LevelName;
public DateTime BaseTime; public DateTime BaseTime;
@ -160,8 +82,8 @@ namespace MCGalaxy.Util {
public long DataPosition; public long DataPosition;
} }
static void ReadHeaders(List<ChunkHeader> list, BinaryReader r) { static void ReadHeaders(List<ChunkHeader> list, Stream s) {
Stream s = r.BaseStream; BinaryReader r = new BinaryReader(s);
long len = s.Length; long len = s.Length;
while (s.Position < len) { while (s.Position < len) {
ChunkHeader header = ReadHeader(s, r); ChunkHeader header = ReadHeader(s, r);

View File

@ -19,7 +19,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using MCGalaxy.Drawing;
using MCGalaxy.Levels.IO; using MCGalaxy.Levels.IO;
namespace MCGalaxy.Util { namespace MCGalaxy.Util {
@ -98,130 +97,47 @@ namespace MCGalaxy.Util {
} }
} }
} }
protected override void ReadUndoData(List<Player.UndoPos> buffer, string path) {
Player.UndoPos Pos;
UndoCacheItem item = default(UndoCacheItem);
using (Stream fs = File.OpenRead(path))
using (BinaryReader r = new BinaryReader(fs))
{
int approxEntries = (int)(fs.Length / entrySize);
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++ ) {
item.Flags = r.ReadUInt16();
DateTime time = chunk.BaseTime.AddTicks((item.Flags & 0x3FFF) * TimeSpan.TicksPerSecond);
Pos.timeDelta = (int)time.Subtract(Server.StartTime).TotalSeconds;
int index = r.ReadInt32();
Pos.x = (ushort)(index % chunk.Width);
Pos.y = (ushort)((index / chunk.Width) / chunk.Length);
Pos.z = (ushort)((index / chunk.Width) % chunk.Length);
item.Type = r.ReadByte();
item.NewType = r.ReadByte();
item.GetBlock(out Pos.type, out Pos.extType);
item.GetNewBlock(out Pos.newtype, out Pos.newExtType);
buffer.Add(Pos);
}
}
}
}
protected override bool UndoEntry(Player p, string path, Vec3S32[] marks,
ref byte[] temp, DateTime start) {
List<ChunkHeader> list = new List<ChunkHeader>();
int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds;
Player.UndoPos Pos = default(Player.UndoPos);
Vec3U16 min = (Vec3U16)marks[0], max = (Vec3U16)marks[1];
bool undoArea = min.X != ushort.MaxValue;
UndoCacheItem item = default(UndoCacheItem);
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, start, p, out lvl)) return false;
if (lvl == null) continue;
bool super = p == null || p.ircNick != null;
if (!super && !p.level.name.CaselessEq(lvl.name)) continue;
BufferedBlockSender buffer = new BufferedBlockSender(lvl);
if (!undoArea) {
min = new Vec3U16(0, 0, 0);
max = new Vec3U16((ushort)(lvl.Width - 1), (ushort)(lvl.Height - 1), (ushort)(lvl.Length - 1));
}
Pos.mapName = chunk.LevelName;
fs.Seek(chunk.DataPosition, SeekOrigin.Begin);
if (temp == null) temp = new byte[ushort.MaxValue * entrySize];
fs.Read(temp, 0, chunk.Entries * entrySize);
for (int j = chunk.Entries - 1; j >= 0; j-- ) {
int offset = j * entrySize;
item.Flags = U16(temp, offset + 0);
DateTime time = chunk.BaseTime.AddTicks((item.Flags & 0x3FFF) * TimeSpan.TicksPerSecond);
if (time < start) { buffer.CheckIfSend(true); return false; }
int index = I32(temp, offset + 2);
Pos.x = (ushort)(index % chunk.Width);
Pos.y = (ushort)((index / chunk.Width) / chunk.Length);
Pos.z = (ushort)((index / chunk.Width) % chunk.Length);
if (Pos.x < min.X || Pos.y < min.Y || Pos.z < min.Z ||
Pos.x > max.X || Pos.y > max.Y || Pos.z > max.Z) continue;
item.Type = temp[offset + 6];
item.NewType = temp[offset + 7];
item.GetBlock(out Pos.type, out Pos.extType);
item.GetNewBlock(out Pos.newtype, out Pos.newExtType);
UndoBlock(p, lvl, Pos, timeDelta, buffer);
}
buffer.CheckIfSend(true);
}
}
return true;
}
protected override bool HighlightEntry(Player p, string path, protected override IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args) {
ref byte[] temp, DateTime start) {
List<ChunkHeader> list = new List<ChunkHeader>(); List<ChunkHeader> list = new List<ChunkHeader>();
Player.UndoPos pos = default(Player.UndoPos);
using (Stream fs = File.OpenRead(path)) UndoCacheItem item = default(UndoCacheItem);
using (BinaryReader r = new BinaryReader(fs)) bool super = args.Player == null || args.Player.ircNick != null;
{ DateTime start = args.StartRange;
ReadHeaders(list, r);
for (int i = list.Count - 1; i >= 0; i--) { ReadHeaders(list, s);
ChunkHeader chunk = list[i]; for (int i = list.Count - 1; i >= 0; i--) {
Level lvl; ChunkHeader chunk = list[i];
if (!CheckChunk(chunk, start, p, out lvl)) // Can we safely discard the entire chunk?
return false; bool inRange = chunk.BaseTime.AddTicks((65536 >> 2) * TimeSpan.TicksPerSecond) >= start;
if (lvl == null || lvl != p.level) continue; if (!inRange) { args.Stop = true; yield break; }
if (!super && !args.Player.level.name.CaselessEq(chunk.LevelName)) continue;
pos.mapName = chunk.LevelName;
s.Seek(chunk.DataPosition, SeekOrigin.Begin);
if (args.Temp == null)
args.Temp = new byte[ushort.MaxValue * entrySize];
s.Read(args.Temp, 0, chunk.Entries * entrySize);
byte[] temp = args.Temp;
for (int j = chunk.Entries - 1; j >= 0; j-- ) {
int offset = j * entrySize;
item.Flags = U16(temp, offset + 0);
DateTime time = chunk.BaseTime.AddTicks((item.Flags & 0x3FFF) * TimeSpan.TicksPerSecond);
if (time < start) { args.Stop = true; yield break; }
fs.Seek(chunk.DataPosition, SeekOrigin.Begin); int index = I32(temp, offset + 2);
if (temp == null) temp = new byte[ushort.MaxValue * entrySize]; pos.x = (ushort)(index % chunk.Width);
fs.Read(temp, 0, chunk.Entries * entrySize); pos.y = (ushort)((index / chunk.Width) / chunk.Length);
pos.z = (ushort)((index / chunk.Width) % chunk.Length);
for (int j = chunk.Entries - 1; j >= 0; j-- ) { item.Type = temp[offset + 6];
int offset = j * entrySize; item.NewType = temp[offset + 7];
ushort flags = U16(temp, offset + 0); item.GetBlock(out pos.type, out pos.extType);
DateTime time = chunk.BaseTime.AddTicks((flags & 0x3FFF) * TimeSpan.TicksPerSecond); item.GetNewBlock(out pos.newtype, out pos.newExtType);
if (time < start) return false; yield return pos;
int index = I32(temp, offset + 2);
ushort x = (ushort)(index % chunk.Width);
ushort y = (ushort)((index / chunk.Width) / chunk.Length);
ushort z = (ushort)((index / chunk.Width) % chunk.Length);
HighlightBlock(p, lvl, temp[offset + 6], temp[offset + 7], x, y, z);
}
} }
} }
return true;
} }
static ushort U16(byte[] buffer, int offset) { static ushort U16(byte[] buffer, int offset) {
@ -229,30 +145,21 @@ namespace MCGalaxy.Util {
} }
static int I32(byte[] buffer, int offset) { static int I32(byte[] buffer, int offset) {
return buffer[offset + 0] | buffer[offset + 1] << 8 | return buffer[offset + 0] | buffer[offset + 1] << 8 |
buffer[offset + 2] << 16 | buffer[offset + 3] << 24; buffer[offset + 2] << 16 | buffer[offset + 3] << 24;
} }
static bool CheckChunk(ChunkHeader chunk, DateTime start, Player p, out Level lvl) {
DateTime time = chunk.BaseTime;
lvl = null;
if (time.AddTicks((65536 >> 2) * TimeSpan.TicksPerSecond) < start)
return false; // we can safely discard the entire chunk
lvl = LevelInfo.FindExact(chunk.LevelName);
return true;
}
struct ChunkHeader { struct ChunkHeader {
public string LevelName; public string LevelName;
public DateTime BaseTime; public DateTime BaseTime;
public ushort Entries; public ushort Entries;
public ushort Width, Height, Length; public ushort Width, Height, Length;
public long DataPosition; public long DataPosition;
} }
static void ReadHeaders(List<ChunkHeader> list, BinaryReader r) { static void ReadHeaders(List<ChunkHeader> list, Stream s) {
Stream s = r.BaseStream; BinaryReader r = new BinaryReader(s);
long len = s.Length; long len = s.Length;
while (s.Position < len) { while (s.Position < len) {
ChunkHeader header = ReadHeader(s, r); ChunkHeader header = ReadHeader(s, r);
s.Seek(header.Entries * entrySize, SeekOrigin.Current); s.Seek(header.Entries * entrySize, SeekOrigin.Current);
@ -263,12 +170,12 @@ namespace MCGalaxy.Util {
static ChunkHeader ReadHeader(Stream s, BinaryReader r) { static ChunkHeader ReadHeader(Stream s, BinaryReader r) {
ChunkHeader header = default(ChunkHeader); ChunkHeader header = default(ChunkHeader);
byte[] mapNameData = r.ReadBytes(r.ReadUInt16()); byte[] mapNameData = r.ReadBytes(r.ReadUInt16());
header.LevelName = Encoding.UTF8.GetString(mapNameData); header.LevelName = Encoding.UTF8.GetString(mapNameData);
header.BaseTime = new DateTime(r.ReadInt64(), DateTimeKind.Utc); header.BaseTime = new DateTime(r.ReadInt64(), DateTimeKind.Utc);
header.Entries = r.ReadUInt16(); header.Entries = r.ReadUInt16();
header.Width = r.ReadUInt16(); header.Width = r.ReadUInt16();
header.Height = r.ReadUInt16(); header.Height = r.ReadUInt16();
header.Length = r.ReadUInt16(); header.Length = r.ReadUInt16();
header.DataPosition = s.Position; header.DataPosition = s.Position;
return header; return header;

View File

@ -19,7 +19,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using MCGalaxy.Drawing;
namespace MCGalaxy.Util { namespace MCGalaxy.Util {
@ -35,98 +34,32 @@ namespace MCGalaxy.Util {
throw new NotSupportedException("Text undo files have been deprecated"); throw new NotSupportedException("Text undo files have been deprecated");
} }
protected override void ReadUndoData(List<Player.UndoPos> buffer, string path) { protected override IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args) {
Player.UndoPos Pos; Player.UndoPos pos = default(Player.UndoPos);
Pos.extType = 0; Pos.newExtType = 0; string[] lines = new StreamReader(s).ReadToEnd().Split(' ');
string[] lines = File.ReadAllText(path).Split(' '); Player p = args.Player;
int approxEntries = (int)(lines.Length / 7); bool super = p == null || p.ircNick != null;
if (buffer.Capacity < approxEntries) DateTime start = args.StartRange;
buffer.Capacity = approxEntries;
for (int i = 0; i < lines.Length; i += 7) { // because we have space to end of each entry, need to subtract one otherwise we'll start at a "".
if (lines[i].Length == 0) continue; for (int i = (lines.Length - 1) / 7; i >= 0; i--) {
Pos.mapName = lines[i]; // line format: mapName x y z date oldblock newblock
Pos.x = ushort.Parse(lines[i + 1]); string timeRaw = lines[(i * 7) - 3].Replace('&', ' ');
Pos.y = ushort.Parse(lines[i + 2]); DateTime time = DateTime.Parse(timeRaw, CultureInfo.InvariantCulture);
Pos.z = ushort.Parse(lines[i + 3]); if (time < start) { args.Stop = true; yield break; }
string time = lines[i + 4].Replace('&', ' '); string map = lines[(i * 7) - 7];
DateTime rawTime = DateTime.Parse(time, CultureInfo.InvariantCulture); if (!super && !p.level.name.CaselessEq(map)) continue;
Pos.timeDelta = (int)rawTime.Subtract(Server.StartTimeLocal).TotalSeconds; pos.mapName = map;
Pos.type = byte.Parse(lines[i + 5]);
Pos.newtype = byte.Parse(lines[i + 6]); pos.x = Convert.ToUInt16(lines[(i * 7) - 6]);
buffer.Add(Pos); pos.y = Convert.ToUInt16(lines[(i * 7) - 5]);
pos.z = Convert.ToUInt16(lines[(i * 7) - 4]);
pos.newtype = Convert.ToByte(lines[(i * 7) - 1]);
pos.type = Convert.ToByte(lines[(i * 7) - 2]);
yield return pos;
} }
} }
protected override bool UndoEntry(Player p, string path, Vec3S32[] marks,
ref byte[] temp, DateTime start) {
Player.UndoPos Pos = default(Player.UndoPos);
int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds;
string[] lines = File.ReadAllText(path).Split(' ');
Vec3U16 min = (Vec3U16)marks[0], max = (Vec3U16)marks[1];
bool undoArea = min.X != ushort.MaxValue;
BufferedBlockSender buffer = new BufferedBlockSender();
string last = null;
// 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], start)) return false;
Level lvl = LevelInfo.FindExact(lines[(i * 7) - 7]);
if (lvl == null || (p.level != null && !p.level.name.CaselessEq(lvl.name)))
continue;
if (!undoArea) {
min = new Vec3U16(0, 0, 0);
max = new Vec3U16((ushort)(lvl.Width - 1), (ushort)(lvl.Height - 1), (ushort)(lvl.Length - 1));
}
if (last == null || last != lvl.name) {
buffer.CheckIfSend(true);
last = lvl.name;
}
buffer.level = lvl;
Pos.mapName = lvl.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]);
if (Pos.x < min.X || Pos.y < min.Y || Pos.z < min.Z ||
Pos.x > max.X || Pos.y > max.Y || Pos.z > max.Z) continue;
Pos.newtype = Convert.ToByte(lines[(i * 7) - 1]);
Pos.type = Convert.ToByte(lines[(i * 7) - 2]);
UndoBlock(p, lvl, Pos, timeDelta, buffer);
} catch {
}
}
buffer.CheckIfSend(true);
return true;
}
protected override bool HighlightEntry(Player p, string path, ref byte[] temp, DateTime start) {
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], start)) return false;
Level lvl = LevelInfo.FindExact(lines[(i * 7) - 7]);
if (lvl == null || lvl != p.level) continue;
ushort x = Convert.ToUInt16(lines[(i * 7) - 6]);
ushort y = Convert.ToUInt16(lines[(i * 7) - 5]);
ushort z = Convert.ToUInt16(lines[(i * 7) - 4]);
HighlightBlock(p, lvl, Convert.ToByte(lines[(i * 7) - 2]), Convert.ToByte(lines[(i * 7) - 1]), x, y, z);
} catch { }
}
return true;
}
static bool InTime(string line, DateTime start) {
line = line.Replace('&', ' ');
DateTime time = DateTime.Parse(line, CultureInfo.InvariantCulture);
return time >= start;
}
} }
} }