コード例 #1
0
ファイル: CmdHighlight.cs プロジェクト: takaaptech/MCGalaxy
        static void HighlightPlayer(Player p, TimeSpan delta, string who, int[] ids, Vec3S32[] marks)
        {
            HighlightDrawOp op = new HighlightDrawOp();

            op.Start = DateTime.UtcNow.Subtract(delta);
            op.who   = who; op.ids = ids;
            op.Setup(p, p.level, marks);

            BufferedBlockSender buffer = new BufferedBlockSender(p);

            op.Perform(marks, null,
                       P => {
                int index = p.level.PosToInt(P.X, P.Y, P.Z);
                buffer.Add(index, P.Block);
            });
            buffer.Flush();

            if (op.found)
            {
                p.Message("Now highlighting past &b{0} &Sfor {1}",
                          delta.Shorten(true), p.FormatNick(who));
                p.Message("&WUse /reload to un-highlight");
            }
            else
            {
                p.Message("No changes found by {1} &Sin the past &b{0}",
                          delta.Shorten(true), p.FormatNick(who));
            }
        }
コード例 #2
0
        static void HighlightPlayer(Player p, TimeSpan delta, string who, int[] ids, Vec3S32[] marks)
        {
            HighlightDrawOp op = new HighlightDrawOp();

            op.Start = DateTime.UtcNow.Subtract(delta);
            op.who   = who; op.ids = ids;
            DrawOpPerformer.Setup(op, p, marks);

            BufferedBlockSender buffer = new BufferedBlockSender(p);

            op.Perform(marks, null,
                       P => {
                int index = p.level.PosToInt(P.X, P.Y, P.Z);
                buffer.Add(index, P.Block.BlockID, P.Block.ExtID);
            });
            buffer.Send(true);

            if (op.found)
            {
                Player.Message(p, "Now highlighting past &b{0} %Sfor {1}",
                               delta.Shorten(true), PlayerInfo.GetColoredName(p, who));
                Player.Message(p, "&cUse /reload to un-highlight");
            }
            else
            {
                Player.Message(p, "No changes found by {1} %Sin the past &b{0}",
                               delta.Shorten(true), PlayerInfo.GetColoredName(p, who));
            }
        }
コード例 #3
0
ファイル: DrawOp.Performer.cs プロジェクト: Benedani/MCGalaxy
        static void AppendDrawOp(Player p, DrawOp op, Brush brush, Vec3S32[] marks, long affected)
        {
            if (p == null)
            {
                BufferedBlockSender buffer = new BufferedBlockSender(op.Level);
                op.Perform(marks, brush, b => ConsoleOutputBlock(b, op.Level, buffer));
                buffer.Send(true);
                return;
            }

            PendingDrawOp item = new PendingDrawOp();

            item.Op    = op;
            item.Brush = brush;
            item.Marks = marks;

            lock (p.pendingDrawOpsLock) {
                p.PendingDrawOps.Add(item);
                // Another thread is already processing draw ops.
                if (p.PendingDrawOps.Count > 1)
                {
                    return;
                }
            }
            ProcessDrawOps(p);
        }
