Ejemplo n.º 1
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)
            {
            }
        }
Ejemplo n.º 2
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);
        }