/// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                if (Database.ACR_GetPlayerID(PlayerObject) != Player.PlayerId)
                    continue;

                Database.ACR_IncrementStatistic("DISCONNECT_PLAYER");
                Script.WriteTimestampedLogEntry("DisconnectPlayerEvent.DispatchEvent: Disconnecting player " + Script.GetPCPlayerName(PlayerObject) + " due to IPC request.");
                Script.BootPC(PlayerObject);
                return;
            }

            Script.WriteTimestampedLogEntry("DisconnectPlayerEvent.DispatchEvent: No player '" + Player.PlayerName + "' found locally connected to disconnect.");
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                string FormattedMessage = String.Format(
                    "</c><c=#FFFF00>{0}</c>",
                    Message);

                NWScript.Vector3 v;
                v.x = v.y = v.z = 0.0f;

                Script.SendChatMessage(
                    CLRScriptBase.OBJECT_INVALID,
                    PlayerObject,
                    CLRScriptBase.CHAT_MODE_SERVER,
                    FormattedMessage,
                    CLRScriptBase.FALSE);

                Script.FloatingTextStringOnCreature(FormattedMessage,
                                                    PlayerObject,
                                                    CLRScriptBase.FALSE,
                                                    5.0f,
                                                    CLRScriptBase.COLOR_WHITE,
                                                    CLRScriptBase.COLOR_WHITE,
                                                    0.0f,
                                                    v);
            }

            Database.ACR_IncrementStatistic("BROADCAST_MESSAGE");
            Script.WriteTimestampedLogEntry("Received broadcast notification: " + Message);
        }
Example #3
0
        /// <summary>
        /// Initialize the socket subsystem.
        /// </summary>
        /// <param name="Script">Supplies the main script object.</param>
        /// <returns>True if socket I/O is connected.</returns>
        public static bool Initialize(ACR_ServerCommunicator Script)
        {
            if (Initialized)
            {
                return(true);
            }

            try
            {
                RecvfromDelegate       = new RecvfromCallout(OnLowLevelReceive);
                RecvfromDelegateHandle = GCHandle.Alloc(RecvfromDelegate);

                try
                {
                    SetRecvfromCallout(Marshal.GetFunctionPointerForDelegate(RecvfromDelegate));
                }
                catch (Exception e)
                {
                    Script.WriteTimestampedLogEntry(String.Format("SocketIo.Initialize: Exception: {0}", e));
                    RecvfromDelegate = null;
                    RecvfromDelegateHandle.Free();
                    throw;
                }
            }
            catch
            {
                return(false);
            }

            Initialized = true;
            return(true);
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                string FormattedMessage = String.Format(
                    "</c><c=#FFFF00>{0}</c>",
                    Message);

                NWScript.Vector3 v;
                v.x = v.y = v.z = 0.0f;

                Script.SendChatMessage(
                    CLRScriptBase.OBJECT_INVALID,
                    PlayerObject,
                    CLRScriptBase.CHAT_MODE_SERVER,
                    FormattedMessage,
                    CLRScriptBase.FALSE);

                Script.FloatingTextStringOnCreature(FormattedMessage,
                    PlayerObject,
                    CLRScriptBase.FALSE,
                    5.0f,
                    CLRScriptBase.COLOR_WHITE,
                    CLRScriptBase.COLOR_WHITE,
                    0.0f,
                    v);
            }

            Database.ACR_IncrementStatistic("BROADCAST_MESSAGE");
            Script.WriteTimestampedLogEntry("Received broadcast notification: " + Message);
        }
        /// <summary>
        /// Initialize the socket subsystem.
        /// </summary>
        /// <param name="Script">Supplies the main script object.</param>
        /// <returns>True if socket I/O is connected.</returns>
        public static bool Initialize(ACR_ServerCommunicator Script)
        {
            if (Initialized)
                return true;

            try
            {
                RecvfromDelegate = new RecvfromCallout(OnLowLevelReceive);
                RecvfromDelegateHandle = GCHandle.Alloc(RecvfromDelegate);

                try
                {
                    SetRecvfromCallout(Marshal.GetFunctionPointerForDelegate(RecvfromDelegate));
                }
                catch (Exception e)
                {
                    Script.WriteTimestampedLogEntry(String.Format("SocketIo.Initialize: Exception: {0}", e));
                    RecvfromDelegate = null;
                    RecvfromDelegateHandle.Free();
                    throw;
                }
            }
            catch
            {
                return false;
            }

            Initialized = true;
            return true;
        }
Example #6
0
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                if (Database.ACR_GetPlayerID(PlayerObject) != Player.PlayerId)
                {
                    continue;
                }

                Database.ACR_IncrementStatistic("DISCONNECT_PLAYER");
                Script.WriteTimestampedLogEntry("DisconnectPlayerEvent.DispatchEvent: Disconnecting player " + Script.GetPCPlayerName(PlayerObject) + " due to IPC request.");
                Script.BootPC(PlayerObject);
                return;
            }

            Script.WriteTimestampedLogEntry("DisconnectPlayerEvent.DispatchEvent: No player '" + Player.PlayerName + "' found locally connected to disconnect.");
        }
Example #7
0
        /// <summary>
        /// This when a cross-server ping response is received.  Its purpose is
        /// to compute the channel latency and send a message to that effect to
        /// the requesting player.
        /// </summary>
        /// <param name="SourceServerId">Supplies the server id of the server
        /// that completed the ping request.</param>
        /// <param name="Arguments">Supplies the serialized state arguments
        /// string.</param>
        /// <param name="Script">Supplies the current script object.</param>
        /// <returns>TRUE on success, else FALSE on failure.</returns>
        public static int HandleServerPingResponse(int SourceServerId, string Arguments, ACR_ServerCommunicator Script)
        {
            int Tick = Environment.TickCount;

            try
            {
                PingState RemoteState;

                //
                // Deserialize the remote ping state.  If null, a protocol
                // violation has occurred.
                //

                if ((RemoteState = PingState.FromString(Arguments)) == null)
                {
                    Script.WriteTimestampedLogEntry(String.Format(
                                                        "ACR_ServerCommunicator.ServerLatencyMeasurer.HandleServerPingResponse({0}, {1}): Invalid request.",
                                                        SourceServerId,
                                                        Arguments));
                    return(ACR_ServerCommunicator.FALSE);
                }

                string ServerName = Script.GetServerName(SourceServerId);

                if (ServerName == null)
                {
                    return(ACR_ServerCommunicator.FALSE);
                }

                Script.SendMessageToPC(RemoteState.PCObjectId, String.Format(
                                           "IPC channel latency to {0}: {1}ms", ServerName, Tick - RemoteState.TickCount));
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                                                    "ACR_ServerCommunicator.ServerLatencyMeasurer.HandleServerPingResponse({0}, {1}): Exception: {0}",
                                                    SourceServerId,
                                                    Arguments,
                                                    e));
                return(ACR_ServerCommunicator.FALSE);
            }

            return(ACR_ServerCommunicator.TRUE);
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            string VaultPath = SystemInfo.GetServerVaultPathForAccount(Player.Name);

            if (VaultPath == null)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                    "PurgeCachedCharacterEvent.DispatchEvent: Could not resolve vault path for player '{0}'.",
                    Player.PlayerName));
                return;
            }

            VaultPath += CharacterFileName;

            if (!File.Exists(VaultPath))
            {
                Script.WriteTimestampedLogEntry(String.Format(
                    "PurgeCachedCharacterEvent.DispatchEvent: Character file '{0}' for player '{1}' was not locally cached.",
                    CharacterFileName,
                    Player.PlayerName));
                return;
            }

            try
            {
                File.Delete(VaultPath);
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                    "PurgeCachedCharacterEvent.DispatchEvent: Exception '{0}' removing cached character '{1}' for player '{2}'.",
                    e,
                    CharacterFileName,
                    Player.PlayerName));
                return;
            }

            Script.WriteTimestampedLogEntry(String.Format(
                "PurgeCachedCharacterEvent.DispatchEvent: Removed cached character '{0}' from player '{1}' vault cache.",
                CharacterFileName,
                Player.PlayerName));
        }
        /// <summary>
        /// Run the event queue down.  All events in the queue are given a
        /// chance to run.
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void RunQueue(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
#if DEBUG_MODE
            Script.WriteTimestampedLogEntry(String.Format("ACR_ServerCommunicator: Running queue of {0} entries", EventQueue.Count));
#endif

            while (!Empty())
            {
                IGameEventQueueEvent Event = EventQueue.Dequeue();

                Event.DispatchEvent(Script, Database);
            }
        }
Example #10
0
        /// <summary>
        /// Run the event queue down.  All events in the queue are given a
        /// chance to run.
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void RunQueue(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
#if DEBUG_MODE
            Script.WriteTimestampedLogEntry(String.Format("ACR_ServerCommunicator: Running queue of {0} entries", EventQueue.Count));
#endif

            while (!Empty())
            {
                IGameEventQueueEvent Event = EventQueue.Dequeue();

                Event.DispatchEvent(Script, Database);
            }
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            string FormattedMessage = String.Format(
                "ACR_ServerCommunicator: Received unsupported IPC event {0}: {1}.{2}.{3}.{4}.  Record ID was {5}.",
                EventType,
                P0,
                P1,
                P2,
                P3,
                RecordId);

            Script.WriteTimestampedLogEntry(FormattedMessage);
        }
Example #12
0
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            string FormattedMessage = String.Format(
                "ACR_ServerCommunicator: Received unsupported IPC event {0}: {1}.{2}.{3}.{4}.  Record ID was {5}.",
                EventType,
                P0,
                P1,
                P2,
                P3,
                RecordId);

            Script.WriteTimestampedLogEntry(FormattedMessage);
        }
        /// <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)));
        }
Example #14
0
        /// <summary>
        /// Download a content patch file from a file store.  Currently, Azure
        /// file stores are assumed.  Both compressed and uncompressed versions
        /// of the file are tried in respective order.
        /// </summary>
        /// <param name="FileStorePath">Supplies the remote file name that
        /// designates the file to download.</param>
        /// <param name="LocalFileName">Supplies the local file name to
        /// download to.</param>
        /// <param name="ConnectionString">Supplies the file store connection
        /// string.</param>
        /// <param name="Script">Supplies the script object.</param>
        private static void DownloadContentPatchFromFileStore(string FileStorePath, string LocalFileName, string ConnectionString, ACR_ServerCommunicator Script)
        {
            if (String.IsNullOrEmpty(ConnectionString))
            {
                throw new NotSupportedException();
            }

            //
            // Initialize the file store provider.
            //

            FileStore          UpdaterStore     = FileStoreProvider.CreateAzureFileStore(ConnectionString);
            FileStoreContainer UpdaterContainer = UpdaterStore.GetContainerReference(FileStoreNamespace.ACRUpdater);
            FileStoreFile      UpdaterFile      = UpdaterContainer.GetFileReference(FileStorePath + ".gzip");

            //
            // First attempt to retrieve a gzip compressed version of the file
            // to patch.  If that fails then fall back to a plaintext version.
            //

            try
            {
                using (MemoryStream MemStream = new MemoryStream())
                {
                    UpdaterFile.Read(MemStream);

                    MemStream.Position = 0;

                    using (FileStream OutStream = File.Create(LocalFileName))
                    {
                        using (GZipStream CompressedStream = new GZipStream(MemStream, CompressionMode.Decompress))
                        {
                            CompressedStream.CopyTo(OutStream);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format("ModuleContentPatcher.DownloadContentPatchFromFileStore: Couldn't retrieve compressed file {0} from Azure, trying uncompressed file, due to exception: {1}",
                                                              FileStorePath,
                                                              e));

                UpdaterFile = UpdaterContainer.GetFileReference(FileStorePath);

                using (FileStream OutStream = File.Create(LocalFileName))
                {
                    UpdaterFile.Read(OutStream);
                }
            }
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            int SourceServerId = (SourceServer != null) ? SourceServer.ServerId : 0;

            Script.WriteTimestampedLogEntry(String.Format("RunScriptEvent.DispatchEvent: Executing script {0} ({1}) on request from {2}.",
                ScriptName,
                ScriptArgument,
                SourceServerId));
            Script.AddScriptParameterInt(SourceServerId);
            Script.AddScriptParameterString(ScriptArgument);
            Script.ExecuteScriptEnhanced(ScriptName, Script.GetModule(), CLRScriptBase.TRUE);

            Database.ACR_IncrementStatistic("RUN_REMOTE_SCRIPT");
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            //
            // If the event was for the local server, then don't re-broadcast
            // it.
            //

            if (Database.ACR_GetServerID() == Server.ServerId)
            {
                return;
            }

            string Message = String.Format(
                "<c=#FFFF00>Server {0} is now online.</c>",
                Server.Name);
            string ChatMessage = "</c>" + Message;

            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                PlayerState Player = Script.TryGetPlayerState(PlayerObject);

                if (Player == null)
                {
                    continue;
                }

                if (!Script.IsCrossServerNotificationEnabled(PlayerObject))
                {
                    continue;
                }

                if ((Player.Flags & PlayerStateFlags.SendCrossServerNotificationsToCombatLog) != 0)
                {
                    Script.SendMessageToPC(PlayerObject, Message);
                }
                else
                {
                    Script.SendChatMessage(
                        CLRScriptBase.OBJECT_INVALID,
                        PlayerObject,
                        CLRScriptBase.CHAT_MODE_SERVER,
                        ChatMessage,
                        CLRScriptBase.FALSE);
                }
            }

#if DEBUG_MODE
            Script.WriteTimestampedLogEntry(Message);
#endif
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            int SourceServerId = (SourceServer != null) ? SourceServer.ServerId : 0;

            Script.WriteTimestampedLogEntry(String.Format("RunScriptEvent.DispatchEvent: Executing script {0} ({1}) on request from {2}.",
                                                          ScriptName,
                                                          ScriptArgument,
                                                          SourceServerId));
            Script.ClearScriptParams();
            Script.AddScriptParameterInt(SourceServerId);
            Script.AddScriptParameterString(ScriptArgument);
            Script.ExecuteScriptEnhanced(ScriptName, Script.GetModule(), CLRScriptBase.TRUE);

            Database.ACR_IncrementStatistic("RUN_REMOTE_SCRIPT");
        }
Example #18
0
        /// <summary>
        /// Initialize the server vault connector subsystem.
        /// </summary>
        /// <param name="Script">Supplies the main script object.</param>
        /// <param name="ConnectionString">Supplies the connection string for
        /// the server vault.</param>
        /// <param name="VerboseLogging">Supplies true if verbose logging is
        /// enabled.</param>
        /// <returns>True if the server vault connector is
        /// initialized.</returns>
        public static bool Initialize(ACR_ServerCommunicator Script, string ConnectionString, bool VerboseLogging)
        {
            if (Initialized)
            {
                return(true);
            }

            try
            {
                StoreConnectionString = ConnectionString;

                FileStoreProvider.DefaultVaultConnectionString = ConnectionString;

                SynchronizeAccountFromVaultDelegate = new SynchronizeAccountFromVault(OnSynchronizeAccountFromVault);
                SynchronizeAccountFromVaultHandle   = GCHandle.Alloc(SynchronizeAccountFromVaultDelegate);

                SynchronizeAccountFileToVaultDelegate = new SynchronizeAccountFileToVault(OnSynchronizeAccountFileToVault);
                SynchronizeAccountFileToVaultHandle   = GCHandle.Alloc(SynchronizeAccountFileToVaultDelegate);

                try
                {
                    if (SetStoragePluginCallbacks(Marshal.GetFunctionPointerForDelegate(SynchronizeAccountFromVaultDelegate),
                                                  Marshal.GetFunctionPointerForDelegate(SynchronizeAccountFileToVaultDelegate),
                                                  IntPtr.Zero) == 0)
                    {
                        throw new ApplicationException("Failed to install storage plugin callbacks.");
                    }
                }
                catch (Exception e)
                {
                    Script.WriteTimestampedLogEntry(String.Format("ServerVaultConnector.Initialize: Exception: {0}", e));
                    SynchronizeAccountFileToVaultDelegate = null;
                    SynchronizeAccountFileToVaultHandle.Free();
                    SynchronizeAccountFromVaultDelegate = null;
                    SynchronizeAccountFromVaultHandle.Free();
                    throw;
                }
            }
            catch
            {
                return(false);
            }

            Initialized = true;
            return(true);
        }
        /// <summary>
        /// Initialize the server vault connector subsystem.
        /// </summary>
        /// <param name="Script">Supplies the main script object.</param>
        /// <param name="ConnectionString">Supplies the connection string for
        /// the server vault.</param>
        /// <param name="VerboseLogging">Supplies true if verbose logging is
        /// enabled.</param>
        /// <returns>True if the server vault connector is
        /// initialized.</returns>
        public static bool Initialize(ACR_ServerCommunicator Script, string ConnectionString, bool VerboseLogging)
        {
            if (Initialized)
                return true;

            try
            {
                StoreConnectionString = ConnectionString;

                FileStoreProvider.DefaultVaultConnectionString = ConnectionString;

                SynchronizeAccountFromVaultDelegate = new SynchronizeAccountFromVault(OnSynchronizeAccountFromVault);
                SynchronizeAccountFromVaultHandle = GCHandle.Alloc(SynchronizeAccountFromVaultDelegate);

                SynchronizeAccountFileToVaultDelegate = new SynchronizeAccountFileToVault(OnSynchronizeAccountFileToVault);
                SynchronizeAccountFileToVaultHandle = GCHandle.Alloc(SynchronizeAccountFileToVaultDelegate);

                try
                {
                    if (SetStoragePluginCallbacks(Marshal.GetFunctionPointerForDelegate(SynchronizeAccountFromVaultDelegate),
                        Marshal.GetFunctionPointerForDelegate(SynchronizeAccountFileToVaultDelegate),
                        IntPtr.Zero) == 0)
                    {
                        throw new ApplicationException("Failed to install storage plugin callbacks.");
                    }
                }
                catch (Exception e)
                {
                    Script.WriteTimestampedLogEntry(String.Format("ServerVaultConnector.Initialize: Exception: {0}", e));
                    SynchronizeAccountFileToVaultDelegate = null;
                    SynchronizeAccountFileToVaultHandle.Free();
                    SynchronizeAccountFromVaultDelegate = null;
                    SynchronizeAccountFromVaultHandle.Free();
                    throw;
                }
            }
            catch
            {
                return false;
            }

            Initialized = true;
            return true;
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            //
            // If the event was for the local server, then don't re-broadcast
            // it.
            //

            if (Database.ACR_GetServerID() == Server.ServerId)
                return;

            string Message = String.Format(
                "<c=#FFFF00>Server {0} is now offline.</c>",
                Server.Name);
            string ChatMessage = "</c>" + Message;

            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                PlayerState Player = Script.TryGetPlayerState(PlayerObject);

                if (Player == null)
                    continue;

                if (!Script.IsCrossServerNotificationEnabled(PlayerObject))
                    continue;

                if ((Player.Flags & PlayerStateFlags.SendCrossServerNotificationsToCombatLog) != 0)
                {
                    Script.SendMessageToPC(PlayerObject, Message);
                }
                else
                {
                    Script.SendChatMessage(
                        CLRScriptBase.OBJECT_INVALID,
                        PlayerObject,
                        CLRScriptBase.CHAT_MODE_SERVER,
                        ChatMessage,
                        CLRScriptBase.FALSE);
                }
            }

#if DEBUG_MODE
            Script.WriteTimestampedLogEntry(Message);
#endif
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                string FormattedMessage = String.Format(
                    "</c><c=#FFFF00>Server shutting down: {0}</c>",
                    Message);

                NWScript.Vector3 v;
                v.x = v.y = v.z = 0.0f;

                Script.SendChatMessage(
                    CLRScriptBase.OBJECT_INVALID,
                    PlayerObject,
                    CLRScriptBase.CHAT_MODE_SERVER,
                    FormattedMessage,
                    CLRScriptBase.FALSE);

                Script.FloatingTextStringOnCreature(FormattedMessage,
                    PlayerObject,
                    CLRScriptBase.FALSE,
                    5.0f,
                    CLRScriptBase.COLOR_WHITE,
                    CLRScriptBase.COLOR_WHITE,
                    0.0f,
                    v);
            }

            Script.WriteTimestampedLogEntry("Received shutdown request: " + Message);

            Database.ACR_IncrementStatistic("SERVER_SHUTDOWN");
            Script.SendInfrastructureIrcMessage(String.Format(
                "Server '{0}' shutting down or restarting: {1}",
                Script.GetName(Script.GetModule()),
                Message));

            Script.DelayCommand(5.0f, delegate()
            {
                Database.ACR_FlushAllQueryQueues();
                SystemInfo.ShutdownGameServer(Script);
            });
        }
Example #22
0
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                string FormattedMessage = String.Format(
                    "</c><c=#FFFF00>Server shutting down: {0}</c>",
                    Message);

                NWScript.Vector3 v;
                v.x = v.y = v.z = 0.0f;

                Script.SendChatMessage(
                    CLRScriptBase.OBJECT_INVALID,
                    PlayerObject,
                    CLRScriptBase.CHAT_MODE_SERVER,
                    FormattedMessage,
                    CLRScriptBase.FALSE);

                Script.FloatingTextStringOnCreature(FormattedMessage,
                                                    PlayerObject,
                                                    CLRScriptBase.FALSE,
                                                    5.0f,
                                                    CLRScriptBase.COLOR_WHITE,
                                                    CLRScriptBase.COLOR_WHITE,
                                                    0.0f,
                                                    v);
            }

            Script.WriteTimestampedLogEntry("Received shutdown request: " + Message);

            Database.ACR_IncrementStatistic("SERVER_SHUTDOWN");
            Script.SendInfrastructureIrcMessage(String.Format(
                                                    "Server '{0}' shutting down or restarting: {1}",
                                                    Script.GetName(Script.GetModule()),
                                                    Message));

            Script.DelayCommand(5.0f, delegate()
            {
                Database.ACR_FlushAllQueryQueues();
                SystemInfo.ShutdownGameServer(Script);
            });
        }
        /// <summary>
        /// Process any updates to moduledownloaderresources.xml.
        /// </summary>
        /// <param name="Database">Supplies the database object.</param>
        /// <param name="Script">Supplies the script object.</param>
        /// <returns>True if a patch was applied and a reboot is required for
        /// it to take effect.</returns>
        public static bool ProcessModuleDownloaderResourcesUpdates(ALFA.Database Database, ACR_ServerCommunicator Script)
        {
            bool ContentChanged = false;

            //
            // Check the database for the expected download server configurations.
            //

            Database.ACR_SQLQuery("SELECT `Hash`, `DownloadHash`, `Name`, `DLSize`, `Size` FROM `content_download_config`");

            List<DownloadConfiguration> hakConfigs = new List<DownloadConfiguration>();

            //
            // Build a list of expected download configurations.
            //

            while (Database.ACR_SQLFetch())
            {
                DownloadConfiguration downloadConfig = new DownloadConfiguration();

                downloadConfig.Hash = Database.ACR_SQLGetData(0);
                downloadConfig.DownloadHash = Database.ACR_SQLGetData(1);
                downloadConfig.Name = Database.ACR_SQLGetData(2);
                downloadConfig.DLSize = Database.ACR_SQLGetData(3);
                downloadConfig.Size = Database.ACR_SQLGetData(4);

                if (String.IsNullOrEmpty(downloadConfig.Hash) ||
                    String.IsNullOrEmpty(downloadConfig.DownloadHash) ||
                    String.IsNullOrEmpty(downloadConfig.Name) ||
                    String.IsNullOrEmpty(downloadConfig.DLSize) ||
                    String.IsNullOrEmpty(downloadConfig.Size))
                {
                    continue;
                }

                hakConfigs.Add(downloadConfig);
            }

            //
            // If we have any configuration to do, we loop through and compare the 
            // configuration on the database with the configuration on the server,
            // updating the downloadresources xml if it is new.
            //

            if (hakConfigs.Count > 0)
            {
                XmlDocument moduleDownloadResources = new XmlDocument();
                moduleDownloadResources.Load(ALFA.SystemInfo.GetModuleDirectory() + "\\moduledownloaderresources.xml");
                XmlElement downloadResources = moduleDownloadResources.DocumentElement;

                foreach (DownloadConfiguration config in hakConfigs)
                {
                    foreach (XmlNode node in downloadResources.ChildNodes)
                    {
                        if (node.Attributes["name"].Value == config.Name)
                        {
                            if (node.Attributes["hash"].Value != config.Hash)
                            {
                                node.Attributes["hash"].Value = config.Hash;
                                ContentChanged = true;
                            }
                            if (node.Attributes["downloadHash"].Value != config.DownloadHash)
                            {
                                node.Attributes["downloadHash"].Value = config.DownloadHash;
                                ContentChanged = true;
                            }
                            if (node.Attributes["dlsize"].Value != config.DLSize)
                            {
                                node.Attributes["dlsize"].Value = config.DLSize;
                                ContentChanged = true;
                            }
                            if (node.Attributes["size"].Value != config.Size)
                            {
                                node.Attributes["size"].Value = config.Size;
                                ContentChanged = true;
                            }

                            if (ContentChanged)
                            {
                                Script.WriteTimestampedLogEntry(String.Format(
                                    "ModuleContentPatcher.ProcessModuleDownloaderResourcesUpdates: Updated downloader resource {0} (hash {1}).",
                                    config.Name,
                                    config.Hash));
                            }
                        }
                    }
                }

                if (ContentChanged)
                    moduleDownloadResources.Save(ALFA.SystemInfo.GetModuleDirectory() + "\\moduledownloaderresources.xml");
            }

            return ContentChanged;
        }
        /// <summary>
        /// Recompile all scripts in the module.
        /// </summary>
        /// <param name="Script">Supplies the main script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        private static void CompileModuleScripts(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            ALFA.ScriptCompiler.CompilerResult Result;
            string CompilerOptions = Script.GetLocalString(Script.GetModule(), "ACR_MOD_COMPILER_OPTIONS");
            List<string> CompilerOutput = new List<string>();

            Script.WriteTimestampedLogEntry(String.Format(
                "ModuleContentPatcher.CompileModuleScripts: Compiling module scripts with compiler options '{0}'...", CompilerOptions));
            Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                "Server '{0}' is recompiling module scripts.",
                Script.GetName(Script.GetModule())));

            Result = ALFA.ScriptCompiler.CompileScript("*.nss", CompilerOptions, delegate(string Line)
            {
                if (!String.IsNullOrWhiteSpace(Line))
                {
                    Script.WriteTimestampedLogEntry(Line);
                }
                return false;
            });

            foreach (string Message in Result.Warnings)
            {
                try
                {
                    CompilerOutput.Add(Message);
                }
                catch (Exception)
                {
                }
            }

            if (Result.Compiled)
            {
                Script.WriteTimestampedLogEntry("ModuleContentPatcher.CompileModuleScripts: Module successfully recompiled.");
                Database.ACR_IncrementStatistic("CONTENT_PATCH_RECOMPILE");

                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                    "Server '{0}' successfully recompiled module with {1} warning(s) for content patch deployment.",
                    Script.GetName(Script.GetModule()),
                    Result.Warnings.Count));
            }
            else
            {
                Script.WriteTimestampedLogEntry(String.Format(
                    "ModuleContentPatcher.CompileModuleScripts: {0} error(s) compiling module!", Result.Errors.Count));

                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                    "Server '{0}' had {1} error(s), {2} warning(s) recompiling module for content patch deployment.",
                    Script.GetName(Script.GetModule()),
                    Result.Errors.Count,
                    Result.Warnings.Count));

                foreach (string Message in Result.Errors)
                {
                    Script.WriteTimestampedLogEntry(String.Format(
                        "ModuleContentPatcher.CompileModuleScripts: Error '{0}'.", Message));

                    try
                    {
                        CompilerOutput.Add(Message);
                    }
                    catch (Exception)
                    {
                    }
                }

                Database.ACR_IncrementStatistic("CONTENT_PATCH_RECOMPILE_FAILED");
            }

            //
            // Save compiler output to a temporary file for later retrieval.
            //

            try
            {
                string FileName = String.Format("{0}{1}ALFAModuleRecompile.log", Path.GetTempPath(), Path.DirectorySeparatorChar);

                File.WriteAllLines(FileName, CompilerOutput);
            }
            catch (Exception)
            {

            }

        }
        /// <summary>
        /// Download a content patch file from a file store.  Currently, Azure
        /// file stores are assumed.  Both compressed and uncompressed versions
        /// of the file are tried in respective order.
        /// </summary>
        /// <param name="FileStorePath">Supplies the remote file name that
        /// designates the file to download.</param>
        /// <param name="LocalFileName">Supplies the local file name to
        /// download to.</param>
        /// <param name="ConnectionString">Supplies the file store connection
        /// string.</param>
        /// <param name="Script">Supplies the script object.</param>
        private static void DownloadContentPatchFromFileStore(string FileStorePath, string LocalFileName, string ConnectionString, ACR_ServerCommunicator Script)
        {
            if (String.IsNullOrEmpty(ConnectionString))
                throw new NotSupportedException();

            //
            // Initialize the file store provider.
            //

            FileStore UpdaterStore = FileStoreProvider.CreateAzureFileStore(ConnectionString);
            FileStoreContainer UpdaterContainer = UpdaterStore.GetContainerReference(FileStoreNamespace.ACRUpdater);
            FileStoreFile UpdaterFile = UpdaterContainer.GetFileReference(FileStorePath + ".gzip");

            //
            // First attempt to retrieve a gzip compressed version of the file
            // to patch.  If that fails then fall back to a plaintext version.
            //

            try
            {
                using (MemoryStream MemStream = new MemoryStream())
                {
                    UpdaterFile.Read(MemStream);

                    MemStream.Position = 0;

                    using (FileStream OutStream = File.Create(LocalFileName))
                    {
                        using (GZipStream CompressedStream = new GZipStream(MemStream, CompressionMode.Decompress))
                        {
                            CompressedStream.CopyTo(OutStream);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format("ModuleContentPatcher.DownloadContentPatchFromFileStore: Couldn't retrieve compressed file {0} from Azure, trying uncompressed file, due to exception: {1}",
                    FileStorePath,
                    e));

                UpdaterFile = UpdaterContainer.GetFileReference(FileStorePath);

                using (FileStream OutStream = File.Create(LocalFileName))
                {
                    UpdaterFile.Read(OutStream);
                }
            }
        }
 /// <summary>
 /// Dispatch the event (in a script context).
 /// </summary>
 /// <param name="Script">Supplies the script object.</param>
 /// <param name="Database">Supplies the database connection.</param>
 public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
 {
     Script.WriteTimestampedLogEntry("ACR_ServerCommunicator: " + Message);
 }
Example #27
0
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            //
            // If the event was for a player logging on to the local server,
            // then don't re-broadcast it.
            //
            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                PlayerState Player = Script.TryGetPlayerState(PlayerObject);

                if (Player == null)
                {
                    continue;
                }

                if (!Player.CharacterIdsShown.Contains(Character.CharacterId))
                {
                    string sPlayerListBox = "";

                    if (Server.ServerId == Script.GetDatabase().ACR_GetServerID() || Script.GetLocalInt(PlayerObject, "chatselect_expanded") == 0)
                    {
                        if (IsDM == true)
                        {
                            sPlayerListBox = "LocalDMList";
                            Player.ChatSelectLocalDMsShown += 1;
                        }
                        else
                        {
                            sPlayerListBox = "LocalPlayerList";
                            Player.ChatSelectLocalPlayersShown += 1;
                        }

                        if (Server.ServerId == Script.GetDatabase().ACR_GetServerID())
                        {
                            Script.AddListBoxRow(Player.ObjectId, "ChatSelect", sPlayerListBox, Character.CharacterName, "RosterData=/t \"" + Character.CharacterName + "\"", "", "5=/t \"" + Character.CharacterName + "\" ", "");
                        }
                        else
                        {
                            if (Player.Flags.HasFlag(PlayerStateFlags.ChatSelectShowLocalPlayersOnlyWhenCollapsed))
                            {
                                continue;
                            }

                            Script.AddListBoxRow(Player.ObjectId, "ChatSelect", sPlayerListBox, Character.CharacterName, "RosterData=#t \"" + Character.CharacterName + "\"", "", "5=#t \"" + Character.CharacterName + "\" ", "");
                        }
                    }
                    else
                    {
                        if (IsDM == true)
                        {
                            sPlayerListBox = "RemoteDMList";
                            Player.ChatSelectRemoteDMsShown += 1;
                        }
                        else
                        {
                            sPlayerListBox = "RemotePlayerList";
                            Player.ChatSelectRemotePlayersShown += 1;
                        }

                        Script.AddListBoxRow(Player.ObjectId, "ChatSelect", sPlayerListBox, Character.CharacterName, "RosterData=#t \"" + Character.CharacterName + "\"", "", "5=#t \"" + Character.CharacterName + "\" ", "");
                    }

                    Player.CharacterIdsShown.Add(Character.CharacterId);
                    Player.UpdateChatSelectGUIHeaders();
                }
            }

            if (Database.ACR_GetServerID() == Server.ServerId)
            {
                return;
            }

            string Message = String.Format(
                "{0}<c=#FFA500>{1} ({2}) joined {3}.</c>", // <c=Orange...>
                IsDM ? "<c=#99CCFF>[DM] </c>": "",
                Character.Name,
                Character.Player.Name,
                Server.Name);
            string ChatMessage = "</c>" + Message;

            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                PlayerState Player = Script.TryGetPlayerState(PlayerObject);

                if (Player == null)
                {
                    continue;
                }

                if (!Script.IsCrossServerNotificationEnabled(PlayerObject))
                {
                    continue;
                }

                if ((Player.Flags & PlayerStateFlags.SendCrossServerNotificationsToCombatLog) != 0)
                {
                    Script.SendMessageToPC(PlayerObject, Message);
                }
                else
                {
                    Script.SendChatMessage(
                        CLRScriptBase.OBJECT_INVALID,
                        PlayerObject,
                        CLRScriptBase.CHAT_MODE_SERVER,
                        ChatMessage,
                        CLRScriptBase.FALSE);
                }
            }

#if DEBUG_MODE
            Script.WriteTimestampedLogEntry(Message);
#endif
        }
        /// <summary>
        /// Process and apply content patches.  A server restart is scheduled
        /// if required for the content patch.
        /// </summary>
        /// <param name="ContentPatchPath">Supplies the configured content
        /// patch base path, from the database config table.</param>
        /// <param name="Database">Supplies the database object.</param>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="ConnectionString">Supplies the updater file store
        /// connection string.</param>
        /// <returns>True if a patch was applied and a reboot is required for
        /// it to take effect.</returns>
        public static bool ProcessContentPatches(string ContentPatchPath, ALFA.Database Database, ACR_ServerCommunicator Script, string ConnectionString)
        {
            bool ContentChanged = false;
            string Version = Database.ACR_GetHAKVersion();
            List<ContentPatchFile> PatchFiles = new List<ContentPatchFile>();
            ContentPatchPaths LocalPaths = new ContentPatchPaths()
            {
                HakPath = ALFA.SystemInfo.GetHakDirectory(),
                OverridePath = ALFA.SystemInfo.GetOverrideDirectory() + "ACR_ContentPatches"
            };
            string RemoteContentPatchPath = String.Format("{0}{1}\\{2}", ALFA.SystemInfo.GetCentralVaultPath(), ContentPatchPath, Version);
            string FileStoreContentPatchPath = String.Format("{0}/{1}", ContentPatchPath, Version).Replace('\\', '/');
            bool RecompileModule = false;
            bool SentNotification = false;

            Database.ACR_SQLQuery(String.Format(
                "SELECT `FileName`, `Location`, `Checksum`, `RecompileModule` FROM `content_patch_files` WHERE `HakVersion` = '{0}'",
                Database.ACR_SQLEncodeSpecialChars(Version)));

            while (Database.ACR_SQLFetch())
            {
                ContentPatchFile PatchFile = new ContentPatchFile();

                PatchFile.FileName = Database.ACR_SQLGetData(0);
                PatchFile.Location = Database.ACR_SQLGetData(1);
                PatchFile.Checksum = Database.ACR_SQLGetData(2);
                PatchFile.RecompileModule = ALFA.Database.ACR_ConvertDatabaseStringToBoolean(Database.ACR_SQLGetData(3));

                if (PatchFile.Location != "override" &&
                    PatchFile.Location != "hak")
                {
                    continue;
                }

                if (!ALFA.SystemInfo.IsSafeFileName(PatchFile.FileName))
                    continue;

                PatchFiles.Add(PatchFile);
            }

            if (!Directory.Exists(LocalPaths.OverridePath))
                Directory.CreateDirectory(LocalPaths.OverridePath);

            //
            // Remove entries in the ACR patch override directory that are not
            // listed in the patch table.  These may be patches for a previous
            // version, and are not applicable.
            //

            foreach (string DirFile in Directory.GetFiles(LocalPaths.OverridePath))
            {
                ContentPatchFile FoundPatch = (from PF in PatchFiles
                                               where (PF.FileName.Equals(Path.GetFileName(DirFile), StringComparison.InvariantCultureIgnoreCase) && PF.Location == "override")
                                               select PF).FirstOrDefault();
                if (FoundPatch == null)
                {
                    Database.ACR_IncrementStatistic("CONTENT_PATCH_REMOVE_FILE");

                    Script.WriteTimestampedLogEntry(String.Format(
                        "ModuleContentPatcher.ProcessContentPatches: Removing extraneous file {0} from {1}", Path.GetFileName(DirFile), LocalPaths.OverridePath));

                    File.Delete(DirFile);
                    ContentChanged = true;
                }
            }

            //
            // Verify that the MD5 checksum of each of the content files in the
            // ACR patch override directory matches the database's expected
            // checksum.  If not (or if the file in question didn't exist),
            // then copy the new version over from the central vault.
            //

            using (MD5CryptoServiceProvider MD5Csp = new MD5CryptoServiceProvider())
            {
                foreach (ContentPatchFile PatchFile in PatchFiles)
                {
                    bool Matched = false;
                    bool Rename = false;
                    FileUpdateMethod UpdateMethod;
                    string LocalPath = PatchFile.GetLocalPath(LocalPaths);
                    string RemotePath = String.Format("{0}\\{1}", RemoteContentPatchPath, PatchFile.FileName);
                    string FileStorePath = String.Format("{0}/{1}", FileStoreContentPatchPath, PatchFile.FileName).Replace('\\', '/');
                    string LocalHashString = "<no such file>";
                    string TransferTempFilePath = LocalPath + ".patchxfer";

                    if (File.Exists(LocalPath))
                    {
                        LocalHashString = GetFileChecksum(LocalPath, MD5Csp);

                        Matched = (LocalHashString.ToString() == PatchFile.Checksum.ToLower());
                    }

                    if (Matched)
                    {
                        Script.WriteTimestampedLogEntry(String.Format(
                            "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} is up to date (checksum {1}).",
                            PatchFile.FileName,
                            LocalHashString));
                    }
                    else
                    {
                        if (File.Exists(TransferTempFilePath))
                        {
                            try
                            {
                                File.Delete(TransferTempFilePath);
                            }
                            catch (Exception e)
                            {
                                Script.WriteTimestampedLogEntry(String.Format(
                                    "ModuleContentPatcher.ProcessContentPatches: Warning: Exception {0} removing transfer temp file {1} for transfer file {2}.",
                                    e,
                                    TransferTempFilePath,
                                    PatchFile.FileName));
                            }
                        }

                        Script.WriteTimestampedLogEntry(String.Format(
                            "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} needs to be updated (local checksum {1}, remote checksum {2}).  Copying file...",
                            PatchFile.FileName,
                            LocalHashString,
                            PatchFile.Checksum));

                        if (PatchFile.RecompileModule)
                        {
                            Script.WriteTimestampedLogEntry(String.Format(
                                "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} requires a module recompile, flagging module for recompilation.",
                                PatchFile.FileName));
                            RecompileModule = true;
                        }

                        if (!SentNotification)
                        {
                            Script.SendInfrastructureIrcMessage(String.Format(
                                "Server '{0}' is applying a content patch, and will restart shortly.",
                                Script.GetName(Script.GetModule())));
                            SentNotification = true;
                        }

                        Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                            "Server '{0}' is updating content file '{1}'.",
                            Script.GetName(Script.GetModule()),
                            PatchFile.FileName));

                        //
                        // The file needs to be updated.  Copy it over and
                        // re-validate the checksum.  If the checksum did not
                        // match, log an error and delete the file.
                        //

                        try
                        {
                            try
                            {
                                //
                                // Try first to download via the file store
                                // provider.  If that fails (e.g. the file
                                // store is not supported), then fall back to
                                // the traditional remote vault share transfer
                                // mechanism.
                                //

                                try
                                {
                                    DownloadContentPatchFromFileStore(FileStorePath, TransferTempFilePath, ConnectionString, Script);
                                }
                                catch (Exception e)
                                {
                                    Script.WriteTimestampedLogEntry(String.Format("ModuleContentPatcher.ProcessContentPatches: Couldn't retrieve uncompressed file {0} from Azure, falling back to file share, due to exception: {1}", FileStorePath, e));
                                    File.Copy(RemotePath, TransferTempFilePath, true);
                                }
                            }
                            catch
                            {
                                throw;
                            }

                            UpdateMethod = PatchFile.UpdateMethod;

                            //
                            // If we are patching a hak, rename it away so that
                            // the file can be written to.
                            //

                            switch (UpdateMethod)
                            {

                                case FileUpdateMethod.FileUpdateMethodRename:
                                    if (File.Exists(LocalPath))
                                    {
                                        string OldFileName = LocalPath + ".old";

                                        if (File.Exists(OldFileName))
                                            File.Delete(OldFileName);

                                        File.Move(LocalPath, OldFileName);
                                        Rename = true;
                                    }
                                    break;

                                case FileUpdateMethod.FileUpdateMethodDirectReplace:
                                    break;

                            }

                            Database.ACR_IncrementStatistic("CONTENT_PATCH_" + PatchFile.Location.ToUpper());

                            try
                            {
                                if (Rename)
                                    File.Move(TransferTempFilePath, LocalPath);
                                else
                                    File.Copy(TransferTempFilePath, LocalPath, true);
                            }
                            catch
                            {
                                if (UpdateMethod == FileUpdateMethod.FileUpdateMethodRename)
                                {
                                    string OldFileName = LocalPath + ".old";

                                    try
                                    {
                                        if (File.Exists(LocalPath))
                                            File.Delete(LocalPath);
                                    }
                                    catch
                                    {
                                    }

                                    File.Move(OldFileName, LocalPath);
                                }
                                else
                                {
                                    File.Delete(LocalPath);
                                }

                                throw;
                            }

                            if (GetFileChecksum(LocalPath, MD5Csp) != PatchFile.Checksum.ToLower())
                            {
                                Script.WriteTimestampedLogEntry(String.Format(
                                    "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} was copied, but checksum did not match {1}!  This may indicate a configuration error in the database, or file corruption in transit.",
                                    PatchFile.FileName,
                                    PatchFile.Checksum));
                                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                    "Server '{0}' had checksum mismatch for content patch file {1} after hotfix file deployment, rolling back file.",
                                    Script.GetName(Script.GetModule()),
                                    PatchFile.FileName));

                                if (UpdateMethod == FileUpdateMethod.FileUpdateMethodRename)
                                {
                                    string OldFileName = LocalPath + ".old";

                                    try
                                    {
                                        if (File.Exists(LocalPath))
                                            File.Delete(LocalPath);
                                    }
                                    catch
                                    {
                                    }

                                    File.Move(OldFileName, LocalPath);
                                }
                                else
                                {
                                    File.Delete(LocalPath);
                                }

                                Database.ACR_IncrementStatistic("CONTENT_PATCH_INCORRECT_CHECKSUM");
                            }
                            else
                            {
                                ContentChanged = true;

                                Database.ACR_IncrementStatistic("CONTENT_PATCH_UPDATED_FILE");

                                Script.WriteTimestampedLogEntry(String.Format(
                                    "ModuleContentPatcher.ProcessContentPatches: Successfully updated content patch file {0}.",
                                    PatchFile.FileName));
                            }
                        }
                        catch (Exception e)
                        {
                            Script.WriteTimestampedLogEntry(String.Format(
                                "ModuleContentPatcher.ProcessContentPatches: Exception {0} updating content patch file {1}.",
                                e,
                                PatchFile.FileName));

                            Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                "Server '{0}' encountered exception deploying content patch file {1}, rolling back file.",
                                Script.GetName(Script.GetModule()),
                                PatchFile.FileName));
                        }
                    }

                    if (File.Exists(TransferTempFilePath))
                    {
                        try
                        {
                            File.Delete(TransferTempFilePath);
                        }
                        catch (Exception e)
                        {
                            Script.WriteTimestampedLogEntry(String.Format(
                                "ModuleContentPatcher.ProcessContentPatches: Warning: Exception {0} removing transfer temp file {1} for transfer file {2} after patching completed.",
                                e,
                                TransferTempFilePath,
                                PatchFile.FileName));
                        }
                    }
                }
            }

            //
            // Update autodownloader configuration, as necessary.
            //

            try
            {
                if (ProcessModuleDownloaderResourcesUpdates(Database, Script))
                {
                    Script.WriteTimestampedLogEntry("ModuleContentPatcher.ProcessContentPatches: Autodownloader configuration updated.");
                    ContentChanged = true;
                }
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                    "ModuleContentPatcher.ProcessContentPatches: Warning: Exception {0} updating autodownloader configuration.",
                    e));

                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                    "Server '{0}' encountered exception updating autodownloader configuration.",
                    Script.GetName(Script.GetModule())));
            }

            if (ContentChanged)
            {
                if (RecompileModule)
                {
                    Script.WriteTimestampedLogEntry("ModuleContentPatcher.ProcessContentPatches: A module recompile is required; recompiling module...");
                    CompileModuleScripts(Script, Database);
                }

                Database.ACR_IncrementStatistic("CONTENT_PATCH_REBOOT");

                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                    "Server '{0}' restarting after content patch deployment (old HAK ACR version {1} build date {2}, old module ACR version {3}).",
                    Script.GetName(Script.GetModule()),
                    Database.ACR_GetHAKVersion(),
                    Database.ACR_GetHAKBuildDate(),
                    Database.ACR_GetVersion()));
            }

            return ContentChanged;
        }
        /// <summary>
        /// Process and apply content patches.  A server restart is scheduled
        /// if required for the content patch.
        /// </summary>
        /// <param name="ContentPatchPath">Supplies the configured content
        /// patch base path, from the database config table.</param>
        /// <param name="Database">Supplies the database object.</param>
        /// <param name="Script">Supplies the script object.</param>
        /// <returns>True if a patch was applied and a reboot is required for
        /// it to take effect.</returns>
        public static bool ProcessContentPatches(string ContentPatchPath, ALFA.Database Database, ACR_ServerCommunicator Script)
        {
            bool ContentChanged = false;
            string Version = Database.ACR_GetHAKVersion();
            List<ContentPatchFile> PatchFiles = new List<ContentPatchFile>();
            string LocalContentPatchPath = ALFA.SystemInfo.GetOverrideDirectory() + "ACR_ContentPatches";
            string LocalContentPatchHakPath = ALFA.SystemInfo.GetHakDirectory();
            string RemoteContentPatchPath = String.Format("{0}{1}\\{2}", ALFA.SystemInfo.GetCentralVaultPath(), ContentPatchPath, Version);

            Database.ACR_SQLQuery(String.Format(
                "SELECT `FileName`, `Location`, `Checksum` FROM `content_patch_files` WHERE `HakVersion` = '{0}'",
                Database.ACR_SQLEncodeSpecialChars(Version)));

            while (Database.ACR_SQLFetch())
            {
                ContentPatchFile PatchFile = new ContentPatchFile();

                PatchFile.FileName = Database.ACR_SQLGetData(0);
                PatchFile.Location = Database.ACR_SQLGetData(1);
                PatchFile.Checksum = Database.ACR_SQLGetData(2);

                if (PatchFile.Location != "override" &&
                    PatchFile.Location != "hak")
                {
                    continue;
                }

                if (!ALFA.SystemInfo.IsSafeFileName(PatchFile.FileName))
                    continue;

                PatchFiles.Add(PatchFile);
            }

            if (!Directory.Exists(LocalContentPatchPath))
                Directory.CreateDirectory(LocalContentPatchPath);

            //
            // Remove entries in the ACR patch override directory that are not
            // listed in the patch table.  These may be patches for a previous
            // version, and are not applicable.
            //

            foreach (string DirFile in Directory.GetFiles(LocalContentPatchPath))
            {
                ContentPatchFile FoundPatch = (from PF in PatchFiles
                                               where (PF.FileName.Equals(Path.GetFileName(DirFile), StringComparison.InvariantCultureIgnoreCase) && PF.Location == "override")
                                               select PF).FirstOrDefault();
                if (FoundPatch == null)
                {
                    Database.ACR_IncrementStatistic("CONTENT_PATCH_REMOVE_FILE");

                    Script.WriteTimestampedLogEntry(String.Format(
                        "ModuleContentPatcher.ProcessContentPatches: Removing extraneous file {0} from {1}", Path.GetFileName(DirFile), LocalContentPatchPath));

                    File.Delete(DirFile);
                    ContentChanged = true;
                }
            }

            //
            // Verify that the MD5 checksum of each of the content files in the
            // ACR patch override directory matches the database's expected
            // checksum.  If not (or if the file in question didn't exist),
            // then copy the new version over from the central vault.
            //

            using (MD5CryptoServiceProvider MD5Csp = new MD5CryptoServiceProvider())
            {
                foreach (ContentPatchFile PatchFile in PatchFiles)
                {
                    bool Matched = false;
                    string LocalPath = PatchFile.GetLocalPath(LocalContentPatchPath, LocalContentPatchHakPath);
                    string RemotePath = String.Format("{0}\\{1}", RemoteContentPatchPath, PatchFile.FileName);
                    string LocalHashString = "<no such file>";

                    if (File.Exists(LocalPath) && File.Exists(RemotePath))
                    {
                        LocalHashString = GetFileChecksum(LocalPath, MD5Csp);

                        Matched = (LocalHashString.ToString() == PatchFile.Checksum.ToLower());
                    }

                    if (Matched)
                    {
                        Script.WriteTimestampedLogEntry(String.Format(
                            "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} is up to date (checksum {1}).",
                            PatchFile.FileName,
                            LocalHashString));
                    }
                    else
                    {
                        Script.WriteTimestampedLogEntry(String.Format(
                            "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} needs to be updated (local checksum {1}, remote checksum {2}).  Copying file...",
                            PatchFile.FileName,
                            LocalHashString,
                            PatchFile.Checksum));

                        //
                        // The file needs to be updated.  Copy it over and
                        // re-validate the checksum.  If the checksum did not
                        // match, log an error and delete the file.
                        //

                        try
                        {
                            //
                            // If we are patching a hak, rename it away so that
                            // the file can be written to.
                            //

                            if (PatchFile.Location == "hak" && File.Exists(LocalPath))
                            {
                                string OldFileName = LocalPath + ".old";

                                if (File.Exists(OldFileName))
                                    File.Delete(OldFileName);

                                File.Move(LocalPath, OldFileName);

                                Database.ACR_IncrementStatistic("CONTENT_PATCH_HAK");
                            }
                            else if (PatchFile.Location == "override")
                            {
                                Database.ACR_IncrementStatistic("CONTENT_PATCH_OVERRIDE");
                            }

                            try
                            {
                                File.Copy(RemotePath, LocalPath, true);
                            }
                            catch
                            {
                                if (PatchFile.Location == "hak" && File.Exists(LocalPath))
                                {
                                    string OldFileName = LocalPath + ".old";

                                    File.Move(OldFileName, LocalPath);
                                }
                                else
                                {
                                    File.Delete(LocalPath);
                                }

                                throw;
                            }

                            if (GetFileChecksum(LocalPath, MD5Csp) != PatchFile.Checksum.ToLower())
                            {
                                Script.WriteTimestampedLogEntry(String.Format(
                                    "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} was copied, but checksum did not match {1}!  This may indicate a configuration error in the database, or file corruption in transit.",
                                    PatchFile.FileName,
                                    PatchFile.Checksum));

                                if (PatchFile.Location == "hak")
                                {
                                    string OldFileName = LocalPath + ".old";

                                    File.Move(OldFileName, LocalPath);
                                }
                                else
                                {
                                    File.Delete(LocalPath);
                                }

                                Database.ACR_IncrementStatistic("CONTENT_PATCH_INCORRECT_CHECKSUM");
                            }
                            else
                            {
                                ContentChanged = true;

                                Database.ACR_IncrementStatistic("CONTENT_PATCH_UPDATED_FILE");

                                Script.WriteTimestampedLogEntry(String.Format(
                                    "ModuleContentPatcher.ProcessContentPatches: Successfully updated content patch file {0}.",
                                    PatchFile.FileName));
                            }
                        }
                        catch (Exception e)
                        {
                            Script.WriteTimestampedLogEntry(String.Format(
                                "ModuleContentPatcher.ProcessContentPatches: Exception {0} updating content patch file {1}.",
                                e,
                                PatchFile.FileName));
                        }
                    }
                }
            }

            if (ContentChanged)
                Database.ACR_IncrementStatistic("CONTENT_PATCH_REBOOT");

            return ContentChanged;
        }