コード例 #4
0
ファイル: CmdFixGrass.cs プロジェクト: derekdinan/MCGalaxy
        static void FixLight(Player p, Level lvl, ref int totalFixed)
        {
            int index = 0, oneY = lvl.Width * lvl.Length;
            BufferedBlockSender buffer = new BufferedBlockSender(lvl);
            BlockID             above, block;

            for (ushort y = 0; y < lvl.Height - 1; y++)
            {
                for (ushort z = 0; z < lvl.Length; z++)
                {
                    for (ushort x = 0; x < lvl.Width; x++)
                    {
                        block = lvl.FastGetBlock(index);
                        bool inShadow = false;

                        if (lvl.Props[block].GrassBlock != Block.Invalid)
                        {
                            for (int i = 1; i < (lvl.Height - y); i++)
                            {
                                above = lvl.FastGetBlock(index + oneY * i);
                                if (!lvl.LightPasses(above))
                                {
                                    inShadow = true; break;
                                }
                            }

                            BlockID grass = lvl.Props[block].GrassBlock;
                            if (!inShadow && p.level.TryChangeBlock(p, x, y, z, grass) == ChangeResult.Modified)
                            {
                                buffer.Add(lvl.PosToInt(x, y, z), grass);
                                totalFixed++;
                            }
                        }
                        else if (lvl.Props[block].DirtBlock != Block.Invalid)
                        {
                            for (int i = 1; i < (lvl.Height - y); i++)
                            {
                                above = lvl.FastGetBlock(index + oneY * i);
                                if (!lvl.LightPasses(above))
                                {
                                    inShadow = true; break;
                                }
                            }

                            BlockID dirt = lvl.Props[block].DirtBlock;
                            if (inShadow && p.level.TryChangeBlock(p, x, y, z, dirt) == ChangeResult.Modified)
                            {
                                buffer.Add(lvl.PosToInt(x, y, z), dirt);
                                totalFixed++;
                            }
                        }
                        index++;
                    }
                }
            }
            buffer.Flush();
        }
コード例 #5
0
ファイル: CmdFixGrass.cs プロジェクト: ProtheanGod/KingMC
        static void Fix(Player p, Level lvl, ref int totalFixed, bool fixGrass, bool fixDirt)
        {
            int index = 0, maxY = lvl.Height - 1, oneY = lvl.Width * lvl.Length;
            BufferedBlockSender buffer = new BufferedBlockSender(lvl);
            ExtBlock            above = default(ExtBlock), block = default(ExtBlock);

            for (ushort y = 0; y < lvl.Height; y++)
            {
                for (ushort z = 0; z < lvl.Length; z++)
                {
                    for (ushort x = 0; x < lvl.Width; x++)
                    {
                        block.BlockID = lvl.blocks[index];
                        block.ExtID   = 0;
                        if (block.BlockID == Block.custom_block)
                        {
                            block.ExtID = lvl.GetExtTile(x, y, z);
                        }

                        if (fixGrass && lvl.Props[block.Index].GrassIndex != Block.Invalid)
                        {
                            above.BlockID = y == maxY ? Block.Air : lvl.blocks[index + oneY];
                            above.ExtID   = 0;
                            if (above.BlockID == Block.custom_block)
                            {
                                above.ExtID = lvl.GetExtTile(x, (ushort)(y + 1), z);
                            }

                            ExtBlock grass = ExtBlock.FromIndex(lvl.Props[block.Index].GrassIndex);
                            if (lvl.LightPasses(above) && p.level.DoBlockchange(p, x, y, z, grass) == 2)
                            {
                                buffer.Add(index, grass.BlockID, grass.ExtID);
                                totalFixed++;
                            }
                        }
                        else if (fixDirt && lvl.Props[block.Index].DirtIndex != Block.Invalid)
                        {
                            above.BlockID = y == maxY ? Block.Air : lvl.blocks[index + oneY];
                            above.ExtID   = 0;
                            if (above.BlockID == Block.custom_block)
                            {
                                above.ExtID = lvl.GetExtTile(x, (ushort)(y + 1), z);
                            }

                            ExtBlock dirt = ExtBlock.FromIndex(lvl.Props[block.Index].DirtIndex);
                            if (!lvl.LightPasses(above) && p.level.DoBlockchange(p, x, y, z, dirt) == 2)
                            {
                                buffer.Add(index, dirt.BlockID, dirt.ExtID);
                                totalFixed++;
                            }
                        }
                        index++;
                    }
                }
            }
            buffer.Send(true);
        }
コード例 #6
0
ファイル: UndoFileText.cs プロジェクト: Peteys93/MCGalaxy
        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);
        }
