mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
261 lines
10 KiB
C#
261 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using MinecraftClient.CommandHandler;
|
|
using MinecraftClient.Scripting;
|
|
|
|
namespace MinecraftClient.ChatBots
|
|
{
|
|
/// <summary>
|
|
/// Runs a list of commands
|
|
/// </summary>
|
|
|
|
public class Script : ChatBot
|
|
{
|
|
private string? file;
|
|
private string[] lines = Array.Empty<string>();
|
|
private string[] args = Array.Empty<string>();
|
|
private int sleepticks = 10;
|
|
private int nextline = 0;
|
|
private readonly string? owner;
|
|
private bool csharp;
|
|
private Thread? thread;
|
|
private readonly Dictionary<string, object>? localVars;
|
|
|
|
public Script(string filename)
|
|
{
|
|
ParseArguments(filename);
|
|
}
|
|
|
|
public Script(string filename, string? ownername, Dictionary<string, object>? localVars)
|
|
: this(filename)
|
|
{
|
|
owner = ownername;
|
|
this.localVars = localVars;
|
|
}
|
|
|
|
private void ParseArguments(string argstr)
|
|
{
|
|
List<string> args = new();
|
|
StringBuilder str = new();
|
|
|
|
bool escape = false;
|
|
bool quotes = false;
|
|
|
|
foreach (char c in argstr)
|
|
{
|
|
if (escape)
|
|
{
|
|
if (c != '"')
|
|
str.Append('\\');
|
|
str.Append(c);
|
|
escape = false;
|
|
}
|
|
else
|
|
{
|
|
if (c == '\\')
|
|
escape = true;
|
|
else if (c == '"')
|
|
quotes = !quotes;
|
|
else if (c == ' ' && !quotes)
|
|
{
|
|
if (str.Length > 0)
|
|
args.Add(str.ToString());
|
|
str.Clear();
|
|
}
|
|
else str.Append(c);
|
|
}
|
|
}
|
|
|
|
if (str.Length > 0)
|
|
args.Add(str.ToString());
|
|
|
|
if (args.Count > 0)
|
|
{
|
|
file = args[0];
|
|
args.RemoveAt(0);
|
|
this.args = args.ToArray();
|
|
}
|
|
else file = "";
|
|
}
|
|
|
|
public static bool LookForScript(ref string filename)
|
|
{
|
|
//Automatically look in subfolders and try to add ".txt" file extension
|
|
char dir_slash = Path.DirectorySeparatorChar;
|
|
string[] files = new string[]
|
|
{
|
|
filename,
|
|
filename + ".txt",
|
|
filename + ".cs",
|
|
"scripts" + dir_slash + filename,
|
|
"scripts" + dir_slash + filename + ".txt",
|
|
"scripts" + dir_slash + filename + ".cs",
|
|
"config" + dir_slash + filename,
|
|
"config" + dir_slash + filename + ".txt",
|
|
"config" + dir_slash + filename + ".cs",
|
|
};
|
|
|
|
foreach (string possible_file in files)
|
|
{
|
|
if (System.IO.File.Exists(possible_file))
|
|
{
|
|
filename = possible_file;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (Settings.Config.Logging.DebugMessages)
|
|
{
|
|
string caller = "Script";
|
|
try
|
|
{
|
|
StackFrame frame = new(1);
|
|
MethodBase method = frame.GetMethod()!;
|
|
Type type = method.DeclaringType!;
|
|
caller = type.Name;
|
|
}
|
|
catch { }
|
|
ConsoleIO.WriteLineFormatted(string.Format(Translations.bot_script_not_found, caller, filename));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public override void Initialize()
|
|
{
|
|
//Load the given file from the startup parameters
|
|
if (LookForScript(ref file!))
|
|
{
|
|
lines = System.IO.File.ReadAllLines(file, Encoding.UTF8);
|
|
csharp = file.EndsWith(".cs");
|
|
thread = null;
|
|
|
|
if (!String.IsNullOrEmpty(owner))
|
|
SendPrivateMessage(owner, string.Format(Translations.bot_script_pm_loaded, file));
|
|
}
|
|
else
|
|
{
|
|
LogToConsole(string.Format(Translations.bot_script_file_not_found, System.IO.Path.GetFullPath(file)));
|
|
|
|
if (!String.IsNullOrEmpty(owner))
|
|
SendPrivateMessage(owner, string.Format(Translations.bot_script_file_not_found, file));
|
|
|
|
UnloadBot(); //No need to keep the bot active
|
|
}
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
if (csharp) //C# compiled script
|
|
{
|
|
//Initialize thread on first update
|
|
if (thread == null)
|
|
{
|
|
thread = new Thread(() =>
|
|
{
|
|
try
|
|
{
|
|
CSharpRunner.Run(this, lines, args, localVars, scriptName: file!);
|
|
}
|
|
catch (CSharpException e)
|
|
{
|
|
string errorMessage = string.Format(Translations.bot_script_fail, file, e.ExceptionType);
|
|
LogToConsole(errorMessage);
|
|
if (owner != null)
|
|
SendPrivateMessage(owner, errorMessage);
|
|
LogToConsole(e.InnerException);
|
|
}
|
|
})
|
|
{
|
|
Name = "MCC Script - " + file
|
|
};
|
|
thread.Start();
|
|
}
|
|
|
|
//Unload bot once the thread has finished running
|
|
if (thread != null && !thread.IsAlive)
|
|
{
|
|
UnloadBot();
|
|
}
|
|
}
|
|
else //Classic MCC script interpreter
|
|
{
|
|
if (sleepticks > 0) { sleepticks--; }
|
|
else
|
|
{
|
|
if (nextline < lines.Length) //Is there an instruction left to interpret?
|
|
{
|
|
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.Config.AppVar.ExpandVars(instruction_line, localVars);
|
|
string instruction_name = instruction_line.Split(' ')[0];
|
|
switch (instruction_name.ToLower())
|
|
{
|
|
case "wait":
|
|
int ticks = 10;
|
|
try
|
|
{
|
|
if (instruction_line[5..].Contains("to", StringComparison.OrdinalIgnoreCase) ||
|
|
instruction_line[5..].Contains("-"))
|
|
{
|
|
var processedLine = instruction_line.Replace("wait", "")
|
|
.Trim()
|
|
.ToLower();
|
|
processedLine = string.Join("", processedLine.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
|
|
var parts = processedLine.Contains("to") ? processedLine.Split("to") : processedLine.Split("-");
|
|
|
|
if (parts.Length == 2)
|
|
{
|
|
var min = Convert.ToInt32(parts[0]);
|
|
var max = Convert.ToInt32(parts[1]);
|
|
|
|
ticks = new Random().Next(min, max);
|
|
} else ticks = Convert.ToInt32(instruction_line[5..]);
|
|
} else ticks = Convert.ToInt32(instruction_line[5..]);
|
|
}
|
|
catch { }
|
|
sleepticks = ticks;
|
|
break;
|
|
default:
|
|
CmdResult response = new();
|
|
if (PerformInternalCommand(instruction_line, ref response))
|
|
{
|
|
if (instruction_name.ToLower() != "log")
|
|
{
|
|
LogToConsole(instruction_line);
|
|
}
|
|
if (response.status != CmdResult.Status.Done || !string.IsNullOrWhiteSpace(response.result))
|
|
{
|
|
LogToConsole(response);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Update(); //Unknown command : process next line immediately
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else { Update(); } //Comment: process next line immediately
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//No more instructions to interpret
|
|
UnloadBot();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|