public override void Process(ClientConnection conn, byte[] readBuffer, int length, int num)
        {
            var data = Encoding.ASCII.GetString (readBuffer, num, length - 1);
            var lines = data.Split ('\n');

            foreach (var line in lines)
            {
                if (line == "tdcm1")
                {
                    //player.HasClientMod = true;
                    ProgramLog.Log ("{0} is a TDCM protocol version 1 client.", conn.RemoteAddress);
                }
                else if (line == "tdsmcomp1")
                {
                    conn.CompressionVersion = 1;
                    ProgramLog.Log ("{0} supports TDSM compression version 1.", conn.RemoteAddress);
                }
            }

            var ctx = new HookContext
            {
                Connection = conn,
            };

            var args = new HookArgs.DisconnectReceived
            {
                Content = data,
                Lines   = lines,
            };

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

            ctx.CheckForKick ();
        }
        public override void Process(ClientConnection conn, byte[] readBuffer, int length, int num)
        {
            if (conn.State == SlotState.ACCEPTED)
            {
                SlotManager.Schedule (conn, conn.DesiredQueue);
                return;
            }

            int whoAmI = conn.SlotIndex;

            if (NetPlay.slots[whoAmI].state == SlotState.ASSIGNING_SLOT)
            {
                NetPlay.slots[whoAmI].state = SlotState.SENDING_WORLD;
            }

            var ctx = new HookContext() { Connection = conn, Player = conn.Player };
            var args = new HookArgs.WorldRequestMessage()
            {
                SpawnX = Main.spawnTileX,
                SpawnY = Main.spawnTileY
            };
            HookPoints.WorldRequestMessage.Invoke(ref ctx, ref args);

            //NetMessage.SendData(7, whoAmI);
            var msg = NetMessage.PrepareThreadInstance();
            msg.WorldData(args.SpawnX, args.SpawnY);
            msg.Send(whoAmI);
        }
 public override void Process(ClientConnection conn, byte[] readBuffer, int length, int pos)
 {
     var slot = conn.SlotIndex;
     if (slot >= 0)
         Process (slot, readBuffer, length, pos);
     else
         ProgramLog.Error.Log ("Attempt to process packet {0} before slot assignment.", GetPacket());
 }
        public override void Process(ClientConnection conn, byte[] readBuffer, int length, int num)
        {
            if (conn.State == SlotState.ACCEPTED)
            {
                SlotManager.Schedule (conn, conn.DesiredQueue);
                return;
            }

            int whoAmI = conn.SlotIndex;

            if (NetPlay.slots[whoAmI].state == SlotState.ASSIGNING_SLOT)
            {
                NetPlay.slots[whoAmI].state = SlotState.SENDING_WORLD;
            }
            NetMessage.SendData(7, whoAmI);
        }
        public override void Process(ClientConnection conn,  byte[] readBuffer, int length, int num)
        {
            QNPC_Types qType    = (QNPC_Types)readBuffer[num++];
            int QuestID         = readBuffer[num++];
            if (QuestID == 255)
                QuestID = -1;

            if (qType != QNPC_Types.GHOST &&
                qType != QNPC_Types.ALCHEMIST &&
                qType != QNPC_Types.DARK_MAGE &&
                qType != QNPC_Types.PALADIN &&
                qType != QNPC_Types.TINKERER)
            {
                conn.Kick("Sent unknown Quest NPC Name ID.");
                return;
            }

            if (!Main.players[conn.SlotIndex].HasClientMod)
            {
                conn.Kick("Sent Quest NPC Name Packet without permissions.");
                return;
            }

            if (!Server.AllowTDCMRPG)
            {
                conn.Kick("Invalid Client Message, Acting as TDCM");
                return;
            }

            if(QuestID > (int)QuestType.QUESTS_END || QuestID < (int)QuestType.NO_QUEST)
            {
                conn.Kick("Uknown Quest ID.");
                return;
            }

            //Set the players Quest NPC Name & Current Quest Id
            Main.players[conn.SlotIndex].QuestNPCName = QuestNPCType.GetQuestNPCName(qType);
            Main.players[conn.SlotIndex].CurrentQuest = QuestID;
        }
        static bool AcceptClient(Socket client)
        {
            client.NoDelay = true;

            string addr;

            try
            {
                var rep = client.RemoteEndPoint;
                if (rep != null)
                {
                    addr = rep.ToString();
                }
                else
                {
                    ProgramLog.Debug.Log("Accepted socket disconnected");
                    return(false);
                }
            }
            catch (Exception e)
            {
                ProgramLog.Debug.Log("Accepted socket exception ({0})", HandleSocketException(e));
                return(false);
            }

            try
            {
                ProgramLog.Users.Log("{0} is connecting...", addr);
                var conn = new Networking.ClientConnection(client, -1);                 //ignore the warning
            }
            catch (SocketException)
            {
                client.SafeClose();
                ProgramLog.Users.Log("{0} disconnected.", addr);
            }

            return(true);
        }
        internal static string WaitingMessage(ClientConnection conn)
        {
            int i = 0;
            for (int q = 3; q > conn.Queue; q--)
            {
                i += queues[q].Count;
            }

            i += Math.Min (queues[conn.Queue].Count, Math.Max (0, conn.IndexInQueue - queues[conn.Queue].TotalDequeued));

            if (i <= 0) i = 1;

            string suffix = "th";

            switch (i % 10)
            {
                case 1:
                    suffix = "st";
                    break;

                case 2:
                    suffix = "nd";
                    break;

                case 3:
                    suffix = "rd";
                    break;
            }

            switch (i % 100)
            {
                case 11:
                case 12:
                case 13:
                    suffix = "th";
                    break;
            }

            return String.Format (waitingMessage, i, suffix);
        }
        static bool AssignSlot(ClientConnection conn, int id)
        {
            if (! conn.AssignSlot (id))
                return false;

            conn.State = SlotState.ASSIGNING_SLOT;
            conn.ResetTimeout ();

            var slot = NetPlay.slots[id];
            slot.remoteAddress = conn.RemoteAddress;
            slot.conn = conn;

            conn.Player.whoAmi = id;
            Main.players[id] = conn.Player;

            var age = conn.Age;
            if (age > TimeSpan.FromMilliseconds (500))
                ProgramLog.Debug.Log ("Slot {1} assigned to {0} after {2}.", slot.remoteAddress, id, age);
            else
                ProgramLog.Debug.Log ("Slot {1} assigned to {0}.", slot.remoteAddress, id);

            return true;
        }
 // TODO: optimize
 public static void RemoveFromQueues(ClientConnection conn)
 {
     lock (syncRoot)
     {
         for (int q = 3; q >= 0; q--)
         {
             queues[q].Remove (conn);
         }
     }
 }
        public static int Schedule(ClientConnection conn, int priority)
        {
            int id;

            lock (syncRoot)
            {
                if (priority > 2 || priority < 0)
                {
                    // bypass queue
                    id = AssignSlotOrQueue (conn, 3);
                }
                else
                    id = AssignSlotOrQueue (conn, priority);
            }

            // we send here, so as to prevent a deadlock between our lock
            // and the connection's sendqueue lock
            if (id == -1)
            {
                var msg = NetMessage.PrepareThreadInstance ();
                msg.SendTileLoading (1, WaitingMessage (conn));
                conn.Send (msg.Output);
            }
            else if (id >= 0)
            {
                var msg = NetMessage.PrepareThreadInstance ();
                msg.ConnectionResponse (id);
                conn.Send (msg.Output);
            }

            return id;
        }
 public void Send(ClientConnection conn)
 {
     conn.CopyAndSend (Segment);
 }
        public static bool HandoverSlot(ClientConnection src, ClientConnection dst)
        {
            int id = src.SlotIndex;

            if (id < 0 || id > MAX_SLOTS) return false;

            lock (syncRoot)
            {
                handovers[id] = dst;

                // in case the connection dies during the operation
                if (src.SlotIndex != id)
                {
                    handovers[id] = null;
                    return false;
                }
            }

            return true;
        }
        public void Reset()
        {
            tileSection = new bool[Main.maxTilesX / 200, Main.maxTilesY / 150];

            if (tileSection.GetLength(0) >= Main.maxSectionsX && tileSection.GetLength(1) >= Main.maxSectionsY)
            {
                for (int i = 0; i < Main.maxSectionsX; i++)
                    for (int j = 0; j < Main.maxSectionsY; j++)
                        tileSection[i, j] = false;
            }
            else
            {
                tileSection = new bool [Main.maxSectionsX, Main.maxSectionsY];
            }

            var oldPlayer = Main.players[this.whoAmI];
            if (oldPlayer != null && state != SlotState.VACANT)
            {
                NetMessage.OnPlayerLeft (oldPlayer, this, announced);
            }
            announced = false;
            this.remoteAddress = "<unknown>";

            if (this.whoAmI < 255)
            {
                Main.players[this.whoAmI] = new Player();
            }

            this.timeOut = 0; // TODO: move to connection
            this.statusCount = 0;
            this.statusMax = 0;
            this.statusText2 = "";
            this.statusText = "";
            this.name = "Anonymous";
            this.conn = null;

            this.SpamClear();
            NetMessage.buffer[this.whoAmI].Reset();

            conn = null;
            //socket.SafeClose ();
        }
        public static void Dispatch(ClientConnection conn, byte[] readBuffer, int start, int length)
        {
            try
            {
                int num = start + 1;
                byte pkt = readBuffer[start];

                if (conn.State == SlotState.SERVER_AUTH && pkt != 38)
                {
                    conn.Kick ("Incorrect password.");
                    return;
                }

                if ((conn.State & SlotState.DISCONNECTING) == 0)
                {
                    var handler = messageArray[pkt];
                    var state = conn.State;

                    if (handler != null)
                    {
                        //ProgramLog.Debug.Log ("{2}, packet {0}, len {1}", (Packet)readBuffer[start], length, conn.State);

                        if ((state & handler.IgnoredStates) != 0)
                        {
                            //ProgramLog.Debug.Log ("ignoring");
                        }
                        else if ((state & handler.ValidStates) != 0)
                        {
                            handler.Process (conn, readBuffer, length, num);
                        }
                        else
                        {
                            ProgramLog.Debug.Log ("{0}: sent message {1} in state {2}.", conn.RemoteAddress, (pkt > 0 && pkt <= 51) ? (object)(Packet)pkt : pkt, conn.State);
                            conn.Kick ("Invalid operation in this state.");
                        }
                    }
                    else
                    {
                        var ctx = new HookContext()
                        {

                        };
                        var args = new HookArgs.UnkownReceivedPacket()
                        {
                            Conn = conn,
                            Length = length,
                            Start = start,
                            ReadBuffer = readBuffer
                        };

                        //ProgramLog.Debug.Log("Received unknown packet {0}", pkt);

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

                        if (ctx.Result != HookResult.IGNORE && state != SlotState.PLAYING) // this is what stock would do
                            conn.Kick(String.Format("Message not understood ({0}).", pkt));
                    }
                }
            }
            catch (Exception e)
            {
                string pkt = "invalid packet";
                if (readBuffer.Length > start)
                    pkt = String.Format ("packet {0}", (Packet)readBuffer[start]);

                ProgramLog.Log (e, String.Format ("Exception handling {0} of length {1} from {2}",
                    pkt, length, conn.RemoteAddress));

                conn.Kick ("Server malfunction, please reconnect.");
            }
        }
        public override void Process(ClientConnection conn, byte[] readBuffer, int length, int num)
        {
            string chat;
            if(!ParseString(readBuffer, num + 4, length - 5, out chat))
            {
                conn.Kick("Invalid characters in chat message.");
                return;
            }

            if (conn.State < SlotState.PLAYING)
            {
                if (chat != "/playing")
                {
                    ProgramLog.Debug.Log ("{0}: sent message PLAYER_CHAT in state {1}.", conn.RemoteAddress, conn.State);
                    conn.Kick ("Invalid operation at this state.");
                }
                else
                {
                    ProgramLog.Debug.Log ("Replying to early online player query.");
                    var msg = NetMessage.PrepareThreadInstance ();
                    msg.PlayerChat (255, string.Concat ("Current players: ",
                            String.Join (", ", from p in Main.players where p.Active select p.Name), "."),
                            255, 240, 20);
                    conn.Send (msg.Output);
                }
                return;
            }

            int whoAmI = conn.SlotIndex;
            var player = Main.players[whoAmI];

            if (chat.Length == 0) //TODO: check for undetectable spam
                return;

            if (chat.Substring(0, 1).Equals("/"))
            {
                if (Main.players[whoAmI].Op)
                    ProgramLog.Admin.Log (player.Name + " sent command: " + chat);
                else
                    ProgramLog.Users.Log (player.Name + " sent command: " + chat);

                Program.commandParser.ParsePlayerCommand (player, chat);
                return;
            }

            Color color = ChatColor.White;
            if (player.Op)
            {
                color = ChatColor.DeepSkyBlue;
            }
            else if (player.Difficulty == 1)
            {
                color = ChatColor.Khaki;
            }
            else if (player.Difficulty == 2)
            {
                color = ChatColor.Tomato;
            }
            else if (player.team > 0 && player.team < Main.teamColor.Length)
            {
                color = Main.teamColor[player.team];
            }

            var ctx = new HookContext
            {
                Connection = player.Connection,
                Sender = player,
                Player = player,
            };

            var args = new HookArgs.PlayerChat
            {
                Message = chat,
                Color = color,
            };

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

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

            NetMessage.SendData (Packet.PLAYER_CHAT, -1, -1, chat, whoAmI, args.Color.R, args.Color.G, args.Color.B);
            ProgramLog.Chat.Log ("<" + player.Name + "> " + chat, SendingLogger.PLAYER);
        }
        public void Remove(ClientConnection c)
        {
            var len = array.Length;

            for (int i = 0; i < count; i++)
            {
                int k = (head + i) % len;
                if (array[k] == c)
                {
                    realCount -= 1;
                    array[k] = null;
                }
            }

            while (array[head] == null && count > 0)
            {
                head = (head + 1) % array.Length;
                count -= 1;
                TotalDequeued += 1;
            }

            while (count > 0 && array[(head + count - 1) % array.Length] == null)
            {
                count -= 1;
                TotalDequeued += 1;
            }
        }
        static string GetName(ClientConnection conn, byte[] readBuffer, int num, int len)
        {
            string name;

            try
            {
                name = Encoding.ASCII.GetString (readBuffer, num, len).Trim();
            }
            catch (ArgumentException)
            {
                conn.Kick ("Invalid name: contains non-ASCII characters.");
                return null;
            }

            if (name.Length > 20)
            {
                conn.Kick ("Invalid name: longer than 20 characters.");
                return null;
            }

            if (name == "")
            {
                conn.Kick ("Invalid name: whitespace or empty.");
                return null;
            }

            foreach (char c in name)
            {
                if (c < 32 || c > 126)
                {
                    conn.Kick ("Invalid name: contains non-printable characters.");
                    return null;
                }
            }

            if (name.Contains (" " + " "))
            {
                conn.Kick ("Invalid name: contains double spaces.");
                return null;
            }

            return name;
        }
        public void Reset()
        {
            if (tileSection != null && tileSection.GetLength(0) >= Main.maxSectionsX && tileSection.GetLength(1) >= Main.maxSectionsY)
            {
                Array.Clear (tileSection, 0, tileSection.GetLength(0) * tileSection.GetLength(1));
            }
            else
            {
                tileSection = new bool [Main.maxSectionsX, Main.maxSectionsY];
            }

            var oldPlayer = Main.players[this.whoAmI];
            if (oldPlayer != null && state != SlotState.VACANT)
            {
                NetMessage.OnPlayerLeft (oldPlayer, this, announced);
            }
            announced = false;
            this.remoteAddress = "<unknown>";

            if (this.whoAmI < 255)
            {
                Main.players[this.whoAmI] = new Player();
            }

            this.statusCount = 0;
            this.statusMax = 0;
            this.statusText2 = "";
            this.statusText = "";
            this.name = "Anonymous";
            this.conn = null;

            this.SpamClear();

            conn = null;
        }
        public override void Process(ClientConnection conn, byte[] readBuffer, int length, int num)
        {
            //            ServerSlot slot = Netplay.slots[whoAmI];
            //            PlayerLoginEvent loginEvent = new PlayerLoginEvent();
            //            loginEvent.Slot = slot;
            //            loginEvent.Sender = Main.players[whoAmI];
            //            Server.PluginManager.processHook(Plugin.Hooks.PLAYER_PRELOGIN, loginEvent);
            //            if ((loginEvent.Cancelled || loginEvent.Action == PlayerLoginAction.REJECT) && (slot.state & SlotState.DISCONNECTING) == 0)
            //            {
            //                slot.Kick ("Disconnected by server.");
            //                return;
            //            }

            string clientName = conn.RemoteAddress.Split(':')[0];
            //
            //            if (Server.BanList.containsException(clientName))
            //            {
            //                slot.Kick ("You are banned from this Server.");
            //                return;
            //            }

            if (Program.properties.UseWhiteList && !Server.WhiteList.containsException(clientName))
            {
                conn.Kick ("You are not on the WhiteList.");
                return;
            }

            string version = Networking.StringCache.FindOrMake (new ArraySegment<byte> (readBuffer, num, length - 1));

            var ctx = new HookContext
            {
                Connection = conn,
            };

            var args = new HookArgs.ConnectionRequestReceived
            {
                Version = version,
            };

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

            if (ctx.CheckForKick ())
                return;

            if (ctx.Result == HookResult.DEFAULT && !(version == "Terraria" + Statics.CURRENT_TERRARIA_RELEASE))
            {
                if (version.Length > 30) version = version.Substring (0, 30);
                ProgramLog.Debug.Log ("Client version string: {0}", version);
                conn.Kick (string.Concat ("This server requires Terraria ", Statics.VERSION_NUMBER));
                return;
            }

            var msg = NetMessage.PrepareThreadInstance ();

            if (ctx.Result == HookResult.ASK_PASS || (NetPlay.password != null && NetPlay.password != ""))
            {
                conn.State = SlotState.SERVER_AUTH;
                msg.PasswordRequest ();
                conn.Send (msg.Output);
                return;
            }

            conn.State = SlotState.ACCEPTED;
            msg.ConnectionResponse (253 /* arbitrary fake value, true slot assigned later */);
            conn.Send (msg.Output);
        }
        static int AssignSlotOrQueue(ClientConnection conn, int queue)
        {
            int id = -1;

            conn.Queue = queue;

            if (queue == 3)
            {
                if (privFreeSlots.Count > 0)
                {
                    // use other slots for highest priority connections first
                    id = privFreeSlots.Pop ();
                    privSlotsInUse += 1;
                }
                else if (freeSlots.Count > 0)
                    id = freeSlots.Pop ();
            }
            else if (freeSlots.Count > 0 && freeSlots.Count > (privSlotsInUse - overlimitSlots))
                id = freeSlots.Pop ();

            if (id == -1)
            {
                conn.State = SlotState.QUEUED;
                conn.ResetTimeout ();

                queues[queue].Enqueue (conn);

                ProgramLog.Debug.Log ("Connection {0} queued with priority {1}.", conn.RemoteAddress, queue);

                return -1;
            }

            if (AssignSlot (conn, id))
                return id;

            PushSlot (id);
            return -2;
        }
        static bool AcceptClient(Socket client)
        {
            client.NoDelay = true;

            string addr;
            try
            {
                var rep = client.RemoteEndPoint;
                if (rep != null)
                    addr = rep.ToString();
                else
                {
                    ProgramLog.Debug.Log ("Accepted socket disconnected");
                    return false;
                }
            }
            catch (Exception e)
            {
                ProgramLog.Debug.Log ("Accepted socket exception ({0})", HandleSocketException (e));
                return false;
            }

            try
            {
                ProgramLog.Users.Log ("{0} is connecting...", addr);
                var conn = new Networking.ClientConnection (client, -1); //ignore the warning
            }
            catch (SocketException)
            {
                client.SafeClose ();
                ProgramLog.Users.Log ("{0} disconnected.", addr);
            }

            return true;
        }
        public void Enqueue(ClientConnection c)
        {
            if (count == array.Length) Grow ();

            int tail = (head + count) % array.Length;
            array[tail] = c;
            count += 1;
            realCount += 1;

            TotalEnqueued += 1;
            c.IndexInQueue = TotalEnqueued;
        }
        public override void Process(ClientConnection conn, byte[] readBuffer, int length, int num)
        {
            int start = num - 1;

            if (conn.State == SlotState.ASSIGNING_SLOT)
            {
                // TODO: verify that data didn't change.
                int who = conn.SlotIndex;
                NetMessage.SendData (4, -1, who, conn.Player.Name, who);
                return;
            }

            if (conn.Player != null)
            {
                conn.Kick ("Player data sent twice.");
                return;
            }

            var player = new Player ();
            conn.Player = player;
            player.Connection = conn;
            player.IPAddress = conn.RemoteAddress;
            player.whoAmi = conn.SlotIndex;

            var data = new HookArgs.PlayerDataReceived ();

            data.Parse (readBuffer, num + 1, length);

            if (data.Hair >= MAX_HAIR_ID)
            {
                data.Hair = 0;
            }

            var ctx = new HookContext
            {
                Connection = conn,
                Player = player,
                Sender = player,
            };

            HookPoints.PlayerDataReceived.Invoke (ref ctx, ref data);

            if (ctx.CheckForKick ())
                return;

            if (! data.NameChecked)
            {
                string error;
                if (! data.CheckName (out error))
                {
                    conn.Kick (error);
                    return;
                }
            }

            if (! data.BansChecked)
            {
                string address = conn.RemoteAddress.Split(':')[0];

                if (Server.BanList.containsException (address) || Server.BanList.containsException (data.Name))
                {
                    ProgramLog.Admin.Log ("Prevented user {0} from accessing the server.", data.Name);
                    conn.Kick ("You are banned from this server.");
                    return;
                }
            }

            data.Apply (player);

            if (ctx.Result == HookResult.ASK_PASS)
            {
                conn.State = SlotState.PLAYER_AUTH;

                var msg = NetMessage.PrepareThreadInstance ();
                msg.PasswordRequest ();
                conn.Send (msg.Output);

                return;
            }
            else // HookResult.DEFAULT
            {
                // don't allow replacing connections for guests, but do for registered users
                if (conn.State < SlotState.PLAYING)
                {
                    var lname = player.Name.ToLower();

                    foreach (var otherPlayer in Main.players)
                    {
                        var otherSlot = NetPlay.slots[otherPlayer.whoAmi];
                        if (otherPlayer.Name != null && lname == otherPlayer.Name.ToLower() && otherSlot.state >= SlotState.CONNECTED)
                        {
                            conn.Kick ("A \"" + otherPlayer.Name + "\" is already on this server.");
                            return;
                        }
                    }
                }

                //conn.Queue = (int)loginEvent.Priority; // actual queueing done on world request message

                // and now decide whether to queue the connection
                //SlotManager.Schedule (conn, (int)loginEvent.Priority);

                //NetMessage.SendData (4, -1, -1, player.Name, whoAmI);
            }
        }
        void Grow()
        {
            int len = array.Length;

            var a = new ClientConnection [len * 3 / 2 + 16];

            for (int i = 0; i < count; i++)
            {
                int k = (head + i) % len;
                a[i] = array[k];
            }

            head = 0;

            array = a;
        }
 public abstract void Process(ClientConnection conn, byte[] readBuffer, int length, int pos);
        public override void Process(ClientConnection conn, byte[] readBuffer, int length, int num)
        {
            int start = num - 1;
            string password = Encoding.ASCII.GetString(readBuffer, num, length - num + start);

            if (conn.State == SlotState.SERVER_AUTH)
            {
                var ctx = new HookContext
                {
                    Connection = conn,
                };

                var args = new HookArgs.ServerPassReceived
                {
                    Password = password,
                };

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

                if (ctx.CheckForKick ())
                    return;

                if (ctx.Result == HookResult.ASK_PASS)
                {
                    var msg = NetMessage.PrepareThreadInstance ();
                    msg.PasswordRequest ();
                    conn.Send (msg.Output);
                }
                else if (ctx.Result == HookResult.CONTINUE || password == NetPlay.password)
                {
                    conn.State = SlotState.ACCEPTED;

                    var msg = NetMessage.PrepareThreadInstance ();
                    msg.ConnectionResponse (253 /* dummy value, real slot assigned later */);
                    conn.Send (msg.Output);

                    return;
                }

                conn.Kick ("Incorrect server password.");
            }
            else if (conn.State == SlotState.PLAYER_AUTH)
            {
                var name = conn.Player.Name ?? "";

                var ctx = new HookContext
                {
                    Connection = conn,
                    Player = conn.Player,
                    Sender = conn.Player,
                };

                var args = new HookArgs.PlayerPassReceived
                {
                    Password = password,
                };

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

                if (ctx.CheckForKick ())
                    return;

                if (ctx.Result == HookResult.ASK_PASS)
                {
                    var msg = NetMessage.PrepareThreadInstance ();
                    msg.PasswordRequest ();
                    conn.Send (msg.Output);
                }
                else // HookResult.DEFAULT
                {
                    var lower = name.ToLower();
                    bool reserved = false;

                    //conn.Queue = (int)loginEvent.Priority;

                    foreach (var otherPlayer in Main.players)
                    {
                        //var otherSlot = Netplay.slots[otherPlayer.whoAmi];
                        var otherConn = otherPlayer.Connection;
                        if (otherPlayer.Name != null
                            && lower == otherPlayer.Name.ToLower()
                            && otherConn != null
                            && otherConn.State >= SlotState.CONNECTED)
                        {
                            if (! reserved)
                            {
                                reserved = SlotManager.HandoverSlot (otherConn, conn);
                            }
                            otherConn.Kick ("Replaced by new connection.");
                        }
                    }

                    //conn.State = SlotState.SENDING_WORLD;

                    if (! reserved) // reserved slots get assigned immediately during the kick
                    {
                        SlotManager.Schedule (conn, conn.DesiredQueue);
                    }

                    //NetMessage.SendData (4, -1, whoAmI, name, whoAmI); // broadcast player data now

                    // replay packets from side buffer
                    //conn.conn.ProcessSideBuffer ();
                    //var buf = NetMessage.buffer[whoAmI];
                    //NetMessage.CheckBytes (whoAmI, buf.sideBuffer, ref buf.sideBufferBytes, ref buf.sideBufferMsgLen);
                    //buf.ResetSideBuffer ();

                    //NetMessage.SendData (7, whoAmI); // continue with world data
                }
            }
        }
        public static void FreeSlot(int id)
        {
            ClientConnection assignedTo = null;

            var slot = NetPlay.slots[id];

            slot.Reset();

            lock (syncRoot)
            {
                if (handovers[id] != null)
                {
                    assignedTo    = handovers[id];
                    handovers[id] = null;

                    if (!AssignSlot(assignedTo, id))
                    {
                        assignedTo = null;
                        ProgramLog.Debug.Log("Slot {0} handover failed.", id);
                    }
                    else
                    {
                        ProgramLog.Debug.Log("Slot {0} handover successful.", id);
                    }
                }

                if (assignedTo == null)
                {
                    assignedTo = FindForSlot(id);
                }

                if (assignedTo == null)
                {
                    PushSlot(id);
                    ProgramLog.Debug.Log("Freed slot {0}", id);

                    // this is for when a privileged slot is freed, but no privileged clients are waiting
                    // so we check if we have an unprivileged slot to use
                    if (isPrivileged[id] && (freeSlots.Count > 0 && freeSlots.Count > (privSlotsInUse - overlimitSlots)))
                    {
                        id         = freeSlots.Pop();
                        assignedTo = FindForSlot(id);

                        if (assignedTo == null)
                        {
                            freeSlots.Push(id);
                            return;
                        }
                    }
                    else
                    {
                        return;
                    }
                }
            }

            var msg = NetMessage.PrepareThreadInstance();

            msg.ConnectionResponse(id);
            assignedTo.Send(msg.Output);
        }