/*************************************************************************
* ModernUO *
* Copyright 2019-2026 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: SerializationExtensions.cs *
* *
* 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 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
*************************************************************************/
using System;
using System.Collections.Generic;
using Server.Guilds;
namespace Server;
public static class SerializationExtensions
{
private static readonly Dictionary> _directFinderTable = new();
private static readonly Dictionary> _searchTable = new();
public static void RegisterFindEntity(this Type type, Func func)
{
_searchTable[type] = func;
}
public static T ReadEntity(this IGenericReader reader) where T : class, ISerializable
{
var serial = reader.ReadSerial();
var typeT = typeof(T);
T entity;
// Add to this list when creating new serializable types
if (typeof(BaseGuild).IsAssignableFrom(typeT))
{
// If we check for `entity.Deleted` here during deserialization then all guilds are deleted because
// Deleted -> Disbanded -> No leader, which is the case before deserialization.
// TODO: Use a deleted flag instead, and actively check for disbanded guilds properly.
return World.FindGuild(serial) as T;
}
if (typeof(IEntity).IsAssignableFrom(typeT))
{
return World.FindEntity(serial) as T;
}
if (_directFinderTable.TryGetValue(typeT, out var finder))
{
return finder(serial, false) as T;
}
Type type = null;
foreach (var baseType in _searchTable.Keys)
{
if (baseType.IsAssignableFrom(typeT))
{
type = baseType;
break;
}
}
if (type == null)
{
type = typeT;
while (true)
{
var baseType = type?.BaseType;
// Find the parent class with ISerializable registered. To do this we break on it's parent class (or object)
// that doesn't have ISerializable implemented.
if (baseType?.GetInterface("ISerializable") == null && type?.GetInterface("ISerializable") != null)
{
break;
}
type = baseType;
}
throw new Exception($"No FindEntity registered for '{type.FullName}'.");
}
finder = _searchTable[type];
_directFinderTable[type] = finder;
return finder(serial, false) as T;
}
public static List ReadEntityList(
this IGenericReader reader,
bool nullIfEmpty = false
) where T : class, ISerializable
{
var count = reader.ReadInt();
if (count == 0 && nullIfEmpty)
{
return null;
}
var list = new List(count);
for (var i = 0; i < count; ++i)
{
var entity = reader.ReadEntity();
if (entity != null)
{
list.Add(entity);
}
}
return list;
}
public static HashSet ReadEntitySet(
this IGenericReader reader,
bool nullIfEmpty = false
) where T : class, ISerializable
{
var count = reader.ReadInt();
if (count == 0 && nullIfEmpty)
{
return null;
}
var set = new HashSet(count);
for (var i = 0; i < count; ++i)
{
var entity = reader.ReadEntity();
if (entity != null)
{
set.Add(entity);
}
}
return set;
}
public static void Write(this IGenericWriter writer, ISerializable value)
{
writer.Write(value?.Deleted != false ? Serial.MinusOne : value.Serial);
}
public static void Write(this IGenericWriter writer, ICollection coll) where T : class, ISerializable
{
writer.Write(coll.Count);
foreach (var entry in coll)
{
writer.Write(entry);
}
}
public static void Write(
this IGenericWriter writer, ICollection coll, Action action
) where T : class, ISerializable
{
if (coll == null)
{
writer.Write(0);
return;
}
writer.Write(coll.Count);
foreach (var entry in coll)
{
action(writer, entry);
}
}
public static void Write(this IGenericWriter writer, Poison p)
{
if (p == null)
{
writer.Write(false);
}
else
{
writer.Write(true);
writer.Write((byte)p.Index);
}
}
public static Poison ReadPoison(this IGenericReader reader) =>
reader.ReadBool() ? Poison.GetPoisonByIndex(reader.ReadByte()) : null;
}