Overwrite

Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
Unstable Kitsune
2023-11-28 23:20:26 -05:00
parent 3cd54811de
commit b918192e4e
11608 changed files with 2644205 additions and 47 deletions

View File

@@ -0,0 +1,517 @@
#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.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using Server;
using Server.Commands;
using VitaNex.Collections;
using VitaNex.IO;
using VitaNex.Network;
#endregion
namespace VitaNex
{
public static partial class Clilocs
{
private static readonly Dictionary<Type, int> _TypeCache = new Dictionary<Type, int>(0x1000);
private static readonly ObjectProperty _LabelNumberProp = new ObjectProperty("LabelNumber");
private static readonly ClilocLNG[] _Languages = default(ClilocLNG).EnumerateValues<ClilocLNG>().Not(o => o == ClilocLNG.NULL).ToArray();
public static readonly Regex VarPattern = new Regex(@"~(?<index>\d+)(?<sep>_*)(?<desc>\w*)~", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
public static readonly Regex NumPattern = new Regex(@"#(?<index>\d+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
public static ClilocLNG DefaultLanguage { get; set; } = ClilocLNG.ENU;
public static CoreServiceOptions CSOptions { get; private set; }
public static Dictionary<ClilocLNG, ClilocTable> Tables { get; private set; }
private static void ExportCommand(CommandEventArgs e)
{
if (e.Mobile?.Deleted != false)
{
return;
}
e.Mobile.SendMessage(0x55, "Export requested...");
if (e.Arguments?.Length > 0 && !String.IsNullOrWhiteSpace(e.Arguments[0]))
{
if (Enum.TryParse(e.Arguments[0], true, out ClilocLNG lng) && lng != ClilocLNG.NULL)
{
VitaNexCore.TryCatch(() =>
{
var file = Export(lng);
if (file != null && file.Exists && file.Length > 0)
{
e.Mobile.SendMessage(0x55, $"{lng} clilocs have been exported to: {file.FullName}");
}
else
{
e.Mobile.SendMessage(0x22, $"Could not export clilocs for {lng}");
}
},
ex =>
{
e.Mobile.SendMessage(0x22, "A fatal exception occurred, check the console for details.");
CSOptions.ToConsole(ex);
});
return;
}
}
e.Mobile.SendMessage($"Usage: {CommandSystem.Prefix}{e.Command} <{String.Join(" | ", _Languages)}>");
}
public static FileInfo Export(ClilocLNG lng)
{
if (lng == ClilocLNG.NULL)
{
lng = DefaultLanguage;
}
var list = new XmlDataStore<int, ClilocData>(VitaNexCore.DataDirectory + "/Exported Clilocs/", lng.ToString());
var table = Tables[lng];
list.OnSerialize = doc =>
{
XmlNode node;
XmlCDataSection cdata;
ClilocInfo info;
XmlNode root = doc.CreateElement("clilocs");
var attr = doc.CreateAttribute("len");
attr.Value = table.Count.ToString(CultureInfo.InvariantCulture);
if (root.Attributes != null)
{
root.Attributes.Append(attr);
}
attr = doc.CreateAttribute("lng");
attr.Value = table.Language.ToString();
if (root.Attributes != null)
{
root.Attributes.Append(attr);
}
foreach (var d in table)
{
if (d.Length <= 0)
{
continue;
}
info = d.Lookup(table.InputFile, true);
if (String.IsNullOrWhiteSpace(info?.Text))
{
continue;
}
node = doc.CreateElement("cliloc");
attr = doc.CreateAttribute("idx");
attr.Value = d.Index.ToString(CultureInfo.InvariantCulture);
if (node.Attributes != null)
{
node.Attributes.Append(attr);
}
attr = doc.CreateAttribute("len");
attr.Value = d.Length.ToString(CultureInfo.InvariantCulture);
if (node.Attributes != null)
{
node.Attributes.Append(attr);
}
cdata = doc.CreateCDataSection(info.Text);
node.AppendChild(cdata);
root.AppendChild(node);
}
doc.AppendChild(root);
table.Clear();
return true;
};
list.Export();
list.Clear();
return list.Document;
}
public static ClilocInfo Lookup(this ClilocLNG lng, Type type)
{
if (type == null)
{
return null;
}
if (lng == ClilocLNG.NULL)
{
lng = DefaultLanguage;
}
try
{
if (_TypeCache.TryGetValue(type, out var index))
{
if (index < 0)
{
return null;
}
return Lookup(lng, index);
}
if (!_LabelNumberProp.IsSupported(typeof(int), type))
{
return null;
}
var o = type.CreateInstanceSafe<object>();
if (o != null)
{
index = _LabelNumberProp.GetValue(o, -1); // LabelNumber_get()
o.InvokeMethod("Delete");
}
else
{
index = -1;
}
_TypeCache[type] = index;
if (index >= 0)
{
return Lookup(lng, index);
}
}
catch
{
}
return null;
}
public static ClilocInfo Lookup(this ClilocLNG lng, int index)
{
if (index < 0)
{
return null;
}
if (lng == ClilocLNG.NULL)
{
lng = DefaultLanguage;
}
if (Tables.TryGetValue(lng, out var table) && !table.IsNullOrWhiteSpace(index))
{
return table[index];
}
if (lng != ClilocLNG.ENU)
{
return Lookup(ClilocLNG.ENU, index);
}
return null;
}
public static string GetRawString(this ClilocLNG lng, int index)
{
if (index < 0)
{
return null;
}
if (lng == ClilocLNG.NULL)
{
lng = DefaultLanguage;
}
if (Tables.TryGetValue(lng, out var table) && !table.IsNullOrWhiteSpace(index))
{
return table[index]?.Text ?? String.Empty;
}
if (lng != ClilocLNG.ENU)
{
return GetRawString(ClilocLNG.ENU, index);
}
return String.Empty;
}
public static string GetString(this ClilocLNG lng, int index, StringBuilder args)
{
return Lookup(lng, index)?.ToString(args) ?? String.Empty;
}
public static string GetString(this ClilocLNG lng, int index, string args)
{
return Lookup(lng, index)?.ToString(args) ?? String.Empty;
}
public static string GetString(this ClilocLNG lng, int index, params object[] args)
{
return Lookup(lng, index)?.ToString(args) ?? String.Empty;
}
public static string GetString(this ClilocLNG lng, Type type)
{
return Lookup(lng, type)?.ToString() ?? String.Empty;
}
public static string GetString(this TextDefinition text)
{
return GetString(text, DefaultLanguage);
}
public static string GetString(this TextDefinition text, Mobile m)
{
return GetString(text, GetLanguage(m));
}
public static string GetString(this TextDefinition text, ClilocLNG lng)
{
if (ReferenceEquals(text, null))
{
return String.Empty;
}
return text.Number > 0 ? GetString(lng, text.Number) : (text.String ?? String.Empty);
}
public static bool IsNullOrEmpty(this TextDefinition text)
{
if (ReferenceEquals(text, null))
{
return true;
}
return text.Number <= 0 && String.IsNullOrEmpty(text.String) && String.IsNullOrEmpty(GetString(text));
}
public static bool IsNullOrWhiteSpace(this TextDefinition text)
{
if (ReferenceEquals(text, null))
{
return true;
}
return text.Number <= 0 && String.IsNullOrWhiteSpace(text.String) && String.IsNullOrWhiteSpace(GetString(text));
}
private static IEnumerable<T> Enumerate<T>(ObjectPropertyList list, ClilocLNG lng, bool lookup, Func<int, string, T> selector)
{
if (list?.Entity?.Deleted != false || selector == null)
{
yield break;
}
if (lng == ClilocLNG.NULL)
{
lng = DefaultLanguage;
}
ObjectPool.Acquire(out StringBuilder param);
var data = list.GetBuffer();
var index = 15;
while (index + 4 < data.Length)
{
var num = data[index++] << 24 | data[index++] << 16 | data[index++] << 8 | data[index++];
if (index + 2 > data.Length)
{
break;
}
var paramLength = data[index++] << 8 | data[index++];
if (paramLength > 0)
{
var terminate = index + paramLength;
if (terminate >= data.Length)
{
terminate = data.Length - 1;
}
while (index + 2 <= terminate + 1)
{
if ((data[index++] | data[index++] << 8) == 0)
{
break;
}
param.Append(Encoding.Unicode.GetChars(data, index - 2, 2));
}
}
yield return selector(num, lookup ? GetString(lng, num, param) : String.Empty);
param.Clear();
}
ObjectPool.Free(ref param);
}
public static IEnumerable<int> EnumerateNumbers(this ObjectPropertyList list)
{
return Enumerate(list, DefaultLanguage, false, (id, text) => id);
}
public static IEnumerable<string> EnumerateLines(this ObjectPropertyList list)
{
return EnumerateLines(list, DefaultLanguage);
}
public static IEnumerable<string> EnumerateLines(this ObjectPropertyList list, Mobile m)
{
return EnumerateLines(list, GetLanguage(m));
}
public static IEnumerable<string> EnumerateLines(this ObjectPropertyList list, ClilocLNG lng)
{
return Enumerate(list, lng, true, (id, text) => text);
}
public static IEnumerable<TextDefinition> EnumerateEntries(this ObjectPropertyList list)
{
return EnumerateEntries(list, DefaultLanguage);
}
public static IEnumerable<TextDefinition> EnumerateEntries(this ObjectPropertyList list, Mobile m)
{
return EnumerateEntries(list, GetLanguage(m));
}
public static IEnumerable<TextDefinition> EnumerateEntries(this ObjectPropertyList list, ClilocLNG lng)
{
return Enumerate(list, lng, true, (id, text) => new TextDefinition(id, text));
}
public static string[] DecodePropertyList(this ObjectPropertyList list)
{
return DecodePropertyList(list, DefaultLanguage);
}
public static string[] DecodePropertyList(this ObjectPropertyList list, Mobile m)
{
return DecodePropertyList(list, GetLanguage(m));
}
public static string[] DecodePropertyList(this ObjectPropertyList list, ClilocLNG lng)
{
return EnumerateLines(list, lng).ToArray();
}
public static string DecodePropertyListHeader(this ObjectPropertyList list)
{
return DecodePropertyListHeader(list, DefaultLanguage);
}
public static string DecodePropertyListHeader(this ObjectPropertyList list, Mobile v)
{
return DecodePropertyListHeader(list, GetLanguage(v));
}
public static string DecodePropertyListHeader(this ObjectPropertyList list, ClilocLNG lng)
{
return EnumerateLines(list, lng).FirstOrDefault() ?? String.Empty;
}
public static string[] GetAllLines(this ObjectPropertyList list)
{
return DecodePropertyList(list);
}
public static string[] GetAllLines(this ObjectPropertyList list, ClilocLNG lng)
{
return DecodePropertyList(list, lng);
}
public static string GetHeader(this ObjectPropertyList list)
{
return DecodePropertyListHeader(list);
}
public static string GetHeader(this ObjectPropertyList list, ClilocLNG lng)
{
return DecodePropertyListHeader(list, lng);
}
public static string[] GetBody(this ObjectPropertyList list)
{
return GetBody(list, DefaultLanguage);
}
public static string[] GetBody(this ObjectPropertyList list, ClilocLNG lng)
{
return EnumerateLines(list, lng).Skip(1).ToArray();
}
public static bool Contains(this ObjectPropertyList list, int num)
{
return IndexOf(list, num) != -1;
}
public static int IndexOf(this ObjectPropertyList list, int num)
{
return EnumerateNumbers(list).IndexOf(num);
}
public static ClilocLNG GetLanguage(this Mobile m)
{
if (m == null)
{
return ClilocLNG.NULL;
}
if (!Enum.TryParse(m.Language, out ClilocLNG lng))
{
lng = ClilocLNG.ENU;
}
return lng;
}
}
}

View File

@@ -0,0 +1,90 @@
#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.IO;
using System.Linq;
using System.Threading.Tasks;
using Server;
using VitaNex.IO;
#endregion
namespace VitaNex
{
[CoreService("Clilocs", "1.0.0.0", TaskPriority.High)]
public static partial class Clilocs
{
static Clilocs()
{
CSOptions = new CoreServiceOptions(typeof(Clilocs));
Tables = new Dictionary<ClilocLNG, ClilocTable>
{
{ClilocLNG.ENU, new ClilocTable()},
{ClilocLNG.DEU, new ClilocTable()},
{ClilocLNG.ESP, new ClilocTable()},
{ClilocLNG.FRA, new ClilocTable()},
{ClilocLNG.JPN, new ClilocTable()},
{ClilocLNG.KOR, new ClilocTable()},
{ClilocLNG.CHT, new ClilocTable()}
};
}
private static void CSConfig()
{
CommandUtility.Register("ExportCliloc", AccessLevel.Administrator, ExportCommand);
var tables = new List<ClilocTable>(Tables.Values);
//bool noFind = false;
Core.DataDirectories.TakeWhile(path => !tables.TrueForAll(t => t.Loaded))
.Where(path => !String.IsNullOrWhiteSpace(path))
.ForEach(
path => Parallel.ForEach(
Tables,
kvp =>
{
if (kvp.Value.Loaded)
{
return;
}
var file = "Cliloc." + kvp.Key.ToString().ToLower();
var stub = IOUtility.GetSafeFilePath(path + "/" + file, true);
if (!File.Exists(stub))
{
//CSOptions.ToConsole("WARNING: {0} not found!", file);
//noFind = true;
return;
}
kvp.Value.Load(new FileInfo(stub));
}));
/*if (noFind)
{
CSOptions.ToConsole(
"WARNING: One or more required cliloc files could not be loaded, any features that rely on this service will not work as expected and/or may cause a fatal exception!");
}*/
tables.Free(true);
}
private static void CSInvoke()
{ }
}
}

View File

@@ -0,0 +1,68 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System.IO;
using System.Text;
using Server;
#endregion
namespace VitaNex
{
public sealed class ClilocData
{
private ClilocInfo _Info;
public ClilocLNG Language { get; private set; }
public int Index { get; private set; }
public long Offset { get; private set; }
public long Length { get; private set; }
public ClilocData(ClilocLNG lng, int index, long offset, long length)
{
Language = lng;
Index = index;
Offset = offset;
Length = length;
}
public void Clear()
{
_Info = null;
}
public void Load(GenericReader bin)
{
bin.Seek(Offset, SeekOrigin.Begin);
var data = new byte[Length];
for (long i = 0; i < data.Length; i++)
{
data[i] = bin.ReadByte();
}
_Info = new ClilocInfo(Language, Index, Encoding.UTF8.GetString(data));
}
public ClilocInfo Lookup(FileInfo file, bool forceUpdate = false)
{
if (_Info == null || forceUpdate)
{
VitaNexCore.TryCatch(f => f.Deserialize(Load), file, Clilocs.CSOptions.ToConsole);
}
return _Info;
}
}
}

View File

@@ -0,0 +1,153 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Linq;
using System.Text;
using System.Threading;
using Server;
using VitaNex.Collections;
#endregion
namespace VitaNex
{
public sealed class ClilocInfo : IDisposable
{
private volatile object[] _Buffer;
public ClilocLNG Language { get; private set; }
public int Index { get; private set; }
public int Count { get; private set; }
public string Text { get; private set; }
public string Format { get; private set; }
public bool HasArgs { get; private set; }
public ClilocInfo(ClilocLNG lng, int index, string text)
{
Language = lng;
Index = index;
Text = text ?? String.Empty;
Format = Clilocs.VarPattern.Replace(Text, e => $"{{{Count++}}}");
HasArgs = Count > 0;
}
private string Compile(object[] args)
{
if (!HasArgs)
{
return Text;
}
if (args == null)
{
args = Array.Empty<object>();
}
if (_Buffer == null)
{
_Buffer = new object[Count];
}
var sep = Count > 1 ? "\t" : null;
var max = Math.Max(args.Length, _Buffer.Length);
var lim = _Buffer.Length - 1;
for (var i = 0; i < max; i++)
{
if (i >= _Buffer.Length)
{
_Buffer[lim] = $"{_Buffer[lim]} {args[i]}";
}
else if (i < args.Length)
{
if (i < lim)
{
_Buffer[i] = $"{args[i]}{sep}";
}
else
{
_Buffer[i] = args[i];
}
}
else
{
if (i < lim)
{
_Buffer[i] = sep;
}
else
{
_Buffer[i] = null;
}
}
}
var result = String.Format(Format, _Buffer);
Array.Clear(_Buffer, 0, _Buffer.Length);
return Clilocs.NumPattern.Replace(result, match =>
{
if (Int32.TryParse(match.Groups["index"].Value, out var sid))
{
return Clilocs.GetRawString(Language, sid);
}
return match.Value;
});
}
public override string ToString()
{
return Text;
}
public string ToString(StringBuilder args)
{
return ToString(args?.ToString());
}
public string ToString(string args)
{
return ToString(args?.Split('\t'));
}
public string ToString(params object[] args)
{
return Compile(args);
}
public TextDefinition ToDefinition()
{
return new TextDefinition(Index, Text);
}
void IDisposable.Dispose()
{
GC.SuppressFinalize(this);
_Buffer = null;
Text = null;
Format = null;
}
}
}

View File

@@ -0,0 +1,56 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
namespace VitaNex
{
public enum ClilocLNG
{
NULL,
ENU,
DEU,
ESP,
FRA,
JPN,
KOR,
CHT
}
public static class ClilocLNGExt
{
public static string ToFullString(this ClilocLNG lng)
{
if (lng == ClilocLNG.NULL)
{
lng = Clilocs.DefaultLanguage;
}
switch (lng)
{
case ClilocLNG.ENU:
return "English";
case ClilocLNG.DEU:
return "German";
case ClilocLNG.ESP:
return "Spanish";
case ClilocLNG.FRA:
return "French";
case ClilocLNG.JPN:
return "Japanese";
case ClilocLNG.KOR:
return "Korean";
case ClilocLNG.CHT:
return "Chinese";
}
return lng.ToString();
}
}
}

View File

@@ -0,0 +1,189 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Server;
#endregion
namespace VitaNex
{
public sealed class ClilocTable : IEnumerable<ClilocData>, IDisposable
{
private readonly Dictionary<int, ClilocData> _Table = new Dictionary<int, ClilocData>();
public int Count => _Table.Count;
public ClilocLNG Language { get; private set; }
public FileInfo InputFile { get; private set; }
public bool Loaded { get; private set; }
public ClilocInfo this[int index] => Lookup(index);
public void Dispose()
{
Unload();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _Table.Values.GetEnumerator();
}
public IEnumerator<ClilocData> GetEnumerator()
{
return _Table.Values.GetEnumerator();
}
public void Clear()
{
foreach (var d in _Table.Values)
{
d.Clear();
}
_Table.Clear();
}
public void Unload()
{
if (!Loaded)
{
return;
}
Language = ClilocLNG.NULL;
InputFile = null;
Clear();
Loaded = false;
}
public void Load(FileInfo file)
{
if (Loaded)
{
if (Insensitive.Equals(file?.FullName, InputFile?.FullName))
{
return;
}
Clear();
}
VitaNexCore.TryCatch(f =>
{
if (!Enum.TryParse(f.Extension.TrimStart('.'), true, out ClilocLNG lng))
{
throw new FileLoadException($"Could not detect language for: {f.FullName}");
}
Language = lng;
InputFile = f;
InputFile.Deserialize(reader =>
{
var size = reader.Seek(0, SeekOrigin.End);
reader.Seek(0, SeekOrigin.Begin);
reader.ReadInt();
reader.ReadShort();
while (reader.Seek(0, SeekOrigin.Current) < size)
{
var index = reader.ReadInt();
reader.ReadByte();
int length = reader.ReadShort();
var offset = reader.Seek(0, SeekOrigin.Current);
reader.Seek(length, SeekOrigin.Current);
_Table[index] = new ClilocData(Language, index, offset, length);
}
});
Loaded = true;
}, file, Clilocs.CSOptions.ToConsole);
}
public bool Contains(int index)
{
return _Table.ContainsKey(index);
}
public bool IsNullOrWhiteSpace(int index)
{
if (!_Table.TryGetValue(index, out var data) || data == null)
{
return true;
}
var info = data.Lookup(InputFile);
if (info == null || String.IsNullOrWhiteSpace(info.Text))
{
return true;
}
return false;
}
public ClilocInfo Update(int index)
{
if (!_Table.TryGetValue(index, out var data) || data == null)
{
return null;
}
return data.Lookup(InputFile, true);
}
public ClilocInfo Lookup(int index)
{
if (!_Table.TryGetValue(index, out var data) || data == null)
{
return null;
}
return data.Lookup(InputFile);
}
public void LookupAll()
{
VitaNexCore.TryCatch(() =>
{
InputFile.Deserialize(reader =>
{
foreach (var d in _Table.Values)
{
VitaNexCore.TryCatch(d.Load, reader, Clilocs.CSOptions.ToConsole);
}
});
}, Clilocs.CSOptions.ToConsole);
}
public override string ToString()
{
return Language == ClilocLNG.NULL ? "Not Loaded" : $"Cliloc.{Language}";
}
}
}

View File

@@ -0,0 +1,167 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
#endregion
namespace VitaNex.Crypto
{
/// <summary>
/// Jenkins Lookup 3 non-cryptographic HashAlgorithm implementation.
/// Reference: https://en.wikipedia.org/wiki/Jenkins_hash_function
/// </summary>
[ComVisible(true)]
public abstract class Jenkins3 : HashAlgorithm
{
private static readonly string[] _Names = { "J3", "Jenkins3", "Jenkins-3", "VitaNex.Crypto.Jenkins3" };
static Jenkins3()
{
CryptoConfig.AddAlgorithm(typeof(Jenkins3CryptoServiceProvider), _Names);
}
[SecuritySafeCritical]
public static new Jenkins3 Create()
{
return Create("VitaNex.Crypto.Jenkins3");
}
[SecuritySafeCritical]
public static new Jenkins3 Create(string algName)
{
return (Jenkins3)CryptoConfig.CreateFromName(algName);
}
protected Jenkins3()
{
HashSizeValue = 64;
}
}
/// <summary>
/// Jenkins Lookup 3 non-cryptographic HashAlgorithm implementation.
/// Reference: https://en.wikipedia.org/wiki/Jenkins_hash_function
/// </summary>
public sealed class Jenkins3CryptoServiceProvider : Jenkins3
{
public const uint DeadBeef = 0xDEADBEEF;
private string _Seed;
private uint _X0, _X1, _X2, _X3, _X4, _X5;
public override void Initialize()
{
_Seed = String.Empty;
_X0 = _X1 = _X2 = _X3 = _X4 = _X5 = 0;
}
protected override void HashCore(byte[] array, int ibStart, int cbSize)
{
if (ibStart == 0 && cbSize == array.Length)
{
_Seed += BitConverter.ToString(array);
}
else
{
_Seed += BitConverter.ToString(array, ibStart, cbSize);
}
}
protected override byte[] HashFinal()
{
_X0 = _X1 = _X2 = DeadBeef + (uint)_Seed.Length;
_X3 = _X4 = 0;
int pos;
for (pos = 0; pos + 12 < _Seed.Length; pos += 12)
{
_X1 = (uint)((_Seed[pos + 7] << 24) | (_Seed[pos + 6] << 16) | (_Seed[pos + 5] << 8) | _Seed[pos + 4]) + _X1;
_X2 = (uint)((_Seed[pos + 11] << 24) | (_Seed[pos + 10] << 16) | (_Seed[pos + 9] << 8) | _Seed[pos + 8]) + _X2;
_X3 = (uint)((_Seed[pos + 3] << 24) | (_Seed[pos + 2] << 16) | (_Seed[pos + 1] << 8) | _Seed[pos]) - _X2;
_X3 = (_X3 + _X0) ^ (_X2 >> 28) ^ (_X2 << 4);
_X2 += _X1;
_X1 = (_X1 - _X3) ^ (_X3 >> 26) ^ (_X3 << 6);
_X3 += _X2;
_X2 = (_X2 - _X1) ^ (_X1 >> 24) ^ (_X1 << 8);
_X1 += _X3;
_X0 = (_X3 - _X2) ^ (_X2 >> 16) ^ (_X2 << 16);
_X2 += _X1;
_X1 = (_X1 - _X0) ^ (_X0 >> 13) ^ (_X0 << 19);
_X0 += _X2;
_X2 = (_X2 - _X1) ^ (_X1 >> 28) ^ (_X1 << 4);
_X1 += _X0;
}
var rem = _Seed.Length - pos;
if (rem <= 0)
{
return BitConverter.GetBytes(((ulong)_X2 << 32) | _X4);
}
switch (rem)
{
case 12:
_X2 += (uint)_Seed[pos + 11] << 24;
goto case 11;
case 11:
_X2 += (uint)_Seed[pos + 10] << 16;
goto case 10;
case 10:
_X2 += (uint)_Seed[pos + 9] << 8;
goto case 9;
case 9:
_X2 += _Seed[pos + 8];
goto case 8;
case 8:
_X1 += (uint)_Seed[pos + 7] << 24;
goto case 7;
case 7:
_X1 += (uint)_Seed[pos + 6] << 16;
goto case 6;
case 6:
_X1 += (uint)_Seed[pos + 5] << 8;
goto case 5;
case 5:
_X1 += _Seed[pos + 4];
goto case 4;
case 4:
_X0 += (uint)_Seed[pos + 3] << 24;
goto case 3;
case 3:
_X0 += (uint)_Seed[pos + 2] << 16;
goto case 2;
case 2:
_X0 += (uint)_Seed[pos + 1] << 8;
goto case 1;
case 1:
_X0 += _Seed[pos];
break;
}
_X2 = (_X2 ^ _X1) - ((_X1 >> 18) ^ (_X1 << 14));
_X5 = (_X2 ^ _X0) - ((_X2 >> 21) ^ (_X2 << 11));
_X1 = (_X1 ^ _X5) - ((_X5 >> 7) ^ (_X5 << 25));
_X2 = (_X2 ^ _X1) - ((_X1 >> 16) ^ (_X1 << 16));
_X3 = (_X2 ^ _X5) - ((_X2 >> 28) ^ (_X2 << 4));
_X1 = (_X1 ^ _X3) - ((_X3 >> 18) ^ (_X3 << 14));
_X4 = (_X2 ^ _X1) - ((_X1 >> 8) ^ (_X1 << 24));
return BitConverter.GetBytes(((ulong)_X1 << 32) | _X4);
}
}
}

View File

@@ -0,0 +1,52 @@
#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.Linq;
using System.Security.Cryptography;
#endregion
namespace VitaNex.Crypto
{
public static partial class CryptoService
{
public static CryptoHashType[] HashTypes { get; private set; }
public static Dictionary<int, CryptoHashCodeProvider> Providers { get; private set; }
public static void RegisterProvider(CryptoHashType type, HashAlgorithm hal)
{
Providers[(int)type] = new CryptoHashCodeProvider((int)type, hal);
}
public static void RegisterProvider(int type, HashAlgorithm hal)
{
Providers[type] = new CryptoHashCodeProvider(type, hal);
}
public static CryptoHashCodeProvider GetProvider(CryptoHashType type)
{
return Providers.GetValue((int)type);
}
public static CryptoHashCodeProvider GetProvider(int type)
{
return Providers.GetValue(type);
}
public static bool IsExtended(int type)
{
return HashTypes.Cast<int>().All(t => t != type);
}
}
}

View File

@@ -0,0 +1,32 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Linq;
using System.Security.Cryptography;
#endregion
namespace VitaNex.Crypto
{
[CoreService("Crypto", "3.0.0.1", TaskPriority.Highest)]
public static partial class CryptoService
{
static CryptoService()
{
HashTypes = ((CryptoHashType)0).GetValues<CryptoHashType>();
Providers = HashTypes.Select(h => new CryptoHashCodeProvider((int)h, HashAlgorithm.Create(h.ToString())))
.Where(p => p.Provider != null)
.ToDictionary(p => p.ProviderID, p => p);
}
}
}

View File

@@ -0,0 +1,37 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Security.Cryptography;
#endregion
namespace VitaNex.Crypto
{
public interface ICryptoProvider : IDisposable
{
bool IsDisposed { get; }
int ProviderID { get; }
HashAlgorithm Provider { get; }
string Seed { get; }
byte[] Buffer { get; }
string Generate(string seed);
}
public interface ICryptoMutator : ICryptoProvider
{
string Transform(string seed);
string Mutate(string code);
}
}

View File

@@ -0,0 +1,49 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.Crypto
{
public static class CryptoGenerator
{
public static string GenString(CryptoHashType type, string seed)
{
var p = CryptoService.GetProvider(type);
return p != null ? p.Generate(seed) : String.Empty;
}
public static string GenString(int type, string seed)
{
var p = CryptoService.GetProvider(type);
return p != null ? p.Generate(seed) : String.Empty;
}
public static CryptoHashCode GenHashCode(CryptoHashType type, string seed)
{
return new CryptoHashCode((int)type, seed);
}
public static CryptoHashCode GenHashCode(int type, string seed)
{
return new CryptoHashCode(type, seed);
}
public static CryptoHashCodeTable GenTable(string seed)
{
return new CryptoHashCodeTable(seed);
}
}
}

View File

@@ -0,0 +1,191 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Server;
#endregion
namespace VitaNex.Crypto
{
public class CryptoHashCode : IEquatable<CryptoHashCode>, IComparable<CryptoHashCode>, IEnumerable<char>, IDisposable
{
private int _ValueHash = -1;
public int ValueHash => GetValueHash();
public bool IsDisposed { get; private set; }
public bool IsExtended => CryptoService.IsExtended(ProviderID);
public int ProviderID { get; protected set; }
public virtual string Value { get; private set; }
public int Length => Value.Length;
public char this[int index] => Value[index];
public CryptoHashCode(CryptoHashType type, string seed)
: this((int)type, seed)
{ }
public CryptoHashCode(int providerID, string seed)
{
ProviderID = providerID;
Value = CryptoGenerator.GenString(ProviderID, seed ?? String.Empty);
}
public CryptoHashCode(GenericReader reader)
{
Deserialize(reader);
}
~CryptoHashCode()
{
Dispose();
}
public override string ToString()
{
return Value;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<char> GetEnumerator()
{
return Value.GetEnumerator();
}
public virtual int CompareTo(CryptoHashCode code)
{
return !ReferenceEquals(code, null) ? Insensitive.Compare(Value, code.Value) : -1;
}
public override bool Equals(object obj)
{
return obj is CryptoHashCode && Equals((CryptoHashCode)obj);
}
public bool Equals(CryptoHashCode other)
{
return !ReferenceEquals(other, null) && ValueHash == other.ValueHash;
}
public override int GetHashCode()
{
return unchecked(((ProviderID + 1) * 397) ^ ValueHash);
}
public int GetValueHash()
{
if (_ValueHash > -1)
{
return _ValueHash;
}
var hash = Value.Aggregate(Value.Length, (h, c) => unchecked((h * 397) ^ c));
// It may be negative, so ensure it is positive, normally this wouldn't be the case but negative integers for
// almost unique id's should be positive for things like database keys.
// Note this increases chance of unique collisions by 50%, though still extremely unlikely;
// 1 : 2,147,483,647
return _ValueHash = Math.Abs(hash);
}
public virtual void Dispose()
{
if (IsDisposed)
{
return;
}
IsDisposed = true;
GC.SuppressFinalize(this);
Value = null;
}
public virtual void Serialize(GenericWriter writer)
{
var version = writer.SetVersion(2);
writer.Write(ProviderID);
switch (version)
{
case 2:
{
writer.Write(Value);
writer.Write(_ValueHash);
}
break;
case 1:
case 0:
break;
}
}
public virtual void Deserialize(GenericReader reader)
{
var version = reader.GetVersion();
ProviderID = reader.ReadInt();
switch (version)
{
case 2:
{
Value = reader.ReadString();
_ValueHash = reader.ReadInt();
}
break;
case 1:
{
var seed = reader.ReadBool()
? StringCompression.Unpack(reader.ReadBytes())
: Encoding.UTF32.GetString(reader.ReadBytes());
Value = CryptoGenerator.GenString(ProviderID, seed ?? String.Empty);
}
break;
case 0:
{
var seed = reader.ReadString();
Value = CryptoGenerator.GenString(ProviderID, seed ?? String.Empty);
}
break;
}
}
public static bool operator ==(CryptoHashCode l, CryptoHashCode r)
{
return ReferenceEquals(l, null) ? ReferenceEquals(r, null) : l.Equals(r);
}
public static bool operator !=(CryptoHashCode l, CryptoHashCode r)
{
return ReferenceEquals(l, null) ? !ReferenceEquals(r, null) : !l.Equals(r);
}
}
}

View File

@@ -0,0 +1,102 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Security.Cryptography;
using System.Text;
#endregion
namespace VitaNex.Crypto
{
public sealed class CryptoHashCodeProvider : ICryptoProvider
{
public bool IsDisposed { get; private set; }
public int ProviderID { get; private set; }
public HashAlgorithm Provider { get; private set; }
public string Seed { get; private set; }
public byte[] Buffer { get; private set; }
public CryptoHashCodeProvider(int id, HashAlgorithm hal)
{
ProviderID = id;
Provider = hal;
}
~CryptoHashCodeProvider()
{
Dispose();
}
public override int GetHashCode()
{
return ProviderID;
}
public override bool Equals(object obj)
{
return obj is CryptoHashCodeProvider && Equals((CryptoHashCodeProvider)obj);
}
public override string ToString()
{
return String.Format("{0}:{1}", ProviderID, Provider != null ? Provider.GetType().FullName : "NULL");
}
public string Generate(string seed)
{
seed = seed ?? String.Empty;
if (Buffer == null || Seed != seed)
{
Seed = seed;
Buffer = Encoding.UTF32.GetBytes(Seed);
Buffer = Provider.ComputeHash(Buffer);
}
return BitConverter.ToString(Buffer);
}
public void Dispose()
{
if (IsDisposed)
{
return;
}
IsDisposed = true;
GC.SuppressFinalize(this);
if (Provider != null)
{
VitaNexCore.TryCatch(Provider.Clear);
}
Provider = null;
Seed = null;
Buffer = null;
}
public static bool operator ==(CryptoHashCodeProvider l, CryptoHashCodeProvider r)
{
return ReferenceEquals(l, null) ? ReferenceEquals(r, null) : l.Equals(r);
}
public static bool operator !=(CryptoHashCodeProvider l, CryptoHashCodeProvider r)
{
return ReferenceEquals(l, null) ? !ReferenceEquals(r, null) : !l.Equals(r);
}
}
}

View File

@@ -0,0 +1,78 @@
#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.Linq;
#endregion
namespace VitaNex.Crypto
{
public class CryptoHashCodeTable : List<CryptoHashCode>, IEquatable<CryptoHashCodeTable>, IDisposable
{
public bool IsDisposed { get; private set; }
public CryptoHashCodeTable(string seed)
{
AddRange(CryptoService.Providers.Keys.Select(type => CryptoGenerator.GenHashCode(type, seed)));
}
~CryptoHashCodeTable()
{
Dispose();
}
public virtual void Dispose()
{
if (IsDisposed)
{
return;
}
IsDisposed = true;
GC.SuppressFinalize(this);
this.Free(true);
}
public override int GetHashCode()
{
return this.GetContentsHashCode();
}
public override bool Equals(object obj)
{
return obj is CryptoHashCodeTable && Equals((CryptoHashCodeTable)obj);
}
public virtual bool Equals(CryptoHashCodeTable table)
{
return !ReferenceEquals(table, null) && (ReferenceEquals(table, this) || this.ContentsEqual(table));
}
public override string ToString()
{
return String.Join("+", this);
}
public static bool operator ==(CryptoHashCodeTable l, CryptoHashCodeTable r)
{
return ReferenceEquals(l, null) ? ReferenceEquals(r, null) : l.Equals(r);
}
public static bool operator !=(CryptoHashCodeTable l, CryptoHashCodeTable r)
{
return ReferenceEquals(l, null) ? !ReferenceEquals(r, null) : !l.Equals(r);
}
}
}

View File

@@ -0,0 +1,24 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
namespace VitaNex.Crypto
{
public enum CryptoHashType
{
MD5 = 0,
SHA1 = 1,
SHA256 = 2,
SHA384 = 3,
SHA512 = 4,
RIPEMD160 = 5,
Jenkins3 = 6
}
}

View File

@@ -0,0 +1,466 @@
#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.Drawing;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Server;
using Server.Commands;
using Server.Network;
using VitaNex.IO;
using VitaNex.Text;
#endregion
namespace VitaNex.Notify
{
public static partial class Notify
{
public const AccessLevel Access = AccessLevel.Administrator;
public static CoreServiceOptions CSOptions { get; private set; }
public static Type[] GumpTypes { get; private set; }
public static Dictionary<Type, Type[]> NestedTypes { get; private set; }
public static Dictionary<Type, Type> SettingsMap { get; private set; }
public static BinaryDataStore<Type, NotifySettings> Settings { get; private set; }
public static event Action<NotifyGump> OnGlobalMessage;
public static event Action<NotifyGump> OnLocalMessage;
public static event Action<string> OnBroadcast;
[Usage("Notify <text | html | bbc>"), Description(
"Send a global notification gump to all online clients, " + //
"containing a message parsed from HTML, BBS or plain text.")]
private static void HandleNotify(CommandEventArgs e)
{
if (e.Mobile.AccessLevel >= AccessLevel.Seer && !String.IsNullOrWhiteSpace(e.ArgString) && ValidateCommand(e))
{
Broadcast(e.Mobile, e.ArgString);
return;
}
new NotifySettingsGump(e.Mobile).Send();
}
[Usage("NotifyAC <text | html | bbc>"), Description(
"Send a global notification gump to all online clients, " + //
"containing a message parsed from HTML, BBS or plain text, " + //
"which auto-closes after 10 seconds.")]
private static void HandleNotifyAC(CommandEventArgs e)
{
if (ValidateCommand(e))
{
Broadcast(e.Mobile, e.ArgString, true);
}
}
[Usage("NotifyNA <text | html | bbc>"), Description(
"Send a global notification gump to all online clients, " + //
"containing a message parsed from HTML, BBS or plain text, " + //
"which has no animation delay.")]
private static void HandleNotifyNA(CommandEventArgs e)
{
if (ValidateCommand(e))
{
Broadcast(e.Mobile, e.ArgString, false, false);
}
}
[Usage("NotifyACNA <text | html | bbc>"), Description(
"Send a global notification gump to all online clients, " + //
"containing a message parsed from HTML, BBS or plain text, " + //
"which auto-closes after 10 seconds and has no animation delay.")]
private static void HandleNotifyACNA(CommandEventArgs e)
{
if (ValidateCommand(e))
{
Broadcast(e.Mobile, e.ArgString, true, false);
}
}
private static bool ValidateCommand(CommandEventArgs e)
{
if (e == null || e.Mobile == null)
{
return false;
}
if (String.IsNullOrWhiteSpace(e.ArgString) ||
String.IsNullOrWhiteSpace(Regex.Replace(e.ArgString, @"<[^>]*>", String.Empty)))
{
e.Mobile.SendMessage(0x22, "Html/BBC message must be at least 1 character and not all white-space after parsing.");
e.Mobile.SendMessage(0x22, "Usage: {0}{1} <text | html | bbc>", CommandSystem.Prefix, e.Command);
return false;
}
return true;
}
public static NotifySettings EnsureSettings<TGump>()
where TGump : NotifyGump
{
return EnsureSettings(typeof(TGump));
}
public static NotifySettings EnsureSettings(Type t)
{
if (t == null || !t.IsEqualOrChildOf<NotifyGump>())
{
return null;
}
if (SettingsMap.TryGetValue(t, out var st) && st != null)
{
var o = Settings.GetValue(st);
if (o != null)
{
return o;
}
}
const BindingFlags f = BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic;
var m = t.GetMethod("InitSettings", f);
if (m == null)
{
foreach (var p in t.EnumerateHierarchy())
{
m = p.GetMethod("InitSettings", f);
if (m != null)
{
st = p;
break;
}
}
}
if (st == null)
{
st = t;
}
var init = false;
if (!Settings.TryGetValue(st, out var settings) || settings == null)
{
Settings[st] = settings = new NotifySettings(st);
init = true;
}
SettingsMap[t] = st;
if (init && m != null)
{
m.Invoke(null, new object[] { settings });
}
return settings;
}
public static bool IsAutoClose<TGump>(Mobile pm)
where TGump : NotifyGump
{
return IsAutoClose(typeof(TGump), pm);
}
public static bool IsAutoClose(Type t, Mobile pm)
{
var settings = EnsureSettings(t);
return settings != null && settings.IsAutoClose(pm);
}
public static bool IsIgnored<TGump>(Mobile pm)
where TGump : NotifyGump
{
return IsIgnored(typeof(TGump), pm);
}
public static bool IsIgnored(Type t, Mobile pm)
{
var settings = EnsureSettings(t);
return settings != null && settings.IsIgnored(pm);
}
public static bool IsAnimated<TGump>(Mobile pm)
where TGump : NotifyGump
{
return IsAnimated(typeof(TGump), pm);
}
public static bool IsAnimated(Type t, Mobile pm)
{
var settings = EnsureSettings(t);
return settings == null || settings.IsAnimated(pm);
}
public static bool IsTextOnly<TGump>(Mobile pm)
where TGump : NotifyGump
{
return IsTextOnly(typeof(TGump), pm);
}
public static bool IsTextOnly(Type t, Mobile pm)
{
var settings = EnsureSettings(t);
return settings != null && settings.IsTextOnly(pm);
}
public static void AlterTime<TGump>(Mobile pm, ref double value)
where TGump : NotifyGump
{
AlterTime(typeof(TGump), pm, ref value);
}
public static void AlterTime(Type t, Mobile pm, ref double value)
{
var settings = EnsureSettings(t);
if (settings != null)
{
settings.AlterTime(pm, ref value);
}
}
public static void Broadcast(
Mobile m,
string message,
bool autoClose = false,
bool animate = true,
AccessLevel level = AccessLevel.Player)
{
if (m != null && !m.Deleted)
{
message = String.Format("[{0}] {1}:\n{2}", DateTime.Now.ToSimpleString("t@h:m@"), m.RawName, message);
}
Broadcast(message, autoClose, animate ? 1.0 : 0.0, 10.0, null, null, null, level);
}
public static void Broadcast(
string html,
bool autoClose = true,
double delay = 1.0,
double pause = 5.0,
Color? color = null,
Action<WorldNotifyGump> beforeSend = null,
Action<WorldNotifyGump> afterSend = null,
AccessLevel level = AccessLevel.Player)
{
Broadcast<WorldNotifyGump>(html, autoClose, delay, pause, color, beforeSend, afterSend, level);
}
public static void Broadcast<TGump>(
string html,
bool autoClose = true,
double delay = 1.0,
double pause = 5.0,
Color? color = null,
Action<TGump> beforeSend = null,
Action<TGump> afterSend = null,
AccessLevel level = AccessLevel.Player)
where TGump : NotifyGump
{
var c = NetState.Instances.Count;
while (--c >= 0)
{
if (!NetState.Instances.InBounds(c))
{
continue;
}
var ns = NetState.Instances[c];
if (ns != null && ns.Running && ns.Mobile != null && ns.Mobile.AccessLevel >= level)
{
VitaNexCore.TryCatch(
m => Send(false, m, html, autoClose, delay, pause, color, beforeSend, afterSend, level),
ns.Mobile);
}
}
if (level < AccessLevel.Counselor && OnBroadcast != null)
{
OnBroadcast(html.ParseBBCode());
}
}
public static void Send(
Mobile m,
string html,
bool autoClose = true,
double delay = 1.0,
double pause = 3.0,
Color? color = null,
Action<NotifyGump> beforeSend = null,
Action<NotifyGump> afterSend = null,
AccessLevel level = AccessLevel.Player)
{
Send(true, m, html, autoClose, delay, pause, color, beforeSend, afterSend, level);
}
public static void Send<TGump>(
Mobile m,
string html,
bool autoClose = true,
double delay = 1.0,
double pause = 3.0,
Color? color = null,
Action<TGump> beforeSend = null,
Action<TGump> afterSend = null,
AccessLevel level = AccessLevel.Player)
where TGump : NotifyGump
{
Send(true, m, html, autoClose, delay, pause, color, beforeSend, afterSend, level);
}
private static void Send<TGump>(
bool local,
Mobile m,
string html,
bool autoClose = true,
double delay = 1.0,
double pause = 3.0,
Color? color = null,
Action<TGump> beforeSend = null,
Action<TGump> afterSend = null,
AccessLevel level = AccessLevel.Player)
where TGump : NotifyGump
{
if (!m.IsOnline() || m.AccessLevel < level)
{
return;
}
var t = typeof(TGump);
if (t.IsAbstract || m.HasGump(t))
{
if (!NestedTypes.TryGetValue(t, out var subs) || subs == null)
{
NestedTypes[t] = subs = t.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic) //
.Where(st => st.IsChildOf<NotifyGump>())
.ToArray();
}
var sub = subs.FirstOrDefault(st => !m.HasGump(st));
if (sub != null)
{
t = sub;
}
}
if (IsIgnored(t, m))
{
return;
}
if (!t.IsAbstract && !IsTextOnly(t, m))
{
if (!autoClose && IsAutoClose(t, m))
{
autoClose = true;
}
if (delay > 0.0 && !IsAnimated(t, m))
{
delay = 0.0;
}
if (delay > 0.0)
{
AlterTime(t, m, ref delay);
}
if (pause > 3.0)
{
AlterTime(t, m, ref pause);
pause = Math.Max(3.0, pause);
}
var ng = t.CreateInstanceSafe<TGump>(m, html);
if (ng != null)
{
ng.AutoClose = autoClose;
ng.AnimDuration = TimeSpan.FromSeconds(Math.Max(0, delay));
ng.PauseDuration = TimeSpan.FromSeconds(Math.Max(0, pause));
ng.HtmlColor = color ?? Color.White;
if (ng.IsDisposed)
{
return;
}
if (local && OnLocalMessage != null)
{
OnLocalMessage(ng);
}
else if (!local && OnGlobalMessage != null)
{
OnGlobalMessage(ng);
}
if (beforeSend != null)
{
beforeSend(ng);
}
if (ng.IsDisposed)
{
return;
}
ng.Send();
if (ng.IsDisposed)
{
return;
}
if (afterSend != null)
{
afterSend(ng);
}
return;
}
}
html = html.StripHtmlBreaks(true);
html = html.Replace("\n", " ");
html = html.StripHtml(false);
html = html.StripTabs();
html = html.StripCRLF();
m.SendMessage(html);
}
}
}

View File

@@ -0,0 +1,114 @@
#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 Server;
using VitaNex.IO;
#endregion
namespace VitaNex.Notify
{
[CoreService("Notify", "3.0.0.0", TaskPriority.Highest)]
public static partial class Notify
{
static Notify()
{
CSOptions = new CoreServiceOptions(typeof(Notify));
GumpTypes = typeof(NotifyGump).GetChildren(t => !t.IsNested);
NestedTypes = new Dictionary<Type, Type[]>();
SettingsMap = new Dictionary<Type, Type>();
Settings = new BinaryDataStore<Type, NotifySettings>(VitaNexCore.SavesDirectory + "/Notify", "Settings")
{
Async = true,
OnSerialize = Serialize,
OnDeserialize = Deserialize
};
}
private static void CSConfig()
{
foreach (var t in GumpTypes)
{
EnsureSettings(t);
}
}
private static void CSInvoke()
{
CommandUtility.Register("Notify", AccessLevel.Player, HandleNotify);
CommandUtility.Register("NotifyAC", AccessLevel.Seer, HandleNotifyAC);
CommandUtility.Register("NotifyNA", AccessLevel.Seer, HandleNotifyNA);
CommandUtility.Register("NotifyACNA", AccessLevel.Seer, HandleNotifyACNA);
}
private static void CSLoad()
{
Settings.Import();
Settings.RemoveRange(o => !GumpTypes.Contains(o.Key) || o.Value == null);
}
private static void CSSave()
{
Settings.Export();
}
private static bool Serialize(GenericWriter writer)
{
writer.SetVersion(0);
writer.WriteBlockDictionary(
Settings,
(w, k, v) =>
{
w.WriteType(k);
v.Serialize(w);
});
return true;
}
private static bool Deserialize(GenericReader reader)
{
reader.GetVersion();
reader.ReadBlockDictionary(
r =>
{
var k = r.ReadType();
var v = EnsureSettings(k);
if (v != null)
{
v.Deserialize(r);
if (v.Type == null)
{
v.Type = k;
}
}
return new KeyValuePair<Type, NotifySettings>(k, v);
},
Settings);
return true;
}
}
}

View File

@@ -0,0 +1,28 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.Notify
{
[Flags]
public enum NotifyFlags : ulong
{
None = 0x0,
AutoClose = 0x1,
Ignore = 0x2,
TextOnly = 0x4,
Animate = 0x8
}
}

View File

@@ -0,0 +1,200 @@
#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 Server;
using Server.Accounting;
#endregion
namespace VitaNex.Notify
{
public sealed class NotifySettings : PropertyObject
{
public Dictionary<IAccount, NotifySettingsState> States { get; private set; }
[CommandProperty(Notify.Access)]
public Dictionary<IAccount, NotifySettingsState>.KeyCollection Keys
{
get => States.Keys;
private set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
}
}
[CommandProperty(Notify.Access)]
public Dictionary<IAccount, NotifySettingsState>.ValueCollection Values
{
get => States.Values;
private set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
}
}
[CommandProperty(Notify.Access, true)]
public Type Type { get; set; }
[CommandProperty(Notify.Access)]
public string Name { get; set; }
[CommandProperty(Notify.Access)]
public string Desc { get; set; }
[CommandProperty(Notify.Access)]
public AccessLevel Access { get; set; }
[CommandProperty(Notify.Access)]
public bool CanIgnore { get; set; }
[CommandProperty(Notify.Access)]
public bool CanAutoClose { get; set; }
public NotifySettings(Type t)
{
Type = t;
Name = t.Name;
Desc = String.Empty;
Access = AccessLevel.Player;
CanIgnore = true;
CanAutoClose = true;
States = new Dictionary<IAccount, NotifySettingsState>();
}
public NotifySettings(GenericReader reader)
: base(reader)
{ }
public override void Clear()
{
States.Clear();
}
public override void Reset()
{
States.Clear();
}
public bool IsAutoClose(Mobile m)
{
return CanAutoClose && m != null && m.Account != null && States.ContainsKey(m.Account) && States[m.Account].AutoClose;
}
public bool IsIgnored(Mobile m)
{
return CanIgnore && m != null && m.Account != null && States.ContainsKey(m.Account) && States[m.Account].Ignore;
}
public bool IsTextOnly(Mobile m)
{
return m != null && m.Account != null && States.ContainsKey(m.Account) && States[m.Account].TextOnly;
}
public bool IsAnimated(Mobile m)
{
return m == null || m.Account == null || !States.ContainsKey(m.Account) || States[m.Account].Animate;
}
public void AlterTime(Mobile m, ref double value)
{
if (m == null || m.Account == null || !States.ContainsKey(m.Account) || value <= 0.0)
{
return;
}
var speed = Math.Max(0, Math.Min(200, (int)States[m.Account].Speed));
if (speed > 100)
{
value -= value * ((speed - 100) / 100.0);
}
else if (speed < 100)
{
value += value * ((100 - speed) / 100.0);
}
}
public NotifySettingsState EnsureState(IAccount a)
{
var s = States.GetValue(a);
if (s == null || s.Settings != this)
{
States[a] = s = new NotifySettingsState(this, a);
}
return s;
}
public override string ToString()
{
var name = Name.Replace("Notify", String.Empty).Replace("Gump", String.Empty);
if (String.IsNullOrWhiteSpace(name))
{
name = "Notifications";
}
return name;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.SetVersion(0);
writer.WriteType(Type);
writer.Write(Name);
writer.Write(Desc);
writer.Write(CanIgnore);
writer.Write(CanAutoClose);
writer.WriteBlockDictionary(States, (w, k, v) => v.Serialize(w));
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
reader.GetVersion();
Type = reader.ReadType();
Name = reader.ReadString();
Desc = reader.ReadString();
CanIgnore = reader.ReadBool();
CanAutoClose = reader.ReadBool();
States = reader.ReadBlockDictionary(
r =>
{
var state = new NotifySettingsState(this, r);
return new KeyValuePair<IAccount, NotifySettingsState>(state.Owner, state);
},
States);
}
}
}

