Reorganizes Project (#41)

This commit is contained in:
Kamron Batman 2019-08-02 18:13:40 -07:00 committed by GitHub
parent 08bf44af9a
commit 3614a66aee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3499 changed files with 79 additions and 55 deletions

View file

@ -0,0 +1,137 @@
using System.Collections.Generic;
namespace Server.Commands.Generic
{
public enum ObjectTypes
{
Both,
Items,
Mobiles,
All
}
public abstract class BaseCommand
{
private List<MessageEntry> m_Responses = new List<MessageEntry>();
private List<MessageEntry> m_Failures = new List<MessageEntry>();
public bool ListOptimized{ get; set; }
public string[] Commands{ get; set; }
public string Usage{ get; set; }
public string Description{ get; set; }
public AccessLevel AccessLevel{ get; set; }
public ObjectTypes ObjectTypes{ get; set; }
public CommandSupport Supports{ get; set; }
public static bool IsAccessible(Mobile from, object obj)
{
if (from.AccessLevel >= AccessLevel.Administrator || obj == null)
return true;
Mobile mob = null;
if (obj is Mobile m)
mob = m;
else if (obj is Item item)
mob = item.RootParent as Mobile;
return mob == null || mob == from || from.AccessLevel > mob.AccessLevel;
}
public virtual void ExecuteList(CommandEventArgs e, List<object> list)
{
for (int i = 0; i < list.Count; ++i)
Execute(e, list[i]);
}
public virtual void Execute(CommandEventArgs e, object obj)
{
}
public virtual bool ValidateArgs(BaseCommandImplementor impl, CommandEventArgs e)
{
return true;
}
public void AddResponse(string message)
{
for (int i = 0; i < m_Responses.Count; ++i)
{
MessageEntry entry = m_Responses[i];
if (entry.m_Message == message)
{
++entry.m_Count;
return;
}
}
if (m_Responses.Count == 10)
return;
m_Responses.Add(new MessageEntry(message));
}
public void LogFailure(string message)
{
for (int i = 0; i < m_Failures.Count; ++i)
{
MessageEntry entry = m_Failures[i];
if (entry.m_Message == message)
{
++entry.m_Count;
return;
}
}
if (m_Failures.Count == 10)
return;
m_Failures.Add(new MessageEntry(message));
}
public void Flush(Mobile from, bool flushToLog)
{
if (m_Responses.Count > 0)
for (int i = 0; i < m_Responses.Count; ++i)
{
MessageEntry entry = m_Responses[i];
from.SendMessage(entry.ToString());
if (flushToLog)
CommandLogging.WriteLine(from, entry.ToString());
}
else
for (int i = 0; i < m_Failures.Count; ++i)
from.SendMessage(m_Failures[i].ToString());
m_Responses.Clear();
m_Failures.Clear();
}
private class MessageEntry
{
public int m_Count;
public string m_Message;
public MessageEntry(string message)
{
m_Message = message;
m_Count = 1;
}
public override string ToString()
{
return m_Count > 1 ? $"{m_Message} ({m_Count})" : m_Message;
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,203 @@
using System.Collections.Generic;
using Server.Gumps;
using Server.Items;
using Server.Multis;
using Server.Targeting;
namespace Server.Commands.Generic
{
public class DesignInsertCommand : BaseCommand
{
public enum DesignInsertResult
{
Valid,
InvalidItem,
NotInHouse,
OutsideHouseBounds
}
public DesignInsertCommand()
{
AccessLevel = AccessLevel.GameMaster;
Supports = CommandSupport.Single | CommandSupport.Area;
Commands = new[] { "DesignInsert" };
ObjectTypes = ObjectTypes.Items;
Usage = "DesignInsert [allItems=false]";
Description = "Inserts multiple targeted items into a customizable house's design.";
}
public static void Initialize()
{
TargetCommands.Register(new DesignInsertCommand());
}
public static DesignInsertResult ProcessInsert(Item item, bool staticsOnly, out HouseFoundation house)
{
house = null;
if (item == null || item is BaseMulti || item is HouseSign || staticsOnly && !(item is Static))
return DesignInsertResult.InvalidItem;
house = BaseHouse.FindHouseAt(item) as HouseFoundation;
if (house == null)
return DesignInsertResult.NotInHouse;
int x = item.X - house.X;
int y = item.Y - house.Y;
int z = item.Z - house.Z;
if (!TryInsertIntoState(house.CurrentState, item.ItemID, x, y, z))
return DesignInsertResult.OutsideHouseBounds;
TryInsertIntoState(house.DesignState, item.ItemID, x, y, z);
item.Delete();
return DesignInsertResult.Valid;
}
private static bool TryInsertIntoState(DesignState state, int itemID, int x, int y, int z)
{
MultiComponentList mcl = state.Components;
if (x < mcl.Min.X || y < mcl.Min.Y || x > mcl.Max.X || y > mcl.Max.Y)
return false;
mcl.Add(itemID, x, y, z);
state.OnRevised();
return true;
}
#region Single targeting mode
public override void Execute(CommandEventArgs e, object obj)
{
Target t = new DesignInsertTarget(new List<HouseFoundation>(), e.Length < 1 || !e.GetBoolean(0));
t.Invoke(e.Mobile, obj);
}
private class DesignInsertTarget : Target
{
private List<HouseFoundation> m_Foundations;
private bool m_StaticsOnly;
public DesignInsertTarget(List<HouseFoundation> foundations, bool staticsOnly)
: base(-1, false, TargetFlags.None)
{
m_Foundations = foundations;
m_StaticsOnly = staticsOnly;
}
protected override void OnTargetCancel(Mobile from, TargetCancelType cancelType)
{
if (m_Foundations.Count != 0)
{
from.SendMessage("Your changes have been committed. Updating...");
foreach (HouseFoundation house in m_Foundations)
house.Delta(ItemDelta.Update);
}
}
protected override void OnTarget(Mobile from, object obj)
{
DesignInsertResult result = ProcessInsert(obj as Item, m_StaticsOnly, out HouseFoundation house);
switch (result)
{
case DesignInsertResult.Valid:
{
if (m_Foundations.Count == 0)
from.SendMessage(
"The item has been inserted into the house design. Press ESC when you are finished.");
else
from.SendMessage("The item has been inserted into the house design.");
if (!m_Foundations.Contains(house))
m_Foundations.Add(house);
break;
}
case DesignInsertResult.InvalidItem:
{
from.SendMessage("That cannot be inserted. Try again.");
break;
}
case DesignInsertResult.NotInHouse:
case DesignInsertResult.OutsideHouseBounds:
{
from.SendMessage("That item is not inside a customizable house. Try again.");
break;
}
}
from.Target = new DesignInsertTarget(m_Foundations, m_StaticsOnly);
}
}
#endregion
#region Area targeting mode
public override void ExecuteList(CommandEventArgs e, List<object> list)
{
Mobile from = e.Mobile;
from.SendGump(new WarningGump(1060637, 30720,
$"You are about to insert {list.Count} objects. This cannot be undone without a full server revert.<br><br>Continue?",
0xFFC000, 420, 280, okay => OnConfirmCallback(from, okay, list, e.Length < 1 || !e.GetBoolean(0))));
AddResponse("Awaiting confirmation...");
}
private void OnConfirmCallback(Mobile from, bool okay, List<object> list, bool staticsOnly)
{
bool flushToLog = false;
if (okay)
{
List<HouseFoundation> foundations = new List<HouseFoundation>();
flushToLog = list.Count > 20;
for (int i = 0; i < list.Count; ++i)
{
DesignInsertResult result = ProcessInsert(list[i] as Item, staticsOnly, out HouseFoundation house);
switch (result)
{
case DesignInsertResult.Valid:
{
AddResponse("The item has been inserted into the house design.");
if (!foundations.Contains(house))
foundations.Add(house);
break;
}
case DesignInsertResult.InvalidItem:
{
LogFailure("That cannot be inserted.");
break;
}
case DesignInsertResult.NotInHouse:
case DesignInsertResult.OutsideHouseBounds:
{
LogFailure("That item is not inside a customizable house.");
break;
}
}
}
foreach (HouseFoundation house in foundations)
house.Delta(ItemDelta.Update);
}
else
{
AddResponse("Command aborted.");
}
Flush(from, flushToLog);
}
#endregion
}
}

View file

@ -0,0 +1,568 @@
using System.Collections.Generic;
using System.Reflection;
using Server.Gumps;
using Server.Network;
using Server.Targets;
namespace Server.Commands.Generic
{
public class InterfaceCommand : BaseCommand
{
public InterfaceCommand()
{
AccessLevel = AccessLevel.GameMaster;
Supports = CommandSupport.Complex | CommandSupport.Simple;
Commands = new[] { "Interface" };
ObjectTypes = ObjectTypes.Both;
Usage = "Interface [view <properties ...>]";
Description = "Opens an interface to interact with matched objects. Generally used with condition arguments.";
ListOptimized = true;
}
public override void ExecuteList(CommandEventArgs e, List<object> list)
{
if (list.Count > 0)
{
List<string> columns = new List<string> { "Object" };
if (e.Length > 0)
{
int offset = 0;
if (Insensitive.Equals(e.GetString(0), "view"))
++offset;
while (offset < e.Length)
columns.Add(e.GetString(offset++));
}
e.Mobile.SendGump(new InterfaceGump(e.Mobile, columns.ToArray(), list, 0, null));
}
else
{
AddResponse("No matching objects found.");
}
}
}
public class InterfaceGump : BaseGridGump
{
private const int EntriesPerPage = 15;
private string[] m_Columns;
private Mobile m_From;
private List<object> m_List;
private int m_Page;
private object m_Select;
public InterfaceGump(Mobile from, string[] columns, List<object> list, int page, object select) : base(30, 30)
{
m_From = from;
m_Columns = columns;
m_List = list;
m_Page = page;
m_Select = select;
Render();
}
public void Render()
{
AddNewPage();
if (m_Page > 0)
AddEntryButton(20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight);
else
AddEntryHeader(20);
AddEntryHtml(40 + m_Columns.Length * 130 - 20 + (m_Columns.Length - 2) * OffsetSize, Center(
$"Page {m_Page + 1} of {(m_List.Count + EntriesPerPage - 1) / EntriesPerPage}"));
if ((m_Page + 1) * EntriesPerPage < m_List.Count)
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight);
else
AddEntryHeader(20);
if (m_Columns.Length > 1)
{
AddNewLine();
for (int i = 0; i < m_Columns.Length; ++i)
{
if (i > 0 && m_List.Count > 0)
{
object obj = m_List[0];
if (obj != null)
{
string failReason = null;
PropertyInfo[] chain = Properties.GetPropertyInfoChain(m_From, obj.GetType(), m_Columns[i],
PropertyAccess.Read, ref failReason);
if (chain?.Length > 0)
{
m_Columns[i] = "";
for (int j = 0; j < chain.Length; ++j)
{
if (j > 0)
m_Columns[i] += '.';
m_Columns[i] += chain[j].Name;
}
}
}
}
AddEntryHtml(130 + (i == 0 ? 40 : 0), m_Columns[i]);
}
AddEntryHeader(20);
}
for (int i = m_Page * EntriesPerPage, line = 0; line < EntriesPerPage && i < m_List.Count; ++i, ++line)
{
AddNewLine();
object obj = m_List[i];
bool isDeleted = false;
if (obj is Item item)
{
if (!(isDeleted = item.Deleted))
AddEntryHtml(40 + 130, item.GetType().Name);
}
else if (obj is Mobile mob)
{
if (!(isDeleted = mob.Deleted))
AddEntryHtml(40 + 130, mob.Name);
}
if (isDeleted)
{
AddEntryHtml(40 + 130, "(deleted)");
for (int j = 1; j < m_Columns.Length; ++j)
AddEntryHtml(130, "---");
AddEntryHeader(20);
}
else
{
for (int j = 1; j < m_Columns.Length; ++j)
{
object src = obj;
string value;
string failReason = "";
PropertyInfo[] chain = Properties.GetPropertyInfoChain(m_From, src.GetType(), m_Columns[j],
PropertyAccess.Read, ref failReason);
if (chain == null || chain.Length == 0)
{
value = "---";
}
else
{
PropertyInfo p = Properties.GetPropertyInfo(ref src, chain, ref failReason);
if (p == null)
value = "---";
else
value = PropertiesGump.ValueToString(src, p);
}
AddEntryHtml(130, value);
}
bool isSelected = m_Select != null && obj == m_Select;
AddEntryButton(20, isSelected ? 9762 : ArrowRightID1, isSelected ? 9763 : ArrowRightID2, 3 + i,
ArrowRightWidth, ArrowRightHeight);
}
}
FinishPage();
}
public override void OnResponse(NetState sender, RelayInfo info)
{
switch (info.ButtonID)
{
case 1:
{
if (m_Page > 0)
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page - 1, m_Select));
break;
}
case 2:
{
if ((m_Page + 1) * EntriesPerPage < m_List.Count)
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page + 1, m_Select));
break;
}
default:
{
int v = info.ButtonID - 3;
if (v >= 0 && v < m_List.Count)
{
object obj = m_List[v];
if (!BaseCommand.IsAccessible(m_From, obj))
{
m_From.SendLocalizedMessage(500447); // That is not accessible.
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Select));
break;
}
if (obj is Item item && !item.Deleted)
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, item));
else if (obj is Mobile mobile && !mobile.Deleted)
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, mobile));
else
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Select));
}
break;
}
}
}
}
public class InterfaceItemGump : BaseGridGump
{
private string[] m_Columns;
private Mobile m_From;
private Item m_Item;
private List<object> m_List;
private int m_Page;
public InterfaceItemGump(Mobile from, string[] columns, List<object> list, int page, Item item) : base(30, 30)
{
m_From = from;
m_Columns = columns;
m_List = list;
m_Page = page;
m_Item = item;
Render();
}
public void Render()
{
AddNewPage();
AddEntryButton(20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight);
AddEntryHtml(160, m_Item.GetType().Name);
AddEntryHeader(20);
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Properties");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight);
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Delete");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 3, ArrowRightWidth, ArrowRightHeight);
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Go there");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 4, ArrowRightWidth, ArrowRightHeight);
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Move to target");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 5, ArrowRightWidth, ArrowRightHeight);
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Bring to pack");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 6, ArrowRightWidth, ArrowRightHeight);
FinishPage();
}
private void InvokeCommand(string ip)
{
CommandSystem.Handle(m_From, $"{CommandSystem.Prefix}{ip}");
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (m_Item.Deleted)
{
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Item));
return;
}
if (!BaseCommand.IsAccessible(m_From, m_Item))
{
m_From.SendMessage("That is no longer accessible.");
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Item));
return;
}
switch (info.ButtonID)
{
case 0:
case 1:
{
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Item));
break;
}
case 2: // Properties
{
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, m_Item));
m_From.SendGump(new PropertiesGump(m_From, m_Item));
break;
}
case 3: // Delete
{
CommandLogging.WriteLine(m_From, "{0} {1} deleting {2}", m_From.AccessLevel,
CommandLogging.Format(m_From), CommandLogging.Format(m_Item));
m_Item.Delete();
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Item));
break;
}
case 4: // Go there
{
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, m_Item));
InvokeCommand($"Go {m_Item.Serial.Value}");
break;
}
case 5: // Move to target
{
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, m_Item));
m_From.Target = new MoveTarget(m_Item);
break;
}
case 6: // Bring to pack
{
Mobile owner = m_Item.RootParent as Mobile;
if (owner?.Map != null && owner.Map != Map.Internal &&
!BaseCommand.IsAccessible(m_From, owner) /* !m_From.CanSee( owner )*/)
{
m_From.SendMessage("You can not get what you can not see.");
}
else if (owner != null && (owner.Map == null || owner.Map == Map.Internal) && owner.Hidden &&
owner.AccessLevel >= m_From.AccessLevel)
{
m_From.SendMessage("You can not get what you can not see.");
}
else
{
m_From.SendGump(new InterfaceItemGump(m_From, m_Columns, m_List, m_Page, m_Item));
m_From.AddToBackpack(m_Item);
}
break;
}
}
}
}
public class InterfaceMobileGump : BaseGridGump
{
private string[] m_Columns;
private Mobile m_From;
private List<object> m_List;
private Mobile m_Mobile;
private int m_Page;
public InterfaceMobileGump(Mobile from, string[] columns, List<object> list, int page, Mobile mob)
: base(30, 30)
{
m_From = from;
m_Columns = columns;
m_List = list;
m_Page = page;
m_Mobile = mob;
Render();
}
public void Render()
{
AddNewPage();
AddEntryButton(20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight);
AddEntryHtml(160, m_Mobile.Name);
AddEntryHeader(20);
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Properties");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight);
if (!m_Mobile.Player)
{
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Delete");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 3, ArrowRightWidth, ArrowRightHeight);
}
if (m_Mobile != m_From)
{
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Go to there");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 4, ArrowRightWidth, ArrowRightHeight);
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Bring them here");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 5, ArrowRightWidth, ArrowRightHeight);
}
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Move to target");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 6, ArrowRightWidth, ArrowRightHeight);
if (m_From == m_Mobile || m_From.AccessLevel > m_Mobile.AccessLevel)
{
AddNewLine();
if (m_Mobile.Alive)
{
AddEntryHtml(20 + OffsetSize + 160, "Kill");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 7, ArrowRightWidth, ArrowRightHeight);
}
else
{
AddEntryHtml(20 + OffsetSize + 160, "Resurrect");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 8, ArrowRightWidth, ArrowRightHeight);
}
}
if (m_Mobile.NetState != null)
{
AddNewLine();
AddEntryHtml(20 + OffsetSize + 160, "Client");
AddEntryButton(20, ArrowRightID1, ArrowRightID2, 9, ArrowRightWidth, ArrowRightHeight);
}
FinishPage();
}
private void InvokeCommand(string ip)
{
CommandSystem.Handle(m_From, $"{CommandSystem.Prefix}{ip}");
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (m_Mobile.Deleted)
{
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
return;
}
if (!BaseCommand.IsAccessible(m_From, m_Mobile))
{
m_From.SendMessage("That is no longer accessible.");
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
return;
}
switch (info.ButtonID)
{
case 0:
case 1:
{
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
break;
}
case 2: // Properties
{
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
m_From.SendGump(new PropertiesGump(m_From, m_Mobile));
break;
}
case 3: // Delete
{
if (!m_Mobile.Player)
{
CommandLogging.WriteLine(m_From, "{0} {1} deleting {2}", m_From.AccessLevel,
CommandLogging.Format(m_From), CommandLogging.Format(m_Mobile));
m_Mobile.Delete();
m_From.SendGump(new InterfaceGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
}
break;
}
case 4: // Go there
{
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
InvokeCommand($"Go {m_Mobile.Serial.Value}");
break;
}
case 5: // Bring them here
{
if (m_From.Map == null || m_From.Map == Map.Internal)
{
m_From.SendMessage("You cannot bring that person here.");
}
else
{
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
m_Mobile.MoveToWorld(m_From.Location, m_From.Map);
}
break;
}
case 6: // Move to target
{
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
m_From.Target = new MoveTarget(m_Mobile);
break;
}
case 7: // Kill
{
if (m_From == m_Mobile || m_From.AccessLevel > m_Mobile.AccessLevel)
m_Mobile.Kill();
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
break;
}
case 8: // Res
{
if (m_From == m_Mobile || m_From.AccessLevel > m_Mobile.AccessLevel)
{
m_Mobile.PlaySound(0x214);
m_Mobile.FixedEffect(0x376A, 10, 16);
m_Mobile.Resurrect();
}
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
break;
}
case 9: // Client
{
m_From.SendGump(new InterfaceMobileGump(m_From, m_Columns, m_List, m_Page, m_Mobile));
if (m_Mobile.NetState != null)
m_From.SendGump(new ClientGump(m_From, m_Mobile.NetState));
break;
}
}
}
}
}

