Add support for C# scripts in scripting bot

- Now scripts can also be written in C#
- C# scripts can access ChatBot API
- Add more methods in ChatBot API
- Add an example of C# script file
- Coding style fixes: method names ucfirst
This commit is contained in:
ORelio 2015-06-20 22:58:18 +02:00
parent 3224c59eab
commit 3ce91188c7
22 changed files with 321 additions and 135 deletions

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.IO; using System.IO;
using System.Threading;
namespace MinecraftClient namespace MinecraftClient
{ {
@ -33,8 +34,11 @@ namespace MinecraftClient
public enum DisconnectReason { InGameKick, LoginRejected, ConnectionLost }; public enum DisconnectReason { InGameKick, LoginRejected, ConnectionLost };
//Will be automatically set on bot loading, don't worry about this //Will be automatically set on bot loading, don't worry about this
public void SetHandler(McTcpClient handler) { this.handler = handler; } public void SetHandler(McTcpClient handler) { this._handler = handler; }
private McTcpClient handler; public void SetMaster(ChatBot master) { this.master = master; }
private McTcpClient Handler { get { return master != null ? master.Handler : _handler; } }
private McTcpClient _handler = null;
private ChatBot master = null;
/* ================================================== */ /* ================================================== */
/* Main methods to override for creating your bot */ /* Main methods to override for creating your bot */
@ -80,10 +84,12 @@ 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(string text) protected bool SendText(object text)
{ {
LogToConsole("Sending '" + text + "'"); LogToConsole("Sending '" + text + "'");
return handler.SendText(text); bool result = Handler.SendText(text is string ? (string)text : text.ToString());
Thread.Sleep(1000);
return result;
} }
/// <summary> /// <summary>
@ -92,10 +98,10 @@ namespace MinecraftClient
/// <param name="command">The command to process</param> /// <param name="command">The command to process</param>
/// <returns>TRUE if the command was indeed an internal MCC command</returns> /// <returns>TRUE if the command was indeed an internal MCC command</returns>
protected bool performInternalCommand(string command) protected bool PerformInternalCommand(string command)
{ {
string temp = ""; string temp = "";
return handler.performInternalCommand(command, ref temp); return Handler.PerformInternalCommand(command, ref temp);
} }
/// <summary> /// <summary>
@ -105,16 +111,16 @@ namespace MinecraftClient
/// <param name="response_msg">May contain a confirmation or error message after processing the command, or "" otherwise.</param> /// <param name="response_msg">May contain a confirmation or error message after processing the command, or "" otherwise.</param>
/// <returns>TRUE if the command was indeed an internal MCC command</returns> /// <returns>TRUE if the command was indeed an internal MCC command</returns>
protected bool performInternalCommand(string command, ref string response_msg) protected bool PerformInternalCommand(string command, ref string response_msg)
{ {
return handler.performInternalCommand(command, ref response_msg); return Handler.PerformInternalCommand(command, ref response_msg);
} }
/// <summary> /// <summary>
/// Remove color codes ("§c") from a text message received from the server /// Remove color codes ("§c") from a text message received from the server
/// </summary> /// </summary>
protected static string getVerbatim(string text) protected static string GetVerbatim(string text)
{ {
if ( String.IsNullOrEmpty(text) ) if ( String.IsNullOrEmpty(text) )
return String.Empty; return String.Empty;
@ -135,7 +141,7 @@ namespace MinecraftClient
/// Verify that a string contains only a-z A-Z 0-9 and _ characters. /// Verify that a string contains only a-z A-Z 0-9 and _ characters.
/// </summary> /// </summary>
protected static bool isValidName(string username) protected static bool IsValidName(string username)
{ {
if ( String.IsNullOrEmpty(username) ) if ( String.IsNullOrEmpty(username) )
return false; return false;
@ -158,9 +164,9 @@ namespace MinecraftClient
/// <param name="sender">if it's a private message, this will contain the player name that sends the message</param> /// <param name="sender">if it's a private message, this will contain the player name that sends the message</param>
/// <returns>Returns true if the text is a private message</returns> /// <returns>Returns true if the text is a private message</returns>
protected static bool isPrivateMessage(string text, ref string message, ref string sender) protected static bool IsPrivateMessage(string text, ref string message, ref string sender)
{ {
text = getVerbatim(text); text = GetVerbatim(text);
if (text == "") { return false; } if (text == "") { return false; }
string[] tmp = text.Split(' '); string[] tmp = text.Split(' ');
@ -177,7 +183,7 @@ namespace MinecraftClient
} }
else message = text.Substring(tmp[0].Length + 10); //MC 1.5 else message = text.Substring(tmp[0].Length + 10); //MC 1.5
sender = tmp[0]; sender = tmp[0];
return isValidName(sender); return IsValidName(sender);
} }
//Detect Essentials (Bukkit) /m messages //Detect Essentials (Bukkit) /m messages
@ -189,7 +195,7 @@ namespace MinecraftClient
message = text.Substring(tmp[0].Length + 4 + tmp[2].Length + 1); message = text.Substring(tmp[0].Length + 4 + tmp[2].Length + 1);
sender = tmp[0].Substring(1); sender = tmp[0].Substring(1);
if (sender[0] == '~') { sender = sender.Substring(1); } if (sender[0] == '~') { sender = sender.Substring(1); }
return isValidName(sender); return IsValidName(sender);
} }
//Detect Essentials (Bukkit) /me messages with some custom rank //Detect Essentials (Bukkit) /me messages with some custom rank
@ -201,7 +207,7 @@ namespace MinecraftClient
message = text.Substring(tmp[0].Length + 1 + tmp[1].Length + 4 + tmp[2].Length + 1); message = text.Substring(tmp[0].Length + 1 + tmp[1].Length + 4 + tmp[2].Length + 1);
sender = tmp[0].Substring(1); sender = tmp[0].Substring(1);
if (sender[0] == '~') { sender = sender.Substring(1); } if (sender[0] == '~') { sender = sender.Substring(1); }
return isValidName(sender); return IsValidName(sender);
} }
//Detect HeroChat PMsend //Detect HeroChat PMsend
@ -210,7 +216,7 @@ namespace MinecraftClient
{ {
sender = text.Substring(5).Split(':')[0]; sender = text.Substring(5).Split(':')[0];
message = text.Substring(text.IndexOf(':') + 2); message = text.Substring(text.IndexOf(':') + 2);
return isValidName(sender); return IsValidName(sender);
} }
else return false; else return false;
@ -226,10 +232,10 @@ namespace MinecraftClient
/// <param name="sender">if it's message, this will contain the player name that sends the message</param> /// <param name="sender">if it's message, this will contain the player name that sends the message</param>
/// <returns>Returns true if the text is a chat message</returns> /// <returns>Returns true if the text is a chat message</returns>
protected static bool isChatMessage(string text, ref string message, ref string sender) protected static bool IsChatMessage(string text, ref string message, ref string sender)
{ {
text = getVerbatim(text); text = GetVerbatim(text);
string[] tmp = text.Split(' '); string[] tmp = text.Split(' ');
if (text.Length > 0) if (text.Length > 0)
{ {
@ -251,7 +257,7 @@ namespace MinecraftClient
tmp2 = sender.Split(' '); tmp2 = sender.Split(' ');
sender = tmp2[tmp2.Length - 1]; sender = tmp2[tmp2.Length - 1];
if (sender[0] == '~') { sender = sender.Substring(1); } if (sender[0] == '~') { sender = sender.Substring(1); }
return isValidName(sender); return IsValidName(sender);
} }
catch (IndexOutOfRangeException) { return false; } catch (IndexOutOfRangeException) { return false; }
} }
@ -264,7 +270,7 @@ namespace MinecraftClient
int name_start = text.Substring(0, name_end).LastIndexOf(']') + 2; int name_start = text.Substring(0, name_end).LastIndexOf(']') + 2;
sender = text.Substring(name_start, name_end - name_start); sender = text.Substring(name_start, name_end - name_start);
message = text.Substring(name_end + 2); message = text.Substring(name_end + 2);
return isValidName(sender); return IsValidName(sender);
} }
} }
return false; return false;
@ -277,14 +283,14 @@ namespace MinecraftClient
/// <param name="sender">Will contain the sender's username, if it's a teleport request</param> /// <param name="sender">Will contain the sender's username, if it's a teleport request</param>
/// <returns>Returns true if the text is a teleport request</returns> /// <returns>Returns true if the text is a teleport request</returns>
protected static bool isTeleportRequest(string text, ref string sender) protected static bool IsTeleportRequest(string text, ref string sender)
{ {
text = getVerbatim(text); text = GetVerbatim(text);
sender = text.Split(' ')[0]; sender = text.Split(' ')[0];
if (text.EndsWith("has requested to teleport to you.") if (text.EndsWith("has requested to teleport to you.")
|| text.EndsWith("has requested that you teleport to them.")) || text.EndsWith("has requested that you teleport to them."))
{ {
return isValidName(sender); return IsValidName(sender);
} }
else return false; else return false;
} }
@ -294,10 +300,10 @@ namespace MinecraftClient
/// </summary> /// </summary>
/// <param name="text">Log text to write</param> /// <param name="text">Log text to write</param>
public static void LogToConsole(string text) public static void LogToConsole(object text)
{ {
ConsoleIO.WriteLineFormatted("§8[BOT] " + text); ConsoleIO.WriteLineFormatted("§8[BOT] " + text);
string logfile = Settings.expandVars(Settings.chatbotLogFile); string logfile = Settings.ExpandVars(Settings.chatbotLogFile);
if (!String.IsNullOrEmpty(logfile)) if (!String.IsNullOrEmpty(logfile))
{ {
@ -309,7 +315,7 @@ namespace MinecraftClient
catch { return; /* Invalid file name or access denied */ } catch { return; /* Invalid file name or access denied */ }
} }
File.AppendAllLines(logfile, new string[] { getTimestamp() + ' ' + text }); File.AppendAllLines(logfile, new string[] { GetTimestamp() + ' ' + text });
} }
} }
@ -340,7 +346,7 @@ namespace MinecraftClient
protected void UnloadBot() protected void UnloadBot()
{ {
handler.BotUnLoad(this); Handler.BotUnLoad(this);
} }
/// <summary> /// <summary>
@ -362,14 +368,14 @@ namespace MinecraftClient
protected void RunScript(string filename, string playername = "") protected void RunScript(string filename, string playername = "")
{ {
handler.BotLoad(new ChatBots.Script(filename, playername)); Handler.BotLoad(new ChatBots.Script(filename, playername));
} }
/// <summary> /// <summary>
/// Get a Y-M-D h:m:s timestamp representing the current system date and time /// Get a Y-M-D h:m:s timestamp representing the current system date and time
/// </summary> /// </summary>
protected static string getTimestamp() protected static string GetTimestamp()
{ {
DateTime time = DateTime.Now; DateTime time = DateTime.Now;
return String.Format("{0}-{1}-{2} {3}:{4}:{5}", return String.Format("{0}-{1}-{2} {3}:{4}:{5}",
@ -404,5 +410,59 @@ 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);
}
} }
} }