Example #30
0
        /// <summary>
        /// Recompile all scripts in the module.
        /// </summary>
        /// <param name="Script">Supplies the main script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        private static void CompileModuleScripts(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            ALFA.ScriptCompiler.CompilerResult Result;
            string        CompilerOptions = Script.GetLocalString(Script.GetModule(), "ACR_MOD_COMPILER_OPTIONS");
            List <string> CompilerOutput  = new List <string>();

            Script.WriteTimestampedLogEntry(String.Format(
                                                "ModuleContentPatcher.CompileModuleScripts: Compiling module scripts with compiler options '{0}'...", CompilerOptions));
            Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                                              "Server '{0}' is recompiling module scripts.",
                                                              Script.GetName(Script.GetModule())));

            Result = ALFA.ScriptCompiler.CompileScript("*.nss", CompilerOptions, delegate(string Line)
            {
                if (!String.IsNullOrWhiteSpace(Line))
                {
                    Script.WriteTimestampedLogEntry(Line);
                }
                return(false);
            });

            foreach (string Message in Result.Warnings)
            {
                try
                {
                    CompilerOutput.Add(Message);
                }
                catch (Exception)
                {
                }
            }

            if (Result.Compiled)
            {
                Script.WriteTimestampedLogEntry("ModuleContentPatcher.CompileModuleScripts: Module successfully recompiled.");
                Database.ACR_IncrementStatistic("CONTENT_PATCH_RECOMPILE");

                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                                                  "Server '{0}' successfully recompiled module with {1} warning(s) for content patch deployment.",
                                                                  Script.GetName(Script.GetModule()),
                                                                  Result.Warnings.Count));
            }
            else
            {
                Script.WriteTimestampedLogEntry(String.Format(
                                                    "ModuleContentPatcher.CompileModuleScripts: {0} error(s) compiling module!", Result.Errors.Count));

                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                                                  "Server '{0}' had {1} error(s), {2} warning(s) recompiling module for content patch deployment.",
                                                                  Script.GetName(Script.GetModule()),
                                                                  Result.Errors.Count,
                                                                  Result.Warnings.Count));

                foreach (string Message in Result.Errors)
                {
                    Script.WriteTimestampedLogEntry(String.Format(
                                                        "ModuleContentPatcher.CompileModuleScripts: Error '{0}'.", Message));

                    try
                    {
                        CompilerOutput.Add(Message);
                    }
                    catch (Exception)
                    {
                    }
                }

                Database.ACR_IncrementStatistic("CONTENT_PATCH_RECOMPILE_FAILED");
            }

            //
            // Save compiler output to a temporary file for later retrieval.
            //

            try
            {
                string FileName = String.Format("{0}{1}ALFAModuleRecompile.log", Path.GetTempPath(), Path.DirectorySeparatorChar);

                File.WriteAllLines(FileName, CompilerOutput);
            }
            catch (Exception)
            {
            }
        }
        /// <summary>
        /// This when a cross-server ping response is received.  Its purpose is
        /// to compute the channel latency and send a message to that effect to
        /// the requesting player.
        /// </summary>
        /// <param name="SourceServerId">Supplies the server id of the server
        /// that completed the ping request.</param>
        /// <param name="Arguments">Supplies the serialized state arguments
        /// string.</param>
        /// <param name="Script">Supplies the current script object.</param>
        /// <returns>TRUE on success, else FALSE on failure.</returns>
        public static int HandleServerPingResponse(int SourceServerId, string Arguments, ACR_ServerCommunicator Script)
        {
            int Tick = Environment.TickCount;

            try
            {
                PingState RemoteState;

                //
                // Deserialize the remote ping state.  If null, a protocol
                // violation has occurred.
                //

                if ((RemoteState = PingState.FromString(Arguments)) == null)
                {
                    Script.WriteTimestampedLogEntry(String.Format(
                        "ACR_ServerCommunicator.ServerLatencyMeasurer.HandleServerPingResponse({0}, {1}): Invalid request.",
                        SourceServerId,
                        Arguments));
                    return ACR_ServerCommunicator.FALSE;
                }

                string ServerName = Script.GetServerName(SourceServerId);

                if (ServerName == null)
                    return ACR_ServerCommunicator.FALSE;

                Script.SendMessageToPC(RemoteState.PCObjectId, String.Format(
                    "IPC channel latency to {0}: {1}ms", ServerName, Tick - RemoteState.TickCount));
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                    "ACR_ServerCommunicator.ServerLatencyMeasurer.HandleServerPingResponse({0}, {1}): Exception: {0}",
                    SourceServerId,
                    Arguments,
                    e));
                return ACR_ServerCommunicator.FALSE;
            }

            return ACR_ServerCommunicator.TRUE;
        }
        /// <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 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;
        }
        /// <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);
        }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            string PlayerName = Player.Name;

            if (!SystemInfo.IsSafeFileName(PlayerName))
            {
                Script.WriteTimestampedLogEntry(String.Format(
                                                    "PurgeCachedCharacterEvent.DispatchEvent: Invalid player name '{0}'.", PlayerName));
                return;
            }

            string VaultPath = SystemInfo.GetServerVaultPathForAccount(PlayerName);

            if (VaultPath == null)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                                                    "PurgeCachedCharacterEvent.DispatchEvent: Could not resolve vault path for player '{0}'.",
                                                    Player.PlayerName));
                return;
            }

            VaultPath += CharacterFileName;

            if (!File.Exists(VaultPath))
            {
                Script.WriteTimestampedLogEntry(String.Format(
                                                    "PurgeCachedCharacterEvent.DispatchEvent: Character file '{0}' for player '{1}' was not locally cached.",
                                                    CharacterFileName,
                                                    Player.PlayerName));
                return;
            }

            try
            {
                File.Delete(VaultPath);
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                                                    "PurgeCachedCharacterEvent.DispatchEvent: Exception '{0}' removing cached character '{1}' for player '{2}'.",
                                                    e,
                                                    CharacterFileName,
                                                    Player.PlayerName));
                return;
            }

            try
            {
                if (SystemInfo.GetVaultStoragePluginInUse())
                {
                    VaultPath = SystemInfo.GetCentralVaultPathForAccount(PlayerName);

                    VaultPath += CharacterFileName;

                    if (File.Exists(VaultPath))
                    {
                        File.Delete(VaultPath);
                    }
                }
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                                                    "PurgeCachedCharacterEvent.DispatchEvent: Exception '{0}' removing cached character from Azure vault temporary local storage '{1}' for player '{2}'.",
                                                    e,
                                                    CharacterFileName,
                                                    Player.PlayerName));
                return;
            }

            Script.WriteTimestampedLogEntry(String.Format(
                                                "PurgeCachedCharacterEvent.DispatchEvent: Removed cached character '{0}' from player '{1}' vault cache.",
                                                CharacterFileName,
                                                Player.PlayerName));
        }
 /// <summary>
 /// Dispatch the event (in a script context).
 /// </summary>
 /// <param name="Script">Supplies the script object.</param>
 /// <param name="Database">Supplies the database connection.</param>
 public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
 {
     Script.WriteTimestampedLogEntry("ACR_ServerCommunicator: " + Message);
 }
        /// <summary>
        /// Dispatch the event (in a script context).
        /// </summary>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        public void DispatchEvent(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            //
            // If the event was for a player logging off of the local server,
            // then don't re-broadcast it.
            //

            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                PlayerState Player = Script.TryGetPlayerState(PlayerObject);

                if (Player == null)
                    continue;

                if (Player.CharacterIdsShown.Contains(Character.CharacterId))
                {
                    string sPlayerListBox = "";

                    if (Server.ServerId == Script.GetDatabase().ACR_GetServerID() || Script.GetLocalInt(PlayerObject, "chatselect_expanded") == 0)
                    {
                        if (IsDM == true)
                        {
                            sPlayerListBox = "LocalDMList";
                            Player.ChatSelectLocalDMsShown -= 1;
                        }
                        else
                        {
                            sPlayerListBox = "LocalPlayerList";
                            Player.ChatSelectLocalPlayersShown -= 1;
                        }
                    }
                    else
                    {
                        if (IsDM == true)
                        {
                            sPlayerListBox = "RemoteDMList";
                            Player.ChatSelectRemoteDMsShown -= 1;
                        }
                        else
                        {
                            sPlayerListBox = "RemotePlayerList";
                            Player.ChatSelectRemotePlayersShown -= 1;
                        }
                    }

                    Script.RemoveListBoxRow(Player.ObjectId, "ChatSelect", sPlayerListBox, Character.CharacterName);
                    Player.CharacterIdsShown.Remove(Character.CharacterId);
                    Player.UpdateChatSelectGUIHeaders();
                }
            }

            if (Database.ACR_GetServerID() == Server.ServerId)
                return;

            string Message = String.Format(
                "{0}<c=#FFDAB9>{1} ({2}) left {3}.</c>", // <c=Peachpuff...>
                IsDM ? "<c=#99CCFF>[DM] </c>" : "",
                Character.Name,
                Character.Player.Name,
                Server.Name);
            string ChatMessage = "</c>" + Message;

            foreach (uint PlayerObject in Script.GetPlayers(true))
            {
                PlayerState Player = Script.TryGetPlayerState(PlayerObject);

                if (Player == null)
                    continue;

                if (!Script.IsCrossServerNotificationEnabled(PlayerObject))
                    continue;

                if ((Player.Flags & PlayerStateFlags.SendCrossServerNotificationsToCombatLog) != 0)
                {
                    Script.SendMessageToPC(PlayerObject, Message);
                }
                else
                {
                    Script.SendChatMessage(
                        CLRScriptBase.OBJECT_INVALID,
                        PlayerObject,
                        CLRScriptBase.CHAT_MODE_SERVER,
                        ChatMessage,
                        CLRScriptBase.FALSE);
                }
            }