View file

@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
namespace Server.Commands.Generic
{
public delegate BaseExtension ExtensionConstructor();
public sealed class ExtensionInfo
{
public ExtensionInfo(int order, string name, int size, ExtensionConstructor constructor)
{
Name = name;
Size = size;
Order = order;
Constructor = constructor;
}
public static Dictionary<string, ExtensionInfo> Table{ get; } =
new Dictionary<string, ExtensionInfo>(StringComparer.InvariantCultureIgnoreCase);
public int Order{ get; }
public string Name{ get; }
public int Size{ get; }
public bool IsFixedSize => Size >= 0;
public ExtensionConstructor Constructor{ get; }
public static void Register(ExtensionInfo ext)
{
Table[ext.Name] = ext;
}
}
public sealed class Extensions : List<BaseExtension>
{
public bool IsValid(object obj)
{
for (int i = 0; i < Count; ++i)
if (!this[i].IsValid(obj))
return false;
return true;
}
public void Filter(List<object> list)
{
for (int i = 0; i < Count; ++i)
this[i].Filter(list);
}
public static Extensions Parse(Mobile from, ref string[] args)
{
Extensions parsed = new Extensions();
int size = args.Length;
Type baseType = null;
for (int i = args.Length - 1; i >= 0; --i)
{
if (!ExtensionInfo.Table.TryGetValue(args[i], out ExtensionInfo extInfo))
continue;
if (extInfo.IsFixedSize && i != size - extInfo.Size - 1)
throw new Exception("Invalid extended argument count.");
BaseExtension ext = extInfo.Constructor();
ext.Parse(from, args, i + 1, size - i - 1);
if (ext is WhereExtension extension)
baseType = extension.Conditional.Type;
parsed.Add(ext);
size = i;
}
parsed.Sort((a, b) => a.Order - b.Order);
AssemblyEmitter emitter = null;
foreach (BaseExtension update in parsed)
update.Optimize(from, baseType, ref emitter);
if (size != args.Length)
{
string[] old = args;
args = new string[size];
for (int i = 0; i < args.Length; ++i)
args[i] = old[i];
}
return parsed;
}
}
public abstract class BaseExtension
{
public abstract ExtensionInfo Info{ get; }
public string Name => Info.Name;
public int Size => Info.Size;
public bool IsFixedSize => Info.IsFixedSize;
public int Order => Info.Order;
public virtual void Optimize(Mobile from, Type baseType, ref AssemblyEmitter assembly)
{
}
public virtual void Parse(Mobile from, string[] arguments, int offset, int size)
{
}
public virtual bool IsValid(object obj)
{
return true;
}
public virtual void Filter(List<object> list)
{
}
}
}

