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); }
protected override IEnumerable <UndoFormatEntry> GetEntries(Stream s, UndoFormatArgs args) { List <ChunkHeader> list = new List <ChunkHeader>(); UndoFormatEntry pos; UndoCacheItem item = default(UndoCacheItem); bool super = Player.IsSuper(args.Player); DateTime start = args.Start; ReadHeaders(list, s); for (int i = list.Count - 1; i >= 0; i--) { ChunkHeader chunk = list[i]; // Can we safely discard the entire chunk? bool inRange = chunk.BaseTime.AddTicks((65536 >> 2) * TimeSpan.TicksPerSecond) >= start; if (!inRange) { args.Stop = true; yield break; } if (!super && !args.Player.level.name.CaselessEq(chunk.LevelName)) { continue; } pos.LevelName = 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); pos.Time = chunk.BaseTime.AddTicks((item.Flags & 0x3FFF) * TimeSpan.TicksPerSecond); if (pos.Time < start) { args.Stop = true; yield break; } 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); item.Type = temp[offset + 6]; item.NewType = temp[offset + 7]; item.GetBlock(out pos.Block, out pos.ExtBlock); item.GetNewBlock(out pos.NewBlock, out pos.NewExtBlock); yield return(pos); } } }
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); } } }
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++; } }