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 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 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 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 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);
        }
        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);
            }
        }
        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);
        }