From a5423211ad5471a6b63010033c8d3d519a87f621 Mon Sep 17 00:00:00 2001 From: Anon Date: Sat, 6 Jun 2026 12:09:36 +0200 Subject: [PATCH] Fixed Script Scheduler bug --- MinecraftClient/ChatBots/Script.cs | 10 +- MinecraftClient/ChatBots/ScriptScheduler.cs | 124 ++++++++++++------ MinecraftClient/McClient.cs | 22 +++- MinecraftClient/Scripting/CSharpRunner.cs | 7 +- MinecraftClient/Scripting/ChatBot.cs | 13 +- .../DynamicRun/Builder/CompileRunner.cs | 10 +- 6 files changed, 131 insertions(+), 55 deletions(-) diff --git a/MinecraftClient/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs index e2bb6636..4f764aa4 100644 --- a/MinecraftClient/ChatBots/Script.cs +++ b/MinecraftClient/ChatBots/Script.cs @@ -25,6 +25,7 @@ namespace MinecraftClient.ChatBots private bool csharp; private Thread? thread; private readonly Dictionary? localVars; + private readonly string? scriptOwnerKey; public Script(string filename) { @@ -38,6 +39,13 @@ namespace MinecraftClient.ChatBots this.localVars = localVars; } + internal Script(string filename, string? ownername, Dictionary? localVars, string? scriptOwnerKey) + : this(filename, ownername, localVars) + { + this.scriptOwnerKey = scriptOwnerKey; + SetScriptOwnerKey(scriptOwnerKey); + } + private void ParseArguments(string argstr) { List args = new(); @@ -166,7 +174,7 @@ namespace MinecraftClient.ChatBots { try { - CSharpRunner.Run(this, lines, args, localVars, scriptName: file!); + CSharpRunner.Run(this, lines, args, localVars, scriptName: file!, scriptOwnerKey: scriptOwnerKey); } catch (CSharpException e) { diff --git a/MinecraftClient/ChatBots/ScriptScheduler.cs b/MinecraftClient/ChatBots/ScriptScheduler.cs index 8da875f9..f5af44ec 100644 --- a/MinecraftClient/ChatBots/ScriptScheduler.cs +++ b/MinecraftClient/ChatBots/ScriptScheduler.cs @@ -183,64 +183,54 @@ namespace MinecraftClient.ChatBots private int verifytasks_timeleft = Settings.ClientTicksPerSecond; private readonly int verifytasks_delay = Settings.ClientTicksPerSecond; + public override void AfterGameJoined() + { + if (serverlogin_done) + return; + + serverlogin_done = true; + verifytasks_timeleft = verifytasks_delay; + RunLoginTasks(); + } + public override void Update() { + if (!serverlogin_done) + return; + if (verifytasks_timeleft <= 0) { verifytasks_timeleft = verifytasks_delay; - if (serverlogin_done) + for (int taskIndex = 0; taskIndex < Config.TaskList.Length; taskIndex++) { - foreach (TaskConfig task in Config.TaskList) + TaskConfig task = Config.TaskList[taskIndex]; + if (task.Trigger_On_Times.Enable) { - if (task.Trigger_On_Times.Enable) - { - bool matching_time_found = false; + bool matching_time_found = false; - foreach (TimeSpan time in task.Trigger_On_Times.Times) + foreach (TimeSpan time in task.Trigger_On_Times.Times) + { + if (time.Hours == DateTime.Now.Hour && time.Minutes == DateTime.Now.Minute) { - if (time.Hours == DateTime.Now.Hour && time.Minutes == DateTime.Now.Minute) + matching_time_found = true; + if (!task.Trigger_On_Time_Already_Triggered) { - matching_time_found = true; - if (!task.Trigger_On_Time_Already_Triggered) - { - task.Trigger_On_Time_Already_Triggered = true; - LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_time, task.Action)); - CmdResult response = new(); - PerformInternalCommand(task.Action, ref response); - if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) - LogToConsole(response); - } + task.Trigger_On_Time_Already_Triggered = true; + RunTaskAction(task, taskIndex, string.Format(Translations.bot_scriptScheduler_running_time, task.Action)); } } - - if (!matching_time_found) - task.Trigger_On_Time_Already_Triggered = false; } + if (!matching_time_found) + task.Trigger_On_Time_Already_Triggered = false; } } - else - { - foreach (TaskConfig task in Config.TaskList) - { - if (task.Trigger_On_Login || (firstlogin_done == false && task.Trigger_On_First_Login)) - { - LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_login, task.Action)); - CmdResult response = new(); - PerformInternalCommand(task.Action, ref response); - if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) - LogToConsole(response); - } - } - - firstlogin_done = true; - serverlogin_done = true; - } } else verifytasks_timeleft--; - foreach (TaskConfig task in Config.TaskList) + for (int taskIndex = 0; taskIndex < Config.TaskList.Length; taskIndex++) { + TaskConfig task = Config.TaskList[taskIndex]; if (task.Trigger_On_Interval.Enable) { if (task.Trigger_On_Interval_Countdown == 0) @@ -248,11 +238,7 @@ namespace MinecraftClient.ChatBots task.Trigger_On_Interval_Countdown = random.Next( Settings.DoubleToTick(task.Trigger_On_Interval.MinTime), Settings.DoubleToTick(task.Trigger_On_Interval.MaxTime) ); - LogDebugToConsole(string.Format(Translations.bot_scriptScheduler_running_inverval, task.Action)); - CmdResult response = new(); - PerformInternalCommand(task.Action, ref response); - if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) - LogToConsole(response); + RunTaskAction(task, taskIndex, string.Format(Translations.bot_scriptScheduler_running_inverval, task.Action)); } else task.Trigger_On_Interval_Countdown--; } @@ -265,6 +251,58 @@ namespace MinecraftClient.ChatBots return false; } + private void RunLoginTasks() + { + bool isFirstLogin = !firstlogin_done; + + for (int taskIndex = 0; taskIndex < Config.TaskList.Length; taskIndex++) + { + TaskConfig task = Config.TaskList[taskIndex]; + if (task.Trigger_On_Login || (isFirstLogin && task.Trigger_On_First_Login)) + RunTaskAction(task, taskIndex, string.Format(Translations.bot_scriptScheduler_running_login, task.Action)); + } + + firstlogin_done = true; + } + + private void RunTaskAction(TaskConfig task, int taskIndex, string debugMessage) + { + LogDebugToConsole(debugMessage); + + if (TryRunOwnedScript(task, taskIndex)) + return; + + CmdResult response = new(); + PerformInternalCommand(task.Action, ref response); + if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result)) + LogToConsole(response); + } + + private bool TryRunOwnedScript(TaskConfig task, int taskIndex) + { + string action = task.Action.Trim(); + const string scriptCommand = "script"; + if (!action.StartsWith(scriptCommand, StringComparison.OrdinalIgnoreCase)) + return false; + + if (action.Length == scriptCommand.Length || !char.IsWhiteSpace(action[scriptCommand.Length])) + return false; + + string scriptArgs = action[scriptCommand.Length..].Trim(); + if (string.IsNullOrWhiteSpace(scriptArgs)) + return false; + + string scriptOwnerKey = BuildScriptOwnerKey(task, taskIndex); + Handler.UnloadBotsByScriptOwnerKey(scriptOwnerKey); + Handler.BotLoad(new Script(scriptArgs, null, null, scriptOwnerKey)); + return true; + } + + private static string BuildScriptOwnerKey(TaskConfig task, int taskIndex) + { + return $"{nameof(ScriptScheduler)}:{taskIndex}:{task.Task_Name}:{task.Action.Trim()}"; + } + private static string Task2String(TaskConfig task) { return string.Format( diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index e19a0260..a0a0ca4d 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -867,8 +867,11 @@ namespace MinecraftClient DispatchBotEvent(bot => bot.OnDisconnect(ChatBot.DisconnectReason.UserLogout, "")); + foreach (ChatBot bot in bots.Where(bot => bot.ScriptOwnerKey is not null).ToList()) + BotUnLoad(bot); + botsOnHold.Clear(); - botsOnHold.AddRange(bots); + botsOnHold.AddRange(bots.Where(bot => bot.ScriptOwnerKey is null)); if (handler is not null) { @@ -1263,7 +1266,7 @@ namespace MinecraftClient bots.Add(b); if (init) DispatchBotEvent(bot => bot.Initialize(), [b]); - if (handler is not null) + if (CanSendMessage) DispatchBotEvent(bot => bot.AfterGameJoined(), [b]); } @@ -1291,6 +1294,21 @@ namespace MinecraftClient } } + internal void UnloadBotsByScriptOwnerKey(string scriptOwnerKey) + { + if (InvokeRequired) + { + InvokeOnMainThread(() => UnloadBotsByScriptOwnerKey(scriptOwnerKey)); + return; + } + + foreach (ChatBot bot in GetLoadedChatBots()) + { + if (bot.ScriptOwnerKey == scriptOwnerKey) + BotUnLoad(bot); + } + } + /// /// Clear bots /// diff --git a/MinecraftClient/Scripting/CSharpRunner.cs b/MinecraftClient/Scripting/CSharpRunner.cs index 158b6eae..668ab336 100644 --- a/MinecraftClient/Scripting/CSharpRunner.cs +++ b/MinecraftClient/Scripting/CSharpRunner.cs @@ -26,7 +26,7 @@ namespace MinecraftClient.Scripting /// Set to false to compile and cache the script without launching it /// Thrown if an error occured /// Result of the execution, returned by the script - public static object? Run(ChatBot apiHandler, string[] lines, string[] args, Dictionary? localVars, bool run = true, string scriptName = "Unknown Script") + public static object? Run(ChatBot apiHandler, string[] lines, string[] args, Dictionary? localVars, bool run = true, string scriptName = "Unknown Script", string? scriptOwnerKey = null) { //Script compatibility check for handling future versions differently if (lines.Length < 1 || lines[0] != "//MCCScript 1.0") @@ -143,7 +143,7 @@ namespace MinecraftClient.Scripting { try { - var compiled = runner.Execute(assembly!, args, localVars, apiHandler); + var compiled = runner.Execute(assembly!, args, localVars, apiHandler, scriptOwnerKey); return compiled; } catch (Exception e) { throw new CSharpException(CSErrorType.RuntimeError, e); } @@ -209,10 +209,11 @@ namespace MinecraftClient.Scripting /// ChatBot API Handler /// ChatBot tick handler /// Local variables passed along with the script - public CSharpAPI(ChatBot apiHandler, Dictionary? localVars) + public CSharpAPI(ChatBot apiHandler, Dictionary? localVars, string? scriptOwnerKey = null) { SetMaster(apiHandler); this.localVars = localVars; + SetScriptOwnerKey(scriptOwnerKey); } /// diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index 3206092a..b8f21e35 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -39,9 +39,20 @@ namespace MinecraftClient.Scripting //Handler will be automatically set on bot loading, don't worry about this public void SetHandler(McClient handler) { _handler = handler; } protected void SetMaster(ChatBot master) { this.master = master; } - protected void LoadBot(ChatBot bot) { Handler.BotUnLoad(bot); Handler.BotLoad(bot); } + protected void LoadBot(ChatBot bot) + { + if (ScriptOwnerKey is not null) + bot.SetScriptOwnerKey(ScriptOwnerKey); + + if (Handler.GetLoadedChatBots().Any(loadedBot => ReferenceEquals(loadedBot, bot))) + Handler.BotUnLoad(bot); + + Handler.BotLoad(bot); + } protected List GetLoadedChatBots() { return Handler.GetLoadedChatBots(); } protected void UnLoadBot(ChatBot bot) { Handler.BotUnLoad(bot); } + internal string? ScriptOwnerKey { get; private set; } + internal void SetScriptOwnerKey(string? scriptOwnerKey) { ScriptOwnerKey = scriptOwnerKey; } private McClient? _handler = null; private ChatBot? master = null; private readonly List registeredPluginChannels = new(); diff --git a/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs b/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs index ee5bf460..cdd102a1 100644 --- a/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs +++ b/MinecraftClient/Scripting/DynamicRun/Builder/CompileRunner.cs @@ -13,9 +13,9 @@ namespace MinecraftClient.Scripting.DynamicRun.Builder { internal class CompileRunner { - public object? Execute(byte[] compiledAssembly, string[] args, Dictionary? localVars, ChatBot apiHandler) + public object? Execute(byte[] compiledAssembly, string[] args, Dictionary? localVars, ChatBot apiHandler, string? scriptOwnerKey = null) { - var assemblyLoadContextWeakRef = LoadAndExecute(compiledAssembly, args, localVars, apiHandler); + var assemblyLoadContextWeakRef = LoadAndExecute(compiledAssembly, args, localVars, apiHandler, scriptOwnerKey); for (var i = 0; i < 8 && assemblyLoadContextWeakRef.Item1.IsAlive; i++) { @@ -28,18 +28,18 @@ namespace MinecraftClient.Scripting.DynamicRun.Builder } [MethodImpl(MethodImplOptions.NoInlining)] - private static Tuple LoadAndExecute(byte[] compiledAssembly, string[] args, Dictionary? localVars, ChatBot apiHandler) + private static Tuple LoadAndExecute(byte[] compiledAssembly, string[] args, Dictionary? localVars, ChatBot apiHandler, string? scriptOwnerKey) { using var asm = new MemoryStream(compiledAssembly); var assemblyLoadContext = new SimpleUnloadableAssemblyLoadContext(); var assembly = assemblyLoadContext.LoadFromStream(asm); var compiledScript = assembly.CreateInstance("ScriptLoader.Script")!; - var execResult = compiledScript.GetType().GetMethod("__run")!.Invoke(compiledScript, new object[] { new CSharpAPI(apiHandler, localVars), args }); + var execResult = compiledScript.GetType().GetMethod("__run")!.Invoke(compiledScript, new object[] { new CSharpAPI(apiHandler, localVars, scriptOwnerKey), args }); assemblyLoadContext.Unload(); return new(new WeakReference(assemblyLoadContext), execResult); } } -} \ No newline at end of file +}