View file

@ -30,7 +30,7 @@ namespace MinecraftClient.ChatBots
public override void GetText(string text) public override void GetText(string text)
{ {
//Remove color codes and convert to lowercase //Remove color codes and convert to lowercase
text = getVerbatim(text).ToLower(); text = GetVerbatim(text).ToLower();
//Proceed only if no exclusions are found in text //Proceed only if no exclusions are found in text
if (!excludelist.Any(exclusion => text.Contains(exclusion))) if (!excludelist.Any(exclusion => text.Contains(exclusion)))

View file

@ -47,7 +47,7 @@ namespace MinecraftClient.ChatBots
public override bool OnDisconnect(DisconnectReason reason, string message) public override bool OnDisconnect(DisconnectReason reason, string message)
{ {
message = getVerbatim(message); message = GetVerbatim(message);
string comp = message.ToLower(); string comp = message.ToLower();
foreach (string msg in dictionary) foreach (string msg in dictionary)
{ {

View file

@ -182,14 +182,14 @@ namespace MinecraftClient.ChatBots
public override void GetText(string text) public override void GetText(string text)
{ {
//Remove colour codes //Remove colour codes
text = getVerbatim(text).ToLower(); text = GetVerbatim(text).ToLower();
//Check if this is a valid message //Check if this is a valid message
string sender = "", message = ""; string sender = "", message = "";
bool chatMessage = isChatMessage(text, ref message, ref sender); bool chatMessage = IsChatMessage(text, ref message, ref sender);
bool privateMessage = false; bool privateMessage = false;
if (!chatMessage) if (!chatMessage)
privateMessage = isPrivateMessage(text, ref message, ref sender); privateMessage = IsPrivateMessage(text, ref message, ref sender);
//Process only chat messages sent by another user //Process only chat messages sent by another user
if ((chatMessage || privateMessage) && sender != Settings.Username) if ((chatMessage || privateMessage) && sender != Settings.Username)
@ -201,7 +201,7 @@ namespace MinecraftClient.ChatBots
{ {
string response = null; string response = null;
LogToConsole(header + toPerform); LogToConsole(header + toPerform);
performInternalCommand(toPerform, ref response); PerformInternalCommand(toPerform, ref response);
if (!String.IsNullOrEmpty(response)) if (!String.IsNullOrEmpty(response))
LogToConsole(header + response); LogToConsole(header + response);
} }

View file

@ -69,15 +69,15 @@ namespace MinecraftClient.ChatBots
public override void GetText(string text) public override void GetText(string text)
{ {
text = getVerbatim(text); text = GetVerbatim(text);
string sender = ""; string sender = "";
string message = ""; string message = "";
if (saveChat && isChatMessage(text, ref message, ref sender)) if (saveChat && IsChatMessage(text, ref message, ref sender))
{ {
save("Chat " + sender + ": " + message); save("Chat " + sender + ": " + message);
} }
else if (savePrivate && isPrivateMessage(text, ref message, ref sender)) else if (savePrivate && IsPrivateMessage(text, ref message, ref sender))
{ {
save("Private " + sender + ": " + message); save("Private " + sender + ": " + message);
} }
@ -90,7 +90,7 @@ namespace MinecraftClient.ChatBots
private void save(string tosave) private void save(string tosave)
{ {
if (dateandtime) if (dateandtime)
tosave = getTimestamp() + ' ' + tosave; tosave = GetTimestamp() + ' ' + tosave;
string directory = Path.GetDirectoryName(logfile); string directory = Path.GetDirectoryName(logfile);
if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory)) if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory))

View file

@ -52,9 +52,9 @@ namespace MinecraftClient.ChatBots
{ {
string message = ""; string message = "";
string username = ""; string username = "";
text = getVerbatim(text); text = GetVerbatim(text);
if (isPrivateMessage(text, ref message, ref username)) if (IsPrivateMessage(text, ref message, ref username))
{ {
if (Settings.Bots_Owners.Contains(username.ToLower())) if (Settings.Bots_Owners.Contains(username.ToLower()))
{ {
@ -73,7 +73,7 @@ namespace MinecraftClient.ChatBots
} }
else else
{ {
if (running && isChatMessage(text, ref message, ref username)) if (running && IsChatMessage(text, ref message, ref username))
{ {
if (message.Length == 1) if (message.Length == 1)
{ {

View file

@ -46,7 +46,7 @@ namespace MinecraftClient.ChatBots
LogToConsole("Saving Player List"); LogToConsole("Saving Player List");
DateTime now = DateTime.Now; DateTime now = DateTime.Now;
string TimeStamp = "[" + now.Year + '/' + now.Month + '/' + now.Day + ' ' + now.Hour + ':' + now.Minute + ']'; string TimeStamp = "[" + now.Year + '/' + now.Month + '/' + now.Day + ' ' + now.Hour + ':' + now.Minute + ']';
System.IO.File.AppendAllText(file, TimeStamp + "\n" + getVerbatim(text) + "\n\n"); System.IO.File.AppendAllText(file, TimeStamp + "\n" + GetVerbatim(text) + "\n\n");
} }
} }
} }

View file

@ -13,19 +13,19 @@ namespace MinecraftClient.ChatBots
{ {
public override void GetText(string text) public override void GetText(string text)
{ {
text = getVerbatim(text); text = GetVerbatim(text);
string command = "", sender = ""; string command = "", sender = "";
if (isPrivateMessage(text, ref command, ref sender) && Settings.Bots_Owners.Contains(sender.ToLower().Trim())) if (IsPrivateMessage(text, ref command, ref sender) && Settings.Bots_Owners.Contains(sender.ToLower().Trim()))
{ {
string response = ""; string response = "";
performInternalCommand(command, ref response); PerformInternalCommand(command, ref response);
if (response.Length > 0) if (response.Length > 0)
{ {
SendPrivateMessage(sender, response); SendPrivateMessage(sender, response);
} }
} }
else if (Settings.RemoteCtrl_AutoTpaccept else if (Settings.RemoteCtrl_AutoTpaccept
&& isTeleportRequest(text, ref sender) && IsTeleportRequest(text, ref sender)
&& (Settings.RemoteCtrl_AutoTpaccept_Everyone || Settings.Bots_Owners.Contains(sender.ToLower().Trim()))) && (Settings.RemoteCtrl_AutoTpaccept_Everyone || Settings.Bots_Owners.Contains(sender.ToLower().Trim())))
{ {
SendText("/tpaccept"); SendText("/tpaccept");

View file

@ -2,6 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
namespace MinecraftClient.ChatBots namespace MinecraftClient.ChatBots
{ {
@ -14,9 +17,11 @@ namespace MinecraftClient.ChatBots
private string file; private string file;
private string[] lines = new string[0]; private string[] lines = new string[0];
private int sleepticks = 10; private int sleepticks = 10;
private int sleepticks_interval = 10;
private int nextline = 0; private int nextline = 0;
private string owner; private string owner;
private bool csharp;
private Thread thread;
private ManualResetEvent tpause;
public Script(string filename) public Script(string filename)
{ {
@ -38,10 +43,13 @@ namespace MinecraftClient.ChatBots
{ {
filename, filename,
filename + ".txt", filename + ".txt",
filename + ".cs",
"scripts" + dir_slash + filename, "scripts" + dir_slash + filename,
"scripts" + dir_slash + filename + ".txt", "scripts" + dir_slash + filename + ".txt",
"scripts" + dir_slash + filename + ".cs",
"config" + dir_slash + filename, "config" + dir_slash + filename,
"config" + dir_slash + filename + ".txt", "config" + dir_slash + filename + ".txt",
"config" + dir_slash + filename + ".cs",
}; };
foreach (string possible_file in files) foreach (string possible_file in files)
@ -62,63 +70,152 @@ namespace MinecraftClient.ChatBots
if (lookForScript(ref file)) if (lookForScript(ref file))
{ {
lines = System.IO.File.ReadAllLines(file); lines = System.IO.File.ReadAllLines(file);
if (owner != null) { SendPrivateMessage(owner, "Script '" + file + "' loaded."); } csharp = file.EndsWith(".cs");
thread = null;
if (owner != null)
SendPrivateMessage(owner, "Script '" + file + "' loaded.");
} }
else else
{ {
LogToConsole("File not found: '" + file + "'"); LogToConsole("File not found: '" + file + "'");
if (owner != null) if (owner != null)
SendPrivateMessage(owner, "File not found: '" + file + "'"); SendPrivateMessage(owner, "File not found: '" + file + "'");
UnloadBot(); //No need to keep the bot active UnloadBot(); //No need to keep the bot active
} }
} }
public override void Update() public override void Update()
{ {
if (sleepticks > 0) { sleepticks--; } if (csharp) //C# compiled script
else
{ {
if (nextline < lines.Length) //Is there an instruction left to interpret? //Initialize thread on first update
if (thread == null)
{ {
string instruction_line = lines[nextline].Trim(); // Removes all whitespaces at start and end of current line tpause = new ManualResetEvent(false);
nextline++; //Move the cursor so that the next time the following line will be interpreted thread = new Thread(() =>
sleepticks = sleepticks_interval; //Used to delay next command sending and prevent from beign kicked for spamming
if (instruction_line.Length > 1)
{ {
if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/') if (!RunCSharpScript(String.Join("\n", lines), file, tpause) && owner != null)
{ SendPrivateMessage(owner, "Script '" + file + "' failed to run.");
instruction_line = Settings.expandVars(instruction_line); });
string instruction_name = instruction_line.Split(' ')[0]; thread.Start();
switch (instruction_name.ToLower())
{
case "wait":
int ticks = 10;
try
{
ticks = Convert.ToInt32(instruction_line.Substring(5, instruction_line.Length - 5));
}
catch { }
sleepticks = ticks;
break;
default:
if (!performInternalCommand(instruction_line))
{
sleepticks = 0; Update(); //Unknown command : process next line immediately
}
else if (instruction_name.ToLower() != "log") { LogToConsole(instruction_line); }
break;
}
}
else { sleepticks = 0; Update(); } //Comment: process next line immediately
}
} }
//Let the thread run for a short span of time
if (thread != null)
{
tpause.Set();
tpause.Reset();
if (thread.Join(100))
UnloadBot();
}
}
else //Classic MCC script interpreter
{
if (sleepticks > 0) { sleepticks--; }
else else
{ {
//No more instructions to interpret if (nextline < lines.Length) //Is there an instruction left to interpret?
UnloadBot(); {
string instruction_line = lines[nextline].Trim(); // Removes all whitespaces at start and end of current line
nextline++; //Move the cursor so that the next time the following line will be interpreted
if (instruction_line.Length > 1)
{
if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/')
{
instruction_line = Settings.ExpandVars(instruction_line);
string instruction_name = instruction_line.Split(' ')[0];
switch (instruction_name.ToLower())
{
case "wait":
int ticks = 10;
try
{
ticks = Convert.ToInt32(instruction_line.Substring(5, instruction_line.Length - 5));
}
catch { }
sleepticks = ticks;
break;
default:
if (!PerformInternalCommand(instruction_line))
{
Update(); //Unknown command : process next line immediately
}
else if (instruction_name.ToLower() != "log") { LogToConsole(instruction_line); }
break;
}
}
else { Update(); } //Comment: process next line immediately
}
}
else
{
//No more instructions to interpret
UnloadBot();
}
} }
} }
} }
private bool RunCSharpScript(string script, string filename = "C# Script", ManualResetEvent tpause = null)
{
//Script compatibility check for handling future versions differently
if (!script.ToLower().StartsWith("//mccscript 1.0"))
{
ConsoleIO.WriteLineFormatted("§8Script file '" + filename + "' does not start with a valid //MCCScript comment.");
return false;
}
//Create a simple ChatBot class from the given script, allowing access to 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) {",
"SetMaster(master);",
tpause != null
? script.Replace(";\n", ";\ntpause.WaitOne();\n")
: script,
"}}}",
});
//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)
{
ConsoleIO.WriteLineFormatted("§8Error loading '" + filename + "':\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 }); }
catch (Exception e)
{
ConsoleIO.WriteLineFormatted("§8Runtime error for '" + filename + "':\n" + e);
return false;
}
return true;
}
} }
} }

View file

@ -15,13 +15,13 @@ namespace MinecraftClient.ChatBots
{ {
string message = ""; string message = "";
string username = ""; string username = "";
text = getVerbatim(text); text = GetVerbatim(text);
if (isPrivateMessage(text, ref message, ref username)) if (IsPrivateMessage(text, ref message, ref username))
{ {
ConsoleIO.WriteLine("Bot: " + username + " told me : " + message); ConsoleIO.WriteLine("Bot: " + username + " told me : " + message);
} }
else if (isChatMessage(text, ref message, ref username)) else if (IsChatMessage(text, ref message, ref username))
{ {
ConsoleIO.WriteLine("Bot: " + username + " said : " + message); ConsoleIO.WriteLine("Bot: " + username + " said : " + message);
} }

View file

@ -17,13 +17,13 @@ namespace MinecraftClient.Commands
string[] args = getArgs(command); string[] args = getArgs(command);
if (args.Length > 1) if (args.Length > 1)
{ {
if (!Settings.setAccount(args[1])) if (!Settings.SetAccount(args[1]))
{ {
return "Unknown account '" + args[1] + "'."; return "Unknown account '" + args[1] + "'.";
} }
} }
if (Settings.setServerIP(args[0])) if (Settings.SetServerIP(args[0]))
{ {
Program.Restart(); Program.Restart();
return ""; return "";

View file

@ -12,7 +12,7 @@ namespace MinecraftClient.Commands
public override string Run(McTcpClient handler, string command) public override string Run(McTcpClient handler, string command)
{ {
return "PlayerList: " + String.Join(", ", handler.getOnlinePlayers()); return "PlayerList: " + String.Join(", ", handler.GetOnlinePlayers());
} }
} }
} }

View file

@ -15,7 +15,7 @@ namespace MinecraftClient.Commands
string[] args = getArgs(command); string[] args = getArgs(command);
if (args.Length > 0) if (args.Length > 0)
{ {
if (!Settings.setAccount(args[0])) if (!Settings.SetAccount(args[0]))
{ {
return "Unknown account '" + args[0] + "'."; return "Unknown account '" + args[0] + "'.";
} }

View file

@ -17,7 +17,7 @@ namespace MinecraftClient.Commands
string[] temp = getArg(command).Split('='); string[] temp = getArg(command).Split('=');
if (temp.Length > 1) if (temp.Length > 1)
{ {
if (Settings.setVar(temp[0], getArg(command).Substring(temp[0].Length + 1))) if (Settings.SetVar(temp[0], getArg(command).Substring(temp[0].Length + 1)))
{ {
return ""; //Success return ""; //Success
} }

View file

@ -34,11 +34,11 @@ namespace MinecraftClient
private string uuid; private string uuid;
private string sessionid; private string sessionid;
public int getServerPort() { return port; } public int GetServerPort() { return port; }
public string getServerHost() { return host; } public string GetServerHost() { return host; }
public string getUsername() { return username; } public string GetUsername() { return username; }
public string getUserUUID() { return uuid; } public string GetUserUUID() { return uuid; }
public string getSessionID() { return sessionid; } public string GetSessionID() { return sessionid; }
TcpClient client; TcpClient client;
IMinecraftCom handler; IMinecraftCom handler;
@ -101,10 +101,10 @@ namespace MinecraftClient
if (Settings.AntiAFK_Enabled) { BotLoad(new ChatBots.AntiAFK(Settings.AntiAFK_Delay)); } if (Settings.AntiAFK_Enabled) { BotLoad(new ChatBots.AntiAFK(Settings.AntiAFK_Delay)); }
if (Settings.Hangman_Enabled) { BotLoad(new ChatBots.HangmanGame(Settings.Hangman_English)); } if (Settings.Hangman_Enabled) { BotLoad(new ChatBots.HangmanGame(Settings.Hangman_English)); }
if (Settings.Alerts_Enabled) { BotLoad(new ChatBots.Alerts()); } if (Settings.Alerts_Enabled) { BotLoad(new ChatBots.Alerts()); }
if (Settings.ChatLog_Enabled) { BotLoad(new ChatBots.ChatLog(Settings.expandVars(Settings.ChatLog_File), Settings.ChatLog_Filter, Settings.ChatLog_DateTime)); } if (Settings.ChatLog_Enabled) { BotLoad(new ChatBots.ChatLog(Settings.ExpandVars(Settings.ChatLog_File), Settings.ChatLog_Filter, Settings.ChatLog_DateTime)); }
if (Settings.PlayerLog_Enabled) { BotLoad(new ChatBots.PlayerListLogger(Settings.PlayerLog_Delay, Settings.expandVars(Settings.PlayerLog_File))); } if (Settings.PlayerLog_Enabled) { BotLoad(new ChatBots.PlayerListLogger(Settings.PlayerLog_Delay, Settings.ExpandVars(Settings.PlayerLog_File))); }
if (Settings.AutoRelog_Enabled) { BotLoad(new ChatBots.AutoRelog(Settings.AutoRelog_Delay, Settings.AutoRelog_Retries)); } if (Settings.AutoRelog_Enabled) { BotLoad(new ChatBots.AutoRelog(Settings.AutoRelog_Delay, Settings.AutoRelog_Retries)); }
if (Settings.ScriptScheduler_Enabled) { BotLoad(new ChatBots.ScriptScheduler(Settings.expandVars(Settings.ScriptScheduler_TasksFile))); } if (Settings.ScriptScheduler_Enabled) { BotLoad(new ChatBots.ScriptScheduler(Settings.ExpandVars(Settings.ScriptScheduler_TasksFile))); }
if (Settings.RemoteCtrl_Enabled) { BotLoad(new ChatBots.RemoteControl()); } if (Settings.RemoteCtrl_Enabled) { BotLoad(new ChatBots.RemoteControl()); }
if (Settings.AutoRespond_Enabled) { BotLoad(new ChatBots.AutoRespond(Settings.AutoRespond_Matches)); } if (Settings.AutoRespond_Enabled) { BotLoad(new ChatBots.AutoRespond(Settings.AutoRespond_Matches)); }
} }
@ -209,7 +209,7 @@ namespace MinecraftClient
{ {
string response_msg = ""; string response_msg = "";
string command = Settings.internalCmdChar == ' ' ? text : text.Substring(1); string command = Settings.internalCmdChar == ' ' ? text : text.Substring(1);
if (!performInternalCommand(Settings.expandVars(command), ref response_msg) && Settings.internalCmdChar == '/') if (!PerformInternalCommand(Settings.ExpandVars(command), ref response_msg) && Settings.internalCmdChar == '/')
{ {
SendText(text); SendText(text);
} }
@ -234,7 +234,7 @@ namespace MinecraftClient
/// <param name="response_msg">May contain a confirmation or error message after processing the command, or "" otherwise.</param> /// <param name="response_msg">May contain a confirmation or error message after processing the command, or "" otherwise.</param>
/// <returns>TRUE if the command was indeed an internal MCC command</returns> /// <returns>TRUE if the command was indeed an internal MCC command</returns>
public bool performInternalCommand(string command, ref string response_msg) public bool PerformInternalCommand(string command, ref string response_msg)
{ {
/* Load commands from the 'Commands' namespace */ /* Load commands from the 'Commands' namespace */
@ -314,7 +314,8 @@ namespace MinecraftClient
Thread.Sleep(1000); Thread.Sleep(1000);
if (client != null) { client.Close(); } if (client != null)
client.Close();
} }
/// <summary> /// <summary>
@ -476,7 +477,7 @@ namespace MinecraftClient
/// </summary> /// </summary>
/// <returns>Online player names</returns> /// <returns>Online player names</returns>
public string[] getOnlinePlayers() public string[] GetOnlinePlayers()
{ {
lock (onlinePlayers) lock (onlinePlayers)
{ {

View file

@ -63,7 +63,7 @@ namespace MinecraftClient
Settings.Password = args[1]; Settings.Password = args[1];
if (args.Length >= 3) if (args.Length >= 3)
{ {
Settings.setServerIP(args[2]); Settings.SetServerIP(args[2]);
//Single command? //Single command?
if (args.Length >= 4) if (args.Length >= 4)
@ -77,7 +77,7 @@ namespace MinecraftClient
if (Settings.ConsoleTitle != "") if (Settings.ConsoleTitle != "")
{ {
Settings.Username = "New Window"; Settings.Username = "New Window";
Console.Title = Settings.expandVars(Settings.ConsoleTitle); Console.Title = Settings.ExpandVars(Settings.ConsoleTitle);
} }
//Asking the user to type in missing data such as Username and Password //Asking the user to type in missing data such as Username and Password
@ -130,7 +130,7 @@ namespace MinecraftClient
if (result == ProtocolHandler.LoginResult.Success) if (result == ProtocolHandler.LoginResult.Success)
{ {
if (Settings.ConsoleTitle != "") if (Settings.ConsoleTitle != "")
Console.Title = Settings.expandVars(Settings.ConsoleTitle); Console.Title = Settings.ExpandVars(Settings.ConsoleTitle);
if (Settings.playerHeadAsIcon) if (Settings.playerHeadAsIcon)
ConsoleIcon.setPlayerIconAsync(Settings.Username); ConsoleIcon.setPlayerIconAsync(Settings.Username);
@ -140,7 +140,7 @@ namespace MinecraftClient
if (Settings.ServerIP == "") if (Settings.ServerIP == "")
{ {
Console.Write("Server IP : "); Console.Write("Server IP : ");
Settings.setServerIP(Console.ReadLine()); Settings.SetServerIP(Console.ReadLine());
} }
//Get server version //Get server version
@ -186,7 +186,7 @@ namespace MinecraftClient
//Update console title //Update console title
if (Settings.ConsoleTitle != "") if (Settings.ConsoleTitle != "")
Console.Title = Settings.expandVars(Settings.ConsoleTitle); Console.Title = Settings.ExpandVars(Settings.ConsoleTitle);
} }
catch (NotSupportedException) { HandleFailure("Cannot connect to the server : This version is not supported !", true); } catch (NotSupportedException) { HandleFailure("Cannot connect to the server : This version is not supported !", true); }
} }
@ -306,15 +306,15 @@ namespace MinecraftClient
if (command.StartsWith("reco")) if (command.StartsWith("reco"))
{ {
message = new Commands.Reco().Run(null, Settings.expandVars(command)); message = new Commands.Reco().Run(null, Settings.ExpandVars(command));
} }
else if (command.StartsWith("connect")) else if (command.StartsWith("connect"))
{ {
message = new Commands.Connect().Run(null, Settings.expandVars(command)); message = new Commands.Connect().Run(null, Settings.ExpandVars(command));
} }
else if (command.StartsWith("exit") || command.StartsWith("quit")) else if (command.StartsWith("exit") || command.StartsWith("quit"))
{ {
message = new Commands.Exit().Run(null, Settings.expandVars(command)); message = new Commands.Exit().Run(null, Settings.ExpandVars(command));
} }
else if (command.StartsWith("help")) else if (command.StartsWith("help"))
{ {

View file

@ -505,7 +505,7 @@ namespace MinecraftClient.Protocol.Handlers
public bool Login() public bool Login()
{ {
if (Handshake(handler.getUserUUID(), handler.getUsername(), handler.getSessionID(), handler.getServerHost(), handler.getServerPort())) if (Handshake(handler.GetUserUUID(), handler.GetUsername(), handler.GetSessionID(), handler.GetServerHost(), handler.GetServerPort()))
{ {
Send(new byte[] { 0xCD, 0 }); Send(new byte[] { 0xCD, 0 });
try try

View file

@ -349,16 +349,16 @@ namespace MinecraftClient.Protocol.Handlers
{ {
byte[] packet_id = getVarInt(0); byte[] packet_id = getVarInt(0);
byte[] protocol_version = getVarInt(protocolversion); byte[] protocol_version = getVarInt(protocolversion);
byte[] server_adress_val = Encoding.UTF8.GetBytes(handler.getServerHost()); byte[] server_adress_val = Encoding.UTF8.GetBytes(handler.GetServerHost());
byte[] server_adress_len = getVarInt(server_adress_val.Length); byte[] server_adress_len = getVarInt(server_adress_val.Length);
byte[] server_port = BitConverter.GetBytes((ushort)handler.getServerPort()); Array.Reverse(server_port); byte[] server_port = BitConverter.GetBytes((ushort)handler.GetServerPort()); Array.Reverse(server_port);
byte[] next_state = getVarInt(2); byte[] next_state = getVarInt(2);
byte[] handshake_packet = concatBytes(packet_id, protocol_version, server_adress_len, server_adress_val, server_port, next_state); byte[] handshake_packet = concatBytes(packet_id, protocol_version, server_adress_len, server_adress_val, server_port, next_state);
byte[] handshake_packet_tosend = concatBytes(getVarInt(handshake_packet.Length), handshake_packet); byte[] handshake_packet_tosend = concatBytes(getVarInt(handshake_packet.Length), handshake_packet);
Send(handshake_packet_tosend); Send(handshake_packet_tosend);
byte[] username_val = Encoding.UTF8.GetBytes(handler.getUsername()); byte[] username_val = Encoding.UTF8.GetBytes(handler.GetUsername());
byte[] username_len = getVarInt(username_val.Length); byte[] username_len = getVarInt(username_val.Length);
byte[] login_packet = concatBytes(packet_id, username_len, username_val); byte[] login_packet = concatBytes(packet_id, username_len, username_val);
byte[] login_packet_tosend = concatBytes(getVarInt(login_packet.Length), login_packet); byte[] login_packet_tosend = concatBytes(getVarInt(login_packet.Length), login_packet);
@ -377,7 +377,7 @@ namespace MinecraftClient.Protocol.Handlers
string serverID = readNextString(); string serverID = readNextString();
byte[] Serverkey = readNextByteArray(); byte[] Serverkey = readNextByteArray();
byte[] token = readNextByteArray(); byte[] token = readNextByteArray();
return StartEncryption(handler.getUserUUID(), handler.getSessionID(), token, serverID, Serverkey); return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, Serverkey);
} }
else if (pid == 0x02) //Login successful else if (pid == 0x02) //Login successful
{ {

View file

@ -449,15 +449,15 @@ namespace MinecraftClient.Protocol.Handlers
public bool Login() public bool Login()
{ {
byte[] protocol_version = getVarInt(protocolversion); byte[] protocol_version = getVarInt(protocolversion);
byte[] server_adress_val = Encoding.UTF8.GetBytes(handler.getServerHost()); byte[] server_adress_val = Encoding.UTF8.GetBytes(handler.GetServerHost());
byte[] server_adress_len = getVarInt(server_adress_val.Length); byte[] server_adress_len = getVarInt(server_adress_val.Length);
byte[] server_port = BitConverter.GetBytes((ushort)handler.getServerPort()); Array.Reverse(server_port); byte[] server_port = BitConverter.GetBytes((ushort)handler.GetServerPort()); Array.Reverse(server_port);
byte[] next_state = getVarInt(2); byte[] next_state = getVarInt(2);
byte[] handshake_packet = concatBytes(protocol_version, server_adress_len, server_adress_val, server_port, next_state); byte[] handshake_packet = concatBytes(protocol_version, server_adress_len, server_adress_val, server_port, next_state);
SendPacket(0x00, handshake_packet); SendPacket(0x00, handshake_packet);
byte[] username_val = Encoding.UTF8.GetBytes(handler.getUsername()); byte[] username_val = Encoding.UTF8.GetBytes(handler.GetUsername());
byte[] username_len = getVarInt(username_val.Length); byte[] username_len = getVarInt(username_val.Length);
byte[] login_packet = concatBytes(username_len, username_val); byte[] login_packet = concatBytes(username_len, username_val);
@ -478,7 +478,7 @@ namespace MinecraftClient.Protocol.Handlers
string serverID = readNextString(ref packetData); string serverID = readNextString(ref packetData);
byte[] Serverkey = readNextByteArray(ref packetData); byte[] Serverkey = readNextByteArray(ref packetData);
byte[] token = readNextByteArray(ref packetData); byte[] token = readNextByteArray(ref packetData);
return StartEncryption(handler.getUserUUID(), handler.getSessionID(), token, serverID, Serverkey); return StartEncryption(handler.GetUserUUID(), handler.GetSessionID(), token, serverID, Serverkey);
} }
else if (packetID == 0x02) //Login successful else if (packetID == 0x02) //Login successful
{ {

View file

@ -16,12 +16,12 @@ namespace MinecraftClient.Protocol
/* The MinecraftCom Hanler must /* The MinecraftCom Hanler must
* provide these getters */ * provide these getters */
int getServerPort(); int GetServerPort();
string getServerHost(); string GetServerHost();
string getUsername(); string GetUsername();
string getUserUUID(); string GetUserUUID();
string getSessionID(); string GetSessionID();
string[] getOnlinePlayers(); string[] GetOnlinePlayers();
/// <summary> /// <summary>
/// This method is called when the protocol handler receives a chat message /// This method is called when the protocol handler receives a chat message

View file

@ -149,7 +149,7 @@ namespace MinecraftClient
{ {
case "login": Login = argValue; break; case "login": Login = argValue; break;
case "password": Password = argValue; break; case "password": Password = argValue; break;
case "serverip": setServerIP(argValue); break; case "serverip": SetServerIP(argValue); break;
case "singlecommand": SingleCommand = argValue; break; case "singlecommand": SingleCommand = argValue; break;
case "language": Language = argValue; break; case "language": Language = argValue; break;
case "consoletitle": ConsoleTitle = argValue; break; case "consoletitle": ConsoleTitle = argValue; break;
@ -204,7 +204,7 @@ namespace MinecraftClient
if (server_data.Length == 2 if (server_data.Length == 2
&& server_data[0] != "localhost" && server_data[0] != "localhost"
&& !server_data[0].Contains('.') && !server_data[0].Contains('.')
&& setServerIP(server_data[1])) && SetServerIP(server_data[1]))
Servers[server_data[0]] Servers[server_data[0]]
= new KeyValuePair<string, ushort>(ServerIP, ServerPort); = new KeyValuePair<string, ushort>(ServerIP, ServerPort);
} }
@ -313,7 +313,7 @@ namespace MinecraftClient
break; break;
case ParseMode.AppVars: case ParseMode.AppVars:
setVar(argName, argValue); SetVar(argName, argValue);
break; break;
case ParseMode.AutoRespond: case ParseMode.AutoRespond:
@ -433,7 +433,7 @@ namespace MinecraftClient
/// </summary> /// </summary>
/// <returns>True if the account was found and loaded</returns> /// <returns>True if the account was found and loaded</returns>
public static bool setAccount(string accountAlias) public static bool SetAccount(string accountAlias)
{ {
accountAlias = accountAlias.ToLower(); accountAlias = accountAlias.ToLower();
if (Accounts.ContainsKey(accountAlias)) if (Accounts.ContainsKey(accountAlias))
@ -450,7 +450,7 @@ namespace MinecraftClient
/// </summary> /// </summary>
/// <returns>True if the server IP was valid and loaded, false otherwise</returns> /// <returns>True if the server IP was valid and loaded, false otherwise</returns>
public static bool setServerIP(string server) public static bool SetServerIP(string server)
{ {
server = server.ToLower(); server = server.ToLower();
string[] sip = server.Split(':'); string[] sip = server.Split(':');
@ -489,7 +489,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, string varData)
{ {
varName = new string(varName.TakeWhile(char.IsLetterOrDigit).ToArray()).ToLower(); varName = new string(varName.TakeWhile(char.IsLetterOrDigit).ToArray()).ToLower();
if (varName.Length > 0) if (varName.Length > 0)
@ -500,13 +500,26 @@ namespace MinecraftClient
else return false; else return false;
} }
/// <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>
public static string GetVar(string varName)
{
if (AppVars.ContainsKey(varName))
return AppVars[varName];
return null;
}
/// <summary> /// <summary>
/// Replace %variables% with their value /// Replace %variables% with their value
/// </summary> /// </summary>
/// <param name="str">String to parse</param> /// <param name="str">String to parse</param>
/// <returns>Modifier string</returns> /// <returns>Modifier string</returns>
public static string expandVars(string str) public static string ExpandVars(string str)
{ {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
for (int i = 0; i < str.Length; i++) for (int i = 0; i < str.Length; i++)

View file

@ -0,0 +1,15 @@
//MCCScript 1.0
/* This is a sample script for Minecraft Console Client
* 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 */
for (int i = 0; i < 5; i++)
{
int count = GetVarAsInt("test");
count++;
SetVar("test", count);
SendText("Hello World no. " + count);
PerformInternalCommand("log Sleeping for 5 seconds...");
Thread.Sleep(5000);
}