game version overlay, working on actors sync

This commit is contained in:
2025-07-16 23:03:48 +02:00
parent 0da6f275c0
commit 4ec3dedd42
31 changed files with 1826 additions and 680 deletions

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using RebootKit.Engine.Extensions;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Main;
using Unity.Collections;
@@ -30,6 +31,10 @@ namespace RebootKit.Engine.Simulation {
foreach (Actor actor in m_InSceneActors) {
actor.ClientTick(Time.deltaTime);
}
foreach (Actor actor in m_SpawnedActors) {
actor.ClientTick(Time.deltaTime);
}
}
void OnServerTick(ulong tick) {
@@ -39,47 +44,82 @@ namespace RebootKit.Engine.Simulation {
float dt = 1.0f / RR.TickRate.IndexValue;
foreach (Actor actor in m_InSceneActors) {
actor.ServerTick(dt);
TickActorsList(m_InSceneActors, dt);
TickActorsList(m_SpawnedActors, dt);
}
void TickActorsList(List<Actor> actors, float deltaTime) {
foreach (Actor actor in actors) {
actor.ServerTick(deltaTime);
if (actor.IsDataDirty) {
actor.IsDataDirty = false;
NativeArray<byte> data = SerializeActorState(actor);
if (data.IsCreated) {
SynchronizeActorStateClientRpc(actor.ActorID, data);
SynchronizeActorStateWithClients(actor.ActorID, data);
} else {
s_Logger.Error($"Failed to serialize actor data for {actor.name}");
}
}
}
foreach (Actor actor in m_SpawnedActors) {
actor.ServerTick(dt);
if (actor.transformSyncMode != ActorTransformSyncMode.None && actor.MasterActor == null) {
ActorTransformSyncData syncData = actor.GetTransformSyncData();
if (actor.IsDataDirty) {
actor.IsDataDirty = false;
NativeArray<byte> data = SerializeActorState(actor);
if (data.IsCreated) {
SynchronizeActorStateClientRpc(actor.ActorID, data);
} else {
s_Logger.Error($"Failed to serialize actor data for {actor.name}");
foreach ((ulong _, NetworkClientState state) in RR.NetworkSystemInstance.Clients) {
if (state.IsReady) {
SynchronizeActorTransformStateRpc(actor.ActorID, syncData, RpcTarget.NotMe);
}
}
}
}
}
[ClientRpc(Delivery = RpcDelivery.Unreliable)]
void SynchronizeActorStateClientRpc(ulong actorID, NativeArray<byte> data) {
internal void SynchronizeActorCoreStateWithOther(Actor actor) {
if (!RR.IsServer()) {
s_Logger.Error("Only the server can synchronize actor core states.");
return;
}
SynchronizeCoreActorStateRpc(actor.ActorID, actor.GetCoreStateSnapshot(), RpcTarget.NotMe);
}
void SynchronizeActorStateWithClients(ulong actorID, NativeArray<byte> data) {
if (!RR.IsServer()) {
s_Logger.Error("Only the server can synchronize actor states with clients.");
return;
}
foreach ((ulong clientID, NetworkClientState state) in RR.NetworkSystemInstance.Clients) {
if (state.IsReady) {
SynchronizeActorStateRpc(actorID, data, RpcTarget.Single(clientID, RpcTargetUse.Temp));
}
}
}
[Rpc(SendTo.SpecifiedInParams, Delivery = RpcDelivery.Unreliable)]
void SynchronizeActorStateRpc(ulong actorID, NativeArray<byte> data, RpcParams rpcParams) {
Actor actor = FindActorByID(actorID);
if (actor is null) {
return;
}
s_Logger.Info($"Synchronizing actor state for {actor.name} with ID {actorID}");
DeserializeActorState(actor, data);
}
[Rpc(SendTo.SpecifiedInParams, Delivery = RpcDelivery.Unreliable)]
void SynchronizeActorTransformStateRpc(ulong actorID, ActorTransformSyncData syncData, RpcParams rpcParams) {
Actor actor = FindActorByID(actorID);
if (actor is null) {
s_Logger.Error($"Actor with ID {actorID} not found for transform synchronization.");
return;
}
actor.RestoreTransformState(syncData);
}
NativeArray<byte> SerializeActorState(Actor actor) {
return DataSerializationUtils.Serialize(actor.Data);
}
@@ -130,9 +170,9 @@ namespace RebootKit.Engine.Simulation {
continue;
}
SynchronizeActorStateForClientRpc(actor.ActorID, data, sendParams);
SynchronizeActorStateForClientRpc(actor.ActorID, actor.GetCoreStateSnapshot(), data, sendParams);
}
foreach (Actor actor in m_SpawnedActors) {
NativeArray<byte> data = SerializeActorState(actor);
if (!data.IsCreated) {
@@ -140,26 +180,41 @@ namespace RebootKit.Engine.Simulation {
continue;
}
ActorCoreStateSnapshot coreStateSnapshot = actor.GetCoreStateSnapshot();
SpawnActorRpc(actor.SourceActorPath,
actor.ActorID,
actor.transform.position,
actor.transform.localRotation,
coreStateSnapshot,
data,
sendParams);
}
}
[Rpc(SendTo.SpecifiedInParams, Delivery = RpcDelivery.Reliable)]
void SynchronizeActorStateForClientRpc(ulong actorID, NativeArray<byte> data, RpcParams rpcParams) {
void SynchronizeActorStateForClientRpc(ulong actorID,
ActorCoreStateSnapshot coreStateSnapshot,
NativeArray<byte> data,
RpcParams rpcParams) {
Actor actor = FindActorByID(actorID);
if (actor is null) {
return;
}
actor.RestoreCoreState(coreStateSnapshot);
DeserializeActorState(actor, data);
ClientSynchronizedActorRpc();
}
[Rpc(SendTo.SpecifiedInParams, Delivery = RpcDelivery.Reliable)]
void SynchronizeCoreActorStateRpc(ulong actorID, ActorCoreStateSnapshot snapshot, RpcParams rpcParams) {
Actor actor = FindActorByID(actorID);
if (actor is null) {
s_Logger.Error($"Actor with ID {actorID} not found for core state synchronization.");
return;
}
actor.RestoreCoreState(snapshot);
}
[Rpc(SendTo.Server, Delivery = RpcDelivery.Reliable)]
void ClientSynchronizedActorRpc(RpcParams rpcParams = default) {
ulong clientID = rpcParams.Receive.SenderClientId;
@@ -192,8 +247,21 @@ namespace RebootKit.Engine.Simulation {
actor.HandleActorCommand(cmd);
}
[Rpc(SendTo.Everyone)]
internal void SendActorEventToClientsRpc(ActorEvent actorEvent) {
internal void SendActorEvent(ActorEvent actorEvent) {
if (!RR.IsServer()) {
s_Logger.Error("Only the server can send actor events.");
return;
}
foreach ((ulong clientID, NetworkClientState state) in RR.NetworkSystemInstance.Clients) {
if (state.IsReady) {
SendActorEventRpc(actorEvent, RpcTarget.Single(clientID, RpcTargetUse.Temp));
}
}
}
[Rpc(SendTo.SpecifiedInParams)]
void SendActorEventRpc(ActorEvent actorEvent, RpcParams rpcParams) {
Actor actor = FindActorByID(actorEvent.ActorID);
if (actor is null) {
s_Logger.Error($"Actor with ID {actorEvent.ActorID} not found for event {actorEvent.EventID}");
@@ -221,7 +289,7 @@ namespace RebootKit.Engine.Simulation {
m_InSceneActors.Clear();
foreach (Actor actor in m_SpawnedActors) {
if (actor is not null) {
if (actor.OrNull() != null) {
Destroy(actor.gameObject);
}
}
@@ -250,42 +318,48 @@ namespace RebootKit.Engine.Simulation {
return null;
}
public void SpawnActor(AssetReferenceGameObject assetReference, Vector3 position, Quaternion rotation) {
public Actor SpawnActor(AssetReferenceGameObject assetReference, Vector3 position, Quaternion rotation) {
if (!IsServer) {
s_Logger.Error("Only the server can spawn actors.");
return;
return null;
}
if (!assetReference.RuntimeKeyIsValid()) {
s_Logger.Error("Trying to spawn an actor with an invalid asset reference.");
return;
return null;
}
ulong actorID = UniqueID.NewULongFromGuid();
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.");
Destroy(actorObject);
return;
return null;
}
actor.Manager = this;
actor.SourceActorPath = assetReference.AssetGUID;
actor.ActorID = UniqueID.NewULongFromGuid();
actor.ActorID = actorID;
actor.Data = actor.InternalCreateActorData();
m_SpawnedActors.Add(actor);
NativeArray<byte> stateData = SerializeActorState(actor);
SpawnActorRpc(assetReference.AssetGUID, actor.ActorID, position, rotation, stateData, RpcTarget.NotMe);
SpawnActorRpc(assetReference.AssetGUID,
actor.ActorID,
actor.GetCoreStateSnapshot(),
stateData,
RpcTarget.NotMe);
return actor;
}
// @NOTE: This RPC is used to spawn actors on clients.
[Rpc(SendTo.SpecifiedInParams)]
void SpawnActorRpc(string guid,
ulong actorID,
Vector3 position,
Quaternion rotation,
ActorCoreStateSnapshot coreStateSnapshot,
NativeArray<byte> stateData,
RpcParams rpcParams) {
AssetReferenceGameObject assetReference = new AssetReferenceGameObject(guid);
@@ -294,7 +368,9 @@ namespace RebootKit.Engine.Simulation {
return;
}
GameObject actorObject = assetReference.InstantiateAsync(position, rotation).WaitForCompletion();
GameObject actorObject = assetReference
.InstantiateAsync(coreStateSnapshot.Position, coreStateSnapshot.Rotation)
.WaitForCompletion();
if (actorObject == null) {
s_Logger.Error($"Failed to instantiate actor with GUID {guid}");
return;
@@ -312,6 +388,7 @@ namespace RebootKit.Engine.Simulation {
actor.ActorID = actorID;
actor.Data = actor.InternalCreateActorData();
actor.RestoreCoreState(coreStateSnapshot);
DeserializeActorState(actor, stateData);
m_SpawnedActors.Add(actor);
}
@@ -335,39 +412,5 @@ namespace RebootKit.Engine.Simulation {
Destroy(actor.gameObject);
}
public bool IsActorHidden(ulong actorID) {
Actor actor = FindActorByID(actorID);
if (actor is null) {
s_Logger.Error($"Actor with ID {actorID} not found.");
return false;
}
return !actor.gameObject.activeSelf;
}
public void SetActorHidden(ulong actorID, bool hidden) {
if (!IsServer) {
s_Logger.Error("Only the server can set actor visibility.");
return;
}
SetActorHiddenRpc(actorID, hidden, RpcTarget.Everyone);
}
[Rpc(SendTo.SpecifiedInParams)]
void SetActorHiddenRpc(ulong actorID, bool hidden, RpcParams rpcParams) {
Actor actor = FindActorByID(actorID);
if (actor is null) {
s_Logger.Error($"Actor with ID {actorID} not found.");
return;
}
if (hidden) {
actor.gameObject.SetActive(false);
} else {
actor.gameObject.SetActive(true);
}
}
}
}