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,205 @@
/***************************************************************************
* AggressorInfo.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Server
{
public class AggressorInfo
{
private static Queue<AggressorInfo> m_Pool = new Queue<AggressorInfo>();
private Mobile m_Attacker, m_Defender;
private bool m_CanReportMurder;
private bool m_CriminalAggression;
private DateTime m_LastCombatTime;
private bool m_Queued;
private bool m_Reported;
private AggressorInfo(Mobile attacker, Mobile defender, bool criminal)
{
m_Attacker = attacker;
m_Defender = defender;
m_CanReportMurder = criminal;
m_CriminalAggression = criminal;
Refresh();
}
public static TimeSpan ExpireDelay{ get; set; } = TimeSpan.FromMinutes(2.0);
public bool Expired
{
get
{
if (m_Queued)
DumpAccess();
return m_Attacker.Deleted || m_Defender.Deleted || DateTime.UtcNow >= m_LastCombatTime + ExpireDelay;
}
}
public bool CriminalAggression
{
get
{
if (m_Queued)
DumpAccess();
return m_CriminalAggression;
}
set
{
if (m_Queued)
DumpAccess();
m_CriminalAggression = value;
}
}
public Mobile Attacker
{
get
{
if (m_Queued)
DumpAccess();
return m_Attacker;
}
}
public Mobile Defender
{
get
{
if (m_Queued)
DumpAccess();
return m_Defender;
}
}
public DateTime LastCombatTime
{
get
{
if (m_Queued)
DumpAccess();
return m_LastCombatTime;
}
}
public bool Reported
{
get
{
if (m_Queued)
DumpAccess();
return m_Reported;
}
set
{
if (m_Queued)
DumpAccess();
m_Reported = value;
}
}
public bool CanReportMurder
{
get
{
if (m_Queued)
DumpAccess();
return m_CanReportMurder;
}
set
{
if (m_Queued)
DumpAccess();
m_CanReportMurder = value;
}
}
public static AggressorInfo Create(Mobile attacker, Mobile defender, bool criminal)
{
AggressorInfo info;
if (m_Pool.Count > 0)
{
info = m_Pool.Dequeue();
info.m_Attacker = attacker;
info.m_Defender = defender;
info.m_CanReportMurder = criminal;
info.m_CriminalAggression = criminal;
info.m_Queued = false;
info.Refresh();
}
else
{
info = new AggressorInfo(attacker, defender, criminal);
}
return info;
}
public void Free()
{
if (m_Queued)
return;
m_Queued = true;
m_Pool.Enqueue(this);
}
public static void DumpAccess()
{
using (StreamWriter op = new StreamWriter("warnings.log", true))
{
op.WriteLine("Warning: Access to queued AggressorInfo:");
op.WriteLine(new StackTrace());
op.WriteLine();
op.WriteLine();
}
}
public void Refresh()
{
if (m_Queued)
DumpAccess();
m_LastCombatTime = DateTime.UtcNow;
m_Reported = false;
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,165 @@
/***************************************************************************
* Attributes.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Server
{
[AttributeUsage(AttributeTargets.Property)]
public class HueAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
public class BodyAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class PropertyObjectAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class NoSortAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
public class CallPriorityAttribute : Attribute
{
public CallPriorityAttribute(int priority)
{
Priority = priority;
}
public int Priority{ get; set; }
}
public class CallPriorityComparer : IComparer<MethodInfo>
{
public int Compare(MethodInfo x, MethodInfo y)
{
if (x == null && y == null)
return 0;
if (x == null)
return 1;
if (y == null)
return -1;
int xPriority = GetPriority(x);
int yPriority = GetPriority(y);
if (xPriority > yPriority)
return 1;
if (xPriority < yPriority)
return -1;
return 0;
}
private int GetPriority(MethodInfo mi)
{
object[] objs = mi.GetCustomAttributes(typeof(CallPriorityAttribute), true);
if (objs.Length == 0)
return 0;
if (!(objs[0] is CallPriorityAttribute attr))
return 0;
return attr.Priority;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class TypeAliasAttribute : Attribute
{
public TypeAliasAttribute(params string[] aliases)
{
Aliases = aliases;
}
public string[] Aliases{ get; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class ParsableAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
public class CustomEnumAttribute : Attribute
{
public CustomEnumAttribute(string[] names)
{
Names = names;
}
public string[] Names{ get; }
}
[AttributeUsage(AttributeTargets.Constructor)]
public class ConstructibleAttribute : Attribute
{
public ConstructibleAttribute() :
this(AccessLevel.Player) //Lowest accesslevel for current functionality (Level determined by access to [add)
{
}
public ConstructibleAttribute(AccessLevel accessLevel)
{
AccessLevel = accessLevel;
}
public AccessLevel AccessLevel{ get; set; }
}
[AttributeUsage(AttributeTargets.Property)]
public class CommandPropertyAttribute : Attribute
{
public CommandPropertyAttribute(AccessLevel level, bool readOnly)
{
ReadLevel = level;
ReadOnly = readOnly;
}
public CommandPropertyAttribute(AccessLevel level) : this(level, level)
{
}
public CommandPropertyAttribute(AccessLevel readLevel, AccessLevel writeLevel)
{
ReadLevel = readLevel;
WriteLevel = writeLevel;
}
public AccessLevel ReadLevel{ get; }
public AccessLevel WriteLevel{ get; }
public bool ReadOnly{ get; }
}
}

View file

@ -0,0 +1,106 @@
/***************************************************************************
* BaseVendor.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Collections.Generic;
namespace Server.Mobiles
{
public class BuyItemStateComparer : IComparer<BuyItemState>
{
public int Compare(BuyItemState l, BuyItemState r)
{
if (l == null && r == null) return 0;
if (l == null) return -1;
if (r == null) return 1;
return l.MySerial.CompareTo(r.MySerial);
}
}
public class BuyItemResponse
{
public BuyItemResponse(Serial serial, int amount)
{
Serial = serial;
Amount = amount;
}
public Serial Serial{ get; }
public int Amount{ get; }
}
public class SellItemResponse
{
public SellItemResponse(Item i, int amount)
{
Item = i;
Amount = amount;
}
public Item Item{ get; }
public int Amount{ get; }
}
public class SellItemState
{
public SellItemState(Item item, int price, string name)
{
Item = item;
Price = price;
Name = name;
}
public Item Item{ get; }
public int Price{ get; }
public string Name{ get; }
}
public class BuyItemState
{
public BuyItemState(string name, Serial cont, Serial serial, int price, int amount, int itemID, int hue)
{
Description = name;
ContainerSerial = cont;
MySerial = serial;
Price = price;
Amount = amount;
ItemID = itemID;
Hue = hue;
}
public int Price{ get; }
public Serial MySerial{ get; }
public Serial ContainerSerial{ get; }
public int ItemID{ get; }
public int Amount{ get; }
public int Hue{ get; }
public string Description{ get; }
}
}

206
Projects/Server/Body.cs Normal file
View file

@ -0,0 +1,206 @@
/***************************************************************************
* Body.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.IO;
namespace Server
{
public enum BodyType : byte
{
Empty,
Monster,
Sea,
Animal,
Human,
Equipment
}
public struct Body
{
private static BodyType[] m_Types;
static Body()
{
if (File.Exists("Data/bodyTable.cfg"))
{
using (StreamReader ip = new StreamReader("Data/bodyTable.cfg"))
{
m_Types = new BodyType[0x1000];
string line;
while ((line = ip.ReadLine()) != null)
{
if (line.Length == 0 || line.StartsWith("#"))
continue;
string[] split = line.Split('\t');
if (int.TryParse(split[0], out int bodyID) && Enum.TryParse(split[1], true, out BodyType type) && bodyID >= 0 &&
bodyID < m_Types.Length)
{
m_Types[bodyID] = type;
}
else
{
Console.WriteLine("Warning: Invalid bodyTable entry:");
Console.WriteLine(line);
}
}
}
}
else
{
Console.WriteLine("Warning: Data/bodyTable.cfg does not exist");
m_Types = new BodyType[0];
}
}
public Body(int bodyID)
{
BodyID = bodyID;
}
public BodyType Type => BodyID >= 0 && BodyID < m_Types.Length ? m_Types[BodyID] : BodyType.Empty;
public bool IsHuman => BodyID >= 0
&& BodyID < m_Types.Length
&& m_Types[BodyID] == BodyType.Human
&& BodyID != 402
&& BodyID != 403
&& BodyID != 607
&& BodyID != 608
&& BodyID != 694
&& BodyID != 695
&& BodyID != 970;
public bool IsGargoyle => BodyID == 666
|| BodyID == 667
|| BodyID == 694
|| BodyID == 695;
public bool IsMale => BodyID == 183
|| BodyID == 185
|| BodyID == 400
|| BodyID == 402
|| BodyID == 605
|| BodyID == 607
|| BodyID == 666
|| BodyID == 694
|| BodyID == 750;
public bool IsFemale => BodyID == 184
|| BodyID == 186
|| BodyID == 401
|| BodyID == 403
|| BodyID == 606
|| BodyID == 608
|| BodyID == 667
|| BodyID == 695
|| BodyID == 751;
public bool IsGhost => BodyID == 402
|| BodyID == 403
|| BodyID == 607
|| BodyID == 608
|| BodyID == 694
|| BodyID == 695
|| BodyID == 970;
public bool IsMonster => BodyID >= 0
&& BodyID < m_Types.Length
&& m_Types[BodyID] == BodyType.Monster;
public bool IsAnimal => BodyID >= 0
&& BodyID < m_Types.Length
&& m_Types[BodyID] == BodyType.Animal;
public bool IsEmpty => BodyID >= 0
&& BodyID < m_Types.Length
&& m_Types[BodyID] == BodyType.Empty;
public bool IsSea => BodyID >= 0
&& BodyID < m_Types.Length
&& m_Types[BodyID] == BodyType.Sea;
public bool IsEquipment => BodyID >= 0
&& BodyID < m_Types.Length
&& m_Types[BodyID] == BodyType.Equipment;
public int BodyID{ get; }
public static implicit operator int(Body a)
{
return a.BodyID;
}
public static implicit operator Body(int a)
{
return new Body(a);
}
public override string ToString()
{
return $"0x{BodyID:X}";
}
public override int GetHashCode()
{
return BodyID.GetHashCode();
}
public override bool Equals(object o)
{
return o is Body b && b.BodyID == BodyID;
}
public static bool operator ==(Body l, Body r)
{
return l.BodyID == r.BodyID;
}
public static bool operator !=(Body l, Body r)
{
return l.BodyID != r.BodyID;
}
public static bool operator >(Body l, Body r)
{
return l.BodyID > r.BodyID;
}
public static bool operator >=(Body l, Body r)
{
return l.BodyID >= r.BodyID;
}
public static bool operator <(Body l, Body r)
{
return l.BodyID < r.BodyID;
}
public static bool operator <=(Body l, Body r)
{
return l.BodyID <= r.BodyID;
}
}
}

View file

@ -0,0 +1,237 @@
/***************************************************************************
* ClientVersion.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
namespace Server
{
public enum ClientType
{
Regular,
UOTD,
God,
SA
}
public class ClientVersion : IComparable<ClientVersion>, IComparer<ClientVersion>
{
public ClientVersion(int maj, int min, int rev, int pat, ClientType type = ClientType.Regular)
{
Major = maj;
Minor = min;
Revision = rev;
Patch = pat;
Type = type;
SourceString = _ToStringImpl();
}
public ClientVersion(string fmt)
{
SourceString = fmt;
try
{
fmt = fmt.ToLower();
int br1 = fmt.IndexOf('.');
int br2 = fmt.IndexOf('.', br1 + 1);
int br3 = br2 + 1;
while (br3 < fmt.Length && char.IsDigit(fmt, br3))
br3++;
Major = Utility.ToInt32(fmt.Substring(0, br1));
Minor = Utility.ToInt32(fmt.Substring(br1 + 1, br2 - br1 - 1));
Revision = Utility.ToInt32(fmt.Substring(br2 + 1, br3 - br2 - 1));
if (br3 < fmt.Length)
{
if (Major <= 5 && Minor <= 0 && Revision <= 6) //Anything before 5.0.7
{
if (!char.IsWhiteSpace(fmt, br3))
Patch = fmt[br3] - 'a' + 1;
}
else
{
Patch = Utility.ToInt32(fmt.Substring(br3 + 1, fmt.Length - br3 - 1));
}
}
if (fmt.IndexOf("god") >= 0 || fmt.IndexOf("gq") >= 0)
Type = ClientType.God;
else if (fmt.IndexOf("third dawn") >= 0 || fmt.IndexOf("uo:td") >= 0 || fmt.IndexOf("uotd") >= 0 ||
fmt.IndexOf("uo3d") >= 0 || fmt.IndexOf("uo:3d") >= 0)
Type = ClientType.UOTD;
else
Type = ClientType.Regular;
}
catch
{
Major = 0;
Minor = 0;
Revision = 0;
Patch = 0;
Type = ClientType.Regular;
}
}
public int Major{ get; }
public int Minor{ get; }
public int Revision{ get; }
public int Patch{ get; }
public ClientType Type{ get; }
public string SourceString{ get; }
public int CompareTo(ClientVersion o)
{
if (o == null)
return 1;
if (Major > o.Major)
return 1;
if (Major < o.Major)
return -1;
if (Minor > o.Minor)
return 1;
if (Minor < o.Minor)
return -1;
if (Revision > o.Revision)
return 1;
if (Revision < o.Revision)
return -1;
if (Patch > o.Patch)
return 1;
if (Patch < o.Patch)
return -1;
return 0;
}
public static bool operator ==(ClientVersion l, ClientVersion r)
{
return Compare(l, r) == 0;
}
public static bool operator !=(ClientVersion l, ClientVersion r)
{
return Compare(l, r) != 0;
}
public static bool operator >=(ClientVersion l, ClientVersion r)
{
return Compare(l, r) >= 0;
}
public static bool operator >(ClientVersion l, ClientVersion r)
{
return Compare(l, r) > 0;
}
public static bool operator <=(ClientVersion l, ClientVersion r)
{
return Compare(l, r) <= 0;
}
public static bool operator <(ClientVersion l, ClientVersion r)
{
return Compare(l, r) < 0;
}
public override int GetHashCode()
{
return Major ^ Minor ^ Revision ^ Patch ^ (int)Type;
}
int IComparer<ClientVersion>.Compare(ClientVersion x, ClientVersion y)
{
return Compare(x, y);
}
public override bool Equals(object obj)
{
ClientVersion v = obj as ClientVersion;
return Major == v?.Major
&& Minor == v.Minor
&& Revision == v.Revision
&& Patch == v.Patch
&& Type == v.Type;
}
private string _ToStringImpl()
{
StringBuilder builder = new StringBuilder(16);
builder.Append(Major);
builder.Append('.');
builder.Append(Minor);
builder.Append('.');
builder.Append(Revision);
if (Major <= 5 && Minor <= 0 && Revision <= 6) //Anything before 5.0.7
{
if (Patch > 0)
builder.Append((char)('a' + (Patch - 1)));
}
else
{
builder.Append('.');
builder.Append(Patch);
}
if (Type != ClientType.Regular)
{
builder.Append(' ');
builder.Append(Type.ToString());
}
return builder.ToString();
}
public override string ToString()
{
return _ToStringImpl();
}
public static bool IsNull(object x)
{
return ReferenceEquals(x, null);
}
public static int Compare(ClientVersion a, ClientVersion b)
{
if (IsNull(a) && IsNull(b))
return 0;
if (IsNull(a))
return -1;
if (IsNull(b))
return 1;
return a.CompareTo(b);
}
}
}

242
Projects/Server/Commands.cs Normal file
View file

@ -0,0 +1,242 @@
/***************************************************************************
* Commands.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using Server.Network;
namespace Server.Commands
{
public delegate void CommandEventHandler(CommandEventArgs e);
public class CommandEventArgs : EventArgs
{
public CommandEventArgs(Mobile mobile, string command, string argString, string[] arguments)
{
Mobile = mobile;
Command = command;
ArgString = argString;
Arguments = arguments;
}
public Mobile Mobile{ get; }
public string Command{ get; }
public string ArgString{ get; }
public string[] Arguments{ get; }
public int Length => Arguments.Length;
public string GetString(int index)
{
if (index < 0 || index >= Arguments.Length)
return "";
return Arguments[index];
}
public int GetInt32(int index)
{
if (index < 0 || index >= Arguments.Length)
return 0;
return Utility.ToInt32(Arguments[index]);
}
public uint GetUInt32(int index)
{
if (index < 0 || index >= Arguments.Length)
return 0;
return Utility.ToUInt32(Arguments[index]);
}
public bool GetBoolean(int index)
{
if (index < 0 || index >= Arguments.Length)
return false;
return Utility.ToBoolean(Arguments[index]);
}
public double GetDouble(int index)
{
if (index < 0 || index >= Arguments.Length)
return 0.0;
return Utility.ToDouble(Arguments[index]);
}
public TimeSpan GetTimeSpan(int index)
{
if (index < 0 || index >= Arguments.Length)
return TimeSpan.Zero;
return Utility.ToTimeSpan(Arguments[index]);
}
}
public class CommandEntry : IComparable<CommandEntry>
{
public CommandEntry(string command, CommandEventHandler handler, AccessLevel accessLevel)
{
Command = command;
Handler = handler;
AccessLevel = accessLevel;
}
public string Command{ get; }
public CommandEventHandler Handler{ get; }
public AccessLevel AccessLevel{ get; }
public int CompareTo(CommandEntry e)
{
return e == null ? 1 : Command.CompareTo(e.Command);
}
}
public static class CommandSystem
{
public static string Prefix{ get; set; } = "[";
public static Dictionary<string, CommandEntry> Entries{ get; } =
new Dictionary<string, CommandEntry>(StringComparer.OrdinalIgnoreCase);
public static AccessLevel BadCommandIgnoreLevel{ get; set; } = AccessLevel.Player;
public static string[] Split(string value)
{
char[] array = value.ToCharArray();
List<string> list = new List<string>();
int start = 0;
while (start < array.Length)
{
char c = array[start];
if (c == '"')
{
++start;
int end = start;
while (end < array.Length)
if (array[end] != '"' || array[end - 1] == '\\')
++end;
else
break;
list.Add(value.Substring(start, end - start));
start = end + 2;
}
else if (c != ' ')
{
int end = start;
while (end < array.Length)
if (array[end] != ' ')
++end;
else
break;
list.Add(value.Substring(start, end - start));
start = end + 1;
}
else
{
++start;
}
}
return list.ToArray();
}
public static void Register(string command, AccessLevel access, CommandEventHandler handler)
{
Entries[command] = new CommandEntry(command, handler, access);
}
public static bool Handle(Mobile from, string text, MessageType type = MessageType.Regular)
{
if (!text.StartsWith(Prefix) && type != MessageType.Command)
return false;
if (type != MessageType.Command)
text = text.Substring(Prefix.Length);
int indexOf = text.IndexOf(' ');
string command;
string[] args;
string argString;
if (indexOf >= 0)
{
argString = text.Substring(indexOf + 1);
command = text.Substring(0, indexOf);
args = Split(argString);
}
else
{
argString = "";
command = text.ToLower();
args = new string[0];
}
Entries.TryGetValue(command, out CommandEntry entry);
if (entry != null)
{
if (from.AccessLevel >= entry.AccessLevel)
{
if (entry.Handler != null)
{
CommandEventArgs e = new CommandEventArgs(from, command, argString, args);
entry.Handler(e);
EventSink.InvokeCommand(e);
}
}
else
{
if (from.AccessLevel <= BadCommandIgnoreLevel)
return false;
from.SendMessage("You do not have access to that command.");
}
}
else
{
if (from.AccessLevel <= BadCommandIgnoreLevel)
return false;
from.SendMessage("That is not a valid command.");
}
return true;
}
}
}

View file

@ -0,0 +1,93 @@
/***************************************************************************
* ContextMenu.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Collections.Generic;
namespace Server.ContextMenus
{
/// <summary>
/// Represents the state of an active context menu. This includes who opened the menu, the menu's focus object, and a list of
/// <see cref="ContextMenuEntry">entries</see> that the menu is composed of.
/// <seealso cref="ContextMenuEntry" />
/// </summary>
public class ContextMenu
{
/// <summary>
/// Instantiates a new ContextMenu instance.
/// </summary>
/// <param name="from">
/// The <see cref="Mobile" /> who opened this ContextMenu.
/// <seealso cref="From" />
/// </param>
/// <param name="target">
/// The <see cref="Mobile" /> or <see cref="Item" /> for which this ContextMenu is on.
/// <seealso cref="Target" />
/// </param>
public ContextMenu(Mobile from, object target)
{
From = from;
Target = target;
List<ContextMenuEntry> list = new List<ContextMenuEntry>();
if (target is Mobile mobile)
mobile.GetContextMenuEntries(from, list);
else if (target is Item item)
item.GetContextMenuEntries(from, list);
//m_Entries = (ContextMenuEntry[])list.ToArray( typeof( ContextMenuEntry ) );
Entries = list.ToArray();
for (int i = 0; i < Entries.Length; ++i)
Entries[i].Owner = this;
}
/// <summary>
/// Gets the <see cref="Mobile" /> who opened this ContextMenu.
/// </summary>
public Mobile From{ get; }
/// <summary>
/// Gets an object of the <see cref="Mobile" /> or <see cref="Item" /> for which this ContextMenu is on.
/// </summary>
public object Target{ get; }
/// <summary>
/// Gets the list of <see cref="ContextMenuEntry">entries</see> contained in this ContextMenu.
/// </summary>
public ContextMenuEntry[] Entries{ get; }
/// <summary>
/// Returns true if this ContextMenu requires packet version 2.
/// </summary>
public bool RequiresNewPacket
{
get
{
for (int i = 0; i < Entries.Length; ++i)
if (Entries[i].Number < 3000000 || Entries[i].Number > 3032767)
return true;
return false;
}
}
}
}

View file

@ -0,0 +1,98 @@
/***************************************************************************
* ContextMenuEntry.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.ContextMenus
{
/// <summary>
/// Represents a single entry of a <see cref="ContextMenu">context menu</see>.
/// <seealso cref="ContextMenu" />
/// </summary>
public class ContextMenuEntry
{
/// <summary>
/// Instantiates a new ContextMenuEntry with a given <see cref="Number">localization number</see> (<paramref name="number" />)
/// and <see cref="Range">maximum range</see> (<paramref name="range" />).
/// </summary>
/// <param name="number">
/// The localization number containing the name of this entry.
/// <seealso cref="Number" />
/// </param>
/// <param name="range">
/// The maximum range at which this entry can be used.
/// <seealso cref="Range" />
/// </param>
public ContextMenuEntry(int number, int range = -1)
{
if (number <= 0x7FFF) // Legacy code support
Number = 3000000 + number;
else
Number = number;
Range = range;
Enabled = true;
Color = 0xFFFF;
}
/// <summary>
/// Gets or sets additional <see cref="CMEFlags">flags</see> used in client communication.
/// </summary>
public CMEFlags Flags{ get; set; }
/// <summary>
/// Gets or sets the <see cref="ContextMenu" /> that owns this entry.
/// </summary>
public ContextMenu Owner{ get; set; }
/// <summary>
/// Gets or sets the localization number containing the name of this entry.
/// </summary>
public int Number{ get; set; }
/// <summary>
/// Gets or sets the maximum range at which this entry may be used, in tiles. A value of -1 signifies no maximum range.
/// </summary>
public int Range{ get; set; }
/// <summary>
/// Gets or sets the color for this entry. Format is A1-R5-G5-B5.
/// </summary>
public int Color{ get; set; }
/// <summary>
/// Gets or sets whether this entry is enabled. When false, the entry will appear in a gray hue and <see cref="OnClick" />
/// will never be invoked.
/// </summary>
public bool Enabled{ get; set; }
/// <summary>
/// Gets a value indicating if non local use of this entry is permitted.
/// </summary>
public virtual bool NonLocalUse => false;
/// <summary>
/// Overridable. Virtual event invoked when the entry is clicked.
/// </summary>
public virtual void OnClick()
{
}
}
}

View file

@ -0,0 +1,37 @@
/***************************************************************************
* OpenBackpackEntry.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server.ContextMenus
{
public class OpenBackpackEntry : ContextMenuEntry
{
private Mobile m_Mobile;
public OpenBackpackEntry(Mobile m) : base(6145)
{
m_Mobile = m;
}
public override void OnClick()
{
m_Mobile.Use(m_Mobile.Backpack);
}
}
}

View file

@ -0,0 +1,38 @@
/***************************************************************************
* PaperdollEntry.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server.ContextMenus
{
public class PaperdollEntry : ContextMenuEntry
{
private Mobile m_Mobile;
public PaperdollEntry(Mobile m) : base(6123, 18)
{
m_Mobile = m;
}
public override void OnClick()
{
if (m_Mobile.CanPaperdollBeOpenedBy(Owner.From))
m_Mobile.DisplayPaperdollTo(Owner.From);
}
}
}

View file

@ -0,0 +1,88 @@
/***************************************************************************
* PacketProfile.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace Server.Diagnostics
{
public abstract class BaseProfile
{
private Stopwatch _stopwatch;
protected BaseProfile(string name)
{
Name = name;
_stopwatch = new Stopwatch();
}
public string Name{ get; }
public long Count{ get; private set; }
public TimeSpan AverageTime => TimeSpan.FromTicks(TotalTime.Ticks / Math.Max(1, Count));
public TimeSpan PeakTime{ get; private set; }
public TimeSpan TotalTime{ get; private set; }
public static void WriteAll<T>(TextWriter op, IEnumerable<T> profiles) where T : BaseProfile
{
List<T> list = new List<T>(profiles);
list.Sort(delegate(T a, T b) { return -a.TotalTime.CompareTo(b.TotalTime); });
foreach (T prof in list)
{
prof.WriteTo(op);
op.WriteLine();
}
}
public virtual void Start()
{
if (_stopwatch.IsRunning) _stopwatch.Reset();
_stopwatch.Start();
}
public virtual void Finish()
{
TimeSpan elapsed = _stopwatch.Elapsed;
TotalTime += elapsed;
if (elapsed > PeakTime) PeakTime = elapsed;
Count++;
_stopwatch.Reset();
}
public virtual void WriteTo(TextWriter op)
{
op.Write("{0,-100} {1,12:N0} {2,12:F5} {3,-12:F5} {4,12:F5}", Name, Count, AverageTime.TotalSeconds,
PeakTime.TotalSeconds, TotalTime.TotalSeconds);
}
}
}

View file

@ -0,0 +1,47 @@
/***************************************************************************
* PacketProfile.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
namespace Server.Diagnostics
{
public class GumpProfile : BaseProfile
{
private static Dictionary<Type, GumpProfile> _profiles = new Dictionary<Type, GumpProfile>();
public GumpProfile(Type type) : base(type.FullName)
{
}
public static IEnumerable<GumpProfile> Profiles => _profiles.Values;
public static GumpProfile Acquire(Type type)
{
if (!Core.Profiling)
return null;
if (!_profiles.TryGetValue(type, out GumpProfile prof))
_profiles.Add(type, prof = new GumpProfile(type));
return prof;
}
}
}

View file

@ -0,0 +1,108 @@
/***************************************************************************
* PacketProfile.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Server.Diagnostics
{
public abstract class BasePacketProfile : BaseProfile
{
protected BasePacketProfile(string name) : base(name)
{
}
public long TotalLength{ get; private set; }
public double AverageLength => (double)TotalLength / Math.Max(1, Count);
public void Finish(long length)
{
Finish();
TotalLength += length;
}
public override void WriteTo(TextWriter op)
{
base.WriteTo(op);
op.Write("\t{0,12:F2} {1,-12:N0}", AverageLength, TotalLength);
}
}
public class PacketSendProfile : BasePacketProfile
{
private static Dictionary<Type, PacketSendProfile> _profiles = new Dictionary<Type, PacketSendProfile>();
private long _created;
public PacketSendProfile(Type type) : base(type.FullName)
{
}
public static IEnumerable<PacketSendProfile> Profiles => _profiles.Values;
[MethodImpl(MethodImplOptions.Synchronized)]
public static PacketSendProfile Acquire(Type type)
{
if (!_profiles.TryGetValue(type, out PacketSendProfile prof))
_profiles.Add(type, prof = new PacketSendProfile(type));
return prof;
}
public void Increment()
{
Interlocked.Increment(ref _created);
}
public override void WriteTo(TextWriter op)
{
base.WriteTo(op);
op.Write("\t{0,12:N0}", _created);
}
}
public class PacketReceiveProfile : BasePacketProfile
{
private static Dictionary<int, PacketReceiveProfile> _profiles = new Dictionary<int, PacketReceiveProfile>();
public PacketReceiveProfile(int packetId)
: base($"0x{packetId:X2}")
{
}
public static IEnumerable<PacketReceiveProfile> Profiles => _profiles.Values;
[MethodImpl(MethodImplOptions.Synchronized)]
public static PacketReceiveProfile Acquire(int packetId)
{
if (!_profiles.TryGetValue(packetId, out PacketReceiveProfile prof))
_profiles.Add(packetId, prof = new PacketReceiveProfile(packetId));
return prof;
}
}
}

View file

@ -0,0 +1,48 @@
/***************************************************************************
* PacketProfile.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
namespace Server.Diagnostics
{
public class TargetProfile : BaseProfile
{
private static Dictionary<Type, TargetProfile> _profiles = new Dictionary<Type, TargetProfile>();
public TargetProfile(Type type)
: base(type.FullName)
{
}
public static IEnumerable<TargetProfile> Profiles => _profiles.Values;
public static TargetProfile Acquire(Type type)
{
if (!Core.Profiling)
return null;
if (!_profiles.TryGetValue(type, out TargetProfile prof))
_profiles.Add(type, prof = new TargetProfile(type));
return prof;
}
}
}

View file

@ -0,0 +1,61 @@
/***************************************************************************
* PacketProfile.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Collections.Generic;
using System.IO;
namespace Server.Diagnostics
{
public class TimerProfile : BaseProfile
{
private static Dictionary<string, TimerProfile> _profiles = new Dictionary<string, TimerProfile>();
public TimerProfile(string name)
: base(name)
{
}
public static IEnumerable<TimerProfile> Profiles => _profiles.Values;
public long Created{ get; set; }
public long Started{ get; set; }
public long Stopped{ get; set; }
public static TimerProfile Acquire(string name)
{
if (!Core.Profiling)
return null;
if (!_profiles.TryGetValue(name, out TimerProfile prof))
_profiles.Add(name, prof = new TimerProfile(name));
return prof;
}
public override void WriteTo(TextWriter op)
{
base.WriteTo(op);
op.Write("\t{0,12:N0} {1,12:N0} {2,-12:N0}", Created, Started, Stopped);
}
}
}

411
Projects/Server/Effects.cs Normal file
View file

@ -0,0 +1,411 @@
/***************************************************************************
* Effects.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server
{
public enum EffectLayer
{
Head = 0,
RightHand = 1,
LeftHand = 2,
Waist = 3,
LeftFoot = 4,
RightFoot = 5,
CenterFeet = 7
}
public enum ParticleSupportType
{
Full,
Detect,
None
}
public static class Effects
{
public static ParticleSupportType ParticleSupportType{ get; set; } = ParticleSupportType.Detect;
public static bool SendParticlesTo(NetState state)
{
return ParticleSupportType == ParticleSupportType.Full ||
ParticleSupportType == ParticleSupportType.Detect && state.IsUOTDClient;
}
public static void PlaySound(IPoint3D p, Map map, int soundID)
{
if (soundID <= -1)
return;
if (map != null)
{
Packet playSound = null;
IPooledEnumerable<NetState> eable = map.GetClientsInRange(new Point3D(p));
foreach (NetState state in eable)
{
state.Mobile.ProcessDelta();
if (playSound == null)
playSound = Packet.Acquire(new PlaySound(soundID, p));
state.Send(playSound);
}
Packet.Release(playSound);
eable.Free();
}
}
public static void SendBoltEffect(IEntity e)
{
SendBoltEffect(e, true, 0);
}
public static void SendBoltEffect(IEntity e, bool sound)
{
SendBoltEffect(e, sound, 0);
}
public static void SendBoltEffect(IEntity e, bool sound, int hue)
{
Map map = e.Map;
if (map == null)
return;
e.ProcessDelta();
Packet preEffect = null, boltEffect = null, playSound = null;
IPooledEnumerable<NetState> eable = map.GetClientsInRange(e.Location);
foreach (NetState state in eable)
if (state.Mobile.CanSee(e))
{
if (SendParticlesTo(state))
{
if (preEffect == null)
preEffect = Packet.Acquire(new TargetParticleEffect(e, 0, 10, 5, 0, 0, 5031, 3, 0));
state.Send(preEffect);
}
if (boltEffect == null)
boltEffect = Packet.Acquire(new BoltEffect(e, hue));
state.Send(boltEffect);
if (sound)
{
if (playSound == null)
playSound = Packet.Acquire(new PlaySound(0x29, e));
state.Send(playSound);
}
}
Packet.Release(preEffect);
Packet.Release(boltEffect);
Packet.Release(playSound);
eable.Free();
}
public static void SendLocationEffect(IPoint3D p, Map map, int itemID, int duration)
{
SendLocationEffect(p, map, itemID, duration, 10, 0, 0);
}
public static void SendLocationEffect(IPoint3D p, Map map, int itemID, int duration, int speed)
{
SendLocationEffect(p, map, itemID, duration, speed, 0, 0);
}
public static void SendLocationEffect(IPoint3D p, Map map, int itemID, int duration, int hue, int renderMode)
{
SendLocationEffect(p, map, itemID, duration, 10, hue, renderMode);
}
public static void SendLocationEffect(IPoint3D p, Map map, int itemID, int duration, int speed, int hue,
int renderMode)
{
SendPacket(p, map, new LocationEffect(p, itemID, speed, duration, hue, renderMode));
}
public static void SendLocationParticles(IEntity e, int itemID, int speed, int duration, int effect)
{
SendLocationParticles(e, itemID, speed, duration, 0, 0, effect, 0);
}
public static void SendLocationParticles(IEntity e, int itemID, int speed, int duration, int effect, int unknown)
{
SendLocationParticles(e, itemID, speed, duration, 0, 0, effect, unknown);
}
public static void SendLocationParticles(IEntity e, int itemID, int speed, int duration, int hue, int renderMode,
int effect, int unknown)
{
Map map = e.Map;
if (map != null)
{
Packet particles = null, regular = null;
IPooledEnumerable<NetState> eable = map.GetClientsInRange(e.Location);
foreach (NetState state in eable)
{
state.Mobile.ProcessDelta();
if (SendParticlesTo(state))
{
if (particles == null)
particles = Packet.Acquire(new LocationParticleEffect(e, itemID, speed, duration, hue,
renderMode, effect, unknown));
state.Send(particles);
}
else if (itemID != 0)
{
if (regular == null)
regular = Packet.Acquire(new LocationEffect(e, itemID, speed, duration, hue, renderMode));
state.Send(regular);
}
}
Packet.Release(particles);
Packet.Release(regular);
eable.Free();
}
//SendPacket( e.Location, e.Map, new LocationParticleEffect( e, itemID, speed, duration, hue, renderMode, effect, unknown ) );
}
public static void SendTargetEffect(IEntity target, int itemID, int duration)
{
SendTargetEffect(target, itemID, duration, 0, 0);
}
public static void SendTargetEffect(IEntity target, int itemID, int speed, int duration)
{
SendTargetEffect(target, itemID, speed, duration, 0, 0);
}
public static void SendTargetEffect(IEntity target, int itemID, int duration, int hue, int renderMode)
{
SendTargetEffect(target, itemID, 10, duration, hue, renderMode);
}
public static void SendTargetEffect(IEntity target, int itemID, int speed, int duration, int hue, int renderMode)
{
if (target is Mobile mobile)
mobile.ProcessDelta();
SendPacket(target.Location, target.Map, new TargetEffect(target, itemID, speed, duration, hue, renderMode));
}
public static void SendTargetParticles(IEntity target, int itemID, int speed, int duration, int effect,
EffectLayer layer)
{
SendTargetParticles(target, itemID, speed, duration, 0, 0, effect, layer, 0);
}
public static void SendTargetParticles(IEntity target, int itemID, int speed, int duration, int effect,
EffectLayer layer, int unknown)
{
SendTargetParticles(target, itemID, speed, duration, 0, 0, effect, layer, unknown);
}
public static void SendTargetParticles(IEntity target, int itemID, int speed, int duration, int hue, int renderMode,
int effect, EffectLayer layer, int unknown)
{
if (target is Mobile mobile)
mobile.ProcessDelta();
Map map = target.Map;
if (map != null)
{
Packet particles = null, regular = null;
IPooledEnumerable<NetState> eable = map.GetClientsInRange(target.Location);
foreach (NetState state in eable)
{
state.Mobile.ProcessDelta();
if (SendParticlesTo(state))
{
if (particles == null)
particles = Packet.Acquire(new TargetParticleEffect(target, itemID, speed, duration, hue,
renderMode, effect, (int)layer, unknown));
state.Send(particles);
}
else if (itemID != 0)
{
if (regular == null)
regular = Packet.Acquire(new TargetEffect(target, itemID, speed, duration, hue, renderMode));
state.Send(regular);
}
}
Packet.Release(particles);
Packet.Release(regular);
eable.Free();
}
//SendPacket( target.Location, target.Map, new TargetParticleEffect( target, itemID, speed, duration, hue, renderMode, effect, (int)layer, unknown ) );
}
public static void SendMovingEffect(IEntity from, IEntity to, int itemID, int speed, int duration,
bool fixedDirection, bool explodes, int hue = 0, int renderMode = 0)
{
if (from is Mobile mobile)
mobile.ProcessDelta();
if (to is Mobile mobile1)
mobile1.ProcessDelta();
SendPacket(from.Location, from.Map,
new MovingEffect(from, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode));
}
public static void SendMovingParticles(IEntity from, IEntity to, int itemID, int speed, int duration,
bool fixedDirection, bool explodes, int effect, int explodeEffect, int explodeSound)
{
SendMovingParticles(from, to, itemID, speed, duration, fixedDirection, explodes, 0, 0, effect, explodeEffect,
explodeSound, 0);
}
public static void SendMovingParticles(IEntity from, IEntity to, int itemID, int speed, int duration,
bool fixedDirection, bool explodes, int effect, int explodeEffect, int explodeSound, int unknown)
{
SendMovingParticles(from, to, itemID, speed, duration, fixedDirection, explodes, 0, 0, effect, explodeEffect,
explodeSound, unknown);
}
public static void SendMovingParticles(IEntity from, IEntity to, int itemID, int speed, int duration,
bool fixedDirection, bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound,
int unknown)
{
SendMovingParticles(from, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode, effect,
explodeEffect, explodeSound, (EffectLayer)255, unknown);
}
public static void SendMovingParticles(IEntity from, IEntity to, int itemID, int speed, int duration,
bool fixedDirection, bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound,
EffectLayer layer, int unknown)
{
if (from is Mobile fromMob)
fromMob.ProcessDelta();
if (to is Mobile toMob)
toMob.ProcessDelta();
Map map = from.Map;
if (map != null)
{
Packet particles = null, regular = null;
IPooledEnumerable<NetState> eable = map.GetClientsInRange(from.Location);
foreach (NetState state in eable)
{
state.Mobile.ProcessDelta();
if (SendParticlesTo(state))
{
if (particles == null)
particles = Packet.Acquire(new MovingParticleEffect(from, to, itemID, speed, duration,
fixedDirection, explodes, hue, renderMode, effect, explodeEffect, explodeSound, layer,
unknown));
state.Send(particles);
}
else if (itemID > 1)
{
if (regular == null)
regular = Packet.Acquire(new MovingEffect(from, to, itemID, speed, duration, fixedDirection,
explodes, hue, renderMode));
state.Send(regular);
}
}
Packet.Release(particles);
Packet.Release(regular);
eable.Free();
}
//SendPacket( from.Location, from.Map, new MovingParticleEffect( from, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode, effect, explodeEffect, explodeSound, unknown ) );
}
public static void SendPacket(Point3D origin, Map map, Packet p)
{
if (map == null)
return;
IPooledEnumerable<NetState> eable = map.GetClientsInRange(origin);
p.Acquire();
foreach (NetState state in eable)
{
state.Mobile.ProcessDelta();
state.Send(p);
}
p.Release();
eable.Free();
}
public static void SendPacket(IPoint3D origin, Map map, Packet p)
{
if (map == null)
return;
IPooledEnumerable<NetState> eable = map.GetClientsInRange(new Point3D(origin));
p.Acquire();
foreach (NetState state in eable)
{
state.Mobile.ProcessDelta();
state.Send(p);
}
p.Release();
eable.Free();
}
}
}

1132
Projects/Server/EventSink.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,346 @@
/***************************************************************************
* ExpansionInfo.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
namespace Server
{
public enum Expansion
{
None,
T2A,
UOR,
UOTD,
LBR,
AOS,
SE,
ML,
SA,
HS,
TOL
}
[Flags]
public enum ClientFlags
{
None = 0x00000000,
Felucca = 0x00000001,
Trammel = 0x00000002,
Ilshenar = 0x00000004,
Malas = 0x00000008,
Tokuno = 0x00000010,
TerMur = 0x00000020,
Unk1 = 0x00000040,
Unk2 = 0x00000080,
UOTD = 0x00000100
}
[Flags]
public enum FeatureFlags
{
None = 0x00000000,
T2A = 0x00000001,
UOR = 0x00000002,
UOTD = 0x00000004,
LBR = 0x00000008,
AOS = 0x00000010,
SixthCharacterSlot = 0x00000020,
SE = 0x00000040,
ML = 0x00000080,
EigthAge = 0x00000100,
NinthAge = 0x00000200, /* Crystal/Shadow Custom House Tiles */
TenthAge = 0x00000400,
IncreasedStorage = 0x00000800, /* Increased Housing/Bank Storage */
SeventhCharacterSlot = 0x00001000,
RoleplayFaces = 0x00002000,
TrialAccount = 0x00004000,
LiveAccount = 0x00008000,
SA = 0x00010000,
HS = 0x00020000,
Gothic = 0x00040000,
Rustic = 0x00080000,
Jungle = 0x00100000,
Shadowguard = 0x00200000,
TOL = 0x00400000,
ExpansionNone = None,
ExpansionT2A = T2A,
ExpansionUOR = ExpansionT2A | UOR,
ExpansionUOTD = ExpansionUOR | UOTD,
ExpansionLBR = ExpansionUOTD | LBR,
ExpansionAOS = ExpansionLBR | AOS | LiveAccount,
ExpansionSE = ExpansionAOS | SE,
ExpansionML = ExpansionSE | ML | NinthAge,
ExpansionSA = ExpansionML | SA | Gothic | Rustic,
ExpansionHS = ExpansionSA | HS,
ExpansionTOL = ExpansionHS | TOL | Jungle | Shadowguard
}
[Flags]
public enum CharacterListFlags
{
None = 0x00000000,
Unk1 = 0x00000001,
OverwriteConfigButton = 0x00000002,
OneCharacterSlot = 0x00000004,
ContextMenus = 0x00000008,
SlotLimit = 0x00000010,
AOS = 0x00000020,
SixthCharacterSlot = 0x00000040,
SE = 0x00000080,
ML = 0x00000100,
Unk2 = 0x00000200,
UO3DClientType = 0x00000400,
Unk3 = 0x00000800,
SeventhCharacterSlot = 0x00001000,
Unk4 = 0x00002000,
NewMovementSystem = 0x00004000,
NewFeluccaAreas = 0x00008000,
ExpansionNone = ContextMenus, //
ExpansionT2A = ContextMenus, //
ExpansionUOR = ContextMenus, // None
ExpansionUOTD = ContextMenus, //
ExpansionLBR = ContextMenus, //
ExpansionAOS = ContextMenus | AOS,
ExpansionSE = ExpansionAOS | SE,
ExpansionML = ExpansionSE | ML,
ExpansionSA = ExpansionML,
ExpansionHS = ExpansionSA,
ExpansionTOL = ExpansionHS
}
[Flags]
public enum HousingFlags
{
None = 0x0,
AOS = 0x10,
SE = 0x40,
ML = 0x80,
Crystal = 0x200,
SA = 0x10000,
HS = 0x20000,
Gothic = 0x40000,
Rustic = 0x80000,
Jungle = 0x100000,
Shadowguard = 0x200000,
TOL = 0x400000,
HousingAOS = AOS,
HousingSE = HousingAOS | SE,
HousingML = HousingSE | ML | Crystal,
HousingSA = HousingML | SA | Gothic | Rustic,
HousingHS = HousingSA | HS,
HousingTOL = HousingHS | TOL | Jungle | Shadowguard
}
public class ExpansionInfo
{
static ExpansionInfo()
{
Table = new[]
{
new ExpansionInfo(
0,
"None",
ClientFlags.None,
FeatureFlags.ExpansionNone,
CharacterListFlags.ExpansionNone,
HousingFlags.None),
new ExpansionInfo(
1,
"The Second Age",
ClientFlags.Felucca,
FeatureFlags.ExpansionT2A,
CharacterListFlags.ExpansionT2A,
HousingFlags.None),
new ExpansionInfo(
2,
"Renaissance",
ClientFlags.Trammel,
FeatureFlags.ExpansionUOR,
CharacterListFlags.ExpansionUOR,
HousingFlags.None),
new ExpansionInfo(
3,
"Third Dawn",
ClientFlags.Ilshenar,
FeatureFlags.ExpansionUOTD,
CharacterListFlags.ExpansionUOTD,
HousingFlags.None),
new ExpansionInfo(
4,
"Blackthorn's Revenge",
ClientFlags.Ilshenar,
FeatureFlags.ExpansionLBR,
CharacterListFlags.ExpansionLBR,
HousingFlags.None),
new ExpansionInfo(
5,
"Age of Shadows",
ClientFlags.Malas,
FeatureFlags.ExpansionAOS,
CharacterListFlags.ExpansionAOS,
HousingFlags.HousingAOS),
new ExpansionInfo(
6,
"Samurai Empire",
ClientFlags.Tokuno,
FeatureFlags.ExpansionSE,
CharacterListFlags.ExpansionSE,
HousingFlags.HousingSE),
new ExpansionInfo(
7,
"Mondain's Legacy",
new ClientVersion("5.0.0a"),
FeatureFlags.ExpansionML,
CharacterListFlags.ExpansionML,
HousingFlags.HousingML),
new ExpansionInfo(
8,
"Stygian Abyss",
ClientFlags.TerMur,
FeatureFlags.ExpansionSA,
CharacterListFlags.ExpansionSA,
HousingFlags.HousingSA),
new ExpansionInfo(
9,
"High Seas",
new ClientVersion("7.0.9.0"),
FeatureFlags.ExpansionHS,
CharacterListFlags.ExpansionHS,
HousingFlags.HousingHS),
new ExpansionInfo(
10,
"Time of Legends",
new ClientVersion("7.0.45.65"),
FeatureFlags.ExpansionTOL,
CharacterListFlags.ExpansionTOL,
HousingFlags.HousingTOL)
};
}
public ExpansionInfo(
int id,
string name,
ClientFlags clientFlags,
FeatureFlags supportedFeatures,
CharacterListFlags charListFlags,
HousingFlags customHousingFlag)
: this(id, name, supportedFeatures, charListFlags, customHousingFlag)
{
ClientFlags = clientFlags;
}
public ExpansionInfo(
int id,
string name,
ClientVersion requiredClient,
FeatureFlags supportedFeatures,
CharacterListFlags charListFlags,
HousingFlags customHousingFlag)
: this(id, name, supportedFeatures, charListFlags, customHousingFlag)
{
RequiredClient = requiredClient;
}
private ExpansionInfo(
int id,
string name,
FeatureFlags supportedFeatures,
CharacterListFlags charListFlags,
HousingFlags customHousingFlag)
{
ID = id;
Name = name;
SupportedFeatures = supportedFeatures;
CharacterListFlags = charListFlags;
CustomHousingFlag = customHousingFlag;
}
public static ExpansionInfo CoreExpansion => GetInfo(Core.Expansion);
public static ExpansionInfo[] Table{ get; }
public int ID{ get; }
public string Name{ get; set; }
public ClientFlags ClientFlags{ get; set; }
public FeatureFlags SupportedFeatures{ get; set; }
public CharacterListFlags CharacterListFlags{ get; set; }
public ClientVersion RequiredClient{ get; set; }
public HousingFlags CustomHousingFlag{ get; set; }
public static FeatureFlags GetFeatures(Expansion ex)
{
ExpansionInfo info = GetInfo(ex);
if (info != null) return info.SupportedFeatures;
switch (ex)
{
case Expansion.None:
return FeatureFlags.ExpansionNone;
case Expansion.T2A:
return FeatureFlags.ExpansionT2A;
case Expansion.UOR:
return FeatureFlags.ExpansionUOR;
case Expansion.UOTD:
return FeatureFlags.ExpansionUOTD;
case Expansion.LBR:
return FeatureFlags.ExpansionLBR;
case Expansion.AOS:
return FeatureFlags.ExpansionAOS;
case Expansion.SE:
return FeatureFlags.ExpansionSE;
case Expansion.ML:
return FeatureFlags.ExpansionML;
case Expansion.SA:
return FeatureFlags.ExpansionSA;
case Expansion.HS:
return FeatureFlags.ExpansionHS;
case Expansion.TOL:
return FeatureFlags.ExpansionTOL;
}
return FeatureFlags.ExpansionNone;
}
public static ExpansionInfo GetInfo(Expansion ex)
{
return GetInfo((int)ex);
}
public static ExpansionInfo GetInfo(int ex)
{
int v = ex;
if (v < 0 || v >= Table.Length) v = 0;
return Table[v];
}
public override string ToString()
{
return Name;
}
}
}

