diff --git a/documentation/NEXT_RELEASENOTES.txt b/documentation/NEXT_RELEASENOTES.txt index b304ef22..4c0f0bdc 100644 --- a/documentation/NEXT_RELEASENOTES.txt +++ b/documentation/NEXT_RELEASENOTES.txt @@ -13,6 +13,7 @@ - added World.SubscribeWorldComponentAdded method (#165) - added World.SubscribeWorldComponentChanged method (#165) - added World.SubscribeWorldComponentRemoved method (#165) +- added World.NotifyChanged method - added generic WithAttribute - added generic WithoutAttribute - added generic WhenAddedAttribute diff --git a/source/DefaultEcs.Test/WorldTest.cs b/source/DefaultEcs.Test/WorldTest.cs index a18a4403..7b386f0d 100644 --- a/source/DefaultEcs.Test/WorldTest.cs +++ b/source/DefaultEcs.Test/WorldTest.cs @@ -955,6 +955,36 @@ public void Remove_Should_not_throw_When_no_component() Check.ThatCode(world.Remove).DoesNotThrow(); } + [Fact] + public void NotifyChanged_Should_throw_When_no_component() + { + using World world = new(); + + Check.ThatCode(world.NotifyChanged).Throws(); + } + + [Fact] + public void NotifyChanged_Should_notify_world_component_changed() + { + using World world = new(); + + world.Set(false); + bool notified = false; + + world.SubscribeWorldComponentChanged((World sender, in bool oldValue, in bool newValue) => + { + Check.That(sender).IsEqualTo(world); + Check.That(oldValue).IsFalse(); + Check.That(newValue).IsTrue(); + notified = true; + }); + + world.Get() = true; + world.NotifyChanged(); + + Check.That(notified).IsTrue(); + } + [Fact] public void Has_Should_return_true_when_has_component() { diff --git a/source/DefaultEcs/Entity.cs b/source/DefaultEcs/Entity.cs index fd4691c8..8ab5b0ce 100644 --- a/source/DefaultEcs/Entity.cs +++ b/source/DefaultEcs/Entity.cs @@ -230,7 +230,7 @@ public void SetSameAs(in Entity reference) ThrowIf(WorldId != reference.WorldId, "Reference Entity comes from a different World"); ComponentPool pool = ComponentManager.Get(WorldId); - ThrowIf(!(pool?.Has(reference.EntityId) ?? false), $"Reference Entity does not have a component of type {nameof(T)}"); + ThrowIf(!(pool?.Has(reference.EntityId) ?? false), $"Reference Entity does not have a component of type {typeof(T)}"); InnerSet(pool.SetSameAs(EntityId, reference.EntityId)); } @@ -247,7 +247,7 @@ public void SetSameAsWorld() ThrowIf(WorldId == 0, "Entity was not created from a World"); ComponentPool pool = ComponentManager.Get(WorldId); - ThrowIf(!(pool?.Has(0) ?? false), $"World does not have a component of type {nameof(T)}"); + ThrowIf(!(pool?.Has(0) ?? false), $"World does not have a component of type {typeof(T)}"); InnerSet(pool.SetSameAs(EntityId, 0)); } @@ -278,7 +278,7 @@ public void Remove() public void NotifyChanged() { ThrowIf(WorldId == 0, "Entity was not created from a World"); - ThrowIf(!Has(), $"Entity does not have a component of type {nameof(T)}"); + ThrowIf(!Has(), $"Entity does not have a component of type {typeof(T)}"); Publisher.Publish(WorldId, new EntityComponentChangedMessage(EntityId, Components)); diff --git a/source/DefaultEcs/Internal/ComponentPool.cs b/source/DefaultEcs/Internal/ComponentPool.cs index 1c734036..095e868e 100644 --- a/source/DefaultEcs/Internal/ComponentPool.cs +++ b/source/DefaultEcs/Internal/ComponentPool.cs @@ -160,7 +160,7 @@ private void On(in ComponentReadMessage message) #region Methods [MethodImpl(MethodImplOptions.NoInlining)] - private static void ThrowMaxNumberOfComponentReached() => throw new InvalidOperationException($"Max number of component of type {nameof(T)} reached"); + private static void ThrowMaxNumberOfComponentReached() => throw new InvalidOperationException($"Max number of component of type {typeof(T)} reached"); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Has(int entityId) => entityId < _mapping.Length && _mapping[entityId] != -1; diff --git a/source/DefaultEcs/Internal/Serialization/TextSerializer/ConverterAction/ObjectConverter.cs b/source/DefaultEcs/Internal/Serialization/TextSerializer/ConverterAction/ObjectConverter.cs index abdda425..3691474c 100644 --- a/source/DefaultEcs/Internal/Serialization/TextSerializer/ConverterAction/ObjectConverter.cs +++ b/source/DefaultEcs/Internal/Serialization/TextSerializer/ConverterAction/ObjectConverter.cs @@ -78,7 +78,7 @@ static string GetFriendlyName(string name) writeExpressions.Add(writeField); - DynamicMethod readMethod = new($"Set_{nameof(T)}_{fieldInfo.Name}", typeof(void), new[] { typeof(StreamReaderWrapper), typeof(T).MakeByRefType() }, typeof(ObjectConverter), true); + DynamicMethod readMethod = new($"Set_{typeof(T)}_{fieldInfo.Name}", typeof(void), new[] { typeof(StreamReaderWrapper), typeof(T).MakeByRefType() }, typeof(ObjectConverter), true); ILGenerator readGenerator = readMethod.GetILGenerator(); readGenerator.Emit(OpCodes.Ldarg_1); if (!typeInfo.IsValueType) diff --git a/source/DefaultEcs/World.cs b/source/DefaultEcs/World.cs index 2ed31423..329cf7d6 100644 --- a/source/DefaultEcs/World.cs +++ b/source/DefaultEcs/World.cs @@ -362,6 +362,27 @@ public void Set(in T component) [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Get() => ref ComponentManager.Pools[WorldId].Get(0); + /// + /// Notifies the value of the component of type has changed. + /// This method is not thread safe. + /// + /// The type of the component. + /// does not have a component of type . + public void NotifyChanged() + { + if (!Has()) + { + throw new InvalidOperationException($"World does not have a component of type {typeof(T)}"); + } + + Publish(new WorldComponentChangedMessage()); + + if (ComponentManager.GetPrevious(WorldId) is ComponentPool previousPool && Has()) + { + previousPool.Set(0, Get()); + } + } + /// /// Removes the component of type on the current . /// This method is not thread safe.