mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Various C# Script improvements
Move handling code in a separate file Add caching ability for low-power devices (rpi..) Use a distinct API with MCC.MethodName() Stop script execution only on specific API calls
This commit is contained in:
parent
ca02c7f2e6
commit
3e2622fbb7
8 changed files with 407 additions and 159 deletions
372
MinecraftClient/CSharpRunner.cs
Normal file
372
MinecraftClient/CSharpRunner.cs
Normal file
|
|
@ -0,0 +1,372 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.CSharp;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace MinecraftClient
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// C# Script runner - Compile on-the-fly and run C# scripts
|
||||||
|
/// </summary>
|
||||||
|
class CSharpRunner
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<ulong, Assembly> CompileCache = new Dictionary<ulong, Assembly>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Run the specified C# script file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="apiHandler">ChatBot handler for accessing ChatBot API</param>
|
||||||
|
/// <param name="tickHandler">Tick handler for waiting after some API calls</param>
|
||||||
|
/// <param name="lines">Lines of the script file to run</param>
|
||||||
|
/// <param name="args">Arguments to pass to the script</param>
|
||||||
|
/// <param name="run">Set to false to compile and cache the script without launching it</param>
|
||||||
|
/// <exception cref="CSharpException">Thrown if an error occured</exception>
|
||||||
|
/// <returns>Result of the execution, returned by the script</returns>
|
||||||
|
public static object Run(ChatBot apiHandler, ManualResetEvent tickHandler, string[] lines, string[] args, bool run = true)
|
||||||
|
{
|
||||||
|
//Script compatibility check for handling future versions differently
|
||||||
|
if (lines.Length < 1 || lines[0] != "//MCCScript 1.0")
|
||||||
|
throw new CSharpException(CSErrorType.InvalidScript,
|
||||||
|
new InvalidDataException("The provided script does not have a valid MCCScript header"));
|
||||||
|
|
||||||
|
//Script hash for determining if it was previously compiled
|
||||||
|
ulong scriptHash = QuickHash(lines);
|
||||||
|
Assembly assembly = null;
|
||||||
|
|
||||||
|
//No need to compile two scripts at the same time
|
||||||
|
lock (CompileCache)
|
||||||
|
{
|
||||||
|
///Process and compile script only if not already compiled
|
||||||
|
if (!Settings.CacheScripts || !CompileCache.ContainsKey(scriptHash))
|
||||||
|
{
|
||||||
|
//Process different sections of the script file
|
||||||
|
bool scriptMain = true;
|
||||||
|
List<string> script = new List<string>();
|
||||||
|
List<string> extensions = new List<string>();
|
||||||
|
foreach (string line in lines)
|
||||||
|
{
|
||||||
|
if (line.StartsWith("//MCCScript"))
|
||||||
|
{
|
||||||
|
if (line.EndsWith("Extensions"))
|
||||||
|
scriptMain = false;
|
||||||
|
}
|
||||||
|
else if (scriptMain)
|
||||||
|
script.Add(line);
|
||||||
|
else extensions.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add return statement if missing
|
||||||
|
if (script.All(line => !line.StartsWith("return ") && !line.Contains(" return ")))
|
||||||
|
script.Add("return null;");
|
||||||
|
|
||||||
|
//Generate a class from the given script
|
||||||
|
string code = String.Join("\n", new string[]
|
||||||
|
{
|
||||||
|
"using System;",
|
||||||
|
"using System.Collections.Generic;",
|
||||||
|
"using System.Linq;",
|
||||||
|
"using System.Text;",
|
||||||
|
"using System.IO;",
|
||||||
|
"using System.Threading;",
|
||||||
|
"using MinecraftClient;",
|
||||||
|
"namespace ScriptLoader {",
|
||||||
|
"public class Script {",
|
||||||
|
"public CSharpAPI MCC;",
|
||||||
|
"public object __run(CSharpAPI __apiHandler, string[] args) {",
|
||||||
|
"this.MCC = __apiHandler;",
|
||||||
|
String.Join("\n", script),
|
||||||
|
"}",
|
||||||
|
String.Join("\n", extensions),
|
||||||
|
"}}",
|
||||||
|
});
|
||||||
|
|
||||||
|
//Compile the C# class in memory using all the currently loaded assemblies
|
||||||
|
CSharpCodeProvider compiler = new CSharpCodeProvider();
|
||||||
|
CompilerParameters parameters = new CompilerParameters();
|
||||||
|
parameters.ReferencedAssemblies
|
||||||
|
.AddRange(AppDomain.CurrentDomain
|
||||||
|
.GetAssemblies()
|
||||||
|
.Where(a => !a.IsDynamic)
|
||||||
|
.Select(a => a.Location).ToArray());
|
||||||
|
parameters.CompilerOptions = "/t:library";
|
||||||
|
parameters.GenerateInMemory = true;
|
||||||
|
CompilerResults result = compiler.CompileAssemblyFromSource(parameters, code);
|
||||||
|
|
||||||
|
//Process compile warnings and errors
|
||||||
|
if (result.Errors.Count > 0)
|
||||||
|
throw new CSharpException(CSErrorType.LoadError,
|
||||||
|
new InvalidOperationException(result.Errors[0].ErrorText));
|
||||||
|
|
||||||
|
//Retrieve compiled assembly
|
||||||
|
assembly = result.CompiledAssembly;
|
||||||
|
if (Settings.CacheScripts)
|
||||||
|
CompileCache[scriptHash] = result.CompiledAssembly;
|
||||||
|
}
|
||||||
|
else if (Settings.CacheScripts)
|
||||||
|
assembly = CompileCache[scriptHash];
|
||||||
|
}
|
||||||
|
|
||||||
|
//Run the compiled assembly with exception handling
|
||||||
|
if (run)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object compiledScript
|
||||||
|
= CompileCache[scriptHash].CreateInstance("ScriptLoader.Script");
|
||||||
|
return
|
||||||
|
compiledScript
|
||||||
|
.GetType()
|
||||||
|
.GetMethod("__run")
|
||||||
|
.Invoke(compiledScript,
|
||||||
|
new object[] { new CSharpAPI(apiHandler, tickHandler), args });
|
||||||
|
}
|
||||||
|
catch (Exception e) { throw new CSharpException(CSErrorType.RuntimeError, e); }
|
||||||
|
}
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Quickly calculate a hash for the given script
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lines">script lines</param>
|
||||||
|
/// <returns>Quick hash as unsigned long</returns>
|
||||||
|
private static ulong QuickHash(string[] lines)
|
||||||
|
{
|
||||||
|
ulong hashedValue = 3074457345618258791ul;
|
||||||
|
for (int i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < lines[i].Length; j++)
|
||||||
|
{
|
||||||
|
hashedValue += lines[i][j];
|
||||||
|
hashedValue *= 3074457345618258799ul;
|
||||||
|
}
|
||||||
|
hashedValue += '\n';
|
||||||
|
hashedValue *= 3074457345618258799ul;
|
||||||
|
}
|
||||||
|
return hashedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describe a C# script error type
|
||||||
|
/// </summary>
|
||||||
|
public enum CSErrorType { FileReadError, InvalidScript, LoadError, RuntimeError };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Describe a C# script error with associated error type
|
||||||
|
/// </summary>
|
||||||
|
public class CSharpException : Exception
|
||||||
|
{
|
||||||
|
private CSErrorType _type;
|
||||||
|
public CSErrorType ExceptionType { get { return _type; } }
|
||||||
|
public override string Message { get { return InnerException.Message; } }
|
||||||
|
public override string ToString() { return InnerException.ToString(); }
|
||||||
|
public CSharpException(CSErrorType type, Exception inner)
|
||||||
|
: base(inner != null ? inner.Message : "", inner)
|
||||||
|
{
|
||||||
|
_type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the C# API object accessible from C# Scripts
|
||||||
|
/// </summary>
|
||||||
|
public class CSharpAPI : ChatBot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Thread blocking utility for stopping execution when making a ChatBot API call
|
||||||
|
/// </summary>
|
||||||
|
private ManualResetEvent tickHandler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new C# API Wrapper
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="apiHandler">ChatBot API Handler</param>
|
||||||
|
/// <param name="tickHandler">ChatBot tick handler</param>
|
||||||
|
public CSharpAPI(ChatBot apiHandler, ManualResetEvent tickHandler)
|
||||||
|
{
|
||||||
|
SetMaster(apiHandler);
|
||||||
|
this.tickHandler = tickHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* == Wrappers for ChatBot API with public visibility and call limit to one per tick for safety == */
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write some text in the console. Nothing will be sent to the server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">Log text to write</param>
|
||||||
|
new public void LogToConsole(object text)
|
||||||
|
{
|
||||||
|
base.LogToConsole(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send text to the server. Can be anything such as chat messages or commands
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">Text to send to the server</param>
|
||||||
|
/// <returns>True if the text was sent with no error</returns>
|
||||||
|
new public bool SendText(object text)
|
||||||
|
{
|
||||||
|
bool result = base.SendText(text is string ? (string)text : text.ToString());
|
||||||
|
tickHandler.WaitOne();
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform an internal MCC command (not a server command, use SendText() instead for that!)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command">The command to process</param>
|
||||||
|
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
|
||||||
|
new public bool PerformInternalCommand(string command)
|
||||||
|
{
|
||||||
|
bool result = base.PerformInternalCommand(command);
|
||||||
|
tickHandler.WaitOne();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnect from the server and restart the program
|
||||||
|
/// It will unload and reload all the bots and then reconnect to the server
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="extraAttempts">If connection fails, the client will make X extra attempts</param>
|
||||||
|
new public void ReconnectToTheServer(int extraAttempts = -999999)
|
||||||
|
{
|
||||||
|
if (extraAttempts == -999999)
|
||||||
|
base.ReconnectToTheServer();
|
||||||
|
else base.ReconnectToTheServer(extraAttempts);
|
||||||
|
tickHandler.WaitOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnect from the server and exit the program
|
||||||
|
/// </summary>
|
||||||
|
new public void DisconnectAndExit()
|
||||||
|
{
|
||||||
|
base.DisconnectAndExit();
|
||||||
|
tickHandler.WaitOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load the provided ChatBot object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bot">Bot to load</param>
|
||||||
|
new public void LoadBot(ChatBot bot)
|
||||||
|
{
|
||||||
|
base.LoadBot(bot);
|
||||||
|
tickHandler.WaitOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* == Additional Methods useful for Script API == */
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a global variable by name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="varName">Name of the variable</param>
|
||||||
|
/// <returns>Value of the variable or null if no variable</returns>
|
||||||
|
public object GetVar(string varName)
|
||||||
|
{
|
||||||
|
return Settings.GetVar(varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a global variable by name, as a string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="varName">Name of the variable</param>
|
||||||
|
/// <returns>Value of the variable as string, or null if no variable</returns>
|
||||||
|
public string GetVarAsString(string varName)
|
||||||
|
{
|
||||||
|
object val = GetVar(varName);
|
||||||
|
if (val != null)
|
||||||
|
return val.ToString();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a global variable by name, as an integer
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="varName">Name of the variable</param>
|
||||||
|
/// <returns>Value of the variable as int, or 0 if no variable or not a number</returns>
|
||||||
|
public int GetVarAsInt(string varName)
|
||||||
|
{
|
||||||
|
if (GetVar(varName) is int)
|
||||||
|
return (int)GetVar(varName);
|
||||||
|
int result;
|
||||||
|
if (int.TryParse(GetVarAsString(varName), out result))
|
||||||
|
return result;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a global variable by name, as a boolean
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="varName">Name of the variable</param>
|
||||||
|
/// <returns>Value of the variable as bool, or false if no variable or not a boolean</returns>
|
||||||
|
public bool GetVarAsBool(string varName)
|
||||||
|
{
|
||||||
|
if (GetVar(varName) is bool)
|
||||||
|
return (bool)GetVar(varName);
|
||||||
|
bool result;
|
||||||
|
if (bool.TryParse(GetVarAsString(varName), out result))
|
||||||
|
return result;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a global variable for further use in any other script
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="varName">Name of the variable</param>
|
||||||
|
/// <param name="varValue">Value of the variable</param>
|
||||||
|
public bool SetVar(string varName, object varValue)
|
||||||
|
{
|
||||||
|
return Settings.SetVar(varName, varValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load login/password using an account alias and optionally reconnect to the server
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="accountAlias">Account alias</param>
|
||||||
|
/// <param name="andReconnect">Set to true to reconnecto to the server afterwards</param>
|
||||||
|
/// <returns>True if the account was found and loaded</returns>
|
||||||
|
public bool SetAccount(string accountAlias, bool andReconnect = false)
|
||||||
|
{
|
||||||
|
bool result = Settings.SetAccount(accountAlias);
|
||||||
|
if (result && andReconnect)
|
||||||
|
ReconnectToTheServer();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load new server information and optionally reconnect to the server
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="server">"serverip:port" couple or server alias</param>
|
||||||
|
/// <returns>True if the server IP was valid and loaded, false otherwise</returns>
|
||||||
|
public bool SetServer(string server, bool andReconnect = false)
|
||||||
|
{
|
||||||
|
bool result = Settings.SetServerIP(server);
|
||||||
|
if (result && andReconnect)
|
||||||
|
ReconnectToTheServer();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Synchronously call another script and retrieve the result
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="script">Script to call</param>
|
||||||
|
/// <param name="args">Arguments to pass to the script</param>
|
||||||
|
/// <returns>An object returned by the script, or null</returns>
|
||||||
|
public object CallScript(string script, string[] args)
|
||||||
|
{
|
||||||
|
string[] lines = null;
|
||||||
|
ChatBots.Script.LookForScript(ref script);
|
||||||
|
try { lines = File.ReadAllLines(script); }
|
||||||
|
catch (Exception e) { throw new CSharpException(CSErrorType.FileReadError, e); }
|
||||||
|
return CSharpRunner.Run(this, tickHandler, lines, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -85,12 +85,10 @@ namespace MinecraftClient
|
||||||
/// <param name="text">Text to send to the server</param>
|
/// <param name="text">Text to send to the server</param>
|
||||||
/// <returns>True if the text was sent with no error</returns>
|
/// <returns>True if the text was sent with no error</returns>
|
||||||
|
|
||||||
protected bool SendText(object text)
|
protected bool SendText(string text)
|
||||||
{
|
{
|
||||||
LogToConsole("Sending '" + text + "'");
|
LogToConsole("Sending '" + text + "'");
|
||||||
bool result = Handler.SendText(text is string ? (string)text : text.ToString());
|
return Handler.SendText(text);
|
||||||
Thread.Sleep(1000);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -348,7 +346,7 @@ namespace MinecraftClient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text">Log text to write</param>
|
/// <param name="text">Log text to write</param>
|
||||||
|
|
||||||
public void LogToConsole(object text)
|
protected void LogToConsole(object text)
|
||||||
{
|
{
|
||||||
ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", this.GetType().Name, text));
|
ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", this.GetType().Name, text));
|
||||||
string logfile = Settings.ExpandVars(Settings.chatbotLogFile);
|
string logfile = Settings.ExpandVars(Settings.chatbotLogFile);
|
||||||
|
|
@ -458,59 +456,5 @@ namespace MinecraftClient
|
||||||
return new string[0];
|
return new string[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Set a custom %variable% which will be available through expandVars()
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="varName">Name of the variable</param>
|
|
||||||
/// <param name="varData">Value of the variable</param>
|
|
||||||
/// <returns>True if the parameters were valid</returns>
|
|
||||||
|
|
||||||
protected static bool SetVar(string varName, object varData)
|
|
||||||
{
|
|
||||||
return Settings.SetVar(varName, varData.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a custom %variable% or null if the variable does not exist
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="varName">Variable name</param>
|
|
||||||
/// <returns>The value or null if the variable does not exists</returns>
|
|
||||||
|
|
||||||
protected static string GetVar(string varName)
|
|
||||||
{
|
|
||||||
return Settings.GetVar(varName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a custom %variable% as an Integer or null if the variable does not exist
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="varName">Variable name</param>
|
|
||||||
/// <returns>The value or null if the variable does not exists</returns>
|
|
||||||
|
|
||||||
protected static int GetVarAsInt(string varName)
|
|
||||||
{
|
|
||||||
return Settings.str2int(Settings.GetVar(varName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load login/password using an account alias
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if the account was found and loaded</returns>
|
|
||||||
|
|
||||||
protected static bool SetAccount(string accountAlias)
|
|
||||||
{
|
|
||||||
return Settings.SetAccount(accountAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load server information in ServerIP and ServerPort variables from a "serverip:port" couple or server alias
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if the server IP was valid and loaded, false otherwise</returns>
|
|
||||||
|
|
||||||
protected static bool SetServerIP(string server)
|
|
||||||
{
|
|
||||||
return Settings.SetServerIP(server);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.CSharp;
|
using Microsoft.CSharp;
|
||||||
using System.CodeDom.Compiler;
|
using System.CodeDom.Compiler;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace MinecraftClient.ChatBots
|
namespace MinecraftClient.ChatBots
|
||||||
{
|
{
|
||||||
|
|
@ -143,8 +144,18 @@ namespace MinecraftClient.ChatBots
|
||||||
tpause = new ManualResetEvent(false);
|
tpause = new ManualResetEvent(false);
|
||||||
thread = new Thread(() =>
|
thread = new Thread(() =>
|
||||||
{
|
{
|
||||||
if (!RunCSharpScript() && owner != null)
|
try
|
||||||
SendPrivateMessage(owner, "Script '" + file + "' failed to run.");
|
{
|
||||||
|
CSharpRunner.Run(this, tpause, lines, args);
|
||||||
|
}
|
||||||
|
catch (CSharpException e)
|
||||||
|
{
|
||||||
|
string errorMessage = "Script '" + file + "' failed to run (" + e.ExceptionType + ").";
|
||||||
|
LogToConsole(errorMessage);
|
||||||
|
if (owner != null)
|
||||||
|
SendPrivateMessage(owner, errorMessage);
|
||||||
|
LogToConsole(e.InnerException);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
thread.Start();
|
thread.Start();
|
||||||
}
|
}
|
||||||
|
|
@ -154,7 +165,7 @@ namespace MinecraftClient.ChatBots
|
||||||
{
|
{
|
||||||
tpause.Set();
|
tpause.Set();
|
||||||
tpause.Reset();
|
tpause.Reset();
|
||||||
if (thread.Join(100))
|
if (!thread.IsAlive)
|
||||||
UnloadBot();
|
UnloadBot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -205,84 +216,5 @@ namespace MinecraftClient.ChatBots
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool RunCSharpScript()
|
|
||||||
{
|
|
||||||
//Script compatibility check for handling future versions differently
|
|
||||||
if (lines.Length < 1 || lines[0] != "//MCCScript 1.0")
|
|
||||||
{
|
|
||||||
LogToConsole("Script file '" + file + "' does not start with a valid //MCCScript identifier.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Process different sections of the script file
|
|
||||||
bool scriptMain = true;
|
|
||||||
List<string> script = new List<string>();
|
|
||||||
List<string> extensions = new List<string>();
|
|
||||||
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;",
|
|
||||||
"using System.IO;",
|
|
||||||
"using System.Threading;",
|
|
||||||
"using MinecraftClient;",
|
|
||||||
"namespace ScriptLoader {",
|
|
||||||
"public class Script : ChatBot {",
|
|
||||||
"public void __run(ChatBot master, ManualResetEvent tpause, string[] args) {",
|
|
||||||
"SetMaster(master);",
|
|
||||||
String.Join("\n", script),
|
|
||||||
"}",
|
|
||||||
String.Join("\n", extensions),
|
|
||||||
"}}",
|
|
||||||
});
|
|
||||||
|
|
||||||
//Compile the C# class in memory using all the currently loaded assemblies
|
|
||||||
CSharpCodeProvider compiler = new CSharpCodeProvider();
|
|
||||||
CompilerParameters parameters = new CompilerParameters();
|
|
||||||
parameters.ReferencedAssemblies
|
|
||||||
.AddRange(AppDomain.CurrentDomain
|
|
||||||
.GetAssemblies()
|
|
||||||
.Where(a => !a.IsDynamic)
|
|
||||||
.Select(a => a.Location).ToArray());
|
|
||||||
parameters.CompilerOptions = "/t:library";
|
|
||||||
parameters.GenerateInMemory = true;
|
|
||||||
CompilerResults result
|
|
||||||
= compiler.CompileAssemblyFromSource(parameters, code);
|
|
||||||
|
|
||||||
//Process compile warnings and errors
|
|
||||||
if (result.Errors.Count > 0)
|
|
||||||
{
|
|
||||||
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, args }); }
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LogToConsole("Runtime error for '" + file + "':\n" + e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@
|
||||||
<Compile Include="Crypto\Streams\MonoAesStream.cs" />
|
<Compile Include="Crypto\Streams\MonoAesStream.cs" />
|
||||||
<Compile Include="Crypto\Streams\RegularAesStream.cs" />
|
<Compile Include="Crypto\Streams\RegularAesStream.cs" />
|
||||||
<Compile Include="Crypto\CryptoHandler.cs" />
|
<Compile Include="Crypto\CryptoHandler.cs" />
|
||||||
|
<Compile Include="CSharpRunner.cs" />
|
||||||
<Compile Include="Protocol\Handlers\Compression\CRC32.cs" />
|
<Compile Include="Protocol\Handlers\Compression\CRC32.cs" />
|
||||||
<Compile Include="Protocol\Handlers\Compression\Deflate.cs" />
|
<Compile Include="Protocol\Handlers\Compression\Deflate.cs" />
|
||||||
<Compile Include="Protocol\Handlers\Compression\GZipStream.cs" />
|
<Compile Include="Protocol\Handlers\Compression\GZipStream.cs" />
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ namespace MinecraftClient
|
||||||
public static char internalCmdChar = '/';
|
public static char internalCmdChar = '/';
|
||||||
public static bool playerHeadAsIcon = false;
|
public static bool playerHeadAsIcon = false;
|
||||||
public static string chatbotLogFile = "";
|
public static string chatbotLogFile = "";
|
||||||
|
public static bool CacheScripts = true;
|
||||||
|
|
||||||
//AntiAFK Settings
|
//AntiAFK Settings
|
||||||
public static bool AntiAFK_Enabled = false;
|
public static bool AntiAFK_Enabled = false;
|
||||||
|
|
@ -94,7 +95,7 @@ namespace MinecraftClient
|
||||||
public static string AutoRespond_Matches = "matches.ini";
|
public static string AutoRespond_Matches = "matches.ini";
|
||||||
|
|
||||||
//Custom app variables and Minecraft accounts
|
//Custom app variables and Minecraft accounts
|
||||||
private static readonly Dictionary<string, string> AppVars = new Dictionary<string, string>();
|
private static readonly Dictionary<string, object> AppVars = new Dictionary<string, object>();
|
||||||
private static readonly Dictionary<string, KeyValuePair<string, string>> Accounts = new Dictionary<string, KeyValuePair<string, string>>();
|
private static readonly Dictionary<string, KeyValuePair<string, string>> Accounts = new Dictionary<string, KeyValuePair<string, string>>();
|
||||||
private static readonly Dictionary<string, KeyValuePair<string, ushort>> Servers = new Dictionary<string, KeyValuePair<string, ushort>>();
|
private static readonly Dictionary<string, KeyValuePair<string, ushort>> Servers = new Dictionary<string, KeyValuePair<string, ushort>>();
|
||||||
|
|
||||||
|
|
@ -159,6 +160,7 @@ namespace MinecraftClient
|
||||||
case "chatbotlogfile": chatbotLogFile = argValue; break;
|
case "chatbotlogfile": chatbotLogFile = argValue; break;
|
||||||
case "mcversion": ServerVersion = argValue; break;
|
case "mcversion": ServerVersion = argValue; break;
|
||||||
case "splitmessagedelay": splitMessageDelay = TimeSpan.FromSeconds(str2int(argValue)); break;
|
case "splitmessagedelay": splitMessageDelay = TimeSpan.FromSeconds(str2int(argValue)); break;
|
||||||
|
case "scriptcache": CacheScripts = str2bool(argValue); break;
|
||||||
|
|
||||||
case "botowners":
|
case "botowners":
|
||||||
Bots_Owners.Clear();
|
Bots_Owners.Clear();
|
||||||
|
|
@ -366,6 +368,7 @@ namespace MinecraftClient
|
||||||
+ "serverlist=servers.txt\r\n"
|
+ "serverlist=servers.txt\r\n"
|
||||||
+ "playerheadicon=true\r\n"
|
+ "playerheadicon=true\r\n"
|
||||||
+ "exitonfailure=false\r\n"
|
+ "exitonfailure=false\r\n"
|
||||||
|
+ "scriptcache=true\r\n"
|
||||||
+ "timestamps=false\r\n"
|
+ "timestamps=false\r\n"
|
||||||
+ "\r\n"
|
+ "\r\n"
|
||||||
+ "[AppVars]\r\n"
|
+ "[AppVars]\r\n"
|
||||||
|
|
@ -491,7 +494,7 @@ namespace MinecraftClient
|
||||||
/// <param name="varData">Value of the variable</param>
|
/// <param name="varData">Value of the variable</param>
|
||||||
/// <returns>True if the parameters were valid</returns>
|
/// <returns>True if the parameters were valid</returns>
|
||||||
|
|
||||||
public static bool SetVar(string varName, string varData)
|
public static bool SetVar(string varName, object varData)
|
||||||
{
|
{
|
||||||
lock (AppVars)
|
lock (AppVars)
|
||||||
{
|
{
|
||||||
|
|
@ -511,7 +514,7 @@ namespace MinecraftClient
|
||||||
/// <param name="varName">Variable name</param>
|
/// <param name="varName">Variable name</param>
|
||||||
/// <returns>The value or null if the variable does not exists</returns>
|
/// <returns>The value or null if the variable does not exists</returns>
|
||||||
|
|
||||||
public static string GetVar(string varName)
|
public static object GetVar(string varName)
|
||||||
{
|
{
|
||||||
if (AppVars.ContainsKey(varName))
|
if (AppVars.ContainsKey(varName))
|
||||||
return AppVars[varName];
|
return AppVars[varName];
|
||||||
|
|
@ -559,7 +562,7 @@ namespace MinecraftClient
|
||||||
default:
|
default:
|
||||||
if (AppVars.ContainsKey(varname_lower))
|
if (AppVars.ContainsKey(varname_lower))
|
||||||
{
|
{
|
||||||
result.Append(AppVars[varname_lower]);
|
result.Append(AppVars[varname_lower].ToString());
|
||||||
}
|
}
|
||||||
else result.Append("%" + varname + '%');
|
else result.Append("%" + varname + '%');
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ if (args.Length > 0)
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
int count = GetVarAsInt("test") + 1;
|
int count = MCC.GetVarAsInt("test") + 1;
|
||||||
SetVar("test", count);
|
MCC.SetVar("test", count);
|
||||||
SendHelloWorld(count, text);
|
SendHelloWorld(count, text);
|
||||||
SleepBetweenSends();
|
SleepBetweenSends();
|
||||||
}
|
}
|
||||||
|
|
@ -21,15 +21,11 @@ for (int i = 0; i < 5; i++)
|
||||||
|
|
||||||
void SendHelloWorld(int count, string text)
|
void SendHelloWorld(int count, string text)
|
||||||
{
|
{
|
||||||
/* Warning: Do not make more than one server-related call into a method
|
MCC.SendText("Hello World no. " + count + ": " + text);
|
||||||
* 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 + ": " + text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SleepBetweenSends()
|
void SleepBetweenSends()
|
||||||
{
|
{
|
||||||
LogToConsole("Sleeping for 5 seconds...");
|
MCC.LogToConsole("Sleeping for 5 seconds...");
|
||||||
Thread.Sleep(5000);
|
Thread.Sleep(5000);
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
/* This is a sample script that will load a ChatBot into Minecraft Console Client
|
/* 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 */
|
* Simply execute the script once with /script or the script scheduler to load the bot */
|
||||||
|
|
||||||
LoadBot(new ExampleBot());
|
MCC.LoadBot(new ExampleBot());
|
||||||
|
|
||||||
//MCCScript Extensions
|
//MCCScript Extensions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
/* This is a sample script for Minecraft Console Client
|
/* This is a sample script for Minecraft Console Client
|
||||||
* The code provided in this file will be compiled at runtime and executed
|
* The code provided in this file will be compiled at runtime and executed
|
||||||
* Allowed instructions: Any C# code AND all methods provided by the bot API */
|
* Allowed instructions: Any C# code AND methods provided by the MCC API */
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++)
|
for (int i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
int count = GetVarAsInt("test") + 1;
|
int count = MCC.GetVarAsInt("test") + 1;
|
||||||
SetVar("test", count);
|
MCC.SetVar("test", count);
|
||||||
SendText("Hello World no. " + count);
|
MCC.SendText("Hello World no. " + count);
|
||||||
LogToConsole("Sleeping for 5 seconds...");
|
MCC.LogToConsole("Sleeping for 5 seconds...");
|
||||||
Thread.Sleep(5000);
|
Thread.Sleep(5000);
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue