/// <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>
        /// This method is called to resynchronize the GUI state of a player
        /// that executed a server to server portal, if the player is in an
        /// area.
        /// </summary>
        /// <param name="ResyncInfo">Supplies the resync block for the
        /// player.  This contains the deserialized resynchronization command
        /// data.</param>
        /// <param name="PCObject">Supplies the player object id.</param>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Tries">Supplies the count of retries.</param>
        private static void ResynchronizePlayerState(ResyncState ResyncInfo, uint PCObject, ACR_ServerCommunicator Script, int Tries = 0)
        {
            if (Script.GetIsObjectValid(PCObject) == ACR_ServerCommunicator.FALSE)
            {
                //
                // The player logged out while a resync request was pending.
                // Throw away the state as it is no longer needed on a full log
                // out and log in sequence.
                //

                return;
            }

            if ((Script.GetArea(PCObject) == ACR_ServerCommunicator.OBJECT_INVALID) ||
                (Script.GetScriptHidden(PCObject) != ACR_ServerCommunicator.FALSE))
            {
                //
                // The player may still be in transition or is not yet loaded.
                // Queue the request.
                //

                if (Tries < MAX_RESYNC_RETRIES)
                {
                    Script.DelayCommand(RESYNC_RETRY_INTERVAL, delegate()
                    {
                        ResynchronizePlayerState(ResyncInfo, PCObject, Script, Tries + 1);
                    });
                }

                return;
            }

            PlayerState State = Script.TryGetPlayerState(PCObject);

            if (State == null)
            {
                return;
            }

            //
            // Area transition has finished.  Apply the GUI state now.
            //

            State.ChatSelectGUIExpanded = ((ResyncInfo.ResyncFlags & RESYNC_FLAG_CHAT_SELECT_EXPANDED) != 0);
            State.UpdateChatSelectGUIHeaders();

            Script.SendMessageToPC(PCObject, "Server to server portal completed.");
            Script.WriteTimestampedLogEntry(String.Format(
                                                "ACR_ServerCommunicator.GUIResynchronizer.ResynchronizePlayerState: Resynchronized player GUI state for player {0} after server-to-server portal.",
                                                Script.GetName(PCObject)));
        }
            /// <summary>
            /// Deserialize the resynchronization state from a string.
            /// </summary>
            /// <param name="ResyncCommand">Supplies the state string that was
            /// constructed by a call to ToString().</param>
            /// <returns>A new ResyncState object containing the deserialized
            /// contents, else null on failure.  An exception may also be
            /// raised on a failure that is the result of an unexpected
            /// protocol violation.</returns>
            public static ResyncState FromString(string ResyncCommand)
            {
                string[] Tokens = ResyncCommand.Split(new char[] { ':' });

                if (Tokens == null || Tokens.Length < 1)
                    return null;

                ResyncState State = new ResyncState(Convert.ToInt32(Tokens[0]), 0);

                if (Tokens.Length >= 1)
                    State.ResyncFlags = Convert.ToUInt32(Tokens[1]);

                return State;
            }
            /// <summary>
            /// Deserialize the resynchronization state from a string.
            /// </summary>
            /// <param name="ResyncCommand">Supplies the state string that was
            /// constructed by a call to ToString().</param>
            /// <returns>A new ResyncState object containing the deserialized
            /// contents, else null on failure.  An exception may also be
            /// raised on a failure that is the result of an unexpected
            /// protocol violation.</returns>
            public static ResyncState FromString(string ResyncCommand)
            {
                string[] Tokens = ResyncCommand.Split(new char[] { ':' });

                if (Tokens == null || Tokens.Length < 1)
                {
                    return(null);
                }

                ResyncState State = new ResyncState(Convert.ToInt32(Tokens[0]), 0);

                if (Tokens.Length >= 1)
                {
                    State.ResyncFlags = Convert.ToUInt32(Tokens[1]);
                }

                return(State);
            }
        /// <summary>
        /// This method is called to resynchronize the GUI state of a player
        /// that executed a server to server portal, if the player is in an
        /// area.
        /// </summary>
        /// <param name="ResyncInfo">Supplies the resync block for the
        /// player.  This contains the deserialized resynchronization command
        /// data.</param>
        /// <param name="PCObject">Supplies the player object id.</param>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Tries">Supplies the count of retries.</param>
        private static void ResynchronizePlayerState(ResyncState ResyncInfo, uint PCObject, ACR_ServerCommunicator Script, int Tries = 0)
        {
            if (Script.GetIsObjectValid(PCObject) == ACR_ServerCommunicator.FALSE)
            {
                //
                // The player logged out while a resync request was pending.
                // Throw away the state as it is no longer needed on a full log
                // out and log in sequence.
                //

                return;
            }

            if ((Script.GetArea(PCObject) == ACR_ServerCommunicator.OBJECT_INVALID) ||
                (Script.GetScriptHidden(PCObject) != ACR_ServerCommunicator.FALSE))
            {
                //
                // The player may still be in transition or is not yet loaded.
                // Queue the request.
                //

                if (Tries < MAX_RESYNC_RETRIES)
                {
                    Script.DelayCommand(RESYNC_RETRY_INTERVAL, delegate()
                    {
                        ResynchronizePlayerState(ResyncInfo, PCObject, Script, Tries + 1);
                    });
                }

                return;
            }

            PlayerState State = Script.TryGetPlayerState(PCObject);

            if (State == null)
                return;

            //
            // Area transition has finished.  Apply the GUI state now.
            //

            State.ChatSelectGUIExpanded = ((ResyncInfo.ResyncFlags & RESYNC_FLAG_CHAT_SELECT_EXPANDED) != 0);
            State.UpdateChatSelectGUIHeaders();

            Script.SendMessageToPC(PCObject, "Server to server portal completed.");
            Script.WriteTimestampedLogEntry(String.Format(
                "ACR_ServerCommunicator.GUIResynchronizer.ResynchronizePlayerState: Resynchronized player GUI state for player {0} after server-to-server portal.",
                Script.GetName(PCObject)));
        }
        /// <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>
 /// Converts the <see cref="sourceValue" /> parameter to the <see cref="destinationType" /> parameter using <see cref="formatProvider"
 /// /> and <see cref="ignoreCase" />
 /// </summary>
 /// <param name="sourceValue">the <see cref="System.Object"/> to convert from</param>
 /// <param name="destinationType">the <see cref="System.Type" /> to convert to</param>
 /// <param name="formatProvider">not used by this TypeConverter.</param>
 /// <param name="ignoreCase">when set to <c>true</c>, will ignore the case when converting.</param>
 /// <returns>
 /// an instance of <see cref="ResyncState" />, or <c>null</c> if there is no suitable conversion.
 /// </returns>
 public override object ConvertFrom(object sourceValue, global::System.Type destinationType, global::System.IFormatProvider formatProvider, bool ignoreCase) => ResyncState.CreateFrom(sourceValue);
        /// <summary>
        /// This method is called when a cross-server chat select
        /// resynchronization request is received.  Its purpose is to scan the
        /// player list to check whether the argument player is online, and to
        /// activate chat select GUI resynchronization if appropriate, else to
        /// queue the resynchronization until the player does log in.
        /// </summary>
        /// <param name="SourceServerId">Supplies the server id of the server
        /// that requested the GUI resynchronization.</param>
        /// <param name="ResyncCommand">Supplies the GUI resynchronizer command
        /// line as generated by SendGUIStateToServer().</param>
        /// <param name="Script">Supplies the current script object.</param>
        /// <returns>TRUE on success, else FALSE on failure.</returns>
        public static int HandleGUIResync(int SourceServerId, string ResyncCommand, ACR_ServerCommunicator Script)
        {
            try
            {
                ResyncState RemoteResyncInfo;

                //
                // Deserialize the remote resynchronization state.  On null, an
                // obviously invalid request was received.  Otherwise a protocol
                // violation that is atypical was received and an exception is
                // raised.
                //

                if ((RemoteResyncInfo = ResyncState.FromString(ResyncCommand)) == null)
                {
                    Script.WriteTimestampedLogEntry(String.Format(
                                                        "ACR_ServerCommunicator.GUIResynchronizer.HandleGUIResync({0}, {1}): Invalid request.",
                                                        SourceServerId,
                                                        ResyncCommand));
                    return(ACR_ServerCommunicator.FALSE);
                }

                //
                // If a request was not already enqueued for this player, then
                // enqueue it.
                //

                var ResyncInfo = (from RS in PlayerResyncStates
                                  where RS.PlayerId == RemoteResyncInfo.PlayerId
                                  select RS).FirstOrDefault();

                if (ResyncInfo == null)
                {
                    //
                    // If the player is logged on, directly enqueue the execute
                    // request now.  Otherwise, wait for the ClientEnter event
                    // as the player might still reside on the remote server.
                    //

                    foreach (uint PCObject in Script.GetPlayers(true))
                    {
                        PlayerState State;

                        if ((State = Script.TryGetPlayerState(PCObject)) == null)
                        {
                            continue;
                        }

                        if (State.PlayerId != RemoteResyncInfo.PlayerId)
                        {
                            continue;
                        }

                        ResynchronizePlayerState(RemoteResyncInfo, PCObject, Script);

                        return(ACR_ServerCommunicator.TRUE);
                    }

                    //
                    // Enqueue the request so that it can be processed at
                    // ClientEnter time.
                    //

                    PlayerResyncStates.Add(RemoteResyncInfo);
                }
                else
                {
                    //
                    // Update the resync flags to match the new request.
                    //

                    ResyncInfo.ResyncFlags = RemoteResyncInfo.ResyncFlags;
                }
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                                                    "ACR_ServerCommunicator.GUIResynchronizer.HandleGUIResync({0}, {1}): Exception: {0}",
                                                    SourceServerId,
                                                    ResyncCommand,
                                                    e));
                return(ACR_ServerCommunicator.FALSE);
            }

            return(ACR_ServerCommunicator.TRUE);
        }