2022-11-28 13:55:05 +08:00
|
|
|
|
using System;
|
2015-08-23 18:51:24 +02:00
|
|
|
|
using System.Collections.Generic;
|
2017-03-29 20:07:46 +02:00
|
|
|
|
using System.ComponentModel;
|
2022-10-02 18:31:08 +08:00
|
|
|
|
using System.IO;
|
2022-07-03 22:34:07 +08:00
|
|
|
|
using System.Linq;
|
2022-10-02 18:31:08 +08:00
|
|
|
|
using System.Text;
|
2026-06-13 21:41:52 +00:00
|
|
|
|
using Microsoft.CodeAnalysis;
|
2022-12-06 15:50:17 +08:00
|
|
|
|
using MinecraftClient.Scripting.DynamicRun.Builder;
|
2022-10-05 15:02:30 +08:00
|
|
|
|
using static MinecraftClient.Settings;
|
2015-08-23 18:51:24 +02:00
|
|
|
|
|
2022-12-06 15:50:17 +08:00
|
|
|
|
namespace MinecraftClient.Scripting
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// C# Script runner - Compile on-the-fly and run C# scripts
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
class CSharpRunner
|
|
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
private static readonly Dictionary<ulong, byte[]> CompileCache = new();
|
2015-08-23 18:51:24 +02:00
|
|
|
|
|
2026-06-13 21:41:52 +00:00
|
|
|
|
private readonly record struct ScriptSourceLine(int LineNumber, string Text);
|
|
|
|
|
|
|
2015-08-23 18:51:24 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Run the specified C# script file
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="apiHandler">ChatBot handler for accessing ChatBot API</param>
|
|
|
|
|
|
/// <param name="lines">Lines of the script file to run</param>
|
|
|
|
|
|
/// <param name="args">Arguments to pass to the script</param>
|
2020-03-27 21:14:05 +01:00
|
|
|
|
/// <param name="localVars">Local variables passed along with the script</param>
|
2015-08-23 18:51:24 +02:00
|
|
|
|
/// <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>
|
2026-06-06 12:09:36 +02:00
|
|
|
|
public static object? Run(ChatBot apiHandler, string[] lines, string[] args, Dictionary<string, object>? localVars, bool run = true, string scriptName = "Unknown Script", string? scriptOwnerKey = null)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
//Script compatibility check for handling future versions differently
|
|
|
|
|
|
if (lines.Length < 1 || lines[0] != "//MCCScript 1.0")
|
|
|
|
|
|
throw new CSharpException(CSErrorType.InvalidScript,
|
2022-10-28 11:13:20 +08:00
|
|
|
|
new InvalidDataException(Translations.exception_csrunner_invalid_head));
|
2015-08-23 18:51:24 +02:00
|
|
|
|
|
|
|
|
|
|
//Script hash for determining if it was previously compiled
|
|
|
|
|
|
ulong scriptHash = QuickHash(lines);
|
2022-07-03 22:34:07 +08:00
|
|
|
|
byte[]? assembly = null;
|
2022-10-02 18:31:08 +08:00
|
|
|
|
|
|
|
|
|
|
Compiler compiler = new();
|
|
|
|
|
|
CompileRunner runner = new();
|
2015-08-23 18:51:24 +02:00
|
|
|
|
|
|
|
|
|
|
//No need to compile two scripts at the same time
|
|
|
|
|
|
lock (CompileCache)
|
|
|
|
|
|
{
|
|
|
|
|
|
///Process and compile script only if not already compiled
|
2022-10-05 15:02:30 +08:00
|
|
|
|
if (!Config.Main.Advanced.CacheScript || !CompileCache.ContainsKey(scriptHash))
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
//Process different sections of the script file
|
|
|
|
|
|
bool scriptMain = true;
|
2026-06-13 21:41:52 +00:00
|
|
|
|
List<ScriptSourceLine> script = new();
|
|
|
|
|
|
List<ScriptSourceLine> extensions = new();
|
2022-10-02 18:31:08 +08:00
|
|
|
|
List<string> libs = new();
|
|
|
|
|
|
List<string> dlls = new();
|
2026-06-13 21:41:52 +00:00
|
|
|
|
for (int i = 0; i < lines.Length; i++)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
2026-06-13 21:41:52 +00:00
|
|
|
|
string line = lines[i];
|
2020-02-09 18:25:19 +01:00
|
|
|
|
if (line.StartsWith("//using"))
|
|
|
|
|
|
{
|
|
|
|
|
|
libs.Add(line.Replace("//", "").Trim());
|
|
|
|
|
|
}
|
2020-08-01 16:02:58 +05:00
|
|
|
|
else if (line.StartsWith("//dll"))
|
|
|
|
|
|
{
|
|
|
|
|
|
dlls.Add(line.Replace("//dll ", "").Trim());
|
|
|
|
|
|
}
|
2020-02-09 18:25:19 +01:00
|
|
|
|
else if (line.StartsWith("//MCCScript"))
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (line.EndsWith("Extensions"))
|
|
|
|
|
|
scriptMain = false;
|
|
|
|
|
|
}
|
2026-06-13 21:41:52 +00:00
|
|
|
|
|
2026-06-13 21:49:12 +00:00
|
|
|
|
(scriptMain ? script : extensions).Add(new(i + 1, line));
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Add return statement if missing
|
2026-06-13 21:41:52 +00:00
|
|
|
|
bool hasImplicitReturn = script.All(line => !line.Text.StartsWith("return ", StringComparison.Ordinal)
|
|
|
|
|
|
&& !line.Text.Contains(" return ", StringComparison.Ordinal));
|
2015-08-23 18:51:24 +02:00
|
|
|
|
|
|
|
|
|
|
//Generate a class from the given script
|
2026-06-13 21:41:52 +00:00
|
|
|
|
string code = BuildScriptCode(scriptName, script, extensions, libs, hasImplicitReturn);
|
|
|
|
|
|
|
|
|
|
|
|
ConsoleIO.WriteLogLine(string.Format(Translations.script_compile_started, scriptName));
|
2022-10-20 20:05:36 +08:00
|
|
|
|
|
2015-08-23 18:51:24 +02:00
|
|
|
|
//Compile the C# class in memory using all the currently loaded assemblies
|
2022-10-21 00:29:46 +08:00
|
|
|
|
var result = compiler.Compile(code, Guid.NewGuid().ToString(), dlls);
|
2022-10-02 18:31:08 +08:00
|
|
|
|
|
2015-08-23 18:51:24 +02:00
|
|
|
|
//Process compile warnings and errors
|
2026-03-24 00:27:07 +00:00
|
|
|
|
if (result.Failures is not null)
|
2022-12-06 15:50:17 +08:00
|
|
|
|
{
|
|
|
|
|
|
|
2026-06-13 21:41:52 +00:00
|
|
|
|
ConsoleIO.WriteLogLine(Translations.script_compile_failed);
|
2022-10-20 20:05:36 +08:00
|
|
|
|
|
2022-12-06 15:50:17 +08:00
|
|
|
|
foreach (var failure in result.Failures)
|
|
|
|
|
|
{
|
2026-06-13 21:41:52 +00:00
|
|
|
|
ConsoleIO.WriteLogLine(FormatCompilationFailure(failure, scriptName));
|
2022-10-20 20:05:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-06-22 06:40:23 +08:00
|
|
|
|
throw new CSharpException(CSErrorType.InvalidScript, new InvalidProgramException("Compilation failed due to error(s)."));
|
2022-10-20 20:05:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 21:41:52 +00:00
|
|
|
|
ConsoleIO.WriteLogLine(Translations.script_compile_succeeded);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
|
|
|
|
|
|
//Retrieve compiled assembly
|
2022-07-03 22:34:07 +08:00
|
|
|
|
assembly = result.Assembly;
|
2022-10-05 15:02:30 +08:00
|
|
|
|
if (Config.Main.Advanced.CacheScript)
|
2022-07-03 22:34:07 +08:00
|
|
|
|
CompileCache[scriptHash] = assembly!;
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
2022-10-05 15:02:30 +08:00
|
|
|
|
else if (Config.Main.Advanced.CacheScript)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
assembly = CompileCache[scriptHash];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Run the compiled assembly with exception handling
|
|
|
|
|
|
if (run)
|
|
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2026-06-06 12:09:36 +02:00
|
|
|
|
var compiled = runner.Execute(assembly!, args, localVars, apiHandler, scriptOwnerKey);
|
2022-07-03 22:34:07 +08:00
|
|
|
|
return compiled;
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e) { throw new CSharpException(CSErrorType.RuntimeError, e); }
|
|
|
|
|
|
}
|
|
|
|
|
|
else return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 21:41:52 +00:00
|
|
|
|
private static string BuildScriptCode(string scriptName, IEnumerable<ScriptSourceLine> script, IEnumerable<ScriptSourceLine> extensions, IEnumerable<string> libs, bool hasImplicitReturn)
|
|
|
|
|
|
{
|
|
|
|
|
|
StringBuilder codeBuilder = new();
|
|
|
|
|
|
codeBuilder.AppendLine("using System;");
|
|
|
|
|
|
codeBuilder.AppendLine("using System.Collections.Generic;");
|
|
|
|
|
|
codeBuilder.AppendLine("using System.Text.RegularExpressions;");
|
|
|
|
|
|
codeBuilder.AppendLine("using System.Linq;");
|
|
|
|
|
|
codeBuilder.AppendLine("using System.Text;");
|
|
|
|
|
|
codeBuilder.AppendLine("using System.IO;");
|
|
|
|
|
|
codeBuilder.AppendLine("using System.Net;");
|
|
|
|
|
|
codeBuilder.AppendLine("using System.Threading;");
|
|
|
|
|
|
codeBuilder.AppendLine("using MinecraftClient;");
|
|
|
|
|
|
codeBuilder.AppendLine("using MinecraftClient.Scripting;");
|
|
|
|
|
|
codeBuilder.AppendLine("using MinecraftClient.Mapping;");
|
|
|
|
|
|
codeBuilder.AppendLine("using MinecraftClient.Inventory;");
|
|
|
|
|
|
|
|
|
|
|
|
foreach (string lib in libs)
|
|
|
|
|
|
codeBuilder.AppendLine(lib);
|
|
|
|
|
|
|
|
|
|
|
|
codeBuilder.AppendLine("namespace ScriptLoader {");
|
|
|
|
|
|
codeBuilder.AppendLine("public class Script {");
|
|
|
|
|
|
codeBuilder.AppendLine("public CSharpAPI MCC;");
|
|
|
|
|
|
codeBuilder.AppendLine("public object __run(CSharpAPI __apiHandler, string[] args) {");
|
|
|
|
|
|
codeBuilder.AppendLine("this.MCC = __apiHandler;");
|
|
|
|
|
|
AppendMappedSection(codeBuilder, scriptName, script);
|
|
|
|
|
|
|
|
|
|
|
|
if (hasImplicitReturn)
|
|
|
|
|
|
codeBuilder.AppendLine("return null;");
|
|
|
|
|
|
|
|
|
|
|
|
codeBuilder.AppendLine("}");
|
|
|
|
|
|
AppendMappedSection(codeBuilder, scriptName, extensions);
|
|
|
|
|
|
codeBuilder.AppendLine("}}");
|
|
|
|
|
|
return codeBuilder.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void AppendMappedSection(StringBuilder codeBuilder, string scriptName, IEnumerable<ScriptSourceLine> lines)
|
|
|
|
|
|
{
|
|
|
|
|
|
ScriptSourceLine[] sourceLines = lines.ToArray();
|
|
|
|
|
|
if (sourceLines.Length == 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
codeBuilder.AppendLine($@"#line {sourceLines[0].LineNumber} ""{EscapeLineDirectivePath(scriptName)}""");
|
|
|
|
|
|
|
|
|
|
|
|
foreach (ScriptSourceLine line in sourceLines)
|
|
|
|
|
|
codeBuilder.AppendLine(line.Text);
|
|
|
|
|
|
|
|
|
|
|
|
codeBuilder.AppendLine("#line default");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string EscapeLineDirectivePath(string path)
|
|
|
|
|
|
{
|
2026-06-13 21:49:12 +00:00
|
|
|
|
return path.Replace("\\", "\\\\").Replace("\"", "\\\"");
|
2026-06-13 21:41:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string FormatCompilationFailure(Diagnostic failure, string scriptName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (failure.Location.IsInSource)
|
|
|
|
|
|
{
|
|
|
|
|
|
var location = failure.Location.GetMappedLineSpan();
|
|
|
|
|
|
string sourcePath = string.IsNullOrWhiteSpace(location.Path) ? scriptName : location.Path;
|
|
|
|
|
|
return string.Format(Translations.script_compile_error,
|
|
|
|
|
|
sourcePath,
|
|
|
|
|
|
location.StartLinePosition.Line + 1,
|
|
|
|
|
|
location.StartLinePosition.Character + 1,
|
|
|
|
|
|
failure.Id,
|
|
|
|
|
|
failure.GetMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return string.Format(Translations.script_compile_error_no_location, scriptName, failure.Id, failure.GetMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-08-23 18:51:24 +02:00
|
|
|
|
/// <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
|
|
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
private readonly CSErrorType _type;
|
2015-08-23 18:51:24 +02:00
|
|
|
|
public CSErrorType ExceptionType { get { return _type; } }
|
2022-10-02 18:31:08 +08:00
|
|
|
|
public override string Message { get { return InnerException!.Message; } }
|
|
|
|
|
|
public override string ToString() { return InnerException!.ToString(); }
|
2024-06-22 06:40:23 +08:00
|
|
|
|
public CSharpException(CSErrorType type, Exception inner) : base(inner.Message, inner)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
_type = type;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Represents the C# API object accessible from C# Scripts
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class CSharpAPI : ChatBot
|
|
|
|
|
|
{
|
2020-03-27 21:14:05 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Holds local variables passed along with the script
|
|
|
|
|
|
/// </summary>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
private readonly Dictionary<string, object>? localVars;
|
2020-03-27 21:14:05 +01:00
|
|
|
|
|
2015-08-23 18:51:24 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Create a new C# API Wrapper
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="apiHandler">ChatBot API Handler</param>
|
|
|
|
|
|
/// <param name="tickHandler">ChatBot tick handler</param>
|
2020-03-27 21:14:05 +01:00
|
|
|
|
/// <param name="localVars">Local variables passed along with the script</param>
|
2026-06-06 12:09:36 +02:00
|
|
|
|
public CSharpAPI(ChatBot apiHandler, Dictionary<string, object>? localVars, string? scriptOwnerKey = null)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
SetMaster(apiHandler);
|
2020-03-27 21:14:05 +01:00
|
|
|
|
this.localVars = localVars;
|
2026-06-06 12:09:36 +02:00
|
|
|
|
SetScriptOwnerKey(scriptOwnerKey);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
2026-04-03 19:06:43 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Access the shared MCC gameplay API used by bots and the embedded MCP server.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
new public MccGameApi Game => base.Game;
|
2015-08-23 18:51:24 +02:00
|
|
|
|
|
|
|
|
|
|
/* == 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>
|
2020-10-24 17:41:35 +02:00
|
|
|
|
/// <returns>TRUE if successfully sent (Deprectated, always returns TRUE for compatibility purposes with existing scripts)</returns>
|
2015-11-27 16:52:41 +01:00
|
|
|
|
public bool SendText(object text)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
2022-12-06 15:50:17 +08:00
|
|
|
|
return base.SendText(text is string str ? str : text.ToString() ?? string.Empty);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Perform an internal MCC command (not a server command, use SendText() instead for that!)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="command">The command to process</param>
|
2020-03-28 00:00:46 +01:00
|
|
|
|
/// <param name="localVars">Local variables passed along with the internal command</param>
|
2015-08-23 18:51:24 +02:00
|
|
|
|
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
new public bool PerformInternalCommand(string command, Dictionary<string, object>? localVars = null)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
localVars ??= this.localVars;
|
|
|
|
|
|
return base.PerformInternalCommand(command, localVars);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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>
|
2017-05-31 20:54:16 +02:00
|
|
|
|
/// <param name="delaySeconds">Optional delay, in seconds, before restarting</param>
|
2022-11-05 20:27:10 +08:00
|
|
|
|
new public void ReconnectToTheServer(int extraAttempts = -999999, int delaySeconds = 0, bool keepAccountAndServerSettings = false)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (extraAttempts == -999999)
|
2022-11-05 20:27:10 +08:00
|
|
|
|
base.ReconnectToTheServer(delaySeconds: delaySeconds, keepAccountAndServerSettings: keepAccountAndServerSettings);
|
2022-10-02 18:31:08 +08:00
|
|
|
|
else
|
2022-11-05 20:27:10 +08:00
|
|
|
|
base.ReconnectToTheServer(extraAttempts, delaySeconds, keepAccountAndServerSettings);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Disconnect from the server and exit the program
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
new public void DisconnectAndExit()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.DisconnectAndExit();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Load the provided ChatBot object
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="bot">Bot to load</param>
|
|
|
|
|
|
new public void LoadBot(ChatBot bot)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.LoadBot(bot);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-01-04 21:54:22 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Return the list of currently online players
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>List of online players</returns>
|
|
|
|
|
|
new public string[] GetOnlinePlayers()
|
|
|
|
|
|
{
|
|
|
|
|
|
return base.GetOnlinePlayers();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-03-30 10:47:59 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get a dictionary of online player names and their corresponding UUID
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>
|
|
|
|
|
|
/// dictionary of online player whereby
|
|
|
|
|
|
/// UUID represents the key
|
|
|
|
|
|
/// playername represents the value</returns>
|
|
|
|
|
|
new public Dictionary<string, string> GetOnlinePlayersWithUUID()
|
|
|
|
|
|
{
|
|
|
|
|
|
return base.GetOnlinePlayersWithUUID();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-08-23 18:51:24 +02:00
|
|
|
|
/* == 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>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
public object? GetVar(string varName)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
2026-03-24 00:27:07 +00:00
|
|
|
|
if (localVars is not null && localVars.ContainsKey(varName))
|
2020-03-27 21:14:05 +01:00
|
|
|
|
return localVars[varName];
|
2022-10-02 18:31:08 +08:00
|
|
|
|
else
|
2022-12-06 15:50:17 +08:00
|
|
|
|
return Config.AppVar.GetVar(varName);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2017-03-29 20:07:46 +02:00
|
|
|
|
/// Set a global variable for further use in any other script
|
2015-08-23 18:51:24 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="varName">Name of the variable</param>
|
2017-03-29 20:07:46 +02:00
|
|
|
|
/// <param name="varValue">Value of the variable</param>
|
|
|
|
|
|
public bool SetVar(string varName, object varValue)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
2026-03-24 00:27:07 +00:00
|
|
|
|
if (localVars is not null && localVars.ContainsKey(varName))
|
2020-03-27 21:14:05 +01:00
|
|
|
|
localVars.Remove(varName);
|
2022-12-06 15:50:17 +08:00
|
|
|
|
return Config.AppVar.SetVar(varName, varValue);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-09-11 20:11:01 +02:00
|
|
|
|
/// <summary>
|
2017-03-29 20:07:46 +02:00
|
|
|
|
/// Get a global variable by name, as the specified type, and try converting it if possible.
|
|
|
|
|
|
/// If you know what you are doing and just want a cast, use (T)MCC.GetVar("name") instead.
|
2016-09-11 20:11:01 +02:00
|
|
|
|
/// </summary>
|
2017-03-29 20:07:46 +02:00
|
|
|
|
/// <typeparam name="T">Variable type</typeparam>
|
|
|
|
|
|
/// <param name="varName">Variable name</param>
|
|
|
|
|
|
/// <returns>Variable as specified type or default value for this type</returns>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
public T? GetVar<T>(string varName)
|
2016-09-11 20:11:01 +02:00
|
|
|
|
{
|
2022-10-02 18:31:08 +08:00
|
|
|
|
object? value = GetVar(varName);
|
|
|
|
|
|
if (value is T Tval)
|
|
|
|
|
|
return Tval;
|
2026-03-24 00:27:07 +00:00
|
|
|
|
if (value is not null)
|
2017-03-29 20:07:46 +02:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
|
2026-03-24 00:27:07 +00:00
|
|
|
|
if (converter is not null)
|
2022-10-02 18:31:08 +08:00
|
|
|
|
return (T?)converter.ConvertFromString(value.ToString() ?? string.Empty);
|
2017-03-29 20:07:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
catch (NotSupportedException) { /* Was worth trying */ }
|
|
|
|
|
|
}
|
2022-10-02 18:31:08 +08:00
|
|
|
|
return default;
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-03-29 20:07:46 +02:00
|
|
|
|
//Named shortcuts for GetVar<type>(varname)
|
2022-10-02 18:31:08 +08:00
|
|
|
|
public string? GetVarAsString(string varName) { return GetVar<string>(varName); }
|
2017-03-29 20:07:46 +02:00
|
|
|
|
public int GetVarAsInt(string varName) { return GetVar<int>(varName); }
|
|
|
|
|
|
public double GetVarAsDouble(string varName) { return GetVar<double>(varName); }
|
|
|
|
|
|
public bool GetVarAsBool(string varName) { return GetVar<bool>(varName); }
|
2015-08-23 18:51:24 +02:00
|
|
|
|
|
|
|
|
|
|
/// <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)
|
|
|
|
|
|
{
|
2022-12-06 15:50:17 +08:00
|
|
|
|
bool result = Config.Main.Advanced.SetAccount(accountAlias);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
if (result && andReconnect)
|
2022-11-05 20:27:10 +08:00
|
|
|
|
ReconnectToTheServer(keepAccountAndServerSettings: true);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
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)
|
|
|
|
|
|
{
|
2024-04-16 21:21:08 +03:00
|
|
|
|
bool result = Config.Main.SetServerIP(new MainConfigHelper.MainConfig.ServerInfoConfig(server), true);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
if (result && andReconnect)
|
2022-11-05 20:27:10 +08:00
|
|
|
|
ReconnectToTheServer(keepAccountAndServerSettings: true);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
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>
|
2022-10-02 18:31:08 +08:00
|
|
|
|
public object? CallScript(string script, string[] args)
|
2015-08-23 18:51:24 +02:00
|
|
|
|
{
|
|
|
|
|
|
ChatBots.Script.LookForScript(ref script);
|
2022-10-02 18:31:08 +08:00
|
|
|
|
string[] lines;
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
lines = File.ReadAllLines(script, Encoding.UTF8);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new CSharpException(CSErrorType.FileReadError, e);
|
|
|
|
|
|
}
|
2022-10-20 20:05:36 +08:00
|
|
|
|
return CSharpRunner.Run(this, lines, args, localVars, scriptName: script);
|
2015-08-23 18:51:24 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2022-11-28 13:55:05 +08:00
|
|
|
|
}
|