diff --git a/LiteEntitySystem/ClientEntityManager.cs b/LiteEntitySystem/ClientEntityManager.cs index ef1fa8d..69ee918 100644 --- a/LiteEntitySystem/ClientEntityManager.cs +++ b/LiteEntitySystem/ClientEntityManager.cs @@ -611,7 +611,7 @@ protected override unsafe void OnLogicTick() } } } - else if(classData.UpdateOnClient) + else if(classData.Flags.HasFlagFast(EntityFlags.UpdateOnClient)) { entity.Update(); } @@ -755,13 +755,15 @@ public override unsafe void Update() internal void AddOwned(EntityLogic entity) { - if (entity.GetClassData().IsUpdateable && !entity.GetClassData().UpdateOnClient) + var flags = entity.GetClassData().Flags; + if (flags.HasFlagFast(EntityFlags.Updateable) && !flags.HasFlagFast(EntityFlags.UpdateOnClient)) AliveEntities.Add(entity); } internal void RemoveOwned(EntityLogic entity) { - if (entity.GetClassData().IsUpdateable && !entity.GetClassData().UpdateOnClient) + var flags = entity.GetClassData().Flags; + if (flags.HasFlagFast(EntityFlags.Updateable) && !flags.HasFlagFast(EntityFlags.UpdateOnClient)) AliveEntities.Remove(entity); } diff --git a/LiteEntitySystem/ControllerLogic.cs b/LiteEntitySystem/ControllerLogic.cs index d909783..a8425e7 100644 --- a/LiteEntitySystem/ControllerLogic.cs +++ b/LiteEntitySystem/ControllerLogic.cs @@ -5,6 +5,7 @@ namespace LiteEntitySystem /// /// Base class for Controller entities /// + [SetEntityFlags(EntityFlags.OnlyForOwner)] public abstract class ControllerLogic : InternalEntity { [SyncVarFlags(SyncFlags.NeverRollBack)] @@ -58,7 +59,7 @@ protected ControllerLogic(EntityParams entityParams) : base(entityParams) { } /// /// Base class for AI Controller entities /// - [LocalOnly, UpdateableEntity] + [SetEntityFlags(EntityFlags.LocalOnly | EntityFlags.Updateable)] public abstract class AiControllerLogic : ControllerLogic { public override bool IsBot => true; @@ -69,7 +70,7 @@ protected AiControllerLogic(EntityParams entityParams) : base(entityParams) { } /// /// Base class for AI Controller entities with typed ControlledEntity field /// - [LocalOnly, UpdateableEntity] + [SetEntityFlags(EntityFlags.LocalOnly | EntityFlags.Updateable)] public abstract class AiControllerLogic : AiControllerLogic where T : PawnLogic { public T ControlledEntity => GetControlledEntity(); diff --git a/LiteEntitySystem/EntityLogic.cs b/LiteEntitySystem/EntityLogic.cs index 504614a..12209a0 100644 --- a/LiteEntitySystem/EntityLogic.cs +++ b/LiteEntitySystem/EntityLogic.cs @@ -4,28 +4,27 @@ namespace LiteEntitySystem { - /// - /// Entity has update method - /// + [Flags] + public enum EntityFlags + { + UpdateOnClient = Updateable | (1 << 0), //Update entity on client even when entity isn't owned + Updateable = 1 << 1, //Entity has update method + LocalOnly = 1 << 2, //Entity is local only (only on server or client no difference) + OnlyForOwner = 1 << 3, //Sync entity only for owner player + OnlyForOthers = 1 << 4 //Sync entity only for other players + } + [AttributeUsage(AttributeTargets.Class)] - public class UpdateableEntity : Attribute + public class SetEntityFlags : Attribute { - public readonly bool UpdateOnClient; - - public UpdateableEntity() { } + public readonly EntityFlags Flags; - public UpdateableEntity(bool updateOnClient) + public SetEntityFlags(EntityFlags flags) { - UpdateOnClient = updateOnClient; + Flags = flags; } } - /// - /// Entity is local only (only on server or client no difference) - /// - [AttributeUsage(AttributeTargets.Class)] - public class LocalOnly : Attribute { } - /// /// Base class for simple (not controlled by controller) entity /// diff --git a/LiteEntitySystem/EntityManager.cs b/LiteEntitySystem/EntityManager.cs index 7c529d1..7a6bb16 100644 --- a/LiteEntitySystem/EntityManager.cs +++ b/LiteEntitySystem/EntityManager.cs @@ -499,7 +499,7 @@ private static bool IsEntityLagCompensated(InternalEntity e) => !e.IsLocal && e is EntityLogic { HasLagCompensation: true }; private bool IsEntityAlive(EntityClassData classData, InternalEntity entity) - => classData.IsUpdateable && (IsServer || entity.IsLocal || (IsClient && classData.UpdateOnClient)); + => classData.Flags.HasFlagFast(EntityFlags.Updateable) && (IsServer || entity.IsLocal || (IsClient && classData.Flags.HasFlagFast(EntityFlags.UpdateOnClient))); internal virtual void RemoveEntity(InternalEntity e) { @@ -522,7 +522,7 @@ internal virtual void RemoveEntity(InternalEntity e) AliveEntities.Remove(e); if(IsEntityLagCompensated(e)) LagCompensatedEntities.Remove((EntityLogic)e); - if (classData.IsLocalOnly) + if (classData.Flags.HasFlagFast(EntityFlags.LocalOnly)) _localIdQueue.ReuseId(e.Id); EntitiesDict[e.Id] = null; EntitiesCount--; diff --git a/LiteEntitySystem/EntityTypesMap.cs b/LiteEntitySystem/EntityTypesMap.cs index dd4ad92..a696163 100644 --- a/LiteEntitySystem/EntityTypesMap.cs +++ b/LiteEntitySystem/EntityTypesMap.cs @@ -37,7 +37,11 @@ public ulong EvaluateEntityClassDataHash() //don't hash localonly types foreach (var (entType, _) in RegisteredTypes .OrderBy(kv => kv.Value.ClassId) - .Where(kv => kv.Key.GetCustomAttribute(true) == null)) + .Where(kv => + { + var attr = kv.Key.GetCustomAttribute(true); + return attr == null || !attr.Flags.HasFlagFast(EntityFlags.LocalOnly); + })) { var allTypesStack = Utils.GetBaseTypes(entType, typeof(InternalEntity), true); while(allTypesStack.Count > 0) diff --git a/LiteEntitySystem/HumanControllerLogic.cs b/LiteEntitySystem/HumanControllerLogic.cs index d72bd14..e3443bb 100644 --- a/LiteEntitySystem/HumanControllerLogic.cs +++ b/LiteEntitySystem/HumanControllerLogic.cs @@ -8,7 +8,7 @@ namespace LiteEntitySystem /// /// Base class for human Controller entities /// - [UpdateableEntity(true)] + [SetEntityFlags(EntityFlags.UpdateOnClient)] public abstract class HumanControllerLogic : ControllerLogic where TInput : unmanaged { private struct ServerResponse diff --git a/LiteEntitySystem/Internal/EntityClassData.cs b/LiteEntitySystem/Internal/EntityClassData.cs index 860a9b1..da1cfa0 100644 --- a/LiteEntitySystem/Internal/EntityClassData.cs +++ b/LiteEntitySystem/Internal/EntityClassData.cs @@ -56,9 +56,7 @@ internal struct EntityClassData public readonly EntityFieldInfo[] LagCompensatedFields; public readonly int LagCompensatedSize; public readonly int LagCompensatedCount; - public readonly bool UpdateOnClient; - public readonly bool IsUpdateable; - public readonly bool IsLocalOnly; + public readonly EntityFlags Flags; public readonly EntityConstructor EntityConstructor; public RpcFieldInfo[] RemoteCallsClient; @@ -81,20 +79,8 @@ public EntityClassData(ushort filterId, Type entType, RegisteredTypeInfo typeInf RemoteCallsClient = null; ClassId = typeInfo.ClassId; - - var updateAttribute = entType.GetCustomAttribute(); - if (updateAttribute != null) - { - IsUpdateable = true; - UpdateOnClient = updateAttribute.UpdateOnClient; - } - else - { - IsUpdateable = false; - UpdateOnClient = false; - } - - IsLocalOnly = entType.GetCustomAttribute() != null; + Flags = 0; + EntityConstructor = typeInfo.Constructor; IsSingleton = entType.IsSubclassOf(SingletonEntityType); FilterId = filterId; @@ -110,6 +96,10 @@ public EntityClassData(ushort filterId, Type entType, RegisteredTypeInfo typeInf while(allTypesStack.Count > 0) { var baseType = allTypesStack.Pop(); + + var setFlagsAttribute = baseType.GetCustomAttribute(); + Flags |= setFlagsAttribute != null ? setFlagsAttribute.Flags : 0; + //cache fields foreach (var field in Utils.GetProcessedFields(baseType)) { diff --git a/LiteEntitySystem/Internal/StateSerializer.cs b/LiteEntitySystem/Internal/StateSerializer.cs index 807f8c9..383d696 100644 --- a/LiteEntitySystem/Internal/StateSerializer.cs +++ b/LiteEntitySystem/Internal/StateSerializer.cs @@ -34,6 +34,7 @@ private enum RPCMode private EntityFieldInfo[] _fields; private int _fieldsCount; private int _fieldsFlagsSize; + private EntityFlags _flags; private InternalEntity _entity; private byte[] _latestEntityData; @@ -44,7 +45,6 @@ private enum RPCMode private RemoteCallPacket _rpcTail; private RemoteCallPacket _syncRpcHead; private RemoteCallPacket _syncRpcTail; - private bool _isController; private uint _fullDataSize; private int _syncFrame; private RPCMode _rpcMode; @@ -104,6 +104,7 @@ public void AllocateMemory(ref EntityClassData classData) _fieldsCount = classData.FieldsCount; _fieldsFlagsSize = classData.FieldsFlagsSize; _fullDataSize = (uint)(HeaderSize + classData.FixedFieldsSize); + _flags = classData.Flags; //resize or clean prev data if (_latestEntityData == null || _latestEntityData.Length < _fullDataSize) @@ -121,7 +122,6 @@ public unsafe void Init(InternalEntity e, ushort tick) _state = SerializerState.Active; _syncFrame = -1; _rpcMode = RPCMode.Normal; - _isController = e is ControllerLogic; _versionChangedTick = tick; LastChangedTick = tick; @@ -242,10 +242,13 @@ private unsafe void WriteInitialState(bool isOwned, ushort serverTick, byte* res public unsafe void MakeBaseline(byte playerId, ushort serverTick, byte* resultData, ref int position) { //skip inactive and other controlled controllers - if (_state != SerializerState.Active || (_isController && playerId != _entity.InternalOwnerId.Value)) + bool isOwned = _entity.InternalOwnerId.Value == playerId; + if (_state != SerializerState.Active || + (_flags.HasFlagFast(EntityFlags.OnlyForOwner) && !isOwned) || + (_flags.HasFlagFast(EntityFlags.OnlyForOthers) && isOwned)) return; //don't write total size in full sync and fields - WriteInitialState(_entity.InternalOwnerId.Value == playerId, serverTick, resultData, ref position); + WriteInitialState(isOwned, serverTick, resultData, ref position); //Logger.Log($"[SEM] SendBaseline for entity: {_entity.Id}, pos: {position}, posAfterData: {position + _fullDataSize}"); } @@ -267,11 +270,14 @@ public unsafe DiffResult MakeDiff(byte playerId, ushort serverTick, ushort minim } //make diff - int startPos = position; bool isOwned = _entity.InternalOwnerId.Value == playerId; - if (_isController && !isOwned) + if ((_flags.HasFlagFast(EntityFlags.OnlyForOwner) && !isOwned) || + (_flags.HasFlagFast(EntityFlags.OnlyForOthers) && isOwned)) + { return DiffResult.Skip; - + } + + int startPos = position; //at 0 ushort ushort* fieldFlagAndSize = (ushort*)(resultData + startPos); position += sizeof(ushort); diff --git a/LiteEntitySystem/PawnLogic.cs b/LiteEntitySystem/PawnLogic.cs index ed06f41..8d2c022 100644 --- a/LiteEntitySystem/PawnLogic.cs +++ b/LiteEntitySystem/PawnLogic.cs @@ -3,7 +3,7 @@ /// /// Base class for entites that can be controlled by Controller /// - [UpdateableEntity] + [SetEntityFlags(EntityFlags.Updateable)] public abstract class PawnLogic : EntityLogic { [SyncVarFlags(SyncFlags.OnlyForOwner)] diff --git a/LiteEntitySystem/ServerEntityManager.cs b/LiteEntitySystem/ServerEntityManager.cs index 3e4d76f..722d846 100644 --- a/LiteEntitySystem/ServerEntityManager.cs +++ b/LiteEntitySystem/ServerEntityManager.cs @@ -554,7 +554,7 @@ private T Add(Action initMethod) where T : InternalEntity ref var classData = ref ClassDataDict[EntityClassInfo.ClassId]; T entity; - if (classData.IsLocalOnly) + if (classData.Flags.HasFlagFast(EntityFlags.LocalOnly)) { entity = AddLocalEntity(initMethod); }