Code/Client.cs
namespace ShrimplePawns;
/// <summary>
/// The base client that you should inherit off of.
/// </summary>
public abstract class Client : Component
{
[Sync( SyncFlags.FromHost )]
public System.Guid ConnectionId { get; private set; }
[Sync( SyncFlags.FromHost )]
protected Pawn Pawn { get; set; }
public Connection Connection => Connection.Find( ConnectionId );
/// <summary>
/// Get the pawn only if it is valid.
/// </summary>
public bool TryGetPawn<T>( out T pawn ) where T : Pawn
{
pawn = GetPawn<T>();
return pawn.IsValid();
}
/// <summary>
/// Get the pawn.
/// </summary>
public T GetPawn<T>() where T : Pawn
{
return Pawn as T;
}
/// <summary>
/// Get the pawn.
/// </summary>
public Pawn GetPawn()
{
return Pawn;
}
/// <summary>
/// Assigns the given connection as the client's current connection.
/// This connection will be used to give ownership to any future assigned pawns.
/// This must be called on the host!
/// </summary>
/// <param name="connection">The connection to assign to the client.</param>
public void AssignConnection( Connection connection )
{
if ( !Connection.Local.IsHost )
{
Log.Warning( "Failed to call AssignConnection(...) due to being invoked on non-host client!" );
return;
}
if ( connection == null )
{
Log.Warning( "Failed to call AssignConnection(...) due to null connection param!" );
return;
}
ConnectionId = connection.Id;
GameObject.Name = $"{connection.DisplayName} - CLIENT";
}
/// <summary>
/// Creates a <see cref="GameObject" /> from the given prefab file and assigns it as the client's current pawn.
/// This must be called on the host!
/// </summary>
/// <param name="prefabFile">The prefab file that is used to create the pawn.</param>
public Pawn AssignPawn( PrefabFile prefabFile )
{
var obj = SceneUtility.GetPrefabScene( prefabFile ).Clone();
return InternalAssign( obj );
}
/// <summary>
/// Assigns the given pawn type as the client's current pawn.
/// The pawn type must have a <see cref="PawnAttribute" /> defined in order to use this method.
/// This must be called on the host!
/// </summary>
/// <returns>The pawn component that the client was assigned to.</returns>
public T AssignPawn<T>() where T : Pawn
{
var pawnAttribute = TypeLibrary.GetType<T>().GetAttribute<PawnAttribute>();
var path = pawnAttribute?.PrefabPath;
if ( string.IsNullOrEmpty( path ) )
{
Log.Warning( $"{typeof( T )} had no PawnAttribute prefab assigned to it." );
return null;
}
var obj = SceneUtility.GetPrefabScene( ResourceLibrary.Get<PrefabFile>( path ) ).Clone();
return InternalAssign<T>( obj ); ;
}
/// <summary>
/// Takes a <see cref="GameObject" /> that already exists, networked spawns it, and assigns it to the client.
/// This must be called on the host!
/// </summary>
/// <param name="obj">The pawn game object that will be networked spawned and assigned to the client.</param>
public Pawn AssignPawn( GameObject obj )
{
return InternalAssign( obj );
}
private T InternalAssign<T>( GameObject obj ) where T : Pawn
{
return (T)InternalAssign( obj );
}
private Pawn InternalAssign( GameObject obj )
{
if ( !Connection.Local?.IsHost ?? false )
{
obj.Destroy();
Log.Warning( "Attempted to call AssignPawn(...) on non-host client!" );
return null;
}
var pawn = obj.Components.Get<Pawn>();
if ( !pawn.IsValid() )
{
obj.Destroy();
Log.Warning( $"Assigned GameObject ({obj.Name}) with no pawn component!" );
return null;
}
if ( Pawn.IsValid() )
Pawn.OnUnassign();
var assignedConnection = Connection ?? Connection.Host;
if ( !obj.Network.Active )
obj.NetworkSpawn( assignedConnection );
else
obj.Network.AssignOwnership( assignedConnection );
obj.Name = $"{assignedConnection.DisplayName} - PAWN";
Pawn = pawn;
Pawn.OnAssign( this );
return pawn;
}
}