479
Projects/Server/Geometry.cs Normal file
View file

@ -0,0 +1,479 @@
/***************************************************************************
* Geometry.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
namespace Server
{
[Parsable]
public struct Point2D : IPoint2D, IComparable<Point2D>
{
internal int m_X;
internal int m_Y;
public static readonly Point2D Zero = new Point2D(0, 0);
public Point2D(int x, int y)
{
m_X = x;
m_Y = y;
}
public Point2D(IPoint2D p) : this(p.X, p.Y)
{
}
[CommandProperty(AccessLevel.Counselor)]
public int X
{
get => m_X;
set => m_X = value;
}
[CommandProperty(AccessLevel.Counselor)]
public int Y
{
get => m_Y;
set => m_Y = value;
}
public override string ToString()
{
return $"({m_X}, {m_Y})";
}
public static Point2D Parse(string value)
{
int start = value.IndexOf('(');
int end = value.IndexOf(',', start + 1);
string param1 = value.Substring(start + 1, end - (start + 1)).Trim();
start = end;
end = value.IndexOf(')', start + 1);
string param2 = value.Substring(start + 1, end - (start + 1)).Trim();
return new Point2D(Convert.ToInt32(param1), Convert.ToInt32(param2));
}
public int CompareTo(Point2D other)
{
int v = m_X.CompareTo(other.m_X);
if (v == 0)
v = m_Y.CompareTo(other.m_Y);
return v;
}
public override bool Equals(object o)
{
return o is IPoint2D p && m_X == p.X && m_Y == p.Y;
}
public override int GetHashCode()
{
return m_X ^ m_Y;
}
public static bool operator ==(Point2D l, Point2D r)
{
return l.m_X == r.m_X && l.m_Y == r.m_Y;
}
public static bool operator !=(Point2D l, Point2D r)
{
return l.m_X != r.m_X || l.m_Y != r.m_Y;
}
public static bool operator ==(Point2D l, IPoint2D r)
{
return !ReferenceEquals(r, null) && l.m_X == r.X && l.m_Y == r.Y;
}
public static bool operator !=(Point2D l, IPoint2D r)
{
return !ReferenceEquals(r, null) && (l.m_X != r.X || l.m_Y != r.Y);
}
public static bool operator >(Point2D l, Point2D r)
{
return l.m_X > r.m_X && l.m_Y > r.m_Y;
}
public static bool operator >(Point2D l, Point3D r)
{
return l.m_X > r.m_X && l.m_Y > r.m_Y;
}
public static bool operator >(Point2D l, IPoint2D r)
{
return !ReferenceEquals(r, null) && l.m_X > r.X && l.m_Y > r.Y;
}
public static bool operator <(Point2D l, Point2D r)
{
return l.m_X < r.m_X && l.m_Y < r.m_Y;
}
public static bool operator <(Point2D l, Point3D r)
{
return l.m_X < r.m_X && l.m_Y < r.m_Y;
}
public static bool operator <(Point2D l, IPoint2D r)
{
return !ReferenceEquals(r, null) && l.m_X < r.X && l.m_Y < r.Y;
}
public static bool operator >=(Point2D l, Point2D r)
{
return l.m_X >= r.m_X && l.m_Y >= r.m_Y;
}
public static bool operator >=(Point2D l, Point3D r)
{
return l.m_X >= r.m_X && l.m_Y >= r.m_Y;
}
public static bool operator >=(Point2D l, IPoint2D r)
{
return !ReferenceEquals(r, null) && l.m_X >= r.X && l.m_Y >= r.Y;
}
public static bool operator <=(Point2D l, Point2D r)
{
return l.m_X <= r.m_X && l.m_Y <= r.m_Y;
}
public static bool operator <=(Point2D l, Point3D r)
{
return l.m_X <= r.m_X && l.m_Y <= r.m_Y;
}
public static bool operator <=(Point2D l, IPoint2D r)
{
return !ReferenceEquals(r, null) && l.m_X <= r.X && l.m_Y <= r.Y;
}
}
[Parsable]
public struct Point3D : IPoint3D, IComparable<Point3D>
{
internal int m_X;
internal int m_Y;
internal int m_Z;
public static readonly Point3D Zero = new Point3D(0, 0, 0);
public Point3D(int x, int y, int z)
{
m_X = x;
m_Y = y;
m_Z = z;
}
public Point3D(IPoint3D p)
: this(p.X, p.Y, p.Z)
{
}
public Point3D(IPoint2D p, int z)
: this(p.X, p.Y, z)
{
}
[CommandProperty(AccessLevel.Counselor)]
public int X
{
get => m_X;
set => m_X = value;
}
[CommandProperty(AccessLevel.Counselor)]
public int Y
{
get => m_Y;
set => m_Y = value;
}
[CommandProperty(AccessLevel.Counselor)]
public int Z
{
get => m_Z;
set => m_Z = value;
}
public override string ToString()
{
return $"({m_X}, {m_Y}, {m_Z})";
}
public override bool Equals(object o)
{
return o is IPoint3D p && m_X == p.X && m_Y == p.Y && m_Z == p.Z;
}
public override int GetHashCode()
{
return m_X ^ m_Y ^ m_Z;
}
public static Point3D Parse(string value)
{
int start = value.IndexOf('(');
int end = value.IndexOf(',', start + 1);
string param1 = value.Substring(start + 1, end - (start + 1)).Trim();
start = end;
end = value.IndexOf(',', start + 1);
string param2 = value.Substring(start + 1, end - (start + 1)).Trim();
start = end;
end = value.IndexOf(')', start + 1);
string param3 = value.Substring(start + 1, end - (start + 1)).Trim();
return new Point3D(Convert.ToInt32(param1), Convert.ToInt32(param2), Convert.ToInt32(param3));
}
public static bool operator ==(Point3D l, Point3D r)
{
return l.m_X == r.m_X && l.m_Y == r.m_Y && l.m_Z == r.m_Z;
}
public static bool operator !=(Point3D l, Point3D r)
{
return l.m_X != r.m_X || l.m_Y != r.m_Y || l.m_Z != r.m_Z;
}
public static bool operator ==(Point3D l, IPoint3D r)
{
return !ReferenceEquals(r, null) && l.m_X == r.X && l.m_Y == r.Y && l.m_Z == r.Z;
}
public static bool operator !=(Point3D l, IPoint3D r)
{
return !ReferenceEquals(r, null) && (l.m_X != r.X || l.m_Y != r.Y || l.m_Z != r.Z);
}
public int CompareTo(Point3D other)
{
int v = m_X.CompareTo(other.m_X);
if (v == 0)
{
v = m_Y.CompareTo(other.m_Y);
if (v == 0)
v = m_Z.CompareTo(other.m_Z);
}
return v;
}
}
[NoSort]
[Parsable]
[PropertyObject]
public struct Rectangle2D
{
private Point2D m_Start;
private Point2D m_End;
public Rectangle2D(IPoint2D start, IPoint2D end)
{
m_Start = new Point2D(start);
m_End = new Point2D(end);
}
public Rectangle2D(int x, int y, int width, int height)
{
m_Start = new Point2D(x, y);
m_End = new Point2D(x + width, y + height);
}
public void Set(int x, int y, int width, int height)
{
m_Start = new Point2D(x, y);
m_End = new Point2D(x + width, y + height);
}
public static Rectangle2D Parse(string value)
{
int start = value.IndexOf('(');
int end = value.IndexOf(',', start + 1);
string param1 = value.Substring(start + 1, end - (start + 1)).Trim();
start = end;
end = value.IndexOf(',', start + 1);
string param2 = value.Substring(start + 1, end - (start + 1)).Trim();
start = end;
end = value.IndexOf(',', start + 1);
string param3 = value.Substring(start + 1, end - (start + 1)).Trim();
start = end;
end = value.IndexOf(')', start + 1);
string param4 = value.Substring(start + 1, end - (start + 1)).Trim();
return new Rectangle2D(Convert.ToInt32(param1), Convert.ToInt32(param2), Convert.ToInt32(param3),
Convert.ToInt32(param4));
}
[CommandProperty(AccessLevel.Counselor)]
public Point2D Start
{
get => m_Start;
set => m_Start = value;
}
[CommandProperty(AccessLevel.Counselor)]
public Point2D End
{
get => m_End;
set => m_End = value;
}
[CommandProperty(AccessLevel.Counselor)]
public int X
{
get => m_Start.m_X;
set => m_Start.m_X = value;
}
[CommandProperty(AccessLevel.Counselor)]
public int Y
{
get => m_Start.m_Y;
set => m_Start.m_Y = value;
}
[CommandProperty(AccessLevel.Counselor)]
public int Width
{
get => m_End.m_X - m_Start.m_X;
set => m_End.m_X = m_Start.m_X + value;
}
[CommandProperty(AccessLevel.Counselor)]
public int Height
{
get => m_End.m_Y - m_Start.m_Y;
set => m_End.m_Y = m_Start.m_Y + value;
}
public void MakeHold(Rectangle2D r)
{
if (r.m_Start.m_X < m_Start.m_X)
m_Start.m_X = r.m_Start.m_X;
if (r.m_Start.m_Y < m_Start.m_Y)
m_Start.m_Y = r.m_Start.m_Y;
if (r.m_End.m_X > m_End.m_X)
m_End.m_X = r.m_End.m_X;
if (r.m_End.m_Y > m_End.m_Y)
m_End.m_Y = r.m_End.m_Y;
}
public bool Contains(Point3D p)
{
return m_Start.m_X <= p.m_X && m_Start.m_Y <= p.m_Y && m_End.m_X > p.m_X && m_End.m_Y > p.m_Y;
//return ( m_Start <= p && m_End > p );
}
public bool Contains(Point2D p)
{
return m_Start.m_X <= p.m_X && m_Start.m_Y <= p.m_Y && m_End.m_X > p.m_X && m_End.m_Y > p.m_Y;
//return ( m_Start <= p && m_End > p );
}
public bool Contains(IPoint2D p)
{
return m_Start <= p && m_End > p;
}
public override string ToString()
{
return $"({X}, {Y})+({Width}, {Height})";
}
}
[NoSort]
[PropertyObject]
public struct Rectangle3D
{
public Rectangle3D(Point3D start, Point3D end)
{
Start = start;
End = end;
}
public Rectangle3D(int x, int y, int z, int width, int height, int depth)
{
Start = new Point3D(x, y, z);
End = new Point3D(x + width, y + height, z + depth);
}
[CommandProperty(AccessLevel.Counselor)]
public Point3D Start{ get; set; }
[CommandProperty(AccessLevel.Counselor)]
public Point3D End{ get; set; }
[CommandProperty(AccessLevel.Counselor)]
public int Width => End.X - Start.X;
[CommandProperty(AccessLevel.Counselor)]
public int Height => End.Y - Start.Y;
[CommandProperty(AccessLevel.Counselor)]
public int Depth => End.Z - Start.Z;
public bool Contains(Point3D p)
{
return p.m_X >= Start.m_X
&& p.m_X < End.m_X
&& p.m_Y >= Start.m_Y
&& p.m_Y < End.m_Y
&& p.m_Z >= Start.m_Z
&& p.m_Z < End.m_Z;
}
public bool Contains(IPoint3D p)
{
return p.X >= Start.m_X
&& p.X < End.m_X
&& p.Y >= Start.m_Y
&& p.Y < End.m_Y
&& p.Z >= Start.m_Z
&& p.Z < End.m_Z;
}
}
}

121
Projects/Server/Guild.cs Normal file
View file

@ -0,0 +1,121 @@
/***************************************************************************
* Guild.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Collections.Generic;
namespace Server.Guilds
{
public enum GuildType
{
Regular,
Chaos,
Order
}
public abstract class BaseGuild : ISerializable
{
private static uint m_NextID = 1;
protected BaseGuild(uint Id) //serialization ctor
{
this.Id = Id;
List.Add(this.Id, this);
if (this.Id + 1 > m_NextID)
m_NextID = this.Id + 1;
}
protected BaseGuild()
{
Id = m_NextID++;
List.Add(Id, this);
}
[CommandProperty(AccessLevel.Counselor)]
public uint Id{ get; }
public abstract string Abbreviation{ get; set; }
public abstract string Name{ get; set; }
public abstract GuildType Type{ get; set; }
public abstract bool Disbanded{ get; }
public static Dictionary<uint, BaseGuild> List{ get; } = new Dictionary<uint, BaseGuild>();
int ISerializable.TypeReference => 0;
uint ISerializable.SerialIdentity => Id;
public abstract void Serialize(GenericWriter writer);
public abstract void Deserialize(GenericReader reader);
public abstract void OnDelete(Mobile mob);
public static BaseGuild Find(uint id)
{
List.TryGetValue(id, out BaseGuild g);
return g;
}
public static BaseGuild FindByName(string name)
{
foreach (BaseGuild g in List.Values)
if (g.Name == name)
return g;
return null;
}
public static BaseGuild FindByAbbrev(string abbr)
{
foreach (BaseGuild g in List.Values)
if (g.Abbreviation == abbr)
return g;
return null;
}
public static List<BaseGuild> Search(string find)
{
string[] words = find.ToLower().Split(' ');
List<BaseGuild> results = new List<BaseGuild>();
foreach (BaseGuild g in List.Values)
{
bool match = true;
string name = g.Name.ToLower();
for (int i = 0; i < words.Length; i++)
if (name.IndexOf(words[i]) == -1)
{
match = false;
break;
}
if (match)
results.Add(g);
}
return results;
}
public override string ToString()
{
return $"0x{Id:X} \"{Name} [{Abbreviation}]\"";
}
}
}

View file

@ -0,0 +1,373 @@
/***************************************************************************
* Gump.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using Server.Network;
namespace Server.Gumps
{
public class Gump
{
private static uint m_NextSerial = 1;
private static byte[] m_BeginLayout = StringToBuffer("{ ");
private static byte[] m_EndLayout = StringToBuffer(" }");
private static byte[] m_NoMove = StringToBuffer("{ nomove }");
private static byte[] m_NoClose = StringToBuffer("{ noclose }");
private static byte[] m_NoDispose = StringToBuffer("{ nodispose }");
private static byte[] m_NoResize = StringToBuffer("{ noresize }");
private bool m_Closable = true;
private bool m_Disposable = true;
private bool m_Draggable = true;
private bool m_Resizable = true;
private uint m_Serial;
private List<string> m_Strings;
internal int m_TextEntries, m_Switches;
private int m_X, m_Y;
public Gump(int x, int y)
{
do
{
m_Serial = m_NextSerial++;
} while (m_Serial == 0); // standard client apparently doesn't send a gump response packet if serial == 0
m_X = x;
m_Y = y;
TypeID = GetTypeID(GetType());
Entries = new List<GumpEntry>();
m_Strings = new List<string>();
}
public int TypeID{ get; }
public List<GumpEntry> Entries{ get; }
public uint Serial
{
get => m_Serial;
set
{
if (m_Serial != value)
{
m_Serial = value;
Invalidate();
}
}
}
public int X
{
get => m_X;
set
{
if (m_X != value)
{
m_X = value;
Invalidate();
}
}
}
public int Y
{
get => m_Y;
set
{
if (m_Y != value)
{
m_Y = value;
Invalidate();
}
}
}
public bool Disposable
{
get => m_Disposable;
set
{
if (m_Disposable != value)
{
m_Disposable = value;
Invalidate();
}
}
}
public bool Resizable
{
get => m_Resizable;
set
{
if (m_Resizable != value)
{
m_Resizable = value;
Invalidate();
}
}
}
public bool Draggable
{
get => m_Draggable;
set
{
if (m_Draggable != value)
{
m_Draggable = value;
Invalidate();
}
}
}
public bool Closable
{
get => m_Closable;
set
{
if (m_Closable != value)
{
m_Closable = value;
Invalidate();
}
}
}
public static int GetTypeID(Type type)
{
return type?.FullName?.GetHashCode() ?? -1;
}
public void Invalidate()
{
//if ( m_Strings.Count > 0 )
// m_Strings.Clear();
}
public void AddPage(int page)
{
Add(new GumpPage(page));
}
public void AddAlphaRegion(int x, int y, int width, int height)
{
Add(new GumpAlphaRegion(x, y, width, height));
}
public void AddBackground(int x, int y, int width, int height, int gumpID)
{
Add(new GumpBackground(x, y, width, height, gumpID));
}
public void AddButton(int x, int y, int normalID, int pressedID, int buttonID,
GumpButtonType type = GumpButtonType.Reply, int param = 0)
{
Add(new GumpButton(x, y, normalID, pressedID, buttonID, type, param));
}
public void AddCheck(int x, int y, int inactiveID, int activeID, bool initialState, int switchID)
{
Add(new GumpCheck(x, y, inactiveID, activeID, initialState, switchID));
}
public void AddGroup(int group)
{
Add(new GumpGroup(group));
}
public void AddTooltip(int number)
{
Add(new GumpTooltip(number));
}
public void AddHtml(int x, int y, int width, int height, string text, bool background = false, bool scrollbar = false)
{
Add(new GumpHtml(x, y, width, height, text, background, scrollbar));
}
public void AddHtmlLocalized(int x, int y, int width, int height, int number, bool background = false, bool scrollbar = false)
{
Add(new GumpHtmlLocalized(x, y, width, height, number, background, scrollbar));
}
public void AddHtmlLocalized(int x, int y, int width, int height, int number, int color, bool background = false,
bool scrollbar = false)
{
Add(new GumpHtmlLocalized(x, y, width, height, number, color, background, scrollbar));
}
public void AddHtmlLocalized(int x, int y, int width, int height, int number, string args, int color,
bool background = false, bool scrollbar = false)
{
Add(new GumpHtmlLocalized(x, y, width, height, number, args, color, background, scrollbar));
}
public void AddImage(int x, int y, int gumpID, int hue = 0)
{
Add(new GumpImage(x, y, gumpID, hue));
}
public void AddImageTiled(int x, int y, int width, int height, int gumpID)
{
Add(new GumpImageTiled(x, y, width, height, gumpID));
}
public void AddImageTiledButton(int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type,
int param, int itemID, int hue, int width, int height, int localizedTooltip = -1)
{
Add(new GumpImageTileButton(x, y, normalID, pressedID, buttonID, type, param, itemID, hue, width, height,
localizedTooltip));
}
public void AddItem(int x, int y, int itemID, int hue = 0)
{
Add(new GumpItem(x, y, itemID, hue));
}
public void AddLabel(int x, int y, int hue, string text)
{
Add(new GumpLabel(x, y, hue, text));
}
public void AddLabelCropped(int x, int y, int width, int height, int hue, string text)
{
Add(new GumpLabelCropped(x, y, width, height, hue, text));
}
public void AddRadio(int x, int y, int inactiveID, int activeID, bool initialState, int switchID)
{
Add(new GumpRadio(x, y, inactiveID, activeID, initialState, switchID));
}
public void AddTextEntry(int x, int y, int width, int height, int hue, int entryID, string initialText, int size = 0)
{
Add(new GumpTextEntryLimited(x, y, width, height, hue, entryID, initialText, size));
}
public void AddItemProperty(uint serial)
{
Add(new GumpItemProperty(serial));
}
public void Add(GumpEntry g)
{
if (g.Parent != this)
{
g.Parent = this;
}
else if (!Entries.Contains(g))
{
Invalidate();
Entries.Add(g);
}
}
public void Remove(GumpEntry g)
{
if (g == null || !Entries.Contains(g))
return;
Invalidate();
Entries.Remove(g);
g.Parent = null;
}
public int Intern(string value)
{
int indexOf = m_Strings.IndexOf(value);
if (indexOf >= 0) return indexOf;
Invalidate();
m_Strings.Add(value);
return m_Strings.Count - 1;
}
public void SendTo(NetState state)
{
state.AddGump(this);
state.Send(Compile(state));
}
public static byte[] StringToBuffer(string str)
{
return Encoding.ASCII.GetBytes(str);
}
private Packet Compile(NetState ns = null)
{
IGumpWriter disp;
if (ns?.Unpack == true)
disp = new DisplayGumpPacked(this);
else
disp = new DisplayGumpFast(this);
if (!m_Draggable)
disp.AppendLayout(m_NoMove);
if (!m_Closable)
disp.AppendLayout(m_NoClose);
if (!m_Disposable)
disp.AppendLayout(m_NoDispose);
if (!m_Resizable)
disp.AppendLayout(m_NoResize);
int count = Entries.Count;
for (int i = 0; i < count; ++i)
{
GumpEntry e = Entries[i];
disp.AppendLayout(m_BeginLayout);
e.AppendTo(ns, disp);
disp.AppendLayout(m_EndLayout);
}
disp.WriteStrings(m_Strings);
disp.Flush();
m_TextEntries = disp.TextEntries;
m_Switches = disp.Switches;
return (Packet)disp;
}
public virtual void OnResponse(NetState sender, RelayInfo info)
{
}
public virtual void OnServerClose(NetState owner)
{
}
}
}

View file

@ -0,0 +1,77 @@
/***************************************************************************
* GumpAlphaRegion.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpAlphaRegion : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("checkertrans");
private int m_Width, m_Height;
private int m_X, m_Y;
public GumpAlphaRegion(int x, int y, int width, int height)
{
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int Width
{
get => m_Width;
set => Delta(ref m_Width, value);
}
public int Height
{
get => m_Height;
set => Delta(ref m_Height, value);
}
public override string Compile(NetState ns)
{
return $"{{ checkertrans {m_X} {m_Y} {m_Width} {m_Height} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
}
}
}

View file

@ -0,0 +1,86 @@
/***************************************************************************
* GumpBackground.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpBackground : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("resizepic");
private int m_GumpID;
private int m_Width, m_Height;
private int m_X, m_Y;
public GumpBackground(int x, int y, int width, int height, int gumpID)
{
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
m_GumpID = gumpID;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int Width
{
get => m_Width;
set => Delta(ref m_Width, value);
}
public int Height
{
get => m_Height;
set => Delta(ref m_Height, value);
}
public int GumpID
{
get => m_GumpID;
set => Delta(ref m_GumpID, value);
}
public override string Compile(NetState ns)
{
return $"{{ resizepic {m_X} {m_Y} {m_GumpID} {m_Width} {m_Height} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_GumpID);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
}
}
}

View file

@ -0,0 +1,121 @@
/***************************************************************************
* GumpButton.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public enum GumpButtonType
{
Page = 0,
Reply = 1
}
public class GumpButton : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("button");
private int m_ButtonID;
private int m_ID1, m_ID2;
private int m_Param;
private GumpButtonType m_Type;
private int m_X, m_Y;
public GumpButton(int x, int y, int normalID, int pressedID, int buttonID,
GumpButtonType type = GumpButtonType.Reply, int param = 0)
{
m_X = x;
m_Y = y;
m_ID1 = normalID;
m_ID2 = pressedID;
m_ButtonID = buttonID;
m_Type = type;
m_Param = param;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int NormalID
{
get => m_ID1;
set => Delta(ref m_ID1, value);
}
public int PressedID
{
get => m_ID2;
set => Delta(ref m_ID2, value);
}
public int ButtonID
{
get => m_ButtonID;
set => Delta(ref m_ButtonID, value);
}
public GumpButtonType Type
{
get => m_Type;
set
{
if (m_Type != value)
{
m_Type = value;
Gump parent = Parent;
parent?.Invalidate();
}
}
}
public int Param
{
get => m_Param;
set => Delta(ref m_Param, value);
}
public override string Compile(NetState ns)
{
return $"{{ button {m_X} {m_Y} {m_ID1} {m_ID2} {(int)m_Type} {m_Param} {m_ButtonID} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_ID1);
disp.AppendLayout(m_ID2);
disp.AppendLayout((int)m_Type);
disp.AppendLayout(m_Param);
disp.AppendLayout(m_ButtonID);
}
}
}

View file

@ -0,0 +1,97 @@
/***************************************************************************
* GumpCheck.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpCheck : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("checkbox");
private int m_ID1, m_ID2;
private bool m_InitialState;
private int m_SwitchID;
private int m_X, m_Y;
public GumpCheck(int x, int y, int inactiveID, int activeID, bool initialState, int switchID)
{
m_X = x;
m_Y = y;
m_ID1 = inactiveID;
m_ID2 = activeID;
m_InitialState = initialState;
m_SwitchID = switchID;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int InactiveID
{
get => m_ID1;
set => Delta(ref m_ID1, value);
}
public int ActiveID
{
get => m_ID2;
set => Delta(ref m_ID2, value);
}
public bool InitialState
{
get => m_InitialState;
set => Delta(ref m_InitialState, value);
}
public int SwitchID
{
get => m_SwitchID;
set => Delta(ref m_SwitchID, value);
}
public override string Compile(NetState ns)
{
return $"{{ checkbox {m_X} {m_Y} {m_ID1} {m_ID2} {(m_InitialState ? 1 : 0)} {m_SwitchID} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_ID1);
disp.AppendLayout(m_ID2);
disp.AppendLayout(m_InitialState);
disp.AppendLayout(m_SwitchID);
disp.Switches++;
}
}
}

View file

@ -0,0 +1,78 @@
/***************************************************************************
* GumpEntry.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public abstract class GumpEntry
{
private Gump m_Parent;
public Gump Parent
{
get => m_Parent;
set
{
if (m_Parent != value)
{
m_Parent?.Remove(this);
m_Parent = value;
m_Parent?.Add(this);
}
}
}
protected void Delta(ref uint var, uint val)
{
if (var != val)
var = val;
}
protected void Delta(ref int var, int val)
{
if (var != val)
var = val;
}
protected void Delta(ref bool var, bool val)
{
if (var != val)
var = val;
}
protected void Delta(ref string var, string val)
{
if (var != val)
var = val;
}
protected void Delta(ref object[] var, object[] val)
{
if (var != val)
var = val;
}
public abstract string Compile(NetState ns);
public abstract void AppendTo(NetState ns, IGumpWriter disp);
}
}

View file

@ -0,0 +1,52 @@
/***************************************************************************
* GumpGroup.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpGroup : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("group");
private int m_Group;
public GumpGroup(int group)
{
m_Group = group;
}
public int Group
{
get => m_Group;
set => Delta(ref m_Group, value);
}
public override string Compile(NetState ns)
{
return $"{{ group {m_Group} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_Group);
}
}
}

View file

@ -0,0 +1,104 @@
/***************************************************************************
* GumpHtml.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpHtml : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("htmlgump");
private bool m_Background, m_Scrollbar;
private string m_Text;
private int m_Width, m_Height;
private int m_X, m_Y;
public GumpHtml(int x, int y, int width, int height, string text, bool background, bool scrollbar)
{
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
m_Text = text;
m_Background = background;
m_Scrollbar = scrollbar;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int Width
{
get => m_Width;
set => Delta(ref m_Width, value);
}
public int Height
{
get => m_Height;
set => Delta(ref m_Height, value);
}
public string Text
{
get => m_Text;
set => Delta(ref m_Text, value);
}
public bool Background
{
get => m_Background;
set => Delta(ref m_Background, value);
}
public bool Scrollbar
{
get => m_Scrollbar;
set => Delta(ref m_Scrollbar, value);
}
public override string Compile(NetState ns)
{
return
$"{{ htmlgump {m_X} {m_Y} {m_Width} {m_Height} {Parent.Intern(m_Text)} {(m_Background ? 1 : 0)} {(m_Scrollbar ? 1 : 0)} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
disp.AppendLayout(Parent.Intern(m_Text));
disp.AppendLayout(m_Background);
disp.AppendLayout(m_Scrollbar);
}
}
}

View file

@ -0,0 +1,233 @@
/***************************************************************************
* GumpHtmlLocalized.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public enum GumpHtmlLocalizedType
{
Plain,
Color,
Args
}
public class GumpHtmlLocalized : GumpEntry
{
private static byte[] m_LayoutNamePlain = Gump.StringToBuffer("xmfhtmlgump");
private static byte[] m_LayoutNameColor = Gump.StringToBuffer("xmfhtmlgumpcolor");
private static byte[] m_LayoutNameArgs = Gump.StringToBuffer("xmfhtmltok");
private string m_Args;
private bool m_Background, m_Scrollbar;
private int m_Color;
private int m_Number;
private GumpHtmlLocalizedType m_Type;
private int m_Width, m_Height;
private int m_X, m_Y;
public GumpHtmlLocalized(int x, int y, int width, int height, int number,
bool background = false, bool scrollbar = false)
{
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
m_Number = number;
m_Background = background;
m_Scrollbar = scrollbar;
m_Type = GumpHtmlLocalizedType.Plain;
}
public GumpHtmlLocalized(int x, int y, int width, int height, int number, int color,
bool background = false, bool scrollbar = false)
{
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
m_Number = number;
m_Color = color;
m_Background = background;
m_Scrollbar = scrollbar;
m_Type = GumpHtmlLocalizedType.Color;
}
public GumpHtmlLocalized(int x, int y, int width, int height, int number, string args, int color,
bool background = false, bool scrollbar = false)
{
// Are multiple arguments unsupported? And what about non ASCII arguments?
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
m_Number = number;
m_Args = args;
m_Color = color;
m_Background = background;
m_Scrollbar = scrollbar;
m_Type = GumpHtmlLocalizedType.Args;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int Width
{
get => m_Width;
set => Delta(ref m_Width, value);
}
public int Height
{
get => m_Height;
set => Delta(ref m_Height, value);
}
public int Number
{
get => m_Number;
set => Delta(ref m_Number, value);
}
public string Args
{
get => m_Args;
set => Delta(ref m_Args, value);
}
public int Color
{
get => m_Color;
set => Delta(ref m_Color, value);
}
public bool Background
{
get => m_Background;
set => Delta(ref m_Background, value);
}
public bool Scrollbar
{
get => m_Scrollbar;
set => Delta(ref m_Scrollbar, value);
}
public GumpHtmlLocalizedType Type
{
get => m_Type;
set
{
if (m_Type != value)
{
m_Type = value;
Parent?.Invalidate();
}
}
}
public override string Compile(NetState ns)
{
switch (m_Type)
{
case GumpHtmlLocalizedType.Plain:
return
$"{{ xmfhtmlgump {m_X} {m_Y} {m_Width} {m_Height} {m_Number} {(m_Background ? 1 : 0)} {(m_Scrollbar ? 1 : 0)} }}";
case GumpHtmlLocalizedType.Color:
return
$"{{ xmfhtmlgumpcolor {m_X} {m_Y} {m_Width} {m_Height} {m_Number} {(m_Background ? 1 : 0)} {(m_Scrollbar ? 1 : 0)} {m_Color} }}";
default: // GumpHtmlLocalizedType.Args
return
$"{{ xmfhtmltok {m_X} {m_Y} {m_Width} {m_Height} {(m_Background ? 1 : 0)} {(m_Scrollbar ? 1 : 0)} {m_Color} {m_Number} @{m_Args}@ }}";
}
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
switch (m_Type)
{
case GumpHtmlLocalizedType.Plain:
{
disp.AppendLayout(m_LayoutNamePlain);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
disp.AppendLayout(m_Number);
disp.AppendLayout(m_Background);
disp.AppendLayout(m_Scrollbar);
break;
}
case GumpHtmlLocalizedType.Color:
{
disp.AppendLayout(m_LayoutNameColor);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
disp.AppendLayout(m_Number);
disp.AppendLayout(m_Background);
disp.AppendLayout(m_Scrollbar);
disp.AppendLayout(m_Color);
break;
}
case GumpHtmlLocalizedType.Args:
{
disp.AppendLayout(m_LayoutNameArgs);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
disp.AppendLayout(m_Background);
disp.AppendLayout(m_Scrollbar);
disp.AppendLayout(m_Color);
disp.AppendLayout(m_Number);
disp.AppendLayout(m_Args);
break;
}
}
}
}
}

View file

@ -0,0 +1,85 @@
/***************************************************************************
* GumpImage.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpImage : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("gumppic");
private static byte[] m_HueEquals = Gump.StringToBuffer(" hue=");
private int m_GumpID;
private int m_Hue;
private int m_X, m_Y;
public GumpImage(int x, int y, int gumpID, int hue = 0)
{
m_X = x;
m_Y = y;
m_GumpID = gumpID;
m_Hue = hue;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int GumpID
{
get => m_GumpID;
set => Delta(ref m_GumpID, value);
}
public int Hue
{
get => m_Hue;
set => Delta(ref m_Hue, value);
}
public override string Compile(NetState ns)
{
return m_Hue == 0 ? $"{{ gumppic {m_X} {m_Y} {m_GumpID} }}" :
$"{{ gumppic {m_X} {m_Y} {m_GumpID} hue={m_Hue} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_GumpID);
if (m_Hue != 0)
{
disp.AppendLayout(m_HueEquals);
disp.AppendLayoutNS(m_Hue);
}
}
}
}

View file

@ -0,0 +1,172 @@
/***************************************************************************
* GumpImageTileButton.cs
* -------------------
* begin : April 26, 2005
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpImageTileButton : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("buttontileart");
private static byte[] m_LayoutTooltip = Gump.StringToBuffer(" }{ tooltip");
private int m_ButtonID;
private int m_Height;
private int m_Hue;
private int m_ID1, m_ID2;
private int m_ItemID;
private int m_Param;
private GumpButtonType m_Type;
private int m_Width;
//Note, on OSI, The tooltip supports ONLY clilocs as far as I can figure out, and the tooltip ONLY works after the buttonTileArt (as far as I can tell from testing)
private int m_X, m_Y;
public GumpImageTileButton(int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type, int param,
int itemID, int hue, int width, int height, int localizedTooltip = -1)
{
m_X = x;
m_Y = y;
m_ID1 = normalID;
m_ID2 = pressedID;
m_ButtonID = buttonID;
m_Type = type;
m_Param = param;
m_ItemID = itemID;
m_Hue = hue;
m_Width = width;
m_Height = height;
LocalizedTooltip = localizedTooltip;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int NormalID
{
get => m_ID1;
set => Delta(ref m_ID1, value);
}
public int PressedID
{
get => m_ID2;
set => Delta(ref m_ID2, value);
}
public int ButtonID
{
get => m_ButtonID;
set => Delta(ref m_ButtonID, value);
}
public GumpButtonType Type
{
get => m_Type;
set
{
if (m_Type != value)
{
m_Type = value;
Gump parent = Parent;
parent?.Invalidate();
}
}
}
public int Param
{
get => m_Param;
set => Delta(ref m_Param, value);
}
public int ItemID
{
get => m_ItemID;
set => Delta(ref m_ItemID, value);
}
public int Hue
{
get => m_Hue;
set => Delta(ref m_Hue, value);
}
public int Width
{
get => m_Width;
set => Delta(ref m_Width, value);
}
public int Height
{
get => m_Height;
set => Delta(ref m_Height, value);
}
public int LocalizedTooltip{ get; set; }
public override string Compile(NetState ns)
{
if (LocalizedTooltip > 0)
return
$"{{ buttontileart {m_X} {m_Y} {m_ID1} {m_ID2} {(int)m_Type} {m_Param} {m_ButtonID} {m_ItemID} {m_Hue} {m_Width} {m_Height} }}{{ tooltip {LocalizedTooltip} }}";
return
$"{{ buttontileart {m_X} {m_Y} {m_ID1} {m_ID2} {(int)m_Type} {m_Param} {m_ButtonID} {m_ItemID} {m_Hue} {m_Width} {m_Height} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_ID1);
disp.AppendLayout(m_ID2);
disp.AppendLayout((int)m_Type);
disp.AppendLayout(m_Param);
disp.AppendLayout(m_ButtonID);
disp.AppendLayout(m_ItemID);
disp.AppendLayout(m_Hue);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
if (LocalizedTooltip > 0)
{
disp.AppendLayout(m_LayoutTooltip);
disp.AppendLayout(LocalizedTooltip);
}
}
}
}

View file

@ -0,0 +1,86 @@
/***************************************************************************
* GumpImageTiled.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpImageTiled : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("gumppictiled");
private int m_GumpID;
private int m_Width, m_Height;
private int m_X, m_Y;
public GumpImageTiled(int x, int y, int width, int height, int gumpID)
{
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
m_GumpID = gumpID;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int Width
{
get => m_Width;
set => Delta(ref m_Width, value);
}
public int Height
{
get => m_Height;
set => Delta(ref m_Height, value);
}
public int GumpID
{
get => m_GumpID;
set => Delta(ref m_GumpID, value);
}
public override string Compile(NetState ns)
{
return $"{{ gumppictiled {m_X} {m_Y} {m_Width} {m_Height} {m_GumpID} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
disp.AppendLayout(m_GumpID);
}
}
}

View file

@ -0,0 +1,82 @@
/***************************************************************************
* GumpItem.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpItem : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("tilepic");
private static byte[] m_LayoutNameHue = Gump.StringToBuffer("tilepichue");
private int m_Hue;
private int m_ItemID;
private int m_X, m_Y;
public GumpItem(int x, int y, int itemID, int hue = 0)
{
m_X = x;
m_Y = y;
m_ItemID = itemID;
m_Hue = hue;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int ItemID
{
get => m_ItemID;
set => Delta(ref m_ItemID, value);
}
public int Hue
{
get => m_Hue;
set => Delta(ref m_Hue, value);
}
public override string Compile(NetState ns)
{
return m_Hue == 0 ? $"{{ tilepic {m_X} {m_Y} {m_ItemID} }}" :
$"{{ tilepichue {m_X} {m_Y} {m_ItemID} {m_Hue} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_Hue == 0 ? m_LayoutName : m_LayoutNameHue);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_ItemID);
if (m_Hue != 0)
disp.AppendLayout(m_Hue);
}
}
}

View file

@ -0,0 +1,52 @@
/***************************************************************************
* GumpItemProperty.cs
* -------------------
* begin : May 26, 2013
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpItemProperty : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("itemproperty");
private uint m_Serial;
public GumpItemProperty(uint serial)
{
m_Serial = serial;
}
public uint Serial
{
get => m_Serial;
set => Delta(ref m_Serial, value);
}
public override string Compile(NetState ns)
{
return $"{{ itemproperty {m_Serial} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_Serial);
}
}
}

View file

@ -0,0 +1,78 @@
/***************************************************************************
* GumpLabel.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpLabel : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("text");
private int m_Hue;
private string m_Text;
private int m_X, m_Y;
public GumpLabel(int x, int y, int hue, string text)
{
m_X = x;
m_Y = y;
m_Hue = hue;
m_Text = text;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int Hue
{
get => m_Hue;
set => Delta(ref m_Hue, value);
}
public string Text
{
get => m_Text;
set => Delta(ref m_Text, value);
}
public override string Compile(NetState ns)
{
return $"{{ text {m_X} {m_Y} {m_Hue} {Parent.Intern(m_Text)} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Hue);
disp.AppendLayout(Parent.Intern(m_Text));
}
}
}

View file

@ -0,0 +1,95 @@
/***************************************************************************
* GumpLabelCropped.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpLabelCropped : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("croppedtext");
private int m_Hue;
private string m_Text;
private int m_Width, m_Height;
private int m_X, m_Y;
public GumpLabelCropped(int x, int y, int width, int height, int hue, string text)
{
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
m_Hue = hue;
m_Text = text;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int Width
{
get => m_Width;
set => Delta(ref m_Width, value);
}
public int Height
{
get => m_Height;
set => Delta(ref m_Height, value);
}
public int Hue
{
get => m_Hue;
set => Delta(ref m_Hue, value);
}
public string Text
{
get => m_Text;
set => Delta(ref m_Text, value);
}
public override string Compile(NetState ns)
{
return $"{{ croppedtext {m_X} {m_Y} {m_Width} {m_Height} {m_Hue} {Parent.Intern(m_Text)} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
disp.AppendLayout(m_Hue);
disp.AppendLayout(Parent.Intern(m_Text));
}
}
}

View file

@ -0,0 +1,52 @@
/***************************************************************************
* GumpPage.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpPage : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("page");
private int m_Page;
public GumpPage(int page)
{
m_Page = page;
}
public int Page
{
get => m_Page;
set => Delta(ref m_Page, value);
}
public override string Compile(NetState ns)
{
return $"{{ page {m_Page} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_Page);
}
}
}

View file

@ -0,0 +1,97 @@
/***************************************************************************
* GumpRadio.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpRadio : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("radio");
private int m_ID1, m_ID2;
private bool m_InitialState;
private int m_SwitchID;
private int m_X, m_Y;
public GumpRadio(int x, int y, int inactiveID, int activeID, bool initialState, int switchID)
{
m_X = x;
m_Y = y;
m_ID1 = inactiveID;
m_ID2 = activeID;
m_InitialState = initialState;
m_SwitchID = switchID;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int InactiveID
{
get => m_ID1;
set => Delta(ref m_ID1, value);
}
public int ActiveID
{
get => m_ID2;
set => Delta(ref m_ID2, value);
}
public bool InitialState
{
get => m_InitialState;
set => Delta(ref m_InitialState, value);
}
public int SwitchID
{
get => m_SwitchID;
set => Delta(ref m_SwitchID, value);
}
public override string Compile(NetState ns)
{
return $"{{ radio {m_X} {m_Y} {m_ID1} {m_ID2} {(m_InitialState ? 1 : 0)} {m_SwitchID} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_ID1);
disp.AppendLayout(m_ID2);
disp.AppendLayout(m_InitialState);
disp.AppendLayout(m_SwitchID);
disp.Switches++;
}
}
}

View file

@ -0,0 +1,107 @@
/***************************************************************************
* GumpTextEntry.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpTextEntry : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("textentry");
private int m_EntryID;
private int m_Hue;
private string m_InitialText;
private int m_Width, m_Height;
private int m_X, m_Y;
public GumpTextEntry(int x, int y, int width, int height, int hue, int entryID, string initialText)
{
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
m_Hue = hue;
m_EntryID = entryID;
m_InitialText = initialText;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int Width
{
get => m_Width;
set => Delta(ref m_Width, value);
}
public int Height
{
get => m_Height;
set => Delta(ref m_Height, value);
}
public int Hue
{
get => m_Hue;
set => Delta(ref m_Hue, value);
}
public int EntryID
{
get => m_EntryID;
set => Delta(ref m_EntryID, value);
}
public string InitialText
{
get => m_InitialText;
set => Delta(ref m_InitialText, value);
}
public override string Compile(NetState ns)
{
return
$"{{ textentry {m_X} {m_Y} {m_Width} {m_Height} {m_Hue} {m_EntryID} {Parent.Intern(m_InitialText)} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
disp.AppendLayout(m_Hue);
disp.AppendLayout(m_EntryID);
disp.AppendLayout(Parent.Intern(m_InitialText));
disp.TextEntries++;
}
}
}

View file

@ -0,0 +1,116 @@
/***************************************************************************
* GumpTextEntryLimited.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpTextEntryLimited : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("textentrylimited");
private int m_EntryID;
private int m_Hue;
private string m_InitialText;
private int m_Size;
private int m_Width, m_Height;
private int m_X, m_Y;
public GumpTextEntryLimited(int x, int y, int width, int height, int hue, int entryID, string initialText, int size = 0)
{
m_X = x;
m_Y = y;
m_Width = width;
m_Height = height;
m_Hue = hue;
m_EntryID = entryID;
m_InitialText = initialText;
m_Size = size;
}
public int X
{
get => m_X;
set => Delta(ref m_X, value);
}
public int Y
{
get => m_Y;
set => Delta(ref m_Y, value);
}
public int Width
{
get => m_Width;
set => Delta(ref m_Width, value);
}
public int Height
{
get => m_Height;
set => Delta(ref m_Height, value);
}
public int Hue
{
get => m_Hue;
set => Delta(ref m_Hue, value);
}
public int EntryID
{
get => m_EntryID;
set => Delta(ref m_EntryID, value);
}
public string InitialText
{
get => m_InitialText;
set => Delta(ref m_InitialText, value);
}
public int Size
{
get => m_Size;
set => Delta(ref m_Size, value);
}
public override string Compile(NetState ns)
{
return
$"{{ textentrylimited {m_X} {m_Y} {m_Width} {m_Height} {m_Hue} {m_EntryID} {Parent.Intern(m_InitialText)} {m_Size} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_X);
disp.AppendLayout(m_Y);
disp.AppendLayout(m_Width);
disp.AppendLayout(m_Height);
disp.AppendLayout(m_Hue);
disp.AppendLayout(m_EntryID);
disp.AppendLayout(Parent.Intern(m_InitialText));
disp.AppendLayout(m_Size);
disp.TextEntries++;
}
}
}

View file

@ -0,0 +1,52 @@
/***************************************************************************
* GumpTooltip.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Gumps
{
public class GumpTooltip : GumpEntry
{
private static byte[] m_LayoutName = Gump.StringToBuffer("tooltip");
private int m_Number;
public GumpTooltip(int number)
{
m_Number = number;
}
public int Number
{
get => m_Number;
set => Delta(ref m_Number, value);
}
public override string Compile(NetState ns)
{
return $"{{ tooltip {m_Number} }}";
}
public override void AppendTo(NetState ns, IGumpWriter disp)
{
disp.AppendLayout(m_LayoutName);
disp.AppendLayout(m_Number);
}
}
}

View file

@ -0,0 +1,69 @@
/***************************************************************************
* RelayInfo.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server.Gumps
{
public class TextRelay
{
public TextRelay(int entryID, string text)
{
EntryID = entryID;
Text = text;
}
public int EntryID{ get; }
public string Text{ get; }
}
public class RelayInfo
{
public RelayInfo(int buttonID, int[] switches, TextRelay[] textEntries)
{
ButtonID = buttonID;
Switches = switches;
TextEntries = textEntries;
}
public int ButtonID{ get; }
public int[] Switches{ get; }
public TextRelay[] TextEntries{ get; }
public bool IsSwitched(int switchID)
{
for (int i = 0; i < Switches.Length; ++i)
if (Switches[i] == switchID)
return true;
return false;
}
public TextRelay GetTextEntry(int entryID)
{
for (int i = 0; i < TextEntries.Length; ++i)
if (TextEntries[i].EntryID == entryID)
return TextEntries[i];
return null;
}
}
}

View file

@ -0,0 +1,53 @@
/***************************************************************************
* HuePicker.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.HuePickers
{
public class HuePicker
{
private static int m_NextSerial = 1;
public HuePicker(int itemID)
{
do
{
Serial = m_NextSerial++;
} while (Serial == 0);
ItemID = itemID;
}
public int Serial{ get; }
public int ItemID{ get; }
public virtual void OnResponse(int hue)
{
}
public void SendTo(NetState state)
{
state.Send(new DisplayHuePicker(this));
state.AddHuePicker(this);
}
}
}

126
Projects/Server/IAccount.cs Normal file
View file

@ -0,0 +1,126 @@
/***************************************************************************
* IAccount.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
namespace Server.Accounting
{
public static class AccountGold
{
public static bool Enabled = false;
/// <summary>
/// This amount specifies the value at which point Gold turns to Platinum.
/// By default, when 1,000,000,000 Gold is accumulated, it will transform
/// into 1 Platinum.
/// !!! WARNING !!!
/// The client is designed to perceive the currency threashold at 1,000,000,000
/// if you change this, it may cause unexpected results when using secure trading.
/// </summary>
public static int CurrencyThreshold = 1000000000;
/// <summary>
/// Enables or Disables automatic conversion of Gold and Checks to Bank Currency
/// when they are added to a bank box container.
/// </summary>
public static bool ConvertOnBank = true;
/// <summary>
/// Enables or Disables automatic conversion of Gold and Checks to Bank Currency
/// when they are added to a secure trade container.
/// </summary>
public static bool ConvertOnTrade = false;
}
public interface IGoldAccount
{
/// <summary>
/// This amount represents the current amount of Gold owned by the player.
/// The value does not include the value of Platinum and ranges from
/// 0 to 999,999,999 by default.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
int TotalGold{ get; }
/// <summary>
/// This amount represents the current amount of Platinum owned by the player.
/// The value does not include the value of Gold and ranges from
/// 0 to 2,147,483,647 by default.
/// One Platinum represents the value of CurrencyThreshold in Gold.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
int TotalPlat{ get; }
/// <summary>
/// Attempts to deposit the given amount of Gold into this account.
/// If the given amount is greater than the CurrencyThreshold,
/// Platinum will be deposited to offset the difference.
/// </summary>
/// <param name="amount">Amount to deposit.</param>
/// <returns>True if successful, false if amount given is less than or equal to zero.</returns>
bool DepositGold(int amount);
/// <summary>
/// Attempts to deposit the given amount of Platinum into this account.
/// </summary>
/// <param name="amount">Amount to deposit.</param>
/// <returns>True if successful, false if amount given is less than or equal to zero.</returns>
bool DepositPlat(int amount);
/// <summary>
/// Attempts to withdraw the given amount of Gold from this account.
/// If the given amount is greater than the CurrencyThreshold,
/// Platinum will be withdrawn to offset the difference.
/// </summary>
/// <param name="amount">Amount to withdraw.</param>
/// <returns>True if successful, false if balance was too low.</returns>
bool WithdrawGold(int amount);
/// <summary>
/// Attempts to withdraw the given amount of Platinum from this account.
/// </summary>
/// <param name="amount">Amount to withdraw.</param>
/// <returns>True if successful, false if balance was too low.</returns>
bool WithdrawPlat(int amount);
/// <summary>
/// Returns total gold inclusive of platinum, capped to Int32.
/// This is strictly for backwards compatibility
/// </summary>
/// <returns>Total gold, capped at Int32.MaxValue</returns>
long GetTotalGold();
}
public interface IAccount : IGoldAccount, IComparable<IAccount>
{
string Username{ get; set; }
string Email{ get; set; }
AccessLevel AccessLevel{ get; set; }
int Length{ get; }
int Limit{ get; }
int Count{ get; }
Mobile this[int index]{ get; set; }
void Delete();
void SetPassword(string password);
bool CheckPassword(string password);
}
}

View file

@ -0,0 +1,85 @@
/***************************************************************************
* IEntity.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
namespace Server
{
public interface IEntity : IPoint3D, IComparable<IEntity>
{
Serial Serial{ get; }
Point3D Location{ get; }
Map Map{ get; }
bool Deleted{ get; }
void MoveToWorld(Point3D location, Map map);
void Delete();
void ProcessDelta();
}
public class Entity : IEntity, IComparable<Entity>
{
public Entity(Serial serial, Point3D loc, Map map)
{
Serial = serial;
Location = loc;
Map = map;
Deleted = false;
}
public int CompareTo(Entity other)
{
return CompareTo((IEntity)other);
}
public int CompareTo(IEntity other)
{
return other == null ? -1 : Serial.CompareTo(other.Serial);
}
public Serial Serial{ get; }
public Point3D Location{ get; private set; }
public int X => Location.X;
public int Y => Location.Y;
public int Z => Location.Z;
public Map Map{ get; private set; }
public virtual void MoveToWorld(Point3D newLocation, Map map)
{
Location = newLocation;
Map = map;
}
public bool Deleted{ get; }
public void Delete()
{
}
public void ProcessDelta()
{
}
}
}

View file

@ -0,0 +1,72 @@
/***************************************************************************
* Insensitive.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
namespace Server
{
public static class Insensitive
{
public static IComparer<string> Comparer{ get; } = StringComparer.OrdinalIgnoreCase;
public static int Compare(string a, string b)
{
return Comparer.Compare(a, b);
}
public static bool Equals(string a, string b)
{
if (a == null && b == null)
return true;
if (a == null || b == null || a.Length != b.Length)
return false;
return Comparer.Compare(a, b) == 0;
}
public static bool StartsWith(string a, string b)
{
if (a == null || b == null || a.Length < b.Length)
return false;
return Comparer.Compare(a.Substring(0, b.Length), b) == 0;
}
public static bool EndsWith(string a, string b)
{
if (a == null || b == null || a.Length < b.Length)
return false;
return Comparer.Compare(a.Substring(a.Length - b.Length), b) == 0;
}
public static bool Contains(string a, string b)
{
if (a == null || b == null || a.Length < b.Length)
return false;
a = a.ToLower();
b = b.ToLower();
return a.IndexOf(b) >= 0;
}
}
}

View file

@ -0,0 +1,115 @@
/***************************************************************************
* Interfaces.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using Server.Mobiles;
namespace Server.Mobiles
{
public interface IMount
{
Mobile Rider{ get; set; }
void OnRiderDamaged(int amount, Mobile from, bool willKill);
}
public interface IMountItem
{
IMount Mount{ get; }
}
}
namespace Server
{
public interface IVendor
{
DateTime LastRestock{ get; set; }
TimeSpan RestockDelay{ get; }
bool OnBuyItems(Mobile from, List<BuyItemResponse> list);
bool OnSellItems(Mobile from, List<SellItemResponse> list);
void Restock();
}
public interface IPoint2D
{
int X{ get; }
int Y{ get; }
}
public interface IPoint3D : IPoint2D
{
int Z{ get; }
}
public interface ICarvable
{
void Carve(Mobile from, Item item);
}
public interface IWeapon
{
int MaxRange{ get; }
void OnBeforeSwing(Mobile attacker, Mobile defender);
TimeSpan OnSwing(Mobile attacker, Mobile defender);
void GetStatusDamage(Mobile from, out int min, out int max);
}
public interface IHued
{
int HuedItemID{ get; }
}
public interface ISpell
{
bool IsCasting{ get; }
void OnCasterHurt();
void OnCasterKilled();
void OnConnectionChanged();
bool OnCasterMoving(Direction d);
bool OnCasterEquipping(Item item);
bool OnCasterUsingObject(IEntity entity);
bool OnCastInTown(Region r);
void FinishSequence();
}
public interface IParty
{
void OnStamChanged(Mobile m);
void OnManaChanged(Mobile m);
void OnStatsQuery(Mobile beholder, Mobile beheld);
}
public interface ISpawner
{
bool UnlinkOnTaming{ get; }
Point3D HomeLocation{ get; }
int HomeRange{ get; }
Region Region{ get; }
void Remove(ISpawnable spawn);
}
public interface ISpawnable : IEntity
{
ISpawner Spawner{ get; set; }
void OnBeforeSpawn(Point3D location, Map map);
void OnAfterSpawn();
}
}

3877
Projects/Server/Item.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,58 @@
/***************************************************************************
* ItemBounds.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.IO;
namespace Server
{
public static class ItemBounds
{
static ItemBounds()
{
Table = new Rectangle2D[TileData.ItemTable.Length];
if (File.Exists("Data/Binary/Bounds.bin"))
using (FileStream fs = new FileStream("Data/Binary/Bounds.bin", FileMode.Open, FileAccess.Read,
FileShare.Read))
{
BinaryReader bin = new BinaryReader(fs);
int count = Math.Min(Table.Length, (int)(fs.Length / 8));
for (int i = 0; i < count; ++i)
{
int xMin = bin.ReadInt16();
int yMin = bin.ReadInt16();
int xMax = bin.ReadInt16();
int yMax = bin.ReadInt16();
Table[i].Set(xMin, yMin, xMax - xMin + 1, yMax - yMin + 1);
}
bin.Close();
}
else
Console.WriteLine("Warning: Data/Binary/Bounds.bin does not exist");
}
public static Rectangle2D[] Table{ get; }
}
}

View file

@ -0,0 +1,164 @@
/***************************************************************************
* BaseMulti.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
namespace Server.Items
{
public abstract class BaseMulti : Item
{
public BaseMulti(int itemID) : base(itemID)
{
Movable = false;
}
public BaseMulti(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public override int ItemID
{
get => base.ItemID;
set
{
if (base.ItemID != value)
{
Map facet = Parent == null ? Map : null;
facet?.OnLeave(this);
base.ItemID = value;
facet?.OnEnter(this);
}
}
}
public override int LabelNumber
{
get
{
MultiComponentList mcl = Components;
if (mcl.List.Length > 0)
{
int id = mcl.List[0].m_ItemID;
if (id < 0x4000)
return 1020000 + id;
return 1078872 + id;
}
return base.LabelNumber;
}
}
public virtual bool AllowsRelativeDrop => false;
public virtual MultiComponentList Components => MultiData.GetComponents(ItemID);
[Obsolete("Replace with calls to OnLeave and OnEnter surrounding component invalidation.", true)]
public virtual void RefreshComponents()
{
if (Parent == null)
{
Map facet = Map;
if (facet != null)
{
facet.OnLeave(this);
facet.OnEnter(this);
}
}
}
public override int GetMaxUpdateRange()
{
return 22;
}
public override int GetUpdateRange(Mobile m)
{
return 22;
}
public virtual bool Contains(Point2D p)
{
return Contains(p.m_X, p.m_Y);
}
public virtual bool Contains(Point3D p)
{
return Contains(p.m_X, p.m_Y);
}
public virtual bool Contains(IPoint3D p)
{
return Contains(p.X, p.Y);
}
public virtual bool Contains(int x, int y)
{
MultiComponentList mcl = Components;
x -= X + mcl.Min.m_X;
y -= Y + mcl.Min.m_Y;
return x >= 0
&& x < mcl.Width
&& y >= 0
&& y < mcl.Height
&& mcl.Tiles[x][y].Length > 0;
}
public bool Contains(Mobile m)
{
if (m.Map == Map)
return Contains(m.X, m.Y);
return false;
}
public bool Contains(Item item)
{
if (item.Map == Map)
return Contains(item.X, item.Y);
return false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(1); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
if (version == 0)
if (ItemID >= 0x4000)
ItemID -= 0x4000;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,141 @@
/***************************************************************************
* Containers.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Accounting;
using Server.Network;
namespace Server.Items
{
public class BankBox : Container
{
public BankBox(Serial serial) : base(serial)
{
}
public BankBox(Mobile owner) : base(0xE7C)
{
Layer = Layer.Bank;
Movable = false;
Owner = owner;
}
public override int DefaultMaxWeight => 0;
public override bool IsVirtualItem => true;
public Mobile Owner{ get; private set; }
public bool Opened{ get; private set; }
public static bool SendDeleteOnClose{ get; set; }
public void Open()
{
Opened = true;
if (Owner != null)
{
Owner.PrivateOverheadMessage(MessageType.Regular, 0x3B2, true,
$"Bank container has {TotalItems} items, {TotalWeight} stones", Owner.NetState);
Owner.Send(new EquipUpdate(this));
DisplayTo(Owner);
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
writer.Write(Owner);
writer.Write(Opened);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
Owner = reader.ReadMobile();
Opened = reader.ReadBool();
if (Owner == null)
Delete();
break;
}
}
if (ItemID == 0xE41)
ItemID = 0xE7C;
}
public void Close()
{
Opened = false;
if (SendDeleteOnClose)
Owner?.Send(RemovePacket);
}
public override void OnSingleClick(Mobile from)
{
}
public override void OnDoubleClick(Mobile from)
{
}
public override DeathMoveResult OnParentDeath(Mobile parent)
{
return DeathMoveResult.RemainEquipped;
}
public override bool IsAccessibleTo(Mobile check)
{
return (check == Owner && Opened || check.AccessLevel >= AccessLevel.GameMaster) && base.IsAccessibleTo(check);
}
public override bool OnDragDrop(Mobile from, Item dropped)
{
return (from == Owner && Opened || from.AccessLevel >= AccessLevel.GameMaster) && base.OnDragDrop(from, dropped);
}
public override bool OnDragDropInto(Mobile from, Item item, Point3D p)
{
return (from == Owner && Opened || from.AccessLevel >= AccessLevel.GameMaster) &&
base.OnDragDropInto(from, item, p);
}
public override int GetTotal(TotalType type)
{
if (AccountGold.Enabled && Owner?.Account != null && type == TotalType.Gold)
return Owner.Account.TotalGold;
return base.GetTotal(type);
}
}
}

View file

@ -0,0 +1,120 @@
/***************************************************************************
* SecureTradeContainer.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Accounting;
using Server.Network;
namespace Server.Items
{
public class SecureTradeContainer : Container
{
public SecureTradeContainer(SecureTrade trade) : base(0x1E5E)
{
Trade = trade;
Movable = false;
}
public SecureTradeContainer(Serial serial) : base(serial)
{
}
public SecureTrade Trade{ get; }
public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight)
{
if (item == Trade.From.VirtualCheck || item == Trade.To.VirtualCheck) return true;
Mobile to = Trade.From.Container != this ? Trade.From.Mobile : Trade.To.Mobile;
return m.CheckTrade(to, item, this, message, checkItems, plusItems, plusWeight);
}
public override bool CheckLift(Mobile from, Item item, ref LRReason reject)
{
reject = LRReason.CannotLift;
return false;
}
public override bool IsAccessibleTo(Mobile check)
{
return IsChildOf(check) && Trade?.Valid == true && base.IsAccessibleTo(check);
}
public override void OnItemAdded(Item item)
{
if (!(item is VirtualCheck))
ClearChecks();
}
public override void OnItemRemoved(Item item)
{
if (!(item is VirtualCheck))
ClearChecks();
}
public override void OnSubItemAdded(Item item)
{
if (!(item is VirtualCheck))
ClearChecks();
}
public override void OnSubItemRemoved(Item item)
{
if (!(item is VirtualCheck))
ClearChecks();
}
public void ClearChecks()
{
if (Trade == null)
return;
if (Trade.From?.IsDisposed == false)
Trade.From.Accepted = false;
if (Trade.To?.IsDisposed == false)
Trade.To.Accepted = false;
Trade.Update();
}
public override bool IsChildVisibleTo(Mobile m, Item child)
{
return child is VirtualCheck
? AccountGold.Enabled && m.NetState?.NewSecureTrading != true
: base.IsChildVisibleTo(m, child);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0); // version
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
}
}
}

View file

@ -0,0 +1,353 @@
using Server.Gumps;
using Server.Items;
using Server.Network;
namespace Server
{
public sealed class VirtualCheck : Item
{
public static bool UseEditGump = false;
private int _Gold;
private int _Plat;
public VirtualCheck(int plat = 0, int gold = 0)
: base(0x14F0)
{
Plat = plat;
Gold = gold;
Movable = false;
}
public VirtualCheck(Serial serial)
: base(serial)
{
}
public override bool IsVirtualItem => true;
public override bool DisplayWeight => false;
public override bool DisplayLootType => false;
public override double DefaultWeight => 0;
public override string DefaultName => "Offer Of Currency";
public EditGump Editor{ get; private set; }
[CommandProperty(AccessLevel.Administrator)]
public int Plat
{
get => _Plat;
set
{
_Plat = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.Administrator)]
public int Gold
{
get => _Gold;
set
{
_Gold = value;
InvalidateProperties();
}
}
public override bool IsAccessibleTo(Mobile check)
{
SecureTradeContainer c = GetSecureTradeCont();
if (check == null || c == null) return base.IsAccessibleTo(check);
return c.RootParent == check && IsChildOf(c);
}
public override void OnDoubleClickSecureTrade(Mobile from)
{
if (UseEditGump && IsAccessibleTo(from))
{
if (Editor?.Check?.Deleted != false)
{
Editor = new EditGump(from, this);
Editor.Send();
}
else
{
Editor.Refresh(true);
}
}
else
{
if (Editor != null)
{
Editor.Close();
Editor = null;
}
base.OnDoubleClickSecureTrade(from);
}
}
public override void OnSingleClick(Mobile from)
{
LabelTo(from, "Offer: {0:#,0} platinum, {1:#,0} gold", Plat, Gold);
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
list.Add(1060738, $"{Plat:#,0} platinum, {Gold:#,0} gold"); // value: ~1_val~
}
public void UpdateTrade(Mobile user)
{
SecureTradeContainer c = GetSecureTradeCont();
if (c?.Trade == null) return;
if (user == c.Trade.From.Mobile)
c.Trade.UpdateFromCurrency();
else if (user == c.Trade.To.Mobile) c.Trade.UpdateToCurrency();
c.ClearChecks();
}
public override void OnAfterDelete()
{
base.OnAfterDelete();
if (Editor != null)
{
Editor.Close();
Editor = null;
}
}
public override void Serialize(GenericWriter writer)
{
}
public override void Deserialize(GenericReader reader)
{
Delete();
}
public class EditGump : Gump
{
public enum Buttons
{
Close,
Clear,
Accept,
AllPlat,
AllGold
}
private int _Plat, _Gold;
public EditGump(Mobile user, VirtualCheck check)
: base(50, 50)
{
User = user;
Check = check;
_Plat = Check.Plat;
_Gold = Check.Gold;
Closable = true;
Disposable = true;
Draggable = true;
Resizable = false;
User.CloseGump<EditGump>();
CompileLayout();
}
public Mobile User{ get; }
public VirtualCheck Check{ get; private set; }
public override void OnServerClose(NetState owner)
{
base.OnServerClose(owner);
if (Check?.Deleted == false)
Check.UpdateTrade(User);
}
public void Close()
{
User.CloseGump<EditGump>();
if (Check?.Deleted == false)
Check.UpdateTrade(User);
else
Check = null;
}
public void Send()
{
if (Check?.Deleted == false)
User.SendGump(this);
else
Close();
}
public void Refresh(bool recompile)
{
if (Check?.Deleted != false)
{
Close();
return;
}
if (recompile)
CompileLayout();
Close();
Send();
}
private void CompileLayout()
{
if (Check?.Deleted != false)
return;
Entries.ForEach(e => e.Parent = null);
Entries.Clear();
AddPage(0);
AddBackground(0, 0, 400, 160, 3500);
// Title
AddImageTiled(25, 35, 350, 3, 96);
AddImage(10, 8, 113);
AddImage(360, 8, 113);
string title =
$"<BASEFONT COLOR=#FF2F4F4F><CENTER>BANK OF {User.RawName.ToUpper()}</CENTER>";
AddHtml(40, 15, 320, 20, title);
// Platinum Row
AddBackground(15, 60, 175, 20, 9300);
AddBackground(20, 45, 165, 30, 9350);
AddItem(20, 45, 3826); // Plat
AddLabel(60, 50, 0, User.Account.TotalPlat.ToString("#,0"));
AddButton(195, 50, 95, 95, (int)Buttons.AllPlat); // ->
AddBackground(210, 60, 175, 20, 9300);
AddBackground(215, 45, 165, 30, 9350);
AddTextEntry(225, 50, 145, 20, 0, 0, _Plat.ToString(), User.Account.TotalPlat.ToString().Length);
// Gold Row
AddBackground(15, 100, 175, 20, 9300);
AddBackground(20, 85, 165, 30, 9350);
AddItem(20, 85, 3823); // Gold
AddLabel(60, 90, 0, User.Account.TotalGold.ToString("#,0"));
AddButton(195, 90, 95, 95, (int)Buttons.AllGold); // ->
AddBackground(210, 100, 175, 20, 9300);
AddBackground(215, 85, 165, 30, 9350);
AddTextEntry(225, 90, 145, 20, 0, 1, _Gold.ToString(), User.Account.TotalGold.ToString().Length);
// Buttons
AddButton(20, 128, 12006, 12007, (int)Buttons.Close);
AddButton(215, 128, 12003, 12004, (int)Buttons.Clear);
AddButton(305, 128, 12000, 12002, (int)Buttons.Accept);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (Check?.Deleted != false || sender.Mobile != User)
{
Close();
return;
}
bool refresh = false, updated = false;
switch ((Buttons)info.ButtonID)
{
case Buttons.Close:
break;
case Buttons.Clear:
{
_Plat = _Gold = 0;
refresh = true;
}
break;
case Buttons.Accept:
{
string platText = info.GetTextEntry(0).Text;
string goldText = info.GetTextEntry(1).Text;
if (!int.TryParse(platText, out _Plat))
{
User.SendMessage("That is not a valid amount of platinum.");
refresh = true;
}
else if (!int.TryParse(goldText, out _Gold))
{
User.SendMessage("That is not a valid amount of gold.");
refresh = true;
}
else
{
int totalPlat = User.Account.TotalPlat;
int totalGold = User.Account.TotalGold;
if (totalPlat < _Plat || totalGold < _Gold)
{
_Plat = User.Account.TotalPlat;
_Gold = User.Account.TotalGold;
User.SendMessage("You do not have that much currency.");
refresh = true;
}
else
{
Check.Plat = _Plat;
Check.Gold = _Gold;
updated = true;
}
}
}
break;
case Buttons.AllPlat:
{
_Plat = User.Account.TotalPlat;
refresh = true;
}
break;
case Buttons.AllGold:
{
_Gold = User.Account.TotalGold;
refresh = true;
}
break;
}
if (updated)
User.SendMessage("Your offer has been updated.");
if (refresh && Check?.Deleted == false)
{
Refresh(true);
return;
}
Close();
}
}
}
}

View file

@ -0,0 +1,165 @@
/***************************************************************************
* VirtualHair.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server
{
public abstract class BaseHairInfo
{
protected BaseHairInfo(int itemid, int hue = 0)
{
ItemID = itemid;
Hue = hue;
}
protected BaseHairInfo(GenericReader reader)
{
int version = reader.ReadInt();
switch (version)
{
case 0:
{
ItemID = reader.ReadInt();
Hue = reader.ReadInt();
break;
}
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int ItemID{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int Hue{ get; set; }
public virtual void Serialize(GenericWriter writer)
{
writer.Write(0); //version
writer.Write(ItemID);
writer.Write(Hue);
}
}
public class HairInfo : BaseHairInfo
{
public HairInfo(int itemid)
: base(itemid)
{
}
public HairInfo(int itemid, int hue)
: base(itemid, hue)
{
}
public HairInfo(GenericReader reader)
: base(reader)
{
}
// TOOD: Can we make this higher for newer clients?
public static uint FakeSerial(Mobile parent)
{
return 0x7FFFFFFF - 0x400 - parent.Serial * 4;
}
}
public class FacialHairInfo : BaseHairInfo
{
public FacialHairInfo(int itemid)
: base(itemid)
{
}
public FacialHairInfo(int itemid, int hue)
: base(itemid, hue)
{
}
public FacialHairInfo(GenericReader reader)
: base(reader)
{
}
// TOOD: Can we make this higher for newer clients?
public static uint FakeSerial(Mobile parent)
{
return 0x7FFFFFFF - 0x400 - 1 - parent.Serial * 4;
}
}
public sealed class HairEquipUpdate : Packet
{
public HairEquipUpdate(Mobile parent)
: base(0x2E, 15)
{
int hue = parent.HairHue;
if (parent.SolidHueOverride >= 0)
hue = parent.SolidHueOverride;
m_Stream.Write(HairInfo.FakeSerial(parent));
m_Stream.Write((short)parent.HairItemID);
m_Stream.Write((byte)0);
m_Stream.Write((byte)Layer.Hair);
m_Stream.Write(parent.Serial);
m_Stream.Write((short)hue);
}
}
public sealed class FacialHairEquipUpdate : Packet
{
public FacialHairEquipUpdate(Mobile parent)
: base(0x2E, 15)
{
int hue = parent.FacialHairHue;
if (parent.SolidHueOverride >= 0)
hue = parent.SolidHueOverride;
m_Stream.Write(FacialHairInfo.FakeSerial(parent));
m_Stream.Write((short)parent.FacialHairItemID);
m_Stream.Write((byte)0);
m_Stream.Write((byte)Layer.FacialHair);
m_Stream.Write(parent.Serial);
m_Stream.Write((short)hue);
}
}
public sealed class RemoveHair : Packet
{
public RemoveHair(Mobile parent)
: base(0x1D, 5)
{
m_Stream.Write(HairInfo.FakeSerial(parent));
}
}
public sealed class RemoveFacialHair : Packet
{
public RemoveFacialHair(Mobile parent)
: base(0x1D, 5)
{
m_Stream.Write(FacialHairInfo.FakeSerial(parent));
}
}
}

View file

@ -0,0 +1,75 @@
/***************************************************************************
* KeywordList.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server
{
public class KeywordList
{
private static int[] m_EmptyInts = new int[0];
private int[] m_Keywords;
public KeywordList()
{
m_Keywords = new int[8];
Count = 0;
}
public int Count{ get; private set; }
public bool Contains(int keyword)
{
bool contains = false;
for (int i = 0; !contains && i < Count; ++i)
contains = keyword == m_Keywords[i];
return contains;
}
public void Add(int keyword)
{
if (Count + 1 > m_Keywords.Length)
{
int[] old = m_Keywords;
m_Keywords = new int[old.Length * 2];
for (int i = 0; i < old.Length; ++i)
m_Keywords[i] = old[i];
}
m_Keywords[Count++] = keyword;
}
public int[] ToArray()
{
if (Count == 0)
return m_EmptyInts;
int[] keywords = new int[Count];
for (int i = 0; i < Count; ++i)
keywords[i] = m_Keywords[i];
Count = 0;
return keywords;
}
}
}

173
Projects/Server/Layer.cs Normal file
View file

@ -0,0 +1,173 @@
namespace Server
{
/// <summary>
/// Enumeration of item layer values.
/// </summary>
public enum Layer : byte
{
/// <summary>
/// Invalid layer.
/// </summary>
Invalid = 0x00,
/// <summary>
/// First valid layer. Equivalent to <c>Layer.OneHanded</c>.
/// </summary>
FirstValid = 0x01,
/// <summary>
/// One handed weapon.
/// </summary>
OneHanded = 0x01,
/// <summary>
/// Two handed weapon or shield.
/// </summary>
TwoHanded = 0x02,
/// <summary>
/// Shoes.
/// </summary>
Shoes = 0x03,
/// <summary>
/// Pants.
/// </summary>
Pants = 0x04,
/// <summary>
/// Shirts.
/// </summary>
Shirt = 0x05,
/// <summary>
/// Helmets, hats, and masks.
/// </summary>
Helm = 0x06,
/// <summary>
/// Gloves.
/// </summary>
Gloves = 0x07,
/// <summary>
/// Rings.
/// </summary>
Ring = 0x08,
/// <summary>
/// Talismans.
/// </summary>
Talisman = 0x09,
/// <summary>
/// Gorgets and necklaces.
/// </summary>
Neck = 0x0A,
/// <summary>
/// Hair.
/// </summary>
Hair = 0x0B,
/// <summary>
/// Half aprons.
/// </summary>
Waist = 0x0C,
/// <summary>
/// Torso, inner layer.
/// </summary>
InnerTorso = 0x0D,
/// <summary>
/// Bracelets.
/// </summary>
Bracelet = 0x0E,
/// <summary>
/// Unused.
/// </summary>
Unused_xF = 0x0F,
/// <summary>
/// Beards and mustaches.
/// </summary>
FacialHair = 0x10,
/// <summary>
/// Torso, outer layer.
/// </summary>
MiddleTorso = 0x11,
/// <summary>
/// Earings.
/// </summary>
Earrings = 0x12,
/// <summary>
/// Arms and sleeves.
/// </summary>
Arms = 0x13,
/// <summary>
/// Cloaks.
/// </summary>
Cloak = 0x14,
/// <summary>
/// Backpacks.
/// </summary>
Backpack = 0x15,
/// <summary>
/// Torso, outer layer.
/// </summary>
OuterTorso = 0x16,
/// <summary>
/// Leggings, outer layer.
/// </summary>
OuterLegs = 0x17,
/// <summary>
/// Leggings, inner layer.
/// </summary>
InnerLegs = 0x18,
/// <summary>
/// Last valid non-internal layer. Equivalent to <c>Layer.InnerLegs</c>.
/// </summary>
LastUserValid = 0x18,
/// <summary>
/// Mount item layer.
/// </summary>
Mount = 0x19,
/// <summary>
/// Vendor 'buy pack' layer.
/// </summary>
ShopBuy = 0x1A,
/// <summary>
/// Vendor 'resale pack' layer.
/// </summary>
ShopResale = 0x1B,
/// <summary>
/// Vendor 'sell pack' layer.
/// </summary>
ShopSell = 0x1C,
/// <summary>
/// Bank box layer.
/// </summary>
Bank = 0x1D,
/// <summary>
/// Last valid layer. Equivalent to <c>Layer.Bank</c>.
/// </summary>
LastValid = 0x1D
}
}

View file

@ -0,0 +1,287 @@
namespace Server
{
public enum LightType
{
/// <summary>
/// Window shape, arched, ray shining east.
/// </summary>
ArchedWindowEast,
/// <summary>
/// Medium circular shape.
/// </summary>
Circle225,
/// <summary>
/// Small circular shape.
/// </summary>
Circle150,
/// <summary>
/// Door shape, shining south.
/// </summary>
DoorSouth,
/// <summary>
/// Door shape, shining east.
/// </summary>
DoorEast,
/// <summary>
/// Large semicircular shape (180 degrees), north wall.
/// </summary>
NorthBig,
/// <summary>
/// Large pie shape (90 degrees), north-east corner.
/// </summary>
NorthEastBig,
/// <summary>
/// Large semicircular shape (180 degrees), east wall.
/// </summary>
EastBig,
/// <summary>
/// Large semicircular shape (180 degrees), west wall.
/// </summary>
WestBig,
/// <summary>
/// Large pie shape (90 degrees), south-west corner.
/// </summary>
SouthWestBig,
/// <summary>
/// Large semicircular shape (180 degrees), south wall.
/// </summary>
SouthBig,
/// <summary>
/// Medium semicircular shape (180 degrees), north wall.
/// </summary>
NorthSmall,
/// <summary>
/// Medium pie shape (90 degrees), north-east corner.
/// </summary>
NorthEastSmall,
/// <summary>
/// Medium semicircular shape (180 degrees), east wall.
/// </summary>
EastSmall,
/// <summary>
/// Medium semicircular shape (180 degrees), west wall.
/// </summary>
WestSmall,
/// <summary>
/// Medium semicircular shape (180 degrees), south wall.
/// </summary>
SouthSmall,
/// <summary>
/// Shaped like a wall decoration, north wall.
/// </summary>
DecorationNorth,
/// <summary>
/// Shaped like a wall decoration, north-east corner.
/// </summary>
DecorationNorthEast,
/// <summary>
/// Small semicircular shape (180 degrees), east wall.
/// </summary>
EastTiny,
/// <summary>
/// Shaped like a wall decoration, west wall.
/// </summary>
DecorationWest,
/// <summary>
/// Shaped like a wall decoration, south-west corner.
/// </summary>
DecorationSouthWest,
/// <summary>
/// Small semicircular shape (180 degrees), south wall.
/// </summary>
SouthTiny,
/// <summary>
/// Window shape, rectangular, no ray, shining south.
/// </summary>
RectWindowSouthNoRay,
/// <summary>
/// Window shape, rectangular, no ray, shining east.
/// </summary>
RectWindowEastNoRay,
/// <summary>
/// Window shape, rectangular, ray shining south.
/// </summary>
RectWindowSouth,
/// <summary>
/// Window shape, rectangular, ray shining east.
/// </summary>
RectWindowEast,
/// <summary>
/// Window shape, arched, no ray, shining south.
/// </summary>
ArchedWindowSouthNoRay,
/// <summary>
/// Window shape, arched, no ray, shining east.
/// </summary>
ArchedWindowEastNoRay,
/// <summary>
/// Window shape, arched, ray shining south.
/// </summary>
ArchedWindowSouth,
/// <summary>
/// Large circular shape.
/// </summary>
Circle300,
/// <summary>
/// Large pie shape (90 degrees), north-west corner.
/// </summary>
NorthWestBig,
/// <summary>
/// Negative light. Medium pie shape (90 degrees), south-east corner.
/// </summary>
DarkSouthEast,
/// <summary>
/// Negative light. Medium semicircular shape (180 degrees), south wall.
/// </summary>
DarkSouth,
/// <summary>
/// Negative light. Medium pie shape (90 degrees), north-west corner.
/// </summary>
DarkNorthWest,
/// <summary>
/// Negative light. Medium pie shape (90 degrees), south-east corner. Equivalent to <c>LightType.SouthEast</c>.
/// </summary>
DarkSouthEast2,
/// <summary>
/// Negative light. Medium circular shape (180 degrees), east wall.
/// </summary>
DarkEast,
/// <summary>
/// Negative light. Large circular shape.
/// </summary>
DarkCircle300,
/// <summary>
/// Opened door shape, shining south.
/// </summary>
DoorOpenSouth,
/// <summary>
/// Opened door shape, shining east.
/// </summary>
DoorOpenEast,
/// <summary>
/// Window shape, square, ray shining east.
/// </summary>
SquareWindowEast,
/// <summary>
/// Window shape, square, no ray, shining east.
/// </summary>
SquareWindowEastNoRay,
/// <summary>
/// Window shape, square, ray shining south.
/// </summary>
SquareWindowSouth,
/// <summary>
/// Window shape, square, no ray, shining south.
/// </summary>
SquareWindowSouthNoRay,
/// <summary>
/// Empty.
/// </summary>
Empty,
/// <summary>
/// Window shape, skinny, no ray, shining south.
/// </summary>
SkinnyWindowSouthNoRay,
/// <summary>
/// Window shape, skinny, ray shining east.
/// </summary>
SkinnyWindowEast,
/// <summary>
/// Window shape, skinny, no ray, shining east.
/// </summary>
SkinnyWindowEastNoRay,
/// <summary>
/// Shaped like a hole, shining south.
/// </summary>
HoleSouth,
/// <summary>
/// Shaped like a hole, shining south.
/// </summary>
HoleEast,
/// <summary>
/// Large circular shape with a moongate graphic embedded.
/// </summary>
Moongate,
/// <summary>
/// Unknown usage. Many rows of slightly angled lines.
/// </summary>
Strips,
/// <summary>
/// Shaped like a small hole, shining south.
/// </summary>
SmallHoleSouth,
/// <summary>
/// Shaped like a small hole, shining east.
/// </summary>
SmallHoleEast,
/// <summary>
/// Large semicircular shape (180 degrees), north wall. Identical graphic as <c>LightType.NorthBig</c>, but slightly different
/// positioning.
/// </summary>
NorthBig2,
/// <summary>
/// Large semicircular shape (180 degrees), west wall. Identical graphic as <c>LightType.WestBig</c>, but slightly different
/// positioning.
/// </summary>
WestBig2,
/// <summary>
/// Large pie shape (90 degrees), north-west corner. Equivalent to <c>LightType.NorthWestBig</c>.
/// </summary>
NorthWestBig2
}
}

724
Projects/Server/Main.cs Normal file
View file

@ -0,0 +1,724 @@
/***************************************************************************
* Main.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Server.Network;
namespace Server
{
public delegate void Slice();
public static class Core
{
private static bool m_Crashed;
private static Thread timerThread;
private static string m_BaseDirectory;
private static string m_ExePath;
private static bool m_Cache = true;
private static bool m_Profiling;
private static DateTime m_ProfileStart;
private static TimeSpan m_ProfileTime;
public static Slice Slice;
/*
* DateTime.Now and DateTime.UtcNow are based on actual system clock time.
* The resolution is acceptable but large clock jumps are possible and cause issues.
* GetTickCount and GetTickCount64 have poor resolution.
* GetTickCount64 is unavailable on Windows XP and Windows Server 2003.
* Stopwatch.GetTimestamp() (QueryPerformanceCounter) is high resolution, but
* somewhat expensive to call because of its defference to DateTime.Now,
* which is why Stopwatch has been used to verify HRT before calling GetTimestamp(),
* enabling the usage of DateTime.UtcNow instead.
*/
private static readonly bool _HighRes = Stopwatch.IsHighResolution;
private static readonly double _HighFrequency = 1000.0 / Stopwatch.Frequency;
private static readonly double _LowFrequency = 1000.0 / TimeSpan.TicksPerSecond;
private static bool _UseHRT;
public static readonly bool Is64Bit = Environment.Is64BitProcess;
internal static ConsoleEventHandler m_ConsoleEventHandler;
private static int m_CycleIndex = 1;
private static readonly float[] m_CyclesPerSecond = new float[100];
private static readonly AutoResetEvent m_Signal = new AutoResetEvent(true);
private static int m_ItemCount, m_MobileCount;
private static readonly Type[] m_SerialTypeArray = { typeof(Serial) };
public static MessagePump MessagePump{ get; set; }
public static bool Profiling
{
get => m_Profiling;
set
{
if (m_Profiling == value)
return;
m_Profiling = value;
if (m_ProfileStart > DateTime.MinValue)
m_ProfileTime += DateTime.UtcNow - m_ProfileStart;
m_ProfileStart = m_Profiling ? DateTime.UtcNow : DateTime.MinValue;
}
}
public static TimeSpan ProfileTime
{
get
{
if (m_ProfileStart > DateTime.MinValue)
return m_ProfileTime + (DateTime.UtcNow - m_ProfileStart);
return m_ProfileTime;
}
}
public static bool Service{ get; private set; }
public static bool Debug { get; private set; }
internal static bool HaltOnWarning{ get; private set; }
public static List<string> DataDirectories{ get; } = new List<string>();
public static Assembly Assembly{ get; set; }
public static Version Version => Assembly.GetName().Version;
public static Process Process{ get; private set; }
public static Thread Thread{ get; private set; }
public static MultiTextWriter MultiConsoleOut{ get; private set; }
public static bool UsingHighResolutionTiming => _UseHRT && _HighRes && !Unix;
public static long TickCount => (long)Ticks;
public static double Ticks
{
get
{
if (_UseHRT && _HighRes && !Unix) return Stopwatch.GetTimestamp() * _HighFrequency;
return DateTime.UtcNow.Ticks * _LowFrequency;
}
}
public static bool MultiProcessor{ get; private set; }
public static int ProcessorCount{ get; private set; }
public static bool Unix{ get; private set; }
public static string ExePath => m_ExePath ?? (m_ExePath = Assembly.Location);
public static string BaseDirectory
{
get
{
if (m_BaseDirectory == null)
try
{
m_BaseDirectory = ExePath;
if (m_BaseDirectory.Length > 0)
m_BaseDirectory = Path.GetDirectoryName(m_BaseDirectory);
}
catch
{
m_BaseDirectory = "";
}
return m_BaseDirectory;
}
}
public static bool Closing{ get; private set; }
public static float CyclesPerSecond => m_CyclesPerSecond[(m_CycleIndex - 1) % m_CyclesPerSecond.Length];
public static float AverageCPS => m_CyclesPerSecond.Take(m_CycleIndex).Average();
public static string Arguments
{
get
{
StringBuilder sb = new StringBuilder();
if (Debug)
Utility.Separate(sb, "-debug", " ");
if (Service)
Utility.Separate(sb, "-service", " ");
if (m_Profiling)
Utility.Separate(sb, "-profile", " ");
if (!m_Cache)
Utility.Separate(sb, "-nocache", " ");
if (HaltOnWarning)
Utility.Separate(sb, "-haltonwarning", " ");
if (_UseHRT)
Utility.Separate(sb, "-usehrt", " ");
return sb.ToString();
}
}
public static int GlobalUpdateRange{ get; set; } = 18;
public static int GlobalMaxUpdateRange{ get; set; } = 24;
public static int ScriptItems => m_ItemCount;
public static int ScriptMobiles => m_MobileCount;
public static string FindDataFile(string path)
{
if (DataDirectories.Count == 0)
throw new InvalidOperationException(
"Attempted to FindDataFile before DataDirectories list has been filled.");
string fullPath = null;
foreach (string p in DataDirectories)
{
fullPath = Path.Combine(p, path);
if (File.Exists(fullPath))
break;
fullPath = null;
}
return fullPath;
}
public static string FindDataFile(string format, params object[] args)
{
return FindDataFile(string.Format(format, args));
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine(e.IsTerminating ? "Error:" : "Warning:");
Console.WriteLine(e.ExceptionObject);
if (e.IsTerminating)
{
m_Crashed = true;
bool close = false;
try
{
CrashedEventArgs args = new CrashedEventArgs(e.ExceptionObject as Exception);
EventSink.InvokeCrashed(args);
close = args.Close;
}
catch
{
// ignored
}
if (!close && !Service)
{
try
{
foreach (Listener l in MessagePump.Listeners)
l.Dispose();
}
catch
{
// ignored
}
Console.WriteLine("This exception is fatal, press return to exit");
Console.ReadLine();
}
Kill();
}
}
private static bool OnConsoleEvent(ConsoleEventType type)
{
if (World.Saving || Service && type == ConsoleEventType.CTRL_LOGOFF_EVENT)
return true;
Kill(); //Kill -> HandleClosed will handle waiting for the completion of flushing to disk
return true;
}
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
HandleClosed();
}
public static void Kill()
{
Kill(false);
}
public static void Kill(bool restart)
{
HandleClosed();
if (restart)
Process.Start(ExePath, Arguments);
Process.Kill();
}
private static void HandleClosed()
{
if (Closing)
return;
Closing = true;
Console.WriteLine("Exiting...");
World.WaitForWriteCompletion();
if (!m_Crashed)
EventSink.InvokeShutdown(new ShutdownEventArgs());
Timer.TimerThread.Set();
Console.WriteLine("done");
}
public static void Set()
{
m_Signal.Set();
}
public static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
foreach (string a in args)
if (Insensitive.Equals(a, "-debug"))
Debug = true;
else if (Insensitive.Equals(a, "-service"))
Service = true;
else if (Insensitive.Equals(a, "-profile"))
Profiling = true;
else if (Insensitive.Equals(a, "-nocache"))
m_Cache = false;
else if (Insensitive.Equals(a, "-haltonwarning"))
HaltOnWarning = true;
else if (Insensitive.Equals(a, "-usehrt"))
_UseHRT = true;
try
{
if (Service)
{
if (!Directory.Exists("Logs"))
Directory.CreateDirectory("Logs");
Console.SetOut(MultiConsoleOut = new MultiTextWriter(new FileLogger("Logs/Console.log")));
}
else
{
Console.SetOut(MultiConsoleOut = new MultiTextWriter(Console.Out));
}
}
catch
{
// ignored
}
Thread = Thread.CurrentThread;
Process = Process.GetCurrentProcess();
Assembly = Assembly.GetEntryAssembly();
if (Thread != null)
Thread.Name = "Core Thread";
if (BaseDirectory.Length > 0)
Directory.SetCurrentDirectory(BaseDirectory);
Timer.TimerThread ttObj = new Timer.TimerThread();
timerThread = new Thread(ttObj.TimerMain)
{
Name = "Timer Thread"
};
Version ver = Assembly.GetName().Version;
// Added to help future code support on forums, as a 'check' people can ask for to it see if they recompiled core or not
Console.WriteLine("ModernUO - [https://github.com/kamronbatman/ModernUO] Version {0}.{1}.{2}.{3}", ver.Major, ver.Minor, ver.Build,
ver.Revision);
#if NETCORE
Console.WriteLine("Core: Running on {0}", RuntimeInformation.FrameworkDescription);
#else
Console.WriteLine("Core: Running on .NET Framework Version {0}.{1}.{2}", Environment.Version.Major,
Environment.Version.Minor, Environment.Version.Build);
#endif
string s = Arguments;
if (s.Length > 0)
Console.WriteLine("Core: Running with arguments: {0}", s);
ProcessorCount = Environment.ProcessorCount;
if (ProcessorCount > 1)
MultiProcessor = true;
if (MultiProcessor || Is64Bit)
Console.WriteLine("Core: Optimizing for {0} {2}processor{1}", ProcessorCount, ProcessorCount == 1 ? "" : "s",
Is64Bit ? "64-bit " : "");
int platform = (int)Environment.OSVersion.Platform;
if (platform == 4 || platform == 128)
{
// MS 4, MONO 128
Unix = true;
Console.WriteLine("Core: Unix environment detected");
}
else
{
m_ConsoleEventHandler = OnConsoleEvent;
UnsafeNativeMethods.SetConsoleCtrlHandler(m_ConsoleEventHandler, true);
}
if (GCSettings.IsServerGC)
Console.WriteLine("Core: Server garbage collection mode enabled");
if (_UseHRT)
Console.WriteLine("Core: Requested high resolution timing ({0})",
UsingHighResolutionTiming ? "Supported" : "Unsupported");
Console.WriteLine("RandomImpl: {0} ({1})", RandomImpl.Type.Name,
RandomImpl.IsHardwareRNG ? "Hardware" : "Software");
// Load Assembly Scripts.CS.dll
ScriptCompiler.LoadScripts();
ScriptCompiler.Invoke("Configure");
Region.Load();
World.Load();
ScriptCompiler.Invoke("Initialize");
// Start accepting new connections
MessagePump = new MessagePump();
ScriptCompiler.Invoke("RegisterListeners");
timerThread.Start();
foreach (Map m in Map.AllMaps)
m.Tiles.Force();
NetState.Initialize();
EventSink.InvokeServerStarted();
try
{
long last = TickCount;
const int sampleInterval = 100;
const float ticksPerSecond = 1000.0f * sampleInterval;
long sample = 0;
while (!Closing)
{
m_Signal.WaitOne();
Task.WaitAll(
Task.Run(() => Mobile.ProcessDeltaQueue()),
Task.Run(() => Item.ProcessDeltaQueue()),
Task.Run(() => Timer.Slice()),
Task.Run(() => MessagePump.DoWork())
);
NetState.ProcessDisposedQueue();
Slice?.Invoke();
if (sample++ % sampleInterval != 0)
continue;
long now = TickCount;
m_CyclesPerSecond[m_CycleIndex++ % m_CyclesPerSecond.Length] = ticksPerSecond / (now - last);
last = now;
}
}
catch (Exception e)
{
CurrentDomain_UnhandledException(null, new UnhandledExceptionEventArgs(e, true));
}
}
public static void VerifySerialization()
{
m_ItemCount = 0;
m_MobileCount = 0;
Assembly ca = Assembly.GetCallingAssembly();
VerifySerialization(ca);
foreach (Assembly a in ScriptCompiler.Assemblies.Where(a => a != ca)) VerifySerialization(a);
}
private static void VerifyType(Type t)
{
bool isItem = t.IsSubclassOf(typeof(Item));
if (!isItem && !t.IsSubclassOf(typeof(Mobile))) return;
if (isItem)
Interlocked.Increment(ref m_ItemCount);
else
Interlocked.Increment(ref m_MobileCount);
StringBuilder warningSb = null;
try
{
if (t.GetConstructor(m_SerialTypeArray) == null)
{
warningSb = new StringBuilder();
warningSb.AppendLine(" - No serialization constructor");
}
if (
t.GetMethod(
"Serialize",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly) ==
null)
{
if (warningSb == null) warningSb = new StringBuilder();
warningSb.AppendLine(" - No Serialize() method");
}
if (
t.GetMethod(
"Deserialize",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly) ==
null)
{
if (warningSb == null) warningSb = new StringBuilder();
warningSb.AppendLine(" - No Deserialize() method");
}
if (warningSb?.Length > 0) Console.WriteLine("Warning: {0}\n{1}", t, warningSb);
}
catch
{
Console.WriteLine("Warning: Exception in serialization verification of type {0}", t);
}
}
private static void VerifySerialization(Assembly a)
{
if (a != null) Parallel.ForEach(a.GetTypes(), VerifyType);
}
internal enum ConsoleEventType
{
CTRL_C_EVENT,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
internal delegate bool ConsoleEventHandler(ConsoleEventType type);
internal class UnsafeNativeMethods
{
[DllImport("Kernel32")]
internal static extern bool SetConsoleCtrlHandler(ConsoleEventHandler callback, bool add);
}
#region Expansions
public static Expansion Expansion{ get; set; }
public static bool T2A => Expansion >= Expansion.T2A;
public static bool UOR => Expansion >= Expansion.UOR;
public static bool UOTD => Expansion >= Expansion.UOTD;
public static bool LBR => Expansion >= Expansion.LBR;
public static bool AOS => Expansion >= Expansion.AOS;
public static bool SE => Expansion >= Expansion.SE;
public static bool ML => Expansion >= Expansion.ML;
public static bool SA => Expansion >= Expansion.SA;
public static bool HS => Expansion >= Expansion.HS;
public static bool TOL => Expansion >= Expansion.TOL;
#endregion
}
public class FileLogger : TextWriter
{
public const string DateFormat = "[MMMM dd hh:mm:ss.f tt]: ";
private bool _NewLine;
public FileLogger(string file, bool append = false)
{
FileName = file;
using (
StreamWriter writer =
new StreamWriter(
new FileStream(FileName, append ? FileMode.Append : FileMode.Create, FileAccess.Write,
FileShare.Read)))
{
writer.WriteLine(">>>Logging started on {0}.", DateTime.UtcNow.ToString("f"));
//f = Tuesday, April 10, 2001 3:51 PM
}
_NewLine = true;
}
public string FileName{ get; }
public override Encoding Encoding => Encoding.Default;
public override void Write(char ch)
{
using (StreamWriter writer =
new StreamWriter(new FileStream(FileName, FileMode.Append, FileAccess.Write, FileShare.Read)))
{
if (_NewLine)
{
writer.Write(DateTime.UtcNow.ToString(DateFormat));
_NewLine = false;
}
writer.Write(ch);
}
}
public override void Write(string str)
{
using (StreamWriter writer =
new StreamWriter(new FileStream(FileName, FileMode.Append, FileAccess.Write, FileShare.Read)))
{
if (_NewLine)
{
writer.Write(DateTime.UtcNow.ToString(DateFormat));
_NewLine = false;
}
writer.Write(str);
}
}
public override void WriteLine(string line)
{
using (StreamWriter writer =
new StreamWriter(new FileStream(FileName, FileMode.Append, FileAccess.Write, FileShare.Read)))
{
if (_NewLine) writer.Write(DateTime.UtcNow.ToString(DateFormat));
writer.WriteLine(line);
_NewLine = true;
}
}
}
public class MultiTextWriter : TextWriter
{
private readonly List<TextWriter> _Streams;
public MultiTextWriter(params TextWriter[] streams)
{
_Streams = new List<TextWriter>(streams);
if (_Streams.Count < 0) throw new ArgumentException("You must specify at least one stream.");
}
public override Encoding Encoding => Encoding.Default;
public void Add(TextWriter tw)
{
_Streams.Add(tw);
}
public void Remove(TextWriter tw)
{
_Streams.Remove(tw);
}
public override void Write(char ch)
{
foreach (TextWriter t in _Streams) t.Write(ch);
}
public override void WriteLine(string line)
{
foreach (TextWriter t in _Streams) t.WriteLine(line);
}
public override void WriteLine(string line, params object[] args)
{
WriteLine(string.Format(line, args));
}
}
}

