public override void Exicute(TheScript managerScript) { if (Input.GetKeyDown(KeyCode.Alpha1)) { managerScript.SetState(connections[0]); } if (Input.GetKeyDown(KeyCode.Alpha2)) { managerScript.SetState(connections[1]); } if (Input.GetKeyDown(KeyCode.Alpha3)) { managerScript.SetState(connections[2]); } }
//Helper to update storage list private void UpdateStorageList(string name, string status, int step, TheScript script, TheThing context, bool replay) { ScriptSnapshot existingSnapshot = null; // No longer adding a new entry on replay because the list is now used to disable script/steps etc. existingSnapshot = MyScriptTableStorage.MyMirrorCache.GetEntryByFunc(snapshot => snapshot.ScriptName == name && snapshot.ScriptStep == step); var stepName = step > 0 && step <= script?.Steps.Length ? script?.Steps[step - 1]?.GetName() ?? "" : ""; if (existingSnapshot == null) { var newSnapshot = new ScriptSnapshot { ScriptName = name, ScriptStatus = status, StepName = stepName, ScriptStep = step, ContextScript = script, FileName = script?.FileName, ScriptRaw = script?.ScriptRaw, Context = context, LastUpdate = DateTimeOffset.Now, }; MyScriptTableStorage.AddAnItem(newSnapshot); } else { existingSnapshot.ScriptName = name; existingSnapshot.ScriptStatus = status; existingSnapshot.StepName = stepName; existingSnapshot.ScriptStep = step; existingSnapshot.ContextScript = script; existingSnapshot.FileName = script?.FileName; existingSnapshot.ScriptRaw = script?.ScriptRaw; existingSnapshot.Context = context; existingSnapshot.LastUpdate = DateTimeOffset.Now; MyScriptTableStorage.UpdateItem(existingSnapshot); } }
// Update is called once per frame void Update() { if (theScript == goodEnding || theScript == badEnding || theScript == sadEnding || theScript == creepyEnding) { if (currentLine > 2 && finishedTyping && Input.GetKeyDown(KeyCode.Return)) { TransitionHandler.me.go = true; } } if (currentLine == theScript.script.Count && !done) { overrideline = ""; finishedTyping = false; done = true; currentLine = 0; currentCharacter = 0; float good = 0; float sad = 0; float creepy = 0; for (int i = 0; i < stats.Count; i++) { if (stats[i].name == "Good") { good += stats[i].value; } if (stats[i].name == "Sad") { sad += stats[i].value; } if (stats[i].name == "Creepy") { creepy += stats[i].value; } } if (good > sad + creepy) { theScript = goodEnding; } else if (sad > good + creepy) { theScript = sadEnding; } else if (creepy > good + sad) { theScript = creepyEnding; } else if (good <= 0) { theScript = badEnding; } else { theScript = badEnding; } } // Type character by character until you're done if (!finishedTyping) { if (currentCharacter % meowFrequency == 0) { MeowBox.me.Meow(); } currentCharacter++; if (overrideline == "") { dialogText.text = theScript.script[currentLine].line.Substring(0, currentCharacter); if (dialogText.text.Equals(theScript.script[currentLine].line)) { finishedTyping = true; } } else { dialogText.text = overrideline.Substring(0, currentCharacter); if (dialogText.text.Equals(overrideline)) { finishedTyping = true; } } } else { if (overrideline == "") { dialogText.text = theScript.script[currentLine].line; if (theScript.script[currentLine].isQuestion && answerBox.GetBool("Visible") == false) { selection = 0; answerBox.SetBool("Visible", true); } } else { dialogText.text = overrideline; } } // Show the answer box if the line is a question if (theScript.script[currentLine].isQuestion) { string answers = ""; for (int i = 0; i < theScript.script[currentLine].answers.Length; i++) { answers += "<color=#"; answers += i == selection ? selectedColor : unselectedColor; answers += ">•" + theScript.script[currentLine].answers[i].text + "</color>\r\n"; } answerText.text = answers; } // Key inputs // Next line on enter press if (Input.GetKeyDown(KeyCode.Return)) { if (currentLine < theScript.script.Count) { Next(); } } // Up and down if (Input.GetKeyDown(KeyCode.DownArrow)) { selection++; } if (Input.GetKeyDown(KeyCode.UpArrow)) { selection--; } if (selection < 0) { selection = theScript.script[currentLine].answers.Length - 1; } if (selection >= theScript.script[currentLine].answers.Length) { selection = 0; } }
public virtual void Exicute(TheScript manageScript) { }
private async Task RunScriptAsync(TheScript script, TheThing variables, int stepNumber = 1, bool replay = false) { TheThing variablesSnapshot; try { for (; stepNumber <= script.Steps.Length; stepNumber++) { //Clone thing before step occurs variablesSnapshot = new TheThing(); variables.CloneThingAndPropertyMetaData(variablesSnapshot, true); var step = script.Steps[stepNumber - 1]; var existingSnapshot = MyScriptTableStorage.MyMirrorCache.GetEntryByFunc(snapshot => snapshot.ScriptName == script.Name && snapshot.ScriptStep == stepNumber); if (existingSnapshot?.Disabled == true) { TheBaseAssets.MySYSLOG.WriteToLog(175002, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Finished script step: skipped step because it was disabled", eMsgLevel.l3_ImportantMessage, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> { { "Script", script.Name }, { "Step", stepNumber }, { "Message", step.Message.MessageName }, { "Target", step.Message.Target }, }))); UpdateStorageList(script.Name, "Disabled", stepNumber, script, variablesSnapshot, replay); continue; } if (step.Condition != null) { var condition = TheCommonUtils.GenerateFinalStr(step.Condition, variables); if ( (condition == "" || condition.ToLowerInvariant() == "false" || condition.Trim() == "0") || (condition.StartsWith("!") && condition.Length >= 1 && (condition.Substring(1).ToLowerInvariant() == "true") || condition.Substring(1).Trim() == "1")) { TheBaseAssets.MySYSLOG.WriteToLog(175002, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Finished script step: skipped step due to condition not met", eMsgLevel.l3_ImportantMessage, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> { { "Script", script.Name }, { "Step", stepNumber }, { "Message", step.Message.MessageName }, { "Target", step.Message.Target }, { "Condition", step.Condition }, { "ConditionEvaluated", condition }, }))); UpdateStorageList(script.Name, "Condition Not Met", stepNumber, script, variablesSnapshot, replay); continue; } } var messageType = TheCommonUtils.GenerateFinalStr(step.Message.MessageName, variables); var txtPayload = TheCommonUtils.GenerateFinalStr(step.Message.Parameters?.ToString(), variables); { var txtPayload2 = txtPayload?.Replace("\"\\\"", ""); var txtPayload3 = txtPayload2?.Replace("\\\"\"", ""); txtPayload = txtPayload3; } // TODO Need a simpler and more flexible way to specify thing address in the script JSON var target = step.Message.Target; if (target == null) { if (txtPayload.Contains("EngineName")) { var payloadDict = TheCommonUtils.DeserializeJSONStringToObject <Dictionary <string, object> >(txtPayload); object engineNameInferred = null; if (payloadDict?.TryGetValue("EngineName", out engineNameInferred) == true && !string.IsNullOrEmpty(engineNameInferred?.ToString())) { target = new TheMessageAddress { EngineName = engineNameInferred.ToString() }; } } } if (target.EngineName.StartsWith("%") || target.EngineName.StartsWith("{")) { target.EngineName = TheCommonUtils.GenerateFinalStr(target.EngineName, variables); // TODO Clean this up: support a serialized TheMessageAddress in the engine name, so that an output variable can be fed into a method invocation try { var newTarget = TheCommonUtils.DeserializeJSONStringToObject <TheMessageAddress>(target.EngineName); if (newTarget != null) { target = newTarget; } } catch { // parsing error: ignore, will result in other errors downstream } } await TheThingRegistry.WaitForInitializeAsync(target); bool bDoRetry; int remainingRetryCount = step.RetryCount ?? 0; do { existingSnapshot = MyScriptTableStorage.MyMirrorCache.GetEntryByFunc(snapshot => snapshot.ScriptName == script.Name && snapshot.ScriptStep == stepNumber); if (existingSnapshot?.Disabled == true) { TheBaseAssets.MySYSLOG.WriteToLog(175002, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Finished script step: skipped step because it was disabled", eMsgLevel.l3_ImportantMessage, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> { { "Script", script.Name }, { "Step", stepNumber }, { "Message", step.Message.MessageName }, { "Target", step.Message.Target }, }))); UpdateStorageList(script.Name, "Disabled", stepNumber, script, variablesSnapshot, replay); break; } bDoRetry = false; var response = await TheCommRequestResponse.PublishRequestAsync(MyBaseThing, target, messageType, new TimeSpan(0, 0, 0, 0, step.Message.timeout), null, txtPayload, null); if (!string.IsNullOrEmpty(response?.PLS)) { var outputs = TheCommonUtils.DeserializeJSONStringToObject <Dictionary <string, object> >(response.PLS); if (outputs != null) { if (step.Message.outputs != null) { foreach (var output in step.Message.outputs) { if (output.Key == "*") { variables.SetProperty(output.Value, response.PLS); } else if (outputs.TryGetValue(output.Key, out var outputValue)) { variables.SetProperty(output.Value, outputValue); if (output.Value.Contains("Error") && !string.IsNullOrEmpty(TheCommonUtils.CStr(outputValue))) { TheBaseAssets.MySYSLOG.WriteToLog(175004, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error in script step: output reported error", eMsgLevel.l1_Error, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> { { "Script", script.Name }, { "Step", stepNumber }, { "Message", messageType }, { "Target", target }, { "PLS", txtPayload }, { "Response", response }, { "ResponsePLS", response?.PLS }, }))); UpdateStorageList(script.Name, $"Error {outputValue} in output", stepNumber, script, variablesSnapshot, replay); if (remainingRetryCount < 0 || remainingRetryCount > 0) { remainingRetryCount--; bDoRetry = true; } string retriesRemaining = bDoRetry ? (remainingRetryCount >= 0 ? $"{remainingRetryCount + 1}" : "infinite") : "none"; MyBaseThing.SetStatus(3, $"Error in script '{script?.Name}', step {stepNumber}: output '{output.Value}' reported error {outputValue}. Retries remaining: {retriesRemaining}"); } } else { // TODO provide access to sub-elements in the JSON //var outputParts = output.Key.Split('/'); //dynamic currentNode = outputs; //foreach (var outputPart in outputParts) //{ // if (currentNode.TryGetValue(outputPart, out var nextNode)) // { // currentNode = nextNode; // } //} } } } TheBaseAssets.MySYSLOG.WriteToLog(175003, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Finished script step", eMsgLevel.l3_ImportantMessage, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> { { "Script", script.Name }, { "Step", stepNumber }, { "Message", messageType }, { "Target", target }, { "PLS", txtPayload }, { "ResponsePLS", response.PLS }, }))); UpdateStorageList(script.Name, "Finished", stepNumber, script, variablesSnapshot, replay); } else { TheBaseAssets.MySYSLOG.WriteToLog(175004, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error in script step: no outputs found in response", eMsgLevel.l1_Error, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> { { "Script", script.Name }, { "Step", stepNumber }, { "Message", messageType }, { "Target", target }, { "PLS", txtPayload }, { "Response", response }, { "ResponsePLS", response?.PLS }, }))); UpdateStorageList(script.Name, "Error: No Output", stepNumber, script, variablesSnapshot, replay); if (step.DontRetryOnEmptyResponse != true && (remainingRetryCount < 0 || remainingRetryCount > 0)) { remainingRetryCount--; bDoRetry = true; } string retriesRemaining = bDoRetry ? (remainingRetryCount >= 0 ? $"{remainingRetryCount + 1}" : "infinite") : "none"; MyBaseThing.SetStatus(3, $"Error in script '{script?.Name}', step {stepNumber}: no outputs found in response. Retries remaining: {retriesRemaining}"); } } else { TheBaseAssets.MySYSLOG.WriteToLog(175005, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error Script step: timeout", eMsgLevel.l1_Error, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> { { "Script", script.Name }, { "Step", stepNumber }, { "Message", messageType }, { "Target", target }, { "PLS", txtPayload }, { "Response", response }, }))); UpdateStorageList(script.Name, "Error: Timeout", stepNumber, script, variablesSnapshot, replay); //Retries infinitely unless count is specified if (remainingRetryCount < 0 || remainingRetryCount > 0) { remainingRetryCount--; bDoRetry = true; } string retriesRemaining = bDoRetry ? (remainingRetryCount >= 0 ? $"{remainingRetryCount + 1}" : "infinite") : "none"; MyBaseThing.SetStatus(3, $"Error in script '{script?.Name}', step {stepNumber}: timeout. Retries remaining: {retriesRemaining}"); } if (bDoRetry) { await TheCommonUtils.TaskDelayOneEye(30000, 100).ConfigureAwait(false); } } while (bDoRetry && TheBaseAssets.MasterSwitch); } } catch (Exception e) { TheBaseAssets.MySYSLOG.WriteToLog(175006, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error in script step", eMsgLevel.l1_Error, TheCommonUtils.SerializeObjectToJSONString(new Dictionary <String, object> { { "Script", script.Name }, { "Exception", e.Message }, }))); MyBaseThing.SetStatus(3, $"Error in script '{script?.Name}': {e.Message}"); //Save variables instead of snapshot in case of error UpdateStorageList(script.Name, $"Error: {e.Message}", stepNumber, script, variables, replay); } }
#pragma warning restore CS0649 private async void RunScriptsAsync(object _) { try { MyBaseThing.SetStatus(4, "Running scripts."); var variables = new TheThing(); // Using this as a property bag to hold variables that can be shared across the scripts -> Maybe use the script thing itself for this? Keeps an audit trail of the state of the script... var scriptDir = TheCommonUtils.cdeFixupFileName(@"ClientBin\scripts"); var scriptFiles = Directory.EnumerateFiles(scriptDir, "*.cdescript").ToList(); var scriptsRun = new List <string>(); var pendingScripts = new List <string>(); var scriptsToRun = new List <TheScript>(); int pendingScriptCount; pendingScriptCount = pendingScripts.Count; foreach (var scriptFile in scriptFiles) { try { TheScript script = LoadScript(scriptFile); scriptsToRun.Add(script); } catch (Exception e) { string scriptName = scriptFile; try { scriptName = Path.GetFileNameWithoutExtension(scriptFile); } catch { } UpdateStorageList(scriptFile, $"Failed to read: {e.Message}", -1, null, null, false); TheBaseAssets.MySYSLOG.WriteToLog(175000, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error reading cde script file", eMsgLevel.l1_Error, e.ToString())); } } foreach (var dependentScript in scriptsToRun.Where(script => script.DependsOn != null && script.DependsOn.Length > 0).ToList()) { foreach (var dependencyName in dependentScript.DependsOn) { var dependencyScriptIndex = scriptsToRun.FindIndex(script => script.Name == dependencyName); if (dependencyScriptIndex < 0) { TheBaseAssets.MySYSLOG.WriteToLog(175007, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Unable to run script with dependencies", eMsgLevel.l1_Error, $"Dependent: '{dependentScript.Name}'. Dependency not found: '{dependencyName}'")); } else { var dependentScriptIndex = scriptsToRun.IndexOf(dependentScript); if (dependentScriptIndex >= 0 && dependencyScriptIndex > dependentScriptIndex) { scriptsToRun.Insert(dependencyScriptIndex + 1, dependentScript); scriptsToRun.RemoveAt(dependentScriptIndex); } } } } // This assumes RunScriptsAsync is only called from Init() (= on gate restart) foreach (var oldScript in MyScriptTableStorage.TheValues) { if (oldScript.ScriptStatus == "Not found") { MyScriptTableStorage.RemoveAnItem(oldScript, null); } else { oldScript.ScriptStatus = "Not found"; } } foreach (var script in scriptsToRun) { int stepNumber = 1; foreach (var step in script.Steps ?? new TheScriptStep[0]) { UpdateStorageList(script.Name, "Pending", stepNumber, script, null, false); stepNumber++; } } int index = 0; var scriptTasks = new List <Task>(); foreach (var script in scriptsToRun) { Task task = null; if (script.DependsOn?.Length > 0) { if (index > 1) { task = scriptTasks[index - 1].ContinueWith(t => RunScriptAsync(script, variables)); } } if (task == null) { task = RunScriptAsync(script, variables); } scriptTasks.Add(task); scriptsRun.Add(script.Name); } await TheCommonUtils.TaskWhenAll(scriptTasks); if (MyBaseThing.StatusLevel == 4) { if (scriptTasks?.Count > 0) { MyBaseThing.SetStatus(1, $"All {scriptTasks?.Count} scripts applied."); } else { MyBaseThing.SetStatus(1, "No scripts found."); } TheBaseAssets.MySYSLOG.WriteToLog(175001, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyBaseThing.EngineName, "Scripts applied", eMsgLevel.l3_ImportantMessage, $"Number of scripts: {scriptTasks?.Count}")); } } catch (DirectoryNotFoundException) { TheBaseAssets.MySYSLOG.WriteToLog(175001, TSM.L(eDEBUG_LEVELS.ESSENTIALS) ? null : new TSM(MyBaseThing.EngineName, "Error finding or running script files", eMsgLevel.l3_ImportantMessage, "Script directory not found")); MyBaseThing.SetStatus(1, "No script directory found."); } catch (Exception e) { TheBaseAssets.MySYSLOG.WriteToLog(175001, TSM.L(eDEBUG_LEVELS.OFF) ? null : new TSM(MyBaseThing.EngineName, "Error finding or running script files", eMsgLevel.l1_Error, e.ToString())); MyBaseThing.SetStatus(3, $"Error while finding or running cdescript files: {e.Message}"); } }