internal bool Teleport(int tx, int ty, bool retrying)
        {
            if (Main.players[whoAmi] != this)
            {
                ProgramLog.Error.Log ("Attempt to teleport inactive player {0}.", Name ?? IPAddress);
                return false;
            }

            if (!retrying && System.Threading.Interlocked.CompareExchange (ref this.teleportInProgress, 1, 0) != 0)
            {
                ProgramLog.Error.Log ("Teleportation of player {0} already in progress.", Name ?? IPAddress);
                return false;
            }

            if (tx < 0 || ty < 0 || tx >= Main.maxTilesX || ty >= Main.maxTilesY)
            {
                ProgramLog.Error.Log ("Attempt to teleport player {0} to invalid location: {1}, {2}.", Name ?? IPAddress, tx, ty);
                return false;
            }

            PlayerTeleportEvent playerEvent = new PlayerTeleportEvent();
            playerEvent.ToLocation = new Vector2 (tx * 16, ty * 16);
            playerEvent.FromLocation = new Vector2(this.Position.X, this.Position.Y);
            playerEvent.Sender = this;
            Program.server.PluginManager.processHook(Hooks.PLAYER_TELEPORT, playerEvent);
            if (playerEvent.Cancelled)
            {
                return false;
            }

            bool changeSpawn = false;

            int ox = Main.spawnTileX;
            int oy = Main.spawnTileY;
            if (SpawnX >= 0 && SpawnY >= 0)
            {
                changeSpawn = true;
                ox = SpawnX;
                oy = SpawnY;
            }
            else if (OldSpawnX >= 0 && OldSpawnY >= 0)
            {
                changeSpawn = true;
                ox = OldSpawnX;
                oy = OldSpawnY;
            }

            var slot = Netplay.slots[whoAmi];
            int sx = tx / 200;
            int sy = ty / 150;

            // send up to 9 sections around the player
            int fromX = Math.Max (0, sx - 1);
            int fromY = Math.Max (0, sy - 1);
            int toX = Math.Min (sx + 1, Main.maxTilesX/200-1);
            int toY = Math.Min (sy + 1, Main.maxTilesY/150-1);

            int sections = 0;

            for (int x = fromX; x <= toX; x++)
            {
                for (int y = fromY; y <= toY; y++)
                {
                    if (! slot.tileSection[x, y])
                    {
                        sections += 1;
                    }
                }
            }

            var msg = NetMessage.PrepareThreadInstance ();

            if (sections > 0)
            {
                msg.SendTileLoading (sections * 150, "Teleporting...");
                msg.Send (whoAmi);
                msg.Clear ();

                for (int x = fromX; x <= toX; x++)
                {
                    for (int y = fromY; y <= toY; y++)
                    {
                        if (! slot.tileSection[x, y])
                        {
                            NetMessage.SendSection (whoAmi, x, y);
                        }
                    }
                }

                msg.SendTileConfirm (fromX, fromY, toX, toY);
                msg.Send (whoAmi);
                msg.Clear ();
            }

            // kill players' hooks and vines
            foreach (var proj in Main.projectile)
            {
                if (proj.Active && proj.Owner == whoAmi
                    && (proj.type == ProjectileType.HOOK || proj.type == ProjectileType.WHIP_IVY))
                {
                    proj.Active = false;
                    proj.type = ProjectileType.UNKNOWN;
                    msg.Projectile (proj);
                }
            }
            msg.Broadcast ();
            msg.Clear ();

            int left = 0;
            int right = -1;
            if (changeSpawn && oy > 1)
            {
                // invalidate player's bed temporarily
                // we used to kill a tile under the bed, but that could cause
                // side-effects, like killing objects hanging from the ceiling below

                left = Math.Max (0, ox - 4);
                right = Math.Min (ox + 4, Main.maxTilesX);

                while (left < Main.maxTilesX && Main.tile.At (left, oy - 1).Type != 79) left += 1;
                while (right > 0 && Main.tile.At (right, oy - 1).Type != 79) right -= 1;

                for (int x = left; x <= right; x++)
                {
                    var data = Main.tile.At (x, oy - 1).Data;
                    data.Active = false;
                    msg.SingleTileSquare (x, oy - 1, data);
                    data = Main.tile.At (x, oy - 2).Data;
                    data.Active = false;
                    msg.SingleTileSquare (x, oy - 2, data);
                }
            }

            // change the global spawn point
            msg.WorldData (tx, ty);

            // trigger respawn
            TeleSpawnX = tx;
            TeleSpawnY = ty;
            msg.ReceivingPlayerJoined (whoAmi);

            // fix holes at target location
            int fx = Math.Max (0, Math.Min (Main.maxTilesX - 8, tx - 4));
            int fy = Math.Max (0, Math.Min (Main.maxTilesY - 8, ty - 4));
            msg.TileSquare (7, fx, fy);

            //			msg.Send (whoAmi);
            //			msg.Clear ();

            if (changeSpawn && oy > 1)
            {
                // restore player's bed
                msg.TileSquare (1, ox, oy);

                if (right - left >= 0 && oy >= 2)
                    msg.TileSquare (right - left + 1, left, oy - 2);
            }

            // restore the global spawn point
            msg.WorldData ();

            msg.Send (whoAmi);

            return true;
        }
        public void teleportTo(float tileX, float tileY)
        {
            PlayerTeleportEvent playerEvent = new PlayerTeleportEvent();
            playerEvent.ToLocation = new Vector2(tileX, tileY);
            playerEvent.FromLocation = new Vector2(this.Position.X, this.Position.Y);
            playerEvent.Sender = this;
            Program.server.PluginManager.processHook(Hooks.PLAYER_TELEPORT, playerEvent);
            if (playerEvent.Cancelled)
            {
                return;
            }

            //Preserve out Spawn point.
            int xPreserve = Main.spawnTileX;
            int yPreserve = Main.spawnTileY;

            //The spawn the client wants is the from player Pos /16.
            //This is because the Client reads frames, Not Tile Records.
            Main.spawnTileX = ((int)tileX / 16);
            Main.spawnTileY = ((int)tileY / 16);

            NetMessage.SendData((int)Packet.WORLD_DATA, this.whoAmi); //Trigger Client Data Update (Updates Spawn Position)
            NetMessage.SendData((int)Packet.WORLD_DATA); //Trigger Client Data Update (Updates Spawn Position)
            NetMessage.SendData((int)Packet.RECEIVING_PLAYER_JOINED, -1, -1, "", this.whoAmi); //Trigger the player to spawn

            this.UpdatePlayer(this.whoAmi); //Update players data (I don't think needed by default, But hay)

            this.Position.X = tileX;
            this.Position.Y = tileY;

            //Return our preserved Spawn Point.
            Main.spawnTileX = xPreserve;
            Main.spawnTileY = yPreserve;

            this.SpawnX = Main.spawnTileX;
            this.SpawnY = Main.spawnTileY;

            this.Spawn(); //Tell the Client to Spawn (Sets Defaults)
            this.UpdatePlayer(this.whoAmi); //Update players data (I don't think needed by default, But hay)

            NetMessage.SendData((int)Packet.WORLD_DATA, this.whoAmi); //Trigger Client Data Update (Updates Spawn Position)

            NetMessage.SyncPlayers(); //Sync the Players Position.
        }
 /*
 /// <summary>
 /// Hook method for the player state (Byte) updating
 /// </summary>
 /// <param name="Event">PlayerStateUpdateEvent info</param>*/
 //public virtual void onPlayerStateUpdate(PlayerStateUpdateEvent Event) { } //Send with KB & Movement Events instead
 /// <summary>
 /// Hook method for the player teleport
 /// </summary>
 /// <param name="Event">PlayerTeleportEvent info</param>
 public virtual void onPlayerTeleport(PlayerTeleportEvent Event)
 {
 }