optimizing network stuff

This commit is contained in:
2025-07-30 05:51:39 +02:00
parent ea99249fe2
commit 159e9adcd7
56 changed files with 2272 additions and 1781 deletions

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using RebootKit.Engine.Foundation;
@@ -6,8 +7,9 @@ using RebootKit.Engine.Main;
using RebootKit.Engine.Network;
using TriInspector;
using Unity.Collections;
using Unity.Netcode;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Assertions;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Simulation {
@@ -24,59 +26,54 @@ namespace RebootKit.Engine.Simulation {
}
public struct ActorCommand : ISerializableEntity {
public ulong ActorID;
public ulong ClientID;
public ushort CommandID;
public ushort ActorID;
public byte CommandID;
public NativeArray<byte> Data;
public int GetMaxBytes() {
return sizeof(ulong) + // ActorID
sizeof(ulong) + // ClientID
sizeof(ushort) + // CommandID
sizeof(ushort) + // Data length
sizeof(byte) * Data.Length; // Data
return sizeof(ushort) + // ActorID
sizeof(byte) + // CommandID
sizeof(byte) + // Data length
sizeof(byte) * Data.Length; // Data
}
public void Serialize(NetworkBufferWriter writer) {
writer.Write(ActorID);
writer.Write(ClientID);
writer.Write(CommandID);
writer.Write((ushort) Data.Length);
writer.Write((byte) Data.Length);
if (Data.IsCreated) {
writer.Write(Data);
}
}
public void Deserialize(NetworkBufferReader reader) {
reader.Read(out ActorID);
reader.Read(out ClientID);
reader.Read(out CommandID);
reader.Read(out ushort dataLength);
reader.Read(out byte dataLength);
if (dataLength > 0) {
reader.Read(out Data, dataLength, Allocator.Temp);
}
}
}
// @NOTE: ActorEvent is used to send events from the server to clients and only clients.
// Server should not receive ActorEvents.
public struct ActorEvent : ISerializableEntity {
public ulong ActorID;
public ushort EventID;
public ushort ActorID;
public byte EventID;
public NativeArray<byte> Data;
public int GetMaxBytes() {
return sizeof(ulong) + // ActorID
sizeof(ushort) + // EventID
sizeof(ushort) + // Data length
return sizeof(ushort) + // ActorID
sizeof(byte) + // EventID
sizeof(byte) + // Data length
sizeof(byte) * Data.Length; // Data
}
public void Serialize(NetworkBufferWriter writer) {
writer.Write(ActorID);
writer.Write(EventID);
Assert.IsTrue(Data.Length < byte.MaxValue, "Data of ActorEvent is too large to fit in a byte.");
writer.Write((ushort) Data.Length);
writer.Write((byte) Data.Length);
if (Data.IsCreated) {
writer.Write(Data);
}
@@ -85,7 +82,7 @@ namespace RebootKit.Engine.Simulation {
public void Deserialize(NetworkBufferReader reader) {
reader.Read(out ActorID);
reader.Read(out EventID);
reader.Read(out ushort dataLength);
reader.Read(out byte dataLength);
if (dataLength > 0) {
reader.Read(out Data, dataLength, Allocator.Temp);
@@ -94,13 +91,14 @@ namespace RebootKit.Engine.Simulation {
}
[Flags]
enum ActorPhysicsFlags : byte {
enum ActorFlags : byte {
None = 0,
IsKinematic = 1 << 0,
Hidden = 1 << 0,
DisableColliders = 1 << 1,
}
struct ActorCoreStateSnapshot : ISerializableEntity {
public ushort ActorID;
public DateTime Timestamp;
// @NOTE: Position, Rotation, and Scale are in local space.
@@ -108,102 +106,65 @@ namespace RebootKit.Engine.Simulation {
public Quaternion Rotation;
public Vector3 Scale;
public bool IsHidden;
public ActorPhysicsFlags Flags;
public ActorFlags Flags;
public ulong MasterActorID;
public ushort MasterActorID;
public FixedString32Bytes MasterSocketName;
public void Serialize(NetworkBufferWriter writer) {
writer.Write(ActorID);
writer.Write(Timestamp.Ticks);
writer.Write(Position);
writer.Write(Rotation);
writer.Write(Scale);
writer.Write(IsHidden);
writer.Write((byte) Flags);
writer.Write(MasterActorID);
writer.Write(MasterSocketName);
}
public void Deserialize(NetworkBufferReader reader) {
reader.Read(out ActorID);
reader.Read(out long ticks);
Timestamp = new DateTime(ticks, DateTimeKind.Utc);
reader.Read(out Position);
reader.Read(out Rotation);
reader.Read(out Scale);
reader.Read(out IsHidden);
reader.Read(out byte flagsByte);
Flags = (ActorPhysicsFlags) flagsByte;
Flags = (ActorFlags) flagsByte;
reader.Read(out MasterActorID);
reader.Read(out MasterSocketName);
}
public int GetMaxBytes() {
return sizeof(long) + // Timestamp
return sizeof(ushort) + // ActorID
sizeof(long) + // Timestamp
sizeof(float) * 3 + // Position
sizeof(float) * 4 + // Rotation (Quaternion)
sizeof(float) * 3 + // Scale
sizeof(bool) + // IsHidden
sizeof(byte) + // Flags
sizeof(ulong) + // MasterActorID
sizeof(ushort) + // MasterActorID
sizeof(byte) * 32; // MasterSocketName
}
}
///
/// Represents the synchronization mode for actor transforms (and rigidbody).
/// @TODO: Might be a good idea to keep client-side actors rigidbody as kinematic and simulate physics only on the server.
/// IMPORTANT:
/// - Position, Rotation, and Scale are in local space.
/// - Velocity and AngularVelocity are only used if UsingRigidbody is set.
/// - When Actor is mounted to another actor, sync won't happen.
///
[Flags]
public enum ActorTransformSyncMode : byte {
None = 0,
Position = 1 << 0,
Rotation = 1 << 1,
Scale = 1 << 2,
// @NOTE: If this is set, Position and Rotation will be synced using Rigidbody's position and rotation.
UsingRigidbody = 1 << 3,
Velocity = 1 << 4, // @NOTE: Velocity is only used if UsingRigidbody is set.
AngularVelocity = 1 << 5 // @NOTE: AngularVelocity is only used if UsingRigidbody is set.
Scale = 1 << 2
}
public struct ActorTransformSyncData : ISerializableEntity {
public ushort ActorID;
public ActorTransformSyncMode SyncMode;
public Vector3 Position;
public Quaternion Rotation;
public Vector3 Rotation;
public Vector3 Scale;
public Vector3 Velocity;
public Vector3 AngularVelocity;
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter {
serializer.SerializeValue(ref SyncMode);
if ((SyncMode & ActorTransformSyncMode.Position) != 0) {
serializer.SerializeValue(ref Position);
}
if ((SyncMode & ActorTransformSyncMode.Rotation) != 0) {
serializer.SerializeValue(ref Rotation);
}
if ((SyncMode & ActorTransformSyncMode.Scale) != 0) {
serializer.SerializeValue(ref Scale);
}
if ((SyncMode & ActorTransformSyncMode.Velocity) != 0) {
serializer.SerializeValue(ref Velocity);
}
if ((SyncMode & ActorTransformSyncMode.AngularVelocity) != 0) {
serializer.SerializeValue(ref AngularVelocity);
}
}
public void Serialize(NetworkBufferWriter writer) {
writer.Write(ActorID);
writer.Write((byte) SyncMode);
if ((SyncMode & ActorTransformSyncMode.Position) != 0) {
@@ -211,23 +172,26 @@ namespace RebootKit.Engine.Simulation {
}
if ((SyncMode & ActorTransformSyncMode.Rotation) != 0) {
writer.Write(Rotation);
Rotation.x = Mathf.Repeat(Rotation.x, 360.0f);
Rotation.y = Mathf.Repeat(Rotation.y, 360.0f);
Rotation.z = Mathf.Repeat(Rotation.z, 360.0f);
ushort rotX = QuantizationUtility.FloatToUShort(Rotation.x, 0.0f, 360.0f);
ushort rotY = QuantizationUtility.FloatToUShort(Rotation.y, 0.0f, 360.0f);
ushort rotZ = QuantizationUtility.FloatToUShort(Rotation.z, 0.0f, 360.0f);
writer.Write(rotX);
writer.Write(rotY);
writer.Write(rotZ);
}
if ((SyncMode & ActorTransformSyncMode.Scale) != 0) {
writer.Write(Scale);
}
if ((SyncMode & ActorTransformSyncMode.Velocity) != 0) {
writer.Write(Velocity);
}
if ((SyncMode & ActorTransformSyncMode.AngularVelocity) != 0) {
writer.Write(AngularVelocity);
}
}
public void Deserialize(NetworkBufferReader reader) {
reader.Read(out ActorID);
reader.Read(out byte syncModeByte);
SyncMode = (ActorTransformSyncMode) syncModeByte;
@@ -236,19 +200,17 @@ namespace RebootKit.Engine.Simulation {
}
if ((SyncMode & ActorTransformSyncMode.Rotation) != 0) {
reader.Read(out Rotation);
reader.Read(out ushort rotX);
reader.Read(out ushort rotY);
reader.Read(out ushort rotZ);
Rotation.x = QuantizationUtility.UShortToFloat(rotX, 0.0f, 360.0f);
Rotation.y = QuantizationUtility.UShortToFloat(rotY, 0.0f, 360.0f);
Rotation.z = QuantizationUtility.UShortToFloat(rotZ, 0.0f, 360.0f);
}
if ((SyncMode & ActorTransformSyncMode.Scale) != 0) {
reader.Read(out Scale);
}
if ((SyncMode & ActorTransformSyncMode.Velocity) != 0) {
reader.Read(out Velocity);
}
if ((SyncMode & ActorTransformSyncMode.AngularVelocity) != 0) {
reader.Read(out AngularVelocity);
reader.Read(out Vector3 scale);
}
}
@@ -260,21 +222,13 @@ namespace RebootKit.Engine.Simulation {
}
if ((SyncMode & ActorTransformSyncMode.Rotation) != 0) {
size += sizeof(float) * 4; // Quaternion
size += sizeof(ushort) * 3; // Vector3 - Euler angles
}
if ((SyncMode & ActorTransformSyncMode.Scale) != 0) {
size += sizeof(float) * 3; // Vector3
}
if ((SyncMode & ActorTransformSyncMode.Velocity) != 0) {
size += sizeof(float) * 3; // Vector3
}
if ((SyncMode & ActorTransformSyncMode.AngularVelocity) != 0) {
size += sizeof(float) * 3; // Vector3
}
return size;
}
}
@@ -283,7 +237,8 @@ namespace RebootKit.Engine.Simulation {
static readonly Logger s_ActorLogger = new Logger(nameof(Actor));
[field: SerializeField, TriInspector.ReadOnly] public string SourceActorPath { get; internal set; } = "";
[field: SerializeField, Unity.Collections.ReadOnly] public ulong ActorID { get; internal set; }
[field: SerializeField, TriInspector.ReadOnly] public ulong ActorStaticID { get; internal set; }
[field: SerializeField, TriInspector.ReadOnly] public ushort ActorID { get; internal set; }
[NonSerialized] internal IActorData Data;
@@ -302,11 +257,22 @@ namespace RebootKit.Engine.Simulation {
[SerializeField] bool m_SetKinematicOnMount = true;
[SerializeField] bool m_DisableCollidersOnMount = true;
internal ActorPhysicsFlags PhysicsFlagsBeforeMount = ActorPhysicsFlags.None;
internal ActorPhysicsFlags PhysicsFlags = ActorPhysicsFlags.None;
internal ActorFlags Flags = ActorFlags.None;
// @NOTE: Sync won't happen if actor is mounted to another actor.
[SerializeField] internal ActorTransformSyncMode transformSyncMode = ActorTransformSyncMode.None;
[SerializeField] internal bool syncTransform = true;
[SerializeField] internal bool syncPosition = true;
[SerializeField] internal bool syncRotation = true;
[SerializeField] internal bool syncScale = false;
class ActorClientState {
public ulong LastSyncTick;
public Vector3 Position;
public Quaternion Rotation;
public Vector3 Scale;
}
readonly Dictionary<ulong, ActorClientState> m_ClientsStates = new Dictionary<ulong, ActorClientState>();
[Serializable]
public struct AttachmentSocket {
@@ -334,8 +300,8 @@ namespace RebootKit.Engine.Simulation {
// @MARK: Unity callbacks
//
void OnValidate() {
if (ActorID == 0) {
ActorID = UniqueID.NewULongFromGuid();
if (ActorStaticID == 0) {
ActorStaticID = UniqueID.NewULongFromGuid();
}
}
@@ -347,7 +313,7 @@ namespace RebootKit.Engine.Simulation {
// @MARK: Server side
public virtual void OnServerTick(float deltaTime) { }
protected virtual void OnActorCommandServer(ActorCommand actorCommand) { }
protected virtual void OnActorCommandServer(ulong senderID, ActorCommand actorCommand) { }
// Override this method to implement client-side logic
public virtual void OnClientTick(float deltaTime) { }
@@ -364,12 +330,18 @@ namespace RebootKit.Engine.Simulation {
bool shouldBeActive = !hidden;
if (gameObject.activeSelf == shouldBeActive) {
s_ActorLogger
.Warning($"Actor {name} (ID: {ActorID}) is already in the desired visibility state: {shouldBeActive.ToString()}");
s_ActorLogger.Warning($"Actor {name} (ID: {ActorID}) is already in the desired visibility state: {shouldBeActive.ToString()}");
return;
}
gameObject.SetActive(shouldBeActive);
if (hidden) {
Flags |= ActorFlags.Hidden;
} else {
Flags &= ~ActorFlags.Hidden;
}
IsCoreStateDirty = true;
}
@@ -383,16 +355,15 @@ namespace RebootKit.Engine.Simulation {
MasterActor = actor;
MasterSocketName = new FixedString32Bytes(slotName);
PhysicsFlagsBeforeMount = PhysicsFlags;
if (m_SetKinematicOnMount) {
PhysicsFlags |= ActorPhysicsFlags.IsKinematic;
}
if (m_DisableCollidersOnMount) {
PhysicsFlags |= ActorPhysicsFlags.DisableColliders;
Flags |= ActorFlags.DisableColliders;
UpdateLocalCollidersState(false);
}
if (m_SetKinematicOnMount) {
actorRigidbody.isKinematic = true;
}
UpdateLocalPhysicsState(PhysicsFlags);
UpdateMountedTransform();
IsCoreStateDirty = true;
}
@@ -413,51 +384,15 @@ namespace RebootKit.Engine.Simulation {
MasterSocketName = default;
UpdateMountedTransform();
PhysicsFlags = PhysicsFlagsBeforeMount;
UpdateLocalPhysicsState(PhysicsFlags);
IsCoreStateDirty = true;
}
public void SetCollidersEnabled(bool enableColliders) {
if (!RR.IsServer()) {
s_ActorLogger.Error($"Only the server can enable/disable colliders. Actor: {name} (ID: {ActorID})");
return;
if (m_DisableCollidersOnMount) {
UpdateLocalCollidersState(true);
Flags &= ~ActorFlags.DisableColliders;
}
if (m_SetKinematicOnMount) {
actorRigidbody.isKinematic = false;
}
if (actorRigidbody is null) {
s_ActorLogger.Error($"Actor {name} (ID: {ActorID}) has no Rigidbody to set colliders on.");
return;
}
if (enableColliders) {
PhysicsFlags &= ~ActorPhysicsFlags.DisableColliders;
} else {
PhysicsFlags |= ActorPhysicsFlags.DisableColliders;
}
UpdateLocalCollidersState(enableColliders);
IsCoreStateDirty = true;
}
public void SetKinematic(bool isKinematic) {
if (!RR.IsServer()) {
s_ActorLogger.Error($"Only the server can set kinematic state. Actor: {name} (ID: {ActorID})");
return;
}
if (actorRigidbody is null) {
s_ActorLogger.Error($"Actor {name} (ID: {ActorID}) has no Rigidbody to set kinematic state on.");
return;
}
if (isKinematic) {
PhysicsFlags |= ActorPhysicsFlags.IsKinematic;
} else {
PhysicsFlags &= ~ActorPhysicsFlags.IsKinematic;
}
actorRigidbody.isKinematic = isKinematic;
IsCoreStateDirty = true;
}
@@ -468,14 +403,14 @@ namespace RebootKit.Engine.Simulation {
return !gameObject.activeSelf;
}
protected void SendActorCommand<TCmdData>(ushort commandID, ref TCmdData commandData)
protected void SendActorCommand<TCmdData>(byte commandID, ref TCmdData commandData)
where TCmdData : struct, ISerializableEntity {
NativeArray<byte> data = DataSerializationUtils.Serialize(commandData);
SendActorCommand(commandID, data);
}
protected void SendActorCommand(ushort commandID, NativeArray<byte> data = default) {
protected void SendActorCommand(byte commandID, NativeArray<byte> data = default) {
if (Manager is null) {
s_ActorLogger.Error($"Cannot send command because Manager is null for actor {name} (ID: {ActorID})");
return;
@@ -483,7 +418,6 @@ namespace RebootKit.Engine.Simulation {
ActorCommand command = new ActorCommand {
ActorID = ActorID,
ClientID = NetworkManager.Singleton.LocalClientId,
CommandID = commandID,
Data = data
};
@@ -491,19 +425,19 @@ namespace RebootKit.Engine.Simulation {
Manager.SendActorCommand(command);
}
protected void SendActorEvent<TEventData>(ushort eventID, ref TEventData eventData)
protected void SendActorEvent<TEventData>(byte eventID, ref TEventData eventData)
where TEventData : struct, ISerializableEntity {
NativeArray<byte> data = DataSerializationUtils.Serialize(eventData);
SendActorEvent(eventID, data);
}
protected void SendActorEvent(ushort eventID, NativeArray<byte> data = default) {
protected void SendActorEvent(byte eventID, NativeArray<byte> data = default) {
if (!RR.IsServer()) {
s_ActorLogger.Error($"Only the server can send actor events. Actor: {name} (ID: {ActorID})");
return;
}
if (Manager is null) {
if (Manager == null) {
s_ActorLogger.Error($"Cannot send event because Manager is null for actor {name} (ID: {ActorID})");
return;
}
@@ -540,17 +474,22 @@ namespace RebootKit.Engine.Simulation {
//
// @MARK: Internal
//
internal void InitializeOnClient() {
if (actorRigidbody != null) {
actorRigidbody.isKinematic = true;
}
}
internal ActorCoreStateSnapshot GetCoreStateSnapshot() {
ActorCoreStateSnapshot snapshot = new ActorCoreStateSnapshot();
snapshot.ActorID = ActorID;
snapshot.Timestamp = DateTime.UtcNow;
snapshot.Position = transform.localPosition;
snapshot.Rotation = transform.localRotation;
snapshot.Scale = transform.localScale;
snapshot.Flags = Flags;
snapshot.IsHidden = !gameObject.activeSelf;
snapshot.Flags = PhysicsFlags;
snapshot.MasterActorID = MasterActor != null ? MasterActor.ActorID : 0;
snapshot.MasterActorID = MasterActor != null ? MasterActor.ActorID : (ushort)0;
if (snapshot.MasterActorID != 0) {
snapshot.MasterSocketName = MasterSocketName;
} else {
@@ -567,7 +506,7 @@ namespace RebootKit.Engine.Simulation {
return;
}
LastCoreStateSyncTime = snapshot.Timestamp;
PhysicsFlags = snapshot.Flags;
Flags = snapshot.Flags;
if (snapshot.MasterActorID != 0) {
MasterActor = RR.FindSpawnedActor(snapshot.MasterActorID);
@@ -583,69 +522,78 @@ namespace RebootKit.Engine.Simulation {
transform.localScale = snapshot.Scale;
}
if (snapshot.IsHidden) {
if ((snapshot.Flags & ActorFlags.Hidden) != 0) {
gameObject.SetActive(false);
} else {
gameObject.SetActive(true);
}
UpdateLocalPhysicsState(PhysicsFlags);
bool enableColliders = (Flags & ActorFlags.DisableColliders) == 0;
UpdateLocalCollidersState(enableColliders);
}
internal ActorTransformSyncData GetTransformSyncData() {
ActorTransformSyncData data = new ActorTransformSyncData {
SyncMode = transformSyncMode
ActorClientState GetActorClientState(ulong clientID) {
if (m_ClientsStates.TryGetValue(clientID, out ActorClientState clientState)) {
return clientState;
}
clientState = new ActorClientState {
LastSyncTick = 0,
Position = transform.localPosition,
Rotation = transform.localRotation,
Scale = transform.localScale
};
m_ClientsStates[clientID] = clientState;
bool useRigidbody = (data.SyncMode & ActorTransformSyncMode.UsingRigidbody) != 0;
return clientState;
}
internal ulong GetLastSyncTick(ulong clientID) {
ActorClientState actorClientState = GetActorClientState(clientID);
return actorClientState.LastSyncTick;
}
if (useRigidbody && actorRigidbody == null) {
s_ActorLogger
.Error($"Actor {name} (ID: {ActorID.ToString()}) has no Rigidbody to sync transform. Ignoring transform sync.");
data.SyncMode = ActorTransformSyncMode.None;
internal void UpdateClientState(ulong clientID, ulong serverTick) {
ActorClientState actorClientState = GetActorClientState(clientID);
actorClientState.LastSyncTick = serverTick;
actorClientState.Position = transform.localPosition;
actorClientState.Rotation = transform.localRotation;
actorClientState.Scale = transform.localScale;
}
internal ActorTransformSyncData GetTransformSyncDataForClient(ulong clientID) {
ActorTransformSyncData data = new ActorTransformSyncData {
ActorID = ActorID,
SyncMode = ActorTransformSyncMode.None
};
if (!syncTransform || MasterActor != null) {
return data;
}
data.Position = transform.localPosition;
data.Rotation = transform.localRotation.eulerAngles;
data.Scale = transform.localScale;
if ((data.SyncMode & ActorTransformSyncMode.Position) != 0) {
if (useRigidbody) {
data.Position = actorRigidbody.position;
} else {
data.Position = transform.localPosition;
}
ActorClientState actorClientState = GetActorClientState(clientID);
if (syncPosition && math.distancesq(actorClientState.Position, transform.localPosition) > 0.01f) {
data.SyncMode |= ActorTransformSyncMode.Position;
}
if (syncRotation && Quaternion.Angle(actorClientState.Rotation, transform.localRotation) > 0.01f) {
data.SyncMode |= ActorTransformSyncMode.Rotation;
}
if ((data.SyncMode & ActorTransformSyncMode.Rotation) != 0) {
if (useRigidbody) {
data.Rotation = actorRigidbody.rotation;
} else {
data.Rotation = transform.localRotation;
}
}
if ((data.SyncMode & ActorTransformSyncMode.Scale) != 0) {
data.Scale = transform.localScale;
}
if (useRigidbody && actorRigidbody != null) {
if ((data.SyncMode & ActorTransformSyncMode.Velocity) != 0) {
data.Velocity = actorRigidbody.linearVelocity;
}
if ((data.SyncMode & ActorTransformSyncMode.AngularVelocity) != 0) {
data.AngularVelocity = actorRigidbody.angularVelocity;
}
if (syncScale && math.distancesq(actorClientState.Scale, transform.localScale) > 0.01f) {
data.SyncMode |= ActorTransformSyncMode.Scale;
}
return data;
}
internal void RestoreTransformState(ActorTransformSyncData data) {
bool useRigidbody = (data.SyncMode & ActorTransformSyncMode.UsingRigidbody) != 0;
if (useRigidbody && actorRigidbody == null) {
s_ActorLogger
.Error($"Actor {name} (ID: {ActorID.ToString()}) has no Rigidbody to restore transform state. Ignoring transform sync.");
return;
}
bool useRigidbody = actorRigidbody != null;
if ((data.SyncMode & ActorTransformSyncMode.Position) != 0) {
if (useRigidbody) {
@@ -657,38 +605,15 @@ namespace RebootKit.Engine.Simulation {
if ((data.SyncMode & ActorTransformSyncMode.Rotation) != 0) {
if (useRigidbody) {
actorRigidbody.rotation = data.Rotation;
actorRigidbody.rotation = Quaternion.Euler(data.Rotation);
} else {
transform.localRotation = data.Rotation;
transform.localRotation = Quaternion.Euler(data.Rotation);
}
}
if ((data.SyncMode & ActorTransformSyncMode.Scale) != 0) {
transform.localScale = data.Scale;
}
if (useRigidbody && (data.SyncMode & ActorTransformSyncMode.Velocity) != 0) {
actorRigidbody.linearVelocity = data.Velocity;
}
if (useRigidbody && (data.SyncMode & ActorTransformSyncMode.AngularVelocity) != 0) {
actorRigidbody.angularVelocity = data.AngularVelocity;
}
}
void UpdateLocalPhysicsState(ActorPhysicsFlags flags) {
if (actorRigidbody == null) {
return;
}
if ((flags & ActorPhysicsFlags.IsKinematic) != 0) {
actorRigidbody.isKinematic = true;
} else {
actorRigidbody.isKinematic = false;
}
bool enableColliders = (flags & ActorPhysicsFlags.DisableColliders) == 0;
UpdateLocalCollidersState(enableColliders);
}
void UpdateLocalCollidersState(bool enable) {
@@ -720,7 +645,7 @@ namespace RebootKit.Engine.Simulation {
return CreateActorData();
}
internal void HandleActorCommand(ActorCommand actorCommand) {
internal void HandleActorCommand(ulong senderID, ActorCommand actorCommand) {
if (!RR.IsServer()) {
s_ActorLogger.Error($"Only the server can handle actor commands. Actor: {name} (ID: {ActorID})");
return;
@@ -737,7 +662,7 @@ namespace RebootKit.Engine.Simulation {
return;
}
OnActorCommandServer(actorCommand);
OnActorCommandServer(senderID, actorCommand);
}
internal void HandleActorEvent(ActorEvent actorEvent) {