refactor
This commit is contained in:
@@ -259,25 +259,35 @@ namespace RebootKit.Engine.Simulation {
|
||||
public bool IsDataDirty { get; protected internal set; }
|
||||
|
||||
internal ActorsManager Manager;
|
||||
|
||||
internal DateTime LastCoreStateSyncTime = DateTime.MinValue;
|
||||
|
||||
//
|
||||
// @MARK: Unity callbacks
|
||||
//
|
||||
void OnValidate() {
|
||||
if (ActorID == 0) {
|
||||
ActorID = UniqueID.NewULongFromGuid();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Callbacks to override in derived classes
|
||||
//
|
||||
|
||||
protected abstract IActorData CreateActorData();
|
||||
|
||||
// Override this method to implement server-side logic
|
||||
public virtual void ServerTick(float deltaTime) { }
|
||||
|
||||
// Override this method to implement client-side logic
|
||||
public virtual void ClientTick(float deltaTime) { }
|
||||
|
||||
// @NOTE: Server-side method to handle actor commands
|
||||
// @MARK: Server side
|
||||
public virtual void OnServerTick(float deltaTime) { }
|
||||
protected virtual void OnActorCommandServer(ActorCommand actorCommand) { }
|
||||
|
||||
// @NOTE: Client-side method to handle actor events
|
||||
// Override this method to implement client-side logic
|
||||
public virtual void OnClientTick(float deltaTime) { }
|
||||
protected virtual void OnActorEventClient(ActorEvent actorEvent) { }
|
||||
protected virtual void OnClientFinishedInitialSync() { }
|
||||
|
||||
//
|
||||
// @MARK: Server API
|
||||
//
|
||||
public void SetHidden(bool hidden) {
|
||||
if (!RR.IsServer()) {
|
||||
s_ActorLogger.Error($"Only the server can set actor visibility. Actor: {name} (ID: {ActorID})");
|
||||
@@ -383,7 +393,9 @@ namespace RebootKit.Engine.Simulation {
|
||||
Manager.SynchronizeActorCoreStateWithOther(this);
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Common API
|
||||
//
|
||||
public bool IsHidden() {
|
||||
return !gameObject.activeSelf;
|
||||
}
|
||||
@@ -457,7 +469,9 @@ namespace RebootKit.Engine.Simulation {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @MARK: Internal API
|
||||
//
|
||||
// @MARK: Internal
|
||||
//
|
||||
internal ActorCoreStateSnapshot GetCoreStateSnapshot() {
|
||||
ActorCoreStateSnapshot snapshot = new ActorCoreStateSnapshot();
|
||||
snapshot.Timestamp = DateTime.UtcNow;
|
||||
@@ -637,7 +651,11 @@ namespace RebootKit.Engine.Simulation {
|
||||
internal IActorData InternalCreateActorData() {
|
||||
return CreateActorData();
|
||||
}
|
||||
|
||||
|
||||
internal void InitialSyncFinished() {
|
||||
OnClientFinishedInitialSync();
|
||||
}
|
||||
|
||||
internal void HandleActorCommand(ActorCommand actorCommand) {
|
||||
if (!RR.IsServer()) {
|
||||
s_ActorLogger.Error($"Only the server can handle actor commands. Actor: {name} (ID: {ActorID})");
|
||||
@@ -672,13 +690,5 @@ namespace RebootKit.Engine.Simulation {
|
||||
|
||||
OnActorEventClient(actorEvent);
|
||||
}
|
||||
|
||||
|
||||
// @MARK: Unity lifecycle methods
|
||||
void OnValidate() {
|
||||
if (ActorID == 0) {
|
||||
ActorID = UniqueID.NewULongFromGuid();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,20 @@ using Logger = RebootKit.Engine.Foundation.Logger;
|
||||
namespace RebootKit.Engine.Simulation {
|
||||
// @TODO:
|
||||
// - Actors States might be packed into chunks to reduce the number of RPCs sent.
|
||||
// - Release addressables when they are no longer needed.
|
||||
public class ActorsManager : NetworkBehaviour {
|
||||
static readonly Logger s_Logger = new Logger(nameof(ActorsManager));
|
||||
|
||||
readonly List<Actor> m_InSceneActors = new List<Actor>();
|
||||
readonly List<Actor> m_SpawnedActors = new List<Actor>();
|
||||
|
||||
public int InSceneActorsCount { get { return m_InSceneActors.Count; } }
|
||||
public int SpawnedActorsCount { get { return m_SpawnedActors.Count; } }
|
||||
public int TotalActorsCount { get { return m_InSceneActors.Count + m_SpawnedActors.Count; } }
|
||||
|
||||
//
|
||||
// @MARK: NetworkBehaviour callbacks
|
||||
//
|
||||
public override void OnNetworkSpawn() {
|
||||
base.OnNetworkSpawn();
|
||||
RR.ServerTick += OnServerTick;
|
||||
@@ -27,16 +35,22 @@ namespace RebootKit.Engine.Simulation {
|
||||
RR.ServerTick -= OnServerTick;
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Unity callbacks
|
||||
//
|
||||
void Update() {
|
||||
foreach (Actor actor in m_InSceneActors) {
|
||||
actor.ClientTick(Time.deltaTime);
|
||||
actor.OnClientTick(Time.deltaTime);
|
||||
}
|
||||
|
||||
foreach (Actor actor in m_SpawnedActors) {
|
||||
actor.ClientTick(Time.deltaTime);
|
||||
actor.OnClientTick(Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Server-side logic
|
||||
//
|
||||
void OnServerTick(ulong tick) {
|
||||
if (!IsServer) {
|
||||
return;
|
||||
@@ -50,14 +64,14 @@ namespace RebootKit.Engine.Simulation {
|
||||
|
||||
void TickActorsList(List<Actor> actors, float deltaTime) {
|
||||
foreach (Actor actor in actors) {
|
||||
actor.ServerTick(deltaTime);
|
||||
actor.OnServerTick(deltaTime);
|
||||
|
||||
if (actor.IsDataDirty) {
|
||||
actor.IsDataDirty = false;
|
||||
|
||||
NativeArray<byte> data = SerializeActorState(actor);
|
||||
if (data.IsCreated) {
|
||||
SynchronizeActorStateWithClients(actor.ActorID, data);
|
||||
SendActorStateToClients(actor.ActorID, data);
|
||||
} else {
|
||||
s_Logger.Error($"Failed to serialize actor data for {actor.name}");
|
||||
}
|
||||
@@ -84,7 +98,7 @@ namespace RebootKit.Engine.Simulation {
|
||||
SynchronizeCoreActorStateRpc(actor.ActorID, actor.GetCoreStateSnapshot(), RpcTarget.NotMe);
|
||||
}
|
||||
|
||||
void SynchronizeActorStateWithClients(ulong actorID, NativeArray<byte> data) {
|
||||
void SendActorStateToClients(ulong actorID, NativeArray<byte> data) {
|
||||
if (!RR.IsServer()) {
|
||||
s_Logger.Error("Only the server can synchronize actor states with clients.");
|
||||
return;
|
||||
@@ -103,9 +117,8 @@ namespace RebootKit.Engine.Simulation {
|
||||
if (actor is null) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_Logger.Info($"Synchronizing actor state for {actor.name} with ID {actorID}");
|
||||
|
||||
s_Logger.Info($"Synchronizing actor state for {actor.name} with ID {actorID}");
|
||||
DeserializeActorState(actor, data);
|
||||
}
|
||||
|
||||
@@ -127,197 +140,10 @@ namespace RebootKit.Engine.Simulation {
|
||||
void DeserializeActorState(Actor actor, NativeArray<byte> data) {
|
||||
DataSerializationUtils.Deserialize(data, ref actor.Data);
|
||||
}
|
||||
|
||||
internal void SynchronizeActorsForClient(ulong clientID) {
|
||||
NetworkClientState clientState = RR.NetworkSystemInstance.GetClientState(clientID);
|
||||
if (clientState == null) {
|
||||
s_Logger.Error($"Client state for {clientID} not found. Cannot synchronize actors.");
|
||||
return;
|
||||
}
|
||||
|
||||
PrepareClientForActorsSyncRpc(RpcTarget.Single(clientState.ClientID, RpcTargetUse.Temp));
|
||||
}
|
||||
|
||||
[Rpc(SendTo.SpecifiedInParams, Delivery = RpcDelivery.Reliable)]
|
||||
void PrepareClientForActorsSyncRpc(RpcParams rpcParams) {
|
||||
foreach (Actor spawnedActor in m_SpawnedActors) {
|
||||
Destroy(spawnedActor.gameObject);
|
||||
}
|
||||
|
||||
m_SpawnedActors.Clear();
|
||||
|
||||
ClientIsReadyForActorsSyncRpc();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
void ClientIsReadyForActorsSyncRpc(RpcParams rpcParams = default) {
|
||||
ulong clientID = rpcParams.Receive.SenderClientId;
|
||||
NetworkClientState clientState = RR.NetworkSystemInstance.GetClientState(clientID);
|
||||
if (clientState == null) {
|
||||
s_Logger.Error($"Client state for {clientID} not found. Cannot mark client as ready for actors sync.");
|
||||
return;
|
||||
}
|
||||
|
||||
clientState.IsReadyForActorsSync = true;
|
||||
clientState.ActorsSyncPacketsLeft = m_InSceneActors.Count;
|
||||
|
||||
RpcSendParams sendParams = RpcTarget.Single(clientID, RpcTargetUse.Temp);
|
||||
|
||||
foreach (Actor actor in m_InSceneActors) {
|
||||
NativeArray<byte> data = SerializeActorState(actor);
|
||||
if (!data.IsCreated) {
|
||||
s_Logger.Error($"Failed to serialize actor data for {actor.name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
SynchronizeActorStateForClientRpc(actor.ActorID, actor.GetCoreStateSnapshot(), data, sendParams);
|
||||
}
|
||||
|
||||
foreach (Actor actor in m_SpawnedActors) {
|
||||
NativeArray<byte> data = SerializeActorState(actor);
|
||||
if (!data.IsCreated) {
|
||||
s_Logger.Error($"Failed to serialize actor data for {actor.name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
ActorCoreStateSnapshot coreStateSnapshot = actor.GetCoreStateSnapshot();
|
||||
SpawnActorRpc(actor.SourceActorPath,
|
||||
actor.ActorID,
|
||||
coreStateSnapshot,
|
||||
data,
|
||||
sendParams);
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.SpecifiedInParams, Delivery = RpcDelivery.Reliable)]
|
||||
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;
|
||||
|
||||
NetworkClientState clientState = RR.NetworkSystemInstance.GetClientState(clientID);
|
||||
if (clientState == null) {
|
||||
s_Logger.Error($"Client state for {clientID} not found. Cannot mark client as synchronized.");
|
||||
return;
|
||||
}
|
||||
|
||||
clientState.ActorsSyncPacketsLeft--;
|
||||
if (clientState.ActorsSyncPacketsLeft == 0) {
|
||||
RR.NetworkSystemInstance.ClientSynchronizedActors(clientID);
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, Delivery = RpcDelivery.Reliable)]
|
||||
internal void SendActorCommandToServerRpc(ActorCommand cmd) {
|
||||
if (!IsServer) {
|
||||
s_Logger.Error("Only the server can handle actor events.");
|
||||
return;
|
||||
}
|
||||
|
||||
Actor actor = FindActorByID(cmd.ActorID);
|
||||
if (actor is null) {
|
||||
s_Logger.Error($"Actor with ID {cmd.ActorID} not found for command {cmd.CommandID}");
|
||||
return;
|
||||
}
|
||||
|
||||
actor.HandleActorCommand(cmd);
|
||||
}
|
||||
|
||||
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}");
|
||||
return;
|
||||
}
|
||||
|
||||
actor.HandleActorEvent(actorEvent);
|
||||
}
|
||||
|
||||
public void RegisterInSceneActor(Actor actor) {
|
||||
if (actor.Data == null) {
|
||||
actor.Data = actor.InternalCreateActorData();
|
||||
}
|
||||
|
||||
actor.Manager = this;
|
||||
|
||||
m_InSceneActors.Add(actor);
|
||||
}
|
||||
|
||||
public void CleanUp() {
|
||||
if (IsServer) {
|
||||
CleanUpRpc();
|
||||
}
|
||||
|
||||
m_InSceneActors.Clear();
|
||||
|
||||
foreach (Actor actor in m_SpawnedActors) {
|
||||
if (actor.OrNull() != null) {
|
||||
Destroy(actor.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
m_SpawnedActors.Clear();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.NotMe)]
|
||||
void CleanUpRpc() {
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
public Actor FindActorByID(ulong actorID) {
|
||||
foreach (Actor actor in m_InSceneActors) {
|
||||
if (actor.ActorID == actorID) {
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Actor actor in m_SpawnedActors) {
|
||||
if (actor.ActorID == actorID) {
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// @MARK: Server API
|
||||
//
|
||||
public Actor SpawnActor(AssetReferenceGameObject assetReference, Vector3 position, Quaternion rotation) {
|
||||
if (!IsServer) {
|
||||
s_Logger.Error("Only the server can spawn actors.");
|
||||
@@ -412,5 +238,205 @@ namespace RebootKit.Engine.Simulation {
|
||||
|
||||
Destroy(actor.gameObject);
|
||||
}
|
||||
|
||||
public void CleanUp() {
|
||||
if (IsServer) {
|
||||
CleanUpRpc();
|
||||
}
|
||||
|
||||
m_InSceneActors.Clear();
|
||||
|
||||
foreach (Actor actor in m_SpawnedActors) {
|
||||
if (actor.OrNull() != null) {
|
||||
Destroy(actor.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
m_SpawnedActors.Clear();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.NotMe)]
|
||||
void CleanUpRpc() {
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Common API
|
||||
//
|
||||
public void RegisterInSceneActor(Actor actor) {
|
||||
if (actor.Data == null) {
|
||||
actor.Data = actor.InternalCreateActorData();
|
||||
}
|
||||
|
||||
actor.Manager = this;
|
||||
|
||||
m_InSceneActors.Add(actor);
|
||||
}
|
||||
|
||||
public Actor FindActorByID(ulong actorID) {
|
||||
foreach (Actor actor in m_InSceneActors) {
|
||||
if (actor.ActorID == actorID) {
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Actor actor in m_SpawnedActors) {
|
||||
if (actor.ActorID == actorID) {
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Initial synchronization
|
||||
//
|
||||
internal void InitializeActorsForClient(ulong clientID) {
|
||||
if (RR.NetworkSystemInstance.TryGetClientState(clientID, out NetworkClientState clientState)) {
|
||||
clientState.SyncState = NetworkClientSyncState.PreparingForActorsSync;
|
||||
RR.NetworkSystemInstance.UpdateClientState(clientState);
|
||||
PrepareClientForActorsSyncRpc(RpcTarget.Single(clientState.ClientID, RpcTargetUse.Temp));
|
||||
} else {
|
||||
s_Logger.Error($"Client state for {clientID} not found. Cannot synchronize actors.");
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.SpecifiedInParams, Delivery = RpcDelivery.Reliable)]
|
||||
void PrepareClientForActorsSyncRpc(RpcParams rpcParams) {
|
||||
foreach (Actor spawnedActor in m_SpawnedActors) {
|
||||
Destroy(spawnedActor.gameObject);
|
||||
}
|
||||
|
||||
m_SpawnedActors.Clear();
|
||||
|
||||
ClientIsReadyForActorsSyncRpc();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server)]
|
||||
void ClientIsReadyForActorsSyncRpc(RpcParams rpcParams = default) {
|
||||
ulong clientID = rpcParams.Receive.SenderClientId;
|
||||
if (!RR.NetworkSystemInstance.TryGetClientState(clientID, out NetworkClientState clientState)) {
|
||||
s_Logger.Error($"Client state for {clientID} not found. Cannot mark client as ready for actors sync.");
|
||||
return;
|
||||
}
|
||||
|
||||
clientState.SyncState = NetworkClientSyncState.SyncingActors;
|
||||
clientState.ActorsSyncPacketsLeft = m_InSceneActors.Count;
|
||||
RR.NetworkSystemInstance.UpdateClientState(clientState);
|
||||
|
||||
RpcSendParams sendParams = RpcTarget.Single(clientID, RpcTargetUse.Temp);
|
||||
|
||||
foreach (Actor actor in m_InSceneActors) {
|
||||
NativeArray<byte> data = SerializeActorState(actor);
|
||||
if (!data.IsCreated) {
|
||||
s_Logger.Error($"Failed to serialize actor data for {actor.name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
SynchronizeActorStateForClientRpc(actor.ActorID, actor.GetCoreStateSnapshot(), data, sendParams);
|
||||
}
|
||||
|
||||
foreach (Actor actor in m_SpawnedActors) {
|
||||
NativeArray<byte> data = SerializeActorState(actor);
|
||||
if (!data.IsCreated) {
|
||||
s_Logger.Error($"Failed to serialize actor data for {actor.name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
ActorCoreStateSnapshot coreStateSnapshot = actor.GetCoreStateSnapshot();
|
||||
SpawnActorRpc(actor.SourceActorPath,
|
||||
actor.ActorID,
|
||||
coreStateSnapshot,
|
||||
data,
|
||||
sendParams);
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.SpecifiedInParams, Delivery = RpcDelivery.Reliable)]
|
||||
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;
|
||||
|
||||
if (!RR.NetworkSystemInstance.TryGetClientState(clientID, out NetworkClientState clientState)) {
|
||||
s_Logger.Error($"Client state for {clientID} not found. Cannot mark client as synchronized.");
|
||||
return;
|
||||
}
|
||||
|
||||
clientState.ActorsSyncPacketsLeft--;
|
||||
RR.NetworkSystemInstance.UpdateClientState(clientState);
|
||||
|
||||
if (clientState.ActorsSyncPacketsLeft == 0) {
|
||||
RR.NetworkSystemInstance.ClientSynchronizedActors(clientID);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Actor Commands and Events
|
||||
//
|
||||
[Rpc(SendTo.Server, Delivery = RpcDelivery.Reliable)]
|
||||
internal void SendActorCommandToServerRpc(ActorCommand cmd) {
|
||||
if (!IsServer) {
|
||||
s_Logger.Error("Only the server can handle actor events.");
|
||||
return;
|
||||
}
|
||||
|
||||
Actor actor = FindActorByID(cmd.ActorID);
|
||||
if (actor is null) {
|
||||
s_Logger.Error($"Actor with ID {cmd.ActorID} not found for command {cmd.CommandID}");
|
||||
return;
|
||||
}
|
||||
|
||||
actor.HandleActorCommand(cmd);
|
||||
}
|
||||
|
||||
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}");
|
||||
return;
|
||||
}
|
||||
|
||||
actor.HandleActorEvent(actorEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user