View file

@ -0,0 +1,549 @@
using System;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
namespace Server.Commands.Generic
{
public interface IConditional
{
bool Verify(object obj);
}
public interface ICondition
{
// Invoked during the constructor
void Construct(TypeBuilder typeBuilder, ILGenerator il, int index);
// Target object will be loaded on the stack
void Compile(MethodEmitter emitter);
}
public sealed class TypeCondition : ICondition
{
public static TypeCondition Default = new TypeCondition();
void ICondition.Construct(TypeBuilder typeBuilder, ILGenerator il, int index)
{
}
void ICondition.Compile(MethodEmitter emitter)
{
// The object was safely cast to be the conditionals type
// If it's null, then the type cast didn't work...
emitter.LoadNull();
emitter.Compare(OpCodes.Ceq);
emitter.LogicalNot();
}
}
public sealed class PropertyValue
{
public PropertyValue(Type type, object value)
{
Type = type;
Value = value;
}
public Type Type{ get; }
public object Value{ get; private set; }
public FieldInfo Field{ get; private set; }
public bool HasField => Field != null;
public void Load(MethodEmitter method)
{
if (Field != null)
{
method.LoadArgument(0);
method.LoadField(Field);
}
else if (Value == null)
{
method.LoadNull(Type);
}
else
{
if (Value is int i)
method.Load(i);
else if (Value is long l)
method.Load(l);
else if (Value is float f)
method.Load(f);
else if (Value is double d)
method.Load(d);
else if (Value is char c)
method.Load(c);
else if (Value is bool b)
method.Load(b);
else if (Value is string s)
method.Load(s);
else if (Value is Enum e)
method.Load(e);
else
throw new InvalidOperationException("Unrecognized comparison value.");
}
}
public void Acquire(TypeBuilder typeBuilder, ILGenerator il, string fieldName)
{
if (!(Value is string toParse))
return;
if (!Type.IsValueType && toParse == "null")
{
Value = null;
}
else if (Type == typeof(string))
{
if (toParse == @"@""null""")
toParse = "null";
Value = toParse;
}
else if (Type.IsEnum)
{
Value = Enum.Parse(Type, toParse, true);
}
else
{
MethodInfo parseMethod;
object[] parseArgs;
MethodInfo parseNumber = Type.GetMethod(
"Parse",
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(string), typeof(NumberStyles) },
null
);
if (parseNumber != null)
{
NumberStyles style = NumberStyles.Integer;
if (Insensitive.StartsWith(toParse, "0x"))
{
style = NumberStyles.HexNumber;
toParse = toParse.Substring(2);
}
parseMethod = parseNumber;
parseArgs = new object[] { toParse, style };
}
else
{
MethodInfo parseGeneral = Type.GetMethod(
"Parse",
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(string) },
null
);
parseMethod = parseGeneral;
parseArgs = new object[] { toParse };
}
if (parseMethod != null)
{
Value = parseMethod.Invoke(null, parseArgs);
if (!Type.IsPrimitive)
{
Field = typeBuilder.DefineField(
fieldName,
Type,
FieldAttributes.Private | FieldAttributes.InitOnly
);
// parseMethod.Invoke(null,
// parseArgs.Length == 2 ? new object[] {toParse, (int) parseArgs[1]} : new object[] {toParse});
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, toParse);
if (parseArgs.Length == 2) // dirty evil hack :-(
il.Emit(OpCodes.Ldc_I4, (int)parseArgs[1]);
il.Emit(OpCodes.Call, parseMethod);
il.Emit(OpCodes.Stfld, Field);
}
}
else
{
throw new InvalidOperationException(
$"Unable to convert string \"{Value}\" into type '{Type}'."
);
}
}
}
}
public abstract class PropertyCondition : ICondition
{
protected bool m_Not;
protected Property m_Property;
public PropertyCondition(Property property, bool not)
{
m_Property = property;
m_Not = not;
}
public abstract void Construct(TypeBuilder typeBuilder, ILGenerator il, int index);
public abstract void Compile(MethodEmitter emitter);
}
public enum StringOperator
{
Equal,
NotEqual,
Contains,
StartsWith,
EndsWith
}
public sealed class StringCondition : PropertyCondition
{
private bool m_IgnoreCase;
private StringOperator m_Operator;
private PropertyValue m_Value;
public StringCondition(Property property, bool not, StringOperator op, object value, bool ignoreCase)
: base(property, not)
{
m_Operator = op;
m_Value = new PropertyValue(property.Type, value);
m_IgnoreCase = ignoreCase;
}
public override void Construct(TypeBuilder typeBuilder, ILGenerator il, int index)
{
m_Value.Acquire(typeBuilder, il, "v" + index);
}
public override void Compile(MethodEmitter emitter)
{
bool inverse = false;
string methodName;
switch (m_Operator)
{
case StringOperator.Equal:
methodName = "Equals";
break;
case StringOperator.NotEqual:
methodName = "Equals";
inverse = true;
break;
case StringOperator.Contains:
methodName = "Contains";
break;
case StringOperator.StartsWith:
methodName = "StartsWith";
break;
case StringOperator.EndsWith:
methodName = "EndsWith";
break;
default:
throw new InvalidOperationException("Invalid string comparison operator.");
}
if (m_IgnoreCase || methodName == "Equals")
{
Type type = m_IgnoreCase ? typeof(Insensitive) : typeof(string);
emitter.BeginCall(
type.GetMethod(
methodName,
BindingFlags.Public | BindingFlags.Static,
null,
new[]
{
typeof(string),
typeof(string)
},
null
)
);
emitter.Chain(m_Property);
m_Value.Load(emitter);
emitter.FinishCall();
}
else
{
Label notNull = emitter.CreateLabel();
Label moveOn = emitter.CreateLabel();
LocalBuilder temp = emitter.AcquireTemp(m_Property.Type);
emitter.Chain(m_Property);
emitter.StoreLocal(temp);
emitter.LoadLocal(temp);
emitter.BranchIfTrue(notNull);
emitter.Load(false);
emitter.Pop();
emitter.Branch(moveOn);
emitter.MarkLabel(notNull);
emitter.LoadLocal(temp);
emitter.BeginCall(
typeof(string).GetMethod(
methodName,
BindingFlags.Public | BindingFlags.Instance,
null,
new[]
{
typeof(string)
},
null
)
);
m_Value.Load(emitter);
emitter.FinishCall();
emitter.MarkLabel(moveOn);
}
if (m_Not != inverse)
emitter.LogicalNot();
}
}
public enum ComparisonOperator
{
Equal,
NotEqual,
Greater,
GreaterEqual,
Lesser,
LesserEqual
}
public sealed class ComparisonCondition : PropertyCondition
{
private ComparisonOperator m_Operator;
private PropertyValue m_Value;
public ComparisonCondition(Property property, bool not, ComparisonOperator op, object value)
: base(property, not)
{
m_Operator = op;
m_Value = new PropertyValue(property.Type, value);
}
public override void Construct(TypeBuilder typeBuilder, ILGenerator il, int index)
{
m_Value.Acquire(typeBuilder, il, "v" + index);
}
public override void Compile(MethodEmitter emitter)
{
emitter.Chain(m_Property);
bool inverse = false;
bool couldCompare =
emitter.CompareTo(1, delegate { m_Value.Load(emitter); });
if (couldCompare)
{
emitter.Load(0);
switch (m_Operator)
{
case ComparisonOperator.Equal:
emitter.Compare(OpCodes.Ceq);
break;
case ComparisonOperator.NotEqual:
emitter.Compare(OpCodes.Ceq);
inverse = true;
break;
case ComparisonOperator.Greater:
emitter.Compare(OpCodes.Cgt);
break;
case ComparisonOperator.GreaterEqual:
emitter.Compare(OpCodes.Clt);
inverse = true;
break;
case ComparisonOperator.Lesser:
emitter.Compare(OpCodes.Clt);
break;
case ComparisonOperator.LesserEqual:
emitter.Compare(OpCodes.Cgt);
inverse = true;
break;
default:
throw new InvalidOperationException("Invalid comparison operator.");
}
}
else
{
// This type is -not- comparable
// We can only support == and != operations
m_Value.Load(emitter);
switch (m_Operator)
{
case ComparisonOperator.Equal:
emitter.Compare(OpCodes.Ceq);
break;
case ComparisonOperator.NotEqual:
emitter.Compare(OpCodes.Ceq);
inverse = true;
break;
case ComparisonOperator.Greater:
case ComparisonOperator.GreaterEqual:
case ComparisonOperator.Lesser:
case ComparisonOperator.LesserEqual:
throw new InvalidOperationException("Property does not support relational comparisons.");
default:
throw new InvalidOperationException("Invalid operator.");
}
}
if (m_Not != inverse)
emitter.LogicalNot();
}
}
public static class ConditionalCompiler
{
public static IConditional Compile(AssemblyEmitter assembly, Type objectType, ICondition[] conditions, int index)
{
TypeBuilder typeBuilder = assembly.DefineType(
"__conditional" + index,
TypeAttributes.Public,
typeof(object)
);
#region Constructor
{
ConstructorBuilder ctor = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
Type.EmptyTypes
);
ILGenerator il = ctor.GetILGenerator();
// : base()
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
for (int i = 0; i < conditions.Length; ++i)
conditions[i].Construct(typeBuilder, il, i);
// return;
il.Emit(OpCodes.Ret);
}
#endregion
#region IComparer
typeBuilder.AddInterfaceImplementation(typeof(IConditional));
MethodBuilder compareMethod;
#region Compare
{
MethodEmitter emitter = new MethodEmitter(typeBuilder);
emitter.Define(
/* name */ "Verify",
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
/* return */ typeof(bool),
/* params */ new[] { typeof(object) });
LocalBuilder obj = emitter.CreateLocal(objectType);
LocalBuilder eq = emitter.CreateLocal(typeof(bool));
emitter.LoadArgument(1);
emitter.CastAs(objectType);
emitter.StoreLocal(obj);
Label done = emitter.CreateLabel();
for (int i = 0; i < conditions.Length; ++i)
{
if (i > 0)
{
emitter.LoadLocal(eq);
emitter.BranchIfFalse(done);
}
emitter.LoadLocal(obj);
conditions[i].Compile(emitter);
emitter.StoreLocal(eq);
}
emitter.MarkLabel(done);
emitter.LoadLocal(eq);
emitter.Return();
typeBuilder.DefineMethodOverride(
emitter.Method,
typeof(IConditional).GetMethod(
"Verify",
new[]
{
typeof(object)
}
)
);
compareMethod = emitter.Method;
}
#endregion
#endregion
Type conditionalType = typeBuilder.CreateType();
return (IConditional)Activator.CreateInstance(conditionalType);
}
}
}