1586
Projects/Server/Map.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
/***************************************************************************
* IMenu.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Menus
{
public interface IMenu
{
int Serial{ get; }
int EntryLength{ get; }
void SendTo(NetState state);
void OnCancel(NetState state);
void OnResponse(NetState state, int index);
}
}

View file

@ -0,0 +1,82 @@
/***************************************************************************
* ItemListMenu.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Menus.ItemLists
{
public class ItemListEntry
{
public ItemListEntry(string name, int itemID, int hue = 0)
{
Name = name;
ItemID = itemID;
Hue = hue;
}
public string Name{ get; }
public int ItemID{ get; }
public int Hue{ get; }
}
public class ItemListMenu : IMenu
{
private static int m_NextSerial;
private int m_Serial;
public ItemListMenu(string question, ItemListEntry[] entries)
{
Question = question;
Entries = entries;
do
{
m_Serial = m_NextSerial++;
m_Serial &= 0x7FFFFFFF;
} while (m_Serial == 0);
m_Serial = (int)((uint)m_Serial | 0x80000000);
}
public string Question{ get; }
public ItemListEntry[] Entries{ get; set; }
int IMenu.Serial => m_Serial;
int IMenu.EntryLength => Entries.Length;
public virtual void OnCancel(NetState state)
{
}
public virtual void OnResponse(NetState state, int index)
{
}
public void SendTo(NetState state)
{
state.AddMenu(this);
state.Send(new DisplayItemListMenu(this));
}
}
}

View file

@ -0,0 +1,64 @@
/***************************************************************************
* QuestionMenu.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Network;
namespace Server.Menus.Questions
{
public class QuestionMenu : IMenu
{
private static int m_NextSerial;
private int m_Serial;
public QuestionMenu(string question, string[] answers)
{
Question = question;
Answers = answers;
do
{
m_Serial = ++m_NextSerial;
m_Serial &= 0x7FFFFFFF;
} while (m_Serial == 0);
}
public string Question{ get; set; }
public string[] Answers{ get; }
int IMenu.Serial => m_Serial;
int IMenu.EntryLength => Answers.Length;
public virtual void OnCancel(NetState state)
{
}
public virtual void OnResponse(NetState state, int index)
{
}
public void SendTo(NetState state)
{
state.AddMenu(this);
state.Send(new DisplayQuestionMenu(this));
}
}
}

9902
Projects/Server/Mobile.cs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,86 @@
/***************************************************************************
* Movement.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server.Movement
{
public static class Movement
{
public static IMovementImpl Impl{ get; set; }
public static bool CheckMovement(Mobile m, Direction d, out int newZ)
{
if (Impl != null)
return Impl.CheckMovement(m, d, out newZ);
newZ = m.Z;
return false;
}
public static bool CheckMovement(Mobile m, Map map, Point3D loc, Direction d, out int newZ)
{
if (Impl != null)
return Impl.CheckMovement(m, map, loc, d, out newZ);
newZ = m.Z;
return false;
}
public static void Offset(Direction d, ref int x, ref int y)
{
switch (d & Direction.Mask)
{
case Direction.North:
--y;
break;
case Direction.South:
++y;
break;
case Direction.West:
--x;
break;
case Direction.East:
++x;
break;
case Direction.Right:
++x;
--y;
break;
case Direction.Left:
--x;
++y;
break;
case Direction.Down:
++x;
++y;
break;
case Direction.Up:
--x;
--y;
break;
}
}
}
public interface IMovementImpl
{
bool CheckMovement(Mobile m, Direction d, out int newZ);
bool CheckMovement(Mobile m, Map map, Point3D loc, Direction d, out int newZ);
}
}

View file

@ -0,0 +1,583 @@
/***************************************************************************
* MultiData.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.IO;
namespace Server
{
public static class MultiData
{
private static MultiComponentList[] m_Components;
private static FileStream m_Index, m_Stream;
private static BinaryReader m_IndexReader, m_StreamReader;
static MultiData()
{
string idxPath = Core.FindDataFile("multi.idx");
string mulPath = Core.FindDataFile("multi.mul");
if (File.Exists(idxPath) && File.Exists(mulPath))
{
m_Index = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read);
m_IndexReader = new BinaryReader(m_Index);
m_Stream = new FileStream(mulPath, FileMode.Open, FileAccess.Read, FileShare.Read);
m_StreamReader = new BinaryReader(m_Stream);
m_Components = new MultiComponentList[(int)(m_Index.Length / 12)];
string vdPath = Core.FindDataFile("verdata.mul");
if (File.Exists(vdPath))
using (FileStream fs = new FileStream(vdPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
BinaryReader bin = new BinaryReader(fs);
int count = bin.ReadInt32();
for (int i = 0; i < count; ++i)
{
int file = bin.ReadInt32();
int index = bin.ReadInt32();
int lookup = bin.ReadInt32();
int length = bin.ReadInt32();
int extra = bin.ReadInt32();
if (file == 14 && index >= 0 && index < m_Components.Length && lookup >= 0 && length > 0)
{
bin.BaseStream.Seek(lookup, SeekOrigin.Begin);
m_Components[index] = new MultiComponentList(bin, length / 12);
bin.BaseStream.Seek(24 + i * 20, SeekOrigin.Begin);
}
}
bin.Close();
}
}
else
{
Console.WriteLine("Warning: Multi data files not found");
m_Components = new MultiComponentList[0];
}
}
public static MultiComponentList GetComponents(int multiID)
{
MultiComponentList mcl;
if (multiID >= 0 && multiID < m_Components.Length)
{
mcl = m_Components[multiID];
if (mcl == null)
m_Components[multiID] = mcl = Load(multiID);
}
else
{
mcl = MultiComponentList.Empty;
}
return mcl;
}
public static MultiComponentList Load(int multiID)
{
try
{
m_IndexReader.BaseStream.Seek(multiID * 12, SeekOrigin.Begin);
int lookup = m_IndexReader.ReadInt32();
int length = m_IndexReader.ReadInt32();
if (lookup < 0 || length <= 0)
return MultiComponentList.Empty;
m_StreamReader.BaseStream.Seek(lookup, SeekOrigin.Begin);
return new MultiComponentList(m_StreamReader, length / (MultiComponentList.PostHSFormat ? 16 : 12));
}
catch
{
return MultiComponentList.Empty;
}
}
}
public struct MultiTileEntry
{
public ushort m_ItemID;
public short m_OffsetX, m_OffsetY, m_OffsetZ;
public int m_Flags;
public MultiTileEntry(ushort itemID, short xOffset, short yOffset, short zOffset, int flags)
{
m_ItemID = itemID;
m_OffsetX = xOffset;
m_OffsetY = yOffset;
m_OffsetZ = zOffset;
m_Flags = flags;
}
}
public sealed class MultiComponentList
{
public static readonly MultiComponentList Empty = new MultiComponentList();
private Point2D m_Min, m_Max;
public MultiComponentList(MultiComponentList toCopy)
{
m_Min = toCopy.m_Min;
m_Max = toCopy.m_Max;
Center = toCopy.Center;
Width = toCopy.Width;
Height = toCopy.Height;
Tiles = new StaticTile[Width][][];
for (int x = 0; x < Width; ++x)
{
Tiles[x] = new StaticTile[Height][];
for (int y = 0; y < Height; ++y)
{
Tiles[x][y] = new StaticTile[toCopy.Tiles[x][y].Length];
for (int i = 0; i < Tiles[x][y].Length; ++i)
Tiles[x][y][i] = toCopy.Tiles[x][y][i];
}
}
List = new MultiTileEntry[toCopy.List.Length];
for (int i = 0; i < List.Length; ++i)
List[i] = toCopy.List[i];
}
public MultiComponentList(GenericReader reader)
{
int version = reader.ReadInt();
m_Min = reader.ReadPoint2D();
m_Max = reader.ReadPoint2D();
Center = reader.ReadPoint2D();
Width = reader.ReadInt();
Height = reader.ReadInt();
int length = reader.ReadInt();
MultiTileEntry[] allTiles = List = new MultiTileEntry[length];
if (version == 0)
for (int i = 0; i < length; ++i)
{
int id = reader.ReadShort();
if (id >= 0x4000)
id -= 0x4000;
allTiles[i].m_ItemID = (ushort)id;
allTiles[i].m_OffsetX = reader.ReadShort();
allTiles[i].m_OffsetY = reader.ReadShort();
allTiles[i].m_OffsetZ = reader.ReadShort();
allTiles[i].m_Flags = reader.ReadInt();
}
else
for (int i = 0; i < length; ++i)
{
allTiles[i].m_ItemID = reader.ReadUShort();
allTiles[i].m_OffsetX = reader.ReadShort();
allTiles[i].m_OffsetY = reader.ReadShort();
allTiles[i].m_OffsetZ = reader.ReadShort();
allTiles[i].m_Flags = reader.ReadInt();
}
TileList[][] tiles = new TileList[Width][];
Tiles = new StaticTile[Width][][];
for (int x = 0; x < Width; ++x)
{
tiles[x] = new TileList[Height];
Tiles[x] = new StaticTile[Height][];
for (int y = 0; y < Height; ++y)
tiles[x][y] = new TileList();
}
for (int i = 0; i < allTiles.Length; ++i)
if (i == 0 || allTiles[i].m_Flags != 0)
{
int xOffset = allTiles[i].m_OffsetX + Center.m_X;
int yOffset = allTiles[i].m_OffsetY + Center.m_Y;
tiles[xOffset][yOffset].Add(allTiles[i].m_ItemID, (sbyte)allTiles[i].m_OffsetZ);
}
for (int x = 0; x < Width; ++x)
for (int y = 0; y < Height; ++y)
Tiles[x][y] = tiles[x][y].ToArray();
}
public MultiComponentList(BinaryReader reader, int count)
{
MultiTileEntry[] allTiles = List = new MultiTileEntry[count];
for (int i = 0; i < count; ++i)
{
allTiles[i].m_ItemID = reader.ReadUInt16();
allTiles[i].m_OffsetX = reader.ReadInt16();
allTiles[i].m_OffsetY = reader.ReadInt16();
allTiles[i].m_OffsetZ = reader.ReadInt16();
allTiles[i].m_Flags = reader.ReadInt32();
if (PostHSFormat)
reader.ReadInt32(); // ??
MultiTileEntry e = allTiles[i];
if (i == 0 || e.m_Flags != 0)
{
if (e.m_OffsetX < m_Min.m_X)
m_Min.m_X = e.m_OffsetX;
if (e.m_OffsetY < m_Min.m_Y)
m_Min.m_Y = e.m_OffsetY;
if (e.m_OffsetX > m_Max.m_X)
m_Max.m_X = e.m_OffsetX;
if (e.m_OffsetY > m_Max.m_Y)
m_Max.m_Y = e.m_OffsetY;
}
}
Center = new Point2D(-m_Min.m_X, -m_Min.m_Y);
Width = m_Max.m_X - m_Min.m_X + 1;
Height = m_Max.m_Y - m_Min.m_Y + 1;
TileList[][] tiles = new TileList[Width][];
Tiles = new StaticTile[Width][][];
for (int x = 0; x < Width; ++x)
{
tiles[x] = new TileList[Height];
Tiles[x] = new StaticTile[Height][];
for (int y = 0; y < Height; ++y)
tiles[x][y] = new TileList();
}
for (int i = 0; i < allTiles.Length; ++i)
if (i == 0 || allTiles[i].m_Flags != 0)
{
int xOffset = allTiles[i].m_OffsetX + Center.m_X;
int yOffset = allTiles[i].m_OffsetY + Center.m_Y;
tiles[xOffset][yOffset].Add(allTiles[i].m_ItemID, (sbyte)allTiles[i].m_OffsetZ);
}
for (int x = 0; x < Width; ++x)
for (int y = 0; y < Height; ++y)
Tiles[x][y] = tiles[x][y].ToArray();
}
private MultiComponentList()
{
Tiles = new StaticTile[0][][];
List = new MultiTileEntry[0];
}
public static bool PostHSFormat{ get; set; }
public Point2D Min => m_Min;
public Point2D Max => m_Max;
public Point2D Center{ get; }
public int Width{ get; private set; }
public int Height{ get; private set; }
public StaticTile[][][] Tiles{ get; private set; }
public MultiTileEntry[] List{ get; private set; }
public void Add(int itemID, int x, int y, int z)
{
int vx = x + Center.m_X;
int vy = y + Center.m_Y;
if (vx >= 0 && vx < Width && vy >= 0 && vy < Height)
{
StaticTile[] oldTiles = Tiles[vx][vy];
for (int i = oldTiles.Length - 1; i >= 0; --i)
{
ItemData data = TileData.ItemTable[itemID & TileData.MaxItemValue];
if (oldTiles[i].Z == z && oldTiles[i].Height > 0 == data.Height > 0)
{
bool newIsRoof = (data.Flags & TileFlag.Roof) != 0;
bool oldIsRoof =
(TileData.ItemTable[oldTiles[i].ID & TileData.MaxItemValue].Flags & TileFlag.Roof) != 0;
if (newIsRoof == oldIsRoof)
Remove(oldTiles[i].ID, x, y, z);
}
}
oldTiles = Tiles[vx][vy];
StaticTile[] newTiles = new StaticTile[oldTiles.Length + 1];
for (int i = 0; i < oldTiles.Length; ++i)
newTiles[i] = oldTiles[i];
newTiles[oldTiles.Length] = new StaticTile((ushort)itemID, (sbyte)z);
Tiles[vx][vy] = newTiles;
MultiTileEntry[] oldList = List;
MultiTileEntry[] newList = new MultiTileEntry[oldList.Length + 1];
for (int i = 0; i < oldList.Length; ++i)
newList[i] = oldList[i];
newList[oldList.Length] = new MultiTileEntry((ushort)itemID, (short)x, (short)y, (short)z, 1);
List = newList;
if (x < m_Min.m_X)
m_Min.m_X = x;
if (y < m_Min.m_Y)
m_Min.m_Y = y;
if (x > m_Max.m_X)
m_Max.m_X = x;
if (y > m_Max.m_Y)
m_Max.m_Y = y;
}
}
public void RemoveXYZH(int x, int y, int z, int minHeight)
{
int vx = x + Center.m_X;
int vy = y + Center.m_Y;
if (vx >= 0 && vx < Width && vy >= 0 && vy < Height)
{
StaticTile[] oldTiles = Tiles[vx][vy];
for (int i = 0; i < oldTiles.Length; ++i)
{
StaticTile tile = oldTiles[i];
if (tile.Z == z && tile.Height >= minHeight)
{
StaticTile[] newTiles = new StaticTile[oldTiles.Length - 1];
for (int j = 0; j < i; ++j)
newTiles[j] = oldTiles[j];
for (int j = i + 1; j < oldTiles.Length; ++j)
newTiles[j - 1] = oldTiles[j];
Tiles[vx][vy] = newTiles;
break;
}
}
MultiTileEntry[] oldList = List;
for (int i = 0; i < oldList.Length; ++i)
{
MultiTileEntry tile = oldList[i];
if (tile.m_OffsetX == (short)x && tile.m_OffsetY == (short)y && tile.m_OffsetZ == (short)z &&
TileData.ItemTable[tile.m_ItemID & TileData.MaxItemValue].Height >= minHeight)
{
MultiTileEntry[] newList = new MultiTileEntry[oldList.Length - 1];
for (int j = 0; j < i; ++j)
newList[j] = oldList[j];
for (int j = i + 1; j < oldList.Length; ++j)
newList[j - 1] = oldList[j];
List = newList;
break;
}
}
}
}
public void Remove(int itemID, int x, int y, int z)
{
int vx = x + Center.m_X;
int vy = y + Center.m_Y;
if (vx >= 0 && vx < Width && vy >= 0 && vy < Height)
{
StaticTile[] oldTiles = Tiles[vx][vy];
for (int i = 0; i < oldTiles.Length; ++i)
{
StaticTile tile = oldTiles[i];
if (tile.ID == itemID && tile.Z == z)
{
StaticTile[] newTiles = new StaticTile[oldTiles.Length - 1];
for (int j = 0; j < i; ++j)
newTiles[j] = oldTiles[j];
for (int j = i + 1; j < oldTiles.Length; ++j)
newTiles[j - 1] = oldTiles[j];
Tiles[vx][vy] = newTiles;
break;
}
}
MultiTileEntry[] oldList = List;
for (int i = 0; i < oldList.Length; ++i)
{
MultiTileEntry tile = oldList[i];
if (tile.m_ItemID == itemID && tile.m_OffsetX == (short)x && tile.m_OffsetY == (short)y &&
tile.m_OffsetZ == (short)z)
{
MultiTileEntry[] newList = new MultiTileEntry[oldList.Length - 1];
for (int j = 0; j < i; ++j)
newList[j] = oldList[j];
for (int j = i + 1; j < oldList.Length; ++j)
newList[j - 1] = oldList[j];
List = newList;
break;
}
}
}
}
public void Resize(int newWidth, int newHeight)
{
int oldWidth = Width, oldHeight = Height;
StaticTile[][][] oldTiles = Tiles;
int totalLength = 0;
StaticTile[][][] newTiles = new StaticTile[newWidth][][];
for (int x = 0; x < newWidth; ++x)
{
newTiles[x] = new StaticTile[newHeight][];
for (int y = 0; y < newHeight; ++y)
{
if (x < oldWidth && y < oldHeight)
newTiles[x][y] = oldTiles[x][y];
else
newTiles[x][y] = new StaticTile[0];
totalLength += newTiles[x][y].Length;
}
}
Tiles = newTiles;
List = new MultiTileEntry[totalLength];
Width = newWidth;
Height = newHeight;
m_Min = Point2D.Zero;
m_Max = Point2D.Zero;
int index = 0;
for (int x = 0; x < newWidth; ++x)
for (int y = 0; y < newHeight; ++y)
{
StaticTile[] tiles = newTiles[x][y];
for (int i = 0; i < tiles.Length; ++i)
{
StaticTile tile = tiles[i];
int vx = x - Center.X;
int vy = y - Center.Y;
if (vx < m_Min.m_X)
m_Min.m_X = vx;
if (vy < m_Min.m_Y)
m_Min.m_Y = vy;
if (vx > m_Max.m_X)
m_Max.m_X = vx;
if (vy > m_Max.m_Y)
m_Max.m_Y = vy;
List[index++] = new MultiTileEntry((ushort)tile.ID, (short)vx, (short)vy, (short)tile.Z, 1);
}
}
}
public void Serialize(GenericWriter writer)
{
writer.Write(1); // version;
writer.Write(m_Min);
writer.Write(m_Max);
writer.Write(Center);
writer.Write(Width);
writer.Write(Height);
writer.Write(List.Length);
for (int i = 0; i < List.Length; ++i)
{
MultiTileEntry ent = List[i];
writer.Write(ent.m_ItemID);
writer.Write(ent.m_OffsetX);
writer.Write(ent.m_OffsetY);
writer.Write(ent.m_OffsetZ);
writer.Write(ent.m_Flags);
}
}
}
}

View file

@ -0,0 +1,82 @@
/***************************************************************************
* NativeReader.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace Server
{
public static class NativeReader
{
private static readonly INativeReader m_NativeReader;
static NativeReader()
{
if (Core.Unix)
m_NativeReader = new NativeReaderUnix();
else
m_NativeReader = new NativeReaderWin32();
}
public static unsafe void Read(IntPtr ptr, void* buffer, int length)
{
m_NativeReader.Read(ptr, buffer, length);
}
}
public interface INativeReader
{
unsafe void Read(IntPtr ptr, void* buffer, int length);
}
public sealed class NativeReaderWin32 : INativeReader
{
public unsafe void Read(IntPtr ptr, void* buffer, int length)
{
uint lpNumberOfBytesRead = 0;
UnsafeNativeMethods.ReadFile(ptr, buffer, (uint)length, ref lpNumberOfBytesRead, null);
}
internal static class UnsafeNativeMethods
{
/*[DllImport("kernel32")]
internal unsafe static extern int _lread(IntPtr hFile, void* lpBuffer, int wBytes);*/
[DllImport("kernel32")]
internal static extern unsafe bool ReadFile(IntPtr hFile, void* lpBuffer, uint nNumberOfBytesToRead,
ref uint lpNumberOfBytesRead, NativeOverlapped* lpOverlapped);
}
}
public sealed class NativeReaderUnix : INativeReader
{
public unsafe void Read(IntPtr ptr, void* buffer, int length)
{
UnsafeNativeMethods.read(ptr, buffer, length);
}
internal static class UnsafeNativeMethods
{
[DllImport("libc")]
internal static extern unsafe int read(IntPtr ptr, void* buffer, int length);
}
}
}

