Files
Unstable Kitsune b918192e4e Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
2023-11-28 23:20:26 -05:00

1454 lines
31 KiB
C#

#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using Server;
using Server.Commands;
using Server.Gumps;
using VitaNex.IO;
using VitaNex.Network;
using VitaNex.SuperGumps;
using VitaNex.SuperGumps.UI;
#endregion
namespace VitaNex
{
public static class TaskPriority
{
public const int Highest = 0, High = 250000, Medium = 500000, Low = 750000, Lowest = 1000000;
}
/// <summary>
/// Exposes an interface for managing VitaNexCore and its' sub-systems.
/// </summary>
public static partial class VitaNexCore
{
public const AccessLevel Access = AccessLevel.Administrator;
public static VersionInfo Version { get; } = new VersionInfo(5, 3, 1, 0)
{
Name = "VitaNexCore",
Description = "Represents the local version value of Vita-Nex: Core"
};
private static readonly CallPriorityComparer _PriorityComparer = new CallPriorityComparer();
public static readonly object ConsoleLock = new object();
public static readonly object IOLock = new object();
private static readonly DateTime _Started = DateTime.UtcNow;
public static long Ticks
{
get
{
if (Stopwatch.IsHighResolution && !Core.Unix)
{
return (long)(Stopwatch.GetTimestamp() * (1000.0 / Stopwatch.Frequency));
}
return (long)(DateTime.UtcNow.Ticks * (1000.0 / TimeSpan.TicksPerSecond));
}
}
private static long _Tick = Ticks;
public static long Tick => _Tick;
private static readonly List<ICorePluginInfo> _Plugins = new List<ICorePluginInfo>(0x20);
public static IEnumerable<ICorePluginInfo> Plugins
{
get
{
var idx = _Plugins.Count;
while (--idx >= 0)
{
if (_Plugins.InBounds(idx))
{
yield return _Plugins[idx];
}
}
}
}
public static int PluginCount => _Plugins.Count;
/// <summary>
/// Gets the amount of time that has passed since VitaNexCore was first initialized.
/// </summary>
public static TimeSpan UpTime => DateTime.UtcNow - _Started;
/// <summary>
/// Gets the root directory for VitaNexCore.
/// This is the directory where the core scripts reside.
/// </summary>
public static DirectoryInfo RootDirectory { get; private set; }
/// <summary>
/// Gets the working directory for VitaNexCore.
/// </summary>
public static DirectoryInfo BaseDirectory { get; private set; }
/// <summary>
/// Gets the build directory for VitaNexCore
/// </summary>
public static DirectoryInfo BuildDirectory => IOUtility.EnsureDirectory(BaseDirectory + "/Build/");
/// <summary>
/// Gets the data directory for VitaNexCore.
/// </summary>
public static DirectoryInfo DataDirectory => IOUtility.EnsureDirectory(BaseDirectory + "/Data/");
/// <summary>
/// Gets the cache directory for VitaNexCore
/// </summary>
public static DirectoryInfo CacheDirectory => IOUtility.EnsureDirectory(BaseDirectory + "/Cache/");
/// <summary>
/// Gets the services directory for VitaNexCore
/// </summary>
public static DirectoryInfo ServicesDirectory => IOUtility.EnsureDirectory(BaseDirectory + "/Services/");
/// <summary>
/// Gets the modules directory for VitaNexCore
/// </summary>
public static DirectoryInfo ModulesDirectory => IOUtility.EnsureDirectory(BaseDirectory + "/Modules/");
/// <summary>
/// Gets the saves backup directory for VitaNexCore
/// </summary>
public static DirectoryInfo BackupDirectory => IOUtility.EnsureDirectory(BaseDirectory + "/Backups/");
/// <summary>
/// Gets the saves directory for VitaNexCore
/// </summary>
public static DirectoryInfo SavesDirectory => IOUtility.EnsureDirectory(BaseDirectory + "/Saves/");
/// <summary>
/// Gets the logs directory for VitaNexCore
/// </summary>
public static DirectoryInfo LogsDirectory => IOUtility.EnsureDirectory(BaseDirectory + "/Logs/");
/// <summary>
/// Gets a file used for unhandled and generec exception logging.
/// </summary>
public static FileInfo LogFile => IOUtility.EnsureFile(LogsDirectory + "/Logs (" + DateTime.Now.ToSimpleString("D d M y") + ").log");
/// <summary>
/// Gets a value representing whether VitaNexCore is busy performing a save or load action
/// </summary>
public static bool Busy { get; private set; }
/// <summary>
/// Gets a value representing the compile state of VitaNexCore
/// </summary>
public static bool Compiled { get; private set; }
/// <summary>
/// Gets a value representing the configure state of VitaNexCore
/// </summary>
public static bool Configured { get; private set; }
/// <summary>
/// Gets a value representing the initialize state of VitaNexCore
/// </summary>
public static bool Initialized { get; private set; }
/// <summary>
/// Gets a value representing whether this run is the first boot of VitaNexCore
/// </summary>
public static bool FirstBoot { get; private set; }
public static bool Disposing { get; private set; }
public static bool Disposed { get; private set; }
public static bool Crashed { get; private set; }
public static TimeSpan BackupExpireAge { get; set; }
public static event Action OnCompiled;
public static event Action OnConfigured;
public static event Action OnInitialized;
public static event Action OnBackup;
public static event Action OnSaved;
public static event Action OnLoaded;
public static event Action OnDispose;
public static event Action OnDisposed;
public static event Action<Exception> OnExceptionThrown;
/// <summary>
/// Configure method entry point, called by ScriptCompiler during compile of 'Scripts' directory.
/// Performs a global invoke action, processing all Services and Modules that support CSConfig() and CMConfig()
/// </summary>
[CallPriority(Int32.MaxValue)]
public static void Configure()
{
if (Configured)
{
return;
}
/// DisplayRetroBoot();
CommandUtility.Register("VNC", AccessLevel.Administrator, OnCoreCommand);
OutgoingPacketOverrides.Init();
ExtendedOPL.Init();
var now = DateTime.UtcNow;
ToConsole(String.Empty);
ToConsole("Compile action started...");
TryCatch(CompileServices, ToConsole);
TryCatch(CompileModules, ToConsole);
Compiled = true;
InvokeByPriority(OnCompiled);
var time = (DateTime.UtcNow - now).TotalSeconds;
ToConsole("Compile action completed in {0:F2} second{1}", time, (time != 1) ? "s" : String.Empty);
now = DateTime.UtcNow;
ToConsole(String.Empty);
ToConsole("Configure action started...");
TryCatch(ConfigureServices, ToConsole);
TryCatch(ConfigureModules, ToConsole);
Configured = true;
InvokeByPriority(OnConfigured);
time = (DateTime.UtcNow - now).TotalSeconds;
ToConsole("Configure action completed in {0:F2} second{1}", time, (time != 1) ? "s" : String.Empty);
ProcessINIT();
EventSink.ServerStarted += () =>
{
EventSink.WorldSave += e =>
{
TryCatch(Backup, ToConsole);
TryCatch(Save, ToConsole);
};
EventSink.Shutdown += e => TryCatch(Dispose, ToConsole);
EventSink.Crashed += e => TryCatch(Dispose, ToConsole);
};
try
{
var crashed = typeof(EventSink).GetEventDelegates("Crashed");
foreach (var m in crashed.OfType<CrashedEventHandler>())
{
EventSink.Crashed -= m;
}
EventSink.Crashed += e => Crashed = true;
foreach (var m in crashed.OfType<CrashedEventHandler>())
{
EventSink.Crashed += m;
}
}
catch (Exception x)
{
ToConsole(x);
EventSink.Crashed += e => Crashed = true;
}
}
/// <summary>
/// Initialize method entry point, called by ScriptCompiler after compile of 'Scripts' directory.
/// Performs a global invoke action, processing all Services and Modules that support CSInvoke() and CMInvoke()
/// </summary>
[CallPriority(Int32.MaxValue)]
public static void Initialize()
{
if (Initialized)
{
return;
}
TryCatch(Load, ToConsole);
var now = DateTime.UtcNow;
ToConsole(String.Empty);
ToConsole("Invoke action started...");
TryCatch(InvokeServices, ToConsole);
TryCatch(InvokeModules, ToConsole);
Initialized = true;
InvokeByPriority(OnInitialized);
var time = (DateTime.UtcNow - now).TotalSeconds;
ToConsole("Invoke action completed in {0:F2} second{1}", time, (time != 1) ? "s" : String.Empty);
}
/// <summary>
/// Performs a global save action, processing all Services and Modules that support CSSave() and CMSave()
/// </summary>
public static void Save()
{
if (Busy)
{
ToConsole("Could not perform save action, the service is busy.");
return;
}
Busy = true;
var now = DateTime.UtcNow;
try
{
ToConsole(String.Empty);
ToConsole("Save action started...");
TryCatch(SaveServices, ToConsole);
TryCatch(SaveModules, ToConsole);
InvokeByPriority(OnSaved);
}
finally
{
Busy = false;
}
var time = (DateTime.UtcNow - now).TotalSeconds;
ToConsole("Save action completed in {0:F2} second{1}", time, (time != 1) ? "s" : String.Empty);
}
/// <summary>
/// Performs a global load action, processing all Services and Modules that support CSLoad() and CMLoad()
/// </summary>
public static void Load()
{
if (Busy)
{
ToConsole("Could not perform load action, the service is busy.");
return;
}
Busy = true;
var now = DateTime.UtcNow;
try
{
ToConsole(String.Empty);
ToConsole("Load action started...");
TryCatch(LoadServices, ToConsole);
TryCatch(LoadModules, ToConsole);
InvokeByPriority(OnLoaded);
}
finally
{
Busy = false;
}
var time = (DateTime.UtcNow - now).TotalSeconds;
ToConsole("Load action completed in {0:F2} second{1}", time, (time != 1) ? "s" : String.Empty);
}
/// <summary>
/// Performs a global dispose action, processing all Services and Modules that support CSDispose() and CMDispose()
/// </summary>
public static void Dispose()
{
if (Busy || Disposing || Disposed)
{
ToConsole("Could not perform dispose action, the service is busy.");
return;
}
Busy = Disposing = Disposed = true;
var now = DateTime.UtcNow;
try
{
ToConsole(String.Empty);
ToConsole("Dispose action started...");
InvokeByPriority(OnDispose);
TryCatch(DisposeServices, ToConsole);
TryCatch(DisposeModules, ToConsole);
InvokeByPriority(OnDisposed);
}
finally
{
Busy = Disposing = false;
}
var time = (DateTime.UtcNow - now).TotalSeconds;
ToConsole("Dispose action completed in {0:F2} second{1}", time, (time != 1) ? "s" : String.Empty);
}
/// <summary>
/// Performs a global backup action, copying all files in the SavesDirectory to the BackupDirectory.
/// </summary>
public static void Backup()
{
if (Busy)
{
ToConsole("Could not perform backup action, the service is busy.");
return;
}
Busy = true;
var now = DateTime.UtcNow;
try
{
ToConsole(String.Empty);
ToConsole("Backup action started...");
if (BackupExpireAge > TimeSpan.Zero)
{
ToConsole("Backup Expire Age: {0}", BackupExpireAge);
lock (IOLock)
{
BackupDirectory.EmptyDirectory(BackupExpireAge);
}
}
lock (IOLock)
{
SavesDirectory.CopyDirectory(
IOUtility.EnsureDirectory(BackupDirectory + "/" + DateTime.Now.ToSimpleString("D d M y"), true));
}
InvokeByPriority(OnBackup);
}
finally
{
Busy = false;
}
var time = (DateTime.UtcNow - now).TotalSeconds;
ToConsole("Backup action completed in {0:F2} second{1}", time, (time != 1) ? "s" : String.Empty);
}
private static void OnCoreCommand(CommandEventArgs e)
{
if (e == null || e.Mobile == null || e.Mobile.Deleted)
{
return;
}
var cmd = e.GetString(0);
var search = e.GetString(1);
switch (cmd.ToLower())
{
case "srv":
{
var cs = !String.IsNullOrWhiteSpace(search)
? Services.FirstOrDefault(o => Insensitive.Contains(o.Name, search))
: null;
VitaNexCoreUI.DisplayTo(e.Mobile, cs);
}
break;
case "mod":
{
var cm = !String.IsNullOrWhiteSpace(search)
? Modules.FirstOrDefault(o => Insensitive.Contains(o.Name, search))
: null;
VitaNexCoreUI.DisplayTo(e.Mobile, cm);
}
break;
case "plg":
{
var cp = !String.IsNullOrWhiteSpace(search)
? Plugins.FirstOrDefault(o => Insensitive.Contains(o.Name, search))
: null;
VitaNexCoreUI.DisplayTo(e.Mobile, cp);
}
break;
default:
VitaNexCoreUI.DisplayTo(e.Mobile);
break;
}
}
private static void InvokeByPriority(Delegate action)
{
if (action == null)
{
return;
}
var list = action.GetInvocationList();
foreach (var d in list.OrderBy(d => d.Method, _PriorityComparer))
{
TryCatch(d, ToConsole);
}
}
public static T TryCatchGet<T>(Func<T> func)
{
return TryCatchGet(func, ToConsole);
}
public static T TryCatchGet<T>(Func<T> func, Action<Exception> handler)
{
if (func == null)
{
return default(T);
}
try
{
return func();
}
catch (Exception e)
{
if (handler == null)
{
ToConsole("{0} at {1}:", e.GetType(), Trace(func));
}
else
{
handler(e);
}
}
return default(T);
}
public static T TryCatchGet<T, TState>(Func<TState, T> func, TState state)
{
return TryCatchGet(func, state, ToConsole);
}
public static T TryCatchGet<T, TState>(Func<TState, T> func, TState state, Action<Exception> handler)
{
if (func == null)
{
return default(T);
}
try
{
return func(state);
}
catch (Exception e)
{
if (handler == null)
{
ToConsole("{0} at {1}:", e.GetType(), Trace(func));
}
else
{
handler(e);
}
}
return default(T);
}
public static void TryCatch(Delegate action)
{
TryCatch(action, ToConsole);
}
public static void TryCatch(Delegate action, Action<Exception> handler)
{
if (action == null)
{
return;
}
try
{
if (action.Method.IsStatic)
{
action.Method.Invoke(null, null);
}
else
{
action.Method.Invoke(action.Target, null);
}
}
catch (Exception e)
{
if (handler == null)
{
ToConsole("{0} at {1}:", e.GetType(), Trace(action));
}
else
{
handler(e);
}
}
}
public static void TryCatch(Action action)
{
TryCatch(action, ToConsole);
}
public static void TryCatch(Action action, Action<Exception> handler)
{
if (action == null)
{
return;
}
try
{
action();
}
catch (Exception e)
{
if (handler == null)
{
ToConsole("{0} at {1}:", e.GetType(), Trace(action));
}
else
{
handler(e);
}
}
}
public static void TryCatch<T>(Action<T> action, T state)
{
TryCatch(action, state, ToConsole);
}
public static void TryCatch<T>(Action<T> action, T state, Action<Exception> handler)
{
if (action == null)
{
return;
}
try
{
action(state);
}
catch (Exception e)
{
if (handler == null)
{
ToConsole("{0} at {1}:", e.GetType(), Trace(action));
}
else
{
handler(e);
}
}
}
public static void WaitWhile(Func<bool> func)
{
WaitWhile(func, TimeSpan.MaxValue);
}
public static void WaitWhile(Func<bool> func, TimeSpan timeOut)
{
if (func == null)
{
return;
}
var expire = DateTime.UtcNow.Add(timeOut);
var exit = false;
while (!exit)
{
exit = !func();
if (DateTime.UtcNow >= expire)
{
break;
}
Thread.Sleep(1);
}
}
public static void ToConsole(string format, params object[] args)
{
lock (ConsoleLock)
{
Console.Write('[');
Utility.PushColor(ConsoleColor.Yellow);
Console.Write("VitaNexCore");
Utility.PopColor();
Console.Write("]: ");
Utility.PushColor(ConsoleColor.DarkYellow);
if (args.Length > 0)
{
Console.WriteLine(format, args);
}
else
{
Console.WriteLine(format);
}
Utility.PopColor();
}
}
public static void ToConsole(Exception e)
{
lock (ConsoleLock)
{
Console.Write('[');
Utility.PushColor(ConsoleColor.Yellow);
Console.Write("VitaNexCore");
Utility.PopColor();
Console.Write("]: ");
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine(e);
Utility.PopColor();
}
e.Log(LogFile);
InvokeByPriority(OnExceptionThrown);
}
public static string Trace(this Delegate d)
{
return Trace(d, false);
}
public static string Trace(this Delegate d, bool output)
{
if (d == null)
{
return "NULL";
}
var value = "";
var dt = d.Method.DeclaringType;
while (dt != null)
{
value = dt.Name + '.' + value;
dt = dt.DeclaringType;
}
value += d.Method.Name;
if (output)
{
ToConsole("Trace: {0}", value);
}
return value;
}
private const ConsoleColor _BackgroundColor = ConsoleColor.Black;
private const ConsoleColor _BorderColor = ConsoleColor.Green;
private const ConsoleColor _TextColor = ConsoleColor.White;
private static void DrawLine(string text = "", int align = 0)
{
text = text ?? String.Empty;
align = Math.Max(0, Math.Min(2, align));
var defBG = Console.BackgroundColor;
const int borderWidth = 2;
const int indentWidth = 1;
var maxWidth = Math.Max(80, Console.WindowWidth) - ((borderWidth + indentWidth) * 2);
var lines = new List<string>();
if (text.Length > maxWidth)
{
var words = text.Split(' ');
if (words.Length == 0)
{
for (int i = 0, offset = 0, count; i < (text.Length / maxWidth); i++)
{
lines.Add(text.Substring(offset, (count = Math.Min(text.Length - offset, maxWidth))));
offset += count;
}
}
else
{
var rebuild = String.Empty;
for (var wi = 0; wi < words.Length; wi++)
{
if (rebuild.Length + (words[wi].Length + 1) <= maxWidth)
{
rebuild += words[wi] + ' ';
}
else
{
lines.Add(rebuild);
rebuild = words[wi] + ' ';
}
if (wi + 1 >= words.Length)
{
lines.Add(rebuild);
}
}
}
}
else
{
lines.Add(text);
}
lock (ConsoleLock)
{
Utility.PushColor(_TextColor);
foreach (var line in lines)
{
Console.BackgroundColor = _BorderColor;
Console.Write(new string(' ', borderWidth));
Console.BackgroundColor = _BackgroundColor;
Console.Write(new string(' ', indentWidth));
var len = maxWidth - line.Length;
var str = line;
switch (align)
{
//Center
case 1:
str = new string(' ', len / 2) + str + new string(' ', len / 2);
break;
//Right
case 2:
str = new string(' ', len) + str;
break;
}
if (str.Length < maxWidth)
{
str += new string(' ', maxWidth - str.Length);
}
Console.Write(str);
Console.Write(new string(' ', indentWidth));
Console.BackgroundColor = _BorderColor;
Console.Write(new string(' ', borderWidth));
}
lines.Free(true);
Console.BackgroundColor = defBG;
Utility.PopColor();
}
}
private static void DisplayRetroBoot()
{
ConsoleColor defBG;
lock (ConsoleLock)
{
defBG = Console.BackgroundColor;
Console.WriteLine();
Console.BackgroundColor = _BorderColor;
Console.CursorLeft = 0;
Console.Write(new string(' ', Math.Max(80, Console.WindowWidth)));
}
DrawLine();
DrawLine("**** VITA-NEX: CORE " + Version + " ****", 1);
DrawLine();
DrawLine("Root Directory: " + RootDirectory.FullName.Replace(Core.BaseDirectory, "."));
DrawLine("Working Directory: " + BaseDirectory.FullName.Replace(Core.BaseDirectory, "."));
DrawLine();
DrawLine("http://core.vita-nex.com", 1);
DrawLine();
if (FirstBoot)
{
try
{
var readme = IOUtility.GetSafeFilePath(RootDirectory + "/LICENSE.md", true);
var lines = File.ReadAllLines(readme);
lines.ForEach(line => DrawLine(line));
DrawLine();
}
catch
{ }
}
if (Core.Debug)
{
DrawLine("Server is running in DEBUG mode.");
DrawLine();
}
lock (ConsoleLock)
{
Console.BackgroundColor = _BorderColor;
Console.Write(new string(' ', Console.WindowWidth));
Console.BackgroundColor = defBG;
Utility.PopColor();
Console.WriteLine();
}
}
public static void RegisterPlugin(ICorePluginInfo cp)
{
if (cp == null)
{
return;
}
_Plugins.Update(cp);
TryCatch(cp.OnRegistered, cp.ToConsole);
}
}
public sealed class VitaNexCoreUI : TreeGump
{
public static void DisplayTo(Mobile user, CoreServiceInfo cs)
{
var node = "Plugins|Services";
if (cs != null)
{
node += "|" + cs.FullName;
}
DisplayTo(user, false, node);
}
public static void DisplayTo(Mobile user, CoreModuleInfo cm)
{
var node = "Plugins|Modules";
if (cm != null)
{
node += "|" + cm.FullName;
}
DisplayTo(user, false, node);
}
public static void DisplayTo(Mobile user, ICorePluginInfo cp)
{
if (cp is CoreServiceInfo)
{
DisplayTo(user, (CoreServiceInfo)cp);
return;
}
if (cp is CoreModuleInfo)
{
DisplayTo(user, (CoreModuleInfo)cp);
return;
}
var node = "Plugins";
if (cp != null)
{
node += "|" + cp.FullName;
}
DisplayTo(user, false, node);
}
public static void DisplayTo(Mobile user)
{
DisplayTo(user, String.Empty);
}
public static void DisplayTo(Mobile user, string node)
{
DisplayTo(user, false, node);
}
public static void DisplayTo(Mobile user, bool refreshOnly)
{
DisplayTo(user, refreshOnly, String.Empty);
}
public static void DisplayTo(Mobile user, bool refreshOnly, string node)
{
if (user == null)
{
return;
}
if (user.AccessLevel < VitaNexCore.Access)
{
user.SendMessage(0x22, "You do not have access to this feature.");
return;
}
var info = EnumerateInstances<VitaNexCoreUI>(user).FirstOrDefault(g => g != null && !g.IsDisposed && g.IsOpen);
if (info == null)
{
if (refreshOnly)
{
return;
}
info = new VitaNexCoreUI(user);
}
if (!String.IsNullOrWhiteSpace(node))
{
info.SelectedNode = node;
}
info.Refresh(true);
}
private List<ICorePluginInfo> _Buffer;
private int[,] _Indicies;
private VitaNexCoreUI(Mobile user)
: base(user)
{
_Buffer = new List<ICorePluginInfo>();
_Indicies = new int[3, 2];
Width = 800;
Height = 600;
Title = "Vita-Nex: Core Control Panel";
}
protected override void OnDispose()
{
_Buffer.Free(true);
_Buffer = null;
_Indicies = null;
base.OnDispose();
}
protected override bool OnBeforeSend()
{
if (base.OnBeforeSend() && User.AccessLevel >= VitaNexCore.Access)
{
return true;
}
User.SendMessage(0x22, "You do not have access to this feature.");
return false;
}
protected override void CompileNodes(Dictionary<TreeGumpNode, Action<Rectangle, int, TreeGumpNode>> list)
{
list.Clear();
list["Core"] = CompileOverview;
list["Plugins"] = CompilePlugins;
list["Plugins|Services"] = CompileServices;
list["Plugins|Modules"] = CompileModules;
foreach (var p in VitaNexCore.Plugins)
{
var name = p.Name;
if (String.IsNullOrWhiteSpace(name))
{
name = p.TypeOf.Name;
}
if (p is CoreServiceInfo)
{
list["Plugins|Services|" + name] = CompileService;
}
else if (p is CoreModuleInfo)
{
list["Plugins|Modules|" + name] = CompileModule;
}
else
{
list["Plugins|" + name] = CompilePlugin;
}
}
base.CompileNodes(list);
}
protected override void CompileEmptyNodeLayout(
SuperGumpLayout layout,
int x,
int y,
int w,
int h,
int index,
TreeGumpNode node)
{
base.CompileEmptyNodeLayout(layout, x, y, w, h, index, node);
layout.Add("node/page/" + index, () => CompileOverview(x, y, w, h));
}
private void CompileOverview(Rectangle b, int i, TreeGumpNode n)
{
CompileOverview(b.X, b.Y, b.Width, b.Height);
}
private void CompileOverview(int x, int y, int w, int h)
{
var html = new StringBuilder();
html.AppendLine();
html.AppendLine("Vita-Nex: Core " + "{0}".WrapUOHtmlColor(Color.LawnGreen, HtmlColor), VitaNexCore.Version);
html.AppendLine("A dynamic extension library for RunUO");
html.AppendLine("<a href=\"http://core.vita-nex.com\">http://core.vita-nex.com</a>");
html.AppendLine();
html.AppendLine("Services: " + "{0:#,0}".WrapUOHtmlColor(Color.LawnGreen, HtmlColor), VitaNexCore.ServiceCount);
html.AppendLine("Modules: " + "{0:#,0}".WrapUOHtmlColor(Color.LawnGreen, HtmlColor), VitaNexCore.ModuleCount);
html.AppendLine();
AddHtml(x + 5, y, w - 10, h, html.ToString().WrapUOHtmlColor(HtmlColor, false), false, true);
}
private void CompileService(Rectangle b, int i, TreeGumpNode n)
{
var cs = VitaNexCore.Services.FirstOrDefault(o => Insensitive.Equals(n.Name, o.Name));
if (cs != null)
{
CompileService(b.X, b.Y, b.Width, b.Height, cs);
}
}
private void CompileService(int x, int y, int w, int h, CoreServiceInfo cs)
{
CompileBufferEntry(x, y, w, h, 0, cs);
y += 75;
h -= 75;
cs.CompileControlPanel(this, x, y, w, h);
}
private void CompileModule(Rectangle b, int i, TreeGumpNode n)
{
var cm = VitaNexCore.Modules.FirstOrDefault(o => Insensitive.Equals(n.Name, o.Name));
if (cm != null)
{
CompileModule(b.X, b.Y, b.Width, b.Height, cm);
}
}
private void CompileModule(int x, int y, int w, int h, CoreModuleInfo cm)
{
CompileBufferEntry(x, y, w, h, 0, cm);
y += 75;
h -= 75;
cm.CompileControlPanel(this, x, y, w, h);
}
private void CompilePlugin(Rectangle b, int i, TreeGumpNode n)
{
var cp = VitaNexCore.Plugins.FirstOrDefault(o => Insensitive.Equals(n.Name, o.Name));
if (cp != null)
{
CompilePlugin(b.X, b.Y, b.Width, b.Height, cp);
}
}
private void CompilePlugin(int x, int y, int w, int h, ICorePluginInfo cp)
{
CompileBufferEntry(x, y, w, h, 0, cp);
y += 75;
h -= 75;
cp.CompileControlPanel(this, x, y, w, h);
}
private void CompileServices(Rectangle b, int i, TreeGumpNode n)
{
CompileBuffer(0, b.X, b.Y, b.Width, b.Height, VitaNexCore.Services);
}
private void CompileModules(Rectangle b, int i, TreeGumpNode n)
{
CompileBuffer(1, b.X, b.Y, b.Width, b.Height, VitaNexCore.Modules);
}
private void CompilePlugins(Rectangle b, int i, TreeGumpNode n)
{
CompileBuffer(2, b.X, b.Y, b.Width, b.Height, VitaNexCore.Plugins);
}
private void CompileBuffer(int i, int x, int y, int w, int h, IEnumerable<ICorePluginInfo> plugins)
{
_Buffer.Clear();
_Buffer.AddRange(plugins);
_Buffer.Sort();
_Indicies[i, 1] = _Buffer.Count;
_Indicies[i, 0] = Math.Max(0, Math.Min(_Indicies[i, 1] - 1, _Indicies[i, 0]));
var count = (int)Math.Floor(h / 55.0);
var idx = 0;
foreach (var cp in _Buffer.Skip(_Indicies[i, 0]).Take(count))
{
CompileBufferEntry(x, y, w - 20, h, idx++, cp);
}
_Buffer.Clear();
AddScrollbarVM(x + (w - 15), y, h, _Indicies[i, 1], _Indicies[i, 0], (b, n) =>
{
_Indicies[i, 0] -= n;
Refresh(true);
},
(b, n) =>
{
_Indicies[i, 0] += n;
Refresh(true);
});
}
private void CompileBufferEntry(int x, int y, int w, int h, int i, ICorePluginInfo cp)
{
if (w * h <= 0 || cp == null)
{
return;
}
var xx = x;
var yy = y + (i * 57);
AddRectangle(xx, yy, w, 55, Color.Black, cp.Active ? Color.PaleGoldenrod : Color.Silver, 1);
xx += 5;
yy += 5;
var label = cp.Name.WrapUOHtmlColor(HtmlColor, false);
AddHtml(xx + 5, yy, w - 80, 40, label, false, false);
xx = x + (w - 60);
label = cp.Version.ToString().WrapUOHtmlColor(HtmlColor, false);
AddHtml(xx, yy, 55, 40, label, false, false);
xx = x + 5;
yy += 25;
Color color, border, fill;
if (cp.Disposed)
{
label = "DISPOSED".WrapUOHtmlBold().WrapUOHtmlCenter();
color = border = Color.OrangeRed;
fill = Color.Black;
AddRectangle(xx, yy, w - 10, 20, fill, border, 1);
AddHtml(xx, yy, w, 20, label.WrapUOHtmlColor(color, false), false, false);
return;
}
var bw = (w - 10) / 4;
label = "CONFIG".WrapUOHtmlBold().WrapUOHtmlCenter();
color = border = Color.PaleGoldenrod;
fill = Color.Black;
AddHtmlButton(xx, yy, bw, 20, o => HandleConfig(cp), label, color, fill, border, 1);
xx += bw;
label = "DEBUG".WrapUOHtmlBold().WrapUOHtmlCenter();
color = border = cp.Debug ? Color.LawnGreen : Color.OrangeRed;
AddHtmlButton(xx, yy, bw, 20, o => HandleDebug(cp), label, color, fill, border, 1);
xx += bw;
label = "QUIET".WrapUOHtmlBold().WrapUOHtmlCenter();
color = border = cp.Quiet ? Color.LawnGreen : Color.OrangeRed;
AddHtmlButton(xx, yy, bw, 20, o => HandleQuiet(cp), label, color, fill, border, 1);
xx += bw;
label = "ACTIVE".WrapUOHtmlBold().WrapUOHtmlCenter();
color = border = cp.Active ? Color.LawnGreen : Color.OrangeRed;
AddHtmlButton(xx, yy, bw, 20, o => HandleActive(cp), label, color, fill, border, 1);
}
private void HandleConfig(ICorePluginInfo cp)
{
Refresh(true);
if (cp != null && !cp.Disposed)
{
User.SendGump(new PropertiesGump(User, cp));
}
}
private void HandleDebug(ICorePluginInfo cp)
{
if (cp != null && !cp.Disposed)
{
var old = cp.Debug;
cp.Debug = !cp.Debug;
if (cp.Debug != old)
{
User.SendMessage(85, "[{0}]: Debugging {1}", cp.Name, cp.Debug ? "enabled" : "disabled");
}
}
Refresh(true);
}
private void HandleQuiet(ICorePluginInfo cp)
{
if (cp != null && !cp.Disposed)
{
var old = cp.Quiet;
cp.Quiet = !cp.Quiet;
if (cp.Quiet != old)
{
User.SendMessage(85, "[{0}]: Using {1} output", cp.Name, cp.Quiet ? "simple" : "extended");
}
}
Refresh(true);
}
private void HandleActive(ICorePluginInfo cp)
{
if (cp != null && !cp.Disposed)
{
var old = cp.Active;
cp.Active = !cp.Active;
if (cp.Active != old)
{
User.SendMessage(85, "[{0}]: Now {1}", cp.Name, cp.Active ? "active" : "inactive");
}
}
Refresh(true);
}
}
public interface ICorePluginInfo : IEquatable<ICorePluginInfo>, IComparable<ICorePluginInfo>
{
bool Active { get; set; }
bool Configured { get; }
bool Invoked { get; }
bool Disposed { get; }
Assembly DynamicAssembly { get; }
FileInfo DynamicAssemblyFile { get; }
bool Dynamic { get; }
int Priority { get; }
Type TypeOf { get; }
VersionInfo Version { get; }
string Name { get; }
string FullName { get; }
bool Debug { get; set; }
bool Quiet { get; set; }
void OnRegistered();
void SaveState();
void LoadState();
void SaveOptions();
void LoadOptions();
void ToConsole(string[] lines);
void ToConsole(string format, params object[] args);
void ToConsole(Exception[] errors);
void ToConsole(Exception e);
void CompileControlPanel(SuperGump g, int x, int y, int w, int h);
}
}