View file

@ -0,0 +1,259 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace Server.Commands.Generic
{
public static class DistinctCompiler
{
public static IComparer<T> Compile<T>(AssemblyEmitter assembly, Type objectType, Property[] props)
{
TypeBuilder typeBuilder = assembly.DefineType(
"__distinct",
TypeAttributes.Public,
typeof(object)
);
#region Constructor
{
ConstructorBuilder ctor = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
Type.EmptyTypes
);
ILGenerator il = ctor.GetILGenerator();
// : base()
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(T).GetConstructor(Type.EmptyTypes) ??
throw new Exception($"Could not find empty constructor for type {typeof(T).FullName}"));
// return;
il.Emit(OpCodes.Ret);
}
#endregion
#region IComparer
typeBuilder.AddInterfaceImplementation(typeof(IComparer<T>));
MethodBuilder compareMethod;
#region Compare
{
MethodEmitter emitter = new MethodEmitter(typeBuilder);
emitter.Define(
/* name */ "Compare",
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
/* return */ typeof(int),
/* params */ new[] { typeof(T), typeof(T) });
LocalBuilder a = emitter.CreateLocal(objectType);
LocalBuilder b = emitter.CreateLocal(objectType);
LocalBuilder v = emitter.CreateLocal(typeof(int));
emitter.LoadArgument(1);
emitter.CastAs(objectType);
emitter.StoreLocal(a);
emitter.LoadArgument(2);
emitter.CastAs(objectType);
emitter.StoreLocal(b);
emitter.Load(0);
emitter.StoreLocal(v);
Label end = emitter.CreateLabel();
for (int i = 0; i < props.Length; ++i)
{
if (i > 0)
{
emitter.LoadLocal(v);
emitter.BranchIfTrue(end); // if ( v != 0 ) return v;
}
Property prop = props[i];
emitter.LoadLocal(a);
emitter.Chain(prop);
bool couldCompare =
emitter.CompareTo(1, delegate
{
emitter.LoadLocal(b);
emitter.Chain(prop);
});
if (!couldCompare)
throw new InvalidOperationException("Property is not comparable.");
emitter.StoreLocal(v);
}
emitter.MarkLabel(end);
emitter.LoadLocal(v);
emitter.Return();
typeBuilder.DefineMethodOverride(
emitter.Method,
typeof(IComparer<T>).GetMethod(
"Compare",
new[]
{
typeof(T),
typeof(T)
}
) ?? throw new Exception($"No Compare method found for type {typeof(T).FullName}")
);
compareMethod = emitter.Method;
}
#endregion
#endregion
#region IEqualityComparer
typeBuilder.AddInterfaceImplementation(typeof(IEqualityComparer<T>));
#region Equals
{
MethodEmitter emitter = new MethodEmitter(typeBuilder);
emitter.Define(
/* name */ "Equals",
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
/* return */ typeof(bool),
/* params */ new[] { typeof(T), typeof(T) });
emitter.Generator.Emit(OpCodes.Ldarg_0);
emitter.Generator.Emit(OpCodes.Ldarg_1);
emitter.Generator.Emit(OpCodes.Ldarg_2);
emitter.Generator.Emit(OpCodes.Call, compareMethod);
emitter.Generator.Emit(OpCodes.Ldc_I4_0);
emitter.Generator.Emit(OpCodes.Ceq);
emitter.Generator.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(
emitter.Method,
typeof(IEqualityComparer<T>).GetMethod(
"Equals",
new[]
{
typeof(T),
typeof(T)
}
) ?? throw new Exception($"No Equals method found for type {typeof(T).FullName}")
);
}
#endregion
#region GetHashCode
{
MethodEmitter emitter = new MethodEmitter(typeBuilder);
emitter.Define(
/* name */ "GetHashCode",
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
/* return */ typeof(int),
/* params */ new[] { typeof(T) });
LocalBuilder obj = emitter.CreateLocal(objectType);
emitter.LoadArgument(1);
emitter.CastAs(objectType);
emitter.StoreLocal(obj);
for (int i = 0; i < props.Length; ++i)
{
Property prop = props[i];
emitter.LoadLocal(obj);
emitter.Chain(prop);
Type active = emitter.Active;
MethodInfo getHashCode = active.GetMethod("GetHashCode", Type.EmptyTypes);
if (getHashCode == null)
getHashCode = typeof(T).GetMethod("GetHashCode", Type.EmptyTypes);
if (active != typeof(int))
{
if (!active.IsValueType)
{
LocalBuilder value = emitter.AcquireTemp(active);
Label valueNotNull = emitter.CreateLabel();
Label done = emitter.CreateLabel();
emitter.StoreLocal(value);
emitter.LoadLocal(value);
emitter.BranchIfTrue(valueNotNull);
emitter.Load(0);
emitter.Pop(typeof(int));
emitter.Branch(done);
emitter.MarkLabel(valueNotNull);
emitter.LoadLocal(value);
emitter.Call(getHashCode);
emitter.ReleaseTemp(value);
emitter.MarkLabel(done);
}
else
{
emitter.Call(getHashCode);
}
}
if (i > 0)
emitter.Xor();
}
emitter.Return();
typeBuilder.DefineMethodOverride(
emitter.Method,
typeof(IEqualityComparer<T>).GetMethod(
"GetHashCode",
new[]
{
typeof(T)
}
) ?? throw new Exception($"No GetHashCode method found for type {typeof(T).FullName}")
);
}
#endregion
#endregion
Type comparerType = typeBuilder.CreateType();
return (IComparer<T>)Activator.CreateInstance(comparerType);
}
}
}

