protected override void Save(UndoCache buffer, string path) { using (FileStream fs = File.Create(path)) { BinaryWriter w = new BinaryWriter(fs); long entriesPos = 0; ChunkHeader last = default(ChunkHeader); UndoCacheNode node = buffer.Head; while (node != null) { List <UndoCacheItem> items = node.Items; for (int i = 0; i < items.Count; i++) { UndoCacheItem uP = items[i]; DateTime time = node.BaseTime.AddSeconds(uP.TimeDelta); int timeDiff = (int)(time - last.BaseTime).TotalSeconds; if (last.LevelName != node.MapName || timeDiff > (65535 >> 2) || last.Entries == ushort.MaxValue) { WriteChunkEntries(w, last.Entries, entriesPos); last = WriteEmptyChunk(w, node, time, ref entriesPos); } int flags = (uP.Flags & 0xC000) | timeDiff; w.Write((ushort)flags); w.Write(uP.Index); w.Write(uP.Type); w.Write(uP.NewType); last.Entries++; } if (last.Entries > 0) { WriteChunkEntries(w, last.Entries, entriesPos); } node = node.Next; } } }
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); }
public static UndoCacheNode Make(Level lvl, DateTime time) { UndoCacheNode node = new UndoCacheNode(); node.MapName = lvl.name; node.Width = lvl.Width; node.Height = lvl.Height; node.Length = lvl.Length; node.BaseTime = time; return(node); }
static ChunkHeader WriteEmptyChunk(BinaryWriter w, UndoCacheNode node, DateTime time, ref long entriesPos) { ChunkHeader header = default(ChunkHeader); time = time.ToUniversalTime(); byte[] mapBytes = Encoding.UTF8.GetBytes(node.MapName); w.Write((ushort)mapBytes.Length); w.Write(mapBytes); header.LevelName = node.MapName; w.Write(time.Ticks); header.BaseTime = time; entriesPos = w.BaseStream.Position; w.Write((ushort)0); w.Write((ushort)node.Width); header.Width = (ushort)node.Width; w.Write((ushort)node.Height); header.Height = (ushort)node.Height; w.Write((ushort)node.Length); header.Length = (ushort)node.Length; return(header); }
protected override void Save(List <Player.UndoPos> buffer, string path) { UndoCacheNode node = new UndoCacheNode(); string lastLoggedName = ""; using (FileStream fs = File.Create(path)) { BinaryWriter w = new BinaryWriter(fs); long entriesPos = 0; ChunkHeader last = default(ChunkHeader); foreach (Player.UndoPos uP in buffer) { DateTime time = Server.StartTime.AddSeconds(uP.timeDelta); int timeDiff = (int)(time - last.BaseTime).TotalSeconds; if (last.LevelName != uP.mapName || timeDiff > (65535 >> 2) || last.Entries == ushort.MaxValue) { if (!LevelInfo.ExistsOffline(uP.mapName)) { if (uP.mapName != lastLoggedName) { lastLoggedName = uP.mapName; Server.s.Log("Missing map file \"" + lastLoggedName + "\", skipping undo entries"); } continue; } Vec3U16 dims = IMapImporter.Formats[0].ReadDimensions(LevelInfo.LevelPath(uP.mapName)); node.Width = dims.X; node.Height = dims.Y; node.Length = dims.Z; WriteChunkEntries(w, last.Entries, entriesPos); node.MapName = uP.mapName; last = WriteEmptyChunk(w, node, time, ref entriesPos); } UndoCacheItem item = UndoCacheItem.Make(node, 0, uP); int flags = (item.Flags & 0xC000) | timeDiff; w.Write((ushort)flags); w.Write(item.Index); w.Write(item.Type); w.Write(item.NewType); last.Entries++; } if (last.Entries > 0) { WriteChunkEntries(w, last.Entries, entriesPos); } } }
/// <summary> Removes all items from the cache and resets the state to default. </summary> public void Clear() { Count = 0; if (Tail == null) { return; } LastClear = DateTime.UtcNow; UndoCacheNode node = Tail; while (node != null) { node.Items = null; node = node.Prev; } Tail = null; Head = null; }
protected override IEnumerable <UndoFormatEntry> GetEntries(Stream s, UndoFormatArgs args) { UndoCacheNode node = cache.Tail; if (node == null) { yield break; } UndoFormatEntry pos; bool super = Player.IsSuper(args.Player); DateTime start = args.Start; while (node != null) { Level lvl = LevelInfo.FindExact(node.MapName); if (!super && !args.Player.level.name.CaselessEq(node.MapName)) { node = node.Prev; continue; } List <UndoCacheItem> items = node.Items; pos.LevelName = node.MapName; for (int i = items.Count - 1; i >= 0; i--) { UndoCacheItem item = items[i]; DateTime time = node.BaseTime.AddTicks(item.TimeDelta * TimeSpan.TicksPerSecond); if (time < start) { args.Stop = true; yield break; } pos.Time = time; node.Unpack(item.Index, out pos.X, out pos.Y, out pos.Z); item.GetBlock(out pos.Block, out pos.ExtBlock); item.GetNewBlock(out pos.NewBlock, out pos.NewExtBlock); yield return(pos); } node = node.Prev; } }
/// <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++; } }
public static UndoCacheItem Make(UndoCacheNode node, short timeDelta, Player.UndoPos pos) { return(Make(node, timeDelta, ref pos)); }