#if DEBUG_MODE
            Script.WriteTimestampedLogEntry(Message);
#endif
        }
Example #38
0
        /// <summary>
        /// Process and apply content patches.  A server restart is scheduled
        /// if required for the content patch.
        /// </summary>
        /// <param name="ContentPatchPath">Supplies the configured content
        /// patch base path, from the database config table.</param>
        /// <param name="Database">Supplies the database object.</param>
        /// <param name="Script">Supplies the script object.</param>
        /// <param name="ConnectionString">Supplies the updater file store
        /// connection string.</param>
        /// <returns>True if a patch was applied and a reboot is required for
        /// it to take effect.</returns>
        public static bool ProcessContentPatches(string ContentPatchPath, ALFA.Database Database, ACR_ServerCommunicator Script, string ConnectionString)
        {
            bool   ContentChanged = false;
            string Version        = Database.ACR_GetHAKVersion();
            List <ContentPatchFile> PatchFiles = new List <ContentPatchFile>();
            ContentPatchPaths       LocalPaths = new ContentPatchPaths()
            {
                HakPath      = ALFA.SystemInfo.GetHakDirectory(),
                OverridePath = ALFA.SystemInfo.GetOverrideDirectory() + "ACR_ContentPatches"
            };
            string RemoteContentPatchPath    = String.Format("{0}{1}\\{2}", ALFA.SystemInfo.GetCentralVaultPath(), ContentPatchPath, Version);
            string FileStoreContentPatchPath = String.Format("{0}/{1}", ContentPatchPath, Version).Replace('\\', '/');
            bool   RecompileModule           = false;
            bool   SentNotification          = false;

            Database.ACR_SQLQuery(String.Format(
                                      "SELECT `FileName`, `Location`, `Checksum`, `RecompileModule` FROM `content_patch_files` WHERE `HakVersion` = '{0}'",
                                      Database.ACR_SQLEncodeSpecialChars(Version)));

            while (Database.ACR_SQLFetch())
            {
                ContentPatchFile PatchFile = new ContentPatchFile();

                PatchFile.FileName        = Database.ACR_SQLGetData(0);
                PatchFile.Location        = Database.ACR_SQLGetData(1);
                PatchFile.Checksum        = Database.ACR_SQLGetData(2);
                PatchFile.RecompileModule = ALFA.Database.ACR_ConvertDatabaseStringToBoolean(Database.ACR_SQLGetData(3));

                if (PatchFile.Location != "override" &&
                    PatchFile.Location != "hak")
                {
                    continue;
                }

                if (!ALFA.SystemInfo.IsSafeFileName(PatchFile.FileName))
                {
                    continue;
                }

                PatchFiles.Add(PatchFile);
            }

            if (!Directory.Exists(LocalPaths.OverridePath))
            {
                Directory.CreateDirectory(LocalPaths.OverridePath);
            }

            //
            // Remove entries in the ACR patch override directory that are not
            // listed in the patch table.  These may be patches for a previous
            // version, and are not applicable.
            //

            foreach (string DirFile in Directory.GetFiles(LocalPaths.OverridePath))
            {
                ContentPatchFile FoundPatch = (from PF in PatchFiles
                                               where (PF.FileName.Equals(Path.GetFileName(DirFile), StringComparison.InvariantCultureIgnoreCase) && PF.Location == "override")
                                               select PF).FirstOrDefault();
                if (FoundPatch == null)
                {
                    Database.ACR_IncrementStatistic("CONTENT_PATCH_REMOVE_FILE");

                    Script.WriteTimestampedLogEntry(String.Format(
                                                        "ModuleContentPatcher.ProcessContentPatches: Removing extraneous file {0} from {1}", Path.GetFileName(DirFile), LocalPaths.OverridePath));

                    File.Delete(DirFile);
                    ContentChanged = true;
                }
            }

            //
            // Verify that the MD5 checksum of each of the content files in the
            // ACR patch override directory matches the database's expected
            // checksum.  If not (or if the file in question didn't exist),
            // then copy the new version over from the central vault.
            //

            using (MD5CryptoServiceProvider MD5Csp = new MD5CryptoServiceProvider())
            {
                foreach (ContentPatchFile PatchFile in PatchFiles)
                {
                    bool             Matched = false;
                    bool             Rename  = false;
                    FileUpdateMethod UpdateMethod;
                    string           LocalPath            = PatchFile.GetLocalPath(LocalPaths);
                    string           RemotePath           = String.Format("{0}\\{1}", RemoteContentPatchPath, PatchFile.FileName);
                    string           FileStorePath        = String.Format("{0}/{1}", FileStoreContentPatchPath, PatchFile.FileName).Replace('\\', '/');
                    string           LocalHashString      = "<no such file>";
                    string           TransferTempFilePath = LocalPath + ".patchxfer";

                    if (File.Exists(LocalPath))
                    {
                        LocalHashString = GetFileChecksum(LocalPath, MD5Csp);

                        Matched = (LocalHashString.ToString() == PatchFile.Checksum.ToLower());
                    }

                    if (Matched)
                    {
                        Script.WriteTimestampedLogEntry(String.Format(
                                                            "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} is up to date (checksum {1}).",
                                                            PatchFile.FileName,
                                                            LocalHashString));
                    }
                    else
                    {
                        if (File.Exists(TransferTempFilePath))
                        {
                            try
                            {
                                File.Delete(TransferTempFilePath);
                            }
                            catch (Exception e)
                            {
                                Script.WriteTimestampedLogEntry(String.Format(
                                                                    "ModuleContentPatcher.ProcessContentPatches: Warning: Exception {0} removing transfer temp file {1} for transfer file {2}.",
                                                                    e,
                                                                    TransferTempFilePath,
                                                                    PatchFile.FileName));
                            }
                        }

                        Script.WriteTimestampedLogEntry(String.Format(
                                                            "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} needs to be updated (local checksum {1}, remote checksum {2}).  Copying file...",
                                                            PatchFile.FileName,
                                                            LocalHashString,
                                                            PatchFile.Checksum));

                        if (PatchFile.RecompileModule)
                        {
                            Script.WriteTimestampedLogEntry(String.Format(
                                                                "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} requires a module recompile, flagging module for recompilation.",
                                                                PatchFile.FileName));
                            RecompileModule = true;
                        }

                        if (!SentNotification)
                        {
                            Script.SendInfrastructureIrcMessage(String.Format(
                                                                    "Server '{0}' is applying a content patch, and will restart shortly.",
                                                                    Script.GetName(Script.GetModule())));
                            SentNotification = true;
                        }

                        Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                                                          "Server '{0}' is updating content file '{1}'.",
                                                                          Script.GetName(Script.GetModule()),
                                                                          PatchFile.FileName));

                        //
                        // The file needs to be updated.  Copy it over and
                        // re-validate the checksum.  If the checksum did not
                        // match, log an error and delete the file.
                        //

                        try
                        {
                            try
                            {
                                //
                                // Try first to download via the file store
                                // provider.  If that fails (e.g. the file
                                // store is not supported), then fall back to
                                // the traditional remote vault share transfer
                                // mechanism.
                                //

                                try
                                {
                                    DownloadContentPatchFromFileStore(FileStorePath, TransferTempFilePath, ConnectionString, Script);
                                }
                                catch (Exception e)
                                {
                                    Script.WriteTimestampedLogEntry(String.Format("ModuleContentPatcher.ProcessContentPatches: Couldn't retrieve uncompressed file {0} from Azure, falling back to file share, due to exception: {1}", FileStorePath, e));
                                    File.Copy(RemotePath, TransferTempFilePath, true);
                                }
                            }
                            catch
                            {
                                throw;
                            }

                            UpdateMethod = PatchFile.UpdateMethod;

                            //
                            // If we are patching a hak, rename it away so that
                            // the file can be written to.
                            //

                            switch (UpdateMethod)
                            {
                            case FileUpdateMethod.FileUpdateMethodRename:
                                if (File.Exists(LocalPath))
                                {
                                    string OldFileName = LocalPath + ".old";

                                    if (File.Exists(OldFileName))
                                    {
                                        File.Delete(OldFileName);
                                    }

                                    File.Move(LocalPath, OldFileName);
                                    Rename = true;
                                }
                                break;

                            case FileUpdateMethod.FileUpdateMethodDirectReplace:
                                break;
                            }

                            Database.ACR_IncrementStatistic("CONTENT_PATCH_" + PatchFile.Location.ToUpper());

                            try
                            {
                                if (Rename)
                                {
                                    File.Move(TransferTempFilePath, LocalPath);
                                }
                                else
                                {
                                    File.Copy(TransferTempFilePath, LocalPath, true);
                                }
                            }
                            catch
                            {
                                if (UpdateMethod == FileUpdateMethod.FileUpdateMethodRename)
                                {
                                    string OldFileName = LocalPath + ".old";

                                    try
                                    {
                                        if (File.Exists(LocalPath))
                                        {
                                            File.Delete(LocalPath);
                                        }
                                    }
                                    catch
                                    {
                                    }

                                    File.Move(OldFileName, LocalPath);
                                }
                                else
                                {
                                    File.Delete(LocalPath);
                                }

                                throw;
                            }

                            if (GetFileChecksum(LocalPath, MD5Csp) != PatchFile.Checksum.ToLower())
                            {
                                Script.WriteTimestampedLogEntry(String.Format(
                                                                    "ModuleContentPatcher.ProcessContentPatches: Content patch file {0} was copied, but checksum did not match {1}!  This may indicate a configuration error in the database, or file corruption in transit.",
                                                                    PatchFile.FileName,
                                                                    PatchFile.Checksum));
                                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                                                                  "Server '{0}' had checksum mismatch for content patch file {1} after hotfix file deployment, rolling back file.",
                                                                                  Script.GetName(Script.GetModule()),
                                                                                  PatchFile.FileName));

                                if (UpdateMethod == FileUpdateMethod.FileUpdateMethodRename)
                                {
                                    string OldFileName = LocalPath + ".old";

                                    try
                                    {
                                        if (File.Exists(LocalPath))
                                        {
                                            File.Delete(LocalPath);
                                        }
                                    }
                                    catch
                                    {
                                    }

                                    File.Move(OldFileName, LocalPath);
                                }
                                else
                                {
                                    File.Delete(LocalPath);
                                }

                                Database.ACR_IncrementStatistic("CONTENT_PATCH_INCORRECT_CHECKSUM");
                            }
                            else
                            {
                                ContentChanged = true;

                                Database.ACR_IncrementStatistic("CONTENT_PATCH_UPDATED_FILE");

                                Script.WriteTimestampedLogEntry(String.Format(
                                                                    "ModuleContentPatcher.ProcessContentPatches: Successfully updated content patch file {0}.",
                                                                    PatchFile.FileName));
                            }
                        }
                        catch (Exception e)
                        {
                            Script.WriteTimestampedLogEntry(String.Format(
                                                                "ModuleContentPatcher.ProcessContentPatches: Exception {0} updating content patch file {1}.",
                                                                e,
                                                                PatchFile.FileName));

                            Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                                                              "Server '{0}' encountered exception deploying content patch file {1}, rolling back file.",
                                                                              Script.GetName(Script.GetModule()),
                                                                              PatchFile.FileName));
                        }
                    }

                    if (File.Exists(TransferTempFilePath))
                    {
                        try
                        {
                            File.Delete(TransferTempFilePath);
                        }
                        catch (Exception e)
                        {
                            Script.WriteTimestampedLogEntry(String.Format(
                                                                "ModuleContentPatcher.ProcessContentPatches: Warning: Exception {0} removing transfer temp file {1} for transfer file {2} after patching completed.",
                                                                e,
                                                                TransferTempFilePath,
                                                                PatchFile.FileName));
                        }
                    }
                }
            }

            //
            // Update autodownloader configuration, as necessary.
            //

            try
            {
                if (ProcessModuleDownloaderResourcesUpdates(Database, Script))
                {
                    Script.WriteTimestampedLogEntry("ModuleContentPatcher.ProcessContentPatches: Autodownloader configuration updated.");
                    ContentChanged = true;
                }
            }
            catch (Exception e)
            {
                Script.WriteTimestampedLogEntry(String.Format(
                                                    "ModuleContentPatcher.ProcessContentPatches: Warning: Exception {0} updating autodownloader configuration.",
                                                    e));

                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                                                  "Server '{0}' encountered exception updating autodownloader configuration.",
                                                                  Script.GetName(Script.GetModule())));
            }

            if (ContentChanged)
            {
                if (RecompileModule)
                {
                    Script.WriteTimestampedLogEntry("ModuleContentPatcher.ProcessContentPatches: A module recompile is required; recompiling module...");
                    CompileModuleScripts(Script, Database);
                }

                Database.ACR_IncrementStatistic("CONTENT_PATCH_REBOOT");

                Script.SendInfrastructureDiagnosticIrcMessage(String.Format(
                                                                  "Server '{0}' restarting after content patch deployment (old HAK ACR version {1} build date {2}, old module ACR version {3}).",
                                                                  Script.GetName(Script.GetModule()),
                                                                  Database.ACR_GetHAKVersion(),
                                                                  Database.ACR_GetHAKBuildDate(),
                                                                  Database.ACR_GetVersion()));
            }

            return(ContentChanged);
        }
