public static UndoCacheItem Make(UndoCacheNode node, short timeDelta, ref Player.UndoPos pos) { UndoCacheItem item = default(UndoCacheItem); item.Index = pos.x + node.Width * (pos.z + node.Length * pos.y); item.Flags = (ushort)(timeDelta & 0x3FFF); if (pos.type == Block.custom_block) { item.Type = pos.extType; 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); } else { item.NewType = pos.newtype; } return(item); }
protected override bool UndoEntry(Player p, string path, Vec3U16[] 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 = marks[0], max = marks[1]; bool undoArea = min.X != ushort.MaxValue; BufferedBlockSender buffer = new BufferedBlockSender(null); 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 internal static void UndoBlock(Player p, Level lvl, Player.UndoPos Pos, int timeDelta, BufferedBlockSender buffer) { byte lvlTile = lvl.GetTile(Pos.x, Pos.y, Pos.z); if (lvlTile == Pos.newtype || Block.Convert(lvlTile) == Block.water || Block.Convert(lvlTile) == Block.lava || lvlTile == Block.grass) { byte newExtType = Pos.newExtType; Pos.newtype = Pos.type; Pos.newExtType = Pos.extType; Pos.extType = newExtType; Pos.type = lvlTile; Pos.timeDelta = timeDelta; if (lvl.DoBlockchange(p, Pos.x, Pos.y, Pos.z, Pos.newtype, Pos.newExtType)) { buffer.Add(lvl.PosToInt(Pos.x, Pos.y, Pos.z), Pos.newtype, Pos.newExtType); buffer.CheckIfSend(false); } } }
/// <summary> Appends an item to the cache. </summary> public void Add(Level lvl, Player.UndoPos item) { lock (AddLock) { DateTime time = Server.StartTime.AddTicks(item.timeDelta * TimeSpan.TicksPerSecond); if (Tail == null) { Tail = UndoCacheNode.Make(lvl, time); Head = Tail; } if (lvl.name != Tail.MapName || lvl.Width != Tail.Width || lvl.Height != Tail.Height || lvl.Length != Tail.Length || Math.Abs((time - Tail.BaseTime).TotalSeconds) > TimeDeltaMax) { UndoCacheNode node = UndoCacheNode.Make(lvl, time); Tail.Next = node; node.Prev = Tail; Tail = node; } short timeDiff = (short)(time - Tail.BaseTime).TotalSeconds; Tail.Items.Add(UndoCacheItem.Make(Tail, timeDiff, ref item)); Count++; } }
void PerformUndo(Player p, ref Level saveLvl) { UndoCache cache = who.UndoBuffer; UndoCacheNode node = cache.Tail; if (node == null) { return; } Vec3U16 min = Min, max = Max; bool undoArea = min.X != ushort.MaxValue; Player.UndoPos Pos = default(Player.UndoPos); int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds; while (node != null) { Level lvl = LevelInfo.FindExact(node.MapName); if (lvl == null || (p.level != null && !p.level.name.CaselessEq(lvl.name))) { node = node.Prev; continue; } Pos.mapName = lvl.name; saveLvl = lvl; List <UndoCacheItem> items = node.Items; 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)); } for (int i = items.Count - 1; i >= 0; i--) { UndoCacheItem item = items[i]; node.Unpack(item.Index, out Pos.x, out Pos.y, out Pos.z); 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; } DateTime time = node.BaseTime.AddTicks(item.TimeDelta * TimeSpan.TicksPerSecond); if (time > End) { continue; } if (time < Start) { buffer.CheckIfSend(true); return; } item.GetNewBlock(out Pos.newtype, out Pos.newExtType); item.GetBlock(out Pos.type, out Pos.extType); UndoFile.UndoBlock(p, lvl, Pos, timeDelta, buffer); } buffer.CheckIfSend(true); node = node.Prev; } }
protected override bool UndoEntry(Player p, string path, Vec3U16[] 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 = marks[0], max = 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 || (p.level != null && !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); }
public static UndoCacheItem Make(UndoCacheNode node, short timeDelta, Player.UndoPos pos) { return(Make(node, timeDelta, ref pos)); }