This object maintains state about a game server, gathered from the database.
Inheritance: IGameEntity
        /// <summary>
        /// Send a shutdown notification message to a destination server.
        /// </summary>
        /// <param name="Server">Supplies the destination server.</param>
        public void SendMessageShutdownNotify(GameServer Server)
        {
            NetworkMessage Message = CreateDatagramMessage(DATAGRAM_MESSAGE_CMD.CMD_SHUTDOWN_NOTIFY);
            BufferBuilder Builder = Message.GetBuilder();

            Builder.WriteInt32(LocalServerId);

            SendMessageToServer(Message, Server);
        }
        /// <summary>
        /// Send an IPC wakeup message to a destination server.
        /// </summary>
        /// <param name="Server">Supplies the destination server.</param>
        public void SendMessageIPCWakeup(GameServer Server)
        {
            NetworkMessage Message = CreateDatagramMessage(DATAGRAM_MESSAGE_CMD.CMD_IPC_WAKE);
            BufferBuilder Builder = Message.GetBuilder();

            Builder.WriteInt32(LocalServerId);
            Builder.WriteInt32(Server.ServerId);

            SendMessageToServer(Message, Server);
        }
        /// <summary>
        /// Send a database online/offline notification to a server.
        /// </summary>
        /// <param name="Server">Supplies the destination server.</param>
        /// <param name="Online">Supplies 1 if the database is viewed as
        /// online, else 0 if the database is viewed as offline.</param>
        public void SendNotifyMessageDatabaseStatus(GameServer Server, bool Online)
        {
            NetworkMessage Message = CreateDatagramMessage(DATAGRAM_MESSAGE_CMD.CMD_NOTIFY_DATABASE_STATUS);
            BufferBuilder Builder = Message.GetBuilder();

            Builder.WriteInt32(LocalServerId);
            Builder.WriteByte(Online ? (byte)1 : (byte)0);

            SendMessageToServer(Message, Server);
        }
        /// <summary>
        /// Send a direct message to a server.  Failures are ignored.
        /// </summary>
        /// <param name="Data">Supplies the message payload.</param>
        /// <param name="Server">Supplies the destination server.</param>
        private void SendMessageToServer(byte[] Data, GameServer Server)
        {
            try
            {
                IPAddress Address;

                if (Server.ServerId == LocalServerId)
                    Address = IPAddress.Loopback;
                else
                    Address = Server.GetIPAddress();

                SocketIo.SendMessage(Data, Address, Server.ServerPort);
            }
            catch
            {
            }
        }
        /// <summary>
        /// This function inserts a server into the various server lists and
        /// issues the server load event.
        /// </summary>
        /// <param name="Server">Supplies the server 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 InsertNewServer(GameServer Server, IALFADatabase Database)
        {
            //
            // Mark the server as visited so that if we come in on the main
            // thread during the middle of a server synchronization cycle, we
            // won't immediate offline the server.
            //

            Server.Visited = true;
            Server.RefreshOnlineStatus(Database);

            ServerList.Add(Server);
            OnServerLoaded(Server);
        }
        /// <summary>
        /// Handle a database status notification.
        /// </summary>
        /// <param name="Message">Supplies the message.</param>
        /// <param name="Source">Supplies the message sender.</param>
        private void OnRecvMessageNotifyDatabaseStatus(NetworkMessage Message, GameServer Source)
        {
            BufferParser Parser = Message.GetParser();
            int SourceServerId = Parser.ReadInt32();
            bool DatabaseOnline = (Parser.ReadByte() == 0 ? false : true);

            Parser.FinishParsing();

            if (SourceServerId != Source.ServerId)
                return;

            lock (WorldManager)
            {
                if (Source.Online)
                    Source.DatabaseOnline = DatabaseOnline;
            }
        }
        /// <summary>
        /// Handle a shutdown notification message.
        /// </summary>
        /// <param name="Message">Supplies the message.</param>
        /// <param name="Source">Supplies the message sender.</param>
        private void OnRecvMessageShutdownNotify(NetworkMessage Message, GameServer Source)
        {
            BufferParser Parser = Message.GetParser();
            int SourceServerId = Parser.ReadInt32();

            Parser.FinishParsing();

            if (SourceServerId != Source.ServerId)
                return;

            //
            // Tear down any state between the two servers.
            //
        }
        /// <summary>
        /// Handle an IPC wakeup message received.
        /// </summary>
        /// <param name="Message">Supplies the message.</param>
        /// <param name="Source">Supplies the message sender.</param>
        private void OnRecvMessageIPCWake(NetworkMessage Message, GameServer Source)
        {
            BufferParser Parser = Message.GetParser();
            int SourceServerId = Parser.ReadInt32();
            int DestinationServerId = Parser.ReadInt32();

            Parser.FinishParsing();

            if (SourceServerId != Source.ServerId || LocalServerId != DestinationServerId)
                return;

            //
            // Signal the world manager that it should break out of the polling
            // wait and immediately check for new IPC messages that are
            // available to process.
            //

            WorldManager.SignalIPCEventWakeup();
        }
        /// <summary>
        /// Send a direct message to a server.  Failures are ignored.
        /// </summary>
        /// <param name="Message">Supplies the message payload.</param>
        /// <param name="Server">Supplies the destination server.</param>
        private void SendMessageToServer(NetworkMessage Message, GameServer Server)
        {
            if (!Server.Online)
                return;

            try
            {
                SendMessageToServer(Message.FinalizeMessage(), Server);
            }
            catch
            {
            }
        }
