fix: Cleans up entity persistence. Generalizes Mobiles/Items/Guilds (#1528)
### Summary - [X] Fixed a bug where entity persistence was serialized out of order, causing world corruption. - [X] Fixed LastSerialized not being utilized properly and dangling references still becoming an issue. - [X] Added a new `GenericEntityPersistence<T>` type to encapsulate `ISerializable` serialization. - [X] Removing the custom logic and moved Items, Mobiles, Guilds, and Accounts to GenericEntityPersistence. - [X] Changed serialization to use the singleton pattern to reduce calling methods from stored variables.
This commit is contained in:
parent
e0225c6e59
commit
f2de2fbb77
34 changed files with 721 additions and 970 deletions
|
|
@ -1,139 +1,95 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using Server.Logging;
|
||||
|
||||
namespace Server.Accounting
|
||||
namespace Server.Accounting;
|
||||
|
||||
public class Accounts : GenericEntityPersistence<Account>
|
||||
{
|
||||
public static class Accounts
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(Accounts));
|
||||
|
||||
private static readonly Dictionary<string, Account> _accountsByName = new(32, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public static int Count => _accountsByName.Count;
|
||||
|
||||
private static Accounts _accountsPersistence;
|
||||
|
||||
public static Serial NewAccount => _accountsPersistence.NewEntity;
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(Accounts));
|
||||
_accountsPersistence = new Accounts();
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, Account> _accountsByName = new(32, StringComparer.OrdinalIgnoreCase);
|
||||
private static Dictionary<Serial, Account> _accountsById = new(32);
|
||||
private static Serial _lastAccount;
|
||||
public Accounts() : base("Accounts", 3, 0x1, 0x7FFFFFFF)
|
||||
{
|
||||
}
|
||||
|
||||
private static void OutOfMemory(string message) => throw new OutOfMemoryException(message);
|
||||
public static IEnumerable<IAccount> GetAccounts() => _accountsByName.Values;
|
||||
|
||||
public static Serial NewAccount
|
||||
public static Account GetAccount(string username)
|
||||
{
|
||||
_accountsByName.TryGetValue(username, out var a);
|
||||
return a;
|
||||
}
|
||||
|
||||
public static void Add(Account a)
|
||||
{
|
||||
_accountsByName[a.Username] = a;
|
||||
_accountsPersistence.AddEntity(a);
|
||||
}
|
||||
|
||||
public static void Remove(Account a)
|
||||
{
|
||||
_accountsByName.Remove(a.Username);
|
||||
_accountsPersistence.RemoveEntity(a);
|
||||
}
|
||||
|
||||
public override void Deserialize(string path, Dictionary<ulong, string> typesDb)
|
||||
{
|
||||
var filePath = Path.Combine(path, "Accounts", "accounts.xml");
|
||||
|
||||
// Backward Compatibility
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
get
|
||||
{
|
||||
var last = _lastAccount;
|
||||
|
||||
for (uint i = 0; i < uint.MaxValue; i++)
|
||||
{
|
||||
last++;
|
||||
|
||||
if (FindAccount(last) == null)
|
||||
{
|
||||
return _lastAccount = last;
|
||||
}
|
||||
}
|
||||
|
||||
OutOfMemory("No serials left to allocate for accounts");
|
||||
return Serial.MinusOne;
|
||||
}
|
||||
DeserializeXml(filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
public static int Count => _accountsByName.Count;
|
||||
base.Deserialize(path, typesDb);
|
||||
|
||||
public static void Configure() =>
|
||||
Persistence.Register("Accounts", Serialize, WriteSnapshot, Deserialize);
|
||||
|
||||
internal static void Serialize()
|
||||
{
|
||||
EntityPersistence.SaveEntities(
|
||||
_accountsById.Values,
|
||||
account => ((ISerializable)account).Serialize(World.SerializedTypes)
|
||||
);
|
||||
}
|
||||
|
||||
internal static void WriteSnapshot(string basePath)
|
||||
{
|
||||
IIndexInfo<Serial> indexInfo = new EntityTypeIndex("Accounts");
|
||||
EntityPersistence.WriteEntities(indexInfo, _accountsById, basePath,World.SerializedTypes, out _);
|
||||
}
|
||||
|
||||
public static IEnumerable<IAccount> GetAccounts() => _accountsByName.Values;
|
||||
|
||||
public static Account GetAccount(string username)
|
||||
{
|
||||
_accountsByName.TryGetValue(username, out var a);
|
||||
return a;
|
||||
}
|
||||
|
||||
public static void Add(Account a)
|
||||
foreach (var a in EntitiesBySerial.Values)
|
||||
{
|
||||
_accountsByName[a.Username] = a;
|
||||
_accountsById[a.Serial] = a;
|
||||
}
|
||||
|
||||
public static void Remove(Account a)
|
||||
{
|
||||
_accountsByName.Remove(a.Username);
|
||||
_accountsById.Remove(a.Serial);
|
||||
}
|
||||
|
||||
internal static void Deserialize(string path, Dictionary<ulong, string> typesDb)
|
||||
{
|
||||
var filePath = Path.Combine(path, "Accounts", "accounts.xml");
|
||||
|
||||
// Backward Compatibility
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
DeserializeXml(filePath);
|
||||
return;
|
||||
}
|
||||
|
||||
IIndexInfo<Serial> indexInfo = new EntityTypeIndex("Accounts");
|
||||
|
||||
_accountsById = EntityPersistence.LoadIndex(path, indexInfo, typesDb, out List<EntitySpan<Account>> accounts);
|
||||
|
||||
if (_accountsById.Count > 0)
|
||||
{
|
||||
_lastAccount = _accountsById.Keys.Max();
|
||||
}
|
||||
|
||||
EntityPersistence.LoadData(path, indexInfo, typesDb, accounts);
|
||||
|
||||
foreach (var a in _accountsById.Values)
|
||||
{
|
||||
_accountsByName[a.Username] = a;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeserializeXml(string filePath)
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(filePath);
|
||||
|
||||
var root = doc["accounts"];
|
||||
|
||||
if (root == null)
|
||||
{
|
||||
throw new FileLoadException("Unable to load xml file");
|
||||
}
|
||||
|
||||
foreach (XmlElement account in root.GetElementsByTagName("account"))
|
||||
{
|
||||
try
|
||||
{
|
||||
new Account(account);
|
||||
}
|
||||
catch
|
||||
{
|
||||
logger.Warning("Account instance load failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IAccount FindAccount(Serial serial)
|
||||
{
|
||||
_accountsById.TryGetValue(serial, out var account);
|
||||
return account;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeserializeXml(string filePath)
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(filePath);
|
||||
|
||||
var root = doc["accounts"];
|
||||
|
||||
if (root == null)
|
||||
{
|
||||
throw new FileLoadException("Unable to load xml file");
|
||||
}
|
||||
|
||||
foreach (XmlElement account in root.GetElementsByTagName("account"))
|
||||
{
|
||||
try
|
||||
{
|
||||
new Account(account);
|
||||
}
|
||||
catch
|
||||
{
|
||||
logger.Warning("Account instance load failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IAccount FindAccount(Serial serial) => _accountsPersistence.FindEntity<Account>(serial);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue