From e29b4ee545648f7548f2042ea3927033d280d18a Mon Sep 17 00:00:00 2001 From: ORelio Date: Sun, 21 Jun 2015 18:45:43 +0200 Subject: [PATCH] Add support for C# script extensions - Allow defining function for use into the script - Allow defining a ChatBot for loading it into MCC - Improve sample script and add more examples - Todo add new documentation into the readme file --- MinecraftClient/ChatBot.cs | 5 +- MinecraftClient/ChatBots/Script.cs | 47 ++++++++++++++----- MinecraftClient/Settings.cs | 19 ++++---- .../config/sample-script-extended.cs | 30 ++++++++++++ .../config/sample-script-with-chatbot.cs | 35 ++++++++++++++ MinecraftClient/config/sample-script.cs | 5 +- 6 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 MinecraftClient/config/sample-script-extended.cs create mode 100644 MinecraftClient/config/sample-script-with-chatbot.cs diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index a3d57840..f1080d8d 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -33,9 +33,10 @@ namespace MinecraftClient { public enum DisconnectReason { InGameKick, LoginRejected, ConnectionLost }; - //Will be automatically set on bot loading, don't worry about this + //Handler will be automatically set on bot loading, don't worry about this public void SetHandler(McTcpClient handler) { this._handler = handler; } - public void SetMaster(ChatBot master) { this.master = master; } + protected void SetMaster(ChatBot master) { this.master = master; } + protected void LoadBot(ChatBot bot) { Handler.BotUnLoad(bot); Handler.BotLoad(bot); } private McTcpClient Handler { get { return master != null ? master.Handler : _handler; } } private McTcpClient _handler = null; private ChatBot master = null; diff --git a/MinecraftClient/ChatBots/Script.cs b/MinecraftClient/ChatBots/Script.cs index 44ae5891..6fc8573d 100644 --- a/MinecraftClient/ChatBots/Script.cs +++ b/MinecraftClient/ChatBots/Script.cs @@ -97,7 +97,7 @@ namespace MinecraftClient.ChatBots tpause = new ManualResetEvent(false); thread = new Thread(() => { - if (!RunCSharpScript(String.Join("\n", lines), file, tpause) && owner != null) + if (!RunCSharpScript() && owner != null) SendPrivateMessage(owner, "Script '" + file + "' failed to run."); }); thread.Start(); @@ -160,16 +160,37 @@ namespace MinecraftClient.ChatBots } } - private bool RunCSharpScript(string script, string filename = "C# Script", ManualResetEvent tpause = null) + private bool RunCSharpScript() { //Script compatibility check for handling future versions differently - if (!script.ToLower().StartsWith("//mccscript 1.0")) + if (lines.Length < 1 || lines[0] != "//MCCScript 1.0") { - ConsoleIO.WriteLineFormatted("§8Script file '" + filename + "' does not start with a valid //MCCScript comment."); + LogToConsole("Script file '" + file + "' does not start with a valid //MCCScript identifier."); return false; } - //Create a simple ChatBot class from the given script, allowing access to ChatBot API + //Process different sections of the script file + bool scriptMain = true; + List script = new List(); + List extensions = new List(); + foreach (string line in lines) + { + if (line.StartsWith("//MCCScript")) + { + if (line.EndsWith("Extensions")) + scriptMain = false; + } + else if (scriptMain) + { + script.Add(line); + //Add breakpoints for step-by-step execution of the script + if (tpause != null && line.Trim().EndsWith(";")) + script.Add("tpause.WaitOne();"); + } + else extensions.Add(line); + } + + //Generate a ChatBot class, allowing access to the ChatBot API string code = String.Join("\n", new string[] { "using System;", @@ -178,12 +199,12 @@ namespace MinecraftClient.ChatBots "using MinecraftClient;", "namespace ScriptLoader {", "public class Script : ChatBot {", - "public void Run(ChatBot master, ManualResetEvent tpause) {", + "public void __run(ChatBot master, ManualResetEvent tpause) {", "SetMaster(master);", - tpause != null - ? script.Replace(";\n", ";\ntpause.WaitOne();\n") - : script, - "}}}", + String.Join("\n", script), + "}", + String.Join("\n", extensions), + "}}", }); //Compile the C# class in memory using all the currently loaded assemblies @@ -202,16 +223,16 @@ namespace MinecraftClient.ChatBots //Process compile warnings and errors if (result.Errors.Count > 0) { - ConsoleIO.WriteLineFormatted("§8Error loading '" + filename + "':\n" + result.Errors[0].ErrorText); + LogToConsole("Error loading '" + file + "':\n" + result.Errors[0].ErrorText); return false; } //Run the compiled script with exception handling object compiledScript = result.CompiledAssembly.CreateInstance("ScriptLoader.Script"); - try { compiledScript.GetType().GetMethod("Run").Invoke(compiledScript, new object[] { this, tpause }); } + try { compiledScript.GetType().GetMethod("__run").Invoke(compiledScript, new object[] { this, tpause }); } catch (Exception e) { - ConsoleIO.WriteLineFormatted("§8Runtime error for '" + filename + "':\n" + e); + LogToConsole("Runtime error for '" + file + "':\n" + e); return false; } diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 735de5cd..f6fc9b3a 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -94,9 +94,9 @@ namespace MinecraftClient public static string AutoRespond_Matches = "matches.ini"; //Custom app variables and Minecraft accounts - private static Dictionary AppVars = new Dictionary(); - private static Dictionary> Accounts = new Dictionary>(); - private static Dictionary> Servers = new Dictionary>(); + private static readonly Dictionary AppVars = new Dictionary(); + private static readonly Dictionary> Accounts = new Dictionary>(); + private static readonly Dictionary> Servers = new Dictionary>(); private enum ParseMode { Default, Main, AppVars, Proxy, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, AutoRespond }; @@ -491,13 +491,16 @@ namespace MinecraftClient public static bool SetVar(string varName, string varData) { - varName = new string(varName.TakeWhile(char.IsLetterOrDigit).ToArray()).ToLower(); - if (varName.Length > 0) + lock (AppVars) { - AppVars[varName] = varData; - return true; + varName = new string(varName.TakeWhile(char.IsLetterOrDigit).ToArray()).ToLower(); + if (varName.Length > 0) + { + AppVars[varName] = varData; + return true; + } + else return false; } - else return false; } /// diff --git a/MinecraftClient/config/sample-script-extended.cs b/MinecraftClient/config/sample-script-extended.cs new file mode 100644 index 00000000..be6694da --- /dev/null +++ b/MinecraftClient/config/sample-script-extended.cs @@ -0,0 +1,30 @@ +//MCCScript 1.0 + +/* This script demonstrates how to add fields and methods */ + +for (int i = 0; i < 5; i++) +{ + int count = GetVarAsInt("test") + 1; + SetVar("test", count); + SendHelloWorld(count); + SleepBetweenSends(); +} + +//MCCScript Extensions + +/* Here you can define methods for use into your script */ + +void SendHelloWorld(int count) +{ + /* Warning: Do not make more than one server-related call into a method + * defined as a script extension eg SendText or switching servers, + * as execution flow is not managed in the Extensions section */ + + SendText("Hello World no. " + count); +} + +void SleepBetweenSends() +{ + LogToConsole("Sleeping for 5 seconds..."); + Thread.Sleep(5000); +} \ No newline at end of file diff --git a/MinecraftClient/config/sample-script-with-chatbot.cs b/MinecraftClient/config/sample-script-with-chatbot.cs new file mode 100644 index 00000000..264711fa --- /dev/null +++ b/MinecraftClient/config/sample-script-with-chatbot.cs @@ -0,0 +1,35 @@ +//MCCScript 1.0 + +/* This is a sample script that will load a ChatBot into Minecraft Console Client + * Simply execute the script once with /script or the script scheduler to load the bot */ + +LoadBot(new ExampleBot()); + +//MCCScript Extensions + +/* The ChatBot class must be defined as an extension of the script in the Extensions section + * The class can override common methods from ChatBot.cs, take a look at MCC's source code */ + +public class ExampleBot : ChatBot +{ + public override void Initialize() + { + LogToConsole("Sucessfully Initialized!"); + } + + public override void GetText(string text) + { + string message = ""; + string username = ""; + text = GetVerbatim(text); + + if (IsChatMessage(text, ref message, ref username)) + { + LogToConsole("Public message from " + username + ": " + message); + } + else if (IsPrivateMessage(text, ref message, ref username)) + { + LogToConsole("Private message from " + username + ": " + message); + } + } +} \ No newline at end of file diff --git a/MinecraftClient/config/sample-script.cs b/MinecraftClient/config/sample-script.cs index 6240c46a..3d61b25c 100644 --- a/MinecraftClient/config/sample-script.cs +++ b/MinecraftClient/config/sample-script.cs @@ -6,10 +6,9 @@ for (int i = 0; i < 5; i++) { - int count = GetVarAsInt("test"); - count++; + int count = GetVarAsInt("test") + 1; SetVar("test", count); SendText("Hello World no. " + count); - PerformInternalCommand("log Sleeping for 5 seconds..."); + LogToConsole("Sleeping for 5 seconds..."); Thread.Sleep(5000); } \ No newline at end of file