View file

@ -0,0 +1,398 @@
/***************************************************************************
* Compression.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Runtime.InteropServices;
namespace Server.Network
{
/// <summary>
/// Handles outgoing packet compression for the network.
/// </summary>
public static class Compression
{
private const int CountIndex = 0;
private const int ValueIndex = 1;
// UO packets may not exceed 64kb in length
private const int BufferSize = 0x10000;
// Optimal compression ratio is 2 / 8; worst compression ratio is 11 / 8
private const int MinimalCodeLength = 2;
private const int MaximalCodeLength = 11;
// Fixed overhead, in bits, per compression call
private const int TerminalCodeLength = 4;
// If our input exceeds this length, we cannot possibly compress it within the buffer
private const int DefiniteOverflow = (BufferSize * 8 - TerminalCodeLength) / MinimalCodeLength;
// If our input exceeds this length, we may potentially overflow the buffer
private const int PossibleOverflow = (BufferSize * 8 - TerminalCodeLength) / MaximalCodeLength;
private static int[] _huffmanTable = {
0x2, 0x000, 0x5, 0x01F, 0x6, 0x022, 0x7, 0x034, 0x7, 0x075, 0x6, 0x028, 0x6, 0x03B, 0x7, 0x032,
0x8, 0x0E0, 0x8, 0x062, 0x7, 0x056, 0x8, 0x079, 0x9, 0x19D, 0x8, 0x097, 0x6, 0x02A, 0x7, 0x057,
0x8, 0x071, 0x8, 0x05B, 0x9, 0x1CC, 0x8, 0x0A7, 0x7, 0x025, 0x7, 0x04F, 0x8, 0x066, 0x8, 0x07D,
0x9, 0x191, 0x9, 0x1CE, 0x7, 0x03F, 0x9, 0x090, 0x8, 0x059, 0x8, 0x07B, 0x8, 0x091, 0x8, 0x0C6,
0x6, 0x02D, 0x9, 0x186, 0x8, 0x06F, 0x9, 0x093, 0xA, 0x1CC, 0x8, 0x05A, 0xA, 0x1AE, 0xA, 0x1C0,
0x9, 0x148, 0x9, 0x14A, 0x9, 0x082, 0xA, 0x19F, 0x9, 0x171, 0x9, 0x120, 0x9, 0x0E7, 0xA, 0x1F3,
0x9, 0x14B, 0x9, 0x100, 0x9, 0x190, 0x6, 0x013, 0x9, 0x161, 0x9, 0x125, 0x9, 0x133, 0x9, 0x195,
0x9, 0x173, 0x9, 0x1CA, 0x9, 0x086, 0x9, 0x1E9, 0x9, 0x0DB, 0x9, 0x1EC, 0x9, 0x08B, 0x9, 0x085,
0x5, 0x00A, 0x8, 0x096, 0x8, 0x09C, 0x9, 0x1C3, 0x9, 0x19C, 0x9, 0x08F, 0x9, 0x18F, 0x9, 0x091,
0x9, 0x087, 0x9, 0x0C6, 0x9, 0x177, 0x9, 0x089, 0x9, 0x0D6, 0x9, 0x08C, 0x9, 0x1EE, 0x9, 0x1EB,
0x9, 0x084, 0x9, 0x164, 0x9, 0x175, 0x9, 0x1CD, 0x8, 0x05E, 0x9, 0x088, 0x9, 0x12B, 0x9, 0x172,
0x9, 0x10A, 0x9, 0x08D, 0x9, 0x13A, 0x9, 0x11C, 0xA, 0x1E1, 0xA, 0x1E0, 0x9, 0x187, 0xA, 0x1DC,
0xA, 0x1DF, 0x7, 0x074, 0x9, 0x19F, 0x8, 0x08D, 0x8, 0x0E4, 0x7, 0x079, 0x9, 0x0EA, 0x9, 0x0E1,
0x8, 0x040, 0x7, 0x041, 0x9, 0x10B, 0x9, 0x0B0, 0x8, 0x06A, 0x8, 0x0C1, 0x7, 0x071, 0x7, 0x078,
0x8, 0x0B1, 0x9, 0x14C, 0x7, 0x043, 0x8, 0x076, 0x7, 0x066, 0x7, 0x04D, 0x9, 0x08A, 0x6, 0x02F,
0x8, 0x0C9, 0x9, 0x0CE, 0x9, 0x149, 0x9, 0x160, 0xA, 0x1BA, 0xA, 0x19E, 0xA, 0x39F, 0x9, 0x0E5,
0x9, 0x194, 0x9, 0x184, 0x9, 0x126, 0x7, 0x030, 0x8, 0x06C, 0x9, 0x121, 0x9, 0x1E8, 0xA, 0x1C1,
0xA, 0x11D, 0xA, 0x163, 0xA, 0x385, 0xA, 0x3DB, 0xA, 0x17D, 0xA, 0x106, 0xA, 0x397, 0xA, 0x24E,
0x7, 0x02E, 0x8, 0x098, 0xA, 0x33C, 0xA, 0x32E, 0xA, 0x1E9, 0x9, 0x0BF, 0xA, 0x3DF, 0xA, 0x1DD,
0xA, 0x32D, 0xA, 0x2ED, 0xA, 0x30B, 0xA, 0x107, 0xA, 0x2E8, 0xA, 0x3DE, 0xA, 0x125, 0xA, 0x1E8,
0x9, 0x0E9, 0xA, 0x1CD, 0xA, 0x1B5, 0x9, 0x165, 0xA, 0x232, 0xA, 0x2E1, 0xB, 0x3AE, 0xB, 0x3C6,
0xB, 0x3E2, 0xA, 0x205, 0xA, 0x29A, 0xA, 0x248, 0xA, 0x2CD, 0xA, 0x23B, 0xB, 0x3C5, 0xA, 0x251,
0xA, 0x2E9, 0xA, 0x252, 0x9, 0x1EA, 0xB, 0x3A0, 0xB, 0x391, 0xA, 0x23C, 0xB, 0x392, 0xB, 0x3D5,
0xA, 0x233, 0xA, 0x2CC, 0xB, 0x390, 0xA, 0x1BB, 0xB, 0x3A1, 0xB, 0x3C4, 0xA, 0x211, 0xA, 0x203,
0x9, 0x12A, 0xA, 0x231, 0xB, 0x3E0, 0xA, 0x29B, 0xB, 0x3D7, 0xA, 0x202, 0xB, 0x3AD, 0xA, 0x213,
0xA, 0x253, 0xA, 0x32C, 0xA, 0x23D, 0xA, 0x23F, 0xA, 0x32F, 0xA, 0x11C, 0xA, 0x384, 0xA, 0x31C,
0xA, 0x17C, 0xA, 0x30A, 0xA, 0x2E0, 0xA, 0x276, 0xA, 0x250, 0xB, 0x3E3, 0xA, 0x396, 0xA, 0x18F,
0xA, 0x204, 0xA, 0x206, 0xA, 0x230, 0xA, 0x265, 0xA, 0x212, 0xA, 0x23E, 0xB, 0x3AC, 0xB, 0x393,
0xB, 0x3E1, 0xA, 0x1DE, 0xB, 0x3D6, 0xA, 0x31D, 0xB, 0x3E5, 0xB, 0x3E4, 0xA, 0x207, 0xB, 0x3C7,
0xA, 0x277, 0xB, 0x3D4, 0x8, 0x0C0, 0xA, 0x162, 0xA, 0x3DA, 0xA, 0x124, 0xA, 0x1B4, 0xA, 0x264,
0xA, 0x33D, 0xA, 0x1D1, 0xA, 0x1AF, 0xA, 0x39E, 0xA, 0x24F, 0xB, 0x373, 0xA, 0x249, 0xB, 0x372,
0x9, 0x167, 0xA, 0x210, 0xA, 0x23A, 0xA, 0x1B8, 0xB, 0x3AF, 0xA, 0x18E, 0xA, 0x2EC, 0x7, 0x062,
0x4, 0x00D
};
public static readonly ICompressor Compressor;
static Compression()
{
if (Core.Unix)
{
if (Core.Is64Bit)
Compressor = new CompressorUnix64();
else
Compressor = new CompressorUnix32();
}
else if (Core.Is64Bit)
{
Compressor = new Compressor64();
}
else
{
Compressor = new Compressor32();
}
}
public static unsafe void Compress(byte[] input, int offset, int count, byte[] output, ref int length)
{
if (input == null) throw new ArgumentNullException(nameof(input));
if (offset < 0 || offset >= input.Length) throw new ArgumentOutOfRangeException(nameof(offset));
if (count < 0 || count > input.Length) throw new ArgumentOutOfRangeException(nameof(count));
if (input.Length - offset < count) throw new ArgumentException();
length = 0;
if (count > DefiniteOverflow) return;
int bitCount = 0;
int bitValue = 0;
fixed (int* pTable = _huffmanTable)
{
int* pEntry;
fixed (byte* pInputBuffer = input)
{
byte* pInput = pInputBuffer + offset, pInputEnd = pInput + count;
fixed (byte* pOutputBuffer = output)
{
byte* pOutput = pOutputBuffer, pOutputEnd = pOutput + BufferSize;
while (pInput < pInputEnd)
{
pEntry = &pTable[*pInput++ << 1];
bitCount += pEntry[CountIndex];
bitValue <<= pEntry[CountIndex];
bitValue |= pEntry[ValueIndex];
while (bitCount >= 8)
{
bitCount -= 8;
if (pOutput < pOutputEnd)
{
*pOutput++ = (byte)(bitValue >> bitCount);
}
else
{
length = 0;
return;
}
}
}
// terminal code
pEntry = &pTable[0x200];
bitCount += pEntry[CountIndex];
bitValue <<= pEntry[CountIndex];
bitValue |= pEntry[ValueIndex];
// align on byte boundary
if ((bitCount & 7) != 0)
{
bitValue <<= 8 - (bitCount & 7);
bitCount += 8 - (bitCount & 7);
}
while (bitCount >= 8)
{
bitCount -= 8;
if (pOutput < pOutputEnd)
{
*pOutput++ = (byte)(bitValue >> bitCount);
}
else
{
length = 0;
return;
}
}
length = (int)(pOutput - pOutputBuffer);
}
}
}
}
public static ZLibError Pack(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
return Compressor.Compress(dest, ref destLength, source, sourceLength);
}
public static ZLibError Pack(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
{
return Compressor.Compress(dest, ref destLength, source, sourceLength, quality);
}
public static ZLibError Unpack(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
return Compressor.Decompress(dest, ref destLength, source, sourceLength);
}
}
public interface ICompressor
{
string Version{ get; }
ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality);
ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
}
public sealed class Compressor32 : ICompressor
{
public string Version => SafeNativeMethods.zlibVersion();
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
return SafeNativeMethods.compress(dest, ref destLength, source, sourceLength);
}
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
{
return SafeNativeMethods.compress2(dest, ref destLength, source, sourceLength, quality);
}
public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength);
}
internal class SafeNativeMethods
{
[DllImport("zlib32")]
internal static extern string zlibVersion();
[DllImport("zlib32")]
internal static extern ZLibError compress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
[DllImport("zlib32")]
internal static extern ZLibError compress2(byte[] dest, ref int destLength, byte[] source, int sourceLength,
ZLibQuality quality);
[DllImport("zlib32")]
internal static extern ZLibError uncompress(byte[] dest, ref int destLen, byte[] source, int sourceLen);
}
}
public sealed class Compressor64 : ICompressor
{
public string Version => SafeNativeMethods.zlibVersion();
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
return SafeNativeMethods.compress(dest, ref destLength, source, sourceLength);
}
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
{
return SafeNativeMethods.compress2(dest, ref destLength, source, sourceLength, quality);
}
public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength);
}
internal class SafeNativeMethods
{
[DllImport("zlib64")]
internal static extern string zlibVersion();
[DllImport("zlib64")]
internal static extern ZLibError compress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
[DllImport("zlib64")]
internal static extern ZLibError compress2(byte[] dest, ref int destLength, byte[] source, int sourceLength,
ZLibQuality quality);
[DllImport("zlib64")]
internal static extern ZLibError uncompress(byte[] dest, ref int destLen, byte[] source, int sourceLen);
}
}
public sealed class CompressorUnix32 : ICompressor
{
public string Version => SafeNativeMethods.zlibVersion();
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
return SafeNativeMethods.compress(dest, ref destLength, source, sourceLength);
}
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
{
return SafeNativeMethods.compress2(dest, ref destLength, source, sourceLength, quality);
}
public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
return SafeNativeMethods.uncompress(dest, ref destLength, source, sourceLength);
}
internal class SafeNativeMethods
{
[DllImport("libz")]
internal static extern string zlibVersion();
[DllImport("libz")]
internal static extern ZLibError compress(byte[] dest, ref int destLength, byte[] source, int sourceLength);
[DllImport("libz")]
internal static extern ZLibError compress2(byte[] dest, ref int destLength, byte[] source, int sourceLength,
ZLibQuality quality);
[DllImport("libz")]
internal static extern ZLibError uncompress(byte[] dest, ref int destLen, byte[] source, int sourceLen);
}
}
public sealed class CompressorUnix64 : ICompressor
{
public string Version => SafeNativeMethods.zlibVersion();
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
ulong destLengthLong = (ulong)destLength;
ZLibError z = SafeNativeMethods.compress(dest, ref destLengthLong, source, sourceLength);
destLength = (int)destLengthLong;
return z;
}
public ZLibError Compress(byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality)
{
ulong destLengthLong = (ulong)destLength;
ZLibError z = SafeNativeMethods.compress2(dest, ref destLengthLong, source, sourceLength, quality);
destLength = (int)destLengthLong;
return z;
}
public ZLibError Decompress(byte[] dest, ref int destLength, byte[] source, int sourceLength)
{
ulong destLengthLong = (ulong)destLength;
ZLibError z = SafeNativeMethods.uncompress(dest, ref destLengthLong, source, sourceLength);
destLength = (int)destLengthLong;
return z;
}
internal class SafeNativeMethods
{
[DllImport("libz")]
internal static extern string zlibVersion();
[DllImport("libz")]
internal static extern ZLibError compress(byte[] dest, ref ulong destLength, byte[] source, int sourceLength);
[DllImport("libz")]
internal static extern ZLibError compress2(byte[] dest, ref ulong destLength, byte[] source, int sourceLength,
ZLibQuality quality);
[DllImport("libz")]
internal static extern ZLibError uncompress(byte[] dest, ref ulong destLen, byte[] source, int sourceLen);
}
}
public enum ZLibError
{
VersionError = -6,
BufferError = -5,
MemoryError = -4,
DataError = -3,
StreamError = -2,
FileError = -1,
Okay = 0,
StreamEnd = 1,
NeedDictionary = 2
}
public enum ZLibQuality
{
Default = -1,
None = 0,
Speed = 1,
Size = 9
}
}

View file

@ -0,0 +1,40 @@
/***************************************************************************
* EncodedPacketHandler.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server.Network
{
public delegate void OnEncodedPacketReceive(NetState state, IEntity ent, EncodedReader pvSrc);
public class EncodedPacketHandler
{
public EncodedPacketHandler(int packetID, bool ingame, OnEncodedPacketReceive onReceive)
{
PacketID = packetID;
Ingame = ingame;
OnReceive = onReceive;
}
public int PacketID{ get; }
public OnEncodedPacketReceive OnReceive{ get; }
public bool Ingame{ get; }
}
}

View file

@ -0,0 +1,48 @@
/***************************************************************************
* EncodedReader.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server.Network
{
public ref struct EncodedReader
{
private PacketReader m_Reader;
public EncodedReader(PacketReader reader)
{
m_Reader = reader;
}
public void Trace(NetState state)
{
m_Reader.Trace(state);
}
public int ReadInt32() => m_Reader.ReadByte() != 0 ? 0 : m_Reader.ReadInt32();
public Point3D ReadPoint3D() => m_Reader.ReadByte() != 3 ? Point3D.Zero :
new Point3D(m_Reader.ReadInt16(), m_Reader.ReadInt16(), m_Reader.ReadByte());
public string ReadUnicodeStringSafe() => m_Reader.ReadByte() != 2 ? string.Empty :
m_Reader.ReadUnicodeStringSafe(m_Reader.ReadUInt16());
public string ReadUnicodeString() => m_Reader.ReadByte() != 2 ? string.Empty :
m_Reader.ReadUnicodeString(m_Reader.ReadUInt16());
}
}

View file

@ -0,0 +1,175 @@
/***************************************************************************
* Listener.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace Server.Network
{
public class Listener
{
private Socket m_Socket;
private IPEndPoint m_EndPoint;
public Listener(IPEndPoint ipep)
{
#pragma warning disable IDE0068 // Use recommended dispose pattern
m_Socket = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
#pragma warning restore IDE0068 // Use recommended dispose pattern
m_Socket.LingerState.Enabled = false;
m_Socket.ExclusiveAddressUse = false;
m_EndPoint = ipep;
}
public virtual async Task Start(MessagePump pump)
{
try
{
m_Socket.Bind(m_EndPoint);
m_Socket.Listen(8);
}
catch (Exception e)
{
if (e is SocketException se)
{
if (se.ErrorCode == 10048)
{
// WSAEADDRINUSE
Console.WriteLine("Listener Failed: {0}:{1} (In Use)", m_EndPoint.Address, m_EndPoint.Port);
}
else if (se.ErrorCode == 10049)
{
// WSAEADDRNOTAVAIL
Console.WriteLine("Listener Failed: {0}:{1} (Unavailable)", m_EndPoint.Address, m_EndPoint.Port);
}
else
{
Console.WriteLine("Listener Exception:");
Console.WriteLine(e);
}
}
m_Socket = null;
return;
}
DisplayListener();
while (true)
{
Socket s;
try
{
s = await m_Socket.AcceptAsync().ConfigureAwait(false);
}
catch (SocketException ex)
{
NetState.TraceException(ex);
continue;
}
if (VerifySocket(s))
_ = new NetState(s, pump);
else
Release(s);
}
}
private void DisplayListener()
{
if (!(m_Socket.LocalEndPoint is IPEndPoint ipep))
return;
if (ipep.Address.Equals(IPAddress.Any) || ipep.Address.Equals(IPAddress.IPv6Any))
{
NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in adapters)
{
IPInterfaceProperties properties = adapter.GetIPProperties();
foreach (UnicastIPAddressInformation unicast in properties.UnicastAddresses)
if (ipep.AddressFamily == unicast.Address.AddressFamily)
Console.WriteLine("Listening: {0}:{1}", unicast.Address, ipep.Port);
}
}
else
{
Console.WriteLine("Listening: {0}:{1}", ipep.Address, ipep.Port);
}
}
private bool VerifySocket(Socket socket)
{
try
{
SocketConnectEventArgs args = new SocketConnectEventArgs(socket);
EventSink.InvokeSocketConnect(args);
return args.AllowConnection;
}
catch (Exception ex)
{
NetState.TraceException(ex);
return false;
}
}
private void Release(Socket socket)
{
try
{
socket.Shutdown(SocketShutdown.Both);
}
catch (SocketException ex)
{
NetState.TraceException(ex);
}
try
{
socket.Close();
}
catch (SocketException ex)
{
NetState.TraceException(ex);
}
try
{
socket.Dispose();
}
catch (SocketException ex)
{
NetState.TraceException(ex);
}
}
public void Dispose()
{
Interlocked.Exchange<Socket>(ref m_Socket, null)?.Close();
GC.SuppressFinalize(this);
}
}
}

View file

@ -0,0 +1,77 @@
/***************************************************************************
* MessagePump.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
namespace Server.Network
{
public class MessagePump
{
private ConcurrentQueue<Work> m_WorkQueue = new ConcurrentQueue<Work>();
public Listener[] Listeners => new Listener[0];
public void AddListener(IPEndPoint ipep)
{
Listener[] listeners = new Listener[Listeners.Length + 1];
Array.Copy(Listeners, listeners, Listeners.Length);
Listener listener = new Listener(ipep);
_ = listener.Start(this);
listeners[Listeners.Length] = listener;
}
public void QueueWork(NetState ns, in ReadOnlySequence<byte> seq, OnPacketReceive onReceive)
{
m_WorkQueue.Enqueue(new Work(ns, seq, onReceive));
Core.Set();
}
public void DoWork()
{
int count = m_WorkQueue.Count;
while (count-- > 0)
{
if (!m_WorkQueue.TryDequeue(out Work work))
break;
work.OnReceive(work.State, new PacketReader(work.Sequence));
}
}
// TODO: Optimize this with a pool
private class Work
{
public NetState State;
public ReadOnlySequence<byte> Sequence;
public OnPacketReceive OnReceive;
public Work(NetState ns, in ReadOnlySequence<byte> seq, OnPacketReceive onReceive)
{
State = ns;
Sequence = seq;
OnReceive = onReceive;
}
}
}
}

View file

@ -0,0 +1,764 @@
/***************************************************************************
* NetState.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Server.Accounting;
using Server.Diagnostics;
using Server.Gumps;
using Server.HuePickers;
using Server.Items;
using Server.Menus;
namespace Server.Network
{
public interface IPacketEncoder
{
void EncodeOutgoingPacket(NetState to, ref Memory<byte> seq);
void DecodeIncomingPacket(NetState from, ref Memory<byte> seq);
}
public delegate void NetStateCreatedCallback(NetState ns);
[Flags]
public enum ProtocolChanges
{
NewSpellbook = 0x00000001,
DamagePacket = 0x00000002,
Unpack = 0x00000004,
BuffIcon = 0x00000008,
NewHaven = 0x00000010,
ContainerGridLines = 0x00000020,
ExtendedSupportedFeatures = 0x00000040,
StygianAbyss = 0x00000080,
HighSeas = 0x00000100,
NewCharacterList = 0x00000200,
NewCharacterCreation = 0x00000400,
ExtendedStatus = 0x00000800,
NewMobileIncoming = 0x00001000,
NewSecureTrading = 0x00002000,
UltimaStore = 0x00004000,
EndlessJourney = 0x00008000,
Version400a = NewSpellbook,
Version407a = Version400a | DamagePacket,
Version500a = Version407a | Unpack,
Version502b = Version500a | BuffIcon,
Version6000 = Version502b | NewHaven,
Version6017 = Version6000 | ContainerGridLines,
Version60142 = Version6017 | ExtendedSupportedFeatures,
Version7000 = Version60142 | StygianAbyss,
Version7090 = Version7000 | HighSeas,
Version70130 = Version7090 | NewCharacterList,
Version70160 = Version70130 | NewCharacterCreation,
Version70300 = Version70160 | ExtendedStatus,
Version70331 = Version70300 | NewMobileIncoming,
Version704565 = Version70331 | NewSecureTrading,
Version70500 = Version704565 | UltimaStore,
Version70610 = Version70500 | EndlessJourney
}
public class AsyncState
{
public bool Paused { get; set; }
public AsyncState(bool paused)
{
Paused = paused;
}
}
public class NetState : IComparable<NetState>
{
private string m_ToString;
private ClientVersion m_Version;
private SendQueue<Packet> m_SendQueue = new SendQueue<Packet>();
public DateTime ConnectedOn { get; }
public TimeSpan ConnectedFor => DateTime.UtcNow - ConnectedOn;
public DateTime ThrottledUntil { get; set; }
internal int m_Seed;
internal int m_AuthID;
public IPAddress Address { get; }
private static AsyncState m_PauseState = new AsyncState(true);
private static AsyncState m_ResumeState = new AsyncState(false);
private static AsyncState m_AsyncState = m_ResumeState;
public IPacketEncoder PacketEncoder { get; set; }
public static NetStateCreatedCallback CreatedCallback { get; set; }
public bool SentFirstPacket { get; set; }
public bool BlockAllPackets { get; set; }
public ClientFlags Flags { get; set; }
public ClientVersion Version
{
get => m_Version;
set
{
m_Version = value;
if (value >= m_Version70610)
ProtocolChanges = ProtocolChanges.Version70610;
if (value >= m_Version70500)
ProtocolChanges = ProtocolChanges.Version70500;
if (value >= m_Version704565)
ProtocolChanges = ProtocolChanges.Version704565;
else if (value >= m_Version70331)
ProtocolChanges = ProtocolChanges.Version70331;
else if (value >= m_Version70300)
ProtocolChanges = ProtocolChanges.Version70300;
else if (value >= m_Version70160)
ProtocolChanges = ProtocolChanges.Version70160;
else if (value >= m_Version70130)
ProtocolChanges = ProtocolChanges.Version70130;
else if (value >= m_Version7090)
ProtocolChanges = ProtocolChanges.Version7090;
else if (value >= m_Version7000)
ProtocolChanges = ProtocolChanges.Version7000;
else if (value >= m_Version60142)
ProtocolChanges = ProtocolChanges.Version60142;
else if (value >= m_Version6017)
ProtocolChanges = ProtocolChanges.Version6017;
else if (value >= m_Version6000)
ProtocolChanges = ProtocolChanges.Version6000;
else if (value >= m_Version502b)
ProtocolChanges = ProtocolChanges.Version502b;
else if (value >= m_Version500a)
ProtocolChanges = ProtocolChanges.Version500a;
else if (value >= m_Version407a)
ProtocolChanges = ProtocolChanges.Version407a;
else if (value >= m_Version400a) ProtocolChanges = ProtocolChanges.Version400a;
}
}
private static readonly ClientVersion m_Version400a = new ClientVersion("4.0.0a");
private static readonly ClientVersion m_Version407a = new ClientVersion("4.0.7a");
private static readonly ClientVersion m_Version500a = new ClientVersion("5.0.0a");
private static readonly ClientVersion m_Version502b = new ClientVersion("5.0.2b");
private static readonly ClientVersion m_Version6000 = new ClientVersion("6.0.0.0");
private static readonly ClientVersion m_Version6017 = new ClientVersion("6.0.1.7");
private static readonly ClientVersion m_Version60142 = new ClientVersion("6.0.14.2");
private static readonly ClientVersion m_Version7000 = new ClientVersion("7.0.0.0");
private static readonly ClientVersion m_Version7090 = new ClientVersion("7.0.9.0");
private static readonly ClientVersion m_Version70130 = new ClientVersion("7.0.13.0");
private static readonly ClientVersion m_Version70160 = new ClientVersion("7.0.16.0");
private static readonly ClientVersion m_Version70300 = new ClientVersion("7.0.30.0");
private static readonly ClientVersion m_Version70331 = new ClientVersion("7.0.33.1");
private static readonly ClientVersion m_Version704565 = new ClientVersion("7.0.45.65");
private static readonly ClientVersion m_Version70500 = new ClientVersion("7.0.50.0");
private static readonly ClientVersion m_Version70610 = new ClientVersion("7.0.61.0");
public bool NewSpellbook => (ProtocolChanges & ProtocolChanges.NewSpellbook) != 0;
public bool DamagePacket => (ProtocolChanges & ProtocolChanges.DamagePacket) != 0;
public bool Unpack => (ProtocolChanges & ProtocolChanges.Unpack) != 0;
public bool BuffIcon => (ProtocolChanges & ProtocolChanges.BuffIcon) != 0;
public bool NewHaven => (ProtocolChanges & ProtocolChanges.NewHaven) != 0;
public bool ContainerGridLines => (ProtocolChanges & ProtocolChanges.ContainerGridLines) != 0;
public bool ExtendedSupportedFeatures => (ProtocolChanges & ProtocolChanges.ExtendedSupportedFeatures) != 0;
public bool StygianAbyss => (ProtocolChanges & ProtocolChanges.StygianAbyss) != 0;
public bool HighSeas => (ProtocolChanges & ProtocolChanges.HighSeas) != 0;
public bool NewCharacterList => (ProtocolChanges & ProtocolChanges.NewCharacterList) != 0;
public bool NewCharacterCreation => (ProtocolChanges & ProtocolChanges.NewCharacterCreation) != 0;
public bool ExtendedStatus => (ProtocolChanges & ProtocolChanges.ExtendedStatus) != 0;
public bool NewMobileIncoming => (ProtocolChanges & ProtocolChanges.NewMobileIncoming) != 0;
public bool NewSecureTrading => (ProtocolChanges & ProtocolChanges.NewSecureTrading) != 0;
public bool IsUOTDClient =>
(Flags & ClientFlags.UOTD) != 0 || m_Version?.Type == ClientType.UOTD;
public bool IsSAClient => m_Version?.Type == ClientType.SA;
public List<SecureTrade> Trades { get; }
public void ValidateAllTrades()
{
for (int i = Trades.Count - 1; i >= 0; --i)
{
if (i >= Trades.Count) continue;
SecureTrade trade = Trades[i];
if (trade.From.Mobile.Deleted || trade.To.Mobile.Deleted || !trade.From.Mobile.Alive ||
!trade.To.Mobile.Alive || !trade.From.Mobile.InRange(trade.To.Mobile, 2) ||
trade.From.Mobile.Map != trade.To.Mobile.Map) trade.Cancel();
}
}
public void CancelAllTrades()
{
for (int i = Trades.Count - 1; i >= 0; --i)
if (i < Trades.Count)
Trades[i].Cancel();
}
public void RemoveTrade(SecureTrade trade)
{
Trades.Remove(trade);
}
public SecureTrade FindTrade(Mobile m)
{
for (int i = 0; i < Trades.Count; ++i)
{
SecureTrade trade = Trades[i];
if (trade.From.Mobile == m || trade.To.Mobile == m) return trade;
}
return null;
}
public SecureTradeContainer FindTradeContainer(Mobile m)
{
for (int i = 0; i < Trades.Count; ++i)
{
SecureTrade trade = Trades[i];
SecureTradeInfo from = trade.From;
SecureTradeInfo to = trade.To;
if (from.Mobile == Mobile && to.Mobile == m) return from.Container;
if (from.Mobile == m && to.Mobile == Mobile) return to.Container;
}
return null;
}
public SecureTradeContainer AddTrade(NetState state)
{
SecureTrade newTrade = new SecureTrade(Mobile, state.Mobile);
Trades.Add(newTrade);
state.Trades.Add(newTrade);
return newTrade.From.Container;
}
public bool Running { get; private set; }
public bool Seeded { get; set; }
public Socket Socket { get; private set; }
public bool CompressionEnabled { get; set; }
public int Sequence { get; set; }
public List<Gump> Gumps { get; private set; }
public List<HuePicker> HuePickers { get; private set; }
public List<IMenu> Menus { get; private set; }
public static int GumpCap { get; set; } = 512;
public static int HuePickerCap { get; set; } = 512;
public static int MenuCap { get; set; } = 512;
public void WriteConsole(string text)
{
Console.WriteLine("Client: {0}: {1}", this, text);
}
public void WriteConsole(string format, params object[] args)
{
WriteConsole(string.Format(format, args));
}
public void AddMenu(IMenu menu)
{
if (Menus == null)
Menus = new List<IMenu>();
if (Menus.Count < MenuCap)
Menus.Add(menu);
else
{
WriteConsole("Exceeded menu cap, disconnecting...");
Dispose();
}
}
public void RemoveMenu(IMenu menu)
{
Menus?.Remove(menu);
}
public void RemoveMenu(int index)
{
Menus?.RemoveAt(index);
}
public void ClearMenus()
{
Menus?.Clear();
}
public void AddHuePicker(HuePicker huePicker)
{
if (HuePickers == null)
HuePickers = new List<HuePicker>();
if (HuePickers.Count < HuePickerCap)
HuePickers.Add(huePicker);
else
{
WriteConsole("Exceeded hue picker cap, disconnecting...");
Dispose();
}
}
public void RemoveHuePicker(HuePicker huePicker)
{
HuePickers?.Remove(huePicker);
}
public void RemoveHuePicker(int index)
{
HuePickers?.RemoveAt(index);
}
public void ClearHuePickers()
{
HuePickers?.Clear();
}
public void AddGump(Gump gump)
{
if (Gumps == null)
Gumps = new List<Gump>();
if (Gumps.Count < GumpCap)
Gumps.Add(gump);
else
{
WriteConsole("Exceeded gump cap, disconnecting...");
Dispose();
}
}
public void RemoveGump(Gump gump)
{
Gumps?.Remove(gump);
}
public void RemoveGump(int index)
{
Gumps?.RemoveAt(index);
}
public void ClearGumps()
{
Gumps?.Clear();
}
public void LaunchBrowser(string url)
{
Send(new MessageLocalized(Serial.MinusOne, -1, MessageType.Label, 0x35, 3, 501231, "", ""));
Send(new LaunchBrowser(url));
}
public CityInfo[] CityInfo { get; set; }
public Mobile Mobile { get; set; }
public ServerInfo[] ServerInfo { get; set; }
public IAccount Account { get; set; }
public override string ToString()
{
return m_ToString;
}
public static List<NetState> Instances { get; } = new List<NetState>();
public NetState(Socket socket, MessagePump pump)
{
Socket = socket;
Seeded = false;
Running = false;
Gumps = new List<Gump>();
HuePickers = new List<HuePicker>();
Menus = new List<IMenu>();
Trades = new List<SecureTrade>();
m_NextCheckActivity = Core.TickCount + 30000;
Instances.Add(this);
try
{
Address = Utility.Intern(((IPEndPoint)Socket.RemoteEndPoint).Address);
m_ToString = Address.ToString();
}
catch (Exception ex)
{
TraceException(ex);
Address = IPAddress.None;
m_ToString = "(error)";
}
ConnectedOn = DateTime.UtcNow;
_ = Start(pump);
CreatedCallback?.Invoke(this);
Console.WriteLine("Client: {0}: Connected. [{1} Online]", this, NetState.Instances.Count);
}
public static void Pause()
{
m_AsyncState = Interlocked.Exchange(ref m_AsyncState, m_PauseState);
}
public static void Resume()
{
m_AsyncState = Interlocked.Exchange(ref m_AsyncState, m_ResumeState);
}
public virtual void Send(Packet p)
{
if (Socket == null || BlockAllPackets)
{
p.OnSend();
return;
}
m_SendQueue.Enqueue(p);
}
public bool CheckEncrypted(int packetID)
{
if (!SentFirstPacket && packetID != 0xF0 && packetID != 0xF1 && packetID != 0xCF && packetID != 0x80 &&
packetID != 0x91 && packetID != 0xA4 && packetID != 0xEF)
{
Console.WriteLine("Client: {0}: Encrypted client detected, disconnecting", this);
Dispose();
return true;
}
return false;
}
private Pipe m_RecvdPipe;
private async Task Start(MessagePump pump)
{
m_RecvdPipe = new Pipe();
Running = true;
await Task.WhenAll(ProcessSends(), ProcessRecvs(), HandlePackets(pump)).ConfigureAwait(false);
}
private async Task ProcessRecvs()
{
PipeWriter w = m_RecvdPipe.Writer;
while (true)
{
if (m_AsyncState.Paused)
{
await Timer.Pause(50).ConfigureAwait(false);
continue;
}
try
{
Memory<byte> memory = w.GetMemory();
int bytesRead = await Socket.ReceiveAsync(memory, SocketFlags.None).ConfigureAwait(false);
if (bytesRead == 0)
break;
Interlocked.Exchange(ref m_NextCheckActivity, Core.TickCount + 90000);
w.Advance(bytesRead);
FlushResult result = await w.FlushAsync().ConfigureAwait(false);
if (result.IsCompleted || result.IsCanceled)
break;
}
catch
{
break;
}
}
w.Complete();
Dispose();
}
private async Task ProcessSends()
{
while (true)
{
try
{
Packet p = await m_SendQueue?.DequeueAsync() ?? null;
if (p == null)
break;
ReadOnlyMemory<byte> buffer = p.Compile(CompressionEnabled, out int length);
if (buffer.Length <= 0 || length <= 0)
{
p.OnSend();
return;
}
buffer = buffer.Slice(0, length);
PacketSendProfile prof = null;
if (Core.Profiling)
prof = PacketSendProfile.Acquire(p.GetType());
prof?.Start();
await Socket.SendAsync(buffer, SocketFlags.None).ConfigureAwait(false);
p.OnSend();
prof?.Finish(length);
}
catch (SocketException ex)
{
Console.WriteLine(ex);
TraceException(ex);
Dispose();
break;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
private async Task HandlePackets(MessagePump pump)
{
PipeReader pr = m_RecvdPipe.Reader;
while (true)
{
ReadResult result = await pr.ReadAsync();
ReadOnlySequence<byte> seq = result.Buffer;
if (seq.Length == 0)
break;
long pos = PacketHandlers.ProcessPacket(pump, this, seq);
if (pos <= 0)
break;
pr.AdvanceTo(seq.GetPosition(pos, seq.Start));
if (result.IsCompleted || result.IsCanceled)
break;
}
pr.Complete();
}
public PacketHandler GetHandler(int packetID)
{
return ContainerGridLines ? PacketHandlers.Get6017Handler(packetID) :
PacketHandlers.GetHandler(packetID);
}
private long m_NextCheckActivity;
public void CheckAlive(long curTicks)
{
if (Socket == null || m_NextCheckActivity - curTicks >= 0)
return;
Console.WriteLine("Client: {0}: Disconnecting due to inactivity...", this);
Dispose();
}
public static void TraceException(Exception ex)
{
if (!Core.Debug)
return;
try
{
using (StreamWriter op = new StreamWriter("network-errors.log", true))
{
op.WriteLine("# {0}", DateTime.UtcNow);
op.WriteLine(ex);
op.WriteLine();
op.WriteLine();
}
}
catch
{
// ignored
}
Console.WriteLine(ex);
}
private int m_Disposing;
public bool IsDisposing { get { return m_Disposing != 0; } private set { m_Disposing = value ? 1 : 0; } }
public virtual void Dispose()
{
int disposing = Interlocked.Exchange(ref m_Disposing, 1);
if (disposing == 1)
return;
Task.Run(async () =>
{
m_RecvdPipe.Reader.CancelPendingRead();
m_RecvdPipe.Reader.Complete();
await m_RecvdPipe.Writer.FlushAsync().ConfigureAwait(false);
m_RecvdPipe.Writer.Complete();
try { Socket.Shutdown(SocketShutdown.Both); } catch (Exception ex) { TraceException(ex); }
try { Socket.Close(); } catch (Exception ex) { TraceException(ex); }
Socket = null;
m_RecvdPipe = null;
m_SendQueue = null;
m_Disposed.Enqueue(this);
});
}
public static void Initialize()
{
Timer.DelayCall(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.5), CheckAllAlive);
}
public static void CheckAllAlive()
{
try
{
long curTicks = Core.TickCount;
if (Instances.Count >= 1024)
Parallel.ForEach(Instances, ns => ns.CheckAlive(curTicks));
else
for (int i = 0; i < Instances.Count; ++i)
Instances[i].CheckAlive(curTicks);
}
catch (Exception ex)
{
TraceException(ex);
}
}
private static ConcurrentQueue<NetState> m_Disposed = new ConcurrentQueue<NetState>();
public static void ProcessDisposedQueue()
{
int breakout = 0;
while (breakout++ < 200)
{
if (!m_Disposed.TryDequeue(out NetState ns))
break;
Mobile m = ns.Mobile;
IAccount a = ns.Account;
if (m != null)
{
m.NetState = null;
ns.Mobile = null;
}
ns.Gumps.Clear();
ns.Menus.Clear();
ns.HuePickers.Clear();
ns.Account = null;
ns.ServerInfo = null;
ns.CityInfo = null;
Instances.Remove(ns);
if (a != null)
ns.WriteConsole("Disconnected. [{0} Online] [{1}]", Instances.Count, a);
else
ns.WriteConsole("Disconnected. [{0} Online]", Instances.Count);
}
}
public ExpansionInfo ExpansionInfo
{
get
{
for (int i = ExpansionInfo.Table.Length - 1; i >= 0; i--)
{
ExpansionInfo info = ExpansionInfo.Table[i];
if (info.RequiredClient != null && Version >= info.RequiredClient || (Flags & info.ClientFlags) != 0)
return info;
}
return ExpansionInfo.GetInfo(Expansion.None);
}
}
public Expansion Expansion => (Expansion)ExpansionInfo.ID;
public ProtocolChanges ProtocolChanges { get; set; }
public bool SupportsExpansion(ExpansionInfo info, bool checkCoreExpansion = true)
{
if (info == null || checkCoreExpansion && (int)Core.Expansion < info.ID)
return false;
return info.RequiredClient != null ? Version >= info.RequiredClient : (Flags & info.ClientFlags) != 0;
}
public bool SupportsExpansion(Expansion ex, bool checkCoreExpansion = true)
{
return SupportsExpansion(ExpansionInfo.GetInfo(ex), checkCoreExpansion);
}
public int CompareTo(NetState other)
{
return other == null ? 1 : m_ToString.CompareTo(other.m_ToString);
}
}
}

View file

@ -0,0 +1,273 @@
/***************************************************************************
* Packet.cs
* -------------------
* begin : August 2, 2019
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using Server.Diagnostics;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace Server.Network
{
public abstract class Packet
{
private const int CompressorBufferSize = 0x10000;
private const int BufferSize = 4096;
private byte[] m_CompiledBuffer;
private int m_CompiledLength;
private int m_Length;
private State m_State;
protected PacketWriter m_Stream;
protected Packet(int packetID)
{
PacketID = packetID;
if (Core.Profiling)
{
PacketSendProfile prof = PacketSendProfile.Acquire(GetType());
prof.Increment();
}
}
protected Packet(int packetID, int length)
{
PacketID = packetID;
m_Length = length;
m_Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length );
m_Stream.Write((byte)packetID);
if (Core.Profiling)
{
PacketSendProfile prof = PacketSendProfile.Acquire(GetType());
prof.Increment();
}
}
public int PacketID { get; }
public PacketWriter UnderlyingStream => m_Stream;
public void EnsureCapacity(int length)
{
m_Stream = PacketWriter.CreateInstance(length); // new PacketWriter( length );
m_Stream.Write((byte)PacketID);
m_Stream.Write((short)0);
}
public static Packet SetStatic(Packet p)
{
p.SetStatic();
return p;
}
public static Packet Acquire(Packet p)
{
p.Acquire();
return p;
}
public static void Release(ref Packet p)
{
p?.Release();
p = null;
}
public static void Release(Packet p)
{
p?.Release();
}
public void SetStatic()
{
m_State |= State.Static | State.Acquired;
}
public void Acquire()
{
m_State |= State.Acquired;
}
public void OnSend()
{
Core.Set(); // Is this still needed if this is done async?
if ((m_State & (State.Acquired | State.Static)) == 0)
Free();
}
private void Free()
{
if (m_CompiledBuffer == null)
return;
if ((m_State & State.Buffered) != 0)
ArrayPool<byte>.Shared.Return(m_CompiledBuffer);
m_State &= ~(State.Static | State.Acquired | State.Buffered);
m_CompiledBuffer = null;
}
public void Release()
{
if ((m_State & State.Acquired) != 0)
Free();
}
public byte[] Compile(bool compress, out int length)
{
lock (this)
{
if (m_CompiledBuffer == null)
{
if ((m_State & State.Accessed) == 0)
{
m_State |= State.Accessed;
}
else
{
if ((m_State & State.Warned) == 0)
{
m_State |= State.Warned;
try
{
using (StreamWriter op = new StreamWriter("net_opt.log", true))
{
op.WriteLine("Redundant compile for packet {0}, use Acquire() and Release()", GetType());
op.WriteLine(new StackTrace());
}
}
catch
{
// ignored
}
}
m_CompiledBuffer = new byte[0];
m_CompiledLength = 0;
length = m_CompiledLength;
return m_CompiledBuffer;
}
InternalCompile(compress);
}
length = m_CompiledLength;
return m_CompiledBuffer;
}
}
private void InternalCompile(bool compress)
{
if (m_Length == 0)
{
long streamLen = m_Stream.Length;
m_Stream.Seek(1, SeekOrigin.Begin);
m_Stream.Write((ushort)streamLen);
}
else if (m_Stream.Length != m_Length)
{
int diff = (int)m_Stream.Length - m_Length;
Console.WriteLine("Packet: 0x{0:X2}: Bad packet length! ({1}{2} bytes)", PacketID, diff >= 0 ? "+" : "",
diff);
}
MemoryStream ms = m_Stream.UnderlyingStream;
m_CompiledBuffer = ms.GetBuffer();
int length = (int)ms.Length;
if (compress)
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(CompressorBufferSize);
Compression.Compress(m_CompiledBuffer, 0, length, buffer, ref length);
if (length <= 0)
{
Console.WriteLine("Warning: Compression buffer overflowed on packet 0x{0:X2} ('{1}') (length={2})",
PacketID, GetType().Name, length);
using (StreamWriter op = new StreamWriter("compression_overflow.log", true))
{
op.WriteLine("{0} Warning: Compression buffer overflowed on packet 0x{1:X2} ('{2}') (length={3})",
DateTime.UtcNow, PacketID, GetType().Name, length);
op.WriteLine(new StackTrace());
}
}
else
{
m_CompiledLength = length;
if ((m_State & State.Static) != 0)
{
m_CompiledBuffer = new byte[length];
Buffer.BlockCopy(buffer, 0, m_CompiledBuffer, 0, length);
ArrayPool<byte>.Shared.Return(buffer);
}
else
{
m_CompiledBuffer = buffer;
m_State |= State.Buffered;
}
}
}
else if (length > 0)
{
byte[] old = m_CompiledBuffer;
m_CompiledLength = length;
if ((m_State & State.Static) != 0)
m_CompiledBuffer = new byte[length];
else
{
m_CompiledBuffer = ArrayPool<byte>.Shared.Rent(length);
m_State |= State.Buffered;
}
Buffer.BlockCopy(old, 0, m_CompiledBuffer, 0, length);
}
PacketWriter.ReleaseInstance(m_Stream);
m_Stream = null;
}
[Flags]
private enum State
{
Inactive = 0x00,
Static = 0x01,
Acquired = 0x02,
Accessed = 0x04,
Buffered = 0x08,
Warned = 0x10
}
}
}

View file

@ -0,0 +1,49 @@
/***************************************************************************
* PacketHandler.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
namespace Server.Network
{
public delegate void OnPacketReceive(NetState state, PacketReader pvSrc);
public delegate TimeSpan ThrottlePacketCallback(NetState state);
public class PacketHandler
{
public PacketHandler(int packetID, long length, bool ingame, OnPacketReceive onReceive)
{
PacketID = packetID;
Length = length;
Ingame = ingame;
OnReceive = onReceive;
}
public int PacketID{ get; }
public long Length{ get; }
public OnPacketReceive OnReceive{ get; }
public ThrottlePacketCallback ThrottleCallback{ get; set; }
public bool Ingame{ get; }
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,315 @@
/***************************************************************************
* PacketReader.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Buffers;
using System.IO;
using System.IO.Pipelines;
using System.Text;
namespace Server.Network
{
public ref struct PacketReader
{
private SequenceReader<byte> m_Reader;
public SequencePosition Position => m_Reader.Position;
public long Length => m_Reader.Length;
public long Consumed => m_Reader.Consumed;
public long Remaining => m_Reader.Remaining;
public PacketReader(ReadOnlySequence<byte> seq)
{
m_Reader = new SequenceReader<byte>(seq);
}
public byte Peek() => m_Reader.TryPeek(out byte value) ? value : (byte)0;
public void Trace(NetState state)
{
try
{
using (StreamWriter sw = new StreamWriter("Packets.log", true))
{
byte[] buffer = m_Reader.Sequence.ToArray();
if (buffer.Length > 0)
sw.WriteLine("Client: {0}: Unhandled packet 0x{1:X2}", state, buffer[0]);
using (MemoryStream ms = new MemoryStream(buffer))
{
Utility.FormatBuffer(sw, ms, buffer.Length);
}
sw.WriteLine();
sw.WriteLine();
}
}
catch
{
// ignored
}
}
public SequencePosition Seek(long offset, SeekOrigin origin)
{
switch (origin)
{
case SeekOrigin.Begin:
if (offset < m_Reader.Consumed)
m_Reader.Rewind(m_Reader.Consumed - Math.Max(offset, 0L));
else
m_Reader.Advance(offset - m_Reader.Consumed);
break;
case SeekOrigin.Current:
if (offset < 0)
m_Reader.Rewind(Math.Min(m_Reader.Consumed, offset * -1));
else
m_Reader.Advance(Math.Min(m_Reader.Remaining, offset));
break;
case SeekOrigin.End:
long count = m_Reader.Remaining - offset;
if (count < 0)
m_Reader.Rewind(count * -1);
else if (count > 0)
m_Reader.Advance(count);
break;
}
return m_Reader.Position;
}
public bool TryReadByte(out byte value) => m_Reader.TryRead(out value);
public int ReadInt32() => m_Reader.TryReadBigEndian(out int value) ? value : 0;
public short ReadInt16() => m_Reader.TryReadBigEndian(out short value) ? value : (short)0;
public byte ReadByte() => m_Reader.TryRead(out byte value) ? value : (byte)0;
public uint ReadUInt32() => (uint)ReadInt32();
public ushort ReadUInt16() => (ushort)ReadInt16();
public sbyte ReadSByte() => (sbyte)ReadByte();
public bool ReadBoolean() => ReadByte() > 0;
public string ReadUnicodeStringLE()
{
StringBuilder sb = new StringBuilder();
while (m_Reader.TryReadLittleEndian(out short c) && c != 0)
sb.Append((char)c);
return sb.ToString();
}
public string ReadUnicodeStringLE(int fixedLength)
{
StringBuilder sb = new StringBuilder();
while (fixedLength-- > 0 && m_Reader.TryReadLittleEndian(out short c) && c != 0)
sb.Append((char)c);
if (fixedLength > 0)
m_Reader.Advance(fixedLength);
return sb.ToString();
}
public string ReadUnicodeStringLESafe(int fixedLength)
{
StringBuilder sb = new StringBuilder();
while (fixedLength-- > 0 && m_Reader.TryReadLittleEndian(out short c) && c != 0)
if (IsSafeChar(c))
sb.Append((char)c);
if (fixedLength > 0)
m_Reader.Advance(fixedLength * 2);
return sb.ToString();
}
public string ReadUnicodeStringLESafe()
{
StringBuilder sb = new StringBuilder();
while (m_Reader.TryReadLittleEndian(out short c) && c != 0)
if (IsSafeChar(c))
sb.Append((char)c);
return sb.ToString();
}
public string ReadUnicodeStringSafe()
{
StringBuilder sb = new StringBuilder();
while (m_Reader.TryReadBigEndian(out short c) && c != 0)
if (IsSafeChar(c))
sb.Append((char)c);
return sb.ToString();
}
public string ReadUnicodeString()
{
StringBuilder sb = new StringBuilder();
while (m_Reader.TryReadBigEndian(out short c) && c != 0)
sb.Append((char)c);
return sb.ToString();
}
public bool IsSafeChar(int c)
{
return c >= 0x20 && c < 0xFFFE;
}
public string ReadUTF8StringSafe(int fixedLength)
{
string s;
if (m_Reader.TryReadTo(out ReadOnlySpan<byte> span, (byte)'\0', true))
s = Utility.UTF8.GetString(span.Length > fixedLength ? span.Slice(0, fixedLength) : span);
else
{
long size = Math.Min(m_Reader.Remaining, fixedLength);
s = Utility.UTF8.GetString(m_Reader.Sequence.Slice(m_Reader.Position, size).ToArray());
m_Reader.Advance(size);
}
StringBuilder sb = new StringBuilder(s.Length);
for (int i = 0; i < s.Length; ++i)
if (IsSafeChar(s[i]))
sb.Append(s[i]);
return sb.ToString();
}
public string ReadUTF8StringSafe()
{
string s;
if (m_Reader.TryReadTo(out ReadOnlySpan<byte> span, (byte)'\0', true))
s = Utility.UTF8.GetString(span);
else
{
s = Utility.UTF8.GetString(m_Reader.Sequence.Slice(m_Reader.Position, m_Reader.Remaining).ToArray());
m_Reader.Advance(m_Reader.Remaining);
}
StringBuilder sb = new StringBuilder(s.Length);
for (int i = 0; i < s.Length; ++i)
if (IsSafeChar(s[i]))
sb.Append(s[i]);
return sb.ToString();
}
public string ReadUTF8String()
{
return Utility.UTF8.GetString(
m_Reader.TryReadTo(out ReadOnlySpan<byte> span, (byte)'\0', true) ? span :
m_Reader.Sequence.Slice(m_Reader.Position, m_Reader.Remaining).ToArray()
);
}
public string ReadString()
{
StringBuilder sb = new StringBuilder();
while (m_Reader.TryRead(out byte c))
sb.Append((char)c);
return sb.ToString();
}
public string ReadStringSafe()
{
StringBuilder sb = new StringBuilder();
while (m_Reader.TryRead(out byte c))
if (IsSafeChar(c))
sb.Append((char)c);
return sb.ToString();
}
public string ReadUnicodeStringSafe(int fixedLength)
{
StringBuilder sb = new StringBuilder();
while (fixedLength-- > 0 && m_Reader.TryReadBigEndian(out short c) && c != 0)
if (IsSafeChar(c))
sb.Append((char)c);
if (fixedLength > 0)
m_Reader.Advance(fixedLength * 2);
return sb.ToString();
}
public string ReadUnicodeString(int fixedLength)
{
StringBuilder sb = new StringBuilder();
while (fixedLength-- > 0 && m_Reader.TryReadBigEndian(out short c) && c != 0)
sb.Append((char)c);
if (fixedLength > 0)
m_Reader.Advance(fixedLength * 2);
return sb.ToString();
}
public string ReadStringSafe(int fixedLength)
{
StringBuilder sb = new StringBuilder();
while (fixedLength-- > 0 && m_Reader.TryRead(out byte c) && c != 0)
if (IsSafeChar(c))
sb.Append((char)c);
if (fixedLength > 0)
m_Reader.Advance(fixedLength);
return sb.ToString();
}
public string ReadString(int fixedLength)
{
StringBuilder sb = new StringBuilder();
while (fixedLength-- > 0 && m_Reader.TryRead(out byte c) && c != 0)
sb.Append((char)c);
if (fixedLength > 0)
m_Reader.Advance(fixedLength);
return sb.ToString();
}
}
}

View file

@ -0,0 +1,346 @@
/***************************************************************************
* PacketWriter.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
namespace Server.Network
{
/// <summary>
/// Provides functionality for writing primitive binary data.
/// </summary>
public class PacketWriter
{
private static ConcurrentQueue<PacketWriter> m_Pool = new ConcurrentQueue<PacketWriter>();
/// <summary>
/// Internal format buffer.
/// </summary>
private byte[] m_Buffer = new byte[4];
private int m_Capacity;
/// <summary>
/// Instantiates a new PacketWriter instance with a given capacity.
/// </summary>
/// <param name="capacity">Initial capacity for the internal stream.</param>
public PacketWriter(int capacity = 32)
{
UnderlyingStream = new MemoryStream(capacity);
m_Capacity = capacity;
}
/// <summary>
/// Gets the total stream length.
/// </summary>
public long Length => UnderlyingStream.Length;
/// <summary>
/// Gets or sets the current stream position.
/// </summary>
public long Position
{
get => UnderlyingStream.Position;
set => UnderlyingStream.Position = value;
}
/// <summary>
/// The internal stream used by this PacketWriter instance.
/// </summary>
public MemoryStream UnderlyingStream { get; private set; }
public static PacketWriter CreateInstance(int capacity = 32)
{
if (m_Pool.TryDequeue(out PacketWriter pw))
{
pw.m_Capacity = capacity;
pw.UnderlyingStream.SetLength(0);
return pw;
}
return new PacketWriter(capacity);
}
public static void ReleaseInstance(PacketWriter pw)
{
m_Pool.Enqueue(pw);
}
/// <summary>
/// Writes a 1-byte boolean value to the underlying stream. False is represented by 0, true by 1.
/// </summary>
public void Write(bool value)
{
UnderlyingStream.WriteByte((byte)(value ? 1 : 0));
}
/// <summary>
/// Writes a 1-byte unsigned integer value to the underlying stream.
/// </summary>
public void Write(byte value)
{
UnderlyingStream.WriteByte(value);
}
/// <summary>
/// Writes a 1-byte signed integer value to the underlying stream.
/// </summary>
public void Write(sbyte value)
{
UnderlyingStream.WriteByte((byte)value);
}
/// <summary>
/// Writes a 2-byte signed integer value to the underlying stream.
/// </summary>
public void Write(short value)
{
m_Buffer[0] = (byte)(value >> 8);
m_Buffer[1] = (byte)value;
UnderlyingStream.Write(m_Buffer, 0, 2);
}
/// <summary>
/// Writes a 2-byte unsigned integer value to the underlying stream.
/// </summary>
public void Write(ushort value)
{
m_Buffer[0] = (byte)(value >> 8);
m_Buffer[1] = (byte)value;
UnderlyingStream.Write(m_Buffer, 0, 2);
}
/// <summary>
/// Writes a 4-byte signed integer value to the underlying stream.
/// </summary>
public void Write(int value)
{
m_Buffer[0] = (byte)(value >> 24);
m_Buffer[1] = (byte)(value >> 16);
m_Buffer[2] = (byte)(value >> 8);
m_Buffer[3] = (byte)value;
UnderlyingStream.Write(m_Buffer, 0, 4);
}
/// <summary>
/// Writes a 4-byte unsigned integer value to the underlying stream.
/// </summary>
public void Write(uint value)
{
m_Buffer[0] = (byte)(value >> 24);
m_Buffer[1] = (byte)(value >> 16);
m_Buffer[2] = (byte)(value >> 8);
m_Buffer[3] = (byte)value;
UnderlyingStream.Write(m_Buffer, 0, 4);
}
/// <summary>
/// Writes a sequence of bytes to the underlying stream
/// </summary>
public void Write(byte[] buffer, int offset, int size)
{
UnderlyingStream.Write(buffer, offset, size);
}
/// <summary>
/// Writes a fixed-length ASCII-encoded string value to the underlying stream. To fit (size), the string content is either
/// truncated or padded with null characters.
/// </summary>
public void WriteAsciiFixed(string value, int size)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteAsciiFixed() with null value");
value = string.Empty;
}
int length = value.Length;
UnderlyingStream.SetLength(UnderlyingStream.Length + size);
if (length >= size)
{
UnderlyingStream.Position +=
Encoding.ASCII.GetBytes(value, 0, size, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
}
else
{
Encoding.ASCII.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += size;
}
}
/// <summary>
/// Writes a dynamic-length ASCII-encoded string value to the underlying stream, followed by a 1-byte null character.
/// </summary>
public void WriteAsciiNull(string value)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteAsciiNull() with null value");
value = string.Empty;
}
int length = value.Length;
UnderlyingStream.SetLength(UnderlyingStream.Length + length + 1);
Encoding.ASCII.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += length + 1;
}
/// <summary>
/// Writes a dynamic-length little-endian unicode string value to the underlying stream, followed by a 2-byte null character.
/// </summary>
public void WriteLittleUniNull(string value)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteLittleUniNull() with null value");
value = string.Empty;
}
int length = value.Length;
UnderlyingStream.SetLength(UnderlyingStream.Length + (length + 1) * 2);
UnderlyingStream.Position += Encoding.Unicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += 2;
}
/// <summary>
/// Writes a fixed-length little-endian unicode string value to the underlying stream. To fit (size), the string content is
/// either truncated or padded with null characters.
/// </summary>
public void WriteLittleUniFixed(string value, int size)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteLittleUniFixed() with null value");
value = string.Empty;
}
int length = value.Length;
size *= 2;
UnderlyingStream.SetLength(UnderlyingStream.Length + size);
if (length * 2 >= size)
{
UnderlyingStream.Position +=
Encoding.Unicode.GetBytes(value, 0, size, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
}
else
{
Encoding.Unicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += size;
}
}
/// <summary>
/// Writes a dynamic-length big-endian unicode string value to the underlying stream, followed by a 2-byte null character.
/// </summary>
public void WriteBigUniNull(string value)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteBigUniNull() with null value");
value = string.Empty;
}
int length = value.Length;
UnderlyingStream.SetLength(UnderlyingStream.Length + (length + 1) * 2);
UnderlyingStream.Position +=
Encoding.BigEndianUnicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += 2;
}
/// <summary>
/// Writes a fixed-length big-endian unicode string value to the underlying stream. To fit (size), the string content is
/// either truncated or padded with null characters.
/// </summary>
public void WriteBigUniFixed(string value, int size)
{
if (value == null)
{
Console.WriteLine("Network: Attempted to WriteBigUniFixed() with null value");
value = string.Empty;
}
int length = value.Length;
size *= 2;
UnderlyingStream.SetLength(UnderlyingStream.Length + size);
if (length * 2 >= size )
{
UnderlyingStream.Position +=
Encoding.BigEndianUnicode.GetBytes(value, 0, size, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
}
else
{
Encoding.BigEndianUnicode.GetBytes(value, 0, length, UnderlyingStream.GetBuffer(), (int)UnderlyingStream.Position);
UnderlyingStream.Position += size;
}
}
/// <summary>
/// Fills the stream from the current position up to (capacity) with 0x00's
/// </summary>
public void Fill()
{
Fill(m_Capacity - UnderlyingStream.Length);
}
/// <summary>
/// Writes a number of 0x00 byte values to the underlying stream.
/// </summary>
public void Fill(long length)
{
if (UnderlyingStream.Position == UnderlyingStream.Length)
{
UnderlyingStream.SetLength(UnderlyingStream.Length + length);
UnderlyingStream.Seek(0, SeekOrigin.End);
}
else
{
UnderlyingStream.Write(new byte[length], 0, (int)length);
}
}
/// <summary>
/// Offsets the current position from an origin.
/// </summary>
public long Seek(long offset, SeekOrigin origin) => UnderlyingStream.Seek(offset, origin);
/// <summary>
/// Gets the entire stream content as a byte array.
/// </summary>
public byte[] ToArray() => UnderlyingStream.ToArray();
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
/***************************************************************************
* SendQueue.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace Server.Network
{
public class SendQueue<T>
{
private BlockingCollection<T> m_Queue = new BlockingCollection<T>(new ConcurrentQueue<T>());
public SendQueue()
{
}
public void Enqueue(T t)
{
m_Queue.Add(t);
}
public Task<T> DequeueAsync()
{
TaskCompletionSource<T> taskCompletion = new TaskCompletionSource<T>();
Task.Run(() => taskCompletion.SetResult(Dequeue()));
return taskCompletion.Task;
}
public T Dequeue()
{
return m_Queue.Take();
}
}
}

View file

@ -0,0 +1,57 @@
/***************************************************************************
* SocketExtensions.cs
* -------------------
* begin : August 2, 2019
* copyright : (C) The ModernUO Team
* email : hi@modernuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Buffers;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace Server.Network
{
public static class SocketExtensions
{
public static Task<int> ReceiveAsync(this Socket socket, Memory<byte> memory, SocketFlags socketFlags)
{
return SocketTaskExtensions.ReceiveAsync(socket, GetArray(memory), socketFlags);
}
public static ArraySegment<byte> GetArray(this Memory<byte> memory)
{
return ((ReadOnlyMemory<byte>)memory).GetArray();
}
public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
{
if (MemoryMarshal.TryGetArray(memory, out var result))
return result;
throw new InvalidOperationException("Buffer backed by array was expected");
}
public static ArraySegment<byte> GetArray(this ReadOnlySequence<byte> memory)
{
if (SequenceMarshal.TryGetArray(memory, out var result))
return result;
throw new InvalidOperationException("Buffer backed by array was expected");
}
}
}

View file

@ -0,0 +1,135 @@
/***************************************************************************
* StaticPacketHandlers.cs
* -------------------
* begin : March 15, 2019
* copyright : (C) The ModernUO Team
* email : hi@modernuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Collections.Concurrent;
namespace Server.Network
{
public static class StaticPacketHandlers
{
private static ConcurrentDictionary<IPropertyListObject,OPLInfo> OPLInfoPackets = new ConcurrentDictionary<IPropertyListObject,OPLInfo>();
private static ConcurrentDictionary<IPropertyListObject,ObjectPropertyList> ObjectPropertyListPackets = new ConcurrentDictionary<IPropertyListObject,ObjectPropertyList>();
private static ConcurrentDictionary<IEntity,RemoveEntity> RemoveEntityPackets = new ConcurrentDictionary<IEntity,RemoveEntity>();
private static ConcurrentDictionary<Item,WorldItem> WorldItemPackets = new ConcurrentDictionary<Item,WorldItem>();
private static ConcurrentDictionary<Item,WorldItemSA> WorldItemSAPackets = new ConcurrentDictionary<Item,WorldItemSA>();
private static ConcurrentDictionary<Item,WorldItemHS> WorldItemHSPackets = new ConcurrentDictionary<Item,WorldItemHS>();
public static OPLInfo GetOPLInfoPacket(IPropertyListObject obj)
{
return OPLInfoPackets.GetOrAdd(obj, value =>
{
OPLInfo packet = new OPLInfo(value.PropertyList);
packet.SetStatic();
return packet;
});
}
public static OPLInfo FreeOPLInfoPacket(IPropertyListObject obj)
{
if (OPLInfoPackets.TryRemove(obj, out OPLInfo p))
Packet.Release(p);
return p;
}
public static ObjectPropertyList GetOPLPacket(IPropertyListObject obj)
{
return ObjectPropertyListPackets.GetOrAdd(obj, value =>
{
ObjectPropertyList list = new ObjectPropertyList(value);
value.GetProperties(list);
if (value is Item item)
item.AppendChildProperties(list);
list.Terminate();
list.SetStatic();
return list;
});
}
public static ObjectPropertyList FreeOPLPacket(IPropertyListObject obj)
{
if (ObjectPropertyListPackets.TryRemove(obj, out ObjectPropertyList list))
Packet.Release(list);
return list;
}
public static RemoveEntity GetRemoveEntityPacket(IEntity entity)
{
return RemoveEntityPackets.GetOrAdd(entity, value =>
{
RemoveEntity packet = new RemoveEntity(value);
packet.SetStatic();
return packet;
});
}
public static void FreeRemoveItemPacket(IEntity entity)
{
if (RemoveEntityPackets.TryRemove(entity, out RemoveEntity p))
Packet.Release(p);
}
public static WorldItem GetWorldItemPacket(Item item)
{
return WorldItemPackets.GetOrAdd(item, value =>
{
WorldItem packet = new WorldItem(value);
packet.SetStatic();
return packet;
});
}
public static WorldItemSA GetWorldItemSAPacket(Item item)
{
return WorldItemSAPackets.GetOrAdd(item, value =>
{
WorldItemSA packet = new WorldItemSA(value);
packet.SetStatic();
return packet;
});
}
public static WorldItemHS GetWorldItemHSPacket(Item item)
{
return WorldItemHSPackets.GetOrAdd(item, value =>
{
WorldItemHS packet = new WorldItemHS(value);
packet.SetStatic();
return packet;
});
}
public static void FreeWorldItemPackets(Item item)
{
if (WorldItemPackets.TryRemove(item, out WorldItem wi))
Packet.Release(wi);
if (WorldItemSAPackets.TryRemove(item, out WorldItemSA wisa))
Packet.Release(wisa);
if (WorldItemHSPackets.TryRemove(item, out WorldItemHS wihs))
Packet.Release(wihs);
}
}
}

View file

@ -0,0 +1,62 @@
/***************************************************************************
* Notoriety.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server
{
public delegate int NotorietyHandler(Mobile source, Mobile target);
public static class Notoriety
{
public const int Innocent = 1;
public const int Ally = 2;
public const int CanBeAttacked = 3;
public const int Criminal = 4;
public const int Enemy = 5;
public const int Murderer = 6;
public const int Invulnerable = 7;
public static NotorietyHandler Handler{ get; set; }
public static int[] Hues{ get; set; } =
{
0x000,
0x059,
0x03F,
0x3B2,
0x3B2,
0x090,
0x022,
0x035
};
public static int GetHue(int noto)
{
if (noto < 0 || noto >= Hues.Length)
return 0;
return Hues[noto];
}
public static int Compute(Mobile source, Mobile target)
{
return Handler?.Invoke(source, target) ?? CanBeAttacked;
}
}
}

View file

@ -0,0 +1,202 @@
/***************************************************************************
* ObjectPropertyList.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.IO;
using System.Text;
using Server.Network;
namespace Server
{
public interface IPropertyListObject : IEntity
{
ObjectPropertyList PropertyList{ get; }
OPLInfo OPLPacket{ get; }
void GetProperties(ObjectPropertyList list);
}
public sealed class ObjectPropertyList : Packet
{
private static byte[] m_Buffer = new byte[1024];
private static Encoding m_Encoding = Encoding.Unicode;
// Each of these are localized to "~1_NOTHING~" which allows the string argument to be used
private static int[] m_StringNumbers =
{
1042971,
1070722
};
private int m_Hash;
private int m_Strings;
public ObjectPropertyList(IEntity e) : base(0xD6)
{
EnsureCapacity(128);
Entity = e;
m_Stream.Write((short)1);
m_Stream.Write(e.Serial);
m_Stream.Write((byte)0);
m_Stream.Write((byte)0);
m_Stream.Write(e.Serial);
}
public IEntity Entity{ get; }
public int Hash => 0x40000000 + m_Hash;
public int Header{ get; set; }
public string HeaderArgs{ get; set; }
public static bool Enabled{ get; set; }
public void Add(int number)
{
if (number == 0)
return;
AddHash(number);
if (Header == 0)
{
Header = number;
HeaderArgs = "";
}
m_Stream.Write(number);
m_Stream.Write((short)0);
}
public void Terminate()
{
m_Stream.Write(0);
m_Stream.Seek(11, SeekOrigin.Begin);
m_Stream.Write(m_Hash);
}
public void AddHash(int val)
{
m_Hash ^= val & 0x3FFFFFF;
m_Hash ^= (val >> 26) & 0x3F;
}
public void Add(int number, string arguments)
{
if (number == 0)
return;
if (arguments == null)
arguments = "";
if (Header == 0)
{
Header = number;
HeaderArgs = arguments;
}
AddHash(number);
AddHash(arguments.GetHashCode());
m_Stream.Write(number);
int byteCount = m_Encoding.GetByteCount(arguments);
if (byteCount > m_Buffer.Length)
m_Buffer = new byte[byteCount];
byteCount = m_Encoding.GetBytes(arguments, 0, arguments.Length, m_Buffer, 0);
m_Stream.Write((short)byteCount);
m_Stream.Write(m_Buffer, 0, byteCount);
}
public void Add(int number, string format, object arg0)
{
Add(number, string.Format(format, arg0));
}
public void Add(int number, string format, object arg0, object arg1)
{
Add(number, string.Format(format, arg0, arg1));
}
public void Add(int number, string format, object arg0, object arg1, object arg2)
{
Add(number, string.Format(format, arg0, arg1, arg2));
}
public void Add(int number, string format, params object[] args)
{
Add(number, string.Format(format, args));
}
private int GetStringNumber()
{
return m_StringNumbers[m_Strings++ % m_StringNumbers.Length];
}
public void Add(string text)
{
Add(GetStringNumber(), text);
}
public void Add(string format, string arg0)
{
Add(GetStringNumber(), string.Format(format, arg0));
}
public void Add(string format, string arg0, string arg1)
{
Add(GetStringNumber(), string.Format(format, arg0, arg1));
}
public void Add(string format, string arg0, string arg1, string arg2)
{
Add(GetStringNumber(), string.Format(format, arg0, arg1, arg2));
}
public void Add(string format, params object[] args)
{
Add(GetStringNumber(), string.Format(format, args));
}
}
public sealed class OPLInfo : Packet
{
/*public OPLInfo( ObjectPropertyList list ) : base( 0xBF )
{
EnsureCapacity( 13 );
m_Stream.Write( (short) 0x10 );
m_Stream.Write( (int) list.Entity.Serial );
m_Stream.Write( (int) list.Hash );
}*/
public OPLInfo(ObjectPropertyList list) : base(0xDC, 9)
{
m_Stream.Write(list.Entity.Serial);
m_Stream.Write(list.Hash);
}
}
}