コード例 #7
0
ファイル: DrawOp.Performer.cs プロジェクト: Benedani/MCGalaxy
        static void ConsoleOutputBlock(DrawOpBlock b, Level lvl, BufferedBlockSender buffer)
        {
            int index = lvl.PosToInt(b.X, b.Y, b.Z);

            if (!lvl.DoPhysicsBlockchange(index, b.Block, false,
                                          default(PhysicsArgs), b.ExtBlock))
            {
                return;
            }
            buffer.Add(index, b.Block, b.ExtBlock);
        }
コード例 #8
0
        static void FixDirtAndGrass(Player p, Level lvl, ref int totalFixed)
        {
            int index = 0, maxY = lvl.Height - 1, oneY = lvl.Width * lvl.Length;
            BufferedBlockSender buffer = new BufferedBlockSender(lvl);

            for (int y = 0; y < lvl.Height; y++)
            {
                for (int z = 0; z < lvl.Length; z++)
                {
                    for (int x = 0; x < lvl.Width; x++)
                    {
                        byte block = lvl.blocks[index];
                        if (block == Block.dirt)
                        {
                            byte above = y == maxY ? Block.air : lvl.blocks[index + oneY], extAbove = 0;
                            if (above == Block.custom_block)
                            {
                                extAbove = lvl.GetExtTile((ushort)x, (ushort)(y + 1), (ushort)z);
                            }

                            if (Block.LightPass(above, extAbove, lvl.CustomBlockDefs))
                            {
                                if (p.level.DoBlockchange(p, (ushort)x, (ushort)y, (ushort)z, Block.grass))
                                {
                                    buffer.Add(index, Block.grass, 0);
                                    totalFixed++;
                                }
                            }
                        }
                        else if (block == Block.grass)
                        {
                            byte above = y == maxY ? Block.air : lvl.blocks[index + oneY], extAbove = 0;
                            if (above == Block.custom_block)
                            {
                                extAbove = lvl.GetExtTile((ushort)x, (ushort)(y + 1), (ushort)z);
                            }

                            if (!Block.LightPass(above, extAbove, lvl.CustomBlockDefs))
                            {
                                if (p.level.DoBlockchange(p, (ushort)x, (ushort)y, (ushort)z, Block.dirt))
                                {
                                    buffer.Add(index, Block.dirt, 0);
                                    totalFixed++;
                                }
                            }
                        }
                        index++;
                    }
                }
            }
            buffer.Send(true);
        }
コード例 #9
0
        public override void Perform(Vec3U16[] marks, Player p, Level lvl, Brush brush)
        {
            UndoCache     cache = p.UndoBuffer;
            UndoCacheNode node  = cache.Tail;

            if (node == null)
            {
                return;
            }
            int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds;

            while (node != null)
            {
                lvl = LevelInfo.FindExact(node.MapName);
                if (lvl == null || (p.level != null && !p.level.name.CaselessEq(lvl.name)))
                {
                    node = node.Prev; continue;
                }
                List <UndoCacheItem> items  = node.Items;
                BufferedBlockSender  buffer = new BufferedBlockSender(lvl);

                for (int i = items.Count - 1; i >= 0; i--)
                {
                    UndoCacheItem item = items[i];
                    ushort        x, y, z;
                    node.Unpack(item.Index, out x, out y, out z);

                    DateTime time = node.BaseTime.AddTicks(item.TimeDelta * TimeSpan.TicksPerSecond);
                    if (time > End)
                    {
                        continue;
                    }
                    if (time < Start)
                    {
                        buffer.CheckIfSend(true); return;
                    }

                    byte tile, extTile;
                    item.GetBlock(out tile, out extTile);
                    if (lvl.DoBlockchange(p, x, y, z, tile, extTile))
                    {
                        buffer.Add(lvl.PosToInt(x, y, z), tile, extTile);
                        buffer.CheckIfSend(false);
                    }
                }
                buffer.CheckIfSend(true);
                node = node.Prev;
            }
        }
コード例 #10
0
        void EndRound(Player winner)
        {
            RoundInProgress = false;
            Alive.Clear();

            UpdateAllStatus1();
            if (winner != null)
            {
                winner.SendMessage("Congratulations, you won this round of NOG!");
                // TODO: Money
            }

            BufferedBlockSender bulk = new BufferedBlockSender(Map);

            bulk.Flush();
        }
コード例 #11
0
        public static void DoHighlight(Stream s, UndoFormat format, UndoFormatArgs args)
        {
            BufferedBlockSender buffer = new BufferedBlockSender(args.Player);
            Level lvl = args.Player.level;

            foreach (UndoFormatEntry P in format.GetEntries(s, args))
            {
                byte block = P.Block, newBlock = P.NewBlock;
                byte highlight = (newBlock == Block.air ||
                                  Block.Convert(block) == Block.water || block == Block.waterstill ||
                                  Block.Convert(block) == Block.lava || block == Block.lavastill)
                    ? Block.red : Block.green;

                buffer.Add(lvl.PosToInt(P.X, P.Y, P.Z), highlight, 0);
            }
            buffer.Send(true);
        }
コード例 #12
0
ファイル: UndoFile.cs プロジェクト: Peteys93/MCGalaxy
        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);
                }
            }
        }
コード例 #13
0
ファイル: CmdFixGrass.cs プロジェクト: derekdinan/MCGalaxy
        static void Fix(Player p, Level lvl, ref int totalFixed, bool fixGrass, bool fixDirt)
        {
            int index = 0, maxY = lvl.Height - 1, oneY = lvl.Width * lvl.Length;
            BufferedBlockSender buffer = new BufferedBlockSender(lvl);
            BlockID             above, block;

            for (ushort y = 0; y < lvl.Height; y++)
            {
                for (ushort z = 0; z < lvl.Length; z++)
                {
                    for (ushort x = 0; x < lvl.Width; x++)
                    {
                        block = lvl.FastGetBlock(index);
                        if (fixGrass && lvl.Props[block].GrassBlock != Block.Invalid)
                        {
                            above = y == maxY ? Block.Air : lvl.FastGetBlock(index + oneY);
                            BlockID grass = lvl.Props[block].GrassBlock;

                            if (lvl.LightPasses(above) && p.level.TryChangeBlock(p, x, y, z, grass) == ChangeResult.Modified)
                            {
                                buffer.Add(index, grass);
                                totalFixed++;
                            }
                        }
                        else if (fixDirt && lvl.Props[block].DirtBlock != Block.Invalid)
                        {
                            above = y == maxY ? Block.Air : lvl.FastGetBlock(index + oneY);
                            BlockID dirt = lvl.Props[block].DirtBlock;

                            if (!lvl.LightPasses(above) && p.level.TryChangeBlock(p, x, y, z, dirt) == ChangeResult.Modified)
                            {
                                buffer.Add(index, dirt);
                                totalFixed++;
                            }
                        }
                        index++;
                    }
                }
            }
            buffer.Flush();
        }
