public override void Process(int whoAmI, byte[] readBuffer, int length, int num)
        {
            var slot = NetPlay.slots[whoAmI];

            byte tileAction = readBuffer[num++];
            int x = BitConverter.ToInt32(readBuffer, num);
            num += 4;
            int y = BitConverter.ToInt32(readBuffer, num);
            num += 4;

            byte tileType = readBuffer[num++];
            byte style = readBuffer[num];
            bool failFlag = (tileType == 1);

            if (x < 0 || y < 0 || x >= Main.maxTilesX || y >= Main.maxTilesY)
            {
                slot.Kick ("Out of range tile received from client.");
                return;
            }

            if (! NetPlay.slots[whoAmI].tileSection[NetPlay.GetSectionX(x), NetPlay.GetSectionY(y)])
            {
                ProgramLog.Debug.Log ("{0} @ {1}: {2} attempted to alter world in unloaded tile.");
                return;
            }

            var editor = staticEditor; //new SandboxEditor<PlayerSandbox> ();
            var sandbox = editor.Sandbox;

            lock (WorldModify.playerEditLock)
            {
                sandbox.Initialize ();
                var player = Main.players[whoAmI];

                switch (tileAction)
                {
                    case 0:
                        editor.KillTile(x, y, failFlag, false, false);
                        break;

                    case 1:

                        if (editor.PlaceTile (x, y, (int)tileType, false, false, whoAmI, style))
                        {
                            if (tileType == 15 && player.direction == 1)
                            {
                                sandbox.SetFrameXAt (x, y, (short)(sandbox.FrameXAt (x, y) + 18));
                                sandbox.SetFrameXAt (x, y - 1, (short)(sandbox.FrameXAt (x, y - 1) + 18));
                            }
                            else if (tileType == 106)
                            {
                                editor.SquareTileFrame (x, y, true);
                            }
                        }

                        break;

                    case 2:
                        editor.KillWall(x, y, failFlag);
                        break;

                    case 3:
                        editor.PlaceWall(x, y, (int)tileType, false);
                        break;

                    case 4:
                        editor.KillTile(x, y, failFlag, false, true);
                        break;
                }

            //				if (sandbox.ChangedTileCount == 0)
            //					return;

                var ctx = new HookContext
                {
                    Connection = NetPlay.slots[whoAmI].conn,
                    Sender = player,
                    Player = player,
                };

                var args = new HookArgs.PlayerWorldAlteration
                {
                    X = x, Y = y,
                    Action = tileAction,
                    Type = tileType,
                    Style = style,
                    Sandbox = sandbox,
                };

                HookPoints.PlayerWorldAlteration.Invoke (ref ctx, ref args);

                if (ctx.CheckForKick ())
                    return;

                if (ctx.Result == HookResult.IGNORE)
                    return;

                if (ctx.Result != HookResult.RECTIFY)
                {
                    if (! args.TypeChecked)
                    {
                        switch (tileAction)
                        {
                            case 1:
                            case 4:
                                if (tileType >= Main.MAX_TILE_SETS)
                                {
                                    slot.Kick ("Invalid tile type received from client.");
                                    return;
                                }
                                break;

                            case 3:
                                if (tileType >= Main.MAX_WALL_SETS)
                                {
                                    slot.Kick ("Invalid wall type received from client.");
                                    return;
                                }
                                break;
                        }
                    }

                    if (!failFlag)
                    {
                        if (tileAction == 0 || tileAction == 2 || tileAction == 4)
                        {
                            NetPlay.slots[whoAmI].spamDelBlock += 1f;
                        }
                        else if (tileAction == 1 || tileAction == 3)
                        {
                            NetPlay.slots[whoAmI].spamAddBlock += 1f;
                        }
                    }

                    NetMessage.SendData(17, -1, whoAmI, "", (int)tileAction, (float)x, (float)y, (float)tileType, style);

                    sandbox.Apply ();

                    return;
                }

                lock (player.rowsToRectify)
                {
                    foreach (var kv in sandbox.changedRows)
                    {
                        int y0 = kv.Key;
                        var x1 = kv.Value.Min;
                        var x2 = kv.Value.Max;
                        uint row;
                        if (player.rowsToRectify.TryGetValue ((ushort) y0, out row))
                        {
                            player.rowsToRectify[(ushort) y0] = (uint) (Math.Min (x1, row >> 16) << 16) | (uint) (Math.Max (x2, row & 0xffff));
                        }
                        else
                        {
                            player.rowsToRectify[(ushort) y0] = (uint) (x1 << 16) | (uint) x2;
                        }
                    }
                }
            }

            //            if (tileAction == 1 && tileType == 53)
            //            {
            //                NetMessage.SendTileSquare(-1, x, y, 1);
            //            }
        }
        public static bool InvokeAlterationHook(ISender sender, Player player, int x, int y, byte action, byte type = 0, byte style = 0)
        {
            var ctx = new HookContext
            {
                Sender = sender,
                Player = player,
            };

            var args = new HookArgs.PlayerWorldAlteration
            {
                X = x,
                Y = y,
                Action = action,
                Type = type,
                Style = style,
            };

            HookPoints.PlayerWorldAlteration.Invoke(ref ctx, ref args);

            if (ctx.CheckForKick())
                return false;

            if (ctx.Result == HookResult.IGNORE)
                return false;

            if (ctx.Result == HookResult.RECTIFY)
            {
                if (player.whoAmi >= 0)
                    NetMessage.SendTileSquare(player.whoAmi, x, y, 1); // FIXME
                return false;
            }

            return true;
        }
        public override void Process(int whoAmI, byte[] readBuffer, int length, int num)
        {
            var slot = NetPlay.slots[whoAmI];

            byte tileAction = readBuffer[num++];
            int x = BitConverter.ToInt32(readBuffer, num);
            num += 4;
            int y = BitConverter.ToInt32(readBuffer, num);
            num += 4;

            byte tileType = readBuffer[num++];
            byte style = readBuffer[num];
            bool failFlag = (tileType == 1);

            if (x < 0 || y < 0 || x >= Main.maxTilesX || y >= Main.maxTilesY)
            {
                slot.Kick ("Out of range tile received from client.");
                return;
            }

            var player = Main.players[whoAmI];

            var ctx = new HookContext
            {
                Connection = NetPlay.slots[whoAmI].conn,
                Sender = player,
                Player = player,
            };

            var args = new HookArgs.PlayerWorldAlteration
            {
                X = x, Y = y,
                Action = tileAction,
                Type = tileType,
                Style = style,
            };

            HookPoints.PlayerWorldAlteration.Invoke (ref ctx, ref args);

            if (ctx.CheckForKick ())
                return;

            if (ctx.Result == HookResult.IGNORE)
                return;

            if (ctx.Result == HookResult.RECTIFY)
            {
                NetMessage.SendTileSquare(whoAmI, x, y, 1); // FIXME
                return;
            }

            if (! args.TypeChecked)
            {
                switch (tileAction)
                {
                    case 1:
                    case 4:
                        if (tileType >= Main.MAX_TILE_SETS)
                        {
                            slot.Kick ("Invalid tile type received from client.");
                            return;
                        }
                        break;

                    case 3:
                        if (tileType >= Main.MAX_WALL_SETS)
                        {
                            slot.Kick ("Invalid wall type received from client.");
                            return;
                        }
                        break;
                }
            }

            if (!failFlag)
            {
                if (tileAction == 0 || tileAction == 2 || tileAction == 4)
                {
                    NetPlay.slots[whoAmI].spamDelBlock += 1f;
                }
                else if (tileAction == 1 || tileAction == 3)
                {
                    NetPlay.slots[whoAmI].spamAddBlock += 1f;
                }
            }

            if (!NetPlay.slots[whoAmI].tileSection[NetPlay.GetSectionX(x), NetPlay.GetSectionY(y)])
            {
                failFlag = true;
            }

            lock (WorldModify.playerEditLock)
            {
                switch (tileAction)
                {
                    case 0:
                        WorldModify.KillTile(x, y, failFlag, false, false);
                        break;

                    case 1:
                        if (WorldModify.PlaceTile(x, y, (int)tileType, false, true, whoAmI, style))
                        {
                            if (tileType == 15 && player.direction == 1)
                            {
                                Main.tile.At(x, y).AddFrameX (18);
                                Main.tile.At(x, y - 1).AddFrameX (18);
                            }
                            else if (tileType == 106)
                            {
                                WorldModify.SquareTileFrame (x, y, true);
                            }
                        }
                        break;

                    case 2:
                        WorldModify.KillWall(x, y, failFlag);
                        break;

                    case 3:
                        WorldModify.PlaceWall(x, y, (int)tileType, false);
                        break;

                    case 4:
                        WorldModify.KillTile(x, y, failFlag, false, true);
                        break;
                }
            }

            NetMessage.SendData(17, -1, whoAmI, "", (int)tileAction, (float)x, (float)y, (float)tileType, style);
            if (tileAction == 1 && tileType == 53)
            {
                NetMessage.SendTileSquare(-1, x, y, 1);
            }
        }