mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-14 09:19:25 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
195 lines
3.9 KiB
C#
195 lines
3.9 KiB
C#
namespace Sandbox;
|
|
|
|
/// <summary>
|
|
/// A class that can serialize and deserialize whole objects to and from byte streams,
|
|
/// without needing to know the type beforehand.
|
|
///
|
|
/// https://docs.facepunch.com/doc/bytepack-FdTR6ZUoOa
|
|
///
|
|
/// </summary>
|
|
internal partial class BytePack
|
|
{
|
|
readonly Dictionary<Type, Packer> types = new();
|
|
readonly Dictionary<Identifier, Packer> handlers = new();
|
|
readonly Dictionary<int, Packer> typeHandler = new();
|
|
|
|
internal Func<Type, Packer> OnCreatePackerFromType { get; set; }
|
|
internal Func<int, Packer> OnCreatePackerFromIdentifier { get; set; }
|
|
|
|
public BytePack()
|
|
{
|
|
InstallPodCommon();
|
|
|
|
Add( new ObjectPacker() );
|
|
Add( new StringPacker() );
|
|
Add( new ValueArrayPacker() );
|
|
Add( new ObjectArrayPacker() );
|
|
Add( new ListPacker() );
|
|
Add( new DictionaryPacker() );
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
types.Clear();
|
|
handlers.Clear();
|
|
typeHandler.Clear();
|
|
|
|
OnCreatePackerFromType = default;
|
|
OnCreatePackerFromIdentifier = default;
|
|
}
|
|
|
|
void Add( Packer ti )
|
|
{
|
|
ti.Init( this );
|
|
}
|
|
|
|
public byte[] Serialize<T>( T obj )
|
|
{
|
|
ByteStream bs = ByteStream.Create( 1024 );
|
|
|
|
try
|
|
{
|
|
Serialize( ref bs, obj );
|
|
return bs.ToArray();
|
|
}
|
|
finally
|
|
{
|
|
bs.Dispose();
|
|
}
|
|
}
|
|
|
|
public void SerializeTo<T>( ref ByteStream bs, T obj )
|
|
{
|
|
Serialize( ref bs, obj );
|
|
}
|
|
|
|
public object Deserialize( byte[] memory ) => Deserialize( new ReadOnlySpan<byte>( memory ) );
|
|
public object Deserialize( ReadOnlySpan<byte> data )
|
|
{
|
|
var bs = ByteStream.CreateReader( data );
|
|
|
|
try
|
|
{
|
|
return Deserialize( ref bs );
|
|
}
|
|
finally
|
|
{
|
|
bs.Dispose();
|
|
}
|
|
}
|
|
|
|
public object Deserialize( ref ByteStream data )
|
|
{
|
|
var h = data.Read<Identifier>();
|
|
|
|
if ( h == Identifier.Runtime )
|
|
{
|
|
int typeIdent = data.Read<int>();
|
|
var p = GetOrCreatePacker( typeIdent );
|
|
|
|
if ( p is not null )
|
|
{
|
|
return p.Read( ref data );
|
|
}
|
|
|
|
throw new System.Exception( $"Unhandled runtime ident {typeIdent}" );
|
|
}
|
|
|
|
if ( handlers.TryGetValue( h, out var typeInfo ) )
|
|
{
|
|
return typeInfo.Read( ref data );
|
|
}
|
|
|
|
|
|
if ( h == Identifier.Null ) return null;
|
|
throw new System.Exception( $"Unhandled header {h}" );
|
|
}
|
|
|
|
void Serialize<T>( ref ByteStream bs, T obj )
|
|
{
|
|
if ( obj is null )
|
|
{
|
|
bs.Write( Identifier.Null );
|
|
return;
|
|
}
|
|
|
|
if ( obj is Array array )
|
|
{
|
|
var element = array.GetType().GetElementType();
|
|
|
|
if ( SandboxedUnsafe.IsAcceptablePod( element ) )
|
|
{
|
|
bs.Write( Identifier.ArrayValue );
|
|
Serialize( Identifier.ArrayValue, ref bs, array );
|
|
return;
|
|
}
|
|
|
|
bs.Write( Identifier.Array );
|
|
Serialize( Identifier.Array, ref bs, array );
|
|
return;
|
|
}
|
|
|
|
var t = obj.GetType();
|
|
|
|
if ( t.IsBasedOnGenericType( typeof( List<> ) ) )
|
|
{
|
|
bs.Write( Identifier.List );
|
|
Serialize( Identifier.List, ref bs, obj );
|
|
return;
|
|
}
|
|
|
|
if ( t.IsBasedOnGenericType( typeof( Dictionary<,> ) ) )
|
|
{
|
|
bs.Write( Identifier.Dictionary );
|
|
Serialize( Identifier.Dictionary, ref bs, obj );
|
|
return;
|
|
}
|
|
|
|
if ( GetOrCreatePacker( t ) is Packer packer )
|
|
{
|
|
packer.WriteTypeIdentifier( ref bs, t );
|
|
packer.Write( ref bs, obj );
|
|
return;
|
|
}
|
|
|
|
throw new System.NotSupportedException( $"Unhandled type {t}" );
|
|
}
|
|
|
|
private Packer GetOrCreatePacker( Type type )
|
|
{
|
|
if ( types.TryGetValue( type, out var packer ) )
|
|
return packer;
|
|
|
|
if ( OnCreatePackerFromType is null )
|
|
return null;
|
|
|
|
packer = OnCreatePackerFromType( type );
|
|
if ( packer is null ) return null;
|
|
|
|
packer.Init( this );
|
|
return packer;
|
|
}
|
|
|
|
|
|
private Packer GetOrCreatePacker( int typeIdentifier )
|
|
{
|
|
if ( typeHandler.TryGetValue( typeIdentifier, out var packer ) )
|
|
return packer;
|
|
|
|
if ( OnCreatePackerFromIdentifier is null )
|
|
return null;
|
|
|
|
packer = OnCreatePackerFromIdentifier( typeIdentifier );
|
|
if ( packer is null )
|
|
return null;
|
|
|
|
packer.Init( this );
|
|
return packer;
|
|
}
|
|
|
|
void Serialize( Identifier ident, ref ByteStream bs, object obj )
|
|
{
|
|
handlers[ident].Write( ref bs, obj );
|
|
}
|
|
}
|