示例#10
0
 /// <summary>
 /// Create a new CharacterJoinEvent.
 /// </summary>
 /// <param name="Character">Supplies the character.</param>
 /// <param name="IsDM">Supplies true if the character was DM
 /// privileged at join time.</param>
 /// <param name="Server">Supplies the server that the character has
 /// joined.</param>
 public CharacterJoinEvent(GameCharacter Character, bool IsDM, GameServer Server)
 {
     this.Character = Character;
     this.IsDM      = IsDM;
     this.Server    = Server;
 }
 /// <summary>
 /// Create a new CharacterPartEvent.
 /// </summary>
 /// <param name="Character">Supplies the character.</param>
 /// <param name="IsDM">Supplies true if the character was DM
 /// privileged at part time.</param>
 /// <param name="Server">Supplies the server that the character has
 /// parted.</param>
 public CharacterPartEvent(GameCharacter Character, bool IsDM, GameServer Server)
 {
     this.Character = Character;
     this.IsDM = IsDM;
     this.Server = Server;
 }
 /// <summary>
 /// This method is called when a server has had all of its data loaded
 /// from the database.  The server is inserted already.
 /// </summary>
 /// <param name="Server">Supplies the server object hwich has been
 /// loaded.</param>
 private void OnServerLoaded(GameServer Server)
 {
     if (Server.Online)
         EnqueueEvent(new ServerJoinEvent(Server));
 }
        /// <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>
 /// This method is called when a run script IPC event is received.
 /// </summary>
 /// <param name="SourceServer">Supplies the sender server, which may be
 /// may be null if the request was not sent by a server but external
 /// automation.</param>
 /// <param name="ScriptName">Supplies the name of the script.</param>
 /// <param name="ScriptArgument">Supplies an optional argument for the
 /// script main function.</param>
 private void OnRunScript(GameServer SourceServer, string ScriptName, string ScriptArgument)
 {
     EnqueueEvent(new RunScriptEvent(SourceServer, ScriptName, ScriptArgument));
 }
 /// <summary>
 /// This method is called when a server is discovered to have gone
 /// offline.
 /// </summary>
 /// <param name="Server">Suplies the server that is now considered to
 /// be offline.</param>
 private void OnServerPart(GameServer Server)
 {
     EnqueueEvent(new ServerPartEvent(Server));
 }
 /// <summary>
 /// This method is called when a server is discovered to have come
 /// online.  The server is inserted already.
 /// </summary>
 /// <param name="Server">Supplies the server that is now considered to
 /// be online.</param>
 private void OnServerJoin(GameServer Server)
 {
     EnqueueEvent(new ServerJoinEvent(Server));
 }
        /// <summary>
        /// Reference the data for a server by the server id.  If the data
        /// was not yet available, it is retrieved from the database.
        /// </summary>
        /// <param name="ServerId">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 GameServer ReferenceServerById(int ServerId, IALFADatabase Database)
        {
            //
            // Check if the object is already known first.
            //

            GameServer Server = (from S in Servers
                                 where S.ServerId == ServerId
                                 select S).FirstOrDefault();

            if (Server != null)
                return Server;

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

            if (Database == null)
                return null;

            Database.ACR_SQLQuery(String.Format(
                "SELECT `Name`, `IPAddress` FROM `servers` WHERE `ID` = {0}",
                ServerId));

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

            Server = new GameServer(this);

            Server.ServerName = Database.ACR_SQLGetData(0);
            Server.ServerId = ServerId;
            Server.SetHostnameAndPort(Database.ACR_SQLGetData(1));

            InsertNewServer(Server, Database);

            return Server;
        }
        /// <summary>
        /// Reference the data for a server by the server name.  If the data
        /// was not yet available, it is retrieved from the database.
        /// </summary>
        /// <param name="ServerName">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 GameServer ReferenceServerByName(string ServerName, IALFADatabase Database)
        {
            //
            // Check if the object is already known first.
            //

            GameServer Server = (from S in Servers
                                 where S.ServerName.Equals(ServerName, StringComparison.InvariantCultureIgnoreCase)
                                 select S).FirstOrDefault();

            if (Server != null)
                return Server;

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

            if (Database == null)
                return null;

            Database.ACR_SQLQuery(String.Format(
                "SELECT `ID`, `IPAddress`, `Name` FROM `servers` WHERE `Name` = '{0}'",
                Database.ACR_SQLEncodeSpecialChars(ServerName)));

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

            Server = new GameServer(this);

            Server.ServerId = Convert.ToInt32(Database.ACR_SQLGetData(0));
            Server.SetHostnameAndPort(Database.ACR_SQLGetData(1));
            Server.ServerName = Database.ACR_SQLGetData(2);

            InsertNewServer(Server, Database);

            return Server;
        }
 /// <summary>
 /// Create a new RunScriptEvent.
 /// </summary>
 /// <param name="SourceServer">Supplies the sender server, which may be
 /// may be null if the request was not sent by a server but external
 /// automation.</param>
 /// <param name="ScriptName">Supplies the name of the script.</param>
 /// <param name="ScriptArgument">Supplies an optional argument for the
 /// script main function.</param>
 public RunScriptEvent(GameServer SourceServer, string ScriptName, string ScriptArgument)
 {
     this.SourceServer = SourceServer;
     this.ScriptName = ScriptName;
     this.ScriptArgument = ScriptArgument;
 }
        /// <summary>
        /// This method periodically notifies the player of the current portal
        /// status until the portal attempt completes (or is aborted).
        /// </summary>
        /// <param name="PlayerObjectId">Supplies the player to enlist in
        /// portal status update notifications.</param>
        /// <param name="Server">Supplies the destination server.</param>
        private void PortalStatusCheck(uint PlayerObjectId, GameServer Server)
        {
            DelayCommand(1.0f, delegate()
            {
                //
                // Bail out if the portal attempt is aborted, e.g. if we have
                // timed out.
                //

                if ((GetDatabase().ACR_GetPCLocalFlags(PlayerObjectId) & ALFA.Database.ACR_PC_LOCAL_FLAG_PORTAL_IN_PROGRESS) == 0)
                    return;

                //
                // If the character is no longer spooled, then complete the
                // portal sequence.
                //

                if (!IsCharacterSpooled(PlayerObjectId))
                {
                    //
                    // Force pending database writes relating to the player to
                    // complete.
                    //

                    GetDatabase().ACR_FlushQueryQueue(PlayerObjectId);

                    //
                    // Disable the next internal character save for this player
                    // object.  This prevents the autosave on logout from
                    // contending with the remote server's initial character
                    // read.
                    //
                    // N.B.  Normally, this is not a problem, as the server
                    //       vault subsystem uses file locking internally.  But
                    //       ALFA uses SSHFS, which does not support any sort
                    //       of file locking at all (all requestors are let on
                    //       through).
                    //
                    //       Thus, to avoid the remote server getting into a
                    //       state where it reads a character being transferred
                    //       from the final autosave on logout, we suppress the
                    //       final autosave.
                    //
                    //       Script-initiated saves are already suppressed by
                    //       the ACR_PC_LOCAL_FLAG_PORTAL_IN_PROGRESS PC local
                    //       flag bit, which just leaves the server's internal
                    //       save.
                    //

                    if (!DisableCharacterSave(PlayerObjectId))
                    {
                        SendMessageToPC(PlayerObjectId, "Unable to setup for server character transfer - internal error.  Please notify the tech team.");
                        SendMessageToPC(PlayerObjectId, "Aborting portal attempt due to error...");
                        GetDatabase().ACR_SetPCLocalFlags(
                            PlayerObjectId,
                            GetDatabase().ACR_GetPCLocalFlags(PlayerObjectId) & ~(ALFA.Database.ACR_PC_LOCAL_FLAG_PORTAL_IN_PROGRESS));
                        return;
                    }

                    //
                    // Now retrieve the portal configuration information from
                    // the data system and transfer the player.
                    //
                    // Note that there is no going back from this point.  If
                    // the client does not disconnect on its own, we will boot
                    // the client later on (because we can never know if the
                    // client would try and log on to the remote server or if
                    // it has given up after having sent the request).
                    //

                    SendMessageToPC(PlayerObjectId, "Transferring to server " + Server.Name + "...");

                    GetDatabase().ACR_SetPCLocalFlags(
                        PlayerObjectId,
                        GetDatabase().ACR_GetPCLocalFlags(PlayerObjectId) | ALFA.Database.ACR_PC_LOCAL_FLAG_PORTAL_COMMITTED);

                    lock (WorldManager)
                    {
                        ActivatePortal(
                            PlayerObjectId,
                            String.Format("{0}:{1}", Server.ServerHostname, Server.ServerPort),
                            WorldManager.Configuration.PlayerPassword,
                            "",
                            TRUE);
                    }

                    return;
                }

                //
                // Otherwise, send a notification to the player and start the
                // next continuation.
                //

                SendMessageToPC(PlayerObjectId, "Character transfer in progress...");
                PortalStatusCheck(PlayerObjectId, Server);
            });
        }
 /// <summary>
 /// Create a new ServerJoinEvent.
 /// </summary>
 /// <param name="Server">Supplies the server.</param>
 public ServerJoinEvent(GameServer Server)
 {
     this.Server = Server;
 }
        /// <summary>
        /// This method is called when a portal transition has been committed
        /// to send a player to a remote server.
        /// </summary>
        /// <param name="State">Supplies the local state for the player that is
        /// committed to a portal transition.
        /// </param>
        /// <param name="Server">Supplies the destination server for the portal
        /// event.</param>
        /// <param name="Script">Supplies the script object.</param>
        public static void SendGUIStateToServer(PlayerState State, GameServer Server, ACR_ServerCommunicator Script)
        {
            //
            // Build the resynchronization command.  The resynchronization
            // command is parsed by HandleGUIResync().  Note that the remote
            // and local servers may not be of the same version, i.e. the
            // remote server may not understand fields that are created by the
            // local server if it is of a newer version.
            //

            ResyncState ResyncInfo = new ResyncState(State.PlayerId, 0);

            if (State.ChatSelectGUIExpanded)
                ResyncInfo.ResyncFlags |= RESYNC_FLAG_CHAT_SELECT_EXPANDED;

            //
            // Dispatch the resync script execution request to the remote
            // server.  The remote server will process it when the request has
            // been received.
            //

            Script.RunScriptOnServer(Server.ServerId, "acr_resync_gui", ResyncInfo.ToString());
        }
 /// <summary>
 /// Create a new ServerPartEvent.
 /// </summary>
 /// <param name="Server">Supplies the server.</param>
 public ServerPartEvent(GameServer Server)
 {
     this.Server = Server;
 }
示例#24
0
 /// <summary>
 /// Create a new ServerPartEvent.
 /// </summary>
 /// <param name="Server">Supplies the server.</param>
 public ServerPartEvent(GameServer Server)
 {
     this.Server = Server;
 }