View File

@@ -0,0 +1,125 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using Server;
using Server.Accounting;
#endregion
namespace VitaNex.Notify
{
public sealed class NotifySettingsState : SettingsObject<NotifyFlags>
{
[CommandProperty(Notify.Access)]
public NotifySettings Settings { get; private set; }
[CommandProperty(Notify.Access, true)]
public IAccount Owner { get; private set; }
[CommandProperty(Notify.Access)]
public override NotifyFlags Flags { get => base.Flags; set => base.Flags = value; }
[CommandProperty(Notify.Access)]
public bool AutoClose
{
get => GetFlag(NotifyFlags.AutoClose);
set => SetFlag(NotifyFlags.AutoClose, value);
}
[CommandProperty(Notify.Access)]
public bool Ignore { get => GetFlag(NotifyFlags.Ignore); set => SetFlag(NotifyFlags.Ignore, value); }
[CommandProperty(Notify.Access)]
public bool TextOnly { get => GetFlag(NotifyFlags.TextOnly); set => SetFlag(NotifyFlags.TextOnly, value); }
[CommandProperty(Notify.Access)]
public bool Animate { get => GetFlag(NotifyFlags.Animate); set => SetFlag(NotifyFlags.Animate, value); }
[CommandProperty(Notify.Access)]
public byte Speed { get; set; }
public NotifySettingsState(NotifySettings settings, IAccount owner)
{
Settings = settings;
Owner = owner;
SetDefaults();
}
public NotifySettingsState(NotifySettings settings, GenericReader reader)
: base(reader)
{
Settings = settings;
}
public override void Clear()
{
SetDefaults();
}
public override void Reset()
{
SetDefaults();
}
public void SetDefaults()
{
AutoClose = false;
Ignore = false;
TextOnly = false;
Animate = true;
Speed = 100;
}
public override string ToString()
{
if (Owner != null)
{
if (Settings != null)
{
return Owner + ": " + Settings;
}
return Owner.ToString();
}
if (Settings != null)
{
return Settings.ToString();
}
return base.ToString();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.SetVersion(0);
writer.Write(Owner);
writer.Write(Speed);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
reader.GetVersion();
Owner = reader.ReadAccount();
Speed = reader.ReadByte();
}
}
}

