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

1958 lines
48 KiB
C#

#region References
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using Server.Commands;
using Server.Items;
using Server.Misc;
using Server.Mobiles;
using Server.Multis;
using Server.Network;
#endregion
namespace Server.Accounting
{
[PropertyObject]
public class Account : IAccount, IComparable, IComparable<Account>
{
public static readonly TimeSpan YoungDuration = TimeSpan.FromHours(40.0);
public static readonly TimeSpan InactiveDuration = TimeSpan.FromDays(180.0);
public static readonly TimeSpan EmptyInactiveDuration = TimeSpan.FromDays(30.0);
private static MD5CryptoServiceProvider m_MD5HashProvider;
private static SHA1CryptoServiceProvider m_SHA1HashProvider;
private static SHA512CryptoServiceProvider m_SHA512HashProvider;
private static byte[] m_HashBuffer;
public static void Configure()
{
CommandSystem.Register("ConvertCurrency", AccessLevel.Owner, ConvertCurrency);
}
private static void ConvertCurrency(CommandEventArgs e)
{
e.Mobile.SendMessage(
"Converting All Banked Gold from {0} to {1}. Please wait...",
AccountGold.Enabled ? "checks and coins" : "account treasury",
AccountGold.Enabled ? "account treasury" : "checks and coins");
NetState.Pause();
double found = 0.0, converted = 0.0;
try
{
BankBox box;
List<Gold> gold;
List<BankCheck> checks;
long share = 0, shared;
int diff;
foreach (var a in Accounts.GetAccounts().OfType<Account>().Where(a => a.Count > 0))
{
try
{
if (!AccountGold.Enabled)
{
share = (int)Math.Truncate((a.TotalCurrency / a.Count) * CurrencyThreshold);
found += a.TotalCurrency * CurrencyThreshold;
}
foreach (var m in a.m_Mobiles.Where(m => m != null))
{
box = m.FindBankNoCreate();
if (box == null)
{
continue;
}
if (AccountGold.Enabled)
{
foreach (var o in checks = box.FindItemsByType<BankCheck>())
{
found += o.Worth;
if (!a.DepositGold(o.Worth))
{
break;
}
converted += o.Worth;
o.Delete();
}
checks.Clear();
checks.TrimExcess();
foreach (var o in gold = box.FindItemsByType<Gold>())
{
found += o.Amount;
if (!a.DepositGold(o.Amount))
{
break;
}
converted += o.Amount;
o.Delete();
}
gold.Clear();
gold.TrimExcess();
}
else
{
shared = share;
while (shared > 0)
{
if (shared > 60000)
{
diff = (int)Math.Min(10000000, shared);
if (a.WithdrawGold(diff))
{
box.DropItem(new BankCheck(diff));
}
else
{
break;
}
}
else
{
diff = (int)Math.Min(60000, shared);
if (a.WithdrawGold(diff))
{
box.DropItem(new Gold(diff));
}
else
{
break;
}
}
converted += diff;
shared -= diff;
}
}
box.UpdateTotals();
}
}
catch
{ }
}
}
catch
{ }
NetState.Resume();
e.Mobile.SendMessage("Operation complete: {0:#,0} of {1:#,0} Gold has been converted in total.", converted, found);
}
private readonly Mobile[] m_Mobiles;
private AccessLevel m_AccessLevel;
private List<AccountComment> m_Comments;
private List<AccountTag> m_Tags;
private TimeSpan m_TotalGameTime;
private Timer m_YoungTimer;
public Account(string username, string password)
{
Username = username;
SetPassword(password);
m_AccessLevel = AccessLevel.Player;
Created = LastLogin = DateTime.UtcNow;
m_TotalGameTime = TimeSpan.Zero;
m_Mobiles = new Mobile[7];
IPRestrictions = new string[0];
LoginIPs = new IPAddress[0];
Accounts.Add(this);
}
public Account(XmlElement node)
{
Username = Utility.GetText(node["username"], "empty");
var plainPassword = Utility.GetText(node["password"], null);
var MD5Password = Utility.GetText(node["cryptPassword"], null);
var SHA1Password = Utility.GetText(node["newCryptPassword"], null);
var SHA512Password = Utility.GetText(node["newSecureCryptPassword"], null);
switch (AccountHandler.ProtectPasswords)
{
case PasswordProtection.None:
{
if (plainPassword != null)
{
SetPassword(plainPassword);
}
else if (SHA512Password != null)
{
_SHA512Password = SHA512Password;
}
else if (SHA1Password != null)
{
_SHA1Password = SHA1Password;
}
else if (MD5Password != null)
{
_MD5Password = MD5Password;
}
else
{
SetPassword("empty");
}
break;
}
case PasswordProtection.Crypt:
{
if (MD5Password != null)
{
_MD5Password = MD5Password;
}
else if (plainPassword != null)
{
SetPassword(plainPassword);
}
else if (SHA1Password != null)
{
_SHA1Password = SHA1Password;
}
else if (SHA512Password != null)
{
_SHA512Password = SHA512Password;
}
else
{
SetPassword("empty");
}
break;
}
case PasswordProtection.NewCrypt:
{
if (SHA1Password != null)
{
_SHA1Password = SHA1Password;
}
else if (plainPassword != null)
{
SetPassword(plainPassword);
}
else if (MD5Password != null)
{
_MD5Password = MD5Password;
}
else if (SHA512Password != null)
{
_SHA512Password = SHA512Password;
}
else
{
SetPassword("empty");
}
break;
}
default: // PasswordProtection.NewSecureCrypt
{
if (SHA512Password != null)
{
_SHA512Password = SHA512Password;
}
else if (plainPassword != null)
{
SetPassword(plainPassword);
}
else if (SHA1Password != null)
{
_SHA1Password = SHA1Password;
}
else if (MD5Password != null)
{
_MD5Password = MD5Password;
}
else
{
SetPassword("empty");
}
break;
}
}
Enum.TryParse(Utility.GetText(node["accessLevel"], "Player"), true, out m_AccessLevel);
Flags = Utility.GetXMLInt32(Utility.GetText(node["flags"], "0"), 0);
Created = Utility.GetXMLDateTime(Utility.GetText(node["created"], null), DateTime.UtcNow);
LastLogin = Utility.GetXMLDateTime(Utility.GetText(node["lastLogin"], null), DateTime.UtcNow);
TotalCurrency = Utility.GetXMLDouble(Utility.GetText(node["totalCurrency"], "0"), 0);
Sovereigns = Utility.GetXMLInt32(Utility.GetText(node["sovereigns"], "0"), 0);
m_Mobiles = LoadMobiles(node);
m_Comments = LoadComments(node);
m_Tags = LoadTags(node);
LoginIPs = LoadAddressList(node);
IPRestrictions = LoadAccessCheck(node);
foreach (Mobile m in m_Mobiles.Where(m => m != null))
{
m.Account = this;
}
var totalGameTime = Utility.GetXMLTimeSpan(Utility.GetText(node["totalGameTime"], null), TimeSpan.Zero);
if (totalGameTime == TimeSpan.Zero)
{
totalGameTime = m_Mobiles.OfType<PlayerMobile>().Aggregate(totalGameTime, (current, m) => current + m.GameTime);
}
m_TotalGameTime = totalGameTime;
if (Young)
{
CheckYoung();
}
LoadSecureAccounts(node);
Accounts.Add(this);
}
/// <summary>
/// Deserializes a list of secure account balances, and converts it to a dictionary containing the account characters
/// </summary>
/// <param name="node"></param>
public void LoadSecureAccounts(XmlElement node)
{
int[] list = new int[7];
XmlElement chars = node["SecureAccounts"];
if (chars != null)
{
foreach (XmlElement ele in chars.GetElementsByTagName("char"))
{
try
{
int index = Utility.GetXMLInt32(Utility.GetAttribute(ele, "index", "0"), 0);
int balance = Utility.GetXMLInt32(Utility.GetText(ele, "0"), 0);
if (balance > 0 && index >= 0 && index < list.Length && index < m_Mobiles.Length)
{
if (SecureAccounts == null)
SecureAccounts = new Dictionary<Mobile, int>();
SecureAccounts[m_Mobiles[index]] = balance;
}
}
catch (Exception ex)
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("Writing Secure Account Exception: {0}", ex);
Utility.PopColor();
}
}
}
}
/// <summary>
/// Object detailing information about the hardware of the last person to log into this account
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public HardwareInfo HardwareInfo { get; set; }
/// <summary>
/// List of IP addresses for restricted access. '*' wildcard supported. If the array contains zero entries, all IP
/// addresses are allowed.
/// </summary>
public string[] IPRestrictions { get; set; }
/// <summary>
/// List of IP addresses which have successfully logged into this account.
/// </summary>
public IPAddress[] LoginIPs { get; set; }
/// <summary>
/// List of account comments. Type of contained objects is AccountComment.
/// </summary>
public List<AccountComment> Comments
{
get { return m_Comments ?? (m_Comments = new List<AccountComment>()); }
}
/// <summary>
/// List of account tags. Type of contained objects is AccountTag.
/// </summary>
public List<AccountTag> Tags
{
get { return m_Tags ?? (m_Tags = new List<AccountTag>()); }
}
/// <summary>
/// Account password. Plain text. Case sensitive validation. May be null.
/// </summary>
public string PlainPassword { get; set; }
/// <summary>
/// Account password. Hashed with MD5. May be null.
/// </summary>
public string _MD5Password { get; set; }
/// <summary>
/// Account username and password hashed with SHA1. May be null.
/// </summary>
public string _SHA1Password { get; set; }
/// <summary>
/// Account username and password hashed with SHA512. May be null.
/// </summary>
public string _SHA512Password { get; set; }
/// <summary>
/// Internal bitfield of account flags. Consider using direct access properties (Banned, Young), or GetFlag/SetFlag
/// methods
/// </summary>
public int Flags { get; set; }
/// <summary>
/// Gets or sets a flag indiciating if this account is banned.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public bool Banned
{
get
{
var isBanned = GetFlag(0);
if (!isBanned)
{
return false;
}
DateTime banTime;
TimeSpan banDuration;
if (!GetBanTags(out banTime, out banDuration) || banDuration == TimeSpan.MaxValue ||
DateTime.UtcNow < (banTime + banDuration))
{
return true;
}
SetUnspecifiedBan(null); // clear
Banned = false;
return false;
}
set { SetFlag(0, value); }
}
/// <summary>
/// Gets or sets a flag indicating if the characters created on this account will have the young status.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public bool Young
{
get { return !GetFlag(1); }
set
{
SetFlag(1, !value);
if (m_YoungTimer == null)
{
return;
}
m_YoungTimer.Stop();
m_YoungTimer = null;
}
}
/// <summary>
/// The date and time of when this account was created.
/// </summary>
[CommandProperty(AccessLevel.Administrator, true)]
public DateTime Created { get; set; }
[CommandProperty(AccessLevel.Administrator)]
public TimeSpan Age { get { return DateTime.UtcNow - Created; } }
/// <summary>
/// Gets or sets the date and time when this account was last accessed.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public DateTime LastLogin { get; set; }
/// <summary>
/// An account is considered inactive based upon LastLogin and InactiveDuration. If the account is empty, it is based
/// upon EmptyInactiveDuration
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public bool Inactive
{
get
{
if (AccessLevel >= AccessLevel.Counselor)
{
return false;
}
var inactiveLength = DateTime.UtcNow - LastLogin;
return inactiveLength > (Count == 0 ? EmptyInactiveDuration : InactiveDuration);
}
}
/// <summary>
/// Gets the total game time of this account, also considering the game time of characters
/// that have been deleted.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public TimeSpan TotalGameTime
{
get
{
foreach (var m in m_Mobiles.OfType<PlayerMobile>().Where(m => m.NetState != null))
{
return m_TotalGameTime + (DateTime.UtcNow - m.SessionStart);
}
return m_TotalGameTime;
}
}
/// <summary>
/// Account username. Case insensitive validation.
/// </summary>
[CommandProperty(AccessLevel.Administrator, true)]
public string Username { get; set; }
/// <summary>
/// Account email address. Case insensitive validation.
/// </summary>
[CommandProperty(AccessLevel.Administrator, true)]
public string Email { get; set; }
/// <summary>
/// Initial AccessLevel for new characters created on this account.
/// </summary>
[CommandProperty(AccessLevel.Administrator, AccessLevel.Owner)]
public AccessLevel AccessLevel { get { return m_AccessLevel; } set { m_AccessLevel = value; } }
/// <summary>
/// Gets the current number of characters on this account.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public int Count
{
get
{
var count = 0;
for (var i = 0; i < Length; ++i)
{
if (this[i] != null)
{
++count;
}
}
return count;
}
}
/// <summary>
/// Gets the maximum amount of characters allowed to be created on this account. Values other than 1, 5, 6, or 7 are
/// not supported by the client.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public int Limit { get { return (Siege.SiegeShard ? Siege.CharacterSlots : Core.SA ? 7 : Core.AOS ? 6 : 5); } }
/// <summary>
/// Gets the maxmimum amount of characters that this account can hold.
/// </summary>
[CommandProperty(AccessLevel.Administrator)]
public int Length { get { return m_Mobiles.Length; } }
/// <summary>
/// Gets or sets the character at a specified index for this account.
/// Out of bound index values are handled; null returned for get, ignored for set.
/// </summary>
public Mobile this[int index]
{
get
{
if (index < 0 || index >= m_Mobiles.Length)
{
return null;
}
var m = m_Mobiles[index];
if (m == null || !m.Deleted)
{
return m;
}
m.Account = null;
m_Mobiles[index] = null;
return null;
}
set
{
if (index < 0 || index >= m_Mobiles.Length)
{
return;
}
if (m_Mobiles[index] != null)
{
m_Mobiles[index].Account = null;
}
m_Mobiles[index] = value;
if (m_Mobiles[index] != null)
{
m_Mobiles[index].Account = this;
}
}
}
/// <summary>
/// Deletes the account, all characters of the account, and all houses of those characters
/// </summary>
public void Delete()
{
for (var i = 0; i < Length; ++i)
{
var m = this[i];
if (m == null)
{
continue;
}
var list = BaseHouse.GetHouses(m);
foreach (BaseHouse h in list)
{
h.Delete();
}
ColUtility.Free(list);
m.Delete();
m.Account = null;
m_Mobiles[i] = null;
}
if (LoginIPs.Length != 0 && AccountHandler.IPTable.ContainsKey(LoginIPs[0]))
{
--AccountHandler.IPTable[LoginIPs[0]];
}
Accounts.Remove(Username);
}
public void SetPassword(string plainPassword)
{
switch (AccountHandler.ProtectPasswords)
{
case PasswordProtection.None:
{
PlainPassword = plainPassword;
_MD5Password = null;
_SHA1Password = null;
_SHA512Password = null;
}
break;
case PasswordProtection.Crypt:
{
PlainPassword = null;
_MD5Password = HashMD5(plainPassword);
_SHA1Password = null;
_SHA512Password = null;
}
break;
case PasswordProtection.NewCrypt:
{
PlainPassword = null;
_MD5Password = null;
_SHA1Password = HashSHA1(Username + plainPassword);
_SHA512Password = null;
}
break;
default: // PasswordProtection.NewSecureCrypt
{
PlainPassword = null;
_MD5Password = null;
_SHA1Password = null;
_SHA512Password = HashSHA512(Username + plainPassword);
}
break;
}
}
public bool CheckPassword(string plainPassword)
{
bool ok;
PasswordProtection curProt;
if (PlainPassword != null)
{
ok = (PlainPassword == plainPassword);
curProt = PasswordProtection.None;
}
else if (_MD5Password != null)
{
ok = (_MD5Password == HashMD5(plainPassword));
curProt = PasswordProtection.Crypt;
}
else if (_SHA1Password != null)
{
ok = (_SHA1Password == HashSHA1(Username + plainPassword));
curProt = PasswordProtection.NewCrypt;
}
else
{
ok = (_SHA512Password == HashSHA512(Username + plainPassword));
curProt = PasswordProtection.NewSecureCrypt;
}
if (ok && curProt != AccountHandler.ProtectPasswords)
{
SetPassword(plainPassword);
}
return ok;
}
public int CompareTo(IAccount other)
{
if (other == null)
{
return 1;
}
return String.Compare(Username, other.Username, StringComparison.Ordinal);
}
public int CompareTo(object obj)
{
if (obj is Account)
{
return CompareTo((Account)obj);
}
throw new ArgumentException();
}
public int CompareTo(Account other)
{
if (other == null)
{
return 1;
}
return String.Compare(Username, other.Username, StringComparison.Ordinal);
}
public static string HashMD5(string phrase)
{
if (m_MD5HashProvider == null)
{
m_MD5HashProvider = new MD5CryptoServiceProvider();
}
if (m_HashBuffer == null)
{
m_HashBuffer = new byte[256];
}
var length = Encoding.ASCII.GetBytes(phrase, 0, phrase.Length > 256 ? 256 : phrase.Length, m_HashBuffer, 0);
var hashed = m_MD5HashProvider.ComputeHash(m_HashBuffer, 0, length);
return BitConverter.ToString(hashed);
}
public static string HashSHA1(string phrase)
{
if (m_SHA1HashProvider == null)
{
m_SHA1HashProvider = new SHA1CryptoServiceProvider();
}
if (m_HashBuffer == null)
{
m_HashBuffer = new byte[256];
}
var length = Encoding.ASCII.GetBytes(phrase, 0, phrase.Length > 256 ? 256 : phrase.Length, m_HashBuffer, 0);
var hashed = m_SHA1HashProvider.ComputeHash(m_HashBuffer, 0, length);
return BitConverter.ToString(hashed);
}
public static string HashSHA512(string phrase)
{
if (m_SHA512HashProvider == null)
{
m_SHA512HashProvider = new SHA512CryptoServiceProvider();
}
if (m_HashBuffer == null)
{
m_HashBuffer = new byte[256];
}
var length = Encoding.ASCII.GetBytes(phrase, 0, phrase.Length > 256 ? 256 : phrase.Length, m_HashBuffer, 0);
var hashed = m_SHA512HashProvider.ComputeHash(m_HashBuffer, 0, length);
return BitConverter.ToString(hashed);
}
public static void Initialize()
{
EventSink.Connected += EventSink_Connected;
EventSink.Disconnected += EventSink_Disconnected;
EventSink.Login += EventSink_Login;
}
/// <summary>
/// Deserializes a list of string values from an xml element. Null values are not added to the list.
/// </summary>
/// <param name="node">The XmlElement from which to deserialize.</param>
/// <returns>String list. Value will never be null.</returns>
public static string[] LoadAccessCheck(XmlElement node)
{
string[] stringList;
var accessCheck = node["accessCheck"];
if (accessCheck != null)
{
stringList =
accessCheck.GetElementsByTagName("ip")
.Cast<XmlElement>()
.Select(ip => Utility.GetText(ip, null))
.Where(text => text != null)
.ToArray();
}
else
{
stringList = new string[0];
}
return stringList;
}
/// <summary>
/// Deserializes a list of IPAddress values from an xml element.
/// </summary>
/// <param name="node">The XmlElement from which to deserialize.</param>
/// <returns>Address list. Value will never be null.</returns>
public static IPAddress[] LoadAddressList(XmlElement node)
{
IPAddress[] list;
var addressList = node["addressList"];
if (addressList != null)
{
var count = Utility.GetXMLInt32(Utility.GetAttribute(addressList, "count", "0"), 0);
list = new IPAddress[count];
count = 0;
foreach (XmlElement ip in addressList.GetElementsByTagName("ip").Cast<XmlElement>().Where(ip => count < list.Length))
{
IPAddress address;
if (!IPAddress.TryParse(Utility.GetText(ip, null), out address))
{
continue;
}
list[count] = Utility.Intern(address);
count++;
}
if (count == list.Length)
{
return list;
}
var old = list;
list = new IPAddress[count];
for (var i = 0; i < count && i < old.Length; ++i)
{
list[i] = old[i];
}
}
else
{
list = new IPAddress[0];
}
return list;
}
/// <summary>
/// Deserializes a list of Mobile instances from an xml element.
/// </summary>
/// <param name="node">The XmlElement instance from which to deserialize.</param>
/// <returns>Mobile list. Value will never be null.</returns>
public static Mobile[] LoadMobiles(XmlElement node)
{
var list = new Mobile[7];
var chars = node["chars"];
//int length = Accounts.GetInt32( Accounts.GetAttribute( chars, "length", "6" ), 6 );
//list = new Mobile[length];
//Above is legacy, no longer used
if (chars == null)
{
return list;
}
foreach (XmlElement ele in chars.GetElementsByTagName("char"))
{
try
{
var index = Utility.GetXMLInt32(Utility.GetAttribute(ele, "index", "0"), 0);
var serial = Utility.GetXMLInt32(Utility.GetText(ele, "0"), 0);
if (index >= 0 && index < list.Length)
{
list[index] = World.FindMobile(serial);
}
}
catch
{ }
}
return list;
}
/// <summary>
/// Deserializes a list of AccountComment instances from an xml element.
/// </summary>
/// <param name="node">The XmlElement from which to deserialize.</param>
/// <returns>Comment list. Value will never be null.</returns>
public static List<AccountComment> LoadComments(XmlElement node)
{
var comments = node["comments"];
if (comments == null)
{
return null;
}
var list = new List<AccountComment>();
foreach (XmlElement comment in comments.GetElementsByTagName("comment"))
{
try
{
list.Add(new AccountComment(comment));
}
catch
{ }
}
return list;
}
/// <summary>
/// Deserializes a list of AccountTag instances from an xml element.
/// </summary>
/// <param name="node">The XmlElement from which to deserialize.</param>
/// <returns>Tag list. Value will never be null.</returns>
public static List<AccountTag> LoadTags(XmlElement node)
{
var tags = node["tags"];
if (tags == null)
{
return null;
}
var list = new List<AccountTag>();
foreach (XmlElement tag in tags.GetElementsByTagName("tag"))
{
try
{
list.Add(new AccountTag(tag));
}
catch
{ }
}
return list;
}
/// <summary>
/// Gets the value of a specific flag in the Flags bitfield.
/// </summary>
/// <param name="index">The zero-based flag index.</param>
public bool GetFlag(int index)
{
return (Flags & (1 << index)) != 0;
}
/// <summary>
/// Sets the value of a specific flag in the Flags bitfield.
/// </summary>
/// <param name="index">The zero-based flag index.</param>
/// <param name="value">The value to set.</param>
public void SetFlag(int index, bool value)
{
if (value)
{
Flags |= (1 << index);
}
else
{
Flags &= ~(1 << index);
}
}
/// <summary>
/// Adds a new tag to this account. This method does not check for duplicate names.
/// </summary>
/// <param name="name">New tag name.</param>
/// <param name="value">New tag value.</param>
public void AddTag(string name, string value)
{
Tags.Add(new AccountTag(name, value));
}
/// <summary>
/// Removes all tags with the specified name from this account.
/// </summary>
/// <param name="name">Tag name to remove.</param>
public void RemoveTag(string name)
{
for (var i = Tags.Count - 1; i >= 0; --i)
{
if (i >= Tags.Count)
{
continue;
}
var tag = Tags[i];
if (tag.Name == name)
{
Tags.RemoveAt(i);
}
}
}
/// <summary>
/// Modifies an existing tag or adds a new tag if no tag exists.
/// </summary>
/// <param name="name">Tag name.</param>
/// <param name="value">Tag value.</param>
public void SetTag(string name, string value)
{
foreach (var tag in Tags.Where(tag => tag.Name == name))
{
tag.Value = value;
return;
}
AddTag(name, value);
}
/// <summary>
/// Gets the value of a tag -or- null if there are no tags with the specified name.
/// </summary>
/// <param name="name">Name of the desired tag value.</param>
public string GetTag(string name)
{
return Tags.Where(tag => tag.Name == name).Select(tag => tag.Value).FirstOrDefault();
}
public void SetUnspecifiedBan(Mobile from)
{
SetBanTags(from, DateTime.MinValue, TimeSpan.Zero);
}
public void SetBanTags(Mobile from, DateTime banTime, TimeSpan banDuration)
{
if (from == null)
{
RemoveTag("BanDealer");
}
else
{
SetTag("BanDealer", from.ToString());
}
if (banTime == DateTime.MinValue)
{
RemoveTag("BanTime");
}
else
{
SetTag("BanTime", XmlConvert.ToString(banTime, XmlDateTimeSerializationMode.Utc));
}
if (banDuration == TimeSpan.Zero)
{
RemoveTag("BanDuration");
}
else
{
SetTag("BanDuration", banDuration.ToString());
}
}
public bool GetBanTags(out DateTime banTime, out TimeSpan banDuration)
{
var tagTime = GetTag("BanTime");
var tagDuration = GetTag("BanDuration");
banTime = tagTime != null ? Utility.GetXMLDateTime(tagTime, DateTime.MinValue) : DateTime.MinValue;
if (tagDuration == "Infinite")
{
banDuration = TimeSpan.MaxValue;
}
else if (tagDuration != null)
{
banDuration = Utility.ToTimeSpan(tagDuration);
}
else
{
banDuration = TimeSpan.Zero;
}
return banTime != DateTime.MinValue && banDuration != TimeSpan.Zero;
}
public void RemoveYoungStatus(int message)
{
Young = false;
foreach (var m in m_Mobiles.OfType<PlayerMobile>().Where(m => m.Young))
{
m.Young = false;
if (m.NetState == null)
{
continue;
}
if (message > 0)
{
m.SendLocalizedMessage(message);
}
m.SendLocalizedMessage(1019039);
// You are no longer considered a young player of Ultima Online, and are no longer subject to the limitations and benefits of being in that caste.
}
}
public void CheckYoung()
{
if (TotalGameTime >= YoungDuration)
{
RemoveYoungStatus(1019038);
// You are old enough to be considered an adult, and have outgrown your status as a young player!
}
}
/// <summary>
/// Checks if a specific NetState is allowed access to this account.
/// </summary>
/// <param name="ns">NetState instance to check.</param>
/// <returns>True if allowed, false if not.</returns>
public bool HasAccess(NetState ns)
{
return (ns != null && HasAccess(ns.Address));
}
public bool HasAccess(IPAddress ipAddress)
{
var level = AccountHandler.LockdownLevel;
if (level >= AccessLevel.Counselor)
{
var hasAccess = false;
if (m_AccessLevel >= level)
{
hasAccess = true;
}
else
{
for (var i = 0; !hasAccess && i < Length; ++i)
{
var m = this[i];
if (m != null && m.AccessLevel >= level)
{
hasAccess = true;
}
}
}
if (!hasAccess)
{
return false;
}
}
var accessAllowed = IPRestrictions.Length == 0 || IPLimiter.IsExempt(ipAddress);
for (var i = 0; !accessAllowed && i < IPRestrictions.Length; ++i)
{
accessAllowed = Utility.IPMatch(IPRestrictions[i], ipAddress);
}
return accessAllowed;
}
/// <summary>
/// Records the IP address of 'ns' in its 'LoginIPs' list.
/// </summary>
/// <param name="ns">NetState instance to record.</param>
public void LogAccess(NetState ns)
{
if (ns != null)
{
LogAccess(ns.Address);
}
}
public void LogAccess(IPAddress ipAddress)
{
if (IPLimiter.IsExempt(ipAddress))
{
return;
}
if (LoginIPs.Length == 0)
{
if (AccountHandler.IPTable.ContainsKey(ipAddress))
{
AccountHandler.IPTable[ipAddress]++;
}
else
{
AccountHandler.IPTable[ipAddress] = 1;
}
}
var contains = false;
for (var i = 0; !contains && i < LoginIPs.Length; ++i)
{
contains = LoginIPs[i].Equals(ipAddress);
}
if (contains)
{
return;
}
var old = LoginIPs;
LoginIPs = new IPAddress[old.Length + 1];
for (var i = 0; i < old.Length; ++i)
{
LoginIPs[i] = old[i];
}
LoginIPs[old.Length] = ipAddress;
}
/// <summary>
/// Checks if a specific NetState is allowed access to this account. If true, the NetState IPAddress is added to the
/// address list.
/// </summary>
/// <param name="ns">NetState instance to check.</param>
/// <returns>True if allowed, false if not.</returns>
public bool CheckAccess(NetState ns)
{
return (ns != null && CheckAccess(ns.Address));
}
public bool CheckAccess(IPAddress ipAddress)
{
var hasAccess = HasAccess(ipAddress);
if (hasAccess)
{
LogAccess(ipAddress);
}
return hasAccess;
}
/// <summary>
/// Serializes this Account instance to an XmlTextWriter.
/// </summary>
/// <param name="xml">The XmlTextWriter instance from which to serialize.</param>
public void Save(XmlTextWriter xml)
{
xml.WriteStartElement("account");
xml.WriteStartElement("username");
xml.WriteString(Username);
xml.WriteEndElement();
if (PlainPassword != null)
{
xml.WriteStartElement("password");
xml.WriteString(PlainPassword);
xml.WriteEndElement();
}
if (_MD5Password != null)
{
xml.WriteStartElement("cryptPassword");
xml.WriteString(_MD5Password);
xml.WriteEndElement();
}
if (_SHA1Password != null)
{
xml.WriteStartElement("newCryptPassword");
xml.WriteString(_SHA1Password);
xml.WriteEndElement();
}
if (_SHA512Password != null)
{
xml.WriteStartElement("newSecureCryptPassword");
xml.WriteString(_SHA512Password);
xml.WriteEndElement();
}
if (m_AccessLevel >= AccessLevel.Counselor)
{
xml.WriteStartElement("accessLevel");
xml.WriteString(m_AccessLevel.ToString());
xml.WriteEndElement();
}
if (Flags != 0)
{
xml.WriteStartElement("flags");
xml.WriteString(XmlConvert.ToString(Flags));
xml.WriteEndElement();
}
xml.WriteStartElement("created");
xml.WriteString(XmlConvert.ToString(Created, XmlDateTimeSerializationMode.Utc));
xml.WriteEndElement();
xml.WriteStartElement("lastLogin");
xml.WriteString(XmlConvert.ToString(LastLogin, XmlDateTimeSerializationMode.Utc));
xml.WriteEndElement();
xml.WriteStartElement("totalGameTime");
xml.WriteString(XmlConvert.ToString(TotalGameTime));
xml.WriteEndElement();
xml.WriteStartElement("chars");
for (var i = 0; i < m_Mobiles.Length; ++i)
{
var m = m_Mobiles[i];
if (m != null && !m.Deleted)
{
xml.WriteStartElement("char");
xml.WriteAttributeString("index", i.ToString());
xml.WriteString(m.Serial.Value.ToString());
xml.WriteEndElement();
}
}
xml.WriteEndElement();
if (m_Comments != null && m_Comments.Count > 0)
{
xml.WriteStartElement("comments");
foreach (AccountComment c in m_Comments)
{
c.Save(xml);
}
xml.WriteEndElement();
}
if (m_Tags != null && m_Tags.Count > 0)
{
xml.WriteStartElement("tags");
foreach (AccountTag t in m_Tags)
{
t.Save(xml);
}
xml.WriteEndElement();
}
if (LoginIPs.Length > 0)
{
xml.WriteStartElement("addressList");
xml.WriteAttributeString("count", LoginIPs.Length.ToString());
foreach (IPAddress ip in LoginIPs)
{
xml.WriteStartElement("ip");
xml.WriteString(ip.ToString());
xml.WriteEndElement();
}
xml.WriteEndElement();
}
if (IPRestrictions.Length > 0)
{
xml.WriteStartElement("accessCheck");
foreach (string ip in IPRestrictions)
{
xml.WriteStartElement("ip");
xml.WriteString(ip);
xml.WriteEndElement();
}
xml.WriteEndElement();
}
xml.WriteStartElement("totalCurrency");
xml.WriteString(XmlConvert.ToString(TotalCurrency));
xml.WriteEndElement();
xml.WriteStartElement("sovereigns");
xml.WriteString(XmlConvert.ToString(Sovereigns));
xml.WriteEndElement();
if (SecureAccounts != null)
{
xml.WriteStartElement("SecureAccounts");
for (int i = 0; i < m_Mobiles.Length; ++i)
{
Mobile m = m_Mobiles[i];
int balance = GetSecureAccountAmount(m);
if (m != null && !m.Deleted && balance > 0)
{
xml.WriteStartElement("char");
xml.WriteAttributeString("index", i.ToString());
xml.WriteString(balance.ToString());
xml.WriteEndElement();
}
}
xml.WriteEndElement();
}
xml.WriteEndElement();
}
public override string ToString()
{
return Username;
}
private static void EventSink_Connected(ConnectedEventArgs e)
{
var acc = e.Mobile.Account as Account;
if (acc == null)
{
return;
}
if (!acc.Young || acc.m_YoungTimer != null)
{
return;
}
acc.m_YoungTimer = new YoungTimer(acc);
acc.m_YoungTimer.Start();
}
private static void EventSink_Disconnected(DisconnectedEventArgs e)
{
var acc = e.Mobile.Account as Account;
if (acc == null)
{
return;
}
if (acc.m_YoungTimer != null)
{
acc.m_YoungTimer.Stop();
acc.m_YoungTimer = null;
}
var m = e.Mobile as PlayerMobile;
if (m != null)
{
acc.m_TotalGameTime += DateTime.UtcNow - m.SessionStart;
}
}
private static void EventSink_Login(LoginEventArgs e)
{
var m = e.Mobile as PlayerMobile;
if (m == null)
{
return;
}
var acc = m.Account as Account;
if (acc == null)
{
return;
}
if (!m.Young || !acc.Young)
{
return;
}
var ts = YoungDuration - acc.TotalGameTime;
var hours = Math.Max((int)ts.TotalHours, 0);
m.SendAsciiMessage(
"You will enjoy the benefits and relatively safe status of a young player for {0} more hour{1}.",
hours,
hours != 1 ? "s" : "");
}
private class YoungTimer : Timer
{
private readonly Account _Account;
public YoungTimer(Account account)
: base(TimeSpan.FromMinutes(1.0), TimeSpan.FromMinutes(1.0))
{
_Account = account;
Priority = TimerPriority.FiveSeconds;
}
protected override void OnTick()
{
_Account.CheckYoung();
}
}
#region Gold Account
/// <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.
/// </summary>
public static int CurrencyThreshold
{
get { return AccountGold.CurrencyThreshold; }
set { AccountGold.CurrencyThreshold = value; }
}
/// <summary>
/// This amount represents the total amount of currency owned by the player.
/// It is cumulative of both Gold and Platinum, the absolute total amount of
/// Gold owned by the player can be found by multiplying this value by the
/// CurrencyThreshold value.
/// </summary>
[CommandProperty(AccessLevel.Administrator, true)]
public double TotalCurrency { get; private set; }
/// <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)]
public int TotalGold
{
get { return (int)Math.Floor((TotalCurrency - Math.Truncate(TotalCurrency)) * Math.Max(1.0, CurrencyThreshold)); }
}
/// <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)]
public int TotalPlat { get { return (int)Math.Truncate(TotalCurrency); } }
/// <summary>
/// Attempts to deposit the given amount of Gold and 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>
public bool DepositCurrency(double amount)
{
if (amount <= 0)
{
return false;
}
double oldAmount = TotalCurrency;
TotalCurrency += amount;
EventSink.InvokeAccountGoldChange(new AccountGoldChangeEventArgs(this, oldAmount, TotalCurrency));
return true;
}
/// <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>
public bool DepositGold(int amount)
{
return DepositCurrency(amount / Math.Max(1.0, CurrencyThreshold));
}
/// <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>
public bool DepositGold(long amount)
{
return DepositCurrency(amount / Math.Max(1.0, CurrencyThreshold));
}
/// <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>
public bool DepositPlat(int amount)
{
return DepositCurrency(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>
public bool DepositPlat(long amount)
{
return DepositCurrency(amount);
}
/// <summary>
/// Attempts to withdraw the given amount of Platinum and Gold from this account.
/// </summary>
/// <param name="amount">Amount to withdraw.</param>
/// <returns>True if successful, false if balance was too low.</returns>
public bool WithdrawCurrency(double amount)
{
if (amount <= 0)
{
return true;
}
if (amount > TotalCurrency)
{
return false;
}
double oldAmount = TotalCurrency;
TotalCurrency -= amount;
EventSink.InvokeAccountGoldChange(new AccountGoldChangeEventArgs(this, oldAmount, TotalCurrency));
return true;
}
/// <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>
public bool WithdrawGold(int amount)
{
return WithdrawCurrency(amount / Math.Max(1.0, CurrencyThreshold));
}
/// <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>
public bool WithdrawGold(long amount)
{
return WithdrawCurrency(amount / Math.Max(1.0, CurrencyThreshold));
}
/// <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>
public bool WithdrawPlat(int amount)
{
return WithdrawCurrency(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>
public bool WithdrawPlat(long amount)
{
return WithdrawCurrency(amount);
}
/// <summary>
/// Gets the total balance of Gold for this account.
/// </summary>
/// <param name="gold">Gold value, Platinum exclusive</param>
/// <param name="totalGold">Gold value, Platinum inclusive</param>
public void GetGoldBalance(out int gold, out double totalGold)
{
gold = TotalGold;
totalGold = TotalCurrency * Math.Max(1.0, CurrencyThreshold);
}
/// <summary>
/// Gets the total balance of Gold for this account.
/// </summary>
/// <param name="gold">Gold value, Platinum exclusive</param>
/// <param name="totalGold">Gold value, Platinum inclusive</param>
public void GetGoldBalance(out long gold, out double totalGold)
{
gold = TotalGold;
totalGold = TotalCurrency * Math.Max(1.0, CurrencyThreshold);
}
/// <summary>
/// Gets the total balance of Platinum for this account.
/// </summary>
/// <param name="plat">Platinum value, Gold exclusive</param>
/// <param name="totalPlat">Platinum value, Gold inclusive</param>
public void GetPlatBalance(out int plat, out double totalPlat)
{
plat = TotalPlat;
totalPlat = TotalCurrency;
}
/// <summary>
/// Gets the total balance of Platinum for this account.
/// </summary>
/// <param name="plat">Platinum value, Gold exclusive</param>
/// <param name="totalPlat">Platinum value, Gold inclusive</param>
public void GetPlatBalance(out long plat, out double totalPlat)
{
plat = TotalPlat;
totalPlat = TotalCurrency;
}
/// <summary>
/// Gets the total balance of Gold and Platinum for this account.
/// </summary>
/// <param name="gold">Gold value, Platinum exclusive</param>
/// <param name="totalGold">Gold value, Platinum inclusive</param>
/// <param name="plat">Platinum value, Gold exclusive</param>
/// <param name="totalPlat">Platinum value, Gold inclusive</param>
public void GetBalance(out int gold, out double totalGold, out int plat, out double totalPlat)
{
GetGoldBalance(out gold, out totalGold);
GetPlatBalance(out plat, out totalPlat);
}
/// <summary>
/// Gets the total balance of Gold and Platinum for this account.
/// </summary>
/// <param name="gold">Gold value, Platinum exclusive</param>
/// <param name="totalGold">Gold value, Platinum inclusive</param>
/// <param name="plat">Platinum value, Gold exclusive</param>
/// <param name="totalPlat">Platinum value, Gold inclusive</param>
public void GetBalance(out long gold, out double totalGold, out long plat, out double totalPlat)
{
GetGoldBalance(out gold, out totalGold);
GetPlatBalance(out plat, out totalPlat);
}
public bool HasGoldBalance(double amount)
{
long gold;
double totalGold;
GetGoldBalance(out gold, out totalGold);
return amount <= totalGold;
}
public bool HasPlatBalance(double amount)
{
long plat;
double totalPlat;
GetPlatBalance(out plat, out totalPlat);
return amount <= totalPlat;
}
#endregion
#region Secure Account
public Dictionary<Mobile, int> SecureAccounts;
public static readonly int MaxSecureAmount = 100000000;
public int GetSecureAccountAmount(Mobile m)
{
for(int i = 0; i < Length; i++)
{
Mobile mob = m_Mobiles[i];
if (mob == null)
continue;
if (mob == m)
{
if (SecureAccounts != null && SecureAccounts.ContainsKey(m))
return SecureAccounts[m];
}
}
return 0;
}
public bool DepositToSecure(Mobile m, int amount)
{
for (int i = 0; i < Length; i++)
{
Mobile mob = m_Mobiles[i];
if (mob == null)
continue;
if (mob == m)
{
if (SecureAccounts == null)
SecureAccounts = new Dictionary<Mobile, int>();
if (!SecureAccounts.ContainsKey(m))
SecureAccounts[m] = Math.Min(MaxSecureAmount, amount);
else
SecureAccounts[m] = Math.Min(MaxSecureAmount, SecureAccounts[m] + amount);
return true;
}
}
return false;
}
public bool WithdrawFromSecure(Mobile m, int amount)
{
for (int i = 0; i < Length; i++)
{
Mobile mob = m_Mobiles[i];
if (mob == null)
continue;
if (m == mob)
{
if (SecureAccounts == null || !SecureAccounts.ContainsKey(m) || SecureAccounts[m] < amount)
return false;
SecureAccounts[m] -= amount;
return true;
}
}
return false;
}
#endregion
#region Sovereigns
/// <summary>
/// Sovereigns which can be used at the shard owners disposal. On EA, they are used for curerncy with the Ultima Store
/// </summary>
[CommandProperty(AccessLevel.Administrator, true)]
public int Sovereigns { get; private set; }
public void SetSovereigns(int amount)
{
Sovereigns = amount;
}
public bool DepositSovereigns(int amount)
{
if (amount <= 0)
{
return false;
}
Sovereigns += amount;
return true;
}
public bool WithdrawSovereigns(int amount)
{
if (amount <= 0)
{
return true;
}
if (amount > Sovereigns)
{
return false;
}
Sovereigns -= amount;
return true;
}
#endregion
}
}