This commit is contained in:
2025-08-31 20:46:27 +02:00
parent 2d06552025
commit 104259f79b
11 changed files with 310 additions and 137 deletions

View File

@@ -9,6 +9,7 @@ using Unity.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Assertions;
using UnityEngine.ResourceManagement.AsyncOperations;
using Logger = RebootKit.Engine.Foundation.Logger;
using Object = UnityEngine.Object;
@@ -21,14 +22,15 @@ namespace RebootKit.Engine.Simulation {
readonly NetworkSystem m_Network;
readonly List<Actor> m_InSceneActors = new List<Actor>();
readonly List<Actor> m_SpawnedActors = new List<Actor>();
internal readonly List<Actor> InSceneActors = new List<Actor>();
internal readonly List<Actor> SpawnedActors = new List<Actor>();
// @NOTE: 0 is reserved for no actor so we should start assigning IDs from 1.
ushort m_ActorIDCounter;
List<ushort> m_ActorIDFreeList = new List<ushort>(ushort.MaxValue);
public ushort InSceneActorsCount { get { return (ushort) m_InSceneActors.Count; } }
public ushort SpawnedActorsCount { get { return (ushort) m_SpawnedActors.Count; } }
public ushort InSceneActorsCount { get { return (ushort) InSceneActors.Count; } }
public ushort SpawnedActorsCount { get { return (ushort) SpawnedActors.Count; } }
public int TotalActorsCount { get { return InSceneActorsCount + SpawnedActorsCount; } }
public ActorsManager(NetworkSystem networkSystem) {
@@ -41,11 +43,11 @@ namespace RebootKit.Engine.Simulation {
// @MARK: Update
//
public void Tick(float deltaTime) {
foreach (Actor actor in m_InSceneActors) {
foreach (Actor actor in InSceneActors) {
actor.OnClientTick(deltaTime);
}
foreach (Actor actor in m_SpawnedActors) {
foreach (Actor actor in SpawnedActors) {
actor.OnClientTick(deltaTime);
}
}
@@ -58,8 +60,8 @@ namespace RebootKit.Engine.Simulation {
return;
}
TickActorsList(m_InSceneActors, dt);
TickActorsList(m_SpawnedActors, dt);
TickActorsList(InSceneActors, dt);
TickActorsList(SpawnedActors, dt);
}
void TickActorsList(List<Actor> actors, float deltaTime) {
@@ -137,19 +139,14 @@ namespace RebootKit.Engine.Simulation {
return null;
}
GameObject actorObject = assetReference.InstantiateAsync(position, rotation).WaitForCompletion();
Actor actor = actorObject.GetComponent<Actor>();
if (actor is null) {
s_Logger.Error($"GameObject {actorObject.name} does not have an Actor component.");
Object.Destroy(actorObject);
return null;
}
actor.Manager = this;
actor.SourceActorPath = assetReference.AssetGUID;
actor.ActorID = actorID;
actor.Data = actor.InternalCreateActorData();
m_SpawnedActors.Add(actor);
Actor actor = InstantiateActor(assetReference.AssetGUID,
actorID,
position,
rotation,
null,
default);
actor.IsLocalOnly = false;
SpawnedActors.Add(actor);
foreach (NetworkClientState client in m_Network.Clients.Values) {
if (client.IsServer) {
@@ -166,6 +163,18 @@ namespace RebootKit.Engine.Simulation {
}
bool TryGenerateNextActorID(out ushort actorID) {
if (m_ActorIDFreeList.Count > 0) {
actorID = m_ActorIDFreeList[0];
m_ActorIDFreeList.RemoveAtSwapBack(0);
return true;
}
if (m_ActorIDCounter >= ushort.MaxValue) {
s_Logger.Error("Reached maximum number of actor ids which is: " + ushort.MaxValue);
actorID = 0;
return false;
}
m_ActorIDCounter += 1;
actorID = m_ActorIDCounter;
return true;
@@ -178,44 +187,42 @@ namespace RebootKit.Engine.Simulation {
}
m_ActorIDCounter = 0;
m_ActorIDFreeList.Clear();
foreach (Actor actor in m_InSceneActors) {
foreach (Actor actor in InSceneActors) {
if (!TryGenerateNextActorID(out ushort actorID)) {
s_Logger.Error("Failed to generate actor ID. Probably reached the limit of 65535 actors.");
return;
}
actor.ActorID = actorID;
}
}
public Actor SpawnLocalOnlyActor(AssetReferenceGameObject assetReference, Vector3 position, Quaternion rotation) {
if (!assetReference.RuntimeKeyIsValid()) {
s_Logger.Error("Trying to spawn an actor with an invalid asset reference.");
return null;
public void DestroyActor(Actor actor) {
if (actor.IsLocalOnly) {
Object.Destroy(actor.gameObject);
return;
}
if (!TryGenerateNextActorID(out ushort actorID)) {
s_Logger.Error("Cannot spawn actor: Failed to generate next actor ID.");
return null;
if (!RR.IsServer()) {
s_Logger.Error("Only the server can destroy non-local only actors");
return;
}
GameObject actorObject = assetReference.InstantiateAsync(position, rotation).WaitForCompletion();
Actor actor = actorObject.GetComponent<Actor>();
if (actor is null) {
s_Logger.Error($"GameObject {actorObject.name} does not have an Actor component.");
Object.Destroy(actorObject);
return null;
if (InSceneActors.Contains(actor)) {
s_Logger.Error("InScene actors cannot be destroyed!");
return;
}
actor.Manager = this;
actor.IsLocalOnly = true;
actor.SourceActorPath = assetReference.AssetGUID;
actor.ActorID = actorID;
actor.Data = actor.InternalCreateActorData();
// m_SpawnedActors.Add(actor);
for (int i = SpawnedActors.Count - 1; i >= 0; --i) {
if (SpawnedActors[i] == actor) {
m_ActorIDFreeList.Add(actor.ActorID);
return;
}
}
return actor;
s_Logger.Error($"Failed to destroy an actor(name: {actor.ActorName}, id: {actor.ActorID})");
}
//
@@ -228,11 +235,11 @@ namespace RebootKit.Engine.Simulation {
actor.Manager = this;
m_InSceneActors.Add(actor);
InSceneActors.Add(actor);
}
Actor FindInSceneActorWithStaticID(ulong staticID) {
foreach (Actor actor in m_InSceneActors) {
foreach (Actor actor in InSceneActors) {
if (actor.ActorStaticID == staticID) {
return actor;
}
@@ -242,13 +249,13 @@ namespace RebootKit.Engine.Simulation {
}
public Actor FindActorByID(ushort actorID) {
foreach (Actor actor in m_InSceneActors) {
foreach (Actor actor in InSceneActors) {
if (actor.ActorID == actorID) {
return actor;
}
}
foreach (Actor actor in m_SpawnedActors) {
foreach (Actor actor in SpawnedActors) {
if (actor.ActorID == actorID) {
return actor;
}
@@ -258,15 +265,40 @@ namespace RebootKit.Engine.Simulation {
}
public void CleanUp() {
m_InSceneActors.Clear();
InSceneActors.Clear();
foreach (Actor actor in m_SpawnedActors) {
foreach (Actor actor in SpawnedActors) {
if (actor.OrNull() != null) {
Object.Destroy(actor.gameObject);
}
}
m_SpawnedActors.Clear();
SpawnedActors.Clear();
}
public Actor SpawnLocalOnlyActor(AssetReferenceGameObject assetReference, Vector3 position, Quaternion rotation) {
if (!assetReference.RuntimeKeyIsValid()) {
s_Logger.Error("Trying to spawn an actor with an invalid asset reference.");
return null;
}
AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync(assetReference, position, rotation);
GameObject actorObject = handle.WaitForCompletion();
Actor actor = actorObject.GetComponent<Actor>();
if (actor == null) {
s_Logger.Error($"GameObject {actorObject.name} does not have an Actor component.");
handle.Release();
return null;
}
actor.Manager = this;
actor.AssetHandle = handle;
actor.IsLocalOnly = true;
actor.SourceActorPath = assetReference.AssetGUID;
actor.ActorID = 0;
actor.Data = actor.InternalCreateActorData();
return actor;
}
///
@@ -355,50 +387,55 @@ namespace RebootKit.Engine.Simulation {
return;
}
SpawnLocalActor(assetGUID.ToString(),
coreState,
stateData);
InstantiateActor(assetGUID.ToString(),
coreState.ActorID,
coreState.Position,
coreState.Rotation,
coreState,
stateData);
}
void SpawnLocalActor(string guid,
ActorCoreStateSnapshot coreStateSnapshot,
NativeSlice<byte> stateData) {
Actor InstantiateActor(string guid,
ushort actorID,
Vector3 position,
Quaternion rotation,
ActorCoreStateSnapshot? coreStateSnapshot,
NativeSlice<byte> stateData) {
AssetReferenceGameObject assetReference = new AssetReferenceGameObject(guid);
if (!assetReference.RuntimeKeyIsValid()) {
s_Logger.Error($"Invalid asset reference for actor with GUID {guid}");
return;
return null;
}
GameObject actorObject = assetReference
.InstantiateAsync(coreStateSnapshot.Position, coreStateSnapshot.Rotation)
.WaitForCompletion();
if (actorObject == null) {
s_Logger.Error($"Failed to instantiate actor with GUID {guid}");
return;
}
AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync(assetReference, position, rotation);
GameObject actorObject = handle.WaitForCompletion();
Actor actor = actorObject.GetComponent<Actor>();
if (actor is null) {
if (actor == null) {
s_Logger.Error($"GameObject {actorObject.name} does not have an Actor component.");
Object.Destroy(actorObject);
return;
Addressables.ReleaseInstance(handle);
return null;
}
actor.Manager = this;
actor.AssetHandle = handle;
actor.SourceActorPath = guid;
actor.ActorID = coreStateSnapshot.ActorID;
actor.ActorID = actorID;
actor.Data = actor.InternalCreateActorData();
if (!RR.IsServer()) {
actor.InitializeOnClient();
}
actor.RestoreCoreState(coreStateSnapshot);
if (coreStateSnapshot.HasValue) {
actor.RestoreCoreState(coreStateSnapshot.Value);
}
if (stateData.Length > 0) {
DataSerializationUtils.Deserialize(stateData, ref actor.Data);
}
m_SpawnedActors.Add(actor);
return actor;
}
//
@@ -427,7 +464,7 @@ namespace RebootKit.Engine.Simulation {
internal void WriteInSceneActorsStates(NetworkBufferWriter writer) {
writer.Write((ushort) InSceneActorsCount);
foreach (Actor actor in m_InSceneActors) {
foreach (Actor actor in InSceneActors) {
writer.Write(actor.ActorStaticID);
writer.Write(actor.ActorID);
actor.GetCoreStateSnapshot().Serialize(writer);
@@ -443,7 +480,7 @@ namespace RebootKit.Engine.Simulation {
for (int i = 0; i < count; ++i) {
reader.Read(out ulong actorStaticID);
reader.Read(out ushort actorID);
s_Logger.Info($"Reading actor with StaticID {actorStaticID} and ID {actorID} during synchronization.");
Actor actor = FindInSceneActorWithStaticID(actorStaticID);
@@ -467,8 +504,8 @@ namespace RebootKit.Engine.Simulation {
actor.ActorID = actorID;
s_Logger.Info("Actor id set to " + actor.ActorID);
}
foreach (Actor inSceneActor in m_InSceneActors) {
foreach (Actor inSceneActor in InSceneActors) {
s_Logger.Info($"InSceneActor: StaticID={inSceneActor.ActorStaticID}, ID={inSceneActor.ActorID}, Path={inSceneActor.SourceActorPath}");
}
@@ -481,7 +518,7 @@ namespace RebootKit.Engine.Simulation {
return;
}
foreach (Actor actor in m_SpawnedActors) {
foreach (Actor actor in SpawnedActors) {
if (actor.OrNull() == null) {
continue;
}