SpawnActor stuff

This commit is contained in:
AeonLucid
2022-01-18 03:13:56 +01:00
parent 458bae0523
commit 16ab2aeb48
11 changed files with 262 additions and 9 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
namespace Prospect.Unreal.Core.Math; namespace Prospect.Unreal.Core.Math;
public ref struct FTransform public class FTransform
{ {
public FVector Location { get; set; } public FVector Location { get; set; }
public FRotator Rotation { get; set; } public FRotator Rotation { get; set; }
@@ -0,0 +1,6 @@
namespace Prospect.Unreal.Core.Objects;
public struct FObjectInstancingGraph
{
}
@@ -13,6 +13,6 @@ public class GUClassArray
public static UClass StaticClass(Type type) public static UClass StaticClass(Type type)
{ {
// TODO: Implement // TODO: Implement
return new UClass(); return new UClass(type);
} }
} }
@@ -4,5 +4,30 @@ namespace Prospect.Unreal.Core.Objects;
public class UClass : UStruct public class UClass : UStruct
{ {
public UClass(Type type)
{
Type = type;
}
public Type Type { get; }
public UObject GetDefaultObject<T>() where T : UObject
{
// TODO: Implement
var obj = (UObject) Activator.CreateInstance<T>();
obj.SetFlags(EObjectFlags.RF_Public | EObjectFlags.RF_ClassDefaultObject | EObjectFlags.RF_ArchetypeObject);
return obj;
}
public UObject GetDefaultObject(bool bCreateIfNeeded = true)
{
throw new NotImplementedException();
}
public UObject CreateDefaultObject()
{
throw new NotImplementedException();
}
} }
+5 -1
View File
@@ -2,5 +2,9 @@
public class UObject : UObjectBaseUtility public class UObject : UObjectBaseUtility
{ {
public bool CheckDefaultSubobjects(bool bForceCheck = false)
{
// TODO: Implement
return true;
}
} }
@@ -40,6 +40,12 @@ public class UObjectBaseUtility : UObjectBase
return false; return false;
} }
public bool IsPendingKill()
{
// TODO: GUObjectArray
return false;
}
/* /*
* Outer & Package * Outer & Package
*/ */
@@ -0,0 +1,34 @@
using Prospect.Unreal.Core.Names;
using Prospect.Unreal.Exceptions;
namespace Prospect.Unreal.Core.Objects;
public class UObjectGlobals
{
public static T? NewObject<T>(UObject outer,
UClass clazz,
FName? name = null,
EObjectFlags flags = EObjectFlags.RF_NoFlags,
UObject? template = null,
bool bCopyTransientsFromClassDefaults = false,
FObjectInstancingGraph? inInstanceGraph = null,
UPackage? externalPackage = null)
{
if (name == null)
{
name = EName.None;
}
// NewObject
// StaticConstructObject_Internal
// StaticAllocateObject
var obj = Activator.CreateInstance(clazz.Type);
if (obj == null)
{
throw new UnrealException("Failed to create object.");
}
return (T?) obj;
}
}
+26
View File
@@ -1,4 +1,5 @@
using Prospect.Unreal.Core; using Prospect.Unreal.Core;
using Prospect.Unreal.Core.Math;
using Prospect.Unreal.Core.Objects; using Prospect.Unreal.Core.Objects;
using Prospect.Unreal.Runtime; using Prospect.Unreal.Runtime;
@@ -7,6 +8,7 @@ namespace Prospect.Unreal.Net.Actors;
public class AActor : UObject public class AActor : UObject
{ {
private bool bActorInitialized; private bool bActorInitialized;
private bool bActorIsBeingDestroyed;
// TODO: UPROPERTY(BlueprintReadWrite, ReplicatedUsing=OnRep_Instigator, meta=(ExposeOnSpawn=true, AllowPrivateAccess=true), Category=Actor) // TODO: UPROPERTY(BlueprintReadWrite, ReplicatedUsing=OnRep_Instigator, meta=(ExposeOnSpawn=true, AllowPrivateAccess=true), Category=Actor)
/// <summary> /// <summary>
@@ -14,6 +16,12 @@ public class AActor : UObject
/// </summary> /// </summary>
private APawn? _instigator; private APawn? _instigator;
// TODO: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Actor)
/// <summary>
/// Controls how to handle spawning this actor in a situation where it's colliding with something else. "Default" means AlwaysSpawn here.
/// </summary>
public ESpawnActorCollisionHandlingMethod SpawnCollisionHandlingMethod { get; set; }
/// <summary> /// <summary>
/// Sets the value of Role without causing other side effects to this instance. /// Sets the value of Role without causing other side effects to this instance.
/// </summary> /// </summary>
@@ -81,4 +89,22 @@ public class AActor : UObject
{ {
return bActorInitialized; return bActorInitialized;
} }
public bool IsPendingKillPending()
{
return bActorIsBeingDestroyed || IsPendingKill();
}
public void PostSpawnInitialize(FTransform userSpawnTransform, AActor? inOwner, AActor? inInstigator, bool bRemoteOwned, bool bNoFail, bool bDeferConstruction)
{
// General flow here is like so
// - Actor sets up the basics.
// - Actor gets PreInitializeComponents()
// - Actor constructs itself, after which its components should be fully assembled
// - Actor components get OnComponentCreated
// - Actor components get InitializeComponent
// - Actor gets PostInitializeComponents() once everything is set up
//
// This should be the same sequence for deferred or nondeferred spawning.
}
} }
+24
View File
@@ -2,6 +2,7 @@
using System.Net; using System.Net;
using Prospect.Unreal.Core.Names; using Prospect.Unreal.Core.Names;
using Prospect.Unreal.Exceptions; using Prospect.Unreal.Exceptions;
using Prospect.Unreal.Net.Actors;
using Prospect.Unreal.Net.Channels; using Prospect.Unreal.Net.Channels;
using Prospect.Unreal.Net.Channels.Actor; using Prospect.Unreal.Net.Channels.Actor;
using Prospect.Unreal.Net.Channels.Control; using Prospect.Unreal.Net.Channels.Control;
@@ -269,6 +270,29 @@ public abstract class UNetDriver : IAsyncDisposable
}; };
} }
public void AddNetworkActor(AActor actor)
{
// if (!IsDormInitialStartupActor(Actor))
// {
// GetNetworkObjectList().FindOrAdd(Actor, this);
// if (ReplicationDriver)
// {
// ReplicationDriver->AddNetworkActor(Actor);
// }
// }
}
public void RemoveNetworkActor(AActor actor)
{
// Remove from renamed list if destroyed
// RenamedStartupActors.Remove(Actor->GetFName());
//
// if (ReplicationDriver)
// {
// ReplicationDriver->RemoveNetworkActor(Actor);
// }
}
public virtual ValueTask DisposeAsync() public virtual ValueTask DisposeAsync()
{ {
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
@@ -1,7 +1,9 @@
using System.Text; using System.Text;
using Prospect.Unreal.Core; using Prospect.Unreal.Core;
using Prospect.Unreal.Core.Math; using Prospect.Unreal.Core.Math;
using Prospect.Unreal.Core.Names;
using Prospect.Unreal.Core.Objects; using Prospect.Unreal.Core.Objects;
using Prospect.Unreal.Exceptions;
using Prospect.Unreal.Net; using Prospect.Unreal.Net;
using Prospect.Unreal.Net.Actors; using Prospect.Unreal.Net.Actors;
@@ -63,7 +65,7 @@ public partial class UWorld
return SpawnActor(clazz, transform, spawnParameters); return SpawnActor(clazz, transform, spawnParameters);
} }
public AActor SpawnActor(UClass? clazz, FTransform userTransform, FActorSpawnParameters spawnParameters) public AActor SpawnActor(UClass? clazz, FTransform? userTransformPtr, FActorSpawnParameters spawnParameters)
{ {
if (clazz == null) if (clazz == null)
{ {
@@ -84,10 +86,91 @@ public partial class UWorld
if (template == null) if (template == null)
{ {
// template = clazz.GetDefaultObject(); template = (AActor)clazz.GetDefaultObject<AActor>();
} }
return null; if (newActorName == EName.None)
{
// If we are using a template object and haven't specified a name, create a name relative to the template, otherwise let the default object naming behavior in Stat
if (!template.HasAnyFlags(EObjectFlags.RF_ClassDefaultObject))
{
throw new NotImplementedException();
}
}
/* else if (StaticFindObjectFast(nullptr, LevelToSpawnIn, NewActorName)) */
// See if we can spawn on ded.server/client only etc (check NeedsLoadForClient & NeedsLoadForServer)
// TODO: CanCreateInCurrentContext
var userTransform = userTransformPtr ?? new FTransform(); // TODO: FTransform::Identity
var collisionHandlingOverride = spawnParameters.SpawnCollisionHandlingOverride;
// "no fail" take preedence over collision handling settings that include fails
if (spawnParameters.bNoFail)
{
// maybe upgrade to disallow fail
if (collisionHandlingOverride == ESpawnActorCollisionHandlingMethod.AdjustIfPossibleButDontSpawnIfColliding)
{
collisionHandlingOverride = ESpawnActorCollisionHandlingMethod.AdjustIfPossibleButAlwaysSpawn;
}
else if (collisionHandlingOverride == ESpawnActorCollisionHandlingMethod.DontSpawnIfColliding)
{
collisionHandlingOverride = ESpawnActorCollisionHandlingMethod.AlwaysSpawn;
}
}
// use override if set, else fall back to actor's preference
var collisionHandlingMethod = (collisionHandlingOverride == ESpawnActorCollisionHandlingMethod.Undefined)
? template.SpawnCollisionHandlingMethod
: collisionHandlingOverride;
// see if we can avoid spawning altogether by checking native components
// note: we can't handle all cases here, since we don't know the full component hierarchy until after the actor is spawned
if (collisionHandlingMethod == ESpawnActorCollisionHandlingMethod.DontSpawnIfColliding)
{
throw new NotImplementedException();
}
var actorFlags = spawnParameters.ObjectFlags;
UPackage? externalPackage = null;
// actually make the actor object
var actor = UObjectGlobals.NewObject<AActor>(levelToSpawnIn, clazz, newActorName, actorFlags, template, false, null, externalPackage);
if (actor == null)
{
throw new UnrealException("Failed to create actor");
}
if (actor.GetLevel() != levelToSpawnIn)
{
throw new UnrealException("Actor spawned with the incorrect level");
}
// tell the actor what method to use, in case it was overridden
actor.SpawnCollisionHandlingMethod = collisionHandlingMethod;
actor.PostSpawnInitialize(userTransform, spawnParameters.Owner, spawnParameters.Instigator, spawnParameters.bRemoteOwned, spawnParameters.bNoFail, spawnParameters.bDeferConstruction);
// if we are spawning an external actor, clear the dirty flag after post spawn initialize which might have dirtied the level package through running construction scripts
if (externalPackage != null)
{
throw new NotImplementedException();
}
if (actor.IsPendingKill() && !spawnParameters.bNoFail)
{
// TODO: GetPathName
Logger.Debug("SpawnActor failed because the spawned actor %s IsPendingKill");
return null;
}
actor.CheckDefaultSubobjects();
// Add this newly spawned actor to the network actor list. Do this after PostSpawnInitialize so that actor has "finished" spawning.
AddNetworkActor(actor);
return actor;
} }
public T SpawnActor<T>(UClass? clazz, FActorSpawnParameters spawnParameters) where T : AActor public T SpawnActor<T>(UClass? clazz, FActorSpawnParameters spawnParameters) where T : AActor
+47 -2
View File
@@ -34,6 +34,8 @@ public abstract partial class UWorld : FNetworkNotify, IAsyncDisposable
_owningGameInstance = null; _owningGameInstance = null;
_authorityGameMode = null; _authorityGameMode = null;
_levels = new List<ULevel>(); _levels = new List<ULevel>();
Url = new FUrl();
} }
/// <summary> /// <summary>
@@ -64,7 +66,7 @@ public abstract partial class UWorld : FNetworkNotify, IAsyncDisposable
/// <summary> /// <summary>
/// The URL that was used when loading this World. /// The URL that was used when loading this World.
/// </summary> /// </summary>
public FUrl? Url { get; private set; } public FUrl Url { get; private set; }
/// <summary> /// <summary>
/// Time in seconds since level began play, but IS paused when the game is paused, and IS dilated/clamped. /// Time in seconds since level began play, but IS paused when the game is paused, and IS dilated/clamped.
@@ -156,7 +158,7 @@ public abstract partial class UWorld : FNetworkNotify, IAsyncDisposable
var options = inUrl.OptionsToString(); var options = inUrl.OptionsToString();
// Set level info. // Set level info.
if (!string.IsNullOrEmpty(inUrl.GetOption("load", null))) if (string.IsNullOrEmpty(inUrl.GetOption("load", null)))
{ {
Url = inUrl; Url = inUrl;
} }
@@ -446,6 +448,49 @@ public abstract partial class UWorld : FNetworkNotify, IAsyncDisposable
connection.SetClientLoginState(EClientLoginState.Welcomed); connection.SetClientLoginState(EClientLoginState.Welcomed);
} }
private void AddNetworkActor(AActor? actor)
{
if (actor == null)
{
Logger.Verbose("Failed to add actor, null");
return;
}
if (actor.IsPendingKillPending())
{
Logger.Verbose("Failed to add actor, IsPendingKillPending");
return;
}
var level = actor.GetLevel();
if (level == null || !ContainsLevel(level))
{
Logger.Verbose("Failed to add actor, world does not contain level");
return;
}
if (NetDriver != null)
{
NetDriver.AddNetworkActor(actor);
}
}
private void RemoveNetworkActor(AActor? actor)
{
if (actor != null)
{
if (NetDriver != null)
{
NetDriver.RemoveNetworkActor(actor);
}
}
}
private bool ContainsLevel(ULevel inLevel)
{
return _levels.Contains(inLevel);
}
public bool IsServer() public bool IsServer()
{ {
if (NetDriver != null) if (NetDriver != null)