LibZNI/PluginSystem.cs
2023-09-20 08:29:44 -07:00

316 lines
9.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace LibAC
{
public interface IPlugin
{
/// <summary>
/// This function is called when the plugin is first activated by the Plugin Loader. Tasks, such as loading configuration should be executed in this function
/// </summary>
#pragma warning disable IDE1006 // Naming Styles
public void onActivate();
#pragma warning restore IDE1006 // Naming Styles
/// <summary>
/// Called upon unloading the assembly
/// </summary>
#pragma warning disable IDE1006 // Naming Styles
public void onDeactivate();
#pragma warning restore IDE1006 // Naming Styles
/// <summary>
/// This function is called every tick. A tick should ideally run every 2 seconds
/// </summary>
/// <param name="lastTick">The last time a tick was executed by the host program</param>
#pragma warning disable IDE1006 // Naming Styles
public void onTick(TimeSpan lastTick);
#pragma warning restore IDE1006 // Naming Styles
public string PluginName { get; }
public VersionNumber PluginVersion { get; }
}
public class VersionNumber
{
public int Major;
public int Minor;
public int Build;
public int Revision;
public static bool operator >(VersionNumber a, VersionNumber b)
{
bool ret = true;
bool f = false;
if (a.Major < b.Major) ret=f;
if (a.Minor < b.Minor) ret = f;
if (a.Build < b.Build) ret = f;
if (a.Revision < b.Revision) ret = f;
return ret;
}
public static bool operator <(VersionNumber a, VersionNumber b)
{
bool ret = true;
bool f = false;
if (a.Major > b.Major) ret = f;
if (a.Minor > b.Minor) ret = f;
if (a.Build > b.Build) ret = f;
if (a.Revision > b.Revision) ret = f;
return ret;
}
public static VersionNumber operator +(VersionNumber a, VersionNumber b)
{
a.Major += b.Major;
a.Minor += b.Minor;
a.Build += b.Build;
a.Revision += b.Revision;
return a;
}
public static VersionNumber operator -(VersionNumber a, VersionNumber b)
{
a.Major -= b.Major;
a.Minor -= b.Minor;
a.Build -= b.Build;
a.Revision -= b.Revision;
return a;
}
public override string ToString()
{
return $"{Major}.{Minor}.{Build}.{Revision}";
}
}
public class PluginSystem
{
public Assembly LoadedASM = null;
public Assembly LoadLibrary(string DLL)
{
LoadedASM = Assembly.LoadFrom(DLL);
return LoadedASM;
}
public List<IPlugin> Activate(Assembly asm)
{
List<IPlugin> ret = new List<IPlugin>();
foreach(Type A in asm.GetTypes())
{
Type check = A.GetInterface("IPlugin");
if (check != null)
{
IPlugin plugin = Activator.CreateInstance(A) as IPlugin;
plugin.onActivate();
ret.Add(plugin);
}
}
return ret;
}
/// <summary>
/// Scans memory for commands
/// </summary>
/// <typeparam name="T">Attribute that should be scanned for</typeparam>
public static void ScanForCommands<T>()
{
}
}
public class CommandRegistry
{
public static CommandRegistry _reg = null;
public static readonly object lckreg = new object();
static CommandRegistry() { }
public static CommandRegistry Master
{
get
{
if (_reg != null) return _reg;
else
{
lock (lckreg)
{
if (_reg == null) _reg = new CommandRegistry();
return _reg;
}
}
}
}
// We now want to have as universal of a command registry as possible.
// The structure of this should be equivalent to a Hive
// All functions should return a CommandResult instance which will provide a error code, and response message.
// The CommandResult should also be able to convey if it is meant to be responded to privately, or in the same context where the command was issued.
/// <summary>
/// This provides the actual registry.
/// </summary>
private Hive Registry { get; set; } = new Hive();
#pragma warning disable IDE1006 // Naming Styles
public void register(CommandBase baseApi)
#pragma warning restore IDE1006 // Naming Styles
{
Registry.register(baseApi);
}
#pragma warning disable IDE1006 // Naming Styles
public CommandBase getCommand(string sCmd)
#pragma warning restore IDE1006 // Naming Styles
{
return Registry.get(sCmd);
}
}
/// <summary>
/// The registry hive. Subject to change to a real registry format.
/// </summary>
public class Hive
{
// This functionality, is just a registry hive
private Dictionary<string, CommandBase> commands = new Dictionary<string, CommandBase>();
#pragma warning disable IDE1006 // Naming Styles
public void register(CommandBase api)
#pragma warning restore IDE1006 // Naming Styles
{
commands[api.Command] = api;
}
#pragma warning disable IDE1006 // Naming Styles
public CommandBase get(string sCmd)
#pragma warning restore IDE1006 // Naming Styles
{
if (commands.ContainsKey(sCmd) == false) return null;
return commands[sCmd];
}
}
public class KVP<T>
{
public string Key;
public T Value;
}
public class CommandArguments
{
public Dictionary<string, object> args = new Dictionary<string, object>();
public int AuthorityLevel;
public CommandArguments(params KVP<object>[] arguments)
{
foreach(KVP<object> arg in arguments)
{
args.Add(arg.Key, arg.Value);
}
}
}
[Flags]
public enum ErrorCodes
{
AUTH_LEVEL=0x0001,
SUCCESS=0x0002,
EXPECTED_PARAMETERS_MISSING=0x0003
}
public class CommandResult
{
public enum ResultType
{
OK,
ERROR
}
public enum ResponseType
{
SAFE,
SENSITIVE
}
public ResultType Type;
public string ResponseText;
public ResponseType Resp_Type;
public int ResponseCode;
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] // Allow alias commands
public class CommandBase : Attribute
{
[Flags]
public enum Destinations
{
ANY = 1,
LOCAL = 2,
GROUP = 4,
IM = 8,
DISCORD = 16
}
public string Command;
public int MinLevel;
public MethodInfo assignedMethod;
public CommandHelp usage;
public Destinations cmdType;
public CommandBase(string cmd, int level, string hlp, Destinations dest)
{
Command = cmd;
MinLevel = level;
usage = new CommandHelp(cmd, level, hlp, dest);
cmdType = dest;
}
}
public class CommandHelp
{
public string Name;
public int lvl;
public string Text;
public string sources;
public CommandBase.Destinations Dests;
public bool HasGroupFlag()
{
return ((Dests & CommandBase.Destinations.GROUP) == CommandBase.Destinations.GROUP);
}
public static readonly string NoArgs = "This command does not take any arguments";
public string GetUsageAsString()
{
return $"_\nCommand [{Name}]\n{sources}\nLevel Required: {lvl}\nUsage: \n\n{Text}";
}
public CommandHelp(string cmdname, int lvls, string htext, CommandBase.Destinations dst)
{
Name = cmdname;
lvl = lvls;
List<string> rss = new List<string>();
if ((Dests & CommandBase.Destinations.GROUP) == CommandBase.Destinations.GROUP) rss.Add("Group");
if ((Dests & CommandBase.Destinations.LOCAL) == CommandBase.Destinations.LOCAL) rss.Add("Local");
if ((Dests & CommandBase.Destinations.IM) == CommandBase.Destinations.IM) rss.Add("IM");
if ((Dests & CommandBase.Destinations.DISCORD) == CommandBase.Destinations.DISCORD) rss.Add("Discord");
if ((Dests & CommandBase.Destinations.ANY) == CommandBase.Destinations.ANY)
{
rss.Clear();
rss.Add("Any");
}
Dests = dst;
Text = htext;
sources = $"Command can be used in: {String.Join(", ", rss)}";
}
}
}