diff --git a/src/Automation/BackupManager.cs b/src/Automation/BackupManager.cs
index 981f6cb..4187489 100644
--- a/src/Automation/BackupManager.cs
+++ b/src/Automation/BackupManager.cs
@@ -15,6 +15,8 @@ public class BackupManager : Manager
public RunConfiguration RunConfig;
///Time in milliseconds to wait until sending next save query command to ProcessManager's process
public int QueryTimeout { get; set; } = 500;
+ private string _tag = "[ VELLUM:BACKUP ] ";
+
#region PLUGIN
public Version Version { get; }
@@ -48,7 +50,7 @@ public BackupManager(ProcessManager p, RunConfiguration runConfig)
public void CreateWorldBackup(string worldPath, string destinationPath, bool fullCopy, bool archive)
{
Processing = true;
-
+ Log(String.Format("{0}Creating initial temporary copy of world directory...", _tag));
CallHook((byte)Hook.BEGIN);
#region PRE EXEC
diff --git a/src/Automation/RenderManager.cs b/src/Automation/RenderManager.cs
index 4cc6f2d..53c6581 100644
--- a/src/Automation/RenderManager.cs
+++ b/src/Automation/RenderManager.cs
@@ -1,129 +1,145 @@
-using System;
-using System.IO;
-using System.Diagnostics;
-using System.Collections.Generic;
-using Vellum.Extension;
-
-namespace Vellum.Automation
-{
- public class RenderManager : Manager
- {
- private ProcessManager _bds;
- private Process _renderer;
- public RunConfiguration RunConfig;
-
- #region PLUGIN
- public Version Version { get; }
- public enum Hook
- {
- BEGIN,
- ABORT,
- NEXT,
- END
- }
- #endregion
-
- public RenderManager(ProcessManager p, RunConfiguration runConfig)
- {
- _bds = p;
- RunConfig = runConfig;
- }
-
- public void Start(string worldPath)
- {
- Processing = true;
-
- // Send tellraw message 1/2
- _bds.SendTellraw("Rendering map...");
-
- Log(String.Format("{0}Initializing map rendering...", _tag));
-
- CallHook((byte)Hook.BEGIN);
-
- // Create temporary copy of latest backup to initiate render on
- string prfx = "_";
- string tempPathCopy = worldPath.Replace(Path.GetFileName(worldPath), prfx + Path.GetFileName(worldPath));
- BackupManager.CopyDirectory(worldPath, tempPathCopy);
-
- // Prepare map render output directory
- if (!Directory.Exists(RunConfig.Renders.PapyrusOutputPath))
- {
- Directory.CreateDirectory(RunConfig.Renders.PapyrusOutputPath);
- }
-
- for (int i = 0; i < RunConfig.Renders.PapyrusTasks.Length; i++)
- {
- Dictionary placeholderReplacements = new Dictionary()
- {
- { "$WORLD_PATH", String.Format("\"{0}\"", tempPathCopy) },
- { "$OUTPUT_PATH", String.Format("\"{0}\"", RunConfig.Renders.PapyrusOutputPath) },
- { "${WORLD_PATH}", String.Format("\"{0}\"", tempPathCopy) },
- { "${OUTPUT_PATH}", String.Format("\"{0}\"", RunConfig.Renders.PapyrusOutputPath) }
- };
-
- string args = RunConfig.Renders.PapyrusGlobalArgs;
-
- foreach (KeyValuePair kv in placeholderReplacements)
- args = args.Replace(kv.Key, kv.Value);
-
- _renderer = new Process();
- _renderer.StartInfo.FileName = RunConfig.Renders.PapyrusBinPath;
- _renderer.StartInfo.WorkingDirectory = Path.GetDirectoryName(RunConfig.Renders.PapyrusBinPath);
- _renderer.StartInfo.Arguments = $"{args} {RunConfig.Renders.PapyrusTasks[i]}";
- _renderer.StartInfo.RedirectStandardOutput = RunConfig.HideStdout;
- _renderer.StartInfo.RedirectStandardInput = true;
-
- Log(String.Format("{0}{1}Rendering map {2}/{3}...", _tag, _indent, i + 1, RunConfig.Renders.PapyrusTasks.Length));
-
- // To pre-emptively start a process with defined priority you need to set calling process to said priority.
- Process parentProcess = Process.GetCurrentProcess();
- ProcessPriorityClass parentPriority = parentProcess.PriorityClass;
- if(RunConfig.Renders.LowPriority)
- {
- parentProcess.PriorityClass = ProcessPriorityClass.Idle;
- }
-
- _renderer.Start();
-
- CallHook((byte)Hook.NEXT, new HookEventArgs() { Attachment = i });
-
- if(RunConfig.Renders.LowPriority)
- {
- // Set back parent process to original priority
- parentProcess.PriorityClass = parentPriority;
- }
-
- _renderer.WaitForExit();
- }
-
- Log(String.Format("{0}{1}Cleaning up...", _tag, _indent));
-
- Directory.Delete(tempPathCopy, true);
-
- Log(String.Format("{0}Rendering done!", _tag, _indent));
-
- // Send tellraw message 2/2
- _bds.SendTellraw("Done rendering!");
-
- CallHook((byte)Hook.END);
-
- Processing = false;
- }
-
- public bool Abort()
- {
- bool result = false;
- if (_renderer != null)
- {
- _renderer.Kill();
- result = true;
- } else {
- result = false;
- }
-
- CallHook((byte)Hook.ABORT, new HookEventArgs() { Attachment = result });
-
- return result;
- }
- }
+using System;
+using System.IO;
+using System.Diagnostics;
+using System.Collections.Generic;
+using Vellum.Extension;
+
+namespace Vellum.Automation
+{
+ public class RenderManager : Manager
+ {
+ private ProcessManager _bds;
+ private Process _renderer;
+ public RunConfiguration RunConfig;
+ private string _tag = "[ VELLUM:RENDER ] ";
+
+ #region PLUGIN
+ public Version Version { get; }
+ public enum Hook
+ {
+ BEGIN,
+ ABORT,
+ NEXT,
+ END
+ }
+ #endregion
+
+ public RenderManager(ProcessManager p, RunConfiguration runConfig)
+ {
+ _bds = p;
+ RunConfig = runConfig;
+ }
+
+
+ public void Start(string worldPath, string keyFilter = "(.)")
+ {
+ Processing = true;
+
+ // Send tellraw message 1/2
+ _bds.SendTellraw("Rendering map...");
+
+ Log(String.Format("{0}Initializing map rendering...", _tag));
+
+ CallHook((byte)Hook.BEGIN);
+
+ // Create temporary copy of latest backup to initiate render on
+ string prfx = "_";
+ string tempPathCopy = worldPath.Replace(Path.GetFileName(worldPath), prfx + Path.GetFileName(worldPath));
+ BackupManager.CopyDirectory(worldPath, tempPathCopy);
+
+
+ // Allow multiple external applications that use the same temporary copy in sequence and iterate through them, skipping over disabled engines.
+ RenderConfig RenderApp;
+ foreach(KeyValuePair renderEntry in RunConfig.Renders)
+ {
+ RenderApp = renderEntry.Value;
+
+ // Global render settings won't be executed, non-existing apps and disabled items will be skipped, and optionally only filtered and active items will run.
+ if (renderEntry.Key != "Global" && System.Text.RegularExpressions.Regex.IsMatch(renderEntry.Key,keyFilter) && File.Exists(RenderApp.RenderAppBinPath) && RenderApp.EnableRenders )
+ {
+
+ // Prepare map render output directory
+ if (!Directory.Exists(RenderApp.RenderAppOutputPath))
+ {
+ Directory.CreateDirectory(RenderApp.RenderAppOutputPath);
+ }
+
+ // Go through this renders task list
+ for (int i = 0; i < RenderApp.RenderAppTasks.Length; i++)
+ {
+ Dictionary placeholderReplacements = new Dictionary()
+ {
+ { "$WORLD_PATH", String.Format("\"{0}\"", tempPathCopy) },
+ { "$OUTPUT_PATH", String.Format("\"{0}\"", RenderApp.RenderAppOutputPath) },
+ { "${WORLD_PATH}", String.Format("\"{0}\"", tempPathCopy) },
+ { "${OUTPUT_PATH}", String.Format("\"{0}\"", RenderApp.RenderAppOutputPath) }
+ };
+
+ string args = RenderApp.RenderAppGlobalArgs;
+
+ foreach (KeyValuePair kv in placeholderReplacements)
+ args = args.Replace(kv.Key, kv.Value);
+
+ _renderer = new Process();
+ _renderer.StartInfo.FileName = RenderApp.RenderAppBinPath;
+ _renderer.StartInfo.WorkingDirectory = Path.GetDirectoryName(RenderApp.RenderAppBinPath);
+ _renderer.StartInfo.Arguments = $"{args} {RenderApp.RenderAppTasks[i]}";
+ _renderer.StartInfo.RedirectStandardOutput = RunConfig.HideStdout;
+ _renderer.StartInfo.RedirectStandardInput = true;
+
+ Log(String.Format("{0}{1}Rendering map {2}/{3}...", _tag, _indent, i + 1, RenderApp.RenderAppTasks.Length));
+
+ // To pre-emptively start a process with defined priority you need to set calling process to said priority.
+ Process parentProcess = Process.GetCurrentProcess();
+ ProcessPriorityClass parentPriority = parentProcess.PriorityClass;
+ if(RenderApp.LowPriority)
+ {
+ parentProcess.PriorityClass = ProcessPriorityClass.Idle;
+ }
+
+ _renderer.Start();
+ // TODO: needs a try /catch block to handle sub-process failure events (e.g. cleanup/recover) without killing the BDS server, since they don't interact
+ CallHook((byte)Hook.NEXT, new HookEventArgs() { Attachment = i });
+
+ if(RenderApp.LowPriority)
+ {
+ // Set back parent process to original priority
+ parentProcess.PriorityClass = parentPriority;
+ }
+
+ _renderer.WaitForExit();
+ }
+ }
+ }
+
+ Log(String.Format("{0}{1}Cleaning up...", _tag, _indent));
+
+ Directory.Delete(tempPathCopy, true);
+
+ Log(String.Format("{0}Rendering done!", _tag, _indent));
+
+ // Send tellraw message 2/2
+ _bds.SendTellraw("Done rendering!");
+
+ CallHook((byte)Hook.END);
+
+ Processing = false;
+ }
+
+ public bool Abort()
+ {
+ bool result = false;
+ if (_renderer != null)
+ {
+ _renderer.Kill();
+ result = true;
+ } else {
+ result = false;
+ }
+
+ CallHook((byte)Hook.ABORT, new HookEventArgs() { Attachment = result });
+
+ return result;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Program.cs b/src/Program.cs
index 2f73ef5..6982ee5 100644
--- a/src/Program.cs
+++ b/src/Program.cs
@@ -136,13 +136,29 @@ public VellumHost(string[] args)
}
}
#endregion
-
- if (RunConfig.Renders.EnableRenders && String.IsNullOrWhiteSpace(RunConfig.Renders.PapyrusBinPath))
- {
- Console.WriteLine("Disabling renders because no valid path to a Papyrus executable has been specified");
- RunConfig.Renders.EnableRenders = false;
- }
-
+ // we report the active render applications
+ RenderConfig RenderApp;
+ foreach(KeyValuePair renderEntry in RunConfig.Renders)
+ {
+ RenderApp = renderEntry.Value;
+ if (RenderApp.EnableRenders && !String.IsNullOrWhiteSpace(RenderApp.RenderAppBinPath))
+ {
+ Console.WriteLine("\t{0} is activated as a renderer, with {1} task(s)", RenderApp.RenderAppBinPath,RenderApp.RenderAppTasks.Length);
+ RenderApp.EnableRenders = true;
+ }
+ }
+
+ // Render interval is taken from the "Global" renderapp settings. Currently per-app settings are ignored. Multi-threading would require independent temp copies of the world.
+ if (RunConfig.Renders["Global"].EnableRenders)
+ {
+ System.Timers.Timer renderIntervalTimer = new System.Timers.Timer(RunConfig.Renders["Global"].RenderInterval * 60000);
+ renderIntervalTimer.AutoReset = true;
+ renderIntervalTimer.Elapsed += (object sender, ElapsedEventArgs e) =>
+ {
+ InvokeRender(worldPath, tempWorldPath);
+ };
+ renderIntervalTimer.Start();
+ }
#region BDS process and input thread
ProcessStartInfo serverStartInfo = new ProcessStartInfo()
@@ -354,22 +370,9 @@ public VellumHost(string[] args)
});
}
- // Render interval
- if (RunConfig.Renders.EnableRenders)
- {
- System.Timers.Timer renderIntervalTimer = new System.Timers.Timer(RunConfig.Renders.RenderInterval * 60000);
- renderIntervalTimer.AutoReset = true;
- renderIntervalTimer.Elapsed += (object sender, ElapsedEventArgs e) =>
- {
- InvokeRender(worldPath, tempWorldPath);
- };
- renderIntervalTimer.Start();
- }
-
if (backupOnStartup)
{
// Create initial world backup
- Console.WriteLine("Creating initial temporary copy of world directory...");
_backupManager.CreateWorldBackup(worldPath, tempWorldPath, true, false); // If "StopBeforeBackup" is set to "true" this will also automatically start the server when it's done
}
@@ -546,20 +549,25 @@ public VellumHost(string[] args)
PreExec = "",
PostExec = "",
},
- Renders = new RenderConfig()
- {
- EnableRenders = true,
- RenderInterval = 180,
- PapyrusBinPath = "",
- PapyrusGlobalArgs = "-w $WORLD_PATH -o $OUTPUT_PATH --htmlfile index.html -f png -q 100 --deleteexistingupdatefolder",
- PapyrusTasks = new string[] {
- "--dim 0",
- "--dim 1",
- "--dim 2"
- },
- PapyrusOutputPath = "",
- LowPriority = false
- },
+ Renders = new Dictionary()
+ {
+ { "Global",
+ new RenderConfig()
+ {
+ EnableRenders = true,
+ RenderInterval = 180,
+ RenderAppBinPath = "",
+ RenderAppGlobalArgs = "-w $WORLD_PATH -o $OUTPUT_PATH --htmlfile index.html -f png -q 100 --deleteexistingupdatefolder",
+ RenderAppTasks = new string[] {
+ "--dim 0",
+ "--dim 1",
+ "--dim 2"
+ },
+ RenderAppOutputPath = "",
+ LowPriority = false
+ }
+ }
+ },
QuietMode = false,
HideStdout = true,
BusyCommands = true,
@@ -583,7 +591,7 @@ public void InvokeBackup(string worldPath, string tempWorldPath)
}
else
{
- if (!RunConfig.QuietMode) { Console.WriteLine("A backup task is still running."); }
+ if (!RunConfig.QuietMode) { Console.WriteLine("A backup (or render) task is still running."); }
}
}
@@ -596,7 +604,7 @@ public void InvokeRender(string worldPath, string tempWorldPath)
}
else
{
- if (!RunConfig.QuietMode) { Console.WriteLine("A render task is still running."); }
+ if (!RunConfig.QuietMode) { Console.WriteLine("A render (or backup) task is still running."); }
}
}
diff --git a/src/RunConfiguration.cs b/src/RunConfiguration.cs
index ecceb50..1d4f47f 100644
--- a/src/RunConfiguration.cs
+++ b/src/RunConfiguration.cs
@@ -4,7 +4,7 @@ public class RunConfiguration
{
public string BdsBinPath;
public BackupConfig Backups;
- public RenderConfig Renders;
+ public System.Collections.Generic.Dictionary Renders;
public bool QuietMode;
public bool HideStdout;
public bool BusyCommands;
@@ -32,11 +32,11 @@ public class BackupConfig
public class RenderConfig
{
public bool EnableRenders;
- public string PapyrusBinPath;
- public string PapyrusOutputPath;
+ public string RenderAppBinPath;
+ public string RenderAppOutputPath;
public double RenderInterval;
- public string PapyrusGlobalArgs;
- public string[] PapyrusTasks;
+ public string RenderAppGlobalArgs;
+ public string[] RenderAppTasks;
public bool LowPriority;
}