Example #39
0
        /// <summary>
        /// Process any updates to moduledownloaderresources.xml.
        /// </summary>
        /// <param name="Database">Supplies the database object.</param>
        /// <param name="Script">Supplies the script object.</param>
        /// <returns>True if a patch was applied and a reboot is required for
        /// it to take effect.</returns>
        public static bool ProcessModuleDownloaderResourcesUpdates(ALFA.Database Database, ACR_ServerCommunicator Script)
        {
            bool ContentChanged = false;

            //
            // Check the database for the expected download server configurations.
            //

            Database.ACR_SQLQuery("SELECT `Hash`, `DownloadHash`, `Name`, `DLSize`, `Size` FROM `content_download_config`");

            List <DownloadConfiguration> hakConfigs = new List <DownloadConfiguration>();

            //
            // Build a list of expected download configurations.
            //

            while (Database.ACR_SQLFetch())
            {
                DownloadConfiguration downloadConfig = new DownloadConfiguration();

                downloadConfig.Hash         = Database.ACR_SQLGetData(0);
                downloadConfig.DownloadHash = Database.ACR_SQLGetData(1);
                downloadConfig.Name         = Database.ACR_SQLGetData(2);
                downloadConfig.DLSize       = Database.ACR_SQLGetData(3);
                downloadConfig.Size         = Database.ACR_SQLGetData(4);

                if (String.IsNullOrEmpty(downloadConfig.Hash) ||
                    String.IsNullOrEmpty(downloadConfig.DownloadHash) ||
                    String.IsNullOrEmpty(downloadConfig.Name) ||
                    String.IsNullOrEmpty(downloadConfig.DLSize) ||
                    String.IsNullOrEmpty(downloadConfig.Size))
                {
                    continue;
                }

                hakConfigs.Add(downloadConfig);
            }

            //
            // If we have any configuration to do, we loop through and compare the
            // configuration on the database with the configuration on the server,
            // updating the downloadresources xml if it is new.
            //

            if (hakConfigs.Count > 0)
            {
                XmlDocument moduleDownloadResources = new XmlDocument();
                moduleDownloadResources.Load(ALFA.SystemInfo.GetModuleDirectory() + "\\moduledownloaderresources.xml");
                XmlElement downloadResources = moduleDownloadResources.DocumentElement;

                foreach (DownloadConfiguration config in hakConfigs)
                {
                    foreach (XmlNode node in downloadResources.ChildNodes)
                    {
                        if (node.Attributes["name"].Value == config.Name)
                        {
                            if (node.Attributes["hash"].Value != config.Hash)
                            {
                                node.Attributes["hash"].Value = config.Hash;
                                ContentChanged = true;
                            }
                            if (node.Attributes["downloadHash"].Value != config.DownloadHash)
                            {
                                node.Attributes["downloadHash"].Value = config.DownloadHash;
                                ContentChanged = true;
                            }
                            if (node.Attributes["dlsize"].Value != config.DLSize)
                            {
                                node.Attributes["dlsize"].Value = config.DLSize;
                                ContentChanged = true;
                            }
                            if (node.Attributes["size"].Value != config.Size)
                            {
                                node.Attributes["size"].Value = config.Size;
                                ContentChanged = true;
                            }

                            if (ContentChanged)
                            {
                                Script.WriteTimestampedLogEntry(String.Format(
                                                                    "ModuleContentPatcher.ProcessModuleDownloaderResourcesUpdates: Updated downloader resource {0} (hash {1}).",
                                                                    config.Name,
                                                                    config.Hash));
                            }
                        }
                    }
                }

                if (ContentChanged)
                {
                    moduleDownloadResources.Save(ALFA.SystemInfo.GetModuleDirectory() + "\\moduledownloaderresources.xml");
                }
            }

            return(ContentChanged);
        }
        /// <summary>
        /// Recompile all scripts in the module.
        /// </summary>
        /// <param name="Script">Supplies the main script object.</param>
        /// <param name="Database">Supplies the database connection.</param>
        private static void CompileModuleScripts(ACR_ServerCommunicator Script, ALFA.Database Database)
        {
            ALFA.ScriptCompiler.CompilerResult Result;
            string CompilerOptions = Script.GetLocalString(Script.GetModule(), "ACR_MOD_COMPILER_OPTIONS");

            Script.WriteTimestampedLogEntry(String.Format(
                "ModuleContentPatcher.CompileModuleScripts: Compiling module scripts with compiler options '{0}'...", CompilerOptions));

            Result = ALFA.ScriptCompiler.CompileScript("*.nss", CompilerOptions, delegate(string Line)
            {
                Script.WriteTimestampedLogEntry(Line);
                return false;
            });

            if (Result.Compiled)
            {
                Script.WriteTimestampedLogEntry("ModuleContentPatcher.CompileModuleScripts: Module successfully recompiled.");
                Database.ACR_IncrementStatistic("CONTENT_PATCH_RECOMPILE");
            }
            else
            {
                Script.WriteTimestampedLogEntry(String.Format(
                    "ModuleContentPatcher.CompileModuleScripts: {0} error(s) compiling module!", Result.Errors.Count));

                foreach (string Message in Result.Errors)
                {
                    Script.WriteTimestampedLogEntry(String.Format(
                        "ModuleContentPatcher.CompileModuleScripts: Error '{0}'.", Message));
                }

                Database.ACR_IncrementStatistic("CONTENT_PATCH_RECOMPILE_FAILED");
            }

        }