protected override void SaveUndoData(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.Tail; 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.Prev; } } }
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 void SaveUndoData(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; } ushort width, height, length; LvlFile.LoadDimensions(LevelInfo.LevelPath(uP.mapName), out width, out height, out length); node.Width = width; node.Height = height; node.Length = length; 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> Appends an item to the cache. </summary> public void Add(Level lvl, Player.UndoPos item) { DateTime time = Server.StartTime.AddTicks(item.timeDelta * TimeSpan.TicksPerSecond); if (Head == null) { Head = UndoCacheNode.Make(lvl, time); Tail = Head; } 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++; }
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, 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); }