View file

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace Server.Commands.Generic
{
public sealed class OrderInfo
{
private int m_Order;
public OrderInfo(Property property, bool isAscending)
{
Property = property;
IsAscending = isAscending;
}
public Property Property{ get; set; }
public bool IsAscending
{
get => m_Order > 0;
set => m_Order = value ? +1 : -1;
}
public bool IsDescending
{
get => m_Order < 0;
set => m_Order = value ? -1 : +1;
}
public int Sign
{
get => Math.Sign(m_Order);
set
{
m_Order = Math.Sign(value);
if (m_Order == 0)
throw new InvalidOperationException("Sign cannot be zero.");
}
}
}
public static class SortCompiler
{
public static IComparer<T> Compile<T>(AssemblyEmitter assembly, Type objectType, OrderInfo[] orders)
{
TypeBuilder typeBuilder = assembly.DefineType(
"__sort",
TypeAttributes.Public,
typeof(T)
);
#region Constructor
{
ConstructorBuilder ctor = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
Type.EmptyTypes
);
ILGenerator il = ctor.GetILGenerator();
// : base()
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(T).GetConstructor(Type.EmptyTypes) ??
throw new Exception($"Could not find empty constructor for type {typeof(T).FullName}"));
// return;
il.Emit(OpCodes.Ret);
}
#endregion
#region IComparer
typeBuilder.AddInterfaceImplementation(typeof(IComparer<T>));
#region Compare
{
MethodEmitter emitter = new MethodEmitter(typeBuilder);
emitter.Define(
/* name */ "Compare",
/* attr */ MethodAttributes.Public | MethodAttributes.Virtual,
/* return */ typeof(int),
/* params */ new[] { typeof(T), typeof(T) });
LocalBuilder a = emitter.CreateLocal(objectType);
LocalBuilder b = emitter.CreateLocal(objectType);
LocalBuilder v = emitter.CreateLocal(typeof(int));
emitter.LoadArgument(1);
emitter.CastAs(objectType);
emitter.StoreLocal(a);
emitter.LoadArgument(2);
emitter.CastAs(objectType);
emitter.StoreLocal(b);
emitter.Load(0);
emitter.StoreLocal(v);
Label end = emitter.CreateLabel();
for (int i = 0; i < orders.Length; ++i)
{
if (i > 0)
{
emitter.LoadLocal(v);
emitter.BranchIfTrue(end); // if ( v != 0 ) return v;
}
OrderInfo orderInfo = orders[i];
Property prop = orderInfo.Property;
int sign = orderInfo.Sign;
emitter.LoadLocal(a);
emitter.Chain(prop);
bool couldCompare =
emitter.CompareTo(sign, delegate
{
emitter.LoadLocal(b);
emitter.Chain(prop);
});
if (!couldCompare)
throw new InvalidOperationException("Property is not comparable.");
emitter.StoreLocal(v);
}
emitter.MarkLabel(end);
emitter.LoadLocal(v);
emitter.Return();
typeBuilder.DefineMethodOverride(
emitter.Method,
typeof(IComparer<T>).GetMethod(
"Compare",
new[]
{
typeof(T),
typeof(T)
}
) ?? throw new Exception($"No Compare method found for type {typeof(T).FullName}")
);
}
#endregion
#endregion
Type comparerType = typeBuilder.CreateType();
return (IComparer<T>)Activator.CreateInstance(comparerType);
}
}
}

View file

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
namespace Server.Commands.Generic
{
public sealed class DistinctExtension : BaseExtension
{
public static ExtensionInfo ExtInfo =
new ExtensionInfo(30, "Distinct", -1, () => new DistinctExtension());
private IComparer<object> m_Comparer;
private List<Property> m_Properties;
public DistinctExtension()
{
m_Properties = new List<Property>();
}
public override ExtensionInfo Info => ExtInfo;
public static void Initialize()
{
ExtensionInfo.Register(ExtInfo);
}
public override void Optimize(Mobile from, Type baseType, ref AssemblyEmitter assembly)
{
if (baseType == null)
throw new Exception("Distinct extension may only be used in combination with an object conditional.");
foreach (Property prop in m_Properties)
{
prop.BindTo(baseType, PropertyAccess.Read);
prop.CheckAccess(from);
}
if (assembly == null)
assembly = new AssemblyEmitter("__dynamic");
m_Comparer = DistinctCompiler.Compile<object>(assembly, baseType, m_Properties.ToArray());
}
public override void Parse(Mobile from, string[] arguments, int offset, int size)
{
if (size < 1)
throw new Exception("Invalid distinction syntax.");
int end = offset + size;
while (offset < end)
{
string binding = arguments[offset++];
m_Properties.Add(new Property(binding));
}
}
public override void Filter(List<object> list)
{
if (m_Comparer == null)
throw new InvalidOperationException("The extension must first be optimized.");
List<object> copy = new List<object>(list);
copy.Sort(m_Comparer);
list.Clear();
object last = null;
for (int i = 0; i < copy.Count; ++i)
{
object obj = copy[i];
if (last == null || m_Comparer.Compare(obj, last) != 0)
{
list.Add(obj);
last = obj;
}
}
}
}
}

View file

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
namespace Server.Commands.Generic
{
public sealed class LimitExtension : BaseExtension
{
public static ExtensionInfo ExtInfo = new ExtensionInfo(80, "Limit", 1, () => new LimitExtension());
public override ExtensionInfo Info => ExtInfo;
public int Limit{ get; private set; }
public static void Initialize()
{
ExtensionInfo.Register(ExtInfo);
}
public override void Parse(Mobile from, string[] arguments, int offset, int size)
{
Limit = Utility.ToInt32(arguments[offset]);
if (Limit < 0)
throw new Exception("Limit cannot be less than zero.");
}
public override void Filter(List<object> list)
{
if (list.Count > Limit)
list.RemoveRange(Limit, list.Count - Limit);
}
}
}

View file

@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
namespace Server.Commands.Generic
{
public sealed class SortExtension : BaseExtension
{
public static ExtensionInfo ExtInfo = new ExtensionInfo(40, "Order", -1, () => new SortExtension());
private IComparer<object> m_Comparer;
private List<OrderInfo> m_Orders;
public SortExtension()
{
m_Orders = new List<OrderInfo>();
}
public override ExtensionInfo Info => ExtInfo;
public static void Initialize()
{
ExtensionInfo.Register(ExtInfo);
}
public override void Optimize(Mobile from, Type baseType, ref AssemblyEmitter assembly)
{
if (baseType == null)
throw new Exception("The ordering extension may only be used in combination with an object conditional.");
foreach (OrderInfo order in m_Orders)
{
order.Property.BindTo(baseType, PropertyAccess.Read);
order.Property.CheckAccess(from);
}
if (assembly == null)
assembly = new AssemblyEmitter("__dynamic");
m_Comparer = SortCompiler.Compile<object>(assembly, baseType, m_Orders.ToArray());
}
public override void Parse(Mobile from, string[] arguments, int offset, int size)
{
if (size < 1)
throw new Exception("Invalid ordering syntax.");
if (Insensitive.Equals(arguments[offset], "by"))
{
++offset;
--size;
if (size < 1)
throw new Exception("Invalid ordering syntax.");
}
int end = offset + size;
while (offset < end)
{
string binding = arguments[offset++];
bool isAscending = true;
if (offset < end)
{
string next = arguments[offset];
switch (next.ToLower())
{
case "+":
case "up":
case "asc":
case "ascending":
isAscending = true;
++offset;
break;
case "-":
case "down":
case "desc":
case "descending":
isAscending = false;
++offset;
break;
}
}
Property property = new Property(binding);
m_Orders.Add(new OrderInfo(property, isAscending));
}
}
public override void Filter(List<object> list)
{
if (m_Comparer == null)
throw new InvalidOperationException("The extension must first be optimized.");
list.Sort(m_Comparer);
}
}
}

View file

@ -0,0 +1,39 @@
using System;
namespace Server.Commands.Generic
{
public sealed class WhereExtension : BaseExtension
{
public static ExtensionInfo ExtInfo = new ExtensionInfo(20, "Where", -1, () => new WhereExtension());
public override ExtensionInfo Info => ExtInfo;
public ObjectConditional Conditional{ get; private set; }
public static void Initialize()
{
ExtensionInfo.Register(ExtInfo);
}
public override void Optimize(Mobile from, Type baseType, ref AssemblyEmitter assembly)
{
if (baseType == null)
throw new InvalidOperationException("Insanity.");
Conditional.Compile(ref assembly);
}
public override void Parse(Mobile from, string[] arguments, int offset, int size)
{
if (size < 1)
throw new Exception("Invalid condition syntax.");
Conditional = ObjectConditional.ParseDirect(from, arguments, offset, size);
}
public override bool IsValid(object obj)
{
return Conditional.CheckCondition(obj);
}
}
}

View file

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Server.Commands.Generic
{
public class AreaCommandImplementor : BaseCommandImplementor
{
public AreaCommandImplementor()
{
Accessors = new[] { "Area", "Group" };
SupportRequirement = CommandSupport.Area;
SupportsConditionals = true;
AccessLevel = AccessLevel.GameMaster;
Usage = "Area <command> [condition]";
Description =
"Invokes the command on all appropriate objects in a targeted area. Optional condition arguments can further restrict the set of objects.";
Instance = this;
}
public static AreaCommandImplementor Instance{ get; private set; }
public override void Process(Mobile from, BaseCommand command, string[] args)
{
BoundingBoxPicker.Begin(from, (map, start, end) => OnTarget(from, map, start, end, command, args));
}
public void OnTarget(Mobile from, Map map, Point3D start, Point3D end, BaseCommand command, string[] args)
{
try
{
Rectangle2D rect = new Rectangle2D(start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1);
Extensions ext = Extensions.Parse(from, ref args);
if (!CheckObjectTypes(from, command, ext, out bool items, out bool mobiles))
return;
if (!(items || mobiles))
return;
IPooledEnumerable<IEntity> eable = map.GetObjectsInBounds(rect, items, mobiles);
List<object> objs = eable.Where(obj => !mobiles || !(obj is Mobile) || BaseCommand.IsAccessible(from, obj))
.Where(obj => ext.IsValid(obj)).Cast<object>().ToList();
eable.Free();
ext.Filter(objs);
RunCommand(from, objs, command, args);
}
catch (Exception ex)
{
from.SendMessage(ex.Message);
}
}
}
}

View file

@ -0,0 +1,295 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Server.Commands.Generic
{
[Flags]
public enum CommandSupport
{
Single = 0x0001,
Global = 0x0002,
Online = 0x0004,
Multi = 0x0008,
Area = 0x0010,
Self = 0x0020,
Region = 0x0040,
Contained = 0x0080,
IPAddress = 0x0100,
All = Single | Global | Online | Multi | Area | Self | Region | Contained | IPAddress,
AllMobiles = All & ~Contained,
AllNPCs = All & ~(IPAddress | Online | Self | Contained),
AllItems = All & ~(IPAddress | Online | Self | Region),
Simple = Single | Multi,
Complex = Global | Online | Area | Region | Contained | IPAddress
}
public abstract class BaseCommandImplementor
{
private static List<BaseCommandImplementor> m_Implementors;
public BaseCommandImplementor()
{
Commands = new Dictionary<string, BaseCommand>(StringComparer.OrdinalIgnoreCase);
}
public bool SupportsConditionals{ get; set; }
public string[] Accessors{ get; set; }
public string Usage{ get; set; }
public string Description{ get; set; }
public AccessLevel AccessLevel{ get; set; }
public CommandSupport SupportRequirement{ get; set; }
public Dictionary<string, BaseCommand> Commands{ get; }
public static List<BaseCommandImplementor> Implementors
{
get
{
if (m_Implementors == null)
{
m_Implementors = new List<BaseCommandImplementor>();
RegisterImplementors();
}
return m_Implementors;
}
}
public static void RegisterImplementors()
{
Register(new RegionCommandImplementor());
Register(new GlobalCommandImplementor());
Register(new OnlineCommandImplementor());
Register(new SingleCommandImplementor());
Register(new SerialCommandImplementor());
Register(new MultiCommandImplementor());
Register(new AreaCommandImplementor());
Register(new SelfCommandImplementor());
Register(new ContainedCommandImplementor());
Register(new IPAddressCommandImplementor());
Register(new RangeCommandImplementor());
Register(new ScreenCommandImplementor());
Register(new FacetCommandImplementor());
}
public virtual void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
{
obj = null;
}
public virtual void Register(BaseCommand command)
{
for (int i = 0; i < command.Commands.Length; ++i)
Commands[command.Commands[i]] = command;
}
public bool CheckObjectTypes(Mobile from, BaseCommand command, Extensions ext, out bool items, out bool mobiles)
{
items = mobiles = false;
ObjectConditional cond = ObjectConditional.Empty;
foreach (BaseExtension check in ext)
if (check is WhereExtension extension)
{
cond = extension.Conditional;
break;
}
bool condIsItem = cond.IsItem;
bool condIsMobile = cond.IsMobile;
switch (command.ObjectTypes)
{
case ObjectTypes.All:
case ObjectTypes.Both:
{
if (condIsItem)
items = true;
if (condIsMobile)
mobiles = true;
break;
}
case ObjectTypes.Items:
{
if (condIsItem)
{
items = true;
}
else if (condIsMobile)
{
from.SendMessage("You may not use a mobile type condition for this command.");
return false;
}
break;
}
case ObjectTypes.Mobiles:
{
if (condIsMobile)
{
mobiles = true;
}
else if (condIsItem)
{
from.SendMessage("You may not use an item type condition for this command.");
return false;
}
break;
}
}
return true;
}
public void RunCommand(Mobile from, BaseCommand command, string[] args)
{
try
{
object obj = null;
Compile(from, command, ref args, ref obj);
RunCommand(from, obj, command, args);
}
catch (Exception ex)
{
from.SendMessage(ex.Message);
}
}
public string GenerateArgString(string[] args)
{
if (args.Length == 0)
return "";
// NOTE: this does not preserve the case where quotation marks are used on a single word
StringBuilder sb = new StringBuilder();
for (int i = 0; i < args.Length; ++i)
{
if (i > 0)
sb.Append(' ');
if (args[i].IndexOf(' ') >= 0)
{
sb.Append('"');
sb.Append(args[i]);
sb.Append('"');
}
else
{
sb.Append(args[i]);
}
}
return sb.ToString();
}
public void RunCommand(Mobile from, object obj, BaseCommand command, string[] args)
{
// try
// {
CommandEventArgs e = new CommandEventArgs(from, command.Commands[0], GenerateArgString(args), args);
if (!command.ValidateArgs(this, e))
return;
bool flushToLog = false;
if (obj is List<object> list)
{
if (list.Count > 20)
CommandLogging.Enabled = false;
else if (list.Count == 0)
command.LogFailure("Nothing was found to use this command on.");
command.ExecuteList(e, list);
if (list.Count > 20)
{
flushToLog = true;
CommandLogging.Enabled = true;
}
}
else if (obj != null)
{
if (command.ListOptimized)
command.ExecuteList(e, new List<object>{ obj });
else
command.Execute(e, obj);
}
command.Flush(from, flushToLog);
// }
// catch ( Exception ex )
// {
// from.SendMessage( ex.Message );
// }
}
public virtual void Process(Mobile from, BaseCommand command, string[] args)
{
RunCommand(from, command, args);
}
public virtual void Execute(CommandEventArgs e)
{
if (e.Length >= 1)
{
if (!Commands.TryGetValue(e.GetString(0), out BaseCommand command))
{
e.Mobile.SendMessage(
"That is either an invalid command name or one that does not support this modifier.");
}
else if (e.Mobile.AccessLevel < command.AccessLevel)
{
e.Mobile.SendMessage("You do not have access to that command.");
}
else
{
string[] oldArgs = e.Arguments;
string[] args = new string[oldArgs.Length - 1];
for (int i = 0; i < args.Length; ++i)
args[i] = oldArgs[i + 1];
Process(e.Mobile, command, args);
}
}
else
{
e.Mobile.SendMessage("You must supply a command name.");
}
}
public void Register()
{
if (Accessors == null)
return;
for (int i = 0; i < Accessors.Length; ++i)
CommandSystem.Register(Accessors[i], AccessLevel, Execute);
}
public static void Register(BaseCommandImplementor impl)
{
m_Implementors.Add(impl);
impl.Register();
}
}
}

View file

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using Server.Items;
using Server.Targeting;
namespace Server.Commands.Generic
{
public class ContainedCommandImplementor : BaseCommandImplementor
{
public ContainedCommandImplementor()
{
Accessors = new[] { "Contained" };
SupportRequirement = CommandSupport.Contained;
AccessLevel = AccessLevel.GameMaster;
Usage = "Contained <command> [condition]";
Description =
"Invokes the command on all child items in a targeted container. Optional condition arguments can further restrict the set of objects.";
}
public override void Process(Mobile from, BaseCommand command, string[] args)
{
if (command.ValidateArgs(this, new CommandEventArgs(from, command.Commands[0], GenerateArgString(args), args)))
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
(m, targeted) => OnTarget(m, targeted, command, args));
}
public void OnTarget(Mobile from, object targeted, BaseCommand command, string[] args)
{
if (!BaseCommand.IsAccessible(from, targeted))
{
from.SendLocalizedMessage(500447); // That is not accessible.
return;
}
if (command.ObjectTypes == ObjectTypes.Mobiles)
return; // sanity check
if (!(targeted is Container cont))
{
from.SendMessage("That is not a container.");
return;
}
try
{
Extensions ext = Extensions.Parse(from, ref args);
if (!CheckObjectTypes(from, command, ext, out bool items, out bool _))
return;
if (!items)
{
from.SendMessage("This command only works on items.");
return;
}
List<object> list = new List<object>();
foreach (Item item in cont.FindItemsByType<Item>())
{
if (ext.IsValid(item))
list.Add(item);
}
ext.Filter(list);
RunCommand(from, list, command, args);
}
catch (Exception e)
{
from.SendMessage(e.Message);
}
}
}
}

View file

@ -0,0 +1,31 @@
namespace Server.Commands.Generic
{
public class FacetCommandImplementor : BaseCommandImplementor
{
public FacetCommandImplementor()
{
Accessors = new[] { "Facet" };
SupportRequirement = CommandSupport.Area;
SupportsConditionals = true;
AccessLevel = AccessLevel.GameMaster;
Usage = "Facet <command> [condition]";
Description =
"Invokes the command on all appropriate objects within your facet's map bounds. Optional condition arguments can further restrict the set of objects.";
}
public override void Process(Mobile from, BaseCommand command, string[] args)
{
AreaCommandImplementor impl = AreaCommandImplementor.Instance;
if (impl == null)
return;
Map map = from.Map;
if (map == null || map == Map.Internal)
return;
impl.OnTarget(from, map, Point3D.Zero, new Point3D(map.Width - 1, map.Height - 1, 0), command, args);
}
}
}