View File

@@ -0,0 +1,551 @@
#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.Drawing;
using System.Linq;
using Server;
using Server.Gumps;
using VitaNex.SuperGumps;
using VitaNex.Text;
#endregion
namespace VitaNex.Notify
{
public abstract class NotifyGump : SuperGump
{
private static void InitSettings(NotifySettings o)
{
o.Name = "Misc Notifications";
o.Desc = "Any notifications that do not fall into a more specific category.";
}
public static event Action<NotifyGump> OnNotify;
public static TimeSpan DefaultAnimDuration = TimeSpan.FromMilliseconds(500.0);
public static TimeSpan DefaultPauseDuration = TimeSpan.FromSeconds(5.0);
public static Size SizeMin = new Size(100, 60);
public static Size SizeMax = new Size(400, 200);
public enum AnimState
{
Show,
Hide,
Pause
}
public TimeSpan AnimDuration { get; set; }
public TimeSpan PauseDuration { get; set; }
public string Html { get; set; }
public Color HtmlColor { get; set; }
public int HtmlIndent { get; set; }
public int BorderID { get; set; }
public int BorderSize { get; set; }
public bool BorderAlpha { get; set; }
public int BackgroundID { get; set; }
public bool BackgroundAlpha { get; set; }
public int WidthMax { get; set; }
public int HeightMax { get; set; }
public bool AutoClose { get; set; }
public int Frame { get; private set; }
public AnimState State { get; private set; }
public int FrameCount => (int)Math.Ceiling(Math.Max(100.0, AnimDuration.TotalMilliseconds) / 100.0);
public int FrameHeight { get; private set; }
public int FrameWidth { get; private set; }
public Size HtmlSize { get; private set; }
public Size OptionsSize { get; private set; }
public List<NotifyGumpOption> Options { get; private set; }
public int OptionsCols { get; private set; }
public int OptionsRows { get; private set; }
public override bool InitPolling => true;
public NotifyGump(Mobile user, string html)
: this(user, html, null)
{ }
public NotifyGump(Mobile user, string html, IEnumerable<NotifyGumpOption> options)
: base(user, null, 0, 140)
{
Options = options.Ensure().ToList();
AnimDuration = DefaultAnimDuration;
PauseDuration = DefaultPauseDuration;
Html = html ?? String.Empty;
HtmlColor = Color.White;
HtmlIndent = 10;
BorderSize = 4;
BorderID = 9204;
BorderAlpha = false;
BackgroundID = 2624;
BackgroundAlpha = true;
AutoClose = true;
Frame = 0;
State = AnimState.Pause;
CanMove = false;
CanResize = false;
CloseSound = -1;
ForceRecompile = true;
AutoRefreshRate = TimeSpan.FromMilliseconds(100.0);
AutoRefresh = true;
WidthMax = 250;
HeightMax = 100;
InitOptions();
}
protected virtual void InitOptions()
{ }
public void AddOption(TextDefinition label, Action<GumpButton> callback)
{
AddOption(label, callback, Color.Empty);
}
public void AddOption(TextDefinition label, Action<GumpButton> callback, Color color)
{
AddOption(label, callback, color, Color.Empty);
}
public void AddOption(TextDefinition label, Action<GumpButton> callback, Color color, Color fill)
{
AddOption(label, callback, color, fill, Color.Empty);
}
public void AddOption(TextDefinition label, Action<GumpButton> callback, Color color, Color fill, Color border)
{
if (color.IsEmpty)
{
color = HtmlColor;
}
var opt = Options.Find(o => o.Label.Equals(label));
if (opt != null)
{
opt.Callback = callback;
opt.LabelColor = color;
opt.FillColor = fill;
opt.BorderColor = border;
}
else
{
Options.Add(new NotifyGumpOption(label, callback, color, fill, border));
}
}
protected virtual Size GetSizeMin()
{
return SizeMin;
}
protected virtual Size GetSizeMax()
{
return SizeMax;
}
protected override void Compile()
{
base.Compile();
var sMin = GetSizeMin();
var sMax = GetSizeMax();
WidthMax = Math.Max(sMin.Width, Math.Min(sMax.Width, WidthMax));
HeightMax = Math.Max(sMin.Height, Math.Min(sMax.Height, HeightMax));
HtmlIndent = Math.Max(0, HtmlIndent);
BorderSize = Math.Max(0, BorderSize);
var wm = WidthMax - (BorderSize * 2);
var text = Html.ParseBBCode(HtmlColor).StripHtmlBreaks(true).StripHtml();
var font = UOFont.Unicode[1];
var s = font.GetSize(text);
s.Width += 4;
s.Height += 4;
if (s.Width > wm)
{
s.Height += (int)Math.Ceiling((s.Width - wm) / (double)wm) * (font.LineHeight + font.LineSpacing);
s.Width = wm;
}
if (!Initialized)
{
s.Height = Math.Max(sMin.Height, Math.Min(HeightMax, Math.Min(sMax.Height, s.Height)));
}
else
{
s.Height = Math.Max(sMin.Height, Math.Min(sMax.Height, Math.Min(sMax.Height, s.Height)));
}
HtmlSize = s;
if (Options.Count > 1)
{
OptionsCols = wm / Math.Max(30, Math.Min(wm, Options.Max(o => o.Width)));
OptionsRows = (int)Math.Ceiling(Options.Count / (double)OptionsCols);
}
else if (Options.Count > 0)
{
OptionsCols = OptionsRows = 1;
}
s.Width = wm;
s.Height = (OptionsRows * 20) + 4;
OptionsSize = s;
HeightMax = (BorderSize * 2) + HtmlSize.Height + OptionsSize.Height;
var f = Frame / (double)FrameCount;
FrameWidth = (int)Math.Ceiling(WidthMax * f);
FrameHeight = (int)Math.Ceiling(HeightMax * f);
}
protected override void CompileLayout(SuperGumpLayout layout)
{
base.CompileLayout(layout);
layout.Add(
"frame",
() =>
{
if (BorderSize > 0 && BorderID >= 0)
{
AddImageTiled(0, 0, FrameWidth, FrameHeight, BorderID);
if (BorderAlpha)
{
AddAlphaRegion(0, 0, FrameWidth, FrameHeight);
}
}
var fw = FrameWidth - (BorderSize * 2);
var fh = FrameHeight - (BorderSize * 2);
if (fw * fh > 0 && BackgroundID > 0)
{
AddImageTiled(BorderSize, BorderSize, fw, fh, BackgroundID);
if (BackgroundAlpha)
{
AddAlphaRegion(BorderSize, BorderSize, fw, fh);
}
}
if (Frame < FrameCount)
{
return;
}
AddButton(FrameWidth - BorderSize, (FrameHeight / 2) - 8, 22153, 22155, OnSettings);
var x = BorderSize + 2 + HtmlIndent;
var y = BorderSize + 2;
var w = fw - (4 + HtmlIndent);
var h = fh - (4 + OptionsSize.Height);
var html = Html.ParseBBCode(HtmlColor).WrapUOHtmlColor(HtmlColor, false);
AddHtml(x, y, w, h, html, false, (HtmlSize.Height - 4) > h);
});
if (Frame >= FrameCount && Options.Count > 0)
{
var x = BorderSize + 2;
var y = BorderSize + HtmlSize.Height + 2;
var w = OptionsSize.Width - 4;
var h = OptionsSize.Height - 4;
CompileOptionsLayout(layout, x, y, w, h, w / OptionsCols);
}
}
protected virtual void CompileOptionsLayout(SuperGumpLayout layout, int x, int y, int w, int h, int bw)
{
if (Frame < FrameCount || Options.Count == 0 || OptionsCols * OptionsRows <= 0)
{
return;
}
layout.Add("opts", () => AddRectangle(x, y, w, h, Color.Black, true));
var oi = 0;
var ox = x;
var oy = y;
foreach (var ob in Options)
{
CompileOptionLayout(layout, oi, ox, oy, bw, 20, ob);
if (++oi % OptionsCols == 0)
{
ox = x;
oy += 20;
}
else
{
ox += bw;
}
}
}
protected virtual void CompileOptionLayout(
SuperGumpLayout layout,
int index,
int x,
int y,
int w,
int h,
NotifyGumpOption option)
{
layout.Add(
"opts/" + index,
() =>
{
AddHtmlButton(
x,
y,
w,
h,
b =>
{
if (option.Callback != null)
{
option.Callback(b);
}
Refresh();
},
option.GetString(User),
option.LabelColor,
option.FillColor,
option.BorderColor,
1);
});
}
protected virtual void OnSettings(GumpButton b)
{
Refresh();
new NotifySettingsGump(User).Send();
}
protected override bool CanAutoRefresh()
{
return State == AnimState.Pause && Frame > 0 ? AutoClose && base.CanAutoRefresh() : base.CanAutoRefresh();
}
protected override void OnAutoRefresh()
{
base.OnAutoRefresh();
AnimateList();
switch (State)
{
case AnimState.Show:
{
if (Frame++ >= FrameCount)
{
AutoRefreshRate = PauseDuration;
State = AnimState.Pause;
Frame = FrameCount;
}
}
break;
case AnimState.Hide:
{
if (Frame-- <= 0)
{
AutoRefreshRate = TimeSpan.FromMilliseconds(100.0);
State = AnimState.Pause;
Frame = 0;
Close(true);
}
}
break;
case AnimState.Pause:
{
AutoRefreshRate = TimeSpan.FromMilliseconds(100.0);
State = Frame <= 0 ? AnimState.Show : AutoClose ? AnimState.Hide : AnimState.Pause;
}
break;
}
}
protected override bool OnBeforeSend()
{
if (!Initialized)
{
if (Notify.IsIgnored(GetType(), User))
{
return false;
}
if (!Notify.IsAnimated(GetType(), User))
{
AnimDuration = TimeSpan.Zero;
}
if (OnNotify != null)
{
OnNotify(this);
}
}
return base.OnBeforeSend();
}
public override void Close(bool all)
{
if (all)
{
base.Close(true);
}
else
{
AutoRefreshRate = TimeSpan.FromMilliseconds(100.0);
AutoClose = true;
}
}
private void AnimateList()
{
VitaNexCore.TryCatch(
() =>
{
var p = this;
foreach (var g in EnumerateInstances<NotifyGump>(User, true)
.Where(g => g != this && g.IsOpen && !g.IsDisposed && g.Y >= p.Y)
.OrderBy(g => g.Y))
{
g.Y = p.Y + p.FrameHeight;
p = g;
if (g.State != AnimState.Pause)
{
return;
}
var lr = g.LastAutoRefresh;
g.Refresh(true);
g.LastAutoRefresh = lr;
}
},
e => e.ToConsole());
}
private class Sub0 : NotifyGump
{
public Sub0(Mobile user, string html)
: base(user, html)
{ }
}
private class Sub1 : NotifyGump
{
public Sub1(Mobile user, string html)
: base(user, html)
{ }
}
private class Sub2 : NotifyGump
{
public Sub2(Mobile user, string html)
: base(user, html)
{ }
}
private class Sub3 : NotifyGump
{
public Sub3(Mobile user, string html)
: base(user, html)
{ }
}
private class Sub4 : NotifyGump
{
public Sub4(Mobile user, string html)
: base(user, html)
{ }
}
private class Sub5 : NotifyGump
{
public Sub5(Mobile user, string html)
: base(user, html)
{ }
}
private class Sub6 : NotifyGump
{
public Sub6(Mobile user, string html)
: base(user, html)
{ }
}
private class Sub7 : NotifyGump
{
public Sub7(Mobile user, string html)
: base(user, html)
{ }
}
private class Sub8 : NotifyGump
{
public Sub8(Mobile user, string html)
: base(user, html)
{ }
}
private class Sub9 : NotifyGump
{
public Sub9(Mobile user, string html)
: base(user, html)
{ }
}
}
}

View File

@@ -0,0 +1,81 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Drawing;
using Server;
using Server.Gumps;
using VitaNex.Text;
#endregion
namespace VitaNex.Notify
{
public class NotifyGumpOption
{
public char? Prefix { get; set; }
public TextDefinition Label { get; set; }
public Action<GumpButton> Callback { get; set; }
public Color LabelColor { get; set; }
public Color FillColor { get; set; }
public Color BorderColor { get; set; }
public int Width => 10 + UOFont.GetUnicodeWidth(1, GetString());
public NotifyGumpOption(TextDefinition label, Action<GumpButton> callback)
: this(label, callback, Color.Empty)
{ }
public NotifyGumpOption(TextDefinition label, Action<GumpButton> callback, Color color)
: this(label, callback, color, Color.Empty)
{ }
public NotifyGumpOption(TextDefinition label, Action<GumpButton> callback, Color color, Color fill)
: this(label, callback, color, fill, Color.Empty)
{ }
public NotifyGumpOption(TextDefinition label, Action<GumpButton> callback, Color color, Color fill, Color border)
{
Prefix = UniGlyph.TriRightFill;
Label = label;
Callback = callback;
LabelColor = color;
FillColor = fill;
BorderColor = border;
}
public override string ToString()
{
return GetString();
}
public string GetString()
{
return GetString(null);
}
public string GetString(Mobile viewer)
{
if (Prefix.HasValue)
{
return Prefix.Value + " " + Label.GetString(viewer);
}
return Label.GetString(viewer);
}
}
}

View File

@@ -0,0 +1,600 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Server;
using Server.Commands.Generic;
using Server.Gumps;
using VitaNex.SuperGumps;
#endregion
namespace VitaNex.Notify
{
public class NotifySettingsGump : SuperGumpList<NotifySettings>
{
public NotifySettingsGump(Mobile user)
: base(user)
{
EntriesPerPage = 5;
}
protected override void CompileList(List<NotifySettings> list)
{
list.Clear();
list.AddRange(Notify.Settings.Values.Where(o => User.AccessLevel >= o.Access).OrderByNatural());
base.CompileList(list);
}
protected override void CompileLayout(SuperGumpLayout layout)
{
base.CompileLayout(layout);
const int width = 400, height = 600;
var sup = SupportsUltimaStore;
var pad = sup ? 15 : 10;
var bgID = sup ? 40000 : 9270;
layout.Add("bg", () => AddBackground(0, 0, width, height, bgID));
layout.Add(
"title",
() =>
{
var title = "Notification Settings";
title = title.WrapUOHtmlBig();
title = title.WrapUOHtmlCenter();
title = title.WrapUOHtmlColor(Color.Gold, false);
AddHtml(pad, pad, width - (pad * 2), 40, title, false, false);
});
layout.Add(
"opts",
() =>
{
var xx = pad;
var yy = pad + 30;
var ww = width - (pad * 2);
var hh = height - ((pad * 2) + 60);
var r = GetListRange();
var o = AddAccordion(xx, yy, ww, hh, r.Values, CompileEntryLayout);
if (o == null || o.Item1 == null)
{
if (o != null)
{
CompileEmptyLayout(xx, yy + o.Item2, ww, hh - o.Item2);
}
else
{
CompileEmptyLayout(xx, yy, ww, hh);
}
}
});
layout.Add(
"pages",
() =>
{
var xx = pad;
var yy = (height - 25) - pad;
var ww = width - (pad * 2);
if (HasPrevPage)
{
if (sup)
{
AddButton(xx, yy, 40016, 40026, PreviousPage);
}
else
{
AddButton(xx, yy, 9766, 9767, PreviousPage);
}
}
xx += 35;
ww -= 70;
var page = String.Format("Page {0:#,0} / {1:#,0}", Page + 1, PageCount);
page = page.WrapUOHtmlCenter();
page = page.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(xx, yy + 2, ww, 40, page, false, false);
xx += ww;
if (HasNextPage)
{
if (sup)
{
AddButton(xx, yy, 40017, 40027, NextPage);
}
else
{
AddButton(xx, yy, 9762, 9763, NextPage);
}
}
});
}
protected virtual void CompileEntryLayout(int x, int y, int w, int h, NotifySettings o)
{
var sup = SupportsUltimaStore;
var pad = sup ? 15 : 10;
var off = sup ? 35 : 10;
var bgID = sup ? 40000 : 9270;
var btnNormal = sup ? 40017 : 9762;
var btnSelected = sup ? 40027 : 9763;
var chkNormal = sup ? 40014 : 9722;
var chkSelected = sup ? 40015 : 9723;
var s = o.EnsureState(User.Account);
string label;
if (User.AccessLevel >= Notify.Access)
{
AddButton(
x,
y,
btnNormal,
btnSelected,
b =>
{
Refresh();
if (User.AccessLevel >= Notify.Access)
{
User.SendGump(new PropertiesGump(User, o));
}
});
label = Notify.Access.ToString().ToUpper();
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + 35, y + 2, w - 35, 40, label, false, false);
y += 30;
h -= 30;
}
if (w * h <= 0)
{
return;
}
if (o.CanIgnore)
{
AddCheck(x, y, chkNormal, chkSelected, s.Ignore, (c, v) => s.Ignore = v);
label = "IGNORE";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
}
else
{
AddImage(x, y, chkNormal, 900);
label = "IGNORE";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.Gray, false);
}
AddHtml(x + 30, y + 2, (w / 2) - 30, 40, label, false, false);
if (o.CanAutoClose)
{
AddCheck(x + (w / 2), y, chkNormal, chkSelected, s.AutoClose, (c, v) => s.AutoClose = v);
label = "AUTO CLOSE";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
}
else
{
AddImage(x + (w / 2), y, chkNormal, 900);
label = "AUTO CLOSE";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.Gray, false);
}
AddHtml(x + (w / 2) + 30, y + 2, (w / 2) - 30, 40, label, false, false);
y += 30;
h -= 30;
if (w * h <= 0)
{
return;
}
AddCheck(x, y, chkNormal, chkSelected, s.Animate, (c, v) => s.Animate = v);
label = "ANIMATE";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + 30, y + 2, (w / 2) - 30, 40, label, false, false);
AddCheck(x + (w / 2), y, chkNormal, chkSelected, s.TextOnly, (c, v) => s.TextOnly = v);
label = "TEXT ONLY";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + (w / 2) + 30, y + 2, (w / 2) - 30, 40, label, false, false);
y += 30;
h -= 30;
if (w * h <= 0)
{
return;
}
var track = (w / 2) - 60;
AddScrollbarH(
x,
y + 4,
200,
s.Speed,
p =>
{
s.Speed = (byte)Math.Max(0, s.Speed - 10);
Refresh(true);
},
n =>
{
s.Speed = (byte)Math.Min(200, s.Speed + 10);
Refresh(true);
},
new Rectangle(30, 0, track, 13),
new Rectangle(0, 0, 28, 13),
new Rectangle(track + 32, 0, 28, 13));
label = String.Format("{0}% SPEED", Math.Max((byte)1, s.Speed));
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + track + 70, y + 2, w - (track + 60), 40, label, false, false);
y += 30;
h -= 30;
if (w * h <= 0)
{
return;
}
if (String.IsNullOrWhiteSpace(o.Desc))
{
return;
}
x -= pad;
w += pad * 2;
AddImage(x, y, bgID);
AddImageTiled(x + off, y, w - (off * 2), off, bgID + 1);
AddImage(x + off + (w - (off * 2)), y, bgID + 2);
x += pad;
w -= pad * 2;
y += pad;
h -= pad;
label = "Description";
label = label.WrapUOHtmlBig();
label = label.WrapUOHtmlCenter();
label = label.WrapUOHtmlColor(Color.Gold, false);
AddHtml(x, y, w, 40, label, false, false);
x += 5;
w -= 5;
y += 20;
h -= 10;
if (w * h <= 0)
{
return;
}
label = o.Desc;
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x, y, w, h, label, false, true);
}
protected virtual void CompileEmptyLayout(int x, int y, int w, int h)
{
var sup = SupportsUltimaStore;
var pad = sup ? 15 : 10;
var off = sup ? 35 : 10;
var bgID = sup ? 40000 : 9270;
var btnNormal = sup ? 40017 : 9762;
var btnSelected = sup ? 40027 : 9763;
var chkNormal = sup ? 40014 : 9722;
var chkSelected = sup ? 40015 : 9723;
x -= pad;
w += pad * 2;
AddImage(x, y, bgID);
AddImageTiled(x + off, y, w - (off * 2), off, bgID + 1);
AddImage(x + off + (w - (off * 2)), y, bgID + 2);
x += pad;
w -= pad * 2;
y += pad;
h -= pad;
var label = "All Notifications";
label = label.WrapUOHtmlBig();
label = label.WrapUOHtmlCenter();
label = label.WrapUOHtmlColor(Color.Gold, false);
AddHtml(x, y + 2, w, 40, label, false, false);
y += 30;
h -= 30;
if (User.AccessLevel >= Notify.Access)
{
AddButton(
x,
y,
btnNormal,
btnSelected,
b =>
{
Refresh();
if (User.AccessLevel >= Notify.Access)
{
var cols = new[] { "Object" };
var list = new ArrayList(List);
User.SendGump(new InterfaceGump(User, cols, list, 0, null));
}
});
label = Notify.Access.ToString().ToUpper();
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + 35, y + 2, w - 35, 40, label, false, false);
y += 30;
h -= 30;
}
if (w * h <= 0)
{
return;
}
var states = List.Select(o => o.EnsureState(User.Account));
var vals = new int[5, 2];
foreach (var o in states)
{
if (o.Settings.CanIgnore)
{
if (o.Ignore)
{
++vals[0, 0];
}
else
{
++vals[0, 1];
}
}
if (o.Settings.CanAutoClose)
{
if (o.AutoClose)
{
++vals[1, 0];
}
else
{
++vals[1, 1];
}
}
if (o.Animate)
{
++vals[2, 0];
}
else
{
++vals[2, 1];
}
if (o.TextOnly)
{
++vals[3, 0];
}
else
{
++vals[3, 1];
}
vals[4, 0] += o.Speed;
++vals[4, 1];
}
var ignore = vals[0, 0] >= vals[0, 1];
AddButton(
x,
y,
ignore ? chkSelected : chkNormal,
ignore ? chkNormal : chkSelected,
b =>
{
foreach (var s in List.Where(o => o.CanIgnore).Select(o => o.EnsureState(User.Account)))
{
s.Ignore = !ignore;
}
Refresh(true);
});
label = "IGNORE";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + 30, y + 2, (w / 2) - 30, 40, label, false, false);
var autoClose = vals[1, 0] >= vals[1, 1];
AddButton(
x + (w / 2),
y,
autoClose ? chkSelected : chkNormal,
autoClose ? chkNormal : chkSelected,
b =>
{
foreach (var s in List.Where(o => o.CanAutoClose).Select(o => o.EnsureState(User.Account)))
{
s.AutoClose = !autoClose;
}
Refresh(true);
});
label = "AUTO CLOSE";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + (w / 2) + 30, y + 2, (w / 2) - 30, 40, label, false, false);
y += 30;
h -= 30;
if (w * h <= 0)
{
return;
}
var animate = vals[2, 0] >= vals[2, 1];
AddButton(
x,
y,
animate ? chkSelected : chkNormal,
animate ? chkNormal : chkSelected,
b =>
{
foreach (var s in List.Select(o => o.EnsureState(User.Account)))
{
s.Animate = !animate;
}
Refresh(true);
});
label = "ANIMATE";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + 30, y + 2, (w / 2) - 30, 40, label, false, false);
var textOnly = vals[3, 0] >= vals[4, 1];
AddButton(
x + (w / 2),
y,
textOnly ? chkSelected : chkNormal,
textOnly ? chkNormal : chkSelected,
b =>
{
foreach (var s in List.Select(o => o.EnsureState(User.Account)))
{
s.TextOnly = !textOnly;
}
Refresh(true);
});
label = "TEXT ONLY";
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + (w / 2) + 30, y + 2, (w / 2) - 30, 40, label, false, false);
y += 30;
h -= 30;
if (w * h <= 0)
{
return;
}
var track = (w / 2) - 60;
var speed = vals[4, 0] / Math.Max(1, vals[4, 1]);
AddScrollbarH(
x,
y + 4,
200,
speed,
p =>
{
foreach (var s in List.Where(o => o.CanIgnore).Select(o => o.EnsureState(User.Account)))
{
s.Speed = (byte)Math.Max(0, speed - 10);
}
Refresh(true);
},
n =>
{
foreach (var s in List.Where(o => o.CanIgnore).Select(o => o.EnsureState(User.Account)))
{
s.Speed = (byte)Math.Min(200, speed + 10);
}
Refresh(true);
},
new Rectangle(30, 0, track, 13),
new Rectangle(0, 0, 28, 13),
new Rectangle(track + 32, 0, 28, 13));
label = String.Format("{0}% SPEED", Math.Max(1, speed));
label = label.WrapUOHtmlBold();
label = label.WrapUOHtmlColor(Color.PaleGoldenrod, false);
AddHtml(x + track + 70, y + 2, w - (track + 60), 40, label, false, false);
}
}
}

