/// <summary>
 /// Create a new DisconnectPlayerEvent.
 /// </summary>
 /// <param name="Player">Supplies the player to disconnect.</param>
 public DisconnectPlayerEvent(GamePlayer Player)
 {
     this.Player = Player;
 }
 /// <summary>
 /// This function inserts a player into the various player lists and
 /// issues the player load event.
 /// </summary>
 /// <param name="Player">Supplies the player object to insert.
 /// </param>
 /// <param name="Database">Supplies the database connection to use for
 /// queries, if required.  The active rowset may be consumed.</param>
 private void InsertNewPlayer(GamePlayer Player, IALFADatabase Database)
 {
     PlayerList.Add(Player);
     OnPlayerLoaded(Player);
 }
        /// <summary>
        /// This method initiates a server-to-server tell.
        /// </summary>
        /// <param name="SenderObjectId">Supplies the local object id of the
        /// sender player.</param>
        /// <param name="SenderPlayer">Supplies the GamePlayer object for the
        /// sender player.</param>
        /// <param name="RecipientPlayer">Supplies the GamePlayer object for
        /// the recipient player.</param>
        /// <param name="Message">Supplies the message to send.</param>
        private void SendServerToServerTell(uint SenderObjectId, GamePlayer SenderPlayer, GamePlayer RecipientPlayer, string Message)
        {
            GameServer DestinationServer = RecipientPlayer.GetOnlineServer();

            Database.ACR_IncrementStatistic("SERVER_TELLS");

            SetLastTellToPlayerId(SenderObjectId, RecipientPlayer.PlayerId);

            if (!RecipientPlayer.IsOnline() || DestinationServer == null)
            {
                SendFeedbackError(SenderObjectId, "That player is not logged on.");
                return;
            }

#if DEBUG_MODE
            //
            // Debug mode always sends through the IPC queue for better
            // testing.
            //
#else
            //
            // If this is a local server tell, dispatch it locally.
            //

            if (DestinationServer.ServerId == Database.ACR_GetServerID())
            {
                foreach (uint PlayerObject in GetPlayers(true))
                {
                    if (Database.ACR_GetPlayerID(PlayerObject) != SenderPlayer.PlayerId)
                        continue;

                    //
                    // Note, we call the chat callback for the first event for
                    // two reasons:
                    //
                    // 1) Let the RP XP script notice the activity.
                    // 2) Set the last tell to/from player ids.
                    //

                    SendChatMessage(SenderObjectId, PlayerObject, CHAT_MODE_TELL, Message, TRUE);
                    return;
                }

                SendFeedbackError(SenderObjectId, "Internal error - attempted to re-route tell to local player, but player wasn't actually on this server.");
                return;
            }
#endif

            if (Database.ACR_GetIsPCQuarantined(SenderObjectId))
            {
                //
                // Since a player in quarantine is not marked as online, a
                // remote server may choose to discard a tell initiated while
                // in quarantine.  Warn the player of this.
                //

                SendFeedbackError(SenderObjectId, "Warning: Cross-server tells may not be delivered when in quarantine.");
            }

            //
            // Otherwise, enqueue it, breaking large tells up into multiple
            // smaller tells if need be.
            //

            while (!String.IsNullOrEmpty(Message))
            {
                string MessagePart;

                if (Message.Length > ACR_SERVER_IPC_MAX_EVENT_LENGTH)
                {
                    MessagePart = Message.Substring(0, ACR_SERVER_IPC_MAX_EVENT_LENGTH);
                    Message = Message.Substring(ACR_SERVER_IPC_MAX_EVENT_LENGTH);
                }
                else
                {
                    MessagePart = Message;
                    Message = null;
                }

                SignalIPCEvent(
                    SenderPlayer.PlayerId,
                    Database.ACR_GetServerID(),
                    RecipientPlayer.PlayerId,
                    DestinationServer.ServerId,
                    GameWorldManager.ACR_SERVER_IPC_EVENT_CHAT_TELL,
                    MessagePart);
                SendChatMessage(
                    OBJECT_INVALID,
                    SenderObjectId,
                    CHAT_MODE_SERVER,
                    String.Format("<c=#FFCC99>{0}: </c><c=#30DDCC>[ServerTell] {1}</c>", GetName(SenderObjectId), MessagePart),
                    FALSE);
            }

            SetLocalInt(SenderObjectId, "ACR_XP_RPXP_ACTIVE", TRUE);
        }
 /// <summary>
 /// This method is called when a page IPC event is received.
 /// </summary>
 /// <param name="Sender">Supplies the sender.</param>
 /// <param name="Recipient">Supplies the recipient.</param>
 /// <param name="Message">Supplies the message text.</param>
 private void OnPage(GamePlayer Sender, GamePlayer Recipient, string Message)
 {
     EnqueueEvent(new PageEvent(Sender, Recipient, Message));
 }
 /// <summary>
 /// This method is called when a player has had all of its data loaded
 /// from the database.  The player is inserted already.
 /// </summary>
 /// <param name="Player">Supplies the player object hwich has been
 /// loaded.</param>
 private void OnPlayerLoaded(GamePlayer Player)
 {
 }
 /// <summary>
 /// This method is called when a disconnect player request is received.
 /// </summary>
 /// <param name="Player">Supplies the player to disconnect.</param>
 private void OnDisconnectPlayer(GamePlayer Player)
 {
     EnqueueEvent(new DisconnectPlayerEvent(Player));
 }
        /// <summary>
        /// This method is called when a purge cached character request is
        /// received.
        /// </summary>
        /// <param name="Player">Supplies the player whose cached vault
        /// contents are to be modified.</param>
        /// <param name="CharacterFileName">Supplies the file name of the
        /// character file to remove from the local cache.  Special file names
        /// and path characters are forbidden.</param>
        private void OnPurgeCachedCharacter(GamePlayer Player, string CharacterFileName)
        {
            if (!SystemInfo.IsSafeFileName(CharacterFileName))
            {
                WriteDiagnosticLog(String.Format(
                    "GameWorldManager.OnPurgeCachedCharacter: Invalid file name '{0}'.", CharacterFileName));
                return;
            }

            EnqueueEvent(new PurgeCachedCharacterEvent(Player, CharacterFileName));
        }
        /// <summary>
        /// Reference the data for a player by the player id.  If the data
        /// was not yet available, it is retrieved from the database.
        /// </summary>
        /// <param name="PlayerId">Supplies the object id.</param>
        /// <param name="Database">Supplies the database connection to use for
        /// queries, if required.  The active rowset may be consumed.</param>
        /// <returns>The object data is returned, else null if the object did
        /// not exist.</returns>
        public GamePlayer ReferencePlayerById(int PlayerId, IALFADatabase Database)
        {
            //
            // Check if the object is already known first.
            //

            GamePlayer Player = (from P in Players
                                 where P.PlayerId == PlayerId
                                 select P).FirstOrDefault();

            if (Player != null)
                return Player;

            //
            // Need to pull the data from the database.
            //

            if (Database == null)
                return null;

            Database.ACR_SQLQuery(String.Format(
                "SELECT `Name`, `IsDM` FROM `players` WHERE `ID` = {0}",
                PlayerId));

            if (!Database.ACR_SQLFetch())
                return null;

            Player = new GamePlayer(this);

            Player.PlayerName = Database.ACR_SQLGetData(0);
            Player.PlayerId = PlayerId;
            Player.IsDM = ConvertToBoolean(Database.ACR_SQLGetData(1));

            InsertNewPlayer(Player, Database);

            return Player;
        }
 /// <summary>
 /// This method is called when a chat tell IPC event is received.
 /// </summary>
 /// <param name="Sender">Supplies the sender.</param>
 /// <param name="Recipient">Supplies the recipient.</param>
 /// <param name="Message">Supplies the message text.</param>
 private void OnChatTell(GameCharacter Sender, GamePlayer Recipient, string Message)
 {
     EnqueueEvent(new ChatTellEvent(Sender, Recipient, Message));
 }
        /// <summary>
        /// Reference the data for a player by the player name.  If the data
        /// was not yet available, it is retrieved from the database.
        /// </summary>
        /// <param name="PlayerName">Supplies the object name.</param>
        /// <param name="Database">Supplies the database connection to use for
        /// queries, if required.  The active rowset may be consumed.</param>
        /// <returns>The object data is returned, else null if the object did
        /// not exist.</returns>
        public GamePlayer ReferencePlayerByName(string PlayerName, IALFADatabase Database)
        {
            //
            // Check if the object is already known first.
            //

            GamePlayer Player = (from P in Players
                                 where P.PlayerName.Equals(PlayerName, StringComparison.InvariantCultureIgnoreCase)
                                 select P).FirstOrDefault();

            if (Player != null)
                return Player;

            //
            // Need to pull the data from the database.
            //

            if (Database == null)
                return null;

            Database.ACR_SQLQuery(String.Format(
                "SELECT `ID`, `IsDM`, `Name` FROM `players` WHERE `Name` = '{0}'",
                Database.ACR_SQLEncodeSpecialChars(PlayerName)));

            if (!Database.ACR_SQLFetch())
                return null;

            Player = new GamePlayer(this);

            Player.PlayerId = Convert.ToInt32(Database.ACR_SQLGetData(0));
            Player.IsDM = ConvertToBoolean(Database.ACR_SQLGetData(1));
            Player.PlayerName = Database.ACR_SQLGetData(2);

            InsertNewPlayer(Player, Database);

            return Player;
        }
        /// <summary>
        /// This method performs the initial synchronization step at first run
        /// that downloads the initial character list.  A bulk query is issued
        /// here to reduce the number of database round-trips at startup time.
        /// 
        /// Note that no attempt is made to mark offline characters here.  That
        /// step is done in the normal synchronization round, as this is the
        /// initial round anyway.
        /// </summary>
        private void PerformInitialSynchronization()
        {
            IALFADatabase Database = DatabaseLinkQueryThread;
            List<InitialSynchronizationRow> Rowset = new List<InitialSynchronizationRow>();

            Database.ACR_SQLQuery(
                "SELECT " +
                    "`characters`.`ID` AS character_id, " +                  //  0
                    "`characters`.`PlayerID` AS player_id, " +               //  1
                    "`characters`.`Name` AS character_name, " +              //  2
                    "`characters`.`ServerID` AS server_id, " +               //  3
                    "`characters`.`Location` AS character_location, " +      //  4
                    "`players`.`IsDM` AS player_is_dm, " +                   //  5
                    "`players`.`Name` AS player_name, " +                    //  6
                    "`servers`.`IPAddress` AS server_address_string, " +     //  7
                    "`servers`.`Name` AS server_name " +                     //  8
                "FROM " +
                    "`characters` " +
                "INNER JOIN `servers` ON `characters`.`ServerID` = `servers`.`ID` " +
                "INNER JOIN `players` ON `characters`.`PlayerID` = `players`.`ID` " +
                "WHERE " +
                    "`characters`.`IsOnline` = 1 "
                );

            while (Database.ACR_SQLFetch())
            {
                InitialSynchronizationRow Row;

                Row.CharacterId = Convert.ToInt32(Database.ACR_SQLGetData(0));
                Row.PlayerId = Convert.ToInt32(Database.ACR_SQLGetData(1));
                Row.CharacterName = Database.ACR_SQLGetData(2);
                Row.ServerId = Convert.ToInt32(Database.ACR_SQLGetData(3));
                Row.CharacterLocation = Database.ACR_SQLGetData(4);
                Row.PlayerIsDM = ConvertToBoolean(Database.ACR_SQLGetData(5));
                Row.PlayerName = Database.ACR_SQLGetData(6);
                Row.ServerAddressString = Database.ACR_SQLGetData(7);
                Row.ServerName = Database.ACR_SQLGetData(8);

                Rowset.Add(Row);
            }

            lock (this)
            {
                //
                // Update entries.
                //

                foreach (InitialSynchronizationRow Row in Rowset)
                {
                    GameServer Server = (from S in Servers
                                         where S.ServerId == Row.ServerId
                                         select S).FirstOrDefault();

                    if (Server == null)
                    {
                        Server = new GameServer(this);

                        Server.ServerName = Row.ServerName;
                        Server.ServerId = Row.ServerId;
                        Server.SetHostnameAndPort(Row.ServerAddressString);

                        InsertNewServer(Server, Database);
                    }

                    GamePlayer Player = (from P in Players
                                         where P.PlayerId == Row.PlayerId
                                         select P).FirstOrDefault();

                    if (Player == null)
                    {
                        Player = new GamePlayer(this);

                        Player.PlayerName = Row.PlayerName;
                        Player.PlayerId = Row.PlayerId;
                        Player.IsDM = Row.PlayerIsDM;

                        InsertNewPlayer(Player, Database);
                    }

                    GameCharacter Character = (from C in Characters
                                               where C.CharacterId == Row.CharacterId
                                               select C).FirstOrDefault();

                    if (Character == null)
                    {
                        Character = new GameCharacter(this);

                        Character.CharacterId = Row.CharacterId;
                        Character.PlayerId = Row.PlayerId;
                        Character.Online = true;
                        Character.CharacterName = Row.CharacterName;
                        Character.LocationString = Row.CharacterLocation;

                        InsertNewCharacter(Character, Row.ServerId, Database, null);
                    }
                }

#if DEBUG_MODE
                WriteDiagnosticLog(String.Format("GameWorldManager.PerformInitialSynchronization: Synchronized {0} servers, {1} players, {2} characters.",
                    ServerList.Count,
                    PlayerList.Count,
                    CharacterList.Count));
#endif
            }
        }
 /// <summary>
 /// Create a new ChatTellEvent.
 /// </summary>
 /// <param name="Sender">Supplies the sender.</param>
 /// <param name="Recipient">Supplies the recipient.</param>
 /// <param name="Message">Supplies the message text.</param>
 public ChatTellEvent(GameCharacter Sender, GamePlayer Recipient, string Message)
 {
     this.Sender = Sender;
     this.Recipient = Recipient;
     this.Message = Message;
 }
 /// <summary>
 /// Create a new PurgeCachedCharacterEvent.
 /// </summary>
 /// <param name="Player">Supplies the player whose vault should have
 /// the specific character file removed.</param>
 /// <param name="CharacterFileName">Supplies the character file name to
 /// delete.  There should be no path separators, for example,
 /// "charfile.bic".</param>
 public PurgeCachedCharacterEvent(GamePlayer Player, string CharacterFileName)
 {
     this.Player = Player;
     this.CharacterFileName = CharacterFileName;
 }
 /// <summary>
 /// Create a new PageEvent.
 /// </summary>
 /// <param name="Sender">Supplies the sender.</param>
 /// <param name="Recipient">Supplies the recipient.</param>
 /// <param name="Message">Supplies the message text.</param>
 public PageEvent(GamePlayer Sender, GamePlayer Recipient, string Message)
 {
     this.Sender = Sender;
     this.Recipient = Recipient;
     this.Message = Message;
 }