View file

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
namespace Server.Commands.Generic
{
public class GlobalCommandImplementor : BaseCommandImplementor
{
public GlobalCommandImplementor()
{
Accessors = new[] { "Global" };
SupportRequirement = CommandSupport.Global;
SupportsConditionals = true;
AccessLevel = AccessLevel.Administrator;
Usage = "Global <command> [condition]";
Description =
"Invokes the command on all appropriate objects in the world. Optional condition arguments can further restrict the set of objects.";
}
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
{
try
{
Extensions ext = Extensions.Parse(from, ref args);
if (!CheckObjectTypes(from, command, ext, out bool items, out bool mobiles))
return;
List<object> list = new List<object>();
if (items)
foreach (Item item in World.Items.Values)
if (ext.IsValid(item))
list.Add(item);
if (mobiles)
foreach (Mobile mob in World.Mobiles.Values)
if (ext.IsValid(mob))
list.Add(mob);
ext.Filter(list);
obj = list;
}
catch (Exception ex)
{
from.SendMessage(ex.Message);
}
}
}
}

View file

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Net;
using Server.Network;
namespace Server.Commands.Generic
{
public class IPAddressCommandImplementor : BaseCommandImplementor
{
public IPAddressCommandImplementor()
{
Accessors = new[] { "IPAddress" };
SupportRequirement = CommandSupport.IPAddress;
SupportsConditionals = true;
AccessLevel = AccessLevel.Administrator;
Usage = "IPAddress <command> [condition]";
Description =
"Invokes the command on one mobile from each IP address that is logged in. Optional condition arguments can further restrict the set of objects.";
}
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
{
try
{
Extensions ext = Extensions.Parse(from, ref args);
if (!CheckObjectTypes(from, command, ext, out bool _, out bool mobiles))
return;
if (!mobiles) // sanity check
{
command.LogFailure("This command does not support items.");
return;
}
List<object> list = new List<object>();
List<IPAddress> addresses = new List<IPAddress>();
List<NetState> states = NetState.Instances;
for (int i = 0; i < states.Count; ++i)
{
NetState ns = states[i];
Mobile mob = ns.Mobile;
if (mob != null && !addresses.Contains(ns.Address) && ext.IsValid(mob))
{
list.Add(mob);
addresses.Add(ns.Address);
}
}
ext.Filter(list);
obj = list;
}
catch (Exception ex)
{
from.SendMessage(ex.Message);
}
}
}
}

View file

@ -0,0 +1,73 @@
using Server.Targeting;
namespace Server.Commands.Generic
{
public class MultiCommandImplementor : BaseCommandImplementor
{
public MultiCommandImplementor()
{
Accessors = new[] { "Multi", "m" };
SupportRequirement = CommandSupport.Multi;
AccessLevel = AccessLevel.Counselor;
Usage = "Multi <command>";
Description = "Invokes the command on multiple targeted objects.";
}
public override void Process(Mobile from, BaseCommand command, string[] args)
{
if (command.ValidateArgs(this, new CommandEventArgs(from, command.Commands[0], GenerateArgString(args), args)))
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
(m, targeted) => OnTarget(m, targeted, command, args));
}
public void OnTarget(Mobile from, object targeted, BaseCommand command, string[] args)
{
if (!BaseCommand.IsAccessible(from, targeted))
{
from.SendLocalizedMessage(500447); // That is not accessible.
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
(m, t) => OnTarget(m, t, command, args));
return;
}
switch (command.ObjectTypes)
{
case ObjectTypes.Both:
{
if (!(targeted is Item || targeted is Mobile))
{
from.SendMessage("This command does not work on that.");
return;
}
break;
}
case ObjectTypes.Items:
{
if (!(targeted is Item))
{
from.SendMessage("This command only works on items.");
return;
}
break;
}
case ObjectTypes.Mobiles:
{
if (!(targeted is Mobile))
{
from.SendMessage("This command only works on mobiles.");
return;
}
break;
}
}
RunCommand(from, targeted, command, args);
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
(m, t) => OnTarget(m, t, command, args));
}
}
}

View file

@ -0,0 +1,249 @@
using System;
using System.Collections.Generic;
namespace Server.Commands.Generic
{
public sealed class ObjectConditional
{
private static readonly Type typeofItem = typeof(Item);
private static readonly Type typeofMobile = typeof(Mobile);
public static readonly ObjectConditional Empty = new ObjectConditional(null, null);
private IConditional[] m_Conditionals;
private ICondition[][] m_Conditions;
public ObjectConditional(Type objectType, ICondition[][] conditions)
{
Type = objectType;
m_Conditions = conditions;
}
public Type Type{ get; }
public bool IsItem => Type == null || Type == typeofItem || Type.IsSubclassOf(typeofItem);
public bool IsMobile => Type == null || Type == typeofMobile || Type.IsSubclassOf(typeofMobile);
public bool HasCompiled => m_Conditionals != null;
public void Compile(ref AssemblyEmitter emitter)
{
if (emitter == null)
emitter = new AssemblyEmitter("__dynamic");
m_Conditionals = new IConditional[m_Conditions.Length];
for (int i = 0; i < m_Conditionals.Length; ++i)
m_Conditionals[i] = ConditionalCompiler.Compile(emitter, Type, m_Conditions[i], i);
}
public bool CheckCondition(object obj)
{
if (Type == null)
return true; // null type means no condition
if (!HasCompiled)
{
AssemblyEmitter emitter = null;
Compile(ref emitter);
}
for (int i = 0; i < m_Conditionals.Length; ++i)
if (m_Conditionals[i].Verify(obj))
return true;
return false; // all conditions false
}
public static ObjectConditional Parse(Mobile from, ref string[] args)
{
string[] conditionArgs = null;
for (int i = 0; i < args.Length; ++i)
if (Insensitive.Equals(args[i], "where"))
{
string[] origArgs = args;
args = new string[i];
for (int j = 0; j < args.Length; ++j)
args[j] = origArgs[j];
conditionArgs = new string[origArgs.Length - i - 1];
for (int j = 0; j < conditionArgs.Length; ++j)
conditionArgs[j] = origArgs[i + j + 1];
break;
}
return ParseDirect(from, conditionArgs, 0, conditionArgs?.Length ?? 0);
}
public static ObjectConditional ParseDirect(Mobile from, string[] args, int offset, int size)
{
if (args == null || size == 0)
return Empty;
int index = 0;
Type objectType = ScriptCompiler.FindTypeByName(args[offset + index], true);
if (objectType == null)
throw new Exception($"No type with that name ({args[offset + index]}) was found.");
++index;
List<ICondition[]> conditions = new List<ICondition[]>();
List<ICondition> current = new List<ICondition>();
current.Add(TypeCondition.Default);
while (index < size)
{
string cur = args[offset + index];
bool inverse = false;
if (Insensitive.Equals(cur, "not") || cur == "!")
{
inverse = true;
++index;
if (index >= size)
throw new Exception("Improperly formatted object conditional.");
}
else if (Insensitive.Equals(cur, "or") || cur == "||")
{
if (current.Count > 1)
{
conditions.Add(current.ToArray());
current.Clear();
current.Add(TypeCondition.Default);
}
++index;
continue;
}
string binding = args[offset + index];
index++;
if (index >= size)
throw new Exception("Improperly formatted object conditional.");
string oper = args[offset + index];
index++;
if (index >= size)
throw new Exception("Improperly formatted object conditional.");
string val = args[offset + index];
index++;
Property prop = new Property(binding);
prop.BindTo(objectType, PropertyAccess.Read);
prop.CheckAccess(from);
ICondition condition = null;
switch (oper)
{
#region Equality
case "=":
case "==":
case "is":
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.Equal, val);
break;
case "!=":
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.NotEqual, val);
break;
#endregion
#region Relational
case ">":
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.Greater, val);
break;
case "<":
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.Lesser, val);
break;
case ">=":
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.GreaterEqual, val);
break;
case "<=":
condition = new ComparisonCondition(prop, inverse, ComparisonOperator.LesserEqual, val);
break;
#endregion
#region Strings
case "==~":
case "~==":
case "=~":
case "~=":
case "is~":
case "~is":
condition = new StringCondition(prop, inverse, StringOperator.Equal, val, true);
break;
case "!=~":
case "~!=":
condition = new StringCondition(prop, inverse, StringOperator.NotEqual, val, true);
break;
case "starts":
condition = new StringCondition(prop, inverse, StringOperator.StartsWith, val, false);
break;
case "starts~":
case "~starts":
condition = new StringCondition(prop, inverse, StringOperator.StartsWith, val, true);
break;
case "ends":
condition = new StringCondition(prop, inverse, StringOperator.EndsWith, val, false);
break;
case "ends~":
case "~ends":
condition = new StringCondition(prop, inverse, StringOperator.EndsWith, val, true);
break;
case "contains":
condition = new StringCondition(prop, inverse, StringOperator.Contains, val, false);
break;
case "contains~":
case "~contains":
condition = new StringCondition(prop, inverse, StringOperator.Contains, val, true);
break;
#endregion
}
if (condition == null)
throw new InvalidOperationException($"Unrecognized operator (\"{oper}\").");
current.Add(condition);
}
conditions.Add(current.ToArray());
return new ObjectConditional(objectType, conditions.ToArray());
}
}
}