View File

@@ -0,0 +1,153 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Drawing;
using Server;
using Server.Mobiles;
#endregion
namespace VitaNex.Notify
{
public abstract class WorldNotifyGump : NotifyGump
{
private static void InitSettings(NotifySettings o)
{
o.CanIgnore = true;
o.Desc = "General broadcasts from the staff and server.";
}
public WorldNotifyGump(Mobile user, string html)
: this(user, html, false)
{ }
public WorldNotifyGump(Mobile user, string html, bool autoClose)
: base(user, html)
{
AnimDuration = TimeSpan.FromSeconds(1.0);
PauseDuration = TimeSpan.FromSeconds(10.0);
HtmlColor = Color.Yellow;
AutoClose = autoClose;
}
private class Sub0 : WorldNotifyGump
{
public Sub0(Mobile user, string html)
: base(user, html)
{ }
public Sub0(Mobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
private class Sub1 : WorldNotifyGump
{
public Sub1(Mobile user, string html)
: base(user, html)
{ }
public Sub1(Mobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
private class Sub2 : WorldNotifyGump
{
public Sub2(Mobile user, string html)
: base(user, html)
{ }
public Sub2(Mobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
private class Sub3 : WorldNotifyGump
{
public Sub3(Mobile user, string html)
: base(user, html)
{ }
public Sub3(PlayerMobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
private class Sub4 : WorldNotifyGump
{
public Sub4(Mobile user, string html)
: base(user, html)
{ }
public Sub4(Mobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
private class Sub5 : WorldNotifyGump
{
public Sub5(Mobile user, string html)
: base(user, html)
{ }
public Sub5(Mobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
private class Sub6 : WorldNotifyGump
{
public Sub6(Mobile user, string html)
: base(user, html)
{ }
public Sub6(Mobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
private class Sub7 : WorldNotifyGump
{
public Sub7(Mobile user, string html)
: base(user, html)
{ }
public Sub7(Mobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
private class Sub8 : WorldNotifyGump
{
public Sub8(Mobile user, string html)
: base(user, html)
{ }
public Sub8(Mobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
private class Sub9 : WorldNotifyGump
{
public Sub9(Mobile user, string html)
: base(user, html)
{ }
public Sub9(Mobile user, string html, bool autoClose)
: base(user, html, autoClose)
{ }
}
}
}

View File

@@ -0,0 +1,270 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System.Collections.Generic;
using System.Linq;
using Server;
using Server.Misc;
#endregion
namespace VitaNex
{
public delegate T NotorietyHandler<out T>(Mobile x, Mobile y, out bool handled);
public class NotorietyEntry<T>
{
public int Priority { get; set; }
public NotorietyHandler<T> Handler { get; set; }
public NotorietyEntry(int priority, NotorietyHandler<T> handler)
{
Priority = priority;
Handler = handler;
}
}
[CoreService("Notoriety", "1.0.0.0", TaskPriority.High)]
public static class NotoUtility
{
public const int Bubble = -1;
private static NotorietyHandler _NotorietyParent;
private static AllowBeneficialHandler _BeneficialParent;
private static AllowHarmfulHandler _HarmfulParent;
public static NotorietyHandler NotorietyParent => _NotorietyParent ?? (_NotorietyParent = Notoriety.Handler);
public static AllowBeneficialHandler BeneficialParent => _BeneficialParent ?? (_BeneficialParent = NotorietyHandlers.Mobile_AllowBeneficial);
public static AllowHarmfulHandler HarmfulParent => _HarmfulParent ?? (_HarmfulParent = NotorietyHandlers.Mobile_AllowHarmful);
private static readonly List<NotorietyEntry<int>> _NameHandlers = new List<NotorietyEntry<int>>();
private static readonly List<NotorietyEntry<bool>> _BeneficialHandlers = new List<NotorietyEntry<bool>>();
private static readonly List<NotorietyEntry<bool>> _HarmfulHandlers = new List<NotorietyEntry<bool>>();
public static List<NotorietyEntry<int>> NameHandlers => _NameHandlers;
public static List<NotorietyEntry<bool>> BeneficialHandlers => _BeneficialHandlers;
public static List<NotorietyEntry<bool>> HarmfulHandlers => _HarmfulHandlers;
private static void CSInvoke()
{
if (_NotorietyParent == null && Notoriety.Handler != MobileNotoriety)
{
_NotorietyParent = Notoriety.Handler ?? NotorietyHandlers.MobileNotoriety;
}
if (_BeneficialParent == null && Mobile.AllowBeneficialHandler != AllowBeneficial)
{
_BeneficialParent = Mobile.AllowBeneficialHandler ?? NotorietyHandlers.Mobile_AllowBeneficial;
}
if (_HarmfulParent == null && Mobile.AllowHarmfulHandler != AllowHarmful)
{
_HarmfulParent = Mobile.AllowHarmfulHandler ?? NotorietyHandlers.Mobile_AllowHarmful;
}
Notoriety.Handler = MobileNotoriety;
Mobile.AllowBeneficialHandler = AllowBeneficial;
Mobile.AllowHarmfulHandler = AllowHarmful;
}
public static void CSDispose()
{
Notoriety.Handler = _NotorietyParent ?? NotorietyHandlers.MobileNotoriety;
Mobile.AllowBeneficialHandler = _BeneficialParent ?? NotorietyHandlers.Mobile_AllowBeneficial;
Mobile.AllowHarmfulHandler = _HarmfulParent ?? NotorietyHandlers.Mobile_AllowHarmful;
_NotorietyParent = null;
_BeneficialParent = null;
_HarmfulParent = null;
}
public static void RegisterNameHandler(NotorietyHandler<int> handler, int priority = 0)
{
UnregisterNameHandler(handler);
_NameHandlers.Add(new NotorietyEntry<int>(priority, handler));
}
public static void UnregisterNameHandler(NotorietyHandler<int> handler)
{
_NameHandlers.RemoveAll(e => e.Handler == handler);
}
public static void RegisterBeneficialHandler(NotorietyHandler<bool> handler, int priority = 0)
{
UnregisterBeneficialHandler(handler);
_BeneficialHandlers.Add(new NotorietyEntry<bool>(priority, handler));
}
public static void UnregisterBeneficialHandler(NotorietyHandler<bool> handler)
{
_BeneficialHandlers.RemoveAll(e => e.Handler == handler);
}
public static void RegisterHarmfulHandler(NotorietyHandler<bool> handler, int priority = 0)
{
UnregisterHarmfulHandler(handler);
_HarmfulHandlers.Add(new NotorietyEntry<bool>(priority, handler));
}
public static void UnregisterHarmfulHandler(NotorietyHandler<bool> handler)
{
_HarmfulHandlers.RemoveAll(e => e.Handler == handler);
}
public static bool AllowBeneficial(Mobile a, Mobile b)
{
if (_BeneficialParent == null)
{
_BeneficialParent = NotorietyHandlers.Mobile_AllowBeneficial;
}
if (a == null || a.Deleted || b == null || b.Deleted)
{
return _BeneficialParent(a, b);
}
foreach (var handler in _BeneficialHandlers.Where(e => e.Handler != null)
.OrderByDescending(e => e.Priority)
.Select(e => e.Handler))
{
var result = handler(a, b, out var handled);
if (handled)
{
return result;
}
}
return _BeneficialParent(a, b);
}
#if ServUO
public static bool AllowHarmful(Mobile a, IDamageable b)
{
if (b is Mobile)
{
return AllowHarmful(a, (Mobile)b);
}
if (_HarmfulParent == null)
{
_HarmfulParent = NotorietyHandlers.Mobile_AllowHarmful;
}
return _HarmfulParent(a, b);
}
#endif
public static bool AllowHarmful(Mobile a, Mobile b)
{
if (_HarmfulParent == null)
{
_HarmfulParent = NotorietyHandlers.Mobile_AllowHarmful;
}
if (a == null || a.Deleted || b == null || b.Deleted)
{
return _HarmfulParent(a, b);
}
foreach (var handler in _HarmfulHandlers.Where(e => e.Handler != null)
.OrderByDescending(e => e.Priority)
.Select(e => e.Handler))
{
var result = handler(a, b, out var handled);
if (handled)
{
return result;
}
}
return _HarmfulParent(a, b);
}
#if ServUO
public static int MobileNotoriety(Mobile a, IDamageable b)
{
if (b is Mobile)
{
return MobileNotoriety(a, (Mobile)b);
}
if (_NotorietyParent == null)
{
_NotorietyParent = NotorietyHandlers.MobileNotoriety;
}
return _NotorietyParent(a, b);
}
#endif
public static int MobileNotoriety(Mobile a, Mobile b)
{
if (_NotorietyParent == null)
{
_NotorietyParent = NotorietyHandlers.MobileNotoriety;
}
if (a == null || a.Deleted || b == null || b.Deleted)
{
return _NotorietyParent(a, b);
}
foreach (var handler in _NameHandlers.Where(e => e.Handler != null)
.OrderByDescending(e => e.Priority)
.Select(e => e.Handler))
{
var result = handler(a, b, out var handled);
if (handled)
{
if (result <= Bubble)
{
break;
}
return result;
}
}
return _NotorietyParent(a, b);
}
public static bool Resolve<T1, T2>(Mobile a, Mobile b, out T1 x, out T2 y)
where T1 : Mobile
where T2 : Mobile
{
x = null;
y = null;
if (a == null || a.Deleted || b == null || b.Deleted)
{
return false;
}
if (!a.IsControlled(out x))
{
x = a as T1;
}
if (!b.IsControlled(out y))
{
y = b as T2;
}
return x != null && y != null;
}
}
}

View File

@@ -0,0 +1,130 @@
#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 Server;
#endregion
namespace VitaNex
{
public class PlayerNamesOptions : CoreServiceOptions
{
private bool _IgnoreCase;
[CommandProperty(PlayerNames.Access)]
public virtual bool IgnoreCase
{
get => _IgnoreCase;
set
{
if (_IgnoreCase && !value)
{
PlayerNames.Registry.Comparer.Impl = EqualityComparer<string>.Default;
}
else if (!_IgnoreCase && value)
{
PlayerNames.Registry.Comparer.Impl = StringComparer.OrdinalIgnoreCase;
}
else
{
return;
}
_IgnoreCase = value;
if (PlayerNames.Registry.Count > 0)
{
PlayerNames.Clear();
PlayerNames.Index();
}
}
}
[CommandProperty(PlayerNames.Access)]
public virtual bool IndexOnStart { get; set; }
[CommandProperty(PlayerNames.Access)]
public virtual bool NameSharing { get; set; }
public PlayerNamesOptions()
: base(typeof(PlayerNames))
{
IndexOnStart = false;
NameSharing = true;
IgnoreCase = false;
}
public PlayerNamesOptions(GenericReader reader)
: base(reader)
{ }
public override void Clear()
{
base.Clear();
IndexOnStart = false;
NameSharing = true;
IgnoreCase = false;
}
public override void Reset()
{
base.Reset();
IndexOnStart = false;
NameSharing = true;
IgnoreCase = false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
var version = writer.SetVersion(2);
switch (version)
{
case 2:
writer.Write(IgnoreCase);
goto case 1;
case 1:
writer.Write(NameSharing);
goto case 0;
case 0:
writer.Write(IndexOnStart);
break;
}
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
var version = reader.GetVersion();
switch (version)
{
case 2:
IgnoreCase = reader.ReadBool();
goto case 1;
case 1:
NameSharing = reader.ReadBool();
goto case 0;
case 0:
IndexOnStart = reader.ReadBool();
break;
}
}
}
}

View File

@@ -0,0 +1,147 @@
#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.Linq;
using Server;
using Server.Mobiles;
using VitaNex.IO;
#endregion
namespace VitaNex
{
public static partial class PlayerNames
{
public const AccessLevel Access = AccessLevel.Administrator;
public static PlayerNamesOptions CSOptions { get; private set; }
public static BinaryDataStore<string, List<PlayerMobile>> Registry { get; private set; }
public static event Action<PlayerMobile> OnRegistered;
private static void InvokeRegistered(PlayerMobile pm)
{
if (OnRegistered != null)
{
OnRegistered(pm);
}
}
public static bool IsRegistered(PlayerMobile pm)
{
return pm != null && !String.IsNullOrWhiteSpace(FindRegisteredName(pm));
}
public static void Register(PlayerMobile pm)
{
if (pm == null || pm.Deleted)
{
return;
}
var rName = FindRegisteredName(pm);
if (!String.IsNullOrWhiteSpace(rName) && rName != pm.RawName)
{
if (String.IsNullOrWhiteSpace(pm.RawName))
{
pm.RawName = rName;
return;
}
Registry[rName].Remove(pm);
Registry[rName].TrimExcess();
if (Registry[rName].Count == 0)
{
Registry.Remove(rName);
}
}
if (!Registry.ContainsKey(pm.RawName))
{
Registry.Add(
pm.RawName,
new List<PlayerMobile>
{
pm
});
InvokeRegistered(pm);
}
else if (!Registry[pm.RawName].Contains(pm))
{
Registry[pm.RawName].Add(pm);
InvokeRegistered(pm);
}
}
public static void ValidateSharedName(PlayerMobile m)
{
if (m != null && !CSOptions.NameSharing &&
FindPlayers(m.RawName, p => p != m && p.CreationTime < m.CreationTime).Any())
{
new ForcePlayerRenameDialog(m).Send();
}
}
public static bool HasNameChanged(PlayerMobile pm)
{
return pm != null && !pm.Deleted && pm.RawName != FindRegisteredName(pm);
}
public static string FindRegisteredName(PlayerMobile pm)
{
if (pm == null || pm.Deleted)
{
return String.Empty;
}
var val = Registry.Values.IndexOf(list => list.Contains(pm));
if (Registry.InBounds(val))
{
return Registry.GetKeyAt(val);
}
return String.Empty;
}
public static IEnumerable<PlayerMobile> FindPlayers(string name)
{
return FindPlayers(name, null);
}
public static IEnumerable<PlayerMobile> FindPlayers(string name, Func<PlayerMobile, bool> match)
{
if (String.IsNullOrWhiteSpace(name))
{
yield break;
}
var players = Registry.GetValue(name);
if (players == null || players.Count == 0)
{
yield break;
}
foreach (var m in (match == null ? players : players.Where(match)))
{
yield return m;
}
}
}
}

View File

@@ -0,0 +1,143 @@
#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.Linq;
using Server;
using Server.Gumps;
using Server.Mobiles;
using VitaNex.IO;
#endregion
namespace VitaNex
{
[CoreService("Player Name Register", "1.0.0.0", TaskPriority.Low)]
public static partial class PlayerNames
{
static PlayerNames()
{
CSOptions = new PlayerNamesOptions();
Registry = new BinaryDataStore<string, List<PlayerMobile>>(VitaNexCore.SavesDirectory + "/PlayerNames", "Registry")
{
OnSerialize = Serialize,
OnDeserialize = Deserialize,
Async = true
};
}
private static void CSConfig()
{
EventSink.CharacterCreated += e => Register(e.Mobile as PlayerMobile);
EventSink.Login += e =>
{
Register(e.Mobile as PlayerMobile);
ValidateSharedName(e.Mobile as PlayerMobile);
};
EventSink.Logout += e => Register(e.Mobile as PlayerMobile);
EventSink.PlayerDeath += e => Register(e.Mobile as PlayerMobile);
CommandUtility.Register(
"PlayerNames",
Access,
e =>
{
if (e.Arguments != null && e.Arguments.Length > 0 && Insensitive.Equals(e.Arguments[0], "index"))
{
e.Mobile.SendMessage("Indexing player names, please wait...");
Index();
e.Mobile.SendMessage(
"Player name indexing complete, there are {0:#,0} registered names by {1:#,0} players.",
Registry.Count,
Registry.Values.Aggregate(0, (c, list) => c + list.Count));
}
else
{
e.Mobile.SendGump(new PropertiesGump(e.Mobile, CSOptions));
}
});
}
public static void Clear()
{
Registry.Clear();
}
public static void Index()
{
CSOptions.ToConsole("Indexing...");
World.Broadcast(0x55, true, "Indexing player names...");
World.Mobiles.Values.AsParallel().OfType<PlayerMobile>().ForEach(Register);
World.Broadcast(0x55, true, "Indexing complete.");
CSOptions.ToConsole("Indexing complete.");
CSOptions.ToConsole(
"{0:#,0} registered names by {1:#,0} players.",
Registry.Count,
Registry.Values.Aggregate(0, (c, list) => c + list.Count));
}
private static void CSInvoke()
{
if (CSOptions.IndexOnStart)
{
Index();
}
}
private static void CSSave()
{
Registry.Export();
}
private static void CSLoad()
{
Registry.Import();
}
private static bool Serialize(GenericWriter writer)
{
writer.WriteBlockDictionary(
Registry,
(w, name, players) =>
{
w.Write(name);
w.WriteMobileList(players, true);
});
return true;
}
private static bool Deserialize(GenericReader reader)
{
reader.ReadBlockDictionary(
r =>
{
var name = r.ReadString();
var players = r.ReadStrongMobileList<PlayerMobile>();
return new KeyValuePair<string, List<PlayerMobile>>(name, players);
},
Registry);
return true;
}
}
}

View File

@@ -0,0 +1,96 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Drawing;
using System.Linq;
using Server;
using Server.Gumps;
using Server.Misc;
using Server.Mobiles;
using VitaNex.SuperGumps;
using VitaNex.SuperGumps.UI;
#endregion
namespace VitaNex
{
public class ForcePlayerRenameDialog : InputDialogGump
{
public ForcePlayerRenameDialog(Mobile user, string input = null)
: base(user, input: input, limit: 16)
{
CanClose = false;
CanDispose = false;
BlockMovement = true;
BlockSpeech = true;
Html = "It appears that another character is already using the name \"" + //
InputText.WrapUOHtmlColor(Color.LawnGreen, HtmlColor) + "\"!\n\n" + //
"Please enter a new name for your character...";
}
protected override void CompileLayout(SuperGumpLayout layout)
{
base.CompileLayout(layout);
layout.Remove("button/body/cancel");
}
protected override void OnAccept(GumpButton button)
{
if (String.IsNullOrWhiteSpace(InputText) || !NameVerification.Validate(
InputText,
2,
20,
true,
false,
true,
1,
NameVerification.SpaceDashPeriodQuote))
{
Html = ("The name \"" + InputText + "\" is invalid.\n\n").WrapUOHtmlColor(Color.OrangeRed, HtmlColor) +
"It appears that another character is already using the name \"" + //
User.RawName.WrapUOHtmlColor(Color.LawnGreen, HtmlColor) + "\"!\n\n" + //
"Please enter a new name for your character...";
InputText = NameList.RandomName(User.Female ? "female" : "male");
Refresh(true);
return;
}
if (InputText == User.RawName || PlayerNames.FindPlayers(
InputText,
p => p != User && p.GameTime > ((PlayerMobile)User).GameTime)
.Any())
{
Html = "It appears that another character is already using the name \"" + //
InputText.WrapUOHtmlColor(Color.LawnGreen, HtmlColor) + "\"!\n\n" + //
"Please enter a new name for your character...";
InputText = NameList.RandomName(User.Female ? "female" : "male");
Refresh(true);
return;
}
User.RawName = InputText;
PlayerNames.Register((PlayerMobile)User);
base.OnAccept(button);
}
}
}

View File

@@ -0,0 +1,82 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System.Collections.Generic;
using System.IO;
using Server;
using VitaNex.IO;
#endregion
namespace VitaNex.Schedules
{
public class ScheduleDataStore : BinaryDataStore<string, Schedule>
{
public ScheduleDataStore(string root, string doc)
: base(root, doc)
{ }
public ScheduleDataStore(DirectoryInfo root, string doc)
: base(root, doc)
{ }
protected override void Serialize(GenericWriter writer)
{
var version = writer.SetVersion(0);
switch (version)
{
case 0:
{
writer.WriteDictionary(
this,
(k, v) =>
{
writer.Write(k);
writer.WriteType(
v,
t =>
{
if (t != null)
{
v.Serialize(writer);
}
});
});
}
break;
}
}
protected override void Deserialize(GenericReader reader)
{
var version = reader.ReadInt();
switch (version)
{
case 0:
{
reader.ReadDictionary(
() =>
{
var key = reader.ReadString();
var val = reader.ReadTypeCreate<Schedule>(reader);
return new KeyValuePair<string, Schedule>(key, val);
},
this);
}
break;
}
}
}
}

View File

@@ -0,0 +1,32 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.Schedules
{
[Flags]
public enum ScheduleDays : short
{
None = 0x000,
Sunday = 0x001,
Monday = 0x002,
Tuesday = 0x004,
Wednesday = 0x008,
Thursday = 0x010,
Friday = 0x020,
Saturday = 0x040,
All = Int16.MaxValue
}
}

View File

@@ -0,0 +1,267 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Linq;
using Server;
#endregion
namespace VitaNex.Schedules
{
[PropertyObject]
public class ScheduleInfo : ICloneable
{
private static readonly TimeSpan _OneDay = TimeSpan.FromDays(1.0);
[CommandProperty(Schedules.Access)]
public ScheduleMonths Months { get; set; }
[CommandProperty(Schedules.Access)]
public ScheduleDays Days { get; set; }
[CommandProperty(Schedules.Access)]
public ScheduleTimes Times { get; set; }
[CommandProperty(Schedules.Access)]
public bool Local { get; set; }
public ScheduleInfo(
ScheduleMonths months = ScheduleMonths.All,
ScheduleDays days = ScheduleDays.All,
ScheduleTimes times = null)
{
Months = months;
Days = days;
Times = times ?? new ScheduleTimes();
}
public ScheduleInfo(GenericReader reader)
{
Deserialize(reader);
}
object ICloneable.Clone()
{
return Clone();
}
public virtual ScheduleInfo Clone()
{
return new ScheduleInfo(Months, Days, Times.Clone());
}
public override string ToString()
{
return "Schedule Info";
}
public virtual void Clear()
{
Months = ScheduleMonths.None;
Days = ScheduleDays.None;
Times.Clear();
}
public virtual bool HasMonth(ScheduleMonths month)
{
return Months.HasFlag(month);
}
public virtual bool HasDay(ScheduleDays day)
{
return Days.HasFlag(day);
}
public virtual bool HasTime(TimeSpan time)
{
Validate(ref time);
return Times.Contains(time);
}
public virtual void Validate(ref TimeSpan time)
{
time = new TimeSpan(0, time.Hours, time.Minutes, 0, 0);
}
public virtual void Validate(ref DateTime dt, out TimeSpan time)
{
Validate(ref dt);
time = new TimeSpan(0, dt.TimeOfDay.Hours, dt.TimeOfDay.Minutes, 0, 0);
}
public virtual void Validate(ref DateTime dt)
{
if (!Local && !dt.Kind.HasFlag(DateTimeKind.Utc))
{
dt = dt.ToUniversalTime();
}
else if (Local && !dt.Kind.HasFlag(DateTimeKind.Local))
{
dt = dt.ToLocalTime();
}
dt = new DateTime(dt.Year, dt.Month, dt.Day, dt.TimeOfDay.Hours, dt.TimeOfDay.Minutes, 0, 0, dt.Kind);
}
public virtual DateTime? FindBefore(DateTime dt)
{
if (Months == ScheduleMonths.None || Days == ScheduleDays.None || Times.Count == 0)
{
return null;
}
Validate(ref dt, out var ts);
try
{
var past = false;
for (var year = dt.Year; year >= dt.Year - 1; year--)
{
for (var month = past ? 12 : dt.Month; month >= 1; month--)
{
if (!HasMonth(Schedules.ConvertMonth(month)))
{
past = true;
continue;
}
var start = new DateTime(year, month, past ? DateTime.DaysInMonth(year, month) : dt.Day, 0, 0, 0, dt.Kind);
var end = new DateTime(year, month, 1, 0, 0, 0, dt.Kind);
for (var date = start; date >= end; date -= _OneDay)
{
if (!HasDay(Schedules.ConvertDay(date.DayOfWeek)))
{
past = true;
continue;
}
foreach (var time in Times.Where(t => past || t < ts).OrderByDescending(t => t.Ticks))
{
return new DateTime(year, month, date.Day, time.Hours, time.Minutes, 0, dt.Kind);
}
past = true;
}
}
past = true;
}
}
catch (Exception e)
{
VitaNexCore.ToConsole(e);
}
return null;
}
public virtual DateTime? FindAfter(DateTime dt)
{
if (Months == ScheduleMonths.None || Days == ScheduleDays.None || Times.Count == 0)
{
return null;
}
Validate(ref dt, out var ts);
try
{
var future = false;
for (var year = dt.Year; year <= dt.Year + 1; year++)
{
for (var month = future ? 1 : dt.Month; month <= 12; month++)
{
if (!HasMonth(Schedules.ConvertMonth(month)))
{
future = true;
continue;
}
var start = new DateTime(year, month, future ? 1 : dt.Day, 0, 0, 0, dt.Kind);
var end = new DateTime(year, month, DateTime.DaysInMonth(year, month), 0, 0, 0, dt.Kind);
for (var date = start; date <= end; date += _OneDay)
{
if (!HasDay(Schedules.ConvertDay(date.DayOfWeek)))
{
future = true;
continue;
}
foreach (var time in Times.Where(t => future || t > ts).OrderBy(t => t.Ticks))
{
return new DateTime(year, month, date.Day, time.Hours, time.Minutes, 0, dt.Kind);
}
future = true;
}
}
future = true;
}
}
catch (Exception e)
{
VitaNexCore.ToConsole(e);
}
return null;
}
public virtual void Serialize(GenericWriter writer)
{
var version = writer.SetVersion(1);
switch (version)
{
case 1:
writer.Write(Local);
goto case 0;
case 0:
{
writer.WriteFlag(Months);
writer.WriteFlag(Days);
writer.WriteType(Times, t => Times.Serialize(writer));
}
break;
}
}
public virtual void Deserialize(GenericReader reader)
{
var version = reader.ReadInt();
switch (version)
{
case 1:
Local = reader.ReadBool();
goto case 0;
case 0:
{
Months = reader.ReadFlag<ScheduleMonths>();
Days = reader.ReadFlag<ScheduleDays>();
Times = reader.ReadTypeCreate<ScheduleTimes>(reader) ?? new ScheduleTimes();
}
break;
}
}
}
}

View File

@@ -0,0 +1,37 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.Schedules
{
[Flags]
public enum ScheduleMonths : short
{
None = 0x000,
January = 0x001,
February = 0x002,
March = 0x004,
April = 0x008,
May = 0x010,
June = 0x020,
July = 0x040,
August = 0x080,
September = 0x100,
October = 0x200,
November = 0x400,
December = 0x800,
All = ~None
}
}

View File

@@ -0,0 +1,500 @@
#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.Drawing;
using System.Text;
using Server;
using VitaNex.Crypto;
using VitaNex.SuperGumps;
#endregion
namespace VitaNex.Schedules
{
[PropertyObject]
public class Schedule : Timer, ICloneable
{
public static List<Schedule> Instances { get; private set; }
static Schedule()
{
Instances = new List<Schedule>(0x20);
}
private ScheduleInfo _Info;
private TimerPriority _DefaultPriority;
private bool _Enabled;
private string _Name;
private DateTime? _LastGlobalTick;
private DateTime? _CurrentGlobalTick;
private DateTime? _NextGlobalTick;
[CommandProperty(Schedules.Access, true)]
public CryptoHashCode UID { get; private set; }
[CommandProperty(Schedules.Access)]
public virtual string Name { get => _Name ?? (_Name = String.Empty); set => _Name = value ?? String.Empty; }
[CommandProperty(Schedules.Access)]
public virtual bool Enabled
{
get => _Enabled;
set
{
if (!_Enabled && value)
{
_Enabled = true;
InvalidateNextTick();
Priority = ComputePriority(Interval = TimeSpan.FromSeconds(1.0));
if (OnEnabled != null)
{
OnEnabled(this);
}
}
else if (_Enabled && !value)
{
_Enabled = false;
InvalidateNextTick();
Priority = ComputePriority(Interval = TimeSpan.FromMinutes(1.0));
if (OnDisabled != null)
{
OnDisabled(this);
}
}
}
}
[CommandProperty(Schedules.Access)]
public virtual ScheduleInfo Info
{
get => _Info ?? (_Info = new ScheduleInfo());
set
{
_Info = value ?? new ScheduleInfo();
InvalidateNextTick();
}
}
[CommandProperty(Schedules.Access)]
public virtual DateTime? LastGlobalTick => _LastGlobalTick;
[CommandProperty(Schedules.Access)]
public virtual DateTime? CurrentGlobalTick => _CurrentGlobalTick;
[CommandProperty(Schedules.Access)]
public virtual DateTime? NextGlobalTick => _NextGlobalTick;
[CommandProperty(Schedules.Access)]
public TimeSpan WaitGlobalTick
{
get
{
if (_NextGlobalTick != null)
{
return TimeSpan.FromTicks(Math.Max(0, (_NextGlobalTick.Value - Now).Ticks));
}
return TimeSpan.Zero;
}
}
[CommandProperty(Schedules.Access)]
public bool IsRegistered => Schedules.IsRegistered(this);
[CommandProperty(Schedules.Access)]
public bool IsLocal
{
get => _Info.Local;
set
{
_Info.Local = value;
InvalidateNextTick();
}
}
public DateTime Now => IsLocal ? DateTime.Now : DateTime.UtcNow;
public event Action<Schedule> OnGlobalTick;
public event Action<Schedule> OnEnabled;
public event Action<Schedule> OnDisabled;
public Schedule(
string name,
bool enabled,
ScheduleMonths months = ScheduleMonths.None,
ScheduleDays days = ScheduleDays.None,
ScheduleTimes times = null,
params Action<Schedule>[] onTick)
: this(name, enabled, new ScheduleInfo(months, days, times), onTick)
{ }
public Schedule(string name, bool enabled, ScheduleInfo info, params Action<Schedule>[] onTick)
: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
{
UID = new CryptoHashCode(CryptoHashType.MD5, TimeStamp.Now + "+" + Utility.RandomDouble());
_Enabled = enabled;
_Name = name ?? String.Empty;
_Info = info ?? new ScheduleInfo();
Instances.Add(this);
UpdateTicks(Now);
if (onTick != null)
{
foreach (var a in onTick)
{
OnGlobalTick += a;
}
}
Start();
}
public Schedule(GenericReader reader)
: base(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0))
{
Instances.Add(this);
Deserialize(reader);
}
~Schedule()
{
Free();
}
object ICloneable.Clone()
{
return Clone();
}
public virtual Schedule Clone()
{
return new Schedule(Name, Enabled, Info.Clone());
}
public void Free()
{
_LastGlobalTick = null;
_CurrentGlobalTick = null;
_NextGlobalTick = null;
OnGlobalTick = null;
OnEnabled = null;
OnDisabled = null;
Instances.Remove(this);
}
public void Register()
{
if (!IsRegistered)
{
Schedules.Register(this);
}
}
public void Unregister()
{
if (IsRegistered)
{
Schedules.Unregister(this);
}
}
private void UpdateTicks(DateTime dt)
{
_LastGlobalTick = dt;
InvalidateNextTick(dt);
}
public void InvalidateNextTick()
{
InvalidateNextTick(Now);
}
public void InvalidateNextTick(DateTime dt)
{
if (!_Enabled)
{
_NextGlobalTick = null;
return;
}
_NextGlobalTick = _Info.FindAfter(dt);
if (_NextGlobalTick != null && _NextGlobalTick < Now)
{
InvalidateNextTick(_NextGlobalTick.Value);
}
}
protected override void OnTick()
{
base.OnTick();
if (!_Enabled)
{
_LastGlobalTick = null;
_CurrentGlobalTick = null;
_NextGlobalTick = null;
return;
}
var now = Now;
if (_NextGlobalTick == null)
{
InvalidateNextTick(now);
}
if (_NextGlobalTick == null || now < _NextGlobalTick)
{
return;
}
_CurrentGlobalTick = now;
InvalidateNextTick(now);
if (OnGlobalTick != null)
{
OnGlobalTick(this);
}
_LastGlobalTick = now;
_CurrentGlobalTick = null;
}
public virtual void Serialize(GenericWriter writer)
{
var version = writer.SetVersion(3);
if (version > 2)
{
UID.Serialize(writer);
}
switch (version)
{
case 3:
case 2:
case 1:
case 0:
{
if (version < 2)
{
writer.WriteType(_Info, t => _Info.Serialize(writer));
}
else
{
writer.WriteBlock(w => w.WriteType(_Info, t => _Info.Serialize(w)));
}
writer.Write(_Enabled);
writer.Write(_Name);
writer.WriteFlag(_DefaultPriority);
if (_LastGlobalTick != null)
{
writer.Write(true);
writer.Write(_LastGlobalTick.Value);
}
else
{
writer.Write(false);
}
if (_NextGlobalTick != null)
{
writer.Write(true);
writer.Write(_NextGlobalTick.Value);
}
else
{
writer.Write(false);
}
writer.Write(Delay);
writer.Write(Interval);
}
break;
}
if (version > 0)
{
writer.Write(Running);
}
}
public virtual void Deserialize(GenericReader reader)
{
var version = reader.GetVersion();
if (version > 2)
{
UID = new CryptoHashCode(reader);
}
else
{
UID = new CryptoHashCode(CryptoHashType.MD5, TimeStamp.Now + "+" + Utility.RandomDouble());
}
switch (version)
{
case 3:
case 2:
case 1:
case 0:
{
if (version < 2)
{
_Info = reader.ReadTypeCreate<ScheduleInfo>(reader) ?? new ScheduleInfo();
}
else
{
_Info = reader.ReadBlock(r => r.ReadTypeCreate<ScheduleInfo>(r)) ?? new ScheduleInfo();
}
_Enabled = reader.ReadBool();
_Name = reader.ReadString();
_DefaultPriority = reader.ReadFlag<TimerPriority>();
if (reader.ReadBool())
{
_LastGlobalTick = reader.ReadDateTime();
}
if (reader.ReadBool())
{
_NextGlobalTick = reader.ReadDateTime();
}
Delay = reader.ReadTimeSpan();
Interval = reader.ReadTimeSpan();
}
break;
}
InvalidateNextTick();
if (version > 0)
{
Running = reader.ReadBool();
}
}
public override int GetHashCode()
{
return UID.ValueHash;
}
public override string ToString()
{
return _Name;
}
public virtual string ToHtmlString(bool big = true)
{
var now = Now;
var html = new StringBuilder();
html.AppendLine("Current Date: {0}", now.ToSimpleString("D, M d y"));
html.AppendLine("Current Time: {0}", now.ToSimpleString("t@h:m@ X"));
html.AppendLine();
html.AppendLine("Schedule Overview:");
html.AppendLine();
if (!_Enabled)
{
html.AppendLine("Schedule is currently disabled.".WrapUOHtmlColor(Color.IndianRed));
return html.ToString();
}
var print = false;
var months = String.Join(", ", _Info.Months.EnumerateValues<string>(true).Not("None".Equals));
var days = String.Join(", ", _Info.Days.EnumerateValues<string>(true).Not("None".Equals));
var times = _Info.Times.ToString(6);
if (months == "None" || String.IsNullOrWhiteSpace(months))
{
html.AppendLine("Schedule requires at least one Month to be set.".WrapUOHtmlColor(Color.IndianRed));
}
else if (days == "None" || String.IsNullOrWhiteSpace(days))
{
html.AppendLine("Schedule requires at least one Day to be set.".WrapUOHtmlColor(Color.IndianRed));
}
else if (times == "None" || String.IsNullOrWhiteSpace(times))
{
html.AppendLine("Schedule requires at least one Time to be set.".WrapUOHtmlColor(Color.IndianRed));
}
else
{
print = true;
}
if (print)
{
html.Append(String.Empty.WrapUOHtmlColor(Color.Cyan, false));
html.AppendLine("Schedule is set to perform an action:");
html.AppendLine();
html.AppendLine("<B>In...</B>");
html.AppendLine(months);
html.AppendLine();
html.AppendLine("<B>On...</B>");
html.AppendLine(days);
html.AppendLine();
html.AppendLine("<B>At...</B>");
html.AppendLine(times);
html.Append(String.Empty.WrapUOHtmlColor(SuperGump.DefaultHtmlColor, false));
if (_NextGlobalTick != null)
{
var today = _NextGlobalTick.Value.Day == now.Day;
var t = _NextGlobalTick.Value.ToSimpleString("t@h:m@ X");
var d = today ? "today" : ("on " + _NextGlobalTick.Value.ToSimpleString("D, M d y"));
var o = WaitGlobalTick.ToSimpleString(@"!<d\d ><h\h ><m\m >s\s");
html.AppendLine();
html.AppendLine("The next tick will be at {0} {1}, in {2}.", t, d, o);
}
}
var value = big ? String.Format("<big>{0}</big>", html) : html.ToString();
return value.WrapUOHtmlColor(SuperGump.DefaultHtmlColor, false);
}
}
}

View File

@@ -0,0 +1,309 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Server;
#endregion
namespace VitaNex.Schedules
{
public class ScheduleTimes : IEnumerable<TimeSpan>, ICloneable
{
private static readonly ScheduleTimes _None;
private static readonly ScheduleTimes _Noon;
private static readonly ScheduleTimes _Midnight;
private static readonly ScheduleTimes _EveryHour;
private static readonly ScheduleTimes _EveryHalfHour;
private static readonly ScheduleTimes _EveryQuarterHour;
private static readonly ScheduleTimes _EveryTenMinutes;
private static readonly ScheduleTimes _EveryFiveMinutes;
private static readonly ScheduleTimes _EveryMinute;
private static readonly ScheduleTimes _FourTwenty;
public static ScheduleTimes None => new ScheduleTimes(_None);
public static ScheduleTimes Noon => new ScheduleTimes(_Noon);
public static ScheduleTimes Midnight => new ScheduleTimes(_Midnight);
public static ScheduleTimes EveryHour => new ScheduleTimes(_EveryHour);
public static ScheduleTimes EveryHalfHour => new ScheduleTimes(_EveryHalfHour);
public static ScheduleTimes EveryQuarterHour => new ScheduleTimes(_EveryQuarterHour);
public static ScheduleTimes EveryTenMinutes => new ScheduleTimes(_EveryTenMinutes);
public static ScheduleTimes EveryFiveMinutes => new ScheduleTimes(_EveryFiveMinutes);
public static ScheduleTimes EveryMinute => new ScheduleTimes(_EveryMinute);
public static ScheduleTimes FourTwenty => new ScheduleTimes(_FourTwenty);
static ScheduleTimes()
{
_None = new ScheduleTimes();
_Noon = new ScheduleTimes(TimeSpan.FromHours(12));
_Midnight = new ScheduleTimes(TimeSpan.Zero);
_EveryHour = new ScheduleTimes();
_EveryHalfHour = new ScheduleTimes();
_EveryQuarterHour = new ScheduleTimes();
_EveryTenMinutes = new ScheduleTimes();
_EveryFiveMinutes = new ScheduleTimes();
_EveryMinute = new ScheduleTimes();
for (var hours = 0; hours < 24; hours++)
{
_EveryHour.Add(new TimeSpan(hours, 0, 0));
for (var minutes = 0; minutes < 60; minutes++)
{
_EveryMinute.Add(new TimeSpan(hours, minutes, 0));
if (minutes % 5 == 0)
{
_EveryFiveMinutes.Add(new TimeSpan(hours, minutes, 0));
}
if (minutes % 10 == 0)
{
_EveryTenMinutes.Add(new TimeSpan(hours, minutes, 0));
}
if (minutes % 15 == 0)
{
_EveryQuarterHour.Add(new TimeSpan(hours, minutes, 0));
}
if (minutes % 30 == 0)
{
_EveryHalfHour.Add(new TimeSpan(hours, minutes, 0));
}
}
}
_FourTwenty = new ScheduleTimes(new TimeSpan(4, 20, 0), new TimeSpan(16, 20, 0));
}
private static void Validate(ref TimeSpan time)
{
time = new TimeSpan(0, time.Hours, time.Minutes, 0, 0);
}
private List<TimeSpan> _List = new List<TimeSpan>();
public int Count => _List.Count;
public TimeSpan? this[int index]
{
get => index < 0 || index >= _List.Count ? default(TimeSpan?) : _List[index];
set
{
if (index < 0 || index >= _List.Count)
{
return;
}
if (value == null)
{
_List.RemoveAt(index);
}
else
{
_List[index] = (TimeSpan)value;
}
}
}
public ScheduleTimes(ScheduleTimes times)
{
Add(times);
}
public ScheduleTimes(IEnumerable<TimeSpan> times)
{
Add(times);
}
public ScheduleTimes(params TimeSpan[] times)
{
Add(times);
}
public ScheduleTimes(GenericReader reader)
{
Deserialize(reader);
}
object ICloneable.Clone()
{
return Clone();
}
public virtual ScheduleTimes Clone()
{
return new ScheduleTimes(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return _List.GetEnumerator();
}
public IEnumerator<TimeSpan> GetEnumerator()
{
return _List.GetEnumerator();
}
public bool Contains(TimeSpan time, bool validate = true)
{
if (validate)
{
Validate(ref time);
}
return _List.Contains(time);
}
public void Add(ScheduleTimes times)
{
foreach (var time in times)
{
InternalAdd(time);
}
_List.Sort();
}
public void Add(IEnumerable<TimeSpan> times)
{
foreach (var time in times)
{
InternalAdd(time);
}
_List.Sort();
}
public void Add(params TimeSpan[] times)
{
foreach (var time in times)
{
InternalAdd(time);
}
_List.Sort();
}
private void InternalAdd(TimeSpan time)
{
Validate(ref time);
if (Contains(time, false))
{
return;
}
_List.Add(time);
}
public void Remove(ScheduleTimes times)
{
foreach (var time in times)
{
InternalRemove(time);
}
_List.TrimExcess();
_List.Sort();
}
public void Remove(IEnumerable<TimeSpan> times)
{
foreach (var time in times)
{
InternalRemove(time);
}
_List.TrimExcess();
_List.Sort();
}
public void Remove(params TimeSpan[] times)
{
foreach (var time in times)
{
InternalRemove(time);
}
_List.TrimExcess();
_List.Sort();
}
private void InternalRemove(TimeSpan time)
{
Validate(ref time);
if (!Contains(time, false))
{
return;
}
_List.Remove(time);
}
public void Clear()
{
_List.Clear();
_List.TrimExcess();
}
public TimeSpan[] ToArray()
{
return _List.ToArray();
}
public string ToString(int cols)
{
if (!_List.IsNullOrEmpty())
{
return _List.Select(t => t.ToSimpleString("h:m")).ToWrappedString(", ", cols);
}
return "None";
}
public override string ToString()
{
return ToString(0);
}
public virtual void Serialize(GenericWriter writer)
{
var version = writer.SetVersion(0);
switch (version)
{
case 0:
writer.WriteList(_List, (w, t) => w.Write(t));
break;
}
}
public virtual void Deserialize(GenericReader reader)
{
var version = reader.GetVersion();
switch (version)
{
case 0:
_List = reader.ReadList(r => r.ReadTimeSpan());
break;
}
}
}
}

View File

@@ -0,0 +1,244 @@
#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 Server;
#endregion
namespace VitaNex.Schedules
{
public static partial class Schedules
{
public const AccessLevel Access = AccessLevel.Administrator;
public static List<Schedule> Registry { get; private set; }
public static Schedule CreateSchedule(
string name,
bool enabled = true,
bool register = true,
ScheduleMonths months = ScheduleMonths.None,
ScheduleDays days = ScheduleDays.None,
ScheduleTimes times = null,
Action<Schedule> handler = null)
{
return CreateSchedule<Schedule>(name, enabled, register, new ScheduleInfo(months, days, times), handler);
}
public static Schedule CreateSchedule(
string name,
bool enabled = true,
bool register = true,
ScheduleInfo info = null,
Action<Schedule> handler = null)
{
return CreateSchedule<Schedule>(name, enabled, register, info, handler);
}
public static TSchedule CreateSchedule<TSchedule>(
string name,
bool enabled = true,
bool register = true,
ScheduleMonths months = ScheduleMonths.None,
ScheduleDays days = ScheduleDays.None,
ScheduleTimes times = null,
Action<Schedule> handler = null)
where TSchedule : Schedule
{
return CreateSchedule<TSchedule>(name, enabled, register, new ScheduleInfo(months, days, times), handler);
}
public static TSchedule CreateSchedule<TSchedule>(
string name,
bool enabled = true,
bool register = true,
ScheduleInfo info = null,
Action<Schedule> handler = null)
where TSchedule : Schedule
{
var st = VitaNexCore.TryCatchGet(() => typeof(TSchedule).CreateInstance<TSchedule>(name, enabled, info, handler));
if (st != null && register)
{
st.Register();
}
return st;
}
public static bool IsRegistered(Schedule schedule)
{
return schedule != null && Registry.Contains(schedule);
}
public static void Register(Schedule schedule)
{
if (schedule != null)
{
Registry.Update(schedule);
}
}
public static void Unregister(Schedule schedule)
{
if (schedule != null)
{
Registry.Remove(schedule);
}
}
public static ScheduleDays ConvertDay(int day)
{
switch (day)
{
case 0:
return ScheduleDays.None;
case 1:
return ScheduleDays.Sunday;
case 2:
return ScheduleDays.Monday;
case 3:
return ScheduleDays.Tuesday;
case 4:
return ScheduleDays.Wednesday;
case 5:
return ScheduleDays.Thursday;
case 6:
return ScheduleDays.Friday;
case 7:
return ScheduleDays.Saturday;
}
return ScheduleDays.All;
}
public static int ConvertDay(ScheduleDays day)
{
switch (day)
{
case ScheduleDays.None:
return 0;
case ScheduleDays.Sunday:
return 1;
case ScheduleDays.Monday:
return 2;
case ScheduleDays.Tuesday:
return 3;
case ScheduleDays.Wednesday:
return 4;
case ScheduleDays.Thursday:
return 5;
case ScheduleDays.Friday:
return 6;
case ScheduleDays.Saturday:
return 7;
}
return 0;
}
public static ScheduleDays ConvertDay(DayOfWeek day)
{
switch (day)
{
case DayOfWeek.Sunday:
return ScheduleDays.Sunday;
case DayOfWeek.Monday:
return ScheduleDays.Monday;
case DayOfWeek.Tuesday:
return ScheduleDays.Tuesday;
case DayOfWeek.Wednesday:
return ScheduleDays.Wednesday;
case DayOfWeek.Thursday:
return ScheduleDays.Thursday;
case DayOfWeek.Friday:
return ScheduleDays.Friday;
case DayOfWeek.Saturday:
return ScheduleDays.Saturday;
}
return ScheduleDays.None;
}
public static ScheduleMonths ConvertMonth(int month)
{
switch (month)
{
case 0:
return ScheduleMonths.None;
case 1:
return ScheduleMonths.January;
case 2:
return ScheduleMonths.February;
case 3:
return ScheduleMonths.March;
case 4:
return ScheduleMonths.April;
case 5:
return ScheduleMonths.May;
case 6:
return ScheduleMonths.June;
case 7:
return ScheduleMonths.July;
case 8:
return ScheduleMonths.August;
case 9:
return ScheduleMonths.September;
case 10:
return ScheduleMonths.October;
case 11:
return ScheduleMonths.November;
case 12:
return ScheduleMonths.December;
}
return ScheduleMonths.All;
}
public static int ConvertMonth(ScheduleMonths month)
{
switch (month)
{
case ScheduleMonths.None:
return 0;
case ScheduleMonths.January:
return 1;
case ScheduleMonths.February:
return 2;
case ScheduleMonths.March:
return 3;
case ScheduleMonths.April:
return 4;
case ScheduleMonths.May:
return 5;
case ScheduleMonths.June:
return 6;
case ScheduleMonths.July:
return 7;
case ScheduleMonths.August:
return 8;
case ScheduleMonths.September:
return 9;
case ScheduleMonths.October:
return 10;
case ScheduleMonths.November:
return 11;
case ScheduleMonths.December:
return 12;
}
return 0;
}
}
}

View File

@@ -0,0 +1,31 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System.Collections.Generic;
#endregion
namespace VitaNex.Schedules
{
[CoreService("Schedules", "3.0.0.0", TaskPriority.High)]
public static partial class Schedules
{
static Schedules()
{
Registry = new List<Schedule>();
}
private static void CSConfig()
{
CommandUtility.Register("Schedules", Access, e => new ScheduleListGump(e.Mobile).Send());
}
}
}

View File

@@ -0,0 +1,113 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using Server;
using Server.Gumps;
using VitaNex.SuperGumps.UI;
#endregion
namespace VitaNex.Schedules
{
public class ScheduleDaysMenuGump : MenuGump
{
public Schedule Schedule { get; set; }
public bool UseConfirmDialog { get; set; }
public ScheduleDaysMenuGump(
Mobile user,
Schedule schedule,
Gump parent = null,
GumpButton clicked = null,
bool useConfirm = true)
: base(user, parent, clicked: clicked)
{
Schedule = schedule;
UseConfirmDialog = useConfirm;
CanMove = false;
CanResize = false;
}
protected override void CompileOptions(MenuGumpOptions list)
{
list.AppendEntry(new ListGumpEntry("None", b => SetDay(b, ScheduleDays.None)));
list.AppendEntry(new ListGumpEntry("All", b => SetDay(b, ScheduleDays.All)));
list.AppendEntry(new ListGumpEntry("Monday", b => SetDay(b, ScheduleDays.Monday)));
list.AppendEntry(new ListGumpEntry("Tuesday", b => SetDay(b, ScheduleDays.Tuesday)));
list.AppendEntry(new ListGumpEntry("Wednesday", b => SetDay(b, ScheduleDays.Wednesday)));
list.AppendEntry(new ListGumpEntry("Thursday", b => SetDay(b, ScheduleDays.Thursday)));
list.AppendEntry(new ListGumpEntry("Friday", b => SetDay(b, ScheduleDays.Friday)));
list.AppendEntry(new ListGumpEntry("Saturday", b => SetDay(b, ScheduleDays.Saturday)));
list.AppendEntry(new ListGumpEntry("Sunday", b => SetDay(b, ScheduleDays.Sunday)));
base.CompileOptions(list);
list.Replace("Cancel", new ListGumpEntry("Done", Cancel));
}
protected override int GetLabelHue(int index, int pageIndex, ListGumpEntry entry)
{
if (Schedule == null)
{
return ErrorHue;
}
switch (entry.Label)
{
case "Monday":
return Schedule.Info.HasDay(ScheduleDays.Monday) ? HighlightHue : ErrorHue;
case "Tuesday":
return Schedule.Info.HasDay(ScheduleDays.Tuesday) ? HighlightHue : ErrorHue;
case "Wednesday":
return Schedule.Info.HasDay(ScheduleDays.Wednesday) ? HighlightHue : ErrorHue;
case "Thursday":
return Schedule.Info.HasDay(ScheduleDays.Thursday) ? HighlightHue : ErrorHue;
case "Friday":
return Schedule.Info.HasDay(ScheduleDays.Friday) ? HighlightHue : ErrorHue;
case "Saturday":
return Schedule.Info.HasDay(ScheduleDays.Saturday) ? HighlightHue : ErrorHue;
case "Sunday":
return Schedule.Info.HasDay(ScheduleDays.Sunday) ? HighlightHue : ErrorHue;
}
return base.GetLabelHue(index, pageIndex, entry);
}
protected virtual void SetDay(GumpButton button, ScheduleDays day)
{
if (Schedule == null)
{
Close();
return;
}
switch (day)
{
case ScheduleDays.None:
Schedule.Info.Days = ScheduleDays.None;
break;
case ScheduleDays.All:
Schedule.Info.Days = ScheduleDays.All;
break;
default:
Schedule.Info.Days ^= day;
break;
}
Schedule.InvalidateNextTick();
Refresh(true);
}
}
}

View File

@@ -0,0 +1,128 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using Server;
using Server.Gumps;
using VitaNex.SuperGumps.UI;
#endregion
namespace VitaNex.Schedules
{
public class ScheduleMonthsMenuGump : MenuGump
{
public Schedule Schedule { get; set; }
public bool UseConfirmDialog { get; set; }
public ScheduleMonthsMenuGump(
Mobile user,
Schedule schedule,
Gump parent = null,
GumpButton clicked = null,
bool useConfirm = true)
: base(user, parent, clicked: clicked)
{
Schedule = schedule;
UseConfirmDialog = useConfirm;
CanMove = false;
CanResize = false;
}
protected override void CompileOptions(MenuGumpOptions list)
{
list.AppendEntry("None", b => SetMonth(b, ScheduleMonths.None));
list.AppendEntry("All", b => SetMonth(b, ScheduleMonths.All));
list.AppendEntry("January", b => SetMonth(b, ScheduleMonths.January));
list.AppendEntry("February", b => SetMonth(b, ScheduleMonths.February));
list.AppendEntry("March", b => SetMonth(b, ScheduleMonths.March));
list.AppendEntry("April", b => SetMonth(b, ScheduleMonths.April));
list.AppendEntry("May", b => SetMonth(b, ScheduleMonths.May));
list.AppendEntry("June", b => SetMonth(b, ScheduleMonths.June));
list.AppendEntry("July", b => SetMonth(b, ScheduleMonths.July));
list.AppendEntry("August", b => SetMonth(b, ScheduleMonths.August));
list.AppendEntry("September", b => SetMonth(b, ScheduleMonths.September));
list.AppendEntry("October", b => SetMonth(b, ScheduleMonths.October));
list.AppendEntry("November", b => SetMonth(b, ScheduleMonths.November));
list.AppendEntry("December", b => SetMonth(b, ScheduleMonths.December));
base.CompileOptions(list);
list.Replace("Cancel", "Done", Cancel);
}
protected override int GetLabelHue(int index, int pageIndex, ListGumpEntry entry)
{
if (Schedule == null)
{
return ErrorHue;
}
switch (entry.Label)
{
case "January":
return Schedule.Info.HasMonth(ScheduleMonths.January) ? HighlightHue : ErrorHue;
case "February":
return Schedule.Info.HasMonth(ScheduleMonths.February) ? HighlightHue : ErrorHue;
case "March":
return Schedule.Info.HasMonth(ScheduleMonths.March) ? HighlightHue : ErrorHue;
case "April":
return Schedule.Info.HasMonth(ScheduleMonths.April) ? HighlightHue : ErrorHue;
case "May":
return Schedule.Info.HasMonth(ScheduleMonths.May) ? HighlightHue : ErrorHue;
case "June":
return Schedule.Info.HasMonth(ScheduleMonths.June) ? HighlightHue : ErrorHue;
case "July":
return Schedule.Info.HasMonth(ScheduleMonths.July) ? HighlightHue : ErrorHue;
case "August":
return Schedule.Info.HasMonth(ScheduleMonths.August) ? HighlightHue : ErrorHue;
case "September":
return Schedule.Info.HasMonth(ScheduleMonths.September) ? HighlightHue : ErrorHue;
case "October":
return Schedule.Info.HasMonth(ScheduleMonths.October) ? HighlightHue : ErrorHue;
case "November":
return Schedule.Info.HasMonth(ScheduleMonths.November) ? HighlightHue : ErrorHue;
case "December":
return Schedule.Info.HasMonth(ScheduleMonths.December) ? HighlightHue : ErrorHue;
}
return base.GetLabelHue(index, pageIndex, entry);
}
protected virtual void SetMonth(GumpButton button, ScheduleMonths month)
{
if (Schedule == null)
{
Close();
return;
}
switch (month)
{
case ScheduleMonths.None:
Schedule.Info.Months = ScheduleMonths.None;
break;
case ScheduleMonths.All:
Schedule.Info.Months = ScheduleMonths.All;
break;
default:
Schedule.Info.Months ^= month;
break;
}
Schedule.InvalidateNextTick();
Refresh(true);
}
}
}

View File

@@ -0,0 +1,120 @@
#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 Server;
using Server.Gumps;
using VitaNex.SuperGumps;
using VitaNex.SuperGumps.UI;
#endregion
namespace VitaNex.Schedules
{
public class ScheduleListGump : ListGump<Schedule>
{
public static string HelpText = "Schedules: Schedules are timers that tick on specific dates at specific times.";
public bool UseConfirmDialog { get; set; }
public ScheduleListGump(Mobile user, Gump parent = null, bool useConfirm = true)
: base(user, parent, emptyText: "There are no schedules to display.", title: "Schedules")
{
UseConfirmDialog = useConfirm;
ForceRecompile = true;
CanMove = false;
CanResize = false;
AutoRefresh = true;
}
protected override string GetLabelText(int index, int pageIndex, Schedule entry)
{
return entry != null ? entry.Name : base.GetLabelText(index, pageIndex, null);
}
protected override int GetLabelHue(int index, int pageIndex, Schedule entry)
{
if (entry != null)
{
if (!entry.Enabled)
{
return ErrorHue;
}
if (!entry.Running || entry.NextGlobalTick == null)
{
return HighlightHue;
}
}
return base.GetLabelHue(index, pageIndex, null);
}
protected override void CompileMenuOptions(MenuGumpOptions list)
{
list.AppendEntry(new ListGumpEntry("Help", ShowHelp));
base.CompileMenuOptions(list);
}
protected virtual void ShowHelp(GumpButton button)
{
Send(new NoticeDialogGump(User, this, title: "Help", html: HelpText));
}
protected override void CompileList(List<Schedule> list)
{
list.Clear();
list.AddRange(Schedules.Registry);
base.CompileList(list);
}
public override string GetSearchKeyFor(Schedule key)
{
return key != null ? key.Name : base.GetSearchKeyFor(null);
}
protected override void CompileLayout(SuperGumpLayout layout)
{
base.CompileLayout(layout);
layout.Replace(
"label/header/title",
() => AddLabelCropped(90, 15, 185, 20, GetTitleHue(), String.IsNullOrEmpty(Title) ? DefaultTitle : Title));
layout.Replace(
"label/header/subtitle",
() =>
{
var loc = DateTime.Now.ToSimpleString("D t@h:m@ X");
var utc = DateTime.UtcNow.ToSimpleString("D t@h:m@ X");
AddLabelCropped(275, 15, 100, 20, HighlightHue, String.Format("[{0}] [{1}]", loc, utc));
});
}
protected override void SelectEntry(GumpButton button, Schedule entry)
{
base.SelectEntry(button, entry);
if (button != null && entry != null)
{
Send(new ScheduleOverviewGump(User, entry, Hide(true), UseConfirmDialog));
}
}
}
}

View File

@@ -0,0 +1,168 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Drawing;
using Server;
using Server.Gumps;
using VitaNex.SuperGumps;
using VitaNex.SuperGumps.UI;
#endregion
namespace VitaNex.Schedules
{
public class ScheduleOverviewGump : HtmlPanelGump<Schedule>
{
public bool UseConfirmDialog { get; set; }
public ScheduleOverviewGump(Mobile user, Schedule schedule, Gump parent = null, bool useConfirm = true)
: base(user, parent, emptyText: "Schedule Unavailable", title: "Schedule Overview", selected: schedule)
{
UseConfirmDialog = useConfirm;
HtmlColor = Color.GreenYellow;
ForceRecompile = true;
AutoRefresh = true;
}
protected override void Compile()
{
base.Compile();
if (Selected != null)
{
Html = Selected.ToHtmlString();
}
}
protected override void CompileLayout(SuperGumpLayout layout)
{
base.CompileLayout(layout);
layout.Replace(
"label/header/title",
() =>
{
var title = String.IsNullOrEmpty(Title) ? "Schedule Overview" : Title;
AddLabelCropped(90, 15, Width - 235, 20, GetTitleHue(), title);
});
layout.Replace(
"label/header/subtitle",
() =>
{
var time = Selected.Now.ToSimpleString("t@h:m@ X");
AddLabelCropped(90 + (Width - 235), 15, 100, 20, HighlightHue, time);
});
}
protected override void CompileMenuOptions(MenuGumpOptions list)
{
list.Clear();
if (Selected != null && User.AccessLevel >= Schedules.Access)
{
if (!Selected.Enabled)
{
list.AppendEntry(
"Enable",
b =>
{
Selected.Enabled = true;
Refresh(true);
},
HighlightHue);
}
else
{
list.AppendEntry(
"Disable",
b =>
{
Selected.Enabled = false;
Refresh(true);
},
HighlightHue);
}
if (Selected.IsLocal)
{
list.AppendEntry(
"Use Universal Time",
b =>
{
Selected.IsLocal = false;
Refresh(true);
},
HighlightHue);
}
else
{
list.AppendEntry(
"Use Local Time",
b =>
{
Selected.IsLocal = true;
Refresh(true);
},
HighlightHue);
}
list.AppendEntry("Edit Months", b => Send(new ScheduleMonthsMenuGump(User, Selected, Refresh(), b)), HighlightHue);
list.AppendEntry("Edit Days", b => Send(new ScheduleDaysMenuGump(User, Selected, Refresh(), b)), HighlightHue);
list.AppendEntry("Edit Times", b => Send(new SheduleTimeListGump(User, Selected, Hide(true))), HighlightHue);
list.AppendEntry(
"Clear Schedule",
b =>
{
if (UseConfirmDialog)
{
new ConfirmDialogGump(User, this)
{
Title = "Clear Schedule?",
Html = "The schedule will be cleared, erasing all data associated with its entries.\n" +
"This action can not be reversed.\n\nDo you want to continue?",
AcceptHandler = OnConfirmClearSchedule,
CancelHandler = Refresh
}.Send();
}
else
{
OnConfirmClearSchedule(b);
}
},
HighlightHue);
}
base.CompileMenuOptions(list);
}
protected virtual void OnConfirmClearSchedule(GumpButton button)
{
if (Selected == null)
{
Close();
return;
}
Selected.Info.Clear();
Selected.InvalidateNextTick();
Refresh(true);
}
}
}

View File

@@ -0,0 +1,217 @@
#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 Server;
using Server.Gumps;
using VitaNex.SuperGumps.UI;
#endregion
namespace VitaNex.Schedules
{
public class SheduleTimeListGump : ListGump<TimeSpan>
{
public static string HelpText = "Schedule Times: List specific times for this schedule.\n" +
"These times determine what time of day the schedule will tick.";
public Schedule Schedule { get; set; }
public bool UseConfirmDialog { get; set; }
public SheduleTimeListGump(Mobile user, Schedule schedule, Gump parent = null, bool useConfirm = true)
: base(user, parent, emptyText: "There are no times to display.", title: "Schedule Times")
{
Schedule = schedule;
UseConfirmDialog = useConfirm;
ForceRecompile = true;
CanMove = false;
CanResize = false;
Columns = 3;
EntriesPerPage = 24;
}
protected override void Compile()
{
var i = Title.IndexOf('(');
if (i > 0)
{
Title = Title.Substring(0, i);
}
Title += String.Concat(' ', '(', Schedule.Now.ToSimpleString("X"), ')');
base.Compile();
}
protected override string GetLabelText(int index, int pageIndex, TimeSpan entry)
{
return entry.ToSimpleString("h:m");
}
protected override void CompileMenuOptions(MenuGumpOptions list)
{
list.AppendEntry("Delete All", OnDeleteAll, HighlightHue);
list.AppendEntry("Add Time", OnAddTime, HighlightHue);
list.AppendEntry("Use Preset", OnPresetsMenu, HighlightHue);
list.AppendEntry("Help", ShowHelp);
base.CompileMenuOptions(list);
}
protected virtual void ShowHelp(GumpButton button)
{
if (User != null && !User.Deleted)
{
Send(new NoticeDialogGump(User, this, title: "Help", html: HelpText));
}
}
protected virtual void OnDeleteAll(GumpButton button)
{
if (UseConfirmDialog)
{
new ConfirmDialogGump(User, this)
{
Title = "Delete All Times?",
Html = "All times in the schedule will be deleted, erasing all data associated with them.\n" +
"This action can not be reversed.\n\nDo you want to continue?",
AcceptHandler = subButton =>
{
Schedule.Info.Times.Clear();
Schedule.InvalidateNextTick();
Refresh(true);
},
CancelHandler = Refresh
}.Send();
}
else
{
Schedule.Info.Times.Clear();
Schedule.InvalidateNextTick();
Refresh(true);
}
}
protected virtual void OnAddTime(GumpButton button)
{
var nowTime = Schedule.Now.TimeOfDay;
new InputDialogGump(User, this)
{
Title = "Add Schedule Time",
Html = "Enter the time of day " + String.Concat('(', nowTime.ToSimpleString("X"), ')') +
" to add to this schedule.\nFormat: HH:MM\nExample: " +
String.Format("{0:D2}:{1:D2}", nowTime.Hours, nowTime.Minutes) +
"\n\nYou can also load a preset list of times, but be aware that presets will " +
"overwrite any custom entries you have created.",
Callback = (b, text) =>
{
ParseTime(text, out var hh, out var mm);
if (hh == -1 || mm == -1)
{
OnAddTime(button);
return;
}
Schedule.Info.Times.Add(new TimeSpan(0, hh, mm, 0, 0));
Schedule.InvalidateNextTick();
OnAddTime(button);
},
CancelHandler = Refresh
}.Send();
}
protected virtual void OnPresetsMenu(GumpButton button)
{
var entries = new[]
{
new ListGumpEntry("Noon", b => UsePreset(b, ScheduleTimes.Noon), HighlightHue),
new ListGumpEntry("Midnight", b => UsePreset(b, ScheduleTimes.Midnight), HighlightHue),
new ListGumpEntry("Every Hour", b => UsePreset(b, ScheduleTimes.EveryHour), HighlightHue),
new ListGumpEntry("Every 30 Minutes", b => UsePreset(b, ScheduleTimes.EveryHalfHour), HighlightHue),
new ListGumpEntry("Every 15 Minutes", b => UsePreset(b, ScheduleTimes.EveryQuarterHour), HighlightHue),
new ListGumpEntry("Every 10 Minutes", b => UsePreset(b, ScheduleTimes.EveryTenMinutes), HighlightHue),
new ListGumpEntry("Every 5 Minutes", b => UsePreset(b, ScheduleTimes.EveryFiveMinutes), HighlightHue),
new ListGumpEntry("Every Minute", b => UsePreset(b, ScheduleTimes.EveryMinute), HighlightHue),
new ListGumpEntry("Four Twenty", b => UsePreset(b, ScheduleTimes.FourTwenty), HighlightHue)
};
Send(new MenuGump(User, this, entries, button));
}
protected virtual void UsePreset(GumpButton button, ScheduleTimes times)
{
Schedule.Info.Times.Clear();
Schedule.Info.Times.Add(times);
Schedule.InvalidateNextTick();
Refresh(true);
}
protected virtual void ParseTime(string text, out int hh, out int mm)
{
var parts = text.Split(':');
if (parts.Length >= 2)
{
if (!Int32.TryParse(parts[0], out hh))
{
hh = -1;
}
if (!Int32.TryParse(parts[1], out mm))
{
mm = -1;
}
hh = (hh < 0) ? 0 : (hh > 23) ? 23 : hh;
mm = (mm < 0) ? 0 : (mm > 59) ? 59 : mm;
}
else
{
hh = -1;
mm = -1;
}
}
protected override void CompileList(List<TimeSpan> list)
{
list.Clear();
list.AddRange(Schedule.Info.Times);
base.CompileList(list);
}
public override string GetSearchKeyFor(TimeSpan key)
{
return key.ToSimpleString("h:m");
}
protected override void SelectEntry(GumpButton button, TimeSpan entry)
{
base.SelectEntry(button, entry);
Send(new ScheduleTimeListEntryGump(User, Schedule, Refresh(), button, entry, UseConfirmDialog));
}
}
}

View File

@@ -0,0 +1,88 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using Server;
using Server.Gumps;
using VitaNex.SuperGumps.UI;
#endregion
namespace VitaNex.Schedules
{
public class ScheduleTimeListEntryGump : MenuGump
{
public Schedule Schedule { get; set; }
public TimeSpan Time { get; set; }
public bool UseConfirmDialog { get; set; }
public ScheduleTimeListEntryGump(
Mobile user,
Schedule schedule,
Gump parent = null,
GumpButton clicked = null,
TimeSpan? time = null,
bool useConfirm = true)
: base(user, parent, clicked: clicked)
{
Schedule = schedule;
Time = time ?? TimeSpan.Zero;
UseConfirmDialog = useConfirm;
CanMove = false;
CanResize = false;
}
protected override void CompileOptions(MenuGumpOptions list)
{
base.CompileOptions(list);
list.PrependEntry(
"Delete",
button =>
{
if (UseConfirmDialog)
{
new ConfirmDialogGump(User, Refresh())
{
Title = "Delete Time?",
Html = "All data associated with this time will be deleted.\n" +
"This action can not be reversed!\nDo you want to continue?",
AcceptHandler = OnConfirmDelete
}.Send();
}
else
{
OnConfirmDelete(button);
}
},
HighlightHue);
}
protected virtual void OnConfirmDelete(GumpButton button)
{
if (Selected == null)
{
Close();
return;
}
Schedule.Info.Times.Remove(Time);
Schedule.InvalidateNextTick();
Close();
}
}
}

View File

@@ -0,0 +1,167 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Drawing;
using Server;
using Server.Items;
using Server.Mobiles;
using VitaNex.Network;
#endregion
namespace VitaNex.TimeBoosts
{
[Flipable(4173, 4174)]
public class TimeBoostToken : Item
{
private ITimeBoost _Boost;
[CommandProperty(AccessLevel.Counselor, true)]
public ITimeBoost Boost
{
get => _Boost ?? (_Boost = TimeBoosts.MinValue);
private set
{
if (_Boost == value)
{
return;
}
_Boost = value ?? TimeBoosts.MinValue;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.Administrator)]
public TimeSpan Value { get => Boost.Value; set => Boost = TimeBoosts.Find(value); }
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
public override int Hue
{
get
{
if (World.Saving)
{
return base.Hue;
}
var hue = base.Hue;
return hue > 0 ? hue : Boost.Hue;
}
set => base.Hue = value;
}
public override string DefaultName => Boost.Name;
[Constructable(AccessLevel.Administrator)]
public TimeBoostToken()
: this(TimeBoosts.RandomValue, 1)
{ }
[Constructable(AccessLevel.Administrator)]
public TimeBoostToken(string time)
: this(time, 1)
{ }
[Constructable(AccessLevel.Administrator)]
public TimeBoostToken(string time, int amount)
: this(TimeSpan.Parse(time), amount)
{ }
public TimeBoostToken(TimeSpan time)
: this(TimeBoosts.Find(time))
{ }
public TimeBoostToken(TimeSpan time, int amount)
: this(TimeBoosts.Find(time), amount)
{ }
public TimeBoostToken(ITimeBoost boost)
: this(boost, 1)
{ }
public TimeBoostToken(ITimeBoost boost, int amount)
: base(Utility.RandomList(4173, 4174))
{
Boost = boost;
Stackable = true;
Amount = Math.Max(1, Math.Min(60000, amount));
LootType = LootType.Blessed;
}
public TimeBoostToken(Serial serial)
: base(serial)
{ }
public override bool StackWith(Mobile m, Item dropped, bool playSound)
{
if (!(dropped is TimeBoostToken) || ((TimeBoostToken)dropped).Boost != Boost)
{
return false;
}
return base.StackWith(m, dropped, playSound);
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
new ExtendedOPL(list)
{
{"Use: Credits {0:#,0} {1} to your account".WrapUOHtmlColor(Color.LawnGreen), Amount, Boost},
"\"Time Boosts reduce the time required to do certain things\"".WrapUOHtmlColor(Color.Gold)
}.Apply();
}
public override void OnDoubleClick(Mobile m)
{
if (!this.CheckDoubleClick(m, true, false, -1, true, false, false) || !(m is PlayerMobile))
{
return;
}
var user = (PlayerMobile)m;
if (TimeBoosts.Credit(user, Boost, Amount))
{
TimeBoostsUI.Update(user);
m.SendMessage(85, "{0:#,0} {1} has been credited to your account.", Amount, Boost);
Delete();
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.SetVersion(0);
writer.Write(Boost);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
reader.GetVersion();
Boost = reader.ReadTimeBoost();
}
}
}

View File

@@ -0,0 +1,27 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.TimeBoosts
{
public interface ITimeBoost
{
int RawValue { get; }
TimeSpan Value { get; }
string Desc { get; }
string Name { get; }
int Hue { get; }
}
}

View File

@@ -0,0 +1,71 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.TimeBoosts
{
public struct TimeBoostHours : ITimeBoost
{
public int RawValue { get; private set; }
public TimeSpan Value { get; private set; }
public string Desc => "Hour";
public string Name => String.Format("{0}-{1} Boost", RawValue, Desc);
public int Hue => 2118;
public TimeBoostHours(int hours)
: this()
{
Value = TimeSpan.FromHours(RawValue = hours);
}
public override string ToString()
{
return Name;
}
public override int GetHashCode()
{
unchecked
{
var hash = RawValue;
hash = (hash * 397) ^ Value.Days;
hash = (hash * 397) ^ Value.Hours;
hash = (hash * 397) ^ Value.Minutes;
return hash;
}
}
public static implicit operator TimeBoostHours(int hours)
{
return new TimeBoostHours(hours);
}
public static implicit operator TimeBoostHours(TimeSpan time)
{
return new TimeBoostHours(time.Hours);
}
public static implicit operator TimeSpan(TimeBoostHours value)
{
return value.Value;
}
public static implicit operator int(TimeBoostHours value)
{
return value.RawValue;
}
}
}

View File

@@ -0,0 +1,71 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.TimeBoosts
{
public struct TimeBoostMinutes : ITimeBoost
{
public int RawValue { get; private set; }
public TimeSpan Value { get; private set; }
public string Desc => "Minute";
public string Name => String.Format("{0}-{1} Boost", RawValue, Desc);
public int Hue => 2106;
public TimeBoostMinutes(int minutes)
: this()
{
Value = TimeSpan.FromMinutes(RawValue = minutes);
}
public override string ToString()
{
return Name;
}
public override int GetHashCode()
{
unchecked
{
var hash = RawValue;
hash = (hash * 397) ^ Value.Days;
hash = (hash * 397) ^ Value.Hours;
hash = (hash * 397) ^ Value.Minutes;
return hash;
}
}
public static implicit operator TimeBoostMinutes(int minutes)
{
return new TimeBoostMinutes(minutes);
}
public static implicit operator TimeBoostMinutes(TimeSpan time)
{
return new TimeBoostMinutes(time.Minutes);
}
public static implicit operator TimeSpan(TimeBoostMinutes value)
{
return value.Value;
}
public static implicit operator int(TimeBoostMinutes value)
{
return value.RawValue;
}
}
}

View File

@@ -0,0 +1,256 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Server;
using Server.Accounting;
#endregion
namespace VitaNex.TimeBoosts
{
public sealed class TimeBoostProfile : PropertyObject, IEnumerable<KeyValuePair<ITimeBoost, int>>
{
public IAccount Owner { get; private set; }
private Dictionary<ITimeBoost, int> _Boosts = new Dictionary<ITimeBoost, int>();
public int this[ITimeBoost key] { get => _Boosts.GetValue(key); set => _Boosts.Update(key, value); }
public TimeSpan TotalTime => TimeSpan.FromTicks(_Boosts.Sum(kv => kv.Key.Value.Ticks * kv.Value));
public TimeBoostProfile(IAccount owner)
{
Owner = owner;
}
public TimeBoostProfile(GenericReader reader)
: base(reader)
{ }
public override void Clear()
{
_Boosts.Clear();
}
public override void Reset()
{
_Boosts.Clear();
}
#region Credit
public bool CanCredit(ITimeBoost b, int amount)
{
return b != null && amount >= 0 && (long)this[b] + amount <= Int32.MaxValue;
}
public bool Credit(ITimeBoost b, int amount)
{
if (CanCredit(b, amount))
{
this[b] += amount;
return true;
}
return false;
}
public bool CreditHours(int value)
{
return Credit(value, 0);
}
public bool CreditMinutes(int value)
{
return Credit(0, value);
}
public bool Credit(int hours, int minutes)
{
return Credit(hours, minutes, false) && Credit(hours, minutes, true);
}
private bool Credit(int hours, int minutes, bool update)
{
return Credit(hours, minutes, update, out _, out _);
}
private bool Credit(int hours, int minutes, bool update, out int totalHours, out int totalMinutes)
{
totalHours = totalMinutes = 0;
return (hours == 0 || Credit(hours, 0, update, out totalHours)) && (minutes == 0 || Credit(minutes, 1, update, out totalMinutes));
}
private bool Credit(int time, byte index, bool update, out int totalTime)
{
totalTime = 0;
if (!TimeBoosts.Times.InBounds(index))
{
return false;
}
ITimeBoost k;
int v, t = time;
var i = TimeBoosts.Times[index].Length;
while (--i >= 0)
{
k = TimeBoosts.Times[index][i];
v = k.RawValue;
if (time >= v && this[k] < Int32.MaxValue)
{
time -= v;
totalTime += v;
if (update)
{
++this[k];
}
}
}
return totalTime >= t;
}
#endregion Credit
#region Consume
public bool CanConsume(ITimeBoost b, int amount)
{
return b != null && amount >= 0 && this[b] >= amount;
}
public bool Consume(ITimeBoost b, int amount)
{
if (CanConsume(b, amount))
{
this[b] -= amount;
return true;
}
return false;
}
public bool ConsumeHours(int value)
{
return Consume(value, 0);
}
public bool ConsumeMinutes(int value)
{
return Consume(0, value);
}
public bool Consume(int hours, int minutes)
{
return Consume(hours, minutes, false) && Consume(hours, minutes, true);
}
private bool Consume(int hours, int minutes, bool update)
{
return Consume(hours, minutes, update, out _, out _);
}
private bool Consume(int hours, int minutes, bool update, out int totalHours, out int totalMinutes)
{
totalHours = totalMinutes = 0;
return (hours == 0 || Consume(hours, 0, update, out totalHours)) && (minutes == 0 || Consume(minutes, 1, update, out totalMinutes));
}
private bool Consume(int time, byte index, bool update, out int totalTime)
{
totalTime = 0;
if (!TimeBoosts.Times.InBounds(index))
{
return false;
}
ITimeBoost k;
int v, t = time;
var i = TimeBoosts.Times[index].Length;
while (--i >= 0)
{
k = TimeBoosts.Times[index][i];
v = k.RawValue;
if (time >= v && this[k] > 0)
{
time -= v;
totalTime += v;
if (update)
{
--this[k];
}
}
}
return totalTime >= t;
}
#endregion Consume
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<ITimeBoost, int>> GetEnumerator()
{
return _Boosts.GetEnumerator();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.SetVersion(0);
writer.Write(Owner);
writer.WriteDictionary(_Boosts, (w, k, v) =>
{
w.Write(k);
w.Write(v);
});
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
reader.GetVersion();
Owner = reader.ReadAccount();
_Boosts = reader.ReadDictionary(r =>
{
var k = r.ReadTimeBoost();
var v = r.ReadInt();
return new KeyValuePair<ITimeBoost, int>(k, v);
},
_Boosts);
_Boosts.RemoveValueRange(v => v <= 0);
}
}
}

View File

@@ -0,0 +1,261 @@
#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.Linq;
using Server;
using Server.Accounting;
using Server.Mobiles;
using VitaNex.IO;
#endregion
namespace VitaNex.TimeBoosts
{
public static partial class TimeBoosts
{
public static TimeBoostHours[] Hours { get; private set; }
public static TimeBoostMinutes[] Minutes { get; private set; }
public static ITimeBoost[][] Times { get; private set; }
public static ITimeBoost[] AllTimes { get; private set; }
public static ITimeBoost RandomHours => Hours.GetRandom();
public static ITimeBoost RandomMinutes => Minutes.GetRandom();
public static ITimeBoost RandomValue => AllTimes.GetRandom();
public static ITimeBoost MinValue => AllTimes.Lowest(b => b.Value);
public static ITimeBoost MaxValue => AllTimes.Highest(b => b.Value);
public static BinaryDataStore<IAccount, TimeBoostProfile> Profiles { get; private set; }
public static TimeBoostProfile FindProfile(PlayerMobile m)
{
if (m == null || m.Account == null)
{
return null;
}
return FindProfile(m.Account);
}
public static TimeBoostProfile FindProfile(IAccount a)
{
return Profiles.GetValue(a);
}
public static TimeBoostProfile EnsureProfile(PlayerMobile m)
{
if (m == null || m.Account == null)
{
return null;
}
return EnsureProfile(m.Account);
}
public static TimeBoostProfile EnsureProfile(IAccount a)
{
TimeBoostProfile profile = null;
Profiles.Update(a, p => profile = p ?? new TimeBoostProfile(a));
return profile;
}
public static IEnumerable<KeyValuePair<ITimeBoost, int>> FindBoosts(PlayerMobile m)
{
if (m == null || m.Account == null)
{
return Enumerable.Empty<KeyValuePair<ITimeBoost, int>>();
}
return FindBoosts(m.Account);
}
public static IEnumerable<KeyValuePair<ITimeBoost, int>> FindBoosts(IAccount a)
{
return FindProfile(a) ?? Enumerable.Empty<KeyValuePair<ITimeBoost, int>>();
}
#region Credit
public static bool CanCredit(PlayerMobile m, ITimeBoost b, int amount)
{
return m != null && CanCredit(m.Account, b, amount);
}
public static bool Credit(PlayerMobile m, ITimeBoost b, int amount)
{
return m != null && Credit(m.Account, b, amount);
}
public static bool CreditHours(PlayerMobile m, int value)
{
return m != null && CreditHours(m.Account, value);
}
public static bool CreditMinutes(PlayerMobile m, int value)
{
return m != null && CreditMinutes(m.Account, value);
}
public static bool Credit(PlayerMobile m, int hours, int minutes)
{
return m != null && Credit(m.Account, hours, minutes);
}
public static bool CanCredit(IAccount a, ITimeBoost b, int amount)
{
var p = EnsureProfile(a);
return p != null && p.CanCredit(b, amount);
}
public static bool Credit(IAccount a, ITimeBoost b, int amount)
{
var p = EnsureProfile(a);
return p != null && p.Credit(b, amount);
}
public static bool CreditHours(IAccount a, int value)
{
var p = EnsureProfile(a);
return p != null && p.CreditHours(value);
}
public static bool CreditMinutes(IAccount a, int value)
{
var p = EnsureProfile(a);
return p != null && p.CreditMinutes(value);
}
public static bool Credit(IAccount a, int hours, int minutes)
{
var p = EnsureProfile(a);
return p != null && p.Credit(hours, minutes);
}
#endregion Credit
#region Consume
public static bool CanConsume(PlayerMobile m, ITimeBoost b, int amount)
{
return m != null && CanConsume(m.Account, b, amount);
}
public static bool Consume(PlayerMobile m, ITimeBoost b, int amount)
{
return m != null && Consume(m.Account, b, amount);
}
public static bool ConsumeHours(PlayerMobile m, int value)
{
return m != null && ConsumeHours(m.Account, value);
}
public static bool ConsumeMinutes(PlayerMobile m, int value)
{
return m != null && ConsumeMinutes(m.Account, value);
}
public static bool Consume(PlayerMobile m, int hours, int minutes)
{
return m != null && Consume(m.Account, hours, minutes);
}
public static bool CanConsume(IAccount a, ITimeBoost b, int amount)
{
var p = EnsureProfile(a);
return p != null && p.CanConsume(b, amount);
}
public static bool Consume(IAccount a, ITimeBoost b, int amount)
{
var p = EnsureProfile(a);
return p != null && p.Consume(b, amount);
}
public static bool ConsumeHours(IAccount a, int value)
{
var p = EnsureProfile(a);
return p != null && p.ConsumeHours(value);
}
public static bool ConsumeMinutes(IAccount a, int value)
{
var p = EnsureProfile(a);
return p != null && p.ConsumeMinutes(value);
}
public static bool Consume(IAccount a, int hours, int minutes)
{
var p = EnsureProfile(a);
return p != null && p.Consume(hours, minutes);
}
#endregion Consume
public static ITimeBoost Find(TimeSpan time)
{
var index = AllTimes.Length;
while (--index >= 0)
{
var b = AllTimes[index];
if (time >= b.Value)
{
return b;
}
}
return null;
}
public static void Write(this GenericWriter writer, ITimeBoost boost)
{
writer.SetVersion(0);
if (boost != null)
{
writer.Write(true);
writer.Write(boost.Value);
}
else
{
writer.Write(false);
}
}
public static ITimeBoost ReadTimeBoost(this GenericReader reader)
{
reader.GetVersion();
if (reader.ReadBool())
{
return Find(reader.ReadTimeSpan());
}
return null;
}
}
}

View File

@@ -0,0 +1,88 @@
#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.Linq;
using Server;
using Server.Accounting;
using VitaNex.IO;
#endregion
namespace VitaNex.TimeBoosts
{
[CoreService("Time Boosts", "1.0.0.0", TaskPriority.High)]
public static partial class TimeBoosts
{
public static CoreServiceOptions CSOptions { get; private set; }
static TimeBoosts()
{
Hours = new TimeBoostHours[] { 1, 3, 6, 12 };
Minutes = new TimeBoostMinutes[] { 1, 3, 5, 15, 30 };
Times = new[] { Hours.CastToArray<ITimeBoost>(), Minutes.CastToArray<ITimeBoost>() };
AllTimes = Times.SelectMany(t => t).OrderBy(b => b.Value).ToArray();
CSOptions = new CoreServiceOptions(typeof(TimeBoosts));
Profiles = new BinaryDataStore<IAccount, TimeBoostProfile>(VitaNexCore.SavesDirectory + "/TimeBoosts", "Profiles")
{
Async = true,
OnSerialize = Serialize,
OnDeserialize = Deserialize
};
}
private static void CSSave()
{
Profiles.Export();
}
private static void CSLoad()
{
Profiles.Import();
}
private static bool Serialize(GenericWriter writer)
{
writer.SetVersion(0);
writer.WriteBlockDictionary(Profiles, (w, k, v) =>
{
w.Write(k);
v.Serialize(w);
});
return true;
}
private static bool Deserialize(GenericReader reader)
{
reader.GetVersion();
reader.ReadBlockDictionary(r =>
{
var k = r.ReadAccount();
var v = new TimeBoostProfile(r);
return new KeyValuePair<IAccount, TimeBoostProfile>(k, v);
},
Profiles);
return true;
}
}
}

View File

@@ -0,0 +1,551 @@
#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.Drawing;
using System.Linq;
using System.Text;
using Server;
using Server.Gumps;
using Server.Mobiles;
using VitaNex.SuperGumps;
using VitaNex.SuperGumps.UI;
#endregion
namespace VitaNex.TimeBoosts
{
public class TimeBoostsUI : SuperGumpList<ITimeBoost>
{
public static string DefaultTitle = "Time Boosts";
public static string DefaultSubTitle = String.Empty;
public static string DefaultEmptyText = "No available Time Boosts.";
public static string DefaultSummaryText = "Time Left";
public static string DefaultHelpText = "Tip: Click an available time boost to update the time remaining!";
public static Color DefaultTitleColor = Color.PaleGoldenrod;
public static Color DefaultSubTitleColor = Color.Goldenrod;
public static Color DefaultEmptyTextColor = Color.SaddleBrown;
public static Color DefaultBoostTextColor = Color.Yellow;
public static Color DefaultBoostCountColor = Color.LawnGreen;
public static Color DefaultSummaryTextColor = Color.Goldenrod;
public static Color DefaultHelpTextColor = Color.Gold;
public static void Update(PlayerMobile user)
{
foreach (var info in EnumerateInstances<TimeBoostsUI>(user, true).Where(g => g != null && !g.IsDisposed && g.IsOpen))
info.Refresh(true);
}
public string Title { get; set; }
public string SubTitle { get; set; }
public string EmptyText { get; set; }
public string SummaryText { get; set; }
public string HelpText { get; set; }
public Color TitleColor { get; set; }
public Color SubTitleColor { get; set; }
public Color EmptyTextColor { get; set; }
public Color BoostTextColor { get; set; }
public Color BoostCountColor { get; set; }
public Color SummaryTextColor { get; set; }
public Color HelpTextColor { get; set; }
public TimeBoostProfile Profile { get; set; }
public bool ApplyAutoAllowed { get; set; }
public bool ApplyAutoPreview { get; set; }
public Func<ITimeBoost, bool> CanApply { get; set; }
public Action<ITimeBoost> BoostUsed { get; set; }
public Func<TimeSpan> GetTime { get; set; }
public Action<TimeSpan> SetTime { get; set; }
public TimeSpan? OldTime { get; private set; }
public TimeSpan? Time { get => GetTime?.Invoke(); set => SetTime?.Invoke(value ?? TimeSpan.Zero); }
public TimeBoostsUI(Mobile user, Gump parent = null, TimeBoostProfile profile = null, Func<ITimeBoost, bool> canApply = null, Action<ITimeBoost> boostUsed = null, Func<TimeSpan> getTime = null, Action<TimeSpan> setTime = null)
: base(user, parent)
{
Profile = profile;
CanApply = canApply;
BoostUsed = boostUsed;
GetTime = getTime;
SetTime = setTime;
Title = DefaultTitle;
SubTitle = DefaultSubTitle;
EmptyText = DefaultEmptyText;
SummaryText = DefaultSummaryText;
HelpText = DefaultHelpText;
TitleColor = DefaultTitleColor;
SubTitleColor = DefaultSubTitleColor;
EmptyTextColor = DefaultEmptyTextColor;
BoostTextColor = DefaultBoostTextColor;
BoostCountColor = DefaultBoostCountColor;
SummaryTextColor = DefaultSummaryTextColor;
HelpTextColor = DefaultHelpTextColor;
ApplyAutoAllowed = true;
EntriesPerPage = 4;
Sorted = true;
CanClose = true;
CanDispose = true;
CanMove = true;
CanResize = false;
AutoRefreshRate = TimeSpan.FromMinutes(1.0);
AutoRefresh = true;
ForceRecompile = true;
}
protected override void Compile()
{
if (Profile == null && User != null)
Profile = TimeBoosts.EnsureProfile(User.Account);
if (!ApplyAutoAllowed)
ApplyAutoPreview = false;
base.Compile();
}
protected override void CompileList(List<ITimeBoost> list)
{
list.Clear();
var boosts = TimeBoosts.AllTimes.Where(b => Profile == null || Profile[b] > 0);
if (ApplyAutoAllowed && ApplyAutoPreview)
{
var time = Time?.Ticks ?? 0;
if (time > 0)
{
foreach (var boost in boosts.OrderByDescending(b => b.Value.Ticks))
{
if (boost.Value.Ticks > time)
break;
var take = Math.Min(Profile[boost], (int)(time / boost.Value.Ticks));
if (take <= 0)
break;
time -= take * boost.Value.Ticks;
list.Add(new BoostPreview(boost, take));
}
}
}
else
list.AddRange(boosts);
base.CompileList(list);
}
protected override void CompileLayout(SuperGumpLayout layout)
{
base.CompileLayout(layout);
layout.Add("background", () =>
{
AddBackground(0, 0, 480, 225, 9270);
AddBackground(15, 40, 450, 140, 9350);
});
layout.Add("title", () =>
{
if (String.IsNullOrWhiteSpace(Title))
return;
var text = Title;
text = text.ToUpper();
text = text.WrapUOHtmlBold();
text = text.WrapUOHtmlColor(TitleColor, false);
AddHtml(20, 15, 235, 40, text, false, false);
});
layout.Add("subtitle", () =>
{
if (String.IsNullOrWhiteSpace(SubTitle))
return;
var text = SubTitle;
text = text.ToUpper();
text = text.WrapUOHtmlBold();
text = text.WrapUOHtmlColor(SubTitleColor, false);
text = text.WrapUOHtmlRight();
AddHtml(255, 15, 200, 40, text, false, false);
});
layout.Add("page/prev", () =>
{
if (HasPrevPage)
AddButton(7, 150, 9910, 9911, PreviousPage);
else
AddImage(7, 150, 9909);
});
layout.Add("page/next", () =>
{
if (HasNextPage)
AddButton(451, 150, 9904, 9905, NextPage);
else
AddImage(451, 150, 9903);
});
CompileEntryLayout(layout, GetListRange());
layout.Add("summary", () =>
{
TimeSpan time;
if (ApplyAutoPreview)
time = TimeSpan.FromTicks(List.OfType<BoostPreview>().Aggregate(0L, (t, b) => t + (b.Value.Ticks * b.Amount)));
else
time = Time ?? TimeSpan.Zero;
if (time < TimeSpan.Zero)
time = TimeSpan.Zero;
var text = SummaryText;
if (ApplyAutoPreview)
text = "Total Time";
text = text.ToUpper();
text = text.WrapUOHtmlBold();
text = text.WrapUOHtmlColor(SummaryTextColor, false);
text = text.WrapUOHtmlCenter();
AddHtml(20, 192, 87, 40, text, false, false);
AddImage(112, 185, 30223);
if (ApplyAutoPreview)
AddImageTime(140, 185, time, 0x55); // 175 x 28
else
AddImageTime(140, 185, time, 0x33); // 175 x 28
AddImage(315, 185, 30223);
});
layout.Add("auto", () =>
{
if (ApplyAutoPreview)
AddButton(350, 187, 2124, 2123, b => ApplyAuto(false));
else if (ApplyAutoAllowed)
AddButton(350, 187, 2113, 2112, b => ApplyAuto(true));
else
AddImage(350, 187, 2113, 900);
});
layout.Add("okay", () =>
{
if (ApplyAutoPreview)
AddButton(410, 187, 2121, 2120, b => ApplyAuto(null));
else
AddButton(410, 187, 2130, 2129, Close);
});
layout.Add("tip", () =>
{
AddBackground(0, 225, 480, 40, 9270);
var text = HelpText;
if (ApplyAutoPreview)
text = "Automatically use boosts as displayed above.";
text = text.WrapUOHtmlSmall();
text = text.WrapUOHtmlCenter();
text = text.WrapUOHtmlColor(HelpTextColor);
AddHtml(20, 235, 440, 40, text, false, false);
});
}
protected virtual void CompileEntryLayout(SuperGumpLayout layout, Dictionary<int, ITimeBoost> range)
{
if (range.Count > 0)
{
var i = 0;
foreach (var kv in range)
{
CompileEntryLayout(layout, range.Count, kv.Key, i, 53 + (i * 93), kv.Value);
++i;
}
}
else
{
layout.Add("empty", () =>
{
var text = EmptyText;
text = text.WrapUOHtmlColor(EmptyTextColor, false);
text = text.WrapUOHtmlCenter();
AddHtml(35, 150, 355, 40, text, false, false);
});
}
}
protected virtual void CompileEntryLayout(SuperGumpLayout layout, int length, int index, int pIndex, int xOffset, ITimeBoost boost)
{
layout.Add("boosts/" + index, () =>
{
BoostPreview? bp = null;
if (boost is BoostPreview p)
bp = p;
int hue = boost.Hue, bellID = 10850;
var text = String.Empty;
if (boost.Value.Hours != 0)
{
bellID = 10810; //Blue Gem
text = "Hour";
}
else if (boost.Value.Minutes != 0)
{
bellID = 10830; //Green Gem
text = "Min";
}
else
hue = 2999;
if (!String.IsNullOrWhiteSpace(text) && boost.RawValue != 1)
text += "s";
if (hue == 2999)
{
AddImage(xOffset + 7, 56, bellID, hue); //Left Bell
AddImage(xOffset + 57, 56, bellID, hue); //Right Bell
}
else
{
AddImage(xOffset + 7, 56, bellID); //Left Bell
AddImage(xOffset + 57, 56, bellID); //Right Bell
}
AddImage(xOffset, 45, 30058, hue); //Hammer
if (Profile != null && bp == null)
AddButton(xOffset + 5, 65, 1417, 1417, b => SelectBoost(boost)); //Disc
AddImage(xOffset + 5, 65, 1417, hue); //Disc
AddImage(xOffset + 14, 75, 5577, 2999); //Blackout
AddImageNumber(xOffset + 44, 99, boost.RawValue, hue, Axis.Both);
if (!String.IsNullOrWhiteSpace(text))
{
text = text.WrapUOHtmlSmall();
text = text.WrapUOHtmlCenter();
text = text.WrapUOHtmlColor(BoostTextColor, false);
AddHtml(xOffset + 20, 110, 50, 40, text, false, false);
}
if (hue == 2999)
AddImage(xOffset, 130, 30062, hue); //Feet
else
{
if (Profile != null)
AddBackground(xOffset + 10, 143, 70, 30, 9270); //9300
AddImage(xOffset, 130, 30062); //Feet
if (Profile != null)
{
text = $"{(bp?.Amount ?? Profile[boost]):N0}";
text = text.WrapUOHtmlBold();
text = text.WrapUOHtmlCenter();
text = text.WrapUOHtmlColor(BoostCountColor, false);
AddHtml(xOffset + 12, 150, 65, 40, text, false, false);
}
}
});
}
public virtual void ApplyAuto(bool? state)
{
if (!ApplyAutoAllowed || state == null)
ApplyAutoPreview = false;
else if (state == true)
ApplyAutoPreview = true;
else if (ApplyAutoPreview)
{
foreach (var bp in List.OfType<BoostPreview>())
{
var amount = bp.Amount;
while (--amount >= 0 && (IsOpen || Hidden))
{
if (ApplyBoost(bp.Boost))
{
OnBoostUsed(bp.Boost);
if (IsOpen || Hidden)
continue;
}
break;
}
if (!IsOpen && !Hidden)
break;
}
ApplyAutoPreview = false;
}
if (IsOpen || Hidden)
Refresh(true);
}
public virtual void SelectBoost(ITimeBoost boost)
{
if (CanApplyBoost(boost))
ApplyBoost(boost, boost.Value.TotalHours >= 6);
else
Refresh(true);
}
public virtual void ApplyBoost(ITimeBoost boost, bool confirm)
{
if (CanApplyBoost(boost))
{
if (confirm)
{
new ConfirmDialogGump(User, Hide(true))
{
Icon = 7057,
Title = $"Use {boost.Name}?",
Html = $"{Title}: {SubTitle}\n{boost.RawValue} {boost.Desc}{(boost.RawValue != 1 ? "s" : String.Empty)}.\nClick OK to apply this Time Boost.",
AcceptHandler = b => ApplyBoost(boost, false),
CancelHandler = b => Refresh(true)
}.Send();
return;
}
if (ApplyBoost(boost))
OnBoostUsed(boost);
}
if (IsOpen || Hidden)
Refresh(true);
}
public virtual bool ApplyBoost(ITimeBoost boost)
{
if (!CanApplyBoost(boost) || !Profile.Consume(boost, 1))
return false;
OldTime = Time;
Time -= boost.Value;
return true;
}
protected virtual bool CanApplyBoost(ITimeBoost boost)
{
return GetTime != null && SetTime != null && Time > TimeSpan.Zero && boost != null && boost.RawValue > 0 &&
Profile != null && Profile.Owner == User.Account && (CanApply == null || CanApply(boost));
}
protected virtual void OnBoostUsed(ITimeBoost boost)
{
BoostUsed?.Invoke(boost);
LogBoostUsed(boost);
}
protected virtual void LogBoostUsed(ITimeBoost boost)
{
var log = new StringBuilder();
log.AppendLine();
log.AppendLine("UI: '{0}' : '{1}' : '{2}'", Title, SubTitle, SummaryText);
log.AppendLine("User: {0}", User);
log.AppendLine("Boost: {0}", boost);
log.AppendLine("Get: {0}", GetTime.Trace());
log.AppendLine("Set: {0}", SetTime.Trace());
log.AppendLine("Time: {0} > {1}", OldTime, Time);
log.AppendLine();
log.Log($"/TimeBoosts/{DateTime.Now.ToDirectoryName()}/{boost}.log");
}
public override int SortCompare(ITimeBoost a, ITimeBoost b)
{
var res = 0;
if (a.CompareNull(b, ref res))
return res;
if (a.Value < b.Value)
return -1;
if (a.Value > b.Value)
return 1;
return 0;
}
protected override void OnDisposed()
{
base.OnDisposed();
Profile = null;
}
protected struct BoostPreview : ITimeBoost
{
public readonly ITimeBoost Boost;
public readonly int Amount;
public int RawValue => Boost.RawValue;
public TimeSpan Value => Boost.Value;
public string Desc => Boost.Desc;
public string Name => Boost.Name;
public int Hue => Boost.Hue;
public BoostPreview(ITimeBoost boost, int amount)
{
Boost = boost;
Amount = amount;
}
}
}
}

View File

@@ -0,0 +1,27 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System.Net;
#endregion
namespace VitaNex.Web
{
public delegate void WebAPIContextHandler(WebAPIContext context);
public delegate void WebAPIClientConnected(WebAPIClient client);
public delegate void WebAPIClientDisconnected(WebAPIClient client);
public delegate void WebAPIRequestSend<in T>(HttpWebRequest req, T state);
public delegate void WebAPIRequestReceive<in T>(HttpWebRequest req, T state, HttpWebResponse res);
}

View File

@@ -0,0 +1,411 @@
#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.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Sockets;
using System.Text;
#endregion
namespace VitaNex.Web
{
public sealed class WebAPIClient : IDisposable
{
private static readonly char[] _Separators = { '\r', '\n' };
private static readonly byte[] _EmptyBuffer = new byte[0];
private static readonly KeyValueString[] _EmptyHeaders = new KeyValueString[0];
private static IEnumerable<KeyValueString> ParseHeaders(IEnumerable<string> headers)
{
int i;
foreach (var h in headers)
{
i = h.IndexOf(' ');
if (i > 0)
{
var k = h.Substring(0, i).Trim().TrimEnd(':');
var v = h.Substring(i + 1);
yield return new KeyValueString(k, v);
}
}
}
public TcpClient Client { get; private set; }
public NetworkStream Stream { get; private set; }
public bool Connected => Client != null && Client.Connected;
public bool IsDisposed { get; private set; }
public WebAPIClient(TcpClient client)
{
Client = client;
Stream = Client.GetStream();
}
public void Encode(Encoding enc, string data, out byte[] buffer, out int length)
{
buffer = enc.GetBytes(data ?? String.Empty);
length = buffer.Length;
}
public void Decode(Encoding enc, byte[] buffer, int length, out string data)
{
if (buffer == null || buffer.Length == 0 || length <= 0)
{
data = String.Empty;
return;
}
data = enc.GetString(buffer, 0, length);
}
public void Compress(ref byte[] buffer, ref int length)
{
using (var outS = new MemoryStream())
{
using (var ds = new DeflateStream(outS, CompressionMode.Compress, true))
{
ds.Write(buffer, 0, length);
}
outS.Position = 0;
// Recycle the buffer?
if (outS.Length <= buffer.Length)
{
length = outS.Read(buffer, 0, buffer.Length);
// Heartbleed: Nope; zero-fill the remaining buffer!
for (var i = length; i < buffer.Length; i++)
{
buffer[i] = 0;
}
}
else
{
buffer = outS.ToArray();
length = buffer.Length;
}
}
}
public void Decompress(ref byte[] buffer, ref int length)
{
using (MemoryStream inS = new MemoryStream(buffer, 0, length), outS = new MemoryStream())
{
using (var ds = new DeflateStream(inS, CompressionMode.Decompress, true))
{
ds.CopyTo(outS);
}
outS.Position = 0;
// Recycle the buffer?
if (outS.Length <= buffer.Length)
{
length = outS.Read(buffer, 0, buffer.Length);
// Heartbleed: Nope; zero-fill the remaining buffer!
for (var i = length; i < buffer.Length; i++)
{
buffer[i] = 0;
}
}
else
{
buffer = outS.ToArray();
length = buffer.Length;
}
}
}
public int Send(bool compress, string data, Encoding enc)
{
Send(compress, data, enc, out var buffer, out var length);
return length;
}
public void Send(bool compress, string data, Encoding enc, out byte[] buffer, out int length)
{
Encode(enc, data, out buffer, out length);
Send(compress, ref buffer, ref length);
}
public void Send(bool compress, ref byte[] buffer, ref int length)
{
if (compress)
{
Compress(ref buffer, ref length);
}
if (buffer.Length > Client.SendBufferSize)
{
Client.SendBufferSize = Math.Min(1048576, buffer.Length);
}
Stream.Write(buffer, 0, length);
WebAPI.CSOptions.ToConsole(
"Sent {0:#,0} bytes ({1:#,0} bytes/write)",
length,
Math.Min(length, Client.SendBufferSize));
}
private static bool Sequence(byte b, ref int seq)
{
switch (b)
{
case 13:
{
if (seq % 2 == 0)
{
++seq;
}
else
{
seq = 0;
}
}
break;
case 10:
{
if (seq % 2 == 1)
{
++seq;
}
else
{
seq = 0;
}
}
break;
default:
seq = 0;
break;
}
return seq > 0;
}
public bool ReceiveHeaders(out KeyValueString[] headers)
{
headers = _EmptyHeaders;
var buffer = _EmptyBuffer;
var length = 0;
if (Stream.CanRead)
{
VitaNexCore.WaitWhile(() => !Stream.DataAvailable, TimeSpan.FromMilliseconds(1000));
if (Stream.DataAvailable)
{
buffer = new byte[Client.ReceiveBufferSize];
using (var ms = new MemoryStream())
{
int idx = 0, seq = 0;
while (Stream.DataAvailable)
{
var r = Stream.ReadByte();
if (r > -1)
{
if (++length > WebAPI.CSOptions.MaxReceiveBufferSizeBytes)
{
throw new InternalBufferOverflowException(
String.Format("Received data exceeded {0:#,0} bytes", WebAPI.CSOptions.MaxReceiveBufferSizeBytes));
}
var b = (byte)r;
buffer[idx++] = b;
if (Sequence(b, ref seq) && seq >= 4)
{
break;
}
if (idx >= buffer.Length)
{
ms.Write(buffer, 0, idx);
idx = 0;
}
}
}
if (idx > 0)
{
ms.Write(buffer, 0, idx);
}
buffer = ms.ToArray();
length = buffer.Length;
}
}
}
WebAPI.CSOptions.ToConsole(
"Received {0:#,0} bytes ({1:#,0} bytes/read)",
length,
Math.Min(length, Client.ReceiveBufferSize));
if (length <= 0)
{
return false;
}
var raw = Encoding.ASCII.GetString(buffer, 0, length);
if (String.IsNullOrWhiteSpace(raw))
{
return false;
}
var h = raw.Split(_Separators, StringSplitOptions.RemoveEmptyEntries);
if (h.Length == 0)
{
return false;
}
headers = ParseHeaders(h).ToArray();
return headers.Length > 0;
}
public void Receive(bool decompress, Encoding enc, out string content, out byte[] buffer, out int length)
{
content = String.Empty;
buffer = _EmptyBuffer;
length = 0;
if (Stream.CanRead)
{
VitaNexCore.WaitWhile(() => !Stream.DataAvailable, TimeSpan.FromMilliseconds(1000));
if (Stream.DataAvailable)
{
buffer = new byte[Client.ReceiveBufferSize];
using (var ms = new MemoryStream())
{
while (Stream.DataAvailable)
{
length = Stream.Read(buffer, 0, buffer.Length);
if (length > 0)
{
if (ms.Length + length > WebAPI.CSOptions.MaxReceiveBufferSizeBytes)
{
throw new InternalBufferOverflowException(
String.Format("Received data exceeded {0:#,0} bytes", WebAPI.CSOptions.MaxReceiveBufferSizeBytes));
}
ms.SetLength(ms.Length + length);
ms.Write(buffer, 0, length);
}
}
buffer = ms.ToArray();
length = buffer.Length;
}
}
}
WebAPI.CSOptions.ToConsole(
"Received {0:#,0} bytes ({1:#,0} bytes/read)",
length,
Math.Min(length, Client.ReceiveBufferSize));
if (length > 0)
{
if (decompress)
{
Decompress(ref buffer, ref length);
}
Decode(enc, buffer, length, out content);
}
}
public void Close()
{
Close(false);
}
public void Close(bool disconnecting)
{
if (IsDisposed || !Client.Connected)
{
return;
}
VitaNexCore.TryCatch(
() =>
{
if (!disconnecting)
{
WebAPI.Disconnect(this);
}
if (Stream != null)
{
using (Stream)
{
Stream.Close();
}
}
if (Client != null)
{
Client.Close();
}
},
WebAPI.CSOptions.ToConsole);
}
public void Dispose()
{
if (IsDisposed)
{
return;
}
Close(false);
IsDisposed = true;
Client = null;
}
public override string ToString()
{
if (Client == null || Client.Client == null || Client.Client.RemoteEndPoint == null)
{
return "?.?.?.?:?";
}
return Client.Client.RemoteEndPoint.ToString();
}
}
}

View File

@@ -0,0 +1,55 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.Web
{
public class WebAPIContext : IDisposable
{
public WebAPIClient Client { get; private set; }
public WebAPIMethod Method { get; private set; }
public string Uri { get; private set; }
public WebAPIRequest Request { get; private set; }
public WebAPIResponse Response { get; private set; }
public bool Authorized { get; set; }
public WebAPIContext(WebAPIClient client, WebAPIMethod method, string uri)
{
Client = client;
Method = method;
Uri = uri;
Request = new WebAPIRequest(Client);
Response = new WebAPIResponse(Client);
}
public void Dispose()
{
Client = null;
Method = WebAPIMethod.UNKNOWN;
Uri = null;
Request.Dispose();
Request = null;
Response.Dispose();
Response = null;
}
}
}

View File

@@ -0,0 +1,35 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.Web
{
public class WebAPIHandler
{
public string Uri { get; private set; }
public Action<WebAPIContext> Handler { get; set; }
public WebAPIHandler(string uri, Action<WebAPIContext> handler)
{
Uri = uri;
Handler = handler;
}
public override int GetHashCode()
{
return Uri.GetHashCode();
}
}
}

View File

@@ -0,0 +1,82 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
#endregion
namespace VitaNex.Web
{
public class WebAPIHeaders : IDisposable, IEnumerable<KeyValuePair<string, string>>
{
private Dictionary<string, string> _Headers;
public string this[string header]
{
get => _Headers.GetValue(header);
set
{
if (value == null)
{
_Headers.Remove(header);
}
else
{
_Headers[header] = value;
}
}
}
public int Count => _Headers.Count;
public WebAPIHeaders()
{
_Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
public WebAPIHeaders(IEnumerable<KeyValuePair<string, string>> headers)
: this()
{
foreach (var kv in headers.Ensure())
{
this[kv.Key] = kv.Value;
}
}
public void Clear()
{
_Headers.Clear();
}
public void Dispose()
{
_Headers = null;
}
public override string ToString()
{
return String.Join("\r\n", _Headers.Select(kv => String.Format("{0}: {1}", kv.Key, kv.Value))) + "\r\n\r\n";
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return _Headers.GetEnumerator();
}
}
}

View File

@@ -0,0 +1,26 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
namespace VitaNex.Web
{
public enum WebAPIMethod
{
UNKNOWN = 0,
OPTIONS,
GET,
HEAD,
POST,
PUT,
DELETE,
TRACE,
CONNECT
}
}

View File

@@ -0,0 +1,85 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections;
using System.Collections.Generic;
#endregion
namespace VitaNex.Web
{
public class WebAPIQueries : IDisposable, IEnumerable<KeyValuePair<string, string>>
{
private Dictionary<string, string> _Queries;
public string this[string query]
{
get => _Queries.GetValue(query);
set
{
if (value == null)
{
_Queries.Remove(query);
}
else
{
_Queries[query] = value;
}
}
}
public int Count => _Queries.Count;
public WebAPIQueries()
{
_Queries = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
public WebAPIQueries(IEnumerable<KeyValuePair<string, string>> queries)
: this()
{
foreach (var kv in queries.Ensure())
{
this[kv.Key] = kv.Value;
}
}
public WebAPIQueries(string query)
: this(WebAPI.DecodeQuery(query))
{ }
public void Clear()
{
_Queries.Clear();
}
public void Dispose()
{
_Queries = null;
}
public override string ToString()
{
return WebAPI.EncodeQuery(_Queries);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return _Queries.GetEnumerator();
}
}
}

View File

@@ -0,0 +1,63 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Text;
using VitaNex.IO;
#endregion
namespace VitaNex.Web
{
public class WebAPIRequest : IDisposable
{
public WebAPIClient Client { get; private set; }
public WebAPIHeaders Headers { get; private set; }
public WebAPIQueries Queries { get; private set; }
public FileMime ContentType { get; set; }
public Encoding Encoding { get; set; }
public string Data { get; set; }
public int Length { get; set; }
public WebAPIRequest(WebAPIClient client)
{
Client = client;
Headers = new WebAPIHeaders();
Queries = new WebAPIQueries();
ContentType = FileMime.Default;
Encoding = Encoding.UTF8;
Data = String.Empty;
Length = 0;
}
public void Dispose()
{
Headers.Dispose();
Headers = null;
Queries.Dispose();
Queries = null;
Encoding = null;
Data = null;
Length = 0;
Client = null;
}
}
}

View File

@@ -0,0 +1,74 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Net;
using System.Text;
using VitaNex.IO;
#endregion
namespace VitaNex.Web
{
public class WebAPIResponse : IDisposable
{
public WebAPIClient Client { get; private set; }
public WebAPIHeaders Headers { get; private set; }
public HttpStatusCode Status { get; set; }
public FileMime ContentType { get; set; }
public Encoding Encoding { get; set; }
public object Data { get; set; }
public bool Compress { get; set; }
public bool FreeData { get; set; }
public string FileName { get; set; }
public int Cache { get; set; }
public WebAPIResponse(WebAPIClient client)
{
Client = client;
Headers = new WebAPIHeaders();
Compress = false;
FreeData = true;
Cache = -1;
FileName = String.Empty;
Encoding = Encoding.UTF8;
Status = HttpStatusCode.OK;
ContentType = FileMime.Default;
}
public void Dispose()
{
if (FreeData && Data is IDisposable)
{
((IDisposable)Data).Dispose();
}
Data = null;
Headers.Dispose();
Headers = null;
Cache = -1;
FileName = "public.html";
Encoding = null;
Client = null;
}
}
}

View File

@@ -0,0 +1,194 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System.Collections.Generic;
using Server;
#endregion
namespace VitaNex.Web
{
public class WebAPIOptions : CoreServiceOptions
{
public List<string> Whitelist { get; private set; }
public List<string> Blacklist { get; private set; }
[CommandProperty(WebAPI.Access)]
public bool UseWhitelist { get; set; }
[CommandProperty(WebAPI.Access)]
public short Port { get; set; }
[CommandProperty(WebAPI.Access)]
public int MaxConnections { get; set; }
[CommandProperty(WebAPI.Access)]
public int MaxSendBufferSize { get; set; }
[CommandProperty(WebAPI.Access)]
public int MaxReceiveBufferSize { get; set; }
[CommandProperty(WebAPI.Access)]
public int MaxSendBufferSizeBytes => MaxSendBufferSize * 1024 * 1024;
[CommandProperty(WebAPI.Access)]
public int MaxReceiveBufferSizeBytes => MaxReceiveBufferSize * 1024 * 1024;
[CommandProperty(WebAPI.Access)]
public bool DirectoryIndex { get; set; }
[CommandProperty(WebAPI.Access)]
public bool WebServer { get; set; }
public WebAPIOptions()
: base(typeof(WebAPI))
{
Whitelist = new List<string>();
Blacklist = new List<string>();
Port = 2595;
MaxConnections = 500;
MaxSendBufferSize = 32;
MaxReceiveBufferSize = 32;
UseWhitelist = false;
WebServer = true;
DirectoryIndex = true;
}
public WebAPIOptions(GenericReader reader)
: base(reader)
{ }
public override void Clear()
{
base.Clear();
Whitelist.Clear();
Blacklist.Clear();
Port = 80;
MaxConnections = 500;
MaxSendBufferSize = 32;
MaxReceiveBufferSize = 32;
UseWhitelist = false;
WebServer = false;
DirectoryIndex = false;
}
public override void Reset()
{
base.Reset();
Whitelist.Clear();
Blacklist.Clear();
Port = 80;
MaxConnections = 500;
MaxSendBufferSize = 32;
MaxReceiveBufferSize = 32;
UseWhitelist = false;
WebServer = false;
DirectoryIndex = false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
var version = writer.SetVersion(4);
switch (version)
{
case 4:
{
writer.Write(WebServer);
writer.Write(DirectoryIndex);
}
goto case 3;
case 3:
case 2:
{
writer.Write(MaxSendBufferSize);
writer.Write(MaxReceiveBufferSize);
}
goto case 1;
case 1:
{
writer.WriteList(Whitelist, (w, m) => w.Write(m));
writer.WriteList(Blacklist, (w, m) => w.Write(m));
writer.Write(UseWhitelist);
}
goto case 0;
case 0:
{
writer.Write(Port);
writer.Write(MaxConnections);
}
break;
}
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
var version = reader.GetVersion();
if (version < 3)
{
MaxSendBufferSize = 32;
MaxReceiveBufferSize = 32;
}
if (version < 2)
{
Whitelist = new List<string>();
Blacklist = new List<string>();
}
switch (version)
{
case 4:
{
WebServer = reader.ReadBool();
DirectoryIndex = reader.ReadBool();
}
goto case 3;
case 3:
case 2:
{
MaxSendBufferSize = reader.ReadInt();
MaxReceiveBufferSize = reader.ReadInt();
}
goto case 1;
case 1:
{
Whitelist = reader.ReadList(r => r.ReadString(), Whitelist);
Blacklist = reader.ReadList(r => r.ReadString(), Blacklist);
UseWhitelist = reader.ReadBool();
}
goto case 0;
case 0:
{
Port = reader.ReadShort();
MaxConnections = reader.ReadInt();
}
break;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,100 @@
#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.Net;
using System.Reflection;
using Server;
using Server.Misc;
#endregion
namespace VitaNex.Web
{
[CoreService("Web API", "3.0.0.1")]
public static partial class WebAPI
{
static WebAPI()
{
CSOptions = new WebAPIOptions();
Clients = new List<WebAPIClient>();
Handlers = new Dictionary<string, WebAPIHandler>(StringComparer.OrdinalIgnoreCase);
_ActivityTimer = PollTimer.FromSeconds(
60.0,
() =>
{
if (!_Listening || Listener == null || Listener.Server == null || !Listener.Server.IsBound)
{
_Listening = false;
ListenerUtility.ListenAsync();
}
Clients.RemoveAll(c => !c.Connected);
},
() => Clients.Count > 0,
false);
ServicePointManager.SecurityProtocol = (SecurityProtocolType)0x3FC0; // Tls, Tls11, Tls12, Tls13
}
private static void CSConfig()
{
if (_ServerStarted)
{
ListenerUtility.ListenAsync();
return;
}
EventSink.ServerStarted += () =>
{
_ServerStarted = true;
ListenerUtility.ListenAsync();
};
var t = typeof(StatusPage);
var f = t.GetField("Enabled", BindingFlags.Public | BindingFlags.Static);
if (f != null)
{
f.SetValue(null, false);
}
}
private static void CSInvoke()
{
_ActivityTimer.Start();
Register("/", HandleRoot);
}
private static void CSDisposed()
{
Clients.ForEachReverse(c => c.Close());
_ActivityTimer.Stop();
_ActivityTimer = null;
if (Listener == null)
{
return;
}
Listener.Stop();
Listener = null;
}
}
}