35
Projects/Server/Party.cs Normal file
View file

@ -0,0 +1,35 @@
/***************************************************************************
* Party.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server
{
public abstract class PartyCommands
{
public static PartyCommands Handler{ get; set; }
public abstract void OnAdd(Mobile from);
public abstract void OnRemove(Mobile from, Mobile target);
public abstract void OnPrivateMessage(Mobile from, Mobile target, string text);
public abstract void OnPublicMessage(Mobile from, string text);
public abstract void OnSetCanLoot(Mobile from, bool canLoot);
public abstract void OnAccept(Mobile from, Mobile leader);
public abstract void OnDecline(Mobile from, Mobile leader);
}
}

View file

@ -0,0 +1,82 @@
/***************************************************************************
* BinaryMemoryWriter.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.IO;
namespace Server
{
public sealed class BinaryMemoryWriter : BinaryFileWriter
{
private static byte[] indexBuffer;
private MemoryStream stream;
public BinaryMemoryWriter()
: base(new MemoryStream(512), true)
{
stream = UnderlyingStream as MemoryStream;
}
protected override int BufferSize => 512;
public int CommitTo(SequentialFileWriter dataFile, SequentialFileWriter indexFile, int typeCode, uint serial)
{
Flush();
byte[] buffer = stream.GetBuffer();
int length = (int)stream.Length;
long position = dataFile.Position;
dataFile.Write(buffer, 0, length);
if (indexBuffer == null) indexBuffer = new byte[20];
indexBuffer[0] = (byte)typeCode;
indexBuffer[1] = (byte)(typeCode >> 8);
indexBuffer[2] = (byte)(typeCode >> 16);
indexBuffer[3] = (byte)(typeCode >> 24);
indexBuffer[4] = (byte)serial;
indexBuffer[5] = (byte)(serial >> 8);
indexBuffer[6] = (byte)(serial >> 16);
indexBuffer[7] = (byte)(serial >> 24);
indexBuffer[8] = (byte)position;
indexBuffer[9] = (byte)(position >> 8);
indexBuffer[10] = (byte)(position >> 16);
indexBuffer[11] = (byte)(position >> 24);
indexBuffer[12] = (byte)(position >> 32);
indexBuffer[13] = (byte)(position >> 40);
indexBuffer[14] = (byte)(position >> 48);
indexBuffer[15] = (byte)(position >> 56);
indexBuffer[16] = (byte)length;
indexBuffer[17] = (byte)(length >> 8);
indexBuffer[18] = (byte)(length >> 16);
indexBuffer[19] = (byte)(length >> 24);
indexFile.Write(indexBuffer, 0, indexBuffer.Length);
stream.SetLength(0);
return length;
}
}
}

View file

@ -0,0 +1,48 @@
/***************************************************************************
* DualSaveStrategy.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Threading;
namespace Server
{
public sealed class DualSaveStrategy : StandardSaveStrategy
{
public override string Name => "Dual";
public override void Save(bool permitBackgroundWrite)
{
PermitBackgroundWrite = permitBackgroundWrite;
Thread saveThread = new Thread(delegate() { SaveItems(); });
saveThread.Name = "Item Save Subset";
saveThread.Start();
SaveMobiles();
SaveGuilds();
saveThread.Join();
if (permitBackgroundWrite && UseSequentialWriters
) //If we're permitted to write in the background, but we don't anyways, then notify.
World.NotifyDiskWriteComplete();
}
}
}

View file

@ -0,0 +1,278 @@
/***************************************************************************
* DynamicSaveStrategy.cs
* -------------------
* begin : December 16, 2010
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using Server.Guilds;
namespace Server
{
public sealed class DynamicSaveStrategy : SaveStrategy
{
private ConcurrentBag<Item> _decayBag;
private SequentialFileWriter _guildData, _guildIndex;
private BlockingCollection<QueuedMemoryWriter> _guildThreadWriters;
private SequentialFileWriter _itemData, _itemIndex;
private BlockingCollection<QueuedMemoryWriter> _itemThreadWriters;
private SequentialFileWriter _mobileData, _mobileIndex;
private BlockingCollection<QueuedMemoryWriter> _mobileThreadWriters;
public DynamicSaveStrategy()
{
_decayBag = new ConcurrentBag<Item>();
_itemThreadWriters = new BlockingCollection<QueuedMemoryWriter>();
_mobileThreadWriters = new BlockingCollection<QueuedMemoryWriter>();
_guildThreadWriters = new BlockingCollection<QueuedMemoryWriter>();
}
public override string Name => "Dynamic";
public override void Save(bool permitBackgroundWrite)
{
OpenFiles();
Task[] saveTasks = new Task[3];
saveTasks[0] = SaveItems();
saveTasks[1] = SaveMobiles();
saveTasks[2] = SaveGuilds();
SaveTypeDatabases();
if (permitBackgroundWrite)
{
//This option makes it finish the writing to disk in the background, continuing even after Save() returns.
Task.Factory.ContinueWhenAll(saveTasks, _ =>
{
CloseFiles();
World.NotifyDiskWriteComplete();
});
}
else
{
Task.WaitAll(saveTasks); //Waits for the completion of all of the tasks(committing to disk)
CloseFiles();
}
}
private Task StartCommitTask(BlockingCollection<QueuedMemoryWriter> threadWriter, SequentialFileWriter data,
SequentialFileWriter index)
{
Task commitTask = Task.Factory.StartNew(() =>
{
while (!threadWriter.IsCompleted)
{
QueuedMemoryWriter writer;
try
{
writer = threadWriter.Take();
}
catch (InvalidOperationException)
{
//Per MSDN, it's fine if we're here, successful completion of adding can rarely put us into this state.
break;
}
writer.CommitTo(data, index);
}
});
return commitTask;
}
private Task SaveItems()
{
//Start the blocking consumer; this runs in background.
Task commitTask = StartCommitTask(_itemThreadWriters, _itemData, _itemIndex);
IEnumerable<Item> items = World.Items.Values;
//Start the producer.
Parallel.ForEach(items, () => new QueuedMemoryWriter(),
(item, state, writer) =>
{
long startPosition = writer.Position;
item.Serialize(writer);
int size = (int)(writer.Position - startPosition);
writer.QueueForIndex(item, size);
if (item.Decays && item.Parent == null && item.Map != Map.Internal &&
DateTime.UtcNow > item.LastMoved + item.DecayTime) _decayBag.Add(item);
return writer;
},
writer =>
{
writer.Flush();
_itemThreadWriters.Add(writer);
});
_itemThreadWriters.CompleteAdding(); //We only get here after the Parallel.ForEach completes. Lets our task
return commitTask;
}
private Task SaveMobiles()
{
//Start the blocking consumer; this runs in background.
Task commitTask = StartCommitTask(_mobileThreadWriters, _mobileData, _mobileIndex);
IEnumerable<Mobile> mobiles = World.Mobiles.Values;
//Start the producer.
Parallel.ForEach(mobiles, () => new QueuedMemoryWriter(),
(mobile, state, writer) =>
{
long startPosition = writer.Position;
mobile.Serialize(writer);
int size = (int)(writer.Position - startPosition);
writer.QueueForIndex(mobile, size);
return writer;
},
writer =>
{
writer.Flush();
_mobileThreadWriters.Add(writer);
});
_mobileThreadWriters
.CompleteAdding(); //We only get here after the Parallel.ForEach completes. Lets our task tell the consumer that we're done
return commitTask;
}
private Task SaveGuilds()
{
//Start the blocking consumer; this runs in background.
Task commitTask = StartCommitTask(_guildThreadWriters, _guildData, _guildIndex);
IEnumerable<BaseGuild> guilds = BaseGuild.List.Values;
//Start the producer.
Parallel.ForEach(guilds, () => new QueuedMemoryWriter(),
(guild, state, writer) =>
{
long startPosition = writer.Position;
guild.Serialize(writer);
int size = (int)(writer.Position - startPosition);
writer.QueueForIndex(guild, size);
return writer;
},
writer =>
{
writer.Flush();
_guildThreadWriters.Add(writer);
});
_guildThreadWriters.CompleteAdding(); //We only get here after the Parallel.ForEach completes. Lets our task
return commitTask;
}
public override void ProcessDecay()
{
while (_decayBag.TryTake(out Item item))
if (item.OnDecay())
item.Delete();
}
private void OpenFiles()
{
_itemData = new SequentialFileWriter(World.ItemDataPath);
_itemIndex = new SequentialFileWriter(World.ItemIndexPath);
_mobileData = new SequentialFileWriter(World.MobileDataPath);
_mobileIndex = new SequentialFileWriter(World.MobileIndexPath);
_guildData = new SequentialFileWriter(World.GuildDataPath);
_guildIndex = new SequentialFileWriter(World.GuildIndexPath);
WriteCount(_itemIndex, World.Items.Count);
WriteCount(_mobileIndex, World.Mobiles.Count);
WriteCount(_guildIndex, BaseGuild.List.Count);
}
private void CloseFiles()
{
_itemData.Close();
_itemIndex.Close();
_mobileData.Close();
_mobileIndex.Close();
_guildData.Close();
_guildIndex.Close();
}
private void WriteCount(SequentialFileWriter indexFile, int count)
{
//Equiv to GenericWriter.Write( (int)count );
byte[] buffer = new byte[4];
buffer[0] = (byte)count;
buffer[1] = (byte)(count >> 8);
buffer[2] = (byte)(count >> 16);
buffer[3] = (byte)(count >> 24);
indexFile.Write(buffer, 0, buffer.Length);
}
private void SaveTypeDatabases()
{
SaveTypeDatabase(World.ItemTypesPath, World.m_ItemTypes);
SaveTypeDatabase(World.MobileTypesPath, World.m_MobileTypes);
}
private void SaveTypeDatabase(string path, List<Type> types)
{
BinaryFileWriter bfw = new BinaryFileWriter(path, false);
bfw.Write(types.Count);
foreach (Type type in types) bfw.Write(type.FullName);
bfw.Flush();
bfw.Close();
}
}
}

View file

@ -0,0 +1,113 @@
/***************************************************************************
* FileOperations.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.IO;
#if !MONO
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
#endif
namespace Server
{
public static class FileOperations
{
public const int KB = 1024;
public const int MB = 1024 * KB;
public static int BufferSize{ get; set; } = 1 * MB;
public static int Concurrency{ get; set; } = 1;
public static bool Unbuffered{ get; set; } = true;
public static bool AreSynchronous => Concurrency < 1;
public static bool AreAsynchronous => Concurrency > 0;
public static FileStream OpenSequentialStream(string path, FileMode mode, FileAccess access, FileShare share)
{
FileOptions options = FileOptions.SequentialScan;
if (Concurrency > 0)
options |= FileOptions.Asynchronous;
#if MONO
return new FileStream( path, mode, access, share, BufferSize, options );
#else
if (Unbuffered)
options |= NoBuffering;
else
return new FileStream(path, mode, access, share, BufferSize, options);
SafeFileHandle fileHandle =
UnsafeNativeMethods.CreateFile(path, (int)access, share, IntPtr.Zero, mode, (int)options, IntPtr.Zero);
if (fileHandle.IsInvalid) throw new IOException();
return new UnbufferedFileStream(fileHandle, access, BufferSize, Concurrency > 0);
#endif
}
#if !MONO
private class UnbufferedFileStream : FileStream
{
private SafeFileHandle fileHandle;
public UnbufferedFileStream(SafeFileHandle fileHandle, FileAccess access, int bufferSize, bool isAsync)
: base(fileHandle, access, bufferSize, isAsync)
{
this.fileHandle = fileHandle;
}
public override void Write(byte[] array, int offset, int count)
{
base.Write(array, offset, BufferSize);
}
public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback userCallback,
object stateObject)
{
return base.BeginWrite(array, offset, BufferSize, userCallback, stateObject);
}
protected override void Dispose(bool disposing)
{
if (!fileHandle.IsClosed) fileHandle.Close();
base.Dispose(disposing);
}
}
#endif
#if !MONO
private const FileOptions NoBuffering = (FileOptions)0x20000000;
internal static class UnsafeNativeMethods
{
[DllImport("Kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, FileShare dwShareMode,
IntPtr securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
}
#endif
}
}

View file

@ -0,0 +1,231 @@
/***************************************************************************
* FileQueue.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Threading;
namespace Server
{
public delegate void FileCommitCallback(FileQueue.Chunk chunk);
public sealed class FileQueue : IDisposable
{
private static int bufferSize;
private Chunk[] active;
private int activeCount;
private Page buffered;
private FileCommitCallback callback;
private ManualResetEvent idle;
private Queue<Page> pending;
private object syncRoot;
static FileQueue()
{
bufferSize = FileOperations.BufferSize;
}
public FileQueue(int concurrentWrites, FileCommitCallback callback)
{
if (concurrentWrites < 1) throw new ArgumentOutOfRangeException("concurrentWrites");
if (bufferSize < 1)
throw new ArgumentOutOfRangeException("bufferSize");
syncRoot = new object();
active = new Chunk[concurrentWrites];
pending = new Queue<Page>();
this.callback = callback;
idle = new ManualResetEvent(true);
}
public long Position{ get; private set; }
public void Dispose()
{
if (idle != null)
{
idle.Close();
idle = null;
}
}
private void Append(Page page)
{
lock (syncRoot)
{
if (activeCount == 0) idle.Reset();
++activeCount;
for (int slot = 0; slot < active.Length; ++slot)
if (active[slot] == null)
{
active[slot] = new Chunk(this, slot, page.buffer, 0, page.length);
callback(active[slot]);
return;
}
pending.Enqueue(page);
}
}
public void Flush()
{
if (buffered.buffer != null)
{
Append(buffered);
buffered.buffer = null;
buffered.length = 0;
}
/*lock ( syncRoot ) {
if ( pending.Count > 0 ) {
idle.Reset();
}
for ( int slot = 0; slot < active.Length && pending.Count > 0; ++slot ) {
if ( active[slot] == null ) {
Page page = pending.Dequeue();
active[slot] = new Chunk( this, slot, page.buffer, 0, page.length );
++activeCount;
callback( active[slot] );
}
}
}*/
idle.WaitOne();
}
private void Commit(Chunk chunk, int slot)
{
if (slot < 0 || slot >= active.Length) throw new ArgumentOutOfRangeException("slot");
lock (syncRoot)
{
if (active[slot] != chunk) throw new ArgumentException();
ArrayPool<byte>.Shared.Return(chunk.Buffer);
if (pending.Count > 0)
{
Page page = pending.Dequeue();
active[slot] = new Chunk(this, slot, page.buffer, 0, page.length);
callback(active[slot]);
}
else
{
active[slot] = null;
}
--activeCount;
if (activeCount == 0) idle.Set();
}
}
public void Enqueue(byte[] buffer, int offset, int size)
{
if (buffer == null) throw new ArgumentNullException("buffer");
if (offset < 0) throw new ArgumentOutOfRangeException("offset");
if (size < 0) throw new ArgumentOutOfRangeException("size");
if (buffer.Length - offset < size) throw new ArgumentException();
Position += size;
while (size > 0)
{
if (buffered.buffer == null)
buffered.buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
byte[] page = buffered.buffer; // buffer page
int pageSpace = page.Length - buffered.length; // available bytes in page
int byteCount = size > pageSpace ? pageSpace : size; // how many bytes we can copy over
Buffer.BlockCopy(buffer, offset, page, buffered.length, byteCount);
buffered.length += byteCount;
offset += byteCount;
size -= byteCount;
if (buffered.length == page.Length)
{
// page full
Append(buffered);
buffered.buffer = null;
buffered.length = 0;
}
}
}
public sealed class Chunk
{
private int offset;
private FileQueue owner;
private int slot;
public Chunk(FileQueue owner, int slot, byte[] buffer, int offset, int size)
{
this.owner = owner;
this.slot = slot;
Buffer = buffer;
this.offset = offset;
Size = size;
}
public byte[] Buffer{ get; }
public int Offset => 0;
public int Size{ get; }
public void Commit()
{
owner.Commit(this, slot);
}
}
private struct Page
{
public byte[] buffer;
public int length;
}
}
}

