mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-02 11:28:19 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
155 lines
5.2 KiB
C#
155 lines
5.2 KiB
C#
using System.Text.Json.Serialization;
|
|
|
|
namespace Sandbox;
|
|
|
|
#nullable enable
|
|
|
|
/// <summary>
|
|
/// A serialized reference to a <see cref="Component"/> that can be resolved at runtime.
|
|
/// Components are referenced by their <see cref="Component.Id"/>, their containing object's
|
|
/// <see cref="GameObject.Id"/>, and their <see cref="TypeDescription.ClassName"/> if available.
|
|
/// </summary>
|
|
[ActionGraphExposeWhenCached]
|
|
internal readonly struct ComponentReference
|
|
{
|
|
private const string ExpectedReferenceType = "component";
|
|
|
|
/// <summary>
|
|
/// Reference a given <see cref="Component"/>.
|
|
/// </summary>
|
|
public static ComponentReference FromInstance( Component component )
|
|
{
|
|
var t = Game.TypeLibrary.GetType( component.GetType() );
|
|
return new ComponentReference( ExpectedReferenceType, component.Id, component.GameObject.Id, t?.ClassName );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a <see cref="ComponentReference"/> into a <see cref="GameObjectReference"/>, referencing the object
|
|
/// that contains the component.
|
|
/// </summary>
|
|
public static explicit operator GameObjectReference( ComponentReference componentRef )
|
|
{
|
|
return GameObjectReference.FromId( componentRef.GameObjectId );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Expected to be <c>"component"</c> for a component reference.
|
|
/// </summary>
|
|
[JsonPropertyName( "_type" )]
|
|
public string ReferenceType { get; }
|
|
|
|
/// <summary>
|
|
/// The <see cref="Component.Id"/> of the referenced component.
|
|
/// </summary>
|
|
[JsonPropertyName( "component_id" )]
|
|
public Guid ComponentId { get; }
|
|
|
|
/// <summary>
|
|
/// The <see cref="GameObject.Id"/> of the object containing the referenced component.
|
|
/// </summary>
|
|
[JsonPropertyName( "go" )]
|
|
public Guid GameObjectId { get; }
|
|
|
|
/// <summary>
|
|
/// If available, the <see cref="TypeDescription.ClassName"/> of the referenced component.
|
|
/// </summary>
|
|
[JsonPropertyName( "component_type" )]
|
|
public string? ComponentTypeName { get; }
|
|
|
|
[JsonConstructor]
|
|
private ComponentReference( string referenceType, Guid componentId, Guid gameObjectId, string? componentTypeName = null )
|
|
{
|
|
ReferenceType = referenceType;
|
|
ComponentId = componentId;
|
|
GameObjectId = gameObjectId;
|
|
ComponentTypeName = componentTypeName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to resolve <see cref="ComponentTypeName"/> into a <see cref="Type"/>. Returns <see langword="null"/> if not resolved.
|
|
/// </summary>
|
|
/// <param name="targetType">Optional base type / interface that the resolved type must derive from / implement. Defaults to <see cref="Component"/>.</param>
|
|
public Type? ResolveComponentType( Type? targetType = null )
|
|
{
|
|
return Game.TypeLibrary.GetType( targetType ?? typeof( Component ), ComponentTypeName, true )?.TargetType;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to resolve this reference in the current <see cref="Game.ActiveScene"/>. Returns <see langword="null"/> if
|
|
/// the reference couldn't be resolved, and logs a warning.
|
|
/// </summary>
|
|
public Component? Resolve() => Resolve( Game.ActiveScene );
|
|
|
|
/// <summary>
|
|
/// Attempt to resolve this reference in the given <paramref name="scene"/>. Returns <see langword="null"/> if
|
|
/// the reference couldn't be resolved.
|
|
/// </summary>
|
|
/// <param name="scene">Scene to attempt to resolve the reference in.</param>
|
|
/// <param name="targetType">Optional base type / interface that the resolved instance must derive from / implement. Defaults to <see cref="Component"/>.</param>
|
|
/// <param name="warn">If true, log a warning to the console if the reference couldn't be resolved.</param>
|
|
public Component? Resolve( Scene scene, Type? targetType = null, bool warn = false )
|
|
{
|
|
if ( ReferenceType != ExpectedReferenceType )
|
|
throw new( $"Tried to deserialize unknown type '{ReferenceType ?? "null"}' as Component" );
|
|
|
|
// TODO: Do we want to throw when not found? We're not doing that in GameObjectReference.Resolve
|
|
|
|
targetType ??= typeof( Component );
|
|
|
|
Component? component = null;
|
|
|
|
if ( ComponentId != Guid.Empty )
|
|
{
|
|
if ( !scene.IsValid() )
|
|
{
|
|
if ( warn ) Log.Warning( "Tried to read component - but active scene was null!" );
|
|
|
|
return null;
|
|
}
|
|
|
|
component = scene.Directory.FindComponentByGuid( ComponentId );
|
|
|
|
if ( component is not null )
|
|
return component;
|
|
}
|
|
|
|
if ( GameObjectId != Guid.Empty )
|
|
{
|
|
if ( !scene.IsValid() )
|
|
{
|
|
if ( warn ) Log.Warning( "Tried to read component - but active scene was null!" );
|
|
|
|
return null;
|
|
}
|
|
|
|
var go = scene.Directory.FindByGuid( GameObjectId );
|
|
|
|
if ( !go.IsValid() )
|
|
{
|
|
// Conna: this is okay - we may be deserializing over the network and this is a property
|
|
// referring to a GameObject that we don't know about. Let's just make the reference null.
|
|
if ( warn ) Log.Warning( $"Unknown GameObject {GameObjectId}" );
|
|
|
|
return null;
|
|
}
|
|
|
|
if ( !string.IsNullOrWhiteSpace( ComponentTypeName ) )
|
|
{
|
|
if ( ResolveComponentType( targetType ) is not { } resolvedType )
|
|
throw new( $"Unable to find type '{ComponentTypeName}' with base type '{targetType.Name}'" );
|
|
|
|
targetType = resolvedType;
|
|
}
|
|
|
|
component = go.Components.Get( targetType, FindMode.EverythingInSelf );
|
|
|
|
if ( component is not null )
|
|
return component;
|
|
|
|
throw new( $"Component '{go}:{targetType}' was not found" );
|
|
}
|
|
|
|
throw new( $"Component '{ComponentId}' was not found" );
|
|
}
|
|
}
|