コード例 #14
0
        static void FixLight(Player p, Level lvl, ref int totalFixed)
        {
            int index = 0;
            BufferedBlockSender buffer = new BufferedBlockSender(lvl);

            for (int y = 0; y < lvl.Height - 1; y++)
            {
                for (int z = 0; z < lvl.Length; z++)
                {
                    for (int x = 0; x < lvl.Width; x++)
                    {
                        byte block    = lvl.blocks[index];
                        bool inShadow = false;
                        if (block == Block.dirt)
                        {
                            for (int i = 1; i < (lvl.Height - y); i++)
                            {
                                byte above = lvl.blocks[index + (lvl.Width * lvl.Length) * i], extAbove = 0;
                                if (above == Block.custom_block)
                                {
                                    extAbove = lvl.GetExtTile((ushort)x, (ushort)(y + i), (ushort)z);
                                }

                                if (!Block.LightPass(above, extAbove, lvl.CustomBlockDefs))
                                {
                                    inShadow = true; break;
                                }
                            }

                            if (!inShadow && p.level.DoBlockchange(p, (ushort)x, (ushort)y, (ushort)z, Block.grass))
                            {
                                buffer.Add(index, Block.grass, 0);
                                totalFixed++;
                            }
                        }
                        else if (block == Block.grass)
                        {
                            for (int i = 1; i < (lvl.Height - y); i++)
                            {
                                byte above = lvl.blocks[index + (lvl.Width * lvl.Length) * i], extAbove = 0;
                                if (above == Block.custom_block)
                                {
                                    extAbove = lvl.GetExtTile((ushort)x, (ushort)(y + i), (ushort)z);
                                }

                                if (!Block.LightPass(above, extAbove, lvl.CustomBlockDefs))
                                {
                                    inShadow = true; break;
                                }
                            }

                            if (inShadow && p.level.DoBlockchange(p, (ushort)x, (ushort)y, (ushort)z, Block.dirt))
                            {
                                buffer.Add(index, Block.dirt, 0);
                                totalFixed++;
                            }
                        }
                        index++;
                    }
                }
            }
            buffer.Send(true);
        }
コード例 #15
0
ファイル: CmdFixGrass.cs プロジェクト: ProtheanGod/KingMC
        static void FixLight(Player p, Level lvl, ref int totalFixed)
        {
            int index = 0;
            BufferedBlockSender buffer = new BufferedBlockSender(lvl);
            ExtBlock            above = default(ExtBlock), block = default(ExtBlock);

            for (ushort y = 0; y < lvl.Height - 1; y++)
            {
                for (ushort z = 0; z < lvl.Length; z++)
                {
                    for (ushort x = 0; x < lvl.Width; x++)
                    {
                        block.BlockID = lvl.blocks[index];
                        block.ExtID   = 0;
                        bool inShadow = false;
                        if (block.BlockID == Block.custom_block)
                        {
                            block.ExtID = lvl.GetExtTile(x, y, z);
                        }

                        if (lvl.Props[block.Index].GrassIndex != Block.Invalid)
                        {
                            for (int i = 1; i < (lvl.Height - y); i++)
                            {
                                above.BlockID = lvl.blocks[index + (lvl.Width * lvl.Length) * i];
                                above.ExtID   = 0;
                                if (above.BlockID == Block.custom_block)
                                {
                                    above.ExtID = lvl.GetExtTile(x, (ushort)(y + i), z);
                                }

                                if (!lvl.LightPasses(above))
                                {
                                    inShadow = true; break;
                                }
                            }

                            ExtBlock grass = ExtBlock.FromIndex(lvl.Props[block.Index].GrassIndex);
                            if (!inShadow && p.level.DoBlockchange(p, x, (ushort)y, z, grass) == 2)
                            {
                                buffer.Add(index, grass.BlockID, grass.ExtID);
                                totalFixed++;
                            }
                        }
                        else if (lvl.Props[block.Index].DirtIndex != Block.Invalid)
                        {
                            for (int i = 1; i < (lvl.Height - y); i++)
                            {
                                above.BlockID = lvl.blocks[index + (lvl.Width * lvl.Length) * i];
                                above.ExtID   = 0;
                                if (above.BlockID == Block.custom_block)
                                {
                                    above.ExtID = lvl.GetExtTile(x, (ushort)(y + i), z);
                                }

                                if (!lvl.LightPasses(above))
                                {
                                    inShadow = true; break;
                                }
                            }

                            ExtBlock dirt = ExtBlock.FromIndex(lvl.Props[block.Index].DirtIndex);
                            if (inShadow && p.level.DoBlockchange(p, x, (ushort)y, z, dirt) == 2)
                            {
                                buffer.Add(index, dirt.BlockID, dirt.ExtID);
                                totalFixed++;
                            }
                        }
                        index++;
                    }
                }
            }
            buffer.Send(true);
        }
コード例 #16
0
        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;
            }
        }
コード例 #17
0
        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);
        }