View file

@ -0,0 +1,326 @@
/***************************************************************************
* ParallelSaveStrategy.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using Server.Guilds;
namespace Server
{
public sealed class ParallelSaveStrategy : SaveStrategy
{
private Queue<Item> _decayQueue;
private Consumer[] consumers;
private int cycle;
private bool finished;
private SequentialFileWriter guildData, guildIndex;
private SequentialFileWriter itemData, itemIndex;
private SequentialFileWriter mobileData, mobileIndex;
private int processorCount;
public ParallelSaveStrategy(int processorCount)
{
this.processorCount = processorCount;
_decayQueue = new Queue<Item>();
}
public override string Name => "Parallel";
private int GetThreadCount()
{
return processorCount - 1;
}
public override void Save(bool permitBackgroundWrite)
{
OpenFiles();
consumers = new Consumer[GetThreadCount()];
for (int i = 0; i < consumers.Length; ++i) consumers[i] = new Consumer(this, 256);
IEnumerable<ISerializable> collection = new Producer();
foreach (ISerializable value in collection)
while (!Enqueue(value))
if (!Commit())
Thread.Sleep(0);
finished = true;
SaveTypeDatabases();
WaitHandle.WaitAll(
Array.ConvertAll<Consumer, WaitHandle>(
consumers,
input => input.completionEvent
)
);
Commit();
CloseFiles();
}
public override void ProcessDecay()
{
while (_decayQueue.Count > 0)
{
Item item = _decayQueue.Dequeue();
if (item.OnDecay()) item.Delete();
}
}
private void SaveTypeDatabases()
{
SaveTypeDatabase(World.ItemTypesPath, World.m_ItemTypes);
SaveTypeDatabase(World.MobileTypesPath, World.m_MobileTypes);
}
private void SaveTypeDatabase(string path, List<Type> types)
{
BinaryFileWriter bfw = new BinaryFileWriter(path, false);
bfw.Write(types.Count);
foreach (Type type in types) bfw.Write(type.FullName);
bfw.Flush();
bfw.Close();
}
private void OpenFiles()
{
itemData = new SequentialFileWriter(World.ItemDataPath);
itemIndex = new SequentialFileWriter(World.ItemIndexPath);
mobileData = new SequentialFileWriter(World.MobileDataPath);
mobileIndex = new SequentialFileWriter(World.MobileIndexPath);
guildData = new SequentialFileWriter(World.GuildDataPath);
guildIndex = new SequentialFileWriter(World.GuildIndexPath);
WriteCount(itemIndex, World.Items.Count);
WriteCount(mobileIndex, World.Mobiles.Count);
WriteCount(guildIndex, BaseGuild.List.Count);
}
private void WriteCount(SequentialFileWriter indexFile, int count)
{
byte[] buffer = new byte[4];
buffer[0] = (byte)count;
buffer[1] = (byte)(count >> 8);
buffer[2] = (byte)(count >> 16);
buffer[3] = (byte)(count >> 24);
indexFile.Write(buffer, 0, buffer.Length);
}
private void CloseFiles()
{
itemData.Close();
itemIndex.Close();
mobileData.Close();
mobileIndex.Close();
guildData.Close();
guildIndex.Close();
World.NotifyDiskWriteComplete();
}
private void OnSerialized(ConsumableEntry entry)
{
ISerializable value = entry.value;
BinaryMemoryWriter writer = entry.writer;
if (value is Item item)
Save(item, writer);
else if (value is Mobile mob)
Save(mob, writer);
else if (value is BaseGuild guild)
Save(guild, writer);
}
private void Save(Item item, BinaryMemoryWriter writer)
{
writer.CommitTo(itemData, itemIndex, item.m_TypeRef, item.Serial);
if (item.Decays && item.Parent == null && item.Map != Map.Internal &&
DateTime.UtcNow > item.LastMoved + item.DecayTime) _decayQueue.Enqueue(item);
}
private void Save(Mobile mob, BinaryMemoryWriter writer)
{
writer.CommitTo(mobileData, mobileIndex, mob.m_TypeRef, mob.Serial);
}
private void Save(BaseGuild guild, BinaryMemoryWriter writer)
{
writer.CommitTo(guildData, guildIndex, 0, guild.Id);
}
private bool Enqueue(ISerializable value)
{
for (int i = 0; i < consumers.Length; ++i)
{
Consumer consumer = consumers[cycle++ % consumers.Length];
if (consumer.tail - consumer.head < consumer.buffer.Length)
{
consumer.buffer[consumer.tail % consumer.buffer.Length].value = value;
consumer.tail++;
return true;
}
}
return false;
}
private bool Commit()
{
bool committed = false;
for (int i = 0; i < consumers.Length; ++i)
{
Consumer consumer = consumers[i];
while (consumer.head < consumer.done)
{
OnSerialized(consumer.buffer[consumer.head % consumer.buffer.Length]);
consumer.head++;
committed = true;
}
}
return committed;
}
private sealed class Producer : IEnumerable<ISerializable>
{
private IEnumerable<BaseGuild> guilds;
private IEnumerable<Item> items;
private IEnumerable<Mobile> mobiles;
public Producer()
{
items = World.Items.Values;
mobiles = World.Mobiles.Values;
guilds = BaseGuild.List.Values;
}
public IEnumerator<ISerializable> GetEnumerator()
{
foreach (Item item in items) yield return item;
foreach (Mobile mob in mobiles) yield return mob;
foreach (BaseGuild guild in guilds) yield return guild;
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
private struct ConsumableEntry
{
public ISerializable value;
public BinaryMemoryWriter writer;
}
private sealed class Consumer
{
public ConsumableEntry[] buffer;
public ManualResetEvent completionEvent;
public int head, done, tail;
private ParallelSaveStrategy owner;
private Thread thread;
public Consumer(ParallelSaveStrategy owner, int bufferSize)
{
this.owner = owner;
buffer = new ConsumableEntry[bufferSize];
for (int i = 0; i < buffer.Length; ++i) buffer[i].writer = new BinaryMemoryWriter();
completionEvent = new ManualResetEvent(false);
thread = new Thread(Processor);
thread.Name = "Parallel Serialization Thread";
thread.Start();
}
private void Processor()
{
try
{
while (!owner.finished)
{
Process();
Thread.Sleep(0);
}
Process();
completionEvent.Set();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private void Process()
{
ConsumableEntry entry;
while (done < tail)
{
entry = buffer[done % buffer.Length];
entry.value.Serialize(entry.writer);
++done;
}
}
}
}
}

View file

@ -0,0 +1,107 @@
#region References
using System;
using System.IO;
#endregion
namespace Server
{
public static class Persistence
{
public static void Serialize(string path, Action<GenericWriter> serializer)
{
Serialize(new FileInfo(path), serializer);
}
public static void Serialize(FileInfo file, Action<GenericWriter> serializer)
{
file.Refresh();
if (file.Directory?.Exists == false)
file.Directory.Create();
if (!file.Exists) file.Create().Close();
file.Refresh();
using (FileStream fs = file.OpenWrite())
{
BinaryFileWriter writer = new BinaryFileWriter(fs, true);
try
{
serializer(writer);
}
finally
{
writer.Flush();
writer.Close();
}
}
}
public static void Deserialize(string path, Action<GenericReader> deserializer)
{
Deserialize(path, deserializer, true);
}
public static void Deserialize(FileInfo file, Action<GenericReader> deserializer)
{
Deserialize(file, deserializer, true);
}
public static void Deserialize(string path, Action<GenericReader> deserializer, bool ensure)
{
Deserialize(new FileInfo(path), deserializer, ensure);
}
public static void Deserialize(FileInfo file, Action<GenericReader> deserializer, bool ensure)
{
file.Refresh();
if (file.Directory?.Exists == false)
{
if (!ensure)
throw new DirectoryNotFoundException();
file.Directory.Create();
}
if (!file.Exists)
{
if (!ensure)
throw new FileNotFoundException
{
Source = file.FullName
};
file.Create().Close();
}
file.Refresh();
using (FileStream fs = file.OpenRead())
{
BinaryFileReader reader = new BinaryFileReader(new BinaryReader(fs));
try
{
deserializer(reader);
}
catch (EndOfStreamException eos)
{
if (file.Length > 0) Console.WriteLine("[Persistence]: {0}", eos);
}
catch (Exception e)
{
Console.WriteLine("[Persistence]: {0}", e);
}
finally
{
reader.Close();
}
}
}
}
}

View file

@ -0,0 +1,116 @@
/***************************************************************************
* QueuedMemoryWriter.cs
* -------------------
* begin : December 16, 2010
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
using System.Collections.Generic;
using System.IO;
namespace Server
{
public sealed class QueuedMemoryWriter : BinaryFileWriter
{
private MemoryStream _memStream;
private List<IndexInfo> _orderedIndexInfo = new List<IndexInfo>();
public QueuedMemoryWriter()
: base(new MemoryStream(1024 * 1024), true)
{
_memStream = UnderlyingStream as MemoryStream;
}
protected override int BufferSize => 512;
public void QueueForIndex(ISerializable serializable, int size)
{
IndexInfo info;
info.size = size;
info.typeCode = serializable.TypeReference; //For guilds, this will automagically be zero.
info.serial = serializable.SerialIdentity;
_orderedIndexInfo.Add(info);
}
public void CommitTo(SequentialFileWriter dataFile, SequentialFileWriter indexFile)
{
Flush();
int memLength = (int)_memStream.Position;
if (memLength > 0)
{
byte[] memBuffer = _memStream.GetBuffer();
long actualPosition = dataFile.Position;
dataFile.Write(memBuffer, 0, memLength); //The buffer contains the data from many items.
//Console.WriteLine("Writing {0} bytes starting at {1}, with {2} things", memLength, actualPosition, _orderedIndexInfo.Count);
byte[] indexBuffer = new byte[20];
//int indexWritten = _orderedIndexInfo.Count * indexBuffer.Length;
//int totalWritten = memLength + indexWritten
for (int i = 0; i < _orderedIndexInfo.Count; i++)
{
IndexInfo info = _orderedIndexInfo[i];
indexBuffer[0] = (byte)info.typeCode;
indexBuffer[1] = (byte)(info.typeCode >> 8);
indexBuffer[2] = (byte)(info.typeCode >> 16);
indexBuffer[3] = (byte)(info.typeCode >> 24);
indexBuffer[4] = (byte)info.serial;
indexBuffer[5] = (byte)(info.serial >> 8);
indexBuffer[6] = (byte)(info.serial >> 16);
indexBuffer[7] = (byte)(info.serial >> 24);
indexBuffer[8] = (byte)actualPosition;
indexBuffer[9] = (byte)(actualPosition >> 8);
indexBuffer[10] = (byte)(actualPosition >> 16);
indexBuffer[11] = (byte)(actualPosition >> 24);
indexBuffer[12] = (byte)(actualPosition >> 32);
indexBuffer[13] = (byte)(actualPosition >> 40);
indexBuffer[14] = (byte)(actualPosition >> 48);
indexBuffer[15] = (byte)(actualPosition >> 56);
indexBuffer[16] = (byte)info.size;
indexBuffer[17] = (byte)(info.size >> 8);
indexBuffer[18] = (byte)(info.size >> 16);
indexBuffer[19] = (byte)(info.size >> 24);
indexFile.Write(indexBuffer, 0, indexBuffer.Length);
actualPosition += info.size;
}
}
Close(); //We're done with this writer.
}
private struct IndexInfo
{
public int size;
public int typeCode;
public uint serial;
}
}
}

View file

@ -0,0 +1,47 @@
/***************************************************************************
* SaveStrategy.cs
* -------------------
* begin : May 1, 2002
* copyright : (C) The RunUO Software Team
* email : info@runuo.com
*
* $Id$
*
***************************************************************************/
/***************************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
***************************************************************************/
namespace Server
{
public abstract class SaveStrategy
{
public abstract string Name{ get; }
public static SaveStrategy Acquire()
{
if (Core.MultiProcessor)
{
int processorCount = Core.ProcessorCount;
if (processorCount > 2)
return
new DualSaveStrategy(); // return new DynamicSaveStrategy(); (4.0 or return new ParallelSaveStrategy(processorCount); (2.0)
return new DualSaveStrategy();
}
return new StandardSaveStrategy();
}
public abstract void Save(bool permitBackgroundWrite);
public abstract void ProcessDecay();
}
}

Some files were not shown because too many files have changed in this diff Show more