View file

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using Server.Network;
namespace Server.Commands.Generic
{
public class OnlineCommandImplementor : BaseCommandImplementor
{
public OnlineCommandImplementor()
{
Accessors = new[] { "Online" };
SupportRequirement = CommandSupport.Online;
SupportsConditionals = true;
AccessLevel = AccessLevel.GameMaster;
Usage = "Online <command> [condition]";
Description =
"Invokes the command on all mobiles that are currently logged in. Optional condition arguments can further restrict the set of objects.";
}
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
{
try
{
Extensions ext = Extensions.Parse(from, ref args);
if (!CheckObjectTypes(from, command, ext, out bool _, out bool mobiles))
return;
if (!mobiles) // sanity check
{
command.LogFailure("This command does not support items.");
return;
}
List<object> list = new List<object>();
List<NetState> states = NetState.Instances;
for (int i = 0; i < states.Count; ++i)
{
NetState ns = states[i];
Mobile mob = ns.Mobile;
if (mob == null)
continue;
if (!BaseCommand.IsAccessible(from, mob))
continue;
if (ext.IsValid(mob))
list.Add(mob);
}
ext.Filter(list);
obj = list;
}
catch (Exception ex)
{
from.SendMessage(ex.Message);
}
}
}
}

View file

@ -0,0 +1,79 @@
namespace Server.Commands.Generic
{
public class RangeCommandImplementor : BaseCommandImplementor
{
public RangeCommandImplementor()
{
Accessors = new[] { "Range" };
SupportRequirement = CommandSupport.Area;
SupportsConditionals = true;
AccessLevel = AccessLevel.GameMaster;
Usage = "Range <range> <command> [condition]";
Description =
"Invokes the command on all appropriate objects within a specified range of you. Optional condition arguments can further restrict the set of objects.";
Instance = this;
}
public static RangeCommandImplementor Instance{ get; private set; }
public override void Execute(CommandEventArgs e)
{
if (e.Length >= 2)
{
int range = e.GetInt32(0);
if (range < 0)
{
e.Mobile.SendMessage("The range must not be negative.");
}
else
{
Commands.TryGetValue(e.GetString(1), out BaseCommand command);
if (command == null)
{
e.Mobile.SendMessage(
"That is either an invalid command name or one that does not support this modifier.");
}
else if (e.Mobile.AccessLevel < command.AccessLevel)
{
e.Mobile.SendMessage("You do not have access to that command.");
}
else
{
string[] oldArgs = e.Arguments;
string[] args = new string[oldArgs.Length - 2];
for (int i = 0; i < args.Length; ++i)
args[i] = oldArgs[i + 2];
Process(range, e.Mobile, command, args);
}
}
}
else
{
e.Mobile.SendMessage("You must supply a range and a command name.");
}
}
public void Process(int range, Mobile from, BaseCommand command, string[] args)
{
AreaCommandImplementor impl = AreaCommandImplementor.Instance;
if (impl == null)
return;
Map map = from.Map;
if (map == null || map == Map.Internal)
return;
Point3D start = new Point3D(from.X - range, from.Y - range, from.Z);
Point3D end = new Point3D(from.X + range, from.Y + range, from.Z);
impl.OnTarget(from, map, start, end, command, args);
}
}
}

View file

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
namespace Server.Commands.Generic
{
public class RegionCommandImplementor : BaseCommandImplementor
{
public RegionCommandImplementor()
{
Accessors = new[] { "Region" };
SupportRequirement = CommandSupport.Region;
SupportsConditionals = true;
AccessLevel = AccessLevel.GameMaster;
Usage = "Region <command> [condition]";
Description =
"Invokes the command on all appropriate mobiles in your current region. Optional condition arguments can further restrict the set of objects.";
}
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
{
try
{
Extensions ext = Extensions.Parse(from, ref args);
if (!CheckObjectTypes(from, command, ext, out bool _, out bool mobiles))
return;
Region reg = from.Region;
List<object> list = new List<object>();
if (mobiles)
{
foreach (Mobile mob in reg.GetMobiles())
{
if (!BaseCommand.IsAccessible(from, mob))
continue;
if (ext.IsValid(mob))
list.Add(mob);
}
}
else
{
command.LogFailure("This command does not support items.");
return;
}
ext.Filter(list);
obj = list;
}
catch (Exception ex)
{
from.SendMessage(ex.Message);
}
}
}
}

View file

@ -0,0 +1,23 @@
namespace Server.Commands.Generic
{
public class ScreenCommandImplementor : BaseCommandImplementor
{
public ScreenCommandImplementor()
{
Accessors = new[] { "Screen" };
SupportRequirement = CommandSupport.Area;
SupportsConditionals = true;
AccessLevel = AccessLevel.GameMaster;
Usage = "Screen <command> [condition]";
Description =
"Invokes the command on all appropriate objects in your screen. Optional condition arguments can further restrict the set of objects.";
}
public override void Process(Mobile from, BaseCommand command, string[] args)
{
RangeCommandImplementor impl = RangeCommandImplementor.Instance;
impl?.Process(18, from, command, args);
}
}
}

View file

@ -0,0 +1,22 @@
namespace Server.Commands.Generic
{
public class SelfCommandImplementor : BaseCommandImplementor
{
public SelfCommandImplementor()
{
Accessors = new[] { "Self" };
SupportRequirement = CommandSupport.Self;
AccessLevel = AccessLevel.Counselor;
Usage = "Self <command>";
Description = "Invokes the command on the commanding player.";
}
public override void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
{
if (command.ObjectTypes == ObjectTypes.Items)
return; // sanity check
obj = from;
}
}
}

View file

@ -0,0 +1,96 @@
namespace Server.Commands.Generic
{
public class SerialCommandImplementor : BaseCommandImplementor
{
public SerialCommandImplementor()
{
Accessors = new[] { "Serial" };
SupportRequirement = CommandSupport.Single;
AccessLevel = AccessLevel.Counselor;
Usage = "Serial <serial> <command>";
Description = "Invokes the command on a single object by serial.";
}
public override void Execute(CommandEventArgs e)
{
if (e.Length >= 2)
{
Serial serial = e.GetUInt32(0);
object obj = null;
if (serial.IsItem)
obj = World.FindItem(serial);
else if (serial.IsMobile)
obj = World.FindMobile(serial);
if (obj == null)
{
e.Mobile.SendMessage("That is not a valid serial.");
}
else
{
Commands.TryGetValue(e.GetString(1), out BaseCommand command);
if (command == null)
{
e.Mobile.SendMessage(
"That is either an invalid command name or one that does not support this modifier.");
}
else if (e.Mobile.AccessLevel < command.AccessLevel)
{
e.Mobile.SendMessage("You do not have access to that command.");
}
else
{
switch (command.ObjectTypes)
{
// case ObjectTypes.Both:
// {
// if (!(obj is Item) && !(obj is Mobile))
// {
// e.Mobile.SendMessage("This command does not work on that.");
// return;
// }
//
// break;
// }
case ObjectTypes.Items:
{
if (!(obj is Item))
{
e.Mobile.SendMessage("This command only works on items.");
return;
}
break;
}
case ObjectTypes.Mobiles:
{
if (!(obj is Mobile))
{
e.Mobile.SendMessage("This command only works on mobiles.");
return;
}
break;
}
}
string[] oldArgs = e.Arguments;
string[] args = new string[oldArgs.Length - 2];
for (int i = 0; i < args.Length; ++i)
args[i] = oldArgs[i + 2];
RunCommand(e.Mobile, obj, command, args);
}
}
}
else
{
e.Mobile.SendMessage("You must supply an object serial and a command name.");
}
}
}
}

View file

@ -0,0 +1,89 @@
using Server.Targeting;
namespace Server.Commands.Generic
{
public class SingleCommandImplementor : BaseCommandImplementor
{
public SingleCommandImplementor()
{
Accessors = new[] { "Single" };
SupportRequirement = CommandSupport.Single;
AccessLevel = AccessLevel.Counselor;
Usage = "Single <command>";
Description =
"Invokes the command on a single targeted object. This is the same as just invoking the command directly.";
}
public override void Register(BaseCommand command)
{
base.Register(command);
for (int i = 0; i < command.Commands.Length; ++i)
CommandSystem.Register(command.Commands[i], command.AccessLevel, Redirect);
}
public void Redirect(CommandEventArgs e)
{
Commands.TryGetValue(e.Command, out BaseCommand command);
if (command == null)
e.Mobile.SendMessage("That is either an invalid command name or one that does not support this modifier.");
else if (e.Mobile.AccessLevel < command.AccessLevel)
e.Mobile.SendMessage("You do not have access to that command.");
else if (command.ValidateArgs(this, e))
Process(e.Mobile, command, e.Arguments);
}
public override void Process(Mobile from, BaseCommand command, string[] args)
{
if (command.ValidateArgs(this, new CommandEventArgs(from, command.Commands[0], GenerateArgString(args), args)))
from.BeginTarget(-1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None,
(m, targeted) => OnTarget(m, targeted, command, args));
}
public void OnTarget(Mobile from, object targeted, BaseCommand command, string[] args)
{
if (!BaseCommand.IsAccessible(from, targeted))
{
from.SendLocalizedMessage(500447); // That is not accessible.
return;
}
switch (command.ObjectTypes)
{
case ObjectTypes.Both:
{
if (!(targeted is Item) && !(targeted is Mobile))
{
from.SendMessage("This command does not work on that.");
return;
}
break;
}
case ObjectTypes.Items:
{
if (!(targeted is Item))
{
from.SendMessage("This command only works on items.");
return;
}
break;
}
case ObjectTypes.Mobiles:
{
if (!(targeted is Mobile))
{
from.SendMessage("This command only works on mobiles.");
return;
}
break;
}
}
RunCommand(from, targeted, command, args);
}
}
}