diff --git a/.gitmodules b/.gitmodules index 5b155fd..269915b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "Engine.Serializers/YamlDotNet"] - path = Engine.Serializers/YamlDotNet +[submodule "Engine.Integration/YamlDotNet"] + path = Engine.Integration/YamlDotNet url = git@github.com:Syntriax/YamlDotNet.git diff --git a/Engine.Core/Abstract/Assignable/IAssignable.cs b/Engine.Core/Abstract/Assignable/IAssignable.cs index b4cac2a..f5c1177 100644 --- a/Engine.Core/Abstract/Assignable/IAssignable.cs +++ b/Engine.Core/Abstract/Assignable/IAssignable.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Indicates the class implementing it has Assignable fields that are necessary for the engine to work properly. diff --git a/Engine.Core/Abstract/Assignable/IHasBehaviourController.cs b/Engine.Core/Abstract/Assignable/IHasBehaviourController.cs index 6feff7e..387c015 100644 --- a/Engine.Core/Abstract/Assignable/IHasBehaviourController.cs +++ b/Engine.Core/Abstract/Assignable/IHasBehaviourController.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Indicates the object is an with an assignable field. diff --git a/Engine.Core/Abstract/Assignable/IHasEntity.cs b/Engine.Core/Abstract/Assignable/IHasEntity.cs index 205ffe5..b972510 100644 --- a/Engine.Core/Abstract/Assignable/IHasEntity.cs +++ b/Engine.Core/Abstract/Assignable/IHasEntity.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Indicates the object is an with an assignable field. diff --git a/Engine.Core/Abstract/Assignable/IHasStateEnable.cs b/Engine.Core/Abstract/Assignable/IHasStateEnable.cs index 0a5c766..2f010bb 100644 --- a/Engine.Core/Abstract/Assignable/IHasStateEnable.cs +++ b/Engine.Core/Abstract/Assignable/IHasStateEnable.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Indicates the object is an with an assignable field. diff --git a/Engine.Core/Abstract/Assignable/IHasUniverse.cs b/Engine.Core/Abstract/Assignable/IHasUniverse.cs index bcf13f2..587a77f 100644 --- a/Engine.Core/Abstract/Assignable/IHasUniverse.cs +++ b/Engine.Core/Abstract/Assignable/IHasUniverse.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Indicates the object is an with an assignable field. diff --git a/Engine.Core/Abstract/Assignable/IHasUniverseObject.cs b/Engine.Core/Abstract/Assignable/IHasUniverseObject.cs index ad1081a..dd21850 100644 --- a/Engine.Core/Abstract/Assignable/IHasUniverseObject.cs +++ b/Engine.Core/Abstract/Assignable/IHasUniverseObject.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Indicates the object is an with an assignable field. diff --git a/Engine.Core/Abstract/IActive.cs b/Engine.Core/Abstract/IActive.cs index 7fbfd6c..9bfa751 100644 --- a/Engine.Core/Abstract/IActive.cs +++ b/Engine.Core/Abstract/IActive.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents an entity which can be active or not. diff --git a/Engine.Core/Abstract/IBehaviour.cs b/Engine.Core/Abstract/IBehaviour.cs index 25ec8cd..48ca81d 100644 --- a/Engine.Core/Abstract/IBehaviour.cs +++ b/Engine.Core/Abstract/IBehaviour.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a behaviour that any object in the engine that might use to interact with itself or other objects. diff --git a/Engine.Core/Abstract/IBehaviour2D.cs b/Engine.Core/Abstract/IBehaviour2D.cs index b0918d4..3249ef9 100644 --- a/Engine.Core/Abstract/IBehaviour2D.cs +++ b/Engine.Core/Abstract/IBehaviour2D.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public interface IBehaviour2D : IBehaviour { diff --git a/Engine.Core/Abstract/IBehaviourCollector.cs b/Engine.Core/Abstract/IBehaviourCollector.cs index ad9ed56..674236d 100644 --- a/Engine.Core/Abstract/IBehaviourCollector.cs +++ b/Engine.Core/Abstract/IBehaviourCollector.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a collector for the class type of . diff --git a/Engine.Core/Abstract/IBehaviourController.cs b/Engine.Core/Abstract/IBehaviourController.cs index 590e2e8..87af209 100644 --- a/Engine.Core/Abstract/IBehaviourController.cs +++ b/Engine.Core/Abstract/IBehaviourController.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a controller for managing s. Connected to an . diff --git a/Engine.Core/Abstract/ICamera2D.cs b/Engine.Core/Abstract/ICamera2D.cs index 30bc362..4c12105 100644 --- a/Engine.Core/Abstract/ICamera2D.cs +++ b/Engine.Core/Abstract/ICamera2D.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a 2D camera in the engine. diff --git a/Engine.Core/Abstract/ICoroutineYield.cs b/Engine.Core/Abstract/ICoroutineYield.cs index 230ca21..c3e04a8 100644 --- a/Engine.Core/Abstract/ICoroutineYield.cs +++ b/Engine.Core/Abstract/ICoroutineYield.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public interface ICoroutineYield { diff --git a/Engine.Core/Abstract/IEntity.cs b/Engine.Core/Abstract/IEntity.cs index 3eeefdd..53a719a 100644 --- a/Engine.Core/Abstract/IEntity.cs +++ b/Engine.Core/Abstract/IEntity.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a basic entity in the engine. diff --git a/Engine.Core/Abstract/IInitializable.cs b/Engine.Core/Abstract/IInitializable.cs index 64230d3..a7f846d 100644 --- a/Engine.Core/Abstract/IInitializable.cs +++ b/Engine.Core/Abstract/IInitializable.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents an entity that can be initialized and finalized. This information is useful for objects we know that are not in use and can be either recycled or dropped for garbage collection. diff --git a/Engine.Core/Abstract/INameable.cs b/Engine.Core/Abstract/INameable.cs index 93442be..08b287b 100644 --- a/Engine.Core/Abstract/INameable.cs +++ b/Engine.Core/Abstract/INameable.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents an entity with a name. diff --git a/Engine.Core/Abstract/IStateEnable.cs b/Engine.Core/Abstract/IStateEnable.cs index 4c61668..96925f5 100644 --- a/Engine.Core/Abstract/IStateEnable.cs +++ b/Engine.Core/Abstract/IStateEnable.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents an entity with an enable state that can be toggled. diff --git a/Engine.Core/Abstract/ITransform2D.cs b/Engine.Core/Abstract/ITransform2D.cs index 495ef9e..d899386 100644 --- a/Engine.Core/Abstract/ITransform2D.cs +++ b/Engine.Core/Abstract/ITransform2D.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents the transformation properties of an object such as position, scale, and rotation in 2D space. diff --git a/Engine.Core/Abstract/IUniverse.cs b/Engine.Core/Abstract/IUniverse.cs index 116967b..e0896a1 100644 --- a/Engine.Core/Abstract/IUniverse.cs +++ b/Engine.Core/Abstract/IUniverse.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a universe responsible for managing s. @@ -37,11 +37,21 @@ public interface IUniverse : IEntity, IEnumerable /// Event OnPostDraw { get; } + /// + /// Event triggered when a is about to be registered to the . + /// + Event OnPreUniverseObjectRegistered { get; } + /// /// Event triggered when a is registered to the . /// Event OnUniverseObjectRegistered { get; } + /// + /// Event triggered when a is about to be unregistered from the . + /// + Event OnPreUniverseObjectUnRegistered { get; } + /// /// Event triggered when a is unregistered from the . /// diff --git a/Engine.Core/Abstract/IUniverseObject.cs b/Engine.Core/Abstract/IUniverseObject.cs index 7f03950..64515c9 100644 --- a/Engine.Core/Abstract/IUniverseObject.cs +++ b/Engine.Core/Abstract/IUniverseObject.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents an that can enter and exit a universe within the system. /// This interface allows for tracking the object's presence in the universe and provides events /// for notifying when the see enters or exits the universe. /// -public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourController, IEnumerable +public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourController { /// /// Event triggered when the enters the universe. @@ -47,7 +47,7 @@ public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourCon /// /// The parent of the . /// - IUniverseObject? Parent { get; } + IUniverseObject? Parent { get; set; } /// /// The s that have this as their . @@ -75,12 +75,6 @@ public interface IUniverseObject : IEntity, IActive, INameable, IHasBehaviourCon /// internal bool ExitUniverse(); - /// - /// Sets the parent of this . - /// - /// The parent to set. - void SetParent(IUniverseObject? universeObject); - /// /// Adds a child to this . /// diff --git a/Engine.Core/ActiveBehaviourCollectorSorted.cs b/Engine.Core/ActiveBehaviourCollectorSorted.cs deleted file mode 100644 index 292f6b5..0000000 --- a/Engine.Core/ActiveBehaviourCollectorSorted.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Syntriax.Engine.Core; - -public class ActiveBehaviourCollectorSorted : ActiveBehaviourCollector where T : class, IBehaviour -{ - private readonly Event.EventHandler delegateOnPriorityChanged = null!; - - private IComparer? _sortBy = null; - public IComparer? SortBy - { - get => _sortBy; - set - { - _sortBy = value; - - if (value is not null) - activeBehaviours.Sort(value); - } - } - - protected override void AddBehaviour(T behaviour) - { - if (SortBy is null) - { - activeBehaviours.Add(behaviour); - return; - } - - int insertionIndex = activeBehaviours.BinarySearch(behaviour, SortBy); - - if (insertionIndex < 0) - insertionIndex = ~insertionIndex; - - activeBehaviours.Insert(insertionIndex, behaviour); - } - - protected override void OnBehaviourAdd(IBehaviour behaviour) - { - behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged); - } - - protected override void OnBehaviourRemove(IBehaviour behaviour) - { - behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged); - } - - private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args) - { - T behaviour = (T)sender; - activeBehaviours.Remove(behaviour); - AddBehaviour(behaviour); - } - - public ActiveBehaviourCollectorSorted() - { - delegateOnPriorityChanged = OnPriorityChanged; - } - - public ActiveBehaviourCollectorSorted(IUniverse universe, Comparison sortBy) : base(universe) - { - delegateOnPriorityChanged = OnPriorityChanged; - - SortBy = Comparer.Create(sortBy); - } -} diff --git a/Engine.Core/BaseEntity.cs b/Engine.Core/BaseEntity.cs index e98e387..a16b072 100644 --- a/Engine.Core/BaseEntity.cs +++ b/Engine.Core/BaseEntity.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public abstract class BaseEntity : IEntity { diff --git a/Engine.Core/Behaviour.cs b/Engine.Core/Behaviour.cs index 729c14e..06b628f 100644 --- a/Engine.Core/Behaviour.cs +++ b/Engine.Core/Behaviour.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public abstract class Behaviour : BehaviourBase { diff --git a/Engine.Core/Behaviour2D.cs b/Engine.Core/Behaviour2D.cs index a6d22c2..6622cc1 100644 --- a/Engine.Core/Behaviour2D.cs +++ b/Engine.Core/Behaviour2D.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public abstract class Behaviour2D : Behaviour, IBehaviour2D { diff --git a/Engine.Core/BehaviourBase.cs b/Engine.Core/BehaviourBase.cs index b8a6fcc..27012c0 100644 --- a/Engine.Core/BehaviourBase.cs +++ b/Engine.Core/BehaviourBase.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; [System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")] public abstract class BehaviourBase : BaseEntity, IBehaviour @@ -44,6 +44,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour _behaviourController = behaviourController; OnAssign(behaviourController); behaviourController.OnUniverseObjectAssigned.AddListener(delegateOnUniverseObjectAssigned); + behaviourController.StateEnable.OnEnabledChanged.AddListener(delegateOnStateEnabledChanged); if (behaviourController.UniverseObject is not null) OnUniverseObjectAssigned(behaviourController); OnBehaviourControllerAssigned?.Invoke(this); @@ -68,6 +69,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour BehaviourController.UniverseObject.OnActiveChanged.RemoveListener(delegateOnUniverseObjectActiveChanged); StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged); BehaviourController.OnUniverseObjectAssigned.RemoveListener(delegateOnUniverseObjectAssigned); + BehaviourController.StateEnable.OnEnabledChanged.RemoveListener(delegateOnStateEnabledChanged); base.UnassignInternal(); _behaviourController = null!; } @@ -76,6 +78,8 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour { Debug.Assert.AssertBehaviourControllerAssigned(this); Debug.Assert.AssertStateEnableAssigned(this); + + UpdateActive(); } private void OnStateEnabledChanged(IStateEnable sender, IStateEnable.EnabledChangedArguments args) => UpdateActive(); @@ -84,7 +88,7 @@ public abstract class BehaviourBase : BaseEntity, IBehaviour private void UpdateActive() { bool previousActive = IsActive; - _isActive = StateEnable.Enabled && _behaviourController.UniverseObject.IsActive; + _isActive = StateEnable.Enabled && _behaviourController.StateEnable.Enabled && _behaviourController.UniverseObject.IsActive; if (previousActive != IsActive) OnActiveChanged?.Invoke(this, new(previousActive)); diff --git a/Engine.Core/BehaviourCollectorSorted.cs b/Engine.Core/BehaviourCollectorSorted.cs deleted file mode 100644 index 35effbc..0000000 --- a/Engine.Core/BehaviourCollectorSorted.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Syntriax.Engine.Core; - -public class BehaviourCollectorSorted : BehaviourCollector where T : class -{ - private readonly Event.EventHandler delegateOnPriorityChanged = null!; - - private IComparer? _sortBy = null; - public IComparer? SortBy - { - get => _sortBy; - set - { - _sortBy = value; - - if (value is not null) - behaviours.Sort(value); - } - } - - protected override void AddBehaviour(T behaviour) - { - if (SortBy is null) - { - behaviours.Add(behaviour); - return; - } - - int insertionIndex = behaviours.BinarySearch(behaviour, SortBy); - - if (insertionIndex < 0) - insertionIndex = ~insertionIndex; - - behaviours.Insert(insertionIndex, behaviour); - } - - protected override void OnBehaviourAdd(IBehaviour behaviour) - { - behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged); - } - - protected override void OnBehaviourRemove(IBehaviour behaviour) - { - behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged); - } - - private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args) - { - T behaviour = (T)sender; - behaviours.Remove(behaviour); - AddBehaviour(behaviour); - } - - public BehaviourCollectorSorted() - { - delegateOnPriorityChanged = OnPriorityChanged; - } - - public BehaviourCollectorSorted(IUniverse universe, Comparison sortBy) : base(universe) - { - delegateOnPriorityChanged = OnPriorityChanged; - - SortBy = Comparer.Create(sortBy); - } -} diff --git a/Engine.Core/BehaviourController.cs b/Engine.Core/BehaviourController.cs index 5e4855f..00618a8 100644 --- a/Engine.Core/BehaviourController.cs +++ b/Engine.Core/BehaviourController.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; -namespace Syntriax.Engine.Core; +namespace Engine.Core; [System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")] public class BehaviourController : BaseEntity, IBehaviourController @@ -11,7 +10,7 @@ public class BehaviourController : BaseEntity, IBehaviourController public Event OnBehaviourRemoved { get; } = new(); public Event OnUniverseObjectAssigned { get; } = new(); - private readonly List behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL); + private readonly FastList behaviours = new(Constants.BEHAVIOURS_SIZE_INITIAL); private IUniverseObject _universeObject = null!; @@ -50,17 +49,13 @@ public class BehaviourController : BaseEntity, IBehaviourController public IReadOnlyList GetBehaviours() { - List? behaviours = null; + List behaviours = []; + foreach (IBehaviour behaviourItem in this.behaviours) - { - if (behaviourItem is not T behaviour) - continue; + if (behaviourItem is T behaviour) + behaviours.Add(behaviour); - behaviours ??= []; - behaviours.Add(behaviour); - } - - return behaviours ?? Enumerable.Empty().ToList(); + return behaviours; } public void GetBehaviours(IList results) @@ -77,7 +72,7 @@ public class BehaviourController : BaseEntity, IBehaviourController public void RemoveBehaviour(bool removeAll = false) where T : class, IBehaviour { - for (int i = behaviours.Count; i >= 0; i--) + for (int i = behaviours.Count - 1; i >= 0; i--) { if (behaviours[i] is not T behaviour) continue; diff --git a/Engine.Core/Collectors/ActiveBehaviourCollector.cs b/Engine.Core/Collectors/ActiveBehaviourCollector.cs new file mode 100644 index 0000000..dfe9a2f --- /dev/null +++ b/Engine.Core/Collectors/ActiveBehaviourCollector.cs @@ -0,0 +1,16 @@ +using System; + +namespace Engine.Core; + +public class ActiveBehaviourCollector : ActiveBehaviourCollectorBase where T : class, IBehaviour +{ + protected readonly FastList activeBehaviours = new(32); + public override T this[Index index] => activeBehaviours[index]; + public override int Count => activeBehaviours.Count; + + public ActiveBehaviourCollector() { } + public ActiveBehaviourCollector(IUniverse universe) : base(universe) { } + + protected override void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour); + protected override bool RemoveBehaviour(T tBehaviour) => activeBehaviours.Remove(tBehaviour); +} diff --git a/Engine.Core/ActiveBehaviourCollector.cs b/Engine.Core/Collectors/ActiveBehaviourCollectorBase.cs similarity index 88% rename from Engine.Core/ActiveBehaviourCollector.cs rename to Engine.Core/Collectors/ActiveBehaviourCollectorBase.cs index f138a59..f389a61 100644 --- a/Engine.Core/ActiveBehaviourCollector.cs +++ b/Engine.Core/Collectors/ActiveBehaviourCollectorBase.cs @@ -1,14 +1,12 @@ using System; using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; -public class ActiveBehaviourCollector : IBehaviourCollector where T : class, IBehaviour +public abstract class ActiveBehaviourCollectorBase : IBehaviourCollector where T : class, IBehaviour { - public Event, IBehaviourCollector.BehaviourCollectedArguments> OnCollected { get; } = new(); - public Event, IBehaviourCollector.BehaviourRemovedArguments> OnRemoved { get; } = new(); - public Event OnUniverseAssigned { get; } = new(); - public Event? OnUnassigned { get; } = new(); + protected readonly Dictionary monitoringActiveToBehaviour = new(32); + protected readonly FastList monitoringBehaviours = new(32); private readonly Event.EventHandler delegateOnBehaviourAdded = null!; private readonly Event.EventHandler delegateOnBehaviourRemoved = null!; @@ -16,80 +14,16 @@ public class ActiveBehaviourCollector : IBehaviourCollector where T : clas private readonly Event.EventHandler delegateOnUniverseObjectRegistered = null!; private readonly Event.EventHandler delegateOnUniverseObjectUnregistered = null!; - private readonly List monitoringBehaviours = new(32); - protected readonly List activeBehaviours = new(32); - protected readonly Dictionary monitoringActiveToBehaviour = new(32); + public Event, IBehaviourCollector.BehaviourCollectedArguments> OnCollected { get; } = new(); + public Event, IBehaviourCollector.BehaviourRemovedArguments> OnRemoved { get; } = new(); + public Event OnUniverseAssigned { get; } = new(); + public Event? OnUnassigned { get; } = new(); + public abstract int Count { get; } + + public abstract T this[Index index] { get; } public IUniverse Universe { get; private set; } = null!; - private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args) - { - IUniverseObject universeObject = args.UniverseObjectRegistered; - - universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded); - universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved); - - for (int i = 0; i < universeObject.BehaviourController.Count; i++) - OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i])); - } - - private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args) - { - IUniverseObject universeObject = args.UniverseObjectUnregistered; - - universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded); - universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved); - - for (int i = 0; i < universeObject.BehaviourController.Count; i++) - OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i])); - } - - protected virtual void OnBehaviourAdd(IBehaviour behaviour) { } - private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args) - { - if (args.BehaviourAdded is not T tBehaviour) - return; - - monitoringBehaviours.Add(tBehaviour); - monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour); - tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged); - OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive)); - } - - protected virtual void AddBehaviour(T behaviour) => activeBehaviours.Add(behaviour); - private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args) - { - T behaviour = monitoringActiveToBehaviour[sender]; - if (sender.IsActive) - { - AddBehaviour(behaviour); - OnBehaviourAdd(behaviour); - OnCollected?.Invoke(this, new(behaviour)); - } - else if (activeBehaviours.Remove(behaviour)) - { - OnBehaviourRemove(behaviour); - OnRemoved?.Invoke(this, new(behaviour)); - } - } - - protected virtual void OnBehaviourRemove(IBehaviour behaviour) { } - private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args) - { - if (args.BehaviourRemoved is not T tBehaviour) - return; - - if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour)) - return; - - tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged); - if (activeBehaviours.Remove(tBehaviour)) - { - OnBehaviourRemove(tBehaviour); - OnRemoved?.Invoke(this, new(tBehaviour)); - } - } - public bool Assign(IUniverse universe) { if (Universe is not null) @@ -123,10 +57,75 @@ public class ActiveBehaviourCollector : IBehaviourCollector where T : clas return true; } - public int Count => activeBehaviours.Count; - public T this[Index index] => activeBehaviours[index]; + protected abstract void AddBehaviour(T behaviour); + protected virtual void OnBehaviourAdd(IBehaviour behaviour) { } + private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args) + { + if (args.BehaviourAdded is not T tBehaviour) + return; - public ActiveBehaviourCollector() + monitoringBehaviours.Add(tBehaviour); + monitoringActiveToBehaviour.Add(tBehaviour, tBehaviour); + tBehaviour.OnActiveChanged.AddListener(delegateOnBehaviourStateChanged); + OnBehaviourStateChanged(tBehaviour, new(!tBehaviour.IsActive)); + } + + protected abstract bool RemoveBehaviour(T behaviour); + protected virtual void OnBehaviourRemove(IBehaviour behaviour) { } + private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args) + { + if (args.BehaviourRemoved is not T tBehaviour) + return; + + if (!monitoringBehaviours.Remove(tBehaviour) || !monitoringActiveToBehaviour.Remove(tBehaviour)) + return; + + tBehaviour.OnActiveChanged.RemoveListener(delegateOnBehaviourStateChanged); + if (!RemoveBehaviour(tBehaviour)) + return; + + OnBehaviourRemove(tBehaviour); + OnRemoved?.Invoke(this, new(tBehaviour)); + } + private void OnBehaviourStateChanged(IActive sender, IActive.ActiveChangedArguments args) + { + T behaviour = monitoringActiveToBehaviour[sender]; + if (sender.IsActive) + { + AddBehaviour(behaviour); + OnBehaviourAdd(behaviour); + OnCollected?.Invoke(this, new(behaviour)); + } + else if (RemoveBehaviour(behaviour)) + { + OnBehaviourRemove(behaviour); + OnRemoved?.Invoke(this, new(behaviour)); + } + } + + private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args) + { + IUniverseObject universeObject = args.UniverseObjectRegistered; + + universeObject.BehaviourController.OnBehaviourAdded.AddListener(delegateOnBehaviourAdded); + universeObject.BehaviourController.OnBehaviourRemoved.AddListener(delegateOnBehaviourRemoved); + + for (int i = 0; i < universeObject.BehaviourController.Count; i++) + OnBehaviourAdded(universeObject.BehaviourController, new(universeObject.BehaviourController[i])); + } + + private void OnUniverseObjectUnregistered(IUniverse manager, IUniverse.UniverseObjectUnRegisteredArguments args) + { + IUniverseObject universeObject = args.UniverseObjectUnregistered; + + universeObject.BehaviourController.OnBehaviourAdded.RemoveListener(delegateOnBehaviourAdded); + universeObject.BehaviourController.OnBehaviourRemoved.RemoveListener(delegateOnBehaviourRemoved); + + for (int i = 0; i < universeObject.BehaviourController.Count; i++) + OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i])); + } + + public ActiveBehaviourCollectorBase() { delegateOnBehaviourAdded = OnBehaviourAdded; delegateOnBehaviourRemoved = OnBehaviourRemoved; @@ -135,7 +134,7 @@ public class ActiveBehaviourCollector : IBehaviourCollector where T : clas delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered; } - public ActiveBehaviourCollector(IUniverse universe) + public ActiveBehaviourCollectorBase(IUniverse universe) { delegateOnBehaviourAdded = OnBehaviourAdded; delegateOnBehaviourRemoved = OnBehaviourRemoved; diff --git a/Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs b/Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs new file mode 100644 index 0000000..502e45c --- /dev/null +++ b/Engine.Core/Collectors/ActiveBehaviourCollectorOrdered.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; + +namespace Engine.Core; + +public class ActiveBehaviourCollectorOrdered : ActiveBehaviourCollector where TItem : class, IBehaviour where TIndex : IComparable +{ + private readonly Event.EventHandler delegateOnPriorityChanged = null!; + + private readonly SortedDictionary> behaviours = null!; + + private readonly Func getIndexFunc = null!; + private readonly IComparer sortBy = null!; + + private int count = 0; + public override int Count => count; + + public override TItem this[Index index] + { + get + { + int actualIndex = index.IsFromEnd + ? count - index.Value + : index.Value; + + if (actualIndex < 0 || actualIndex >= count) + throw new IndexOutOfRangeException(); + + int leftIndex = actualIndex; + foreach ((TIndex i, FastList list) in behaviours) + { + if (leftIndex < list.Count) + return list[leftIndex]; + leftIndex -= list.Count; + } + throw new IndexOutOfRangeException(); + } + } + + protected override bool RemoveBehaviour(TItem tBehaviour) + { + TIndex index = getIndexFunc(tBehaviour); + if (!behaviours.TryGetValue(index, out FastList? list)) + throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector"); + + if (!list.Remove(tBehaviour)) + return false; + + count--; + return true; + } + + protected override void AddBehaviour(TItem behaviour) + { + TIndex key = getIndexFunc(behaviour); + if (!behaviours.TryGetValue(key, out FastList? list)) + behaviours[key] = list = []; + + count++; + list.Add(behaviour); + } + + protected override void OnBehaviourAdd(IBehaviour behaviour) => behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged); + protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged); + + private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args) + { + TItem behaviour = (TItem)sender; + RemoveBehaviour(behaviour); + AddBehaviour(behaviour); + } + + public ActiveBehaviourCollectorOrdered(Func getIndexFunc, Comparison sortBy) + { + delegateOnPriorityChanged = OnPriorityChanged; + this.getIndexFunc = getIndexFunc; + this.sortBy = Comparer.Create(sortBy); + behaviours = new(this.sortBy); + } + + public ActiveBehaviourCollectorOrdered(IUniverse universe, Func getIndexFunc, Comparison sortBy) : base(universe) + { + delegateOnPriorityChanged = OnPriorityChanged; + this.getIndexFunc = getIndexFunc; + this.sortBy = Comparer.Create(sortBy); + behaviours = new(this.sortBy); + } + + public ActiveBehaviourCollectorOrdered(Func getIndexFunc, IComparer sortBy) + { + this.getIndexFunc = getIndexFunc; + delegateOnPriorityChanged = OnPriorityChanged; + this.sortBy = sortBy; + behaviours = new(sortBy); + } + + public ActiveBehaviourCollectorOrdered(IUniverse universe, Func getIndexFunc, IComparer sortBy) : base(universe) + { + delegateOnPriorityChanged = OnPriorityChanged; + this.getIndexFunc = getIndexFunc; + this.sortBy = sortBy; + behaviours = new(sortBy); + } +} diff --git a/Engine.Core/Collectors/BehaviourCollector.cs b/Engine.Core/Collectors/BehaviourCollector.cs new file mode 100644 index 0000000..137088c --- /dev/null +++ b/Engine.Core/Collectors/BehaviourCollector.cs @@ -0,0 +1,17 @@ +using System; + +namespace Engine.Core; + +public class BehaviourCollector : BehaviourCollectorBase where T : class +{ + protected readonly FastList behaviours = new(32); + + public override T this[Index index] => behaviours[index]; + public override int Count => behaviours.Count; + + protected override void AddBehaviour(T behaviour) => behaviours.Add(behaviour); + protected override bool RemoveBehaviour(T tBehaviour) => behaviours.Remove(tBehaviour); + + public BehaviourCollector() { } + public BehaviourCollector(IUniverse universe) : base(universe) { } +} diff --git a/Engine.Core/BehaviourCollector.cs b/Engine.Core/Collectors/BehaviourCollectorBase.cs similarity index 87% rename from Engine.Core/BehaviourCollector.cs rename to Engine.Core/Collectors/BehaviourCollectorBase.cs index 19d8782..e531466 100644 --- a/Engine.Core/BehaviourCollector.cs +++ b/Engine.Core/Collectors/BehaviourCollectorBase.cs @@ -1,24 +1,87 @@ using System; -using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; -public class BehaviourCollector : IBehaviourCollector where T : class +public abstract class BehaviourCollectorBase : IBehaviourCollector where T : class { - public Event, IBehaviourCollector.BehaviourCollectedArguments> OnCollected { get; } = new(); - public Event, IBehaviourCollector.BehaviourRemovedArguments> OnRemoved { get; } = new(); - public Event OnUniverseAssigned { get; } = new(); - public Event? OnUnassigned { get; } = new(); - private readonly Event.EventHandler delegateOnBehaviourAdded = null!; private readonly Event.EventHandler delegateOnBehaviourRemoved = null!; private readonly Event.EventHandler delegateOnUniverseObjectRegistered = null!; private readonly Event.EventHandler delegateOnUniverseObjectUnregistered = null!; - protected readonly List behaviours = new(32); + public Event, IBehaviourCollector.BehaviourCollectedArguments> OnCollected { get; } = new(); + public Event, IBehaviourCollector.BehaviourRemovedArguments> OnRemoved { get; } = new(); + public Event OnUniverseAssigned { get; } = new(); + public Event? OnUnassigned { get; } = new(); public IUniverse Universe { get; private set; } = null!; + public abstract int Count { get; } + + public abstract T this[Index index] { get; } + + public bool Assign(IUniverse universe) + { + if (Universe is not null) + return false; + + foreach (IUniverseObject universeObject in universe.UniverseObjects) + OnUniverseObjectRegistered(universe, new(universeObject)); + + universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered); + universe.OnPreUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered); + + Universe = universe; + OnAssign(universe); + OnUniverseAssigned?.Invoke(this); + + return true; + } + + public bool Unassign() + { + if (Universe is null) + return false; + + foreach (IUniverseObject universeObject in Universe.UniverseObjects) + OnUniverseObjectUnregistered(Universe, new(universeObject)); + + Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered); + Universe.OnPreUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered); + + Universe = null!; + OnUnassigned?.Invoke(this); + return true; + } + + protected virtual void OnAssign(IUniverse universe) { } + + protected abstract void AddBehaviour(T behaviour); + protected virtual void OnBehaviourAdd(IBehaviour behaviour) { } + private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args) + { + if (args.BehaviourAdded is not T tBehaviour) + return; + + AddBehaviour(tBehaviour); + OnBehaviourAdd(args.BehaviourAdded); + OnCollected?.Invoke(this, new(tBehaviour)); + } + + protected abstract bool RemoveBehaviour(T tBehaviour); + protected virtual void OnBehaviourRemove(IBehaviour behaviour) { } + private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args) + { + if (args.BehaviourRemoved is not T tBehaviour) + return; + + if (!RemoveBehaviour(tBehaviour)) + return; + + OnBehaviourRemove(args.BehaviourRemoved); + OnRemoved?.Invoke(this, new(tBehaviour)); + } + private void OnUniverseObjectRegistered(IUniverse manager, IUniverse.UniverseObjectRegisteredArguments args) { IUniverseObject universeObject = args.UniverseObjectRegistered; @@ -41,70 +104,7 @@ public class BehaviourCollector : IBehaviourCollector where T : class OnBehaviourRemoved(universeObject.BehaviourController, new(universeObject.BehaviourController[i])); } - protected virtual void AddBehaviour(T behaviour) => behaviours.Add(behaviour); - protected virtual void OnBehaviourAdd(IBehaviour behaviour) { } - private void OnBehaviourAdded(IBehaviourController controller, IBehaviourController.BehaviourAddedArguments args) - { - if (args.BehaviourAdded is not T tBehaviour) - return; - - AddBehaviour(tBehaviour); - OnBehaviourAdd(args.BehaviourAdded); - OnCollected?.Invoke(this, new(tBehaviour)); - } - - protected virtual void OnBehaviourRemove(IBehaviour behaviour) { } - private void OnBehaviourRemoved(IBehaviourController controller, IBehaviourController.BehaviourRemovedArguments args) - { - if (args.BehaviourRemoved is not T tBehaviour) - return; - - if (!behaviours.Remove(tBehaviour)) - return; - - OnBehaviourRemove(args.BehaviourRemoved); - OnRemoved?.Invoke(this, new(tBehaviour)); - } - - protected virtual void OnAssign(IUniverse universe) { } - public bool Assign(IUniverse universe) - { - if (Universe is not null) - return false; - - foreach (IUniverseObject universeObject in universe.UniverseObjects) - OnUniverseObjectRegistered(universe, new(universeObject)); - - universe.OnUniverseObjectRegistered.AddListener(delegateOnUniverseObjectRegistered); - universe.OnUniverseObjectUnRegistered.AddListener(delegateOnUniverseObjectUnregistered); - - Universe = universe; - OnAssign(universe); - OnUniverseAssigned?.Invoke(this); - - return true; - } - - public bool Unassign() - { - if (Universe is null) - return false; - - foreach (IUniverseObject universeObject in Universe.UniverseObjects) - OnUniverseObjectUnregistered(Universe, new(universeObject)); - - Universe.OnUniverseObjectRegistered.RemoveListener(delegateOnUniverseObjectRegistered); - Universe.OnUniverseObjectUnRegistered.RemoveListener(delegateOnUniverseObjectUnregistered); - - Universe = null!; - OnUnassigned?.Invoke(this); - return true; - } - - public int Count => behaviours.Count; - public T this[Index index] => behaviours[index]; - - public BehaviourCollector() + public BehaviourCollectorBase() { delegateOnBehaviourAdded = OnBehaviourAdded; delegateOnBehaviourRemoved = OnBehaviourRemoved; @@ -112,7 +112,7 @@ public class BehaviourCollector : IBehaviourCollector where T : class delegateOnUniverseObjectUnregistered = OnUniverseObjectUnregistered; } - public BehaviourCollector(IUniverse universe) + public BehaviourCollectorBase(IUniverse universe) { delegateOnBehaviourAdded = OnBehaviourAdded; delegateOnBehaviourRemoved = OnBehaviourRemoved; diff --git a/Engine.Core/Collectors/BehaviourCollectorOrdered.cs b/Engine.Core/Collectors/BehaviourCollectorOrdered.cs new file mode 100644 index 0000000..6f1077a --- /dev/null +++ b/Engine.Core/Collectors/BehaviourCollectorOrdered.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; + +namespace Engine.Core; + +public class BehaviourCollectorOrdered : BehaviourCollectorBase where TItem : class where TIndex : IComparable +{ + private readonly Event.EventHandler delegateOnPriorityChanged = null!; + + private readonly SortedDictionary> behaviours = null!; + + private readonly Func getIndexFunc = null!; + private readonly IComparer sortBy = null!; + + private int count = 0; + public override int Count => count; + + public override TItem this[Index index] + { + get + { + int actualIndex = index.IsFromEnd + ? count - index.Value + : index.Value; + + if (actualIndex < 0 || actualIndex >= count) + throw new IndexOutOfRangeException(); + + int leftIndex = actualIndex; + foreach ((TIndex i, FastList list) in behaviours) + { + if (leftIndex < list.Count) + return list[leftIndex]; + leftIndex -= list.Count; + } + throw new IndexOutOfRangeException(); + } + } + + protected override bool RemoveBehaviour(TItem tBehaviour) + { + TIndex index = getIndexFunc(tBehaviour); + if (!behaviours.TryGetValue(index, out FastList? list)) + throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector"); + + if (!list.Remove(tBehaviour)) + return false; + + count--; + return true; + } + + protected override void AddBehaviour(TItem behaviour) + { + TIndex key = getIndexFunc(behaviour); + if (!behaviours.TryGetValue(key, out FastList? list)) + behaviours[key] = list = []; + + count++; + list.Add(behaviour); + } + + protected override void OnBehaviourAdd(IBehaviour behaviour) => behaviour.OnPriorityChanged.AddListener(delegateOnPriorityChanged); + protected override void OnBehaviourRemove(IBehaviour behaviour) => behaviour.OnPriorityChanged.RemoveListener(delegateOnPriorityChanged); + + private void OnPriorityChanged(IBehaviour sender, IBehaviour.PriorityChangedArguments args) + { + TItem behaviour = (TItem)sender; + RemoveBehaviour(behaviour); + AddBehaviour(behaviour); + } + + public BehaviourCollectorOrdered(Func getIndexFunc, Comparison sortBy) + { + delegateOnPriorityChanged = OnPriorityChanged; + this.getIndexFunc = getIndexFunc; + this.sortBy = Comparer.Create(sortBy); + behaviours = new(this.sortBy); + } + + public BehaviourCollectorOrdered(IUniverse universe, Func getIndexFunc, Comparison sortBy) : base(universe) + { + delegateOnPriorityChanged = OnPriorityChanged; + this.getIndexFunc = getIndexFunc; + this.sortBy = Comparer.Create(sortBy); + behaviours = new(this.sortBy); + } + + public BehaviourCollectorOrdered(Func getIndexFunc, IComparer sortBy) + { + this.getIndexFunc = getIndexFunc; + delegateOnPriorityChanged = OnPriorityChanged; + this.sortBy = sortBy; + behaviours = new(sortBy); + } + + public BehaviourCollectorOrdered(IUniverse universe, Func getIndexFunc, IComparer sortBy) : base(universe) + { + delegateOnPriorityChanged = OnPriorityChanged; + this.getIndexFunc = getIndexFunc; + this.sortBy = sortBy; + behaviours = new(sortBy); + } +} diff --git a/Engine.Core/Debug/AssertHelpers.cs b/Engine.Core/Debug/AssertHelpers.cs index 0da4bc9..6a2f9e5 100644 --- a/Engine.Core/Debug/AssertHelpers.cs +++ b/Engine.Core/Debug/AssertHelpers.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; -namespace Syntriax.Engine.Core.Debug; +namespace Engine.Core.Debug; public static class Assert { @@ -10,21 +10,21 @@ public static class Assert [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void AssertBehaviourControllerAssigned(IHasBehaviourController assignable) - => System.Diagnostics.Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be initialized"); + => System.Diagnostics.Debug.Assert(assignable.BehaviourController is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IBehaviourController)}"); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void AssertEntityAssigned(IHasEntity assignable) - => System.Diagnostics.Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be initialized"); + => System.Diagnostics.Debug.Assert(assignable.Entity is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IEntity)}"); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void AssertUniverseAssigned(IHasUniverse assignable) - => System.Diagnostics.Debug.Assert(assignable.Universe is not null, $"{assignable.GetType().Name} must be initialized"); + => System.Diagnostics.Debug.Assert(assignable.Universe is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IUniverse)}"); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void AssertUniverseObjectAssigned(IHasUniverseObject assignable) - => System.Diagnostics.Debug.Assert(assignable.UniverseObject is not null, $"{assignable.GetType().Name} must be initialized"); + => System.Diagnostics.Debug.Assert(assignable.UniverseObject is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IUniverseObject)}"); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void AssertStateEnableAssigned(IHasStateEnable assignable) - => System.Diagnostics.Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be initialized"); + => System.Diagnostics.Debug.Assert(assignable.StateEnable is not null, $"{assignable.GetType().Name} must be assigned an {nameof(IStateEnable)}"); } diff --git a/Engine.Core/Debug/ConsoleLogger.cs b/Engine.Core/Debug/ConsoleLogger.cs index 242b818..a76b699 100644 --- a/Engine.Core/Debug/ConsoleLogger.cs +++ b/Engine.Core/Debug/ConsoleLogger.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core.Debug; +namespace Engine.Core.Debug; public class ConsoleLogger : LoggerBase { diff --git a/Engine.Core/Debug/FileLogger.cs b/Engine.Core/Debug/FileLogger.cs index a7bc9c4..060ea6a 100644 --- a/Engine.Core/Debug/FileLogger.cs +++ b/Engine.Core/Debug/FileLogger.cs @@ -1,20 +1,32 @@ using System; using System.IO; -namespace Syntriax.Engine.Core.Debug; +namespace Engine.Core.Debug; public class FileLogger : LoggerBase { public readonly string FilePath; - public FileLogger(string filePath) - { - FilePath = filePath; - File.Open(filePath, FileMode.Create).Close(); - } - protected override void Write(string message) { File.AppendAllTextAsync(FilePath, $"{message}{Environment.NewLine}"); } + + public FileLogger(string filePath) + { + if (!filePath.EndsWith(".log")) + filePath += ".log"; + + FilePath = filePath; + + bool isRelativePath = Path.GetFullPath(filePath).CompareTo(filePath) != 0; + + if (isRelativePath) + FilePath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, filePath)); + + if (Path.GetDirectoryName(FilePath) is string directoryPath) + Directory.CreateDirectory(directoryPath); + + File.Open(FilePath, FileMode.Create).Close(); + } } diff --git a/Engine.Core/Debug/ILogger.cs b/Engine.Core/Debug/ILogger.cs index c1e78d2..664ec45 100644 --- a/Engine.Core/Debug/ILogger.cs +++ b/Engine.Core/Debug/ILogger.cs @@ -1,13 +1,16 @@ -namespace Syntriax.Engine.Core.Debug; +namespace Engine.Core.Debug; public interface ILogger { + static ILogger Shared { get; set; } = new ConsoleLogger(); + Level FilterLevel { get; set; } void Log(string message, Level level = Level.Info, bool force = false); enum Level { + Trace, Info, Warning, Error, diff --git a/Engine.Core/Debug/LoggerBase.cs b/Engine.Core/Debug/LoggerBase.cs index 6b0439c..2626b69 100644 --- a/Engine.Core/Debug/LoggerBase.cs +++ b/Engine.Core/Debug/LoggerBase.cs @@ -1,10 +1,10 @@ using System; -namespace Syntriax.Engine.Core.Debug; +namespace Engine.Core.Debug; public abstract class LoggerBase : ILogger { - public ILogger.Level FilterLevel { get; set; } = ILogger.Level.Info; + public ILogger.Level FilterLevel { get; set; } = ILogger.Level.Trace; public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false) { diff --git a/Engine.Core/Debug/LoggerContainer.cs b/Engine.Core/Debug/LoggerContainer.cs new file mode 100644 index 0000000..5cbc259 --- /dev/null +++ b/Engine.Core/Debug/LoggerContainer.cs @@ -0,0 +1,9 @@ +namespace Engine.Core.Debug; + +public class LoggerContainer : Behaviour, ILogger +{ + public ILogger Logger { get; set; } = ILogger.Shared; + + public ILogger.Level FilterLevel { get => Logger.FilterLevel; set => Logger.FilterLevel = value; } + public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false) => Logger.Log(message, level, force); +} diff --git a/Engine.Core/Debug/LoggerExtensions.cs b/Engine.Core/Debug/LoggerExtensions.cs index ac8e44c..d63421a 100644 --- a/Engine.Core/Debug/LoggerExtensions.cs +++ b/Engine.Core/Debug/LoggerExtensions.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics; -namespace Syntriax.Engine.Core.Debug; +namespace Engine.Core.Debug; public static class LoggerExtensions { @@ -16,13 +16,21 @@ public static class LoggerExtensions public static void LogError(this ILogger logger, T caller, string message, bool force = false) { Log(logger, caller, message, ILogger.Level.Error, force); - Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{new StackTrace()}"); + LogTrace(logger, caller, new StackTrace(), force); } public static void LogException(this ILogger logger, T caller, Exception exception, bool force = false) { + Log(logger, caller, $"Exception of type {exception.GetType().Name} occured", ILogger.Level.Error, force); Log(logger, caller, $"Message: {exception.Message}", ILogger.Level.Error, force); Log(logger, caller, $"InnerException: {exception.InnerException}", ILogger.Level.Error, force); - Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{exception.StackTrace}"); + + // Not using LogTrace because exception.StackTrace is a type of string + Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{exception.StackTrace}", ILogger.Level.Trace); + } + + public static void LogTrace(this ILogger logger, T caller, StackTrace? stackTrace = null, bool force = false) + { + Log(logger, caller, $"{nameof(StackTrace)}:{Environment.NewLine}{stackTrace ?? new()}", ILogger.Level.Trace, force); } } diff --git a/Engine.Core/Debug/LoggerWrapper.cs b/Engine.Core/Debug/LoggerWrapper.cs new file mode 100644 index 0000000..7ac1335 --- /dev/null +++ b/Engine.Core/Debug/LoggerWrapper.cs @@ -0,0 +1,23 @@ +namespace Engine.Core.Debug; + +public class LoggerWrapper(ILogger firstLogger, ILogger secondLogger) : ILogger +{ + private readonly ILogger firstLogger = firstLogger; + private readonly ILogger secondLogger = secondLogger; + + public ILogger.Level FilterLevel + { + get => firstLogger.FilterLevel; + set + { + firstLogger.FilterLevel = value; + secondLogger.FilterLevel = value; + } + } + + public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false) + { + firstLogger.Log(message, level, force); + secondLogger.Log(message, level, force); + } +} diff --git a/Engine.Core/Debug/LoggerWrapperExtensions.cs b/Engine.Core/Debug/LoggerWrapperExtensions.cs new file mode 100644 index 0000000..ce5bfc6 --- /dev/null +++ b/Engine.Core/Debug/LoggerWrapperExtensions.cs @@ -0,0 +1,6 @@ +namespace Engine.Core.Debug; + +public static class LoggerWrapperExtensions +{ + public static ILogger WrapWith(this ILogger thisLogger, ILogger logger) => new LoggerWrapper(thisLogger, logger); +} diff --git a/Engine.Core/Debug/RotatingFileLogger.cs b/Engine.Core/Debug/RotatingFileLogger.cs new file mode 100644 index 0000000..ec3e007 --- /dev/null +++ b/Engine.Core/Debug/RotatingFileLogger.cs @@ -0,0 +1,71 @@ +using System; +using System.IO; +using System.Linq; + +namespace Engine.Core.Debug; + +public class RotatingFileLogger : ILogger +{ + public readonly FileLogger FileLogger = null!; + public readonly string Directory = string.Empty; + public readonly int RotateLength = 3; + + public RotatingFileLogger(string directory, string namePrefix, string nameSuffix = "", int rotateLength = 3) + { + RotateLength = rotateLength; + + string fileName = Path.Combine(directory, namePrefix); + if (!string.IsNullOrWhiteSpace(nameSuffix)) + fileName += $"_{nameSuffix}"; + + bool isRelativePath = Path.GetFullPath(fileName).CompareTo(fileName) != 0; + + if (isRelativePath) + fileName = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName)); + + if (File.Exists($"{fileName}.log")) + RenameExistingLogs(fileName, RotateLength); + + FileLogger = new(fileName); + + Directory = Path.GetDirectoryName(fileName) ?? throw new("Unexpected error on getting directory of logger path"); + RotateLastLogs(Directory, namePrefix, RotateLength); + } + + private static void RenameExistingLogs(string filePath, int rotateLength) + { + for (int i = rotateLength - 1; i >= 0; i--) + { + string source = i == 0 + ? $"{filePath}.log" + : $"{filePath}_{i}.log"; + + string dest = $"{filePath}_{i + 1}.log"; + + if (!File.Exists(source)) + continue; + + if (File.Exists(dest)) + File.Delete(dest); + + File.Move(source, dest); + } + } + + private static void RotateLastLogs(string directory, string prefix, int rotateLength) + { + IOrderedEnumerable logs = System.IO.Directory.GetFiles(directory, $"{prefix}*.log") + .OrderBy(File.GetCreationTime); + + foreach (string file in logs.Skip(rotateLength)) + try + { + ILogger.Shared.Log($"Removing log file located at \"{file}\" during rotation."); + File.Delete(file); + } + catch (Exception e) { ILogger.Shared.LogException($"Failed to rotate log file at \"{file}\"", e); } + } + + public ILogger.Level FilterLevel { get => FileLogger.FilterLevel; set => FileLogger.FilterLevel = value; } + public void Log(string message, ILogger.Level level = ILogger.Level.Info, bool force = false) => FileLogger.Log(message, level, force); +} diff --git a/Engine.Core/Engine.Core.csproj b/Engine.Core/Engine.Core.csproj index b71dec1..f777948 100644 --- a/Engine.Core/Engine.Core.csproj +++ b/Engine.Core/Engine.Core.csproj @@ -4,7 +4,8 @@ net9.0 false enable - Syntriax.Engine.Core + Engine.Core + Engine.Core diff --git a/Engine.Core/Exceptions/AssignFailedException.cs b/Engine.Core/Exceptions/AssignFailedException.cs index 2cc474c..8294f17 100644 --- a/Engine.Core/Exceptions/AssignFailedException.cs +++ b/Engine.Core/Exceptions/AssignFailedException.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core.Exceptions; +namespace Engine.Core.Exceptions; public class AssignFailedException(string? message) : Exception(message) { diff --git a/Engine.Core/Exceptions/BehaviourNotFoundException.cs b/Engine.Core/Exceptions/BehaviourNotFoundException.cs index 36ca458..c340f33 100644 --- a/Engine.Core/Exceptions/BehaviourNotFoundException.cs +++ b/Engine.Core/Exceptions/BehaviourNotFoundException.cs @@ -1,3 +1,3 @@ -namespace Syntriax.Engine.Core.Exceptions; +namespace Engine.Core.Exceptions; public class BehaviourNotFoundException(string? message) : NotFoundException(message); diff --git a/Engine.Core/Exceptions/NotAssignedException.cs b/Engine.Core/Exceptions/NotAssignedException.cs index bd1923a..9bdd476 100644 --- a/Engine.Core/Exceptions/NotAssignedException.cs +++ b/Engine.Core/Exceptions/NotAssignedException.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core.Exceptions; +namespace Engine.Core.Exceptions; public class NotAssignedException(string? message) : Exception(message) { diff --git a/Engine.Core/Exceptions/NotFoundException.cs b/Engine.Core/Exceptions/NotFoundException.cs index b6b0d69..77a9656 100644 --- a/Engine.Core/Exceptions/NotFoundException.cs +++ b/Engine.Core/Exceptions/NotFoundException.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core.Exceptions; +namespace Engine.Core.Exceptions; public class NotFoundException(string? message) : Exception(message) { diff --git a/Engine.Core/Exceptions/UniverseObjectNotFoundException.cs b/Engine.Core/Exceptions/UniverseObjectNotFoundException.cs index 9b68b4d..67f909e 100644 --- a/Engine.Core/Exceptions/UniverseObjectNotFoundException.cs +++ b/Engine.Core/Exceptions/UniverseObjectNotFoundException.cs @@ -1,3 +1,3 @@ -namespace Syntriax.Engine.Core.Exceptions; +namespace Engine.Core.Exceptions; public class UniverseObjectNotFoundException(string? message) : NotFoundException(message); diff --git a/Engine.Core/Extensions/BehaviourControllerExtensions.cs b/Engine.Core/Extensions/BehaviourControllerExtensions.cs index 3f71685..bcf0773 100644 --- a/Engine.Core/Extensions/BehaviourControllerExtensions.cs +++ b/Engine.Core/Extensions/BehaviourControllerExtensions.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Syntriax.Engine.Core.Exceptions; +using Engine.Core.Exceptions; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public static class BehaviourControllerExtensions { @@ -27,7 +27,7 @@ public static class BehaviourControllerExtensions /// The to start searching from. /// The of the specified type if found; otherwise, throws . public static T GetRequiredBehaviour(this IBehaviourController behaviourController) where T : class - => behaviourController.GetBehaviour() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}"); + => behaviourController.GetBehaviour() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName}"); /// /// Gets an existing of the specified type, or adds and returns a new one if it doesn't exist. @@ -93,7 +93,7 @@ public static class BehaviourControllerExtensions /// The to start searching from. /// The of the specified type if found; otherwise, throws . public static T GetRequiredBehaviourInParent(this IBehaviourController behaviourController) where T : class - => behaviourController.GetBehaviourInParent() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent"); + => behaviourController.GetBehaviourInParent() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any parent"); /// /// Gets all s of the specified type in it's 's parents recursively and stores them in the provided list. @@ -140,7 +140,7 @@ public static class BehaviourControllerExtensions if (behaviourController.GetBehaviour() is T localBehaviour) return localBehaviour; - foreach (IUniverseObject child in behaviourController.UniverseObject) + foreach (IUniverseObject child in behaviourController.UniverseObject.Children) if (GetBehaviourInChildren(child.BehaviourController) is T behaviour) return behaviour; @@ -154,7 +154,7 @@ public static class BehaviourControllerExtensions /// The to start searching from. /// The of the specified type if found; otherwise, throws . public static T GetRequiredBehaviourInChildren(this IBehaviourController behaviourController) where T : class - => behaviourController.GetBehaviourInChildren() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject.Name}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children "); + => behaviourController.GetBehaviourInChildren() ?? throw new BehaviourNotFoundException($"{behaviourController.UniverseObject?.Name ?? "NULL"}'s {nameof(IBehaviourController)} does not contain any {typeof(T).FullName} on any children "); /// /// Gets all s of the specified type in it's 's children recursively and stores them in the provided list. @@ -176,7 +176,7 @@ public static class BehaviourControllerExtensions foreach (T behaviour in cache) behaviours.Add(behaviour); - foreach (IUniverseObject child in universeObject) + foreach (IUniverseObject child in universeObject.Children) TraverseChildrenForBehaviour(child, behaviours, cache); } } diff --git a/Engine.Core/Extensions/EnumExtensions.cs b/Engine.Core/Extensions/EnumExtensions.cs index 1f7e8b1..06e4a01 100644 --- a/Engine.Core/Extensions/EnumExtensions.cs +++ b/Engine.Core/Extensions/EnumExtensions.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public static class EnumExtensions { diff --git a/Engine.Core/Extensions/FloatExtensions.cs b/Engine.Core/Extensions/FloatExtensions.cs index 019bc39..fd5b828 100644 --- a/Engine.Core/Extensions/FloatExtensions.cs +++ b/Engine.Core/Extensions/FloatExtensions.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public static class FloatExtensions { diff --git a/Engine.Core/Extensions/TransformExtensions.cs b/Engine.Core/Extensions/TransformExtensions.cs index 33e349a..8300449 100644 --- a/Engine.Core/Extensions/TransformExtensions.cs +++ b/Engine.Core/Extensions/TransformExtensions.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public static class TransformExtensions { diff --git a/Engine.Core/Extensions/UniverseExtensions.cs b/Engine.Core/Extensions/UniverseExtensions.cs index 2624712..e7379b0 100644 --- a/Engine.Core/Extensions/UniverseExtensions.cs +++ b/Engine.Core/Extensions/UniverseExtensions.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core.Exceptions; +using Engine.Core.Exceptions; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public static class UniverseExtensions { diff --git a/Engine.Core/Extensions/UniverseObjectExtensions.cs b/Engine.Core/Extensions/UniverseObjectExtensions.cs index 16ad5cb..2f843cb 100644 --- a/Engine.Core/Extensions/UniverseObjectExtensions.cs +++ b/Engine.Core/Extensions/UniverseObjectExtensions.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Syntriax.Engine.Core.Exceptions; +using Engine.Core.Exceptions; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public static class UniverseObjectExtensions { @@ -12,7 +12,7 @@ public static class UniverseObjectExtensions if (!string.IsNullOrWhiteSpace(name)) universeObject.Name = name; if (parent is not null) - universeObject.SetParent(parent); + universeObject.Parent = parent; return universeObject; } @@ -81,7 +81,7 @@ public static class UniverseObjectExtensions /// The of the specified type if found; otherwise, null. public static T? GetUniverseObjectInParent(this IUniverseObject universeObject) where T : class { - if (universeObject.GetUniverseObject() is T localUniverseObject) + if (universeObject.Children.GetUniverseObject() is T localUniverseObject) return localUniverseObject; IUniverseObject? parent = universeObject; @@ -129,10 +129,10 @@ public static class UniverseObjectExtensions /// The of the specified type if found; otherwise, null. public static T? GetUniverseObjectInChildren(this IUniverseObject universeObject) where T : class { - if (universeObject.GetUniverseObject() is T localUniverseObject) + if (universeObject.Children.GetUniverseObject() is T localUniverseObject) return localUniverseObject; - foreach (IUniverseObject child in universeObject) + foreach (IUniverseObject child in universeObject.Children) if (GetUniverseObjectInChildren(child) is T behaviour) return behaviour; @@ -246,7 +246,7 @@ public static class UniverseObjectExtensions foreach (IUniverseObject universeObject in universeObjects) { - universeObject.Find(cache); + universeObject.Children.Find(cache); foreach (T behaviour in cache) instances.Add(behaviour); } diff --git a/Engine.Core/Factory/Abstract/IFactory.cs b/Engine.Core/Factory/Abstract/IFactory.cs index 2d61656..659f073 100644 --- a/Engine.Core/Factory/Abstract/IFactory.cs +++ b/Engine.Core/Factory/Abstract/IFactory.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core.Factory.Abstract; +namespace Engine.Core.Factory.Abstract; public interface IFactory where TInterface : class { diff --git a/Engine.Core/Factory/BehaviourControllerFactory.cs b/Engine.Core/Factory/BehaviourControllerFactory.cs index f3302a6..fb0e6bf 100644 --- a/Engine.Core/Factory/BehaviourControllerFactory.cs +++ b/Engine.Core/Factory/BehaviourControllerFactory.cs @@ -1,13 +1,13 @@ -using Syntriax.Engine.Core.Exceptions; +using Engine.Core.Exceptions; -namespace Syntriax.Engine.Core.Factory; +namespace Engine.Core.Factory; public class BehaviourControllerFactory { - public static IBehaviourController Instantiate(IUniverseObject universeObject) - => Instantiate(universeObject); + public static IBehaviourController Instantiate(IUniverseObject universeObject, IStateEnable? stateEnable = null) + => Instantiate(universeObject, stateEnable); - public static T Instantiate(IUniverseObject universeObject, params object?[]? args) + public static T Instantiate(IUniverseObject universeObject, IStateEnable? stateEnable = null, params object?[]? args) where T : class, IBehaviourController { T behaviourController = TypeFactory.Get(args); @@ -18,6 +18,17 @@ public class BehaviourControllerFactory if (!behaviourController.Assign(universeObject)) throw AssignFailedException.From(behaviourController, universeObject); + if (stateEnable is not null) + { + if (!stateEnable.Assign(behaviourController)) + throw AssignFailedException.From(stateEnable, behaviourController); + + if (!behaviourController.Assign(stateEnable)) + throw AssignFailedException.From(behaviourController, stateEnable); + } + else + StateEnableFactory.Instantiate(behaviourController); + return behaviourController; } } diff --git a/Engine.Core/Factory/BehaviourFactory.cs b/Engine.Core/Factory/BehaviourFactory.cs index f9efb66..4c6bff8 100644 --- a/Engine.Core/Factory/BehaviourFactory.cs +++ b/Engine.Core/Factory/BehaviourFactory.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core.Exceptions; +using Engine.Core.Exceptions; -namespace Syntriax.Engine.Core.Factory; +namespace Engine.Core.Factory; public class BehaviourFactory { @@ -12,12 +12,15 @@ public class BehaviourFactory { T behaviour = TypeFactory.Get(args); - stateEnable ??= TypeFactory.Get(); - if (!stateEnable.Assign(behaviour)) - throw AssignFailedException.From(stateEnable, behaviour); - - if (!behaviour.Assign(stateEnable)) - throw AssignFailedException.From(behaviour, stateEnable); + if (stateEnable is not null) + { + if (!stateEnable.Assign(behaviour)) + throw AssignFailedException.From(stateEnable, behaviour); + if (!behaviour.Assign(stateEnable)) + throw AssignFailedException.From(behaviour, stateEnable); + } + else + StateEnableFactory.Instantiate(behaviour); return behaviour; } diff --git a/Engine.Core/Factory/FactoryBase.cs b/Engine.Core/Factory/FactoryBase.cs index 97f8067..3e90cd8 100644 --- a/Engine.Core/Factory/FactoryBase.cs +++ b/Engine.Core/Factory/FactoryBase.cs @@ -1,7 +1,7 @@ using System; -using Syntriax.Engine.Core.Factory.Abstract; +using Engine.Core.Factory.Abstract; -namespace Syntriax.Engine.Core.Factory; +namespace Engine.Core.Factory; public abstract class FactoryBase : IFactory where TInterface : class diff --git a/Engine.Core/Factory/StateEnableFactory.cs b/Engine.Core/Factory/StateEnableFactory.cs index e7f235a..7bc950f 100644 --- a/Engine.Core/Factory/StateEnableFactory.cs +++ b/Engine.Core/Factory/StateEnableFactory.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core.Exceptions; +using Engine.Core.Exceptions; -namespace Syntriax.Engine.Core.Factory; +namespace Engine.Core.Factory; public class StateEnableFactory { diff --git a/Engine.Core/Factory/TransformFactory.cs b/Engine.Core/Factory/TransformFactory.cs index 6e22f77..182e080 100644 --- a/Engine.Core/Factory/TransformFactory.cs +++ b/Engine.Core/Factory/TransformFactory.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core.Factory; +namespace Engine.Core.Factory; public class TransformFactory { diff --git a/Engine.Core/Factory/TypeFactory.cs b/Engine.Core/Factory/TypeFactory.cs index aa770e1..5f92c68 100644 --- a/Engine.Core/Factory/TypeFactory.cs +++ b/Engine.Core/Factory/TypeFactory.cs @@ -3,7 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -namespace Syntriax.Engine.Core.Factory; +namespace Engine.Core.Factory; public static class TypeFactory { diff --git a/Engine.Core/Factory/UniverseObjectFactory.cs b/Engine.Core/Factory/UniverseObjectFactory.cs index 39c59fc..0d7b21f 100644 --- a/Engine.Core/Factory/UniverseObjectFactory.cs +++ b/Engine.Core/Factory/UniverseObjectFactory.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core.Exceptions; +using Engine.Core.Exceptions; -namespace Syntriax.Engine.Core.Factory; +namespace Engine.Core.Factory; public class UniverseObjectFactory { @@ -18,18 +18,25 @@ public class UniverseObjectFactory { T universeObject = TypeFactory.Get(args); - behaviourController ??= TypeFactory.Get(); - stateEnable ??= TypeFactory.Get(); + if (behaviourController is not null) + { + if (!behaviourController.Assign(universeObject)) + throw AssignFailedException.From(behaviourController, universeObject); + if (!universeObject.Assign(behaviourController)) + throw AssignFailedException.From(universeObject, behaviourController); + } + else + BehaviourControllerFactory.Instantiate(universeObject); - if (!behaviourController.Assign(universeObject)) - throw AssignFailedException.From(behaviourController, universeObject); - if (!stateEnable.Assign(universeObject)) - throw AssignFailedException.From(stateEnable, universeObject); - - if (!universeObject.Assign(behaviourController)) - throw AssignFailedException.From(universeObject, behaviourController); - if (!universeObject.Assign(stateEnable)) - throw AssignFailedException.From(universeObject, stateEnable); + if (stateEnable is not null) + { + if (!stateEnable.Assign(universeObject)) + throw AssignFailedException.From(stateEnable, universeObject); + if (!universeObject.Assign(stateEnable)) + throw AssignFailedException.From(universeObject, stateEnable); + } + else + StateEnableFactory.Instantiate(universeObject); return universeObject; } diff --git a/Engine.Core/Helpers/Event.cs b/Engine.Core/Helpers/Event.cs index c6dd65c..3b4a6db 100644 --- a/Engine.Core/Helpers/Event.cs +++ b/Engine.Core/Helpers/Event.cs @@ -1,35 +1,152 @@ using System; using System.Collections.Generic; -namespace Syntriax.Engine.Core; +using Engine.Core.Debug; +namespace Engine.Core; + +// TODO!: every reverse loop has a chance to have more than 1 unsubscription, +// for (int i = listeners.Count - 1; i >= 0; i--) +// can be replaced with +// for (int i = listeners.Count - 1; i >= 0; i = Math.Min(i - 1, listeners.Count - 1)) +// but this would causes possible double calls on already called callbacks, find a better method. + +/// +/// Represents a simple event with no parameters. +/// Example usage: +/// +/// public class MyBehaviour : Behaviour, IUpdate +/// { +/// public readonly Event MyEvent = new(); +/// +/// public MyBehaviour() +/// { +/// MyEvent.AddListener(OnEventTriggered); +/// MyEvent.AddOneTimeListener(OnEventTriggeredOneTime); +/// } +/// +/// public void Update() +/// { +/// MyEvent.Invoke(); +/// } +/// +/// private void OnEventTriggered() +/// { +/// Console.WriteLine($"Event occurred!"); +/// } +/// +/// private static void OnEventTriggeredOneTime() +/// { +/// Console.WriteLine($"Event called once!"); +/// } +/// } +/// +/// The output of the example code above would be: +/// +/// Event occurred! +/// Event called once! +/// Event occurred! +/// Event occurred! +/// Event occurred! +/// ... +/// +/// public class Event { - private readonly List listeners = null!; - private readonly List onceListeners = null!; + // We use Ascending order because draw calls are running from last to first + private static readonly Comparer SortByAscendingPriority = Comparer.Create((x, y) => x.Priority.CompareTo(y.Priority)); - public void AddListener(EventHandler listener) => listeners.Add(listener); - public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener); - public void RemoveListener(EventHandler listener) => listeners.Remove(listener); - public void RemoveOnceListener(EventHandler listener) => onceListeners.Remove(listener); + private ILogger _logger = ILogger.Shared; + public ILogger Logger { get => _logger; set => _logger = value ?? ILogger.Shared; } + + private readonly List listeners = null!; + private readonly List onceListeners = null!; + + /// + /// Subscribes the callback to be invoked whenever the event is triggered. + /// + /// The callback to be called when the event is triggered. + /// Priority of the callback. + public void AddListener(EventHandler listener, int priority = 0) + { + ListenerData listenerData = new(listener, priority); + + int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority); + if (insertIndex < 0) + insertIndex = ~insertIndex; + + listeners.Insert(insertIndex, listenerData); + } + + /// + /// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once. + /// + /// The callback to be called the next time the event is triggered. + /// Priority of the callback. + public void AddOneTimeListener(EventHandler listener, int priority = 0) + { + ListenerData listenerData = new(listener, priority); + + int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority); + if (insertIndex < 0) + insertIndex = ~insertIndex; + + onceListeners.Insert(insertIndex, listenerData); + } + + /// + /// Unsubscribes the callback that was previously registered by . + /// + /// The callback that was previously registered by + public void RemoveListener(EventHandler listener) + { + for (int i = listeners.Count - 1; i >= 0; i--) + if (listeners[i].Callback == listener) + { + listeners.RemoveAt(i); + return; + } + } + + /// + /// Unsubscribes the callback that was previously registered by . + /// + /// The callback that was previously registered by + public void RemoveOneTimeListener(EventHandler listener) + { + for (int i = 0; i < onceListeners.Count; i++) + if (onceListeners[i].Callback == listener) + { + onceListeners.RemoveAt(i); + return; + } + } + + /// + /// Unsubscribes all listeners that was previously registered by either or . + /// public void Clear() { listeners.Clear(); onceListeners.Clear(); } + + /// + /// Triggers the event. + /// public void Invoke() { - for (int i = 0; i < listeners.Count; i++) - try { listeners[i].Invoke(); } + for (int i = listeners.Count - 1; i >= 0; i--) + try { listeners[i].Callback.Invoke(); } catch (Exception exception) { - string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}()"; - Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); + string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}()"; + EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? this, Logger, exception, methodCallRepresentation); } for (int i = onceListeners.Count - 1; i >= 0; i--) { - try { onceListeners[i].Invoke(); } + try { onceListeners[i].Callback.Invoke(); } catch (Exception exception) { - string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}()"; - Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); + string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}()"; + EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? this, Logger, exception, methodCallRepresentation); } onceListeners.RemoveAt(i); } @@ -48,35 +165,148 @@ public class Event } public delegate void EventHandler(); + private record struct ListenerData(EventHandler Callback, int Priority); } -public class Event +/// +/// Represents an event with only sender parameters. +/// Example usage: +/// +/// public class MyBehaviour : Behaviour, IUpdate +/// { +/// public readonly Event<MyBehaviour> MyEvent = new(); +/// +/// public MyBehaviour() +/// { +/// MyEvent.AddListener(OnEventTriggered); +/// MyEvent.AddOneTimeListener(OnEventTriggeredOneTime); +/// } +/// +/// public void Update() +/// { +/// MyEvent.Invoke(this); +/// } +/// +/// private void OnEventTriggered(MyBehaviour sender) +/// { +/// Console.WriteLine($"{sender.Id}'s event occurred!"); +/// } +/// +/// private static void OnEventTriggeredOneTime(MyBehaviour sender) +/// { +/// Console.WriteLine($"{sender.Id}'s event called once!"); +/// } +/// } +/// +/// The output of the example code above would be: +/// +/// [Id]'s event occurred! +/// [Id]'s event called once! +/// [Id]'s event occurred! +/// [Id]'s event occurred! +/// [Id]'s event occurred! +/// ... +/// +/// +/// +/// Sender type +public class Event where TSender : class { - private readonly List listeners = null!; - private readonly List onceListeners = null!; + // We use Ascending order because draw calls are running from last to first + private static readonly Comparer SortByAscendingPriority = Comparer.Create((x, y) => x.Priority.CompareTo(y.Priority)); - public void AddListener(EventHandler listener) => listeners.Add(listener); - public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener); - public void RemoveListener(EventHandler listener) => listeners.Remove(listener); - public void RemoveOnceListener(EventHandler listener) => onceListeners.Remove(listener); + private ILogger _logger = ILogger.Shared; + public ILogger Logger { get => _logger; set => _logger = value ?? ILogger.Shared; } + + private readonly List listeners = null!; + private readonly List onceListeners = null!; + + /// + /// Subscribes the callback to be invoked whenever the event is triggered. + /// + /// The callback to be called when the event is triggered. + /// Priority of the callback. + public void AddListener(EventHandler listener, int priority = 0) + { + ListenerData listenerData = new(listener, priority); + + int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority); + if (insertIndex < 0) + insertIndex = ~insertIndex; + + listeners.Insert(insertIndex, listenerData); + } + + /// + /// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once. + /// + /// The callback to be called the next time the event is triggered. + /// Priority of the callback. + public void AddOneTimeListener(EventHandler listener, int priority = 0) + { + ListenerData listenerData = new(listener, priority); + + int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority); + if (insertIndex < 0) + insertIndex = ~insertIndex; + + onceListeners.Insert(insertIndex, listenerData); + } + + /// + /// Unsubscribes the callback that was previously registered by . + /// + /// The callback that was previously registered by + public void RemoveListener(EventHandler listener) + { + for (int i = listeners.Count - 1; i >= 0; i--) + if (listeners[i].Callback == listener) + { + listeners.RemoveAt(i); + return; + } + } + + /// + /// Unsubscribes the callback that was previously registered by . + /// + /// The callback that was previously registered by + public void RemoveOneTimeListener(EventHandler listener) + { + for (int i = 0; i < onceListeners.Count; i++) + if (onceListeners[i].Callback == listener) + { + onceListeners.RemoveAt(i); + return; + } + } + + /// + /// Unsubscribes all listeners that was previously registered by either or . + /// public void Clear() { listeners.Clear(); onceListeners.Clear(); } + + /// + /// Triggers the event. + /// + /// The caller that's triggering this event. public void Invoke(TSender sender) { - for (int i = 0; i < listeners.Count; i++) - try { listeners[i].Invoke(sender); } + for (int i = listeners.Count - 1; i >= 0; i--) + try { listeners[i].Callback.Invoke(sender); } catch (Exception exception) { - string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}({sender})"; - Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); + string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({sender})"; + EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation); } for (int i = onceListeners.Count - 1; i >= 0; i--) { - try { onceListeners[i].Invoke(sender); } + try { onceListeners[i].Callback.Invoke(sender); } catch (Exception exception) { - string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}({sender})"; - Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); + string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({sender})"; + EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation); } onceListeners.RemoveAt(i); } @@ -95,35 +325,156 @@ public class Event } public delegate void EventHandler(TSender sender); + private record struct ListenerData(EventHandler Callback, int Priority); } -public class Event +/// +/// Represents an event with sender and argument parameters. +/// Example usage: +/// +/// public class MyBehaviour : Behaviour, IUpdate +/// { +/// public readonly Event<MyBehaviour, MyArguments> MyEvent = new(); +/// +/// private int myInt = 0; +/// private bool myBool = false; +/// +/// public MyBehaviour() +/// { +/// MyEvent.AddOneTimeListener(OnEventTriggeredOneTime); +/// MyEvent.AddListener(OnEventTriggered); +/// } +/// +/// public void Update() +/// { +/// MyEvent.Invoke(this, new MyArguments(myInt, myBool)); +/// myInt++; +/// myBool = !myBool; +/// } +/// +/// private void OnEventTriggered(MyBehaviour sender, MyArguments args) +/// { +/// Console.WriteLine($"{sender.Id}'s event occurred with MyInt: {args.MyInt} and MyBool {args.MyBool}!"); +/// } +/// +/// private static void OnEventTriggeredOneTime(MyBehaviour sender, MyArguments args) +/// { +/// Console.WriteLine($"{sender.Id}'s event called once with MyInt: {args.MyInt} and MyBool {args.MyBool}!"); +/// } +/// +/// public readonly record struct MyArguments(int MyInt, bool MyBool); +/// } +/// +/// The output of the example code above would be: +/// +/// [Id]'s event occurred with MyInt: 0 and MyBool False! +/// [Id]'s event called once with MyInt: 0 and MyBool False! +/// [Id]'s event occurred with MyInt: 1 and MyBool True! +/// [Id]'s event occurred with MyInt: 2 and MyBool False! +/// [Id]'s event occurred with MyInt: 3 and MyBool True! +/// ... +/// +/// +/// +/// Sender type +public class Event where TSender : class { - private readonly List listeners = null!; - private readonly List onceListeners = null!; + // We use Ascending order because draw calls are running from last to first + private static readonly Comparer SortByAscendingPriority = Comparer.Create((x, y) => x.Priority.CompareTo(y.Priority)); - public void AddListener(EventHandler listener) => listeners.Add(listener); - public void AddOnceListener(EventHandler listener) => onceListeners.Add(listener); - public void RemoveListener(EventHandler listener) => listeners.Remove(listener); - public void RemoveOnceListener(EventHandler listener) => onceListeners.Remove(listener); + private ILogger _logger = ILogger.Shared; + public ILogger Logger { get => _logger; set => _logger = value ?? ILogger.Shared; } + + private readonly List listeners = null!; + private readonly List onceListeners = null!; + + /// + /// Subscribes the callback to be invoked whenever the event is triggered. + /// + /// The callback to be called when the event is triggered. + /// Priority of the callback. + public void AddListener(EventHandler listener, int priority = 0) + { + ListenerData listenerData = new(listener, priority); + + int insertIndex = listeners.BinarySearch(listenerData, SortByAscendingPriority); + if (insertIndex < 0) + insertIndex = ~insertIndex; + + listeners.Insert(insertIndex, listenerData); + } + + /// + /// Subscribes the callback to be invoked the next time the event is triggered. The callback will be called only once. + /// + /// The callback to be called the next time the event is triggered. + /// Priority of the callback. + public void AddOneTimeListener(EventHandler listener, int priority = 0) + { + ListenerData listenerData = new(listener, priority); + + int insertIndex = onceListeners.BinarySearch(listenerData, SortByAscendingPriority); + if (insertIndex < 0) + insertIndex = ~insertIndex; + + onceListeners.Insert(insertIndex, listenerData); + } + + /// + /// Unsubscribes the callback that was previously registered by . + /// + /// The callback that was previously registered by + public void RemoveListener(EventHandler listener) + { + for (int i = listeners.Count - 1; i >= 0; i--) + if (listeners[i].Callback == listener) + { + listeners.RemoveAt(i); + return; + } + } + + /// + /// Unsubscribes the callback that was previously registered by . + /// + /// The callback that was previously registered by + public void RemoveOneTimeListener(EventHandler listener) + { + for (int i = 0; i < onceListeners.Count; i++) + if (onceListeners[i].Callback == listener) + { + onceListeners.RemoveAt(i); + return; + } + } + + /// + /// Unsubscribes all listeners that was previously registered by either or . + /// public void Clear() { listeners.Clear(); onceListeners.Clear(); } + + /// + /// Triggers the event. + /// + /// The caller that's triggering this event. + /// The arguments provided for this event. public void Invoke(TSender sender, TArguments args) { - for (int i = 0; i < listeners.Count; i++) - try { listeners[i].Invoke(sender, args); } + for (int i = listeners.Count - 1; i >= 0; i--) + try { listeners[i].Callback.Invoke(sender, args); } catch (Exception exception) { - string methodCallRepresentation = $"{listeners[i].Method.DeclaringType?.FullName}.{listeners[i].Method.Name}({string.Join(", ", sender, args)})"; - Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); + string methodCallRepresentation = $"{listeners[i].Callback.Method.DeclaringType?.FullName}.{listeners[i].Callback.Method.Name}({sender}, {args})"; + EventHelpers.LogInvocationException(listeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation); } for (int i = onceListeners.Count - 1; i >= 0; i--) { - try { onceListeners[i].Invoke(sender, args); } + try { onceListeners[i].Callback.Invoke(sender, args); } catch (Exception exception) { - string methodCallRepresentation = $"{onceListeners[i].Method.DeclaringType?.FullName}.{onceListeners[i].Method.Name}({string.Join(", ", sender, args)})"; - Console.WriteLine($"Unexpected exception on invocation of method {methodCallRepresentation}:{Environment.NewLine}{exception.InnerException}"); + string methodCallRepresentation = $"{onceListeners[i].Callback.Method.DeclaringType?.FullName}.{onceListeners[i].Callback.Method.Name}({sender}, {args})"; + EventHelpers.LogInvocationException(onceListeners[i].Callback.Target ?? sender, Logger, exception, methodCallRepresentation); } onceListeners.RemoveAt(i); } @@ -142,4 +493,14 @@ public class Event } public delegate void EventHandler(TSender sender, TArguments args); + private record struct ListenerData(EventHandler Callback, int Priority); +} + +internal static class EventHelpers +{ + public static void LogInvocationException(object sender, ILogger logger, Exception exception, string methodCallRepresentation) + { + logger.LogException(sender, exception); + logger.LogError(sender, $"Unexpected exception on invocation of method {methodCallRepresentation}"); + } } diff --git a/Engine.Core/Helpers/FastList.cs b/Engine.Core/Helpers/FastList.cs new file mode 100644 index 0000000..32c486b --- /dev/null +++ b/Engine.Core/Helpers/FastList.cs @@ -0,0 +1,102 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Engine.Core; + +public class FastList : IList, IReadOnlyList, IEnumerable where T : notnull +{ + private readonly List items = []; + private readonly Dictionary indexMap = []; + + public bool IsReadOnly { get; set; } = false; + public int Count => items.Count; + public T this[int index] + { + get => items[index]; + set + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + items[index] = value; + } + } + + public void Add(T item) + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + indexMap[item] = items.Count; + items.Add(item); + } + + public void RemoveAt(int i) => Remove(items[i], i); + public bool Remove(T item) + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + if (!indexMap.TryGetValue(item, out int index)) + return false; + + Remove(item, index); + + return true; + } + + private void Remove(T item, int index) + { + int lastIndex = items.Count - 1; + T lastItem = items[lastIndex]; + + items[index] = lastItem; + indexMap[lastItem] = index; + + items.RemoveAt(lastIndex); + indexMap.Remove(item); + } + + public void Insert(int index, T item) + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + items.Insert(index, item); + + for (int i = index; i < items.Count; i++) + indexMap[items[i]] = i; + } + + public void Clear() + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + items.Clear(); + indexMap.Clear(); + } + + public bool Contains(T item) => indexMap.ContainsKey(item); + public int IndexOf(T item) => items.IndexOf(item); + public int BinarySearch(T item, IComparer? comparer = null) => items.BinarySearch(item, comparer); + + public void Sort(IComparer comparer) + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + items.Sort(comparer); + + for (int i = 0; i < items.Count; i++) + indexMap[items[i]] = i; + } + + public void CopyTo(T[] array, int arrayIndex) => items.CopyTo(array, arrayIndex); + + public IEnumerator GetEnumerator() => items.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public FastList() { } + public FastList(int count) { items.Capacity = count; } +} diff --git a/Engine.Core/Helpers/FastListOrdered.cs b/Engine.Core/Helpers/FastListOrdered.cs new file mode 100644 index 0000000..d99c826 --- /dev/null +++ b/Engine.Core/Helpers/FastListOrdered.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Engine.Core; + +/// +/// TODO This is VEERY experimental, and doesn't work well with the indices access. Use with caution +/// +/// +/// +public class FastListOrdered : IList, IReadOnlyList, IEnumerable where TItem : notnull where TIndex : IComparable +{ + private readonly SortedDictionary> items = null!; + + private readonly Func getIndexFunc = null!; + private readonly IComparer sortBy = null!; + + private int count = 0; + public int Count => count; + + public bool IsReadOnly { get; set; } = false; + + public TItem this[int index] + { + get { (TIndex tIndex, int i) = GetAt(index); return items[tIndex][i]; } + set + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + (TIndex tIndex, int i) = GetAt(index); items[tIndex][i] = value; + } + } + + private (TIndex TIndex, int i) GetAt(Index index) + { + int actualIndex = index.IsFromEnd + ? count - index.Value + : index.Value; + + if (actualIndex < 0 || actualIndex >= count) + throw new IndexOutOfRangeException(); + + int leftIndex = actualIndex; + foreach ((TIndex i, FastList list) in items) + { + if (leftIndex < list.Count) + return (i, leftIndex); + leftIndex -= list.Count; + } + throw new IndexOutOfRangeException(); + } + + public int IndexOf(TItem item) + { + int indexCounter = 0; + foreach ((TIndex index, FastList list) in items) + { + int i = list.IndexOf(item); + if (i != -1) + return indexCounter + i; + indexCounter += list.Count; + } + + return -1; + } + + public void Add(TItem item) + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + TIndex key = getIndexFunc(item); + if (!items.TryGetValue(key, out FastList? list)) + items[key] = list = []; + + list.Add(item); + count++; + } + + public void Insert(int index, TItem item) + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + TIndex tIndex = getIndexFunc(item); + if (!items.TryGetValue(tIndex, out FastList? list)) + items[tIndex] = list = []; + + list.Insert(index, item); + count++; + } + + public bool Remove(TItem item) + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + TIndex index = getIndexFunc(item); + if (!items.TryGetValue(index, out FastList? list)) + throw new Exceptions.NotFoundException($"Index of '{index}' is not found in the collector"); + + if (!list.Remove(item)) + return false; + + count--; + return true; + } + + public void RemoveAt(int index) + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + (TIndex tIndex, int i) = GetAt(index); + items[tIndex].RemoveAt(i); + count--; + } + + public void Clear() + { + if (IsReadOnly) + throw new System.Data.ReadOnlyException(); + + foreach ((TIndex index, FastList list) in items) + list.Clear(); + + count = 0; + } + + public bool Contains(TItem item) + { + foreach ((TIndex index, FastList list) in items) + if (list.Contains(item)) + return true; + return false; + } + + public void CopyTo(TItem[] array, int arrayIndex) + { + int indexCounter = 0; + + foreach ((TIndex index, FastList list) in items) + { + list.CopyTo(array, indexCounter); + indexCounter += list.Count; + } + } + + public IEnumerator GetEnumerator() + { + foreach ((TIndex index, FastList list) in items) + foreach (TItem item in list) + yield return item; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public FastListOrdered(Func getIndexFunc, Comparison sortBy) + { + this.getIndexFunc = getIndexFunc; + this.sortBy = Comparer.Create(sortBy); + items = new(this.sortBy); + } + + public FastListOrdered(Func getIndexFunc, IComparer sortBy) + { + this.getIndexFunc = getIndexFunc; + this.sortBy = sortBy; + items = new(sortBy); + } +} diff --git a/Engine.Core/Helpers/IPool.cs b/Engine.Core/Helpers/IPool.cs index e151ff6..09849e9 100644 --- a/Engine.Core/Helpers/IPool.cs +++ b/Engine.Core/Helpers/IPool.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public interface IPool { diff --git a/Engine.Core/Helpers/ListPool.cs b/Engine.Core/Helpers/ListPool.cs index 19884e6..149e015 100644 --- a/Engine.Core/Helpers/ListPool.cs +++ b/Engine.Core/Helpers/ListPool.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public class ListPool : IPool> { @@ -31,10 +31,10 @@ public class ListPool : IPool> OnReturned?.Invoke(this, list); } - public ListPool(Func> generator, int initialCapacity = 1) + public ListPool(int initialListCount = 1, int initialListCapacity = 32) { - this.generator = generator; - for (int i = 0; i < initialCapacity; i++) + generator = () => new(initialListCapacity); + for (int i = 0; i < initialListCount; i++) queue.Enqueue(generator()); } } diff --git a/Engine.Core/Helpers/Pool.cs b/Engine.Core/Helpers/Pool.cs index 1b42c77..a5cfc9a 100644 --- a/Engine.Core/Helpers/Pool.cs +++ b/Engine.Core/Helpers/Pool.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public class Pool : IPool { @@ -10,22 +10,25 @@ public class Pool : IPool private readonly Func generator = null!; private readonly Queue queue = new(); + private readonly HashSet queuedHashes = []; public T Get() { if (!queue.TryDequeue(out T? result)) result = generator(); + queuedHashes.Remove(result); OnRemoved?.Invoke(this, result); return result; } public void Return(T item) { - if (queue.Contains(item)) + if (queuedHashes.Contains(item)) return; queue.Enqueue(item); + queuedHashes.Add(item); OnReturned?.Invoke(this, item); } diff --git a/Engine.Core/Helpers/Progression/IProgressionTracker.cs b/Engine.Core/Helpers/Progression/IProgressionTracker.cs index 7edd8a6..89f5653 100644 --- a/Engine.Core/Helpers/Progression/IProgressionTracker.cs +++ b/Engine.Core/Helpers/Progression/IProgressionTracker.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public interface IProgressionTracker : IReadOnlyProgressionTracker { diff --git a/Engine.Core/Helpers/Progression/IReadOnlyProgressionTracker.cs b/Engine.Core/Helpers/Progression/IReadOnlyProgressionTracker.cs index a92034d..e65f859 100644 --- a/Engine.Core/Helpers/Progression/IReadOnlyProgressionTracker.cs +++ b/Engine.Core/Helpers/Progression/IReadOnlyProgressionTracker.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public interface IReadOnlyProgressionTracker { diff --git a/Engine.Core/Helpers/Progression/ProgressionTracker.cs b/Engine.Core/Helpers/Progression/ProgressionTracker.cs index 93cf936..75fec53 100644 --- a/Engine.Core/Helpers/Progression/ProgressionTracker.cs +++ b/Engine.Core/Helpers/Progression/ProgressionTracker.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public class ProgressionTracker : IProgressionTracker { diff --git a/Engine.Core/Helpers/Progression/ProgressiveTask.cs b/Engine.Core/Helpers/Progression/ProgressiveTask.cs index 701027c..05ae750 100644 --- a/Engine.Core/Helpers/Progression/ProgressiveTask.cs +++ b/Engine.Core/Helpers/Progression/ProgressiveTask.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public record struct ProgressiveTask(IReadOnlyProgressionTracker ProgressionTracker, Task Task) { diff --git a/Engine.Core/Math.cs b/Engine.Core/Math.cs index 4fe4d52..b65cc5e 100644 --- a/Engine.Core/Math.cs +++ b/Engine.Core/Math.cs @@ -1,34 +1,34 @@ using System; using System.Numerics; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public static class Math { /// - /// The value of Pi (π), a mathematical constant approximately equal to 3.14159. + /// The value of Pi (π). /// - public const float PI = 3.1415926535897932f; + public const float Pi = 3.1415926535897932f; /// - /// The value of Tau (τ), a mathematical constant equal to 2π, approximately equal to 6.28319. + /// The value of Tau (τ), mathematical constant equal to 2π. /// - public const float Tau = 2f * PI; + public const float Tau = 2f * Pi; /// - /// The base of the natural logarithm, approximately equal to 2.71828. + /// The base of the natural logarithm. /// public const float E = 2.718281828459045f; /// /// The conversion factor from radians to degrees. /// - public const float RadianToDegree = 180f / PI; + public const float RadianToDegree = 180f / Pi; /// /// The conversion factor from degrees to radians. /// - public const float DegreeToRadian = PI / 180f; + public const float DegreeToRadian = Pi / 180f; /// /// Gets one minus of given . @@ -240,21 +240,33 @@ public static class Math public static T Lerp(T x, T y, T t) where T : IFloatingPoint => x + (y - x) * t; /// - /// Rounds a number to a specified number of fractional digits. + /// Rounds a number to the closest integer. /// /// The number to round. - /// The number of fractional digits in the return value. - /// Specification for how to round if it is midway between two other numbers. - /// The number rounded to fractional digits. - public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode); + /// Specification for how to round if it is midway between two other numbers. + /// The number rounded to the closest integer. + public static float Round(float x, RoundMode roundMode) => RoundToInt(x, roundMode); /// - /// Rounds a number to an integer. + /// Rounds a number to the closest integer. /// /// The number to round. /// Specification for how to round if it's midway between two numbers - /// - public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil) => (int)MathF.Round(x, 0, roundMode == RoundMode.Ceil ? MidpointRounding.ToPositiveInfinity : MidpointRounding.ToNegativeInfinity); + /// The number rounded to the closest integer. + public static int RoundToInt(float x, RoundMode roundMode = RoundMode.Ceil) + { + float remainder = x.Mod(1f); + + if (remainder == .5f) + if (roundMode == RoundMode.Floor) + return (int)x; + else + return (int)(x + .5f); + + if (x < 0f) + return (int)(x - .5f); + return (int)(x + .5f); + } public enum RoundMode { Ceil, Floor }; /// diff --git a/Engine.Core/MathExtensions.cs b/Engine.Core/MathExtensions.cs index e98df7f..d1fd4aa 100644 --- a/Engine.Core/MathExtensions.cs +++ b/Engine.Core/MathExtensions.cs @@ -1,7 +1,7 @@ using System; using System.Numerics; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public static class MathExtensions { @@ -81,7 +81,7 @@ public static class MathExtensions public static T Lerp(this T x, T y, T t) where T : IFloatingPoint => Math.Lerp(x, y, t); /// - public static float Round(this float x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode); + public static float Round(this float x, Math.RoundMode mode) => Math.Round(x, mode); /// public static int RoundToInt(this float x, Math.RoundMode roundMode = Math.RoundMode.Ceil) => Math.RoundToInt(x, roundMode); diff --git a/Engine.Core/Preserver.cs b/Engine.Core/Preserver.cs index a7f330e..1832601 100644 --- a/Engine.Core/Preserver.cs +++ b/Engine.Core/Preserver.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core +namespace Engine.Core { // This is pretty much so the assembly gets loaded automatically because // the builds include the assembly but sometimes doesn't link load it at startup. diff --git a/Engine.Core/Primitives/AABB.cs b/Engine.Core/Primitives/AABB.cs index fad4569..15a2767 100644 --- a/Engine.Core/Primitives/AABB.cs +++ b/Engine.Core/Primitives/AABB.cs @@ -1,6 +1,7 @@ +using System; using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents an Axis-Aligned Bounding Box (AABB) in 2D space. @@ -11,7 +12,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified lower and upper boundaries. /// [System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")] -public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary) +public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary) : IEquatable { /// /// The lower boundary of the . @@ -38,6 +39,9 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary) /// public readonly Vector2D SizeHalf => Size * .5f; + public static bool operator ==(AABB left, AABB right) => left.UpperBoundary == right.UpperBoundary && left.LowerBoundary == right.LowerBoundary; + public static bool operator !=(AABB left, AABB right) => left.UpperBoundary != right.UpperBoundary || left.LowerBoundary != right.LowerBoundary; + /// /// Creates an from a collection of s. /// @@ -63,12 +67,6 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary) return new(lowerBoundary, upperBoundary); } - /// - /// Converts the to its string representation. - /// - /// A string representation of the . - public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})"; - /// /// Checks if two s are approximately equal. /// @@ -78,6 +76,26 @@ public readonly struct AABB(Vector2D lowerBoundary, Vector2D upperBoundary) /// if the s are approximately equal; otherwise, . public static bool ApproximatelyEquals(AABB left, AABB right, float epsilon = float.Epsilon) => left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary, epsilon) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary, epsilon); + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is AABB aabb && this == aabb; + public bool Equals(AABB other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(LowerBoundary, UpperBoundary); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(AABB)}({LowerBoundary}, {UpperBoundary})"; } /// diff --git a/Engine.Core/Primitives/Circle.cs b/Engine.Core/Primitives/Circle.cs index 1b4dccf..2f516ef 100644 --- a/Engine.Core/Primitives/Circle.cs +++ b/Engine.Core/Primitives/Circle.cs @@ -1,6 +1,7 @@ +using System; using System.Diagnostics; -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a 2D circle. @@ -11,7 +12,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified center and radius. /// [DebuggerDisplay("Center: {Center.ToString(),nq}, Radius: {Radius}")] -public readonly struct Circle(Vector2D center, float radius) +public readonly struct Circle(Vector2D center, float radius) : IEquatable { /// /// The center of the circle. @@ -38,6 +39,9 @@ public readonly struct Circle(Vector2D center, float radius) /// public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f); + public static bool operator ==(Circle left, Circle right) => left.Center == right.Center && left.Radius == right.Radius; + public static bool operator !=(Circle left, Circle right) => left.Center != right.Center || left.Radius != right.Radius; + /// /// Sets the center of the . /// @@ -77,6 +81,26 @@ public readonly struct Circle(Vector2D center, float radius) /// if the s are approximately equal; otherwise, . public static bool ApproximatelyEquals(Circle left, Circle right, float epsilon = float.Epsilon) => left.Center.ApproximatelyEquals(right.Center, epsilon) && left.Radius.ApproximatelyEquals(right.Radius, epsilon); + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Circle circle && this == circle; + public bool Equals(Circle other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(Center, Radius); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Circle)}({Center}, {Radius})"; } /// diff --git a/Engine.Core/Primitives/ColorHSV.cs b/Engine.Core/Primitives/ColorHSV.cs index f4404c5..9090694 100644 --- a/Engine.Core/Primitives/ColorHSV.cs +++ b/Engine.Core/Primitives/ColorHSV.cs @@ -1,4 +1,6 @@ -namespace Syntriax.Engine.Core; +using System; + +namespace Engine.Core; /// /// Represents an HSV color. @@ -10,7 +12,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified values. /// [System.Diagnostics.DebuggerDisplay("{ToString(),nq}")] -public readonly struct ColorHSV(float hue, float saturation, float value) +public readonly struct ColorHSV(float hue, float saturation, float value) : IEquatable { /// /// The Hue value of the . @@ -27,49 +29,19 @@ public readonly struct ColorHSV(float hue, float saturation, float value) /// public readonly float Value = value.Clamp(0f, 1f); - public static ColorHSV operator -(ColorHSV color) => new(color.Hue.OneMinus().Clamp(0f, 1f), color.Saturation.OneMinus().Clamp(0f, 1f), color.Value.OneMinus().Clamp(0f, 1f)); - public static ColorHSV operator +(ColorHSV left, ColorHSV right) => new((left.Hue + right.Hue).Clamp(0f, 1f), (left.Saturation + right.Saturation).Clamp(0f, 1f), (left.Value + right.Value).Clamp(0f, 1f)); - public static ColorHSV operator -(ColorHSV left, ColorHSV right) => new((left.Hue - right.Hue).Clamp(0f, 1f), (left.Saturation - right.Saturation).Clamp(0f, 1f), (left.Value - right.Value).Clamp(0f, 1f)); - public static ColorHSV operator *(ColorHSV left, ColorHSV right) => new((left.Hue * right.Hue).Clamp(0f, 1f), (left.Saturation * right.Saturation).Clamp(0f, 1f), (left.Value * right.Value).Clamp(0f, 1f)); - public static ColorHSV operator *(ColorHSV color, float value) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f)); - public static ColorHSV operator *(float value, ColorHSV color) => new((color.Hue * value).Clamp(0f, 1f), (color.Saturation * value).Clamp(0f, 1f), (color.Value * value).Clamp(0f, 1f)); - public static ColorHSV operator /(ColorHSV color, float value) => new((color.Hue / value).Clamp(0f, 1f), (color.Saturation / value).Clamp(0f, 1f), (color.Value / value).Clamp(0f, 1f)); - public static bool operator ==(ColorHSV left, ColorHSV right) => left.Hue.ApproximatelyEquals(right.Hue) && left.Saturation.ApproximatelyEquals(right.Saturation) && left.Value.ApproximatelyEquals(right.Value); - public static bool operator !=(ColorHSV left, ColorHSV right) => !left.Hue.ApproximatelyEquals(right.Hue) || !left.Saturation.ApproximatelyEquals(right.Saturation) || !left.Value.ApproximatelyEquals(right.Value); + public static ColorHSV operator -(ColorHSV color) => new(color.Hue.OneMinus(), color.Saturation.OneMinus(), color.Value.OneMinus()); + public static ColorHSV operator +(ColorHSV left, ColorHSV right) => new(left.Hue + right.Hue, left.Saturation + right.Saturation, left.Value + right.Value); + public static ColorHSV operator -(ColorHSV left, ColorHSV right) => new(left.Hue - right.Hue, left.Saturation - right.Saturation, left.Value - right.Value); + public static ColorHSV operator *(ColorHSV left, ColorHSV right) => new(left.Hue * right.Hue, left.Saturation * right.Saturation, left.Value * right.Value); + public static ColorHSV operator *(ColorHSV color, float value) => new(color.Hue * value, color.Saturation * value, color.Value * value); + public static ColorHSV operator *(float value, ColorHSV color) => new(color.Hue * value, color.Saturation * value, color.Value * value); + public static ColorHSV operator /(ColorHSV color, float value) => new(color.Hue / value, color.Saturation / value, color.Value / value); + public static bool operator ==(ColorHSV left, ColorHSV right) => left.Hue == right.Hue && left.Saturation == right.Saturation && left.Value == right.Value; + public static bool operator !=(ColorHSV left, ColorHSV right) => left.Hue != right.Hue || left.Saturation != right.Saturation || left.Value != right.Value; - public static implicit operator ColorHSV(ColorRGBA rgba) => (ColorRGB)rgba; - public static implicit operator ColorHSV(ColorRGB rgb) - { - float hue; - float saturation; - float value; - - float rd = rgb.R / 255f; - float gd = rgb.G / 255f; - float bd = rgb.B / 255f; - - float max = Math.Max(rd, Math.Max(gd, bd)); - float min = Math.Min(rd, Math.Min(gd, bd)); - float delta = max - min; - - if (delta.ApproximatelyEquals(0)) - hue = 0f; - else if (max.ApproximatelyEquals(rd)) - hue = 60f * ((gd - bd) / delta % 6f); - else if (max.ApproximatelyEquals(gd)) - hue = 60f * (((bd - rd) / delta) + 2f); - else - hue = 60f * (((rd - gd) / delta) + 4f); - - if (hue < 0f) - hue += 360f; - - hue /= 360f; - saturation = max.ApproximatelyEquals(0f) ? 0f : delta / max; - value = max; - - return new(hue, saturation, value); - } + public static implicit operator ColorHSV(ColorHSVA hsva) => new(hsva.Hue, hsva.Saturation, hsva.Value); + public static implicit operator ColorHSV(ColorRGBA rgba) => (ColorHSVA)rgba; + public static implicit operator ColorHSV(ColorRGB rgb) => (ColorHSVA)rgb; /// /// Inverts the given . @@ -110,14 +82,6 @@ public readonly struct ColorHSV(float hue, float saturation, float value) /// The result of dividing the by the scalar value. public static ColorHSV Divide(ColorHSV color, float value) => color / value; - /// - /// Calculates the from one point to another. - /// - /// The starting point. - /// The ending point. - /// The from the starting point to the ending point. - public static ColorHSV FromTo(ColorHSV from, ColorHSV to) => to - from; - /// /// Performs linear interpolation between two s. /// @@ -125,13 +89,14 @@ public readonly struct ColorHSV(float hue, float saturation, float value) /// The ending (t = 1). /// The interpolation parameter. /// The interpolated . - public static ColorHSV Lerp(ColorHSV from, ColorHSV to, float t) => from + FromTo(from, to) * t; + public static ColorHSV Lerp(ColorHSV from, ColorHSV to, float t) + { + float hueDiff = to.Hue - from.Hue; + float saturationDiff = to.Saturation - from.Saturation; + float valueDiff = to.Value - from.Value; - /// - /// Converts the to its string representation. - /// - /// A string representation of the . - public override string ToString() => $"{nameof(ColorHSV)}({Hue}, {Saturation}, {Value})"; + return from + new ColorHSV(hueDiff * t, saturationDiff * t, valueDiff * t); + } /// /// Checks if two s are approximately equal within a specified epsilon range. @@ -148,13 +113,20 @@ public readonly struct ColorHSV(float hue, float saturation, float value) /// /// The object to compare with the current . /// if the specified object is equal to the current ; otherwise, . - public override bool Equals(object? obj) => obj is ColorHSV objVec && Hue.Equals(objVec.Hue) && Saturation.Equals(objVec.Saturation) && Value.Equals(objVec.Value); + public override bool Equals(object? obj) => obj is ColorHSV colorHSV && this == colorHSV; + public bool Equals(ColorHSV other) => this == other; /// /// Generates a hash code for the . /// /// A hash code for the . public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(ColorHSV)}({Hue}, {Saturation}, {Value})"; } /// @@ -174,9 +146,6 @@ public static class ColorHSVExtensions /// public static ColorHSV Divide(this ColorHSV color, float value) => ColorHSV.Divide(color, value); - /// - public static ColorHSV FromTo(this ColorHSV from, ColorHSV to) => ColorHSV.FromTo(from, to); - /// public static ColorHSV Lerp(this ColorHSV from, ColorHSV to, float t) => ColorHSV.Lerp(from, to, t); diff --git a/Engine.Core/Primitives/ColorHSVA.cs b/Engine.Core/Primitives/ColorHSVA.cs new file mode 100644 index 0000000..cb3ffa9 --- /dev/null +++ b/Engine.Core/Primitives/ColorHSVA.cs @@ -0,0 +1,192 @@ +using System; + +namespace Engine.Core; + +/// +/// Represents an HSV color. +/// +/// Hue of the . +/// Saturation of the . +/// Value of the . +/// Alpha of the . +/// +/// Initializes a new instance of the struct with the specified values. +/// +[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")] +public readonly struct ColorHSVA(float hue, float saturation, float value, float alpha = 1) : IEquatable +{ + /// + /// The Hue value of the . + /// + public readonly float Hue = hue.Clamp(0f, 1f); + + /// + /// The Saturation value of the . + /// + public readonly float Saturation = saturation.Clamp(0f, 1f); + + /// + /// The Value value of the . + /// + public readonly float Value = value.Clamp(0f, 1f); + + /// + /// The Alpha value of the . + /// + public readonly float Alpha = alpha; + + public static ColorHSVA operator -(ColorHSVA color) => new(color.Hue.OneMinus(), color.Saturation.OneMinus(), color.Value.OneMinus(), color.Alpha); + public static ColorHSVA operator +(ColorHSVA left, ColorHSVA right) => new(left.Hue + right.Hue, left.Saturation + right.Saturation, left.Value + right.Value, left.Alpha + right.Alpha); + public static ColorHSVA operator -(ColorHSVA left, ColorHSVA right) => new(left.Hue - right.Hue, left.Saturation - right.Saturation, left.Value - right.Value, left.Alpha - right.Alpha); + public static ColorHSVA operator *(ColorHSVA left, ColorHSVA right) => new(left.Hue * right.Hue, left.Saturation * right.Saturation, left.Value * right.Value, left.Alpha * right.Alpha); + public static ColorHSVA operator *(ColorHSVA color, float value) => new(color.Hue * value, color.Saturation * value, color.Value * value, color.Alpha * value); + public static ColorHSVA operator *(float value, ColorHSVA color) => new(color.Hue * value, color.Saturation * value, color.Value * value, color.Alpha * value); + public static ColorHSVA operator /(ColorHSVA color, float value) => new(color.Hue / value, color.Saturation / value, color.Value / value, color.Alpha / value); + public static bool operator ==(ColorHSVA left, ColorHSVA right) => left.Hue == right.Hue && left.Saturation == right.Saturation && left.Value == right.Value; + public static bool operator !=(ColorHSVA left, ColorHSVA right) => left.Hue != right.Hue || left.Saturation != right.Saturation || left.Value != right.Value; + + public static implicit operator ColorHSVA(ColorHSV hsv) => new(hsv.Hue, hsv.Saturation, hsv.Value, 1f); + public static implicit operator ColorHSVA(ColorRGB rgb) => (ColorRGBA)rgb; + public static implicit operator ColorHSVA(ColorRGBA rgba) + { + float hue; + float saturation; + float value; + + float rd = rgba.R / 255f; + float gd = rgba.G / 255f; + float bd = rgba.B / 255f; + + float max = Math.Max(rd, Math.Max(gd, bd)); + float min = Math.Min(rd, Math.Min(gd, bd)); + float delta = max - min; + + if (delta.ApproximatelyEquals(0)) + hue = 0f; + else if (max.ApproximatelyEquals(rd)) + hue = 60f * ((gd - bd) / delta % 6f); + else if (max.ApproximatelyEquals(gd)) + hue = 60f * (((bd - rd) / delta) + 2f); + else + hue = 60f * (((rd - gd) / delta) + 4f); + + if (hue < 0f) + hue += 360f; + + hue /= 360f; + saturation = max.ApproximatelyEquals(0f) ? 0f : delta / max; + value = max; + + return new(hue, saturation, value, rgba.A / 255f); + } + + /// + /// Inverts the given . + /// + /// The . + /// The inverted . + public static ColorHSVA Invert(ColorHSVA color) => -color; + + /// + /// Adds two s. + /// + /// The first . + /// The second . + /// The sum of the two s. + public static ColorHSVA Add(ColorHSVA left, ColorHSVA right) => left + right; + + /// + /// Subtracts one from another. + /// + /// The to subtract from. + /// The to subtract. + /// The result of subtracting the second from the first. + public static ColorHSVA Subtract(ColorHSVA left, ColorHSVA right) => left - right; + + /// + /// Multiplies a by a scalar value. + /// + /// The . + /// The scalar value. + /// The result of multiplying the by the scalar value. + public static ColorHSVA Multiply(ColorHSVA color, float value) => color * value; + + /// + /// Divides a by a scalar value. + /// + /// The . + /// The scalar value. + /// The result of dividing the by the scalar value. + public static ColorHSVA Divide(ColorHSVA color, float value) => color / value; + + /// + /// Performs linear interpolation between two s. + /// + /// The starting (t = 0). + /// The ending (t = 1). + /// The interpolation parameter. + /// The interpolated . + public static ColorHSVA Lerp(ColorHSVA from, ColorHSVA to, float t) + { + float hueDiff = to.Hue - from.Hue; + float saturationDiff = to.Saturation - from.Saturation; + float valueDiff = to.Value - from.Value; + float alphaDiff = to.Alpha - from.Alpha; + + return from + new ColorHSVA(hueDiff * t, saturationDiff * t, valueDiff * t, alphaDiff * t); + } + + /// + /// Checks if two s are approximately equal within a specified epsilon range. + /// + /// The first . + /// The second . + /// The epsilon range. + /// if the s are approximately equal; otherwise, . + public static bool ApproximatelyEquals(ColorHSVA left, ColorHSVA right, float epsilon = float.Epsilon) + => left.Hue.ApproximatelyEquals(right.Hue, epsilon) && left.Saturation.ApproximatelyEquals(right.Saturation, epsilon) && left.Value.ApproximatelyEquals(right.Value, epsilon); + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is ColorHSVA colorHSVA && this == colorHSVA; + public bool Equals(ColorHSVA other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(Hue, Saturation, Value); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(ColorHSVA)}({Hue}, {Saturation}, {Value})"; +} + +/// +/// Provides extension methods for type. +/// +public static class ColorHSVAExtensions +{ + /// + public static ColorHSVA Add(this ColorHSVA color, ColorHSVA value) => ColorHSVA.Add(color, value); + + /// + public static ColorHSVA Subtract(this ColorHSVA color, ColorHSVA value) => ColorHSVA.Subtract(color, value); + + /// + public static ColorHSVA Multiply(this ColorHSVA color, float value) => ColorHSVA.Multiply(color, value); + + /// + public static ColorHSVA Divide(this ColorHSVA color, float value) => ColorHSVA.Divide(color, value); + + /// + public static ColorHSVA Lerp(this ColorHSVA from, ColorHSVA to, float t) => ColorHSVA.Lerp(from, to, t); + + /// + public static bool ApproximatelyEquals(this ColorHSVA left, ColorHSVA right, float epsilon = float.Epsilon) => ColorHSVA.ApproximatelyEquals(left, right, epsilon); +} diff --git a/Engine.Core/Primitives/ColorRGB.cs b/Engine.Core/Primitives/ColorRGB.cs index d37bfb6..1fada67 100644 --- a/Engine.Core/Primitives/ColorRGB.cs +++ b/Engine.Core/Primitives/ColorRGB.cs @@ -1,4 +1,6 @@ -namespace Syntriax.Engine.Core; +using System; + +namespace Engine.Core; /// /// Represents an RGB color. @@ -10,7 +12,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified values. /// [System.Diagnostics.DebuggerDisplay("{ToString(),nq}")] -public readonly struct ColorRGB(byte r, byte g, byte b) +public readonly struct ColorRGB(byte r, byte g, byte b) : IEquatable { /// /// The Red value of the . @@ -38,30 +40,8 @@ public readonly struct ColorRGB(byte r, byte g, byte b) public static bool operator !=(ColorRGB left, ColorRGB right) => left.R != right.R || left.G != right.G || left.B != right.B; public static implicit operator ColorRGB(ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B); - public static implicit operator ColorRGB(ColorHSV hsv) - { - float hue = hsv.Hue * 360f; - float chroma = hsv.Value * hsv.Saturation; - float x = chroma * (1f - Math.Abs(hue / 60f % 2f - 1f)); - float m = hsv.Value - chroma; - - float r1 = 0f; - float g1 = 0f; - float b1 = 0f; - - if (hue < 60) { r1 = chroma; g1 = x; b1 = 0; } - else if (hue < 120) { r1 = x; g1 = chroma; b1 = 0; } - else if (hue < 180) { r1 = 0; g1 = chroma; b1 = x; } - else if (hue < 240) { r1 = 0; g1 = x; b1 = chroma; } - else if (hue < 300) { r1 = x; g1 = 0; b1 = chroma; } - else if (hue <= 360) { r1 = chroma; g1 = 0; b1 = x; } - - byte r = (byte)Math.RoundToInt((r1 + m) * 255); - byte g = (byte)Math.RoundToInt((g1 + m) * 255); - byte b = (byte)Math.RoundToInt((b1 + m) * 255); - - return new(r, g, b); - } + public static implicit operator ColorRGB(ColorHSVA hsva) => (ColorRGBA)hsva; + public static implicit operator ColorRGB(ColorHSV hsv) => (ColorRGBA)hsv; /// /// Inverts the given . @@ -102,14 +82,6 @@ public readonly struct ColorRGB(byte r, byte g, byte b) /// The result of dividing the by the scalar value. public static ColorRGB Divide(ColorRGB color, float value) => color / value; - /// - /// Calculates the from one point to another. - /// - /// The starting point. - /// The ending point. - /// The from the starting point to the ending point. - public static ColorRGB FromTo(ColorRGB from, ColorRGB to) => to - from; - /// /// Performs linear interpolation between two s. /// @@ -117,26 +89,34 @@ public readonly struct ColorRGB(byte r, byte g, byte b) /// The ending (t = 1). /// The interpolation parameter. /// The interpolated . - public static ColorRGB Lerp(ColorRGB from, ColorRGB to, float t) => from + FromTo(from, to) * t; + public static ColorRGB Lerp(ColorRGB from, ColorRGB to, float t) + { + int redDiff = to.R - from.R; + int greenDiff = to.G - from.G; + int blueDiff = to.B - from.B; - /// - /// Converts the to its string representation. - /// - /// A string representation of the . - public override string ToString() => $"{nameof(ColorRGB)}({R}, {G}, {B})"; + return from + new ColorRGB((byte)(redDiff * t), (byte)(greenDiff * t), (byte)(blueDiff * t)); + } /// /// Determines whether the specified object is equal to the current . /// /// The object to compare with the current . /// if the specified object is equal to the current ; otherwise, . - public override bool Equals(object? obj) => obj is ColorRGB objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B); + public override bool Equals(object? obj) => obj is ColorRGB colorRGB && this == colorRGB; + public bool Equals(ColorRGB other) => this == other; /// /// Generates a hash code for the . /// /// A hash code for the . public override int GetHashCode() => System.HashCode.Combine(R, G, B); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(ColorRGB)}({R}, {G}, {B})"; } /// @@ -156,9 +136,6 @@ public static class ColorRGBExtensions /// public static ColorRGB Divide(this ColorRGB color, float value) => ColorRGB.Divide(color, value); - /// - public static ColorRGB FromTo(this ColorRGB from, ColorRGB to) => ColorRGB.FromTo(from, to); - /// public static ColorRGB Lerp(this ColorRGB from, ColorRGB to, float t) => ColorRGB.Lerp(from, to, t); } diff --git a/Engine.Core/Primitives/ColorRGBA.cs b/Engine.Core/Primitives/ColorRGBA.cs index 82d997e..8163840 100644 --- a/Engine.Core/Primitives/ColorRGBA.cs +++ b/Engine.Core/Primitives/ColorRGBA.cs @@ -1,4 +1,6 @@ -namespace Syntriax.Engine.Core; +using System; + +namespace Engine.Core; /// /// Represents an RGBA color. @@ -11,7 +13,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified values. /// [System.Diagnostics.DebuggerDisplay("{ToString(),nq}")] -public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255) +public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255) : IEquatable { /// /// The Red value of the . @@ -44,7 +46,31 @@ public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255) public static bool operator !=(ColorRGBA left, ColorRGBA right) => left.R != right.R || left.G != right.G || left.B != right.B || left.A != right.A; public static implicit operator ColorRGBA(ColorRGB rgb) => new(rgb.R, rgb.G, rgb.B, 255); - public static implicit operator ColorRGBA(ColorHSV hsv) => (ColorRGB)hsv; + public static implicit operator ColorRGBA(ColorHSV hsv) => (ColorHSVA)hsv; + public static implicit operator ColorRGBA(ColorHSVA hsva) + { + float hue = hsva.Hue * 360f; + float chroma = hsva.Value * hsva.Saturation; + float x = chroma * (1f - Math.Abs(hue / 60f % 2f - 1f)); + float m = hsva.Value - chroma; + + float r1 = 0f; + float g1 = 0f; + float b1 = 0f; + + if (hue < 60) { r1 = chroma; g1 = x; b1 = 0; } + else if (hue < 120) { r1 = x; g1 = chroma; b1 = 0; } + else if (hue < 180) { r1 = 0; g1 = chroma; b1 = x; } + else if (hue < 240) { r1 = 0; g1 = x; b1 = chroma; } + else if (hue < 300) { r1 = x; g1 = 0; b1 = chroma; } + else if (hue <= 360) { r1 = chroma; g1 = 0; b1 = x; } + + byte r = (byte)Math.RoundToInt((r1 + m) * 255); + byte g = (byte)Math.RoundToInt((g1 + m) * 255); + byte b = (byte)Math.RoundToInt((b1 + m) * 255); + + return new(r, g, b, (byte)(hsva.Alpha * 255)); + } /// /// Inverts the given . @@ -85,14 +111,6 @@ public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255) /// The result of dividing the by the scalar value. public static ColorRGBA Divide(ColorRGBA color, float value) => color / value; - /// - /// Calculates the from one point to another. - /// - /// The starting point. - /// The ending point. - /// The from the starting point to the ending point. - public static ColorRGBA FromTo(ColorRGBA from, ColorRGBA to) => to - from; - /// /// Performs linear interpolation between two s. /// @@ -100,26 +118,35 @@ public readonly struct ColorRGBA(byte r, byte g, byte b, byte a = 255) /// The ending (t = 1). /// The interpolation parameter. /// The interpolated . - public static ColorRGBA Lerp(ColorRGBA from, ColorRGBA to, float t) => from + FromTo(from, to) * t; + public static ColorRGBA Lerp(ColorRGBA from, ColorRGBA to, float t) + { + int redDiff = to.R - from.R; + int greenDiff = to.G - from.G; + int blueDiff = to.B - from.B; + int alphaDiff = to.A - from.A; - /// - /// Converts the to its string representation. - /// - /// A string representation of the . - public override string ToString() => $"{nameof(ColorRGBA)}({R}, {G}, {B}, {A})"; + return from + new ColorRGBA((byte)(redDiff * t), (byte)(greenDiff * t), (byte)(blueDiff * t), (byte)(alphaDiff * t)); + } /// /// Determines whether the specified object is equal to the current . /// /// The object to compare with the current . /// if the specified object is equal to the current ; otherwise, . - public override bool Equals(object? obj) => obj is ColorRGBA objVec && R.Equals(objVec.R) && G.Equals(objVec.G) && B.Equals(objVec.B) && A.Equals(objVec.A); + public override bool Equals(object? obj) => obj is ColorRGBA colorRGBA && this == colorRGBA; + public bool Equals(ColorRGBA other) => this == other; /// /// Generates a hash code for the . /// /// A hash code for the . public override int GetHashCode() => System.HashCode.Combine(R, G, B, A); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(ColorRGBA)}({R}, {G}, {B}, {A})"; } /// @@ -139,9 +166,6 @@ public static class ColorRGBAExtensions /// public static ColorRGBA Divide(this ColorRGBA color, float value) => ColorRGBA.Divide(color, value); - /// - public static ColorRGBA FromTo(this ColorRGBA from, ColorRGBA to) => ColorRGBA.FromTo(from, to); - /// public static ColorRGBA Lerp(this ColorRGBA from, ColorRGBA to, float t) => ColorRGBA.Lerp(from, to, t); } diff --git a/Engine.Core/Primitives/Line2D.cs b/Engine.Core/Primitives/Line2D.cs index e5c2022..753027b 100644 --- a/Engine.Core/Primitives/Line2D.cs +++ b/Engine.Core/Primitives/Line2D.cs @@ -1,6 +1,7 @@ +using System; using System.Diagnostics.CodeAnalysis; -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a 2D line segment defined by two endpoints. @@ -11,7 +12,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified endpoints. /// [System.Diagnostics.DebuggerDisplay("From: {From.ToString(),nq}, To: {To.ToString(),nq}, Direction: {Direction.ToString(),nq}, Length: {Length}")] -public readonly struct Line2D(Vector2D from, Vector2D to) +public readonly struct Line2D(Vector2D from, Vector2D to) : IEquatable { /// /// The starting point of the segment. @@ -43,6 +44,9 @@ public readonly struct Line2D(Vector2D from, Vector2D to) /// public readonly float LengthSquared => From.FromTo(To).LengthSquared(); + public static bool operator ==(Line2D left, Line2D right) => left.From == right.From && left.To == right.To; + public static bool operator !=(Line2D left, Line2D right) => left.From != right.From || left.To != right.To; + /// /// The equation of the defined by this segment. /// @@ -186,6 +190,26 @@ public readonly struct Line2D(Vector2D from, Vector2D to) /// if the s are approximately equal; otherwise, . public static bool ApproximatelyEquals(Line2D left, Line2D right, float epsilon = float.Epsilon) => left.From.ApproximatelyEquals(right.From, epsilon) && left.To.ApproximatelyEquals(right.To, epsilon); + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Line2D line2D && this == line2D; + public bool Equals(Line2D other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(From, To); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Line2D)}({From}, {To})"; } /// diff --git a/Engine.Core/Primitives/Line2DEquation.cs b/Engine.Core/Primitives/Line2DEquation.cs index d76c20d..fc06e2a 100644 --- a/Engine.Core/Primitives/Line2DEquation.cs +++ b/Engine.Core/Primitives/Line2DEquation.cs @@ -1,47 +1,72 @@ -namespace Syntriax.Engine.Core; +using System; + +namespace Engine.Core; /// -/// Represents a line equation in the form y = mx + b. +/// Represents a in the form y = mx + b. /// /// The slope of the line. -/// The y-intercept of the line. +/// The Y intercept of the line. /// -/// Initializes a new instance of the struct with the specified slope and y-intercept. +/// Initializes a new instance of the struct with the specified slope and Y intercept. /// [System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")] -public readonly struct Line2DEquation(float slope, float offsetY) +public readonly struct Line2DEquation(float slope, float offsetY) : IEquatable { /// - /// The slope of the line equation. + /// The slope of the . /// public readonly float Slope = slope; /// - /// The y-intercept of the line equation. + /// The Y intercept of the . /// public readonly float OffsetY = offsetY; + public static bool operator ==(Line2DEquation left, Line2DEquation right) => left.Slope == right.Slope && left.OffsetY == right.OffsetY; + public static bool operator !=(Line2DEquation left, Line2DEquation right) => left.Slope != right.Slope || left.OffsetY != right.OffsetY; + /// - /// Resolves the y-coordinate for a given x-coordinate using the line equation. + /// Resolves the Y coordinate for a given X coordinate using the . /// - /// The line equation to resolve. - /// The x-coordinate for which to resolve the y-coordinate. - /// The y-coordinate resolved using the line equation. + /// The to resolve. + /// The X coordinate for which to resolve the Y coordinate. + /// The Y coordinate resolved using the . public static float Resolve(Line2DEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b /// - /// Checks if two line equations are approximately equal. + /// Checks if two are approximately equal. /// - /// The first line equation to compare. - /// The second line equation to compare. + /// The first to compare. + /// The second to compare. /// The epsilon range. - /// True if the line equations are approximately equal; otherwise, false. + /// True if the are approximately equal; otherwise, false. public static bool ApproximatelyEquals(Line2DEquation left, Line2DEquation right, float epsilon = float.Epsilon) => left.Slope.ApproximatelyEquals(right.Slope, epsilon) && left.OffsetY.ApproximatelyEquals(right.OffsetY, epsilon); + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Line2DEquation lineEquation && this == lineEquation; + public bool Equals(Line2DEquation other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(Slope, OffsetY); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Line2DEquation)}({Slope}, {OffsetY})"; } /// -/// Provides extension methods for the LineEquation struct. +/// Provides extension methods for the struct. /// public static class Line2DEquationExtensions { diff --git a/Engine.Core/Primitives/Projection1D.cs b/Engine.Core/Primitives/Projection1D.cs index 8a78413..e57eb33 100644 --- a/Engine.Core/Primitives/Projection1D.cs +++ b/Engine.Core/Primitives/Projection1D.cs @@ -1,4 +1,6 @@ -namespace Syntriax.Engine.Core; +using System; + +namespace Engine.Core; /// /// Represents a range of values along a single axis. @@ -9,7 +11,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified minimum and maximum values. /// [System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")] -public readonly struct Projection1D(float min, float max) +public readonly struct Projection1D(float min, float max) : IEquatable { /// /// Gets the minimum value of the projection. @@ -21,6 +23,9 @@ public readonly struct Projection1D(float min, float max) /// public readonly float Max = max; + public static bool operator ==(Projection1D left, Projection1D right) => left.Min == right.Min && left.Max == right.Max; + public static bool operator !=(Projection1D left, Projection1D right) => left.Min != right.Min || left.Max != right.Max; + /// /// Checks if two projections overlap. /// @@ -70,6 +75,36 @@ public readonly struct Projection1D(float min, float max) depth = 0f; return false; } + + /// + /// Checks if two s are approximately equal within a specified epsilon range. + /// + /// The first . + /// The second . + /// The epsilon range. + /// if the s are approximately equal; otherwise, . + public static bool ApproximatelyEquals(Projection1D left, Projection1D right, float epsilon = float.Epsilon) + => left.Min.ApproximatelyEquals(right.Min, epsilon) && left.Max.ApproximatelyEquals(right.Max, epsilon); + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Projection1D projection1D && this == projection1D; + public bool Equals(Projection1D other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(Min, Max); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Projection1D)}({Min}, {Max})"; } /// @@ -82,4 +117,7 @@ public static class Projection1DExtensions /// public static bool Overlaps(this Projection1D left, Projection1D right, out float depth) => Projection1D.Overlaps(left, right, out depth); + + /// + public static bool ApproximatelyEquals(this Projection1D left, Projection1D right, float epsilon = float.Epsilon) => Projection1D.ApproximatelyEquals(left, right, epsilon); } diff --git a/Engine.Core/Primitives/Quaternion.cs b/Engine.Core/Primitives/Quaternion.cs index 75ab4f7..0a4b957 100644 --- a/Engine.Core/Primitives/Quaternion.cs +++ b/Engine.Core/Primitives/Quaternion.cs @@ -1,4 +1,6 @@ -namespace Syntriax.Engine.Core; +using System; + +namespace Engine.Core; /// /// Represents a 3D space rotation. @@ -11,7 +13,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified positions. /// [System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")] -public readonly struct Quaternion(float x, float y, float z, float w) +public readonly struct Quaternion(float x, float y, float z, float w) : IEquatable { /// /// The X(i) imaginary of the . @@ -282,24 +284,25 @@ public readonly struct Quaternion(float x, float y, float z, float w) public static bool ApproximatelyEquals(Quaternion left, Quaternion right, float epsilon = float.Epsilon) => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon) && left.W.ApproximatelyEquals(right.W, epsilon); - /// - /// Converts the to its string representation. - /// - /// A string representation of the . - public override string ToString() => $"{nameof(Quaternion)}({W}, {X}, {Y}, {Z})"; - /// /// Determines whether the specified object is equal to the current . /// /// The object to compare with the current . /// if the specified object is equal to the current ; otherwise, . - public override bool Equals(object? obj) => obj is Quaternion objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y) && Z.Equals(objVec.Z) && W.Equals(objVec.W); + public override bool Equals(object? obj) => obj is Quaternion quaternion && this == quaternion; + public bool Equals(Quaternion other) => this == other; /// /// Generates a hash code for the . /// /// A hash code for the . - public override int GetHashCode() => System.HashCode.Combine(X, Y, Z); + public override int GetHashCode() => System.HashCode.Combine(W, X, Y, Z); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Quaternion)}({W}, {X}, {Y}, {Z})"; } /// diff --git a/Engine.Core/Primitives/Ray2D.cs b/Engine.Core/Primitives/Ray2D.cs index a369d39..29884b7 100644 --- a/Engine.Core/Primitives/Ray2D.cs +++ b/Engine.Core/Primitives/Ray2D.cs @@ -1,6 +1,13 @@ -namespace Syntriax.Engine.Core; +using System; -public readonly struct Ray2D(Vector2D Origin, Vector2D Direction) +namespace Engine.Core; + +/// +/// Represents an infinite ray in 2D space. +/// +/// The in 2D space where the ray starts from. +/// Normalized indicating the ray's is direction. +public readonly struct Ray2D(Vector2D Origin, Vector2D Direction) : IEquatable { /// /// The starting point of the . @@ -17,6 +24,8 @@ public readonly struct Ray2D(Vector2D Origin, Vector2D Direction) /// public readonly Ray2D Reversed => new(Origin, -Direction); + public static bool operator ==(Ray2D left, Ray2D right) => left.Origin == right.Origin && left.Direction == right.Direction; + public static bool operator !=(Ray2D left, Ray2D right) => left.Origin != right.Origin || left.Direction != right.Direction; public static implicit operator Ray2D(Line2D line) => new(line.From, line.From.FromTo(line.To).Normalized); /// @@ -48,6 +57,36 @@ public readonly struct Ray2D(Vector2D Origin, Vector2D Direction) return ray.Origin + ray.Direction * dot; } + + /// + /// Checks if two s are approximately equal within a specified epsilon range. + /// + /// The first . + /// The second . + /// The epsilon range. + /// if the s are approximately equal; otherwise, . + public static bool ApproximatelyEquals(Ray2D left, Ray2D right, float epsilon = float.Epsilon) + => left.Origin.ApproximatelyEquals(right.Origin, epsilon) && left.Direction.ApproximatelyEquals(right.Direction, epsilon); + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Ray2D ray2D && this == ray2D; + public bool Equals(Ray2D other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(Origin, Direction); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Ray2D)}({Origin}, {Direction})"; } /// @@ -63,4 +102,7 @@ public static class Ray2DExtensions /// + public static bool ApproximatelyEquals(this Ray2D left, Ray2D right, float epsilon = float.Epsilon) => Ray2D.ApproximatelyEquals(left, right, epsilon); } diff --git a/Engine.Core/Primitives/Shape2D.cs b/Engine.Core/Primitives/Shape2D.cs index 06122c0..1bd843f 100644 --- a/Engine.Core/Primitives/Shape2D.cs +++ b/Engine.Core/Primitives/Shape2D.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a shape defined by a collection of vertices. @@ -122,7 +122,7 @@ public class Shape2D(List vertices) : IEnumerable triangles.Clear(); for (int i = 2; i < shape.Vertices.Count; i++) - triangles.Add(new Triangle(shape[0], shape[i - 1], shape[i])); + triangles.Add(new Triangle(shape[0], shape[i], shape[i - 1])); } /// @@ -251,6 +251,34 @@ public class Shape2D(List vertices) : IEnumerable return true; } + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Shape2D shape2D && _vertices.Equals(shape2D._vertices); + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(Vertices); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() + { + System.Text.StringBuilder stringBuilder = new(Vertices[0].ToString()); + for (int i = 1; i < Vertices.Count; i++) + { + stringBuilder.Append(", "); + stringBuilder.Append(Vertices[i].ToString()); + } + return $"{nameof(Shape2D)}({stringBuilder})"; + } + /// public IEnumerator GetEnumerator() => Vertices.GetEnumerator(); @@ -270,7 +298,7 @@ public static class Shape2DExtensions public static Triangle ToSuperTriangle(this Shape2D shape) => Shape2D.GetSuperTriangle(shape); /// - public static void ToTrianglesConvex(this Shape2D shape, IList lines) => Shape2D.TriangulateConvex(shape, lines); + public static void ToTrianglesConvex(this Shape2D shape, IList triangles) => Shape2D.TriangulateConvex(shape, triangles); /// public static List ToTrianglesConvex(this Shape2D shape) => Shape2D.TriangulateConvex(shape); diff --git a/Engine.Core/Primitives/Triangle.cs b/Engine.Core/Primitives/Triangle.cs index aead0e3..96477cb 100644 --- a/Engine.Core/Primitives/Triangle.cs +++ b/Engine.Core/Primitives/Triangle.cs @@ -1,12 +1,17 @@ -namespace Syntriax.Engine.Core; +using System; + +namespace Engine.Core; [System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")] -public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C) +public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C) : IEquatable { public readonly Vector2D A { get; init; } = A; public readonly Vector2D B { get; init; } = B; public readonly Vector2D C { get; init; } = C; + public static bool operator ==(Triangle left, Triangle right) => left.A == right.A && left.B == right.B && left.C == right.C; + public static bool operator !=(Triangle left, Triangle right) => left.A != right.A || left.B != right.B || left.C != right.C; + public readonly float Area => .5f * Math.Abs( A.X * (B.Y - C.Y) + @@ -44,6 +49,26 @@ public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C) /// true if the s are approximately equal; otherwise, false. public static bool ApproximatelyEquals(Triangle left, Triangle right, float epsilon = float.Epsilon) => left.A.ApproximatelyEquals(right.A, epsilon) && left.B.ApproximatelyEquals(right.B, epsilon) && left.C.ApproximatelyEquals(right.C, epsilon); + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Triangle triangle && this == triangle; + public bool Equals(Triangle other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(A, B, C); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Triangle)}({A}, {B}, {C})"; } public static class TriangleExtensions diff --git a/Engine.Core/Primitives/Vector2D.cs b/Engine.Core/Primitives/Vector2D.cs index 0d69f05..6009875 100644 --- a/Engine.Core/Primitives/Vector2D.cs +++ b/Engine.Core/Primitives/Vector2D.cs @@ -1,4 +1,6 @@ -namespace Syntriax.Engine.Core; +using System; + +namespace Engine.Core; /// /// Represents a two-dimensional vector. @@ -9,7 +11,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified positions. /// [System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")] -public readonly struct Vector2D(float x, float y) +public readonly struct Vector2D(float x, float y) : IEquatable { /// /// The X coordinate of the . @@ -82,6 +84,7 @@ public readonly struct Vector2D(float x, float y) public static implicit operator System.Numerics.Vector2(Vector2D vector) => new(vector.X, vector.Y); public static implicit operator Vector2D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y); + public static implicit operator Vector2D(Vector2DInt vector) => new(vector.X, vector.Y); public static implicit operator Vector2D(Vector3D vector) => new(vector.X, vector.Y); public static implicit operator Vector2D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y); @@ -302,24 +305,25 @@ public readonly struct Vector2D(float x, float y) public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon) => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon); - /// - /// Converts the to its string representation. - /// - /// A string representation of the . - public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})"; - /// /// Determines whether the specified object is equal to the current . /// /// The object to compare with the current . /// if the specified object is equal to the current ; otherwise, . - public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y); + public override bool Equals(object? obj) => obj is Vector2D vector2D && this == vector2D; + public bool Equals(Vector2D other) => this == other; /// /// Generates a hash code for the . /// /// A hash code for the . public override int GetHashCode() => System.HashCode.Combine(X, Y); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})"; } /// diff --git a/Engine.Core/Primitives/Vector2DInt.cs b/Engine.Core/Primitives/Vector2DInt.cs new file mode 100644 index 0000000..2968723 --- /dev/null +++ b/Engine.Core/Primitives/Vector2DInt.cs @@ -0,0 +1,306 @@ +using System; + +namespace Engine.Core; + +/// +/// Represents a two-dimensional integer vector. +/// +/// X position of the . +/// Y position of the . +/// +/// Initializes a new instance of the struct with the specified positions. +/// +[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")] +public readonly struct Vector2DInt(int x, int y) : IEquatable +{ + /// + /// The X coordinate of the . + /// + public readonly int X = x; + + /// + /// The Y coordinate of the . + /// + public readonly int Y = y; + + /// + /// The magnitude (length) of the . + /// + public float Magnitude => Length(this); + + /// + /// The squared magnitude (length) of the . + /// + public float MagnitudeSquared => LengthSquared(this); + + /// + /// Gets a with the direction reversed. + /// + public readonly Vector2DInt Reversed => -this; + + /// + /// Represents the unit pointing upwards. + /// + public readonly static Vector2DInt Up = new(0, 1); + + /// + /// Represents the unit pointing downwards. + /// + public readonly static Vector2DInt Down = new(0, -1); + + /// + /// Represents the unit pointing leftwards. + /// + public readonly static Vector2DInt Left = new(-1, 0); + + /// + /// Represents the unit pointing rightwards. + /// + public readonly static Vector2DInt Right = new(1, 0); + + /// + /// Represents the zero . + /// + public readonly static Vector2DInt Zero = new(0, 0); + + /// + /// Represents the with both components equal to 1. + /// + public readonly static Vector2DInt One = new(1, 1); + + public static Vector2DInt operator -(Vector2DInt vector) => new(0 - vector.X, 0 - vector.Y); + public static Vector2DInt operator +(Vector2DInt left, Vector2DInt right) => new(left.X + right.X, left.Y + right.Y); + public static Vector2DInt operator -(Vector2DInt left, Vector2DInt right) => new(left.X - right.X, left.Y - right.Y); + public static Vector2DInt operator *(Vector2DInt vector, int value) => new(vector.X * value, vector.Y * value); + public static Vector2DInt operator *(int value, Vector2DInt vector) => new(vector.X * value, vector.Y * value); + public static Vector2DInt operator /(Vector2DInt vector, int value) => new(vector.X / value, vector.Y / value); + public static bool operator ==(Vector2DInt left, Vector2DInt right) => left.X == right.X && left.Y == right.Y; + public static bool operator !=(Vector2DInt left, Vector2DInt right) => left.X != right.X || left.Y != right.Y; + + public static implicit operator Vector2DInt(Vector2D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt()); + public static implicit operator Vector2DInt(Vector3DInt vector) => new(vector.X, vector.Y); + + /// + /// Calculates the length of the . + /// + /// The . + /// The length of the . + public static float Length(Vector2DInt vector) => Engine.Core.Math.Sqrt(LengthSquared(vector)); + + /// + /// Calculates the squared length of the . + /// + /// The . + /// The squared length of the . + public static float LengthSquared(Vector2DInt vector) => vector.X * vector.X + vector.Y * vector.Y; + + /// + /// Calculates the distance between two s. + /// + /// The start . + /// The end . + /// The distance between the two s. + public static float Distance(Vector2DInt from, Vector2DInt to) => Length(FromTo(from, to)); + + /// + /// Inverts the direction of the . + /// + /// The . + /// The inverted . + public static Vector2DInt Invert(Vector2DInt vector) => -vector; + + /// + /// Adds two s. + /// + /// The first . + /// The second . + /// The sum of the two s. + public static Vector2DInt Add(Vector2DInt left, Vector2DInt right) => left + right; + + /// + /// Subtracts one from another. + /// + /// The to subtract from. + /// The to subtract. + /// The result of subtracting the second from the first. + public static Vector2DInt Subtract(Vector2DInt left, Vector2DInt right) => left - right; + + /// + /// Multiplies a by a scalar value. + /// + /// The . + /// The scalar value. + /// The result of multiplying the by the scalar value. + public static Vector2DInt Multiply(Vector2DInt vector, int value) => vector * value; + + /// + /// Divides a by a scalar value. + /// + /// The . + /// The scalar value. + /// The result of dividing the by the scalar value. + public static Vector2DInt Divide(Vector2DInt vector, int value) => vector / value; + + /// + /// Calculates the absolute value of each component of the vector. + /// + /// The . + /// The with each component's absolute value. + public static Vector2DInt Abs(Vector2DInt vector) => new(Engine.Core.Math.Abs(vector.X), Engine.Core.Math.Abs(vector.Y)); + + /// + /// Calculates the from one point to another. + /// + /// The starting point. + /// The ending point. + /// The from the starting point to the ending point. + public static Vector2DInt FromTo(Vector2DInt from, Vector2DInt to) => to - from; + + /// + /// Scales a by another component-wise. + /// + /// The to scale. + /// The containing the scaling factors for each component. + /// The scaled . + public static Vector2DInt Scale(Vector2DInt vector, Vector2DInt scale) => new(vector.X * scale.X, vector.Y * scale.Y); + + /// + /// Calculates a perpendicular to the given . + /// + /// The input . + /// A perpendicular to the input . + public static Vector2DInt Perpendicular(Vector2DInt vector) => new(-vector.Y, vector.X); + + /// + /// Returns the component-wise minimum of two s. + /// + /// The first . + /// The second . + /// The containing the minimum components from both input s. + public static Vector2DInt Min(Vector2DInt left, Vector2DInt right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y); + + /// + /// Returns the component-wise maximum of two s. + /// + /// The first . + /// The second . + /// The containing the maximum components from both input s. + public static Vector2DInt Max(Vector2DInt left, Vector2DInt right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y); + + /// + /// Clamps each component of a between the corresponding component of two other s. + /// + /// The to clamp. + /// The representing the minimum values for each component. + /// The representing the maximum values for each component. + /// A with each component clamped between the corresponding components of the min and max s. + public static Vector2DInt Clamp(Vector2DInt vector, Vector2DInt min, Vector2DInt max) => new(Engine.Core.Math.Clamp(vector.X, min.X, max.X), Engine.Core.Math.Clamp(vector.Y, min.Y, max.Y)); + + /// + /// Performs linear interpolation between two s. + /// + /// The starting (t = 0). + /// The ending (t = 1). + /// The interpolation parameter. + /// The interpolated . + public static Vector2DInt Lerp(Vector2DInt from, Vector2DInt to, int t) => from + FromTo(from, to) * t; + + /// + /// Calculates the cross product of two s. + /// + /// The first . + /// The second . + /// The cross product of the two s. + public static int Cross(Vector2DInt left, Vector2DInt right) => left.X * right.Y - left.Y * right.X; + + /// + /// Calculates the dot product of two s. + /// + /// The first . + /// The second . + /// The dot product of the two s. + public static int Dot(Vector2DInt left, Vector2DInt right) => left.X * right.X + left.Y * right.Y; + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Vector2DInt vector2DInt && this == vector2DInt; + public bool Equals(Vector2DInt other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(X, Y); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Vector2DInt)}({X}, {Y})"; +} + +/// +/// Provides extension methods for type. +/// +public static class Vector2DIntExtensions +{ + /// + public static float Length(this Vector2DInt vector) => Vector2DInt.Length(vector); + + /// + public static float Distance(this Vector2DInt from, Vector2DInt to) => Vector2DInt.Distance(from, to); + + /// + public static Vector2DInt Add(this Vector2DInt vector, Vector2DInt vectorToAdd) => Vector2DInt.Add(vector, vectorToAdd); + + /// + public static Vector2DInt Subtract(this Vector2DInt vector, Vector2DInt vectorToSubtract) => Vector2DInt.Subtract(vector, vectorToSubtract); + + /// + public static Vector2DInt Multiply(this Vector2DInt vector, int value) => Vector2DInt.Multiply(vector, value); + + /// + public static Vector2DInt Divide(this Vector2DInt vector, int value) => Vector2DInt.Divide(vector, value); + + /// + public static Vector2DInt Abs(this Vector2DInt vector) => Vector2DInt.Abs(vector); + + /// + public static Vector2DInt FromTo(this Vector2DInt from, Vector2DInt to) => Vector2DInt.FromTo(from, to); + + /// + public static Vector2DInt Scale(this Vector2DInt vector, Vector2DInt scale) => Vector2DInt.Scale(vector, scale); + + /// + public static Vector2DInt Perpendicular(this Vector2DInt vector) => Vector2DInt.Perpendicular(vector); + + /// + public static Vector2DInt Min(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Min(left, right); + + /// + public static Vector2DInt Max(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Max(left, right); + + /// + public static Vector2DInt Clamp(this Vector2DInt vector, Vector2DInt min, Vector2DInt max) => Vector2DInt.Clamp(vector, min, max); + + /// + public static Vector2DInt Lerp(this Vector2DInt from, Vector2DInt to, int t) => Vector2DInt.Lerp(from, to, t); + + /// + public static int Cross(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Cross(left, right); + + /// + public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right); + + /// + public static int Dot(this Vector2DInt left, Vector2DInt right) => Vector2DInt.Dot(left, right); +} diff --git a/Engine.Core/Primitives/Vector3D.cs b/Engine.Core/Primitives/Vector3D.cs index 420abe4..71e6152 100644 --- a/Engine.Core/Primitives/Vector3D.cs +++ b/Engine.Core/Primitives/Vector3D.cs @@ -1,4 +1,6 @@ -namespace Syntriax.Engine.Core; +using System; + +namespace Engine.Core; /// /// Represents a three-dimensional vector. @@ -10,7 +12,7 @@ namespace Syntriax.Engine.Core; /// Initializes a new instance of the struct with the specified positions. /// [System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")] -public readonly struct Vector3D(float x, float y, float z) +public readonly struct Vector3D(float x, float y, float z) : IEquatable { /// /// The X coordinate of the . @@ -92,6 +94,7 @@ public readonly struct Vector3D(float x, float y, float z) public static implicit operator System.Numerics.Vector3(Vector3D vector) => new(vector.X, vector.Y, vector.Z); public static implicit operator Vector3D(System.Numerics.Vector3 vector) => new(vector.X, vector.Y, vector.Z); + public static implicit operator Vector3D(Vector3DInt vector) => new(vector.X, vector.Y, vector.Z); public static implicit operator Vector3D(Vector2D vector) => new(vector.X, vector.Y, 0f); public static implicit operator Vector3D(System.Numerics.Vector2 vector) => new(vector.X, vector.Y, 0f); @@ -271,24 +274,25 @@ public readonly struct Vector3D(float x, float y, float z) public static bool ApproximatelyEquals(Vector3D left, Vector3D right, float epsilon = float.Epsilon) => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon) && left.Z.ApproximatelyEquals(right.Z, epsilon); - /// - /// Converts the to its string representation. - /// - /// A string representation of the . - public override string ToString() => $"{nameof(Vector3D)}({X}, {Y}, {Z})"; - /// /// Determines whether the specified object is equal to the current . /// /// The object to compare with the current . /// if the specified object is equal to the current ; otherwise, . - public override bool Equals(object? obj) => obj is Vector3D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y) && Z.Equals(objVec.Z); + public override bool Equals(object? obj) => obj is Vector3D vector3D && this == vector3D; + public bool Equals(Vector3D other) => this == other; /// /// Generates a hash code for the . /// /// A hash code for the . public override int GetHashCode() => System.HashCode.Combine(X, Y, Z); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Vector3D)}({X}, {Y}, {Z})"; } /// diff --git a/Engine.Core/Primitives/Vector3DInt.cs b/Engine.Core/Primitives/Vector3DInt.cs new file mode 100644 index 0000000..5e2a427 --- /dev/null +++ b/Engine.Core/Primitives/Vector3DInt.cs @@ -0,0 +1,314 @@ +using System; + +namespace Engine.Core; + +/// +/// Represents a three-dimensional integer vector. +/// +/// X position of the . +/// Y position of the . +/// Z position of the . +/// +/// Initializes a new instance of the struct with the specified positions. +/// +[System.Diagnostics.DebuggerDisplay("{ToString(),nq}")] +public readonly struct Vector3DInt(int x, int y, int z) : IEquatable +{ + /// + /// The X coordinate of the . + /// + public readonly int X = x; + + /// + /// The Y coordinate of the . + /// + public readonly int Y = y; + + /// + /// The Z coordinate of the . + /// + public readonly int Z = z; + + /// + /// The magnitude (length) of the . + /// + public float Magnitude => Length(this); + + /// + /// The squared magnitude (length) of the . + /// + public float MagnitudeSquared => LengthSquared(this); + + /// + /// Represents the unit pointing upwards. + /// + public readonly static Vector3DInt Up = new(0, 1, 0); + + /// + /// Represents the unit pointing downwards. + /// + public readonly static Vector3DInt Down = new(0, -1, 0); + + /// + /// Represents the unit pointing leftwards. + /// + public readonly static Vector3DInt Left = new(-1, 0, 0); + + /// + /// Represents the unit pointing rightwards. + /// + public readonly static Vector3DInt Right = new(1, 0, 0); + + /// + /// Represents the unit pointing forwards. + /// + public readonly static Vector3DInt Forward = new(0, 0, 1); + + /// + /// Represents the unit pointing backwards. + public readonly static Vector3DInt Backward = new(0, 0, -1); + + /// + /// Represents the zero . + /// + public readonly static Vector3DInt Zero = new(0, 0, 0); + + /// + /// Represents the with both components equal to 1. + /// + public readonly static Vector3DInt One = new(1, 1, 1); + + public static Vector3DInt operator -(Vector3DInt vector) => new(0 - vector.X, 0 - vector.Y, 0 - vector.Z); + public static Vector3DInt operator +(Vector3DInt left, Vector3DInt right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + public static Vector3DInt operator -(Vector3DInt left, Vector3DInt right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + public static Vector3DInt operator *(Vector3DInt vector, int value) => new(vector.X * value, vector.Y * value, vector.Z * value); + public static Vector3DInt operator *(int value, Vector3DInt vector) => new(vector.X * value, vector.Y * value, vector.Z * value); + public static Vector3DInt operator /(Vector3DInt vector, int value) => new(vector.X / value, vector.Y / value, vector.Z / value); + public static bool operator ==(Vector3DInt left, Vector3DInt right) => left.X == right.X && left.Y == right.Y && left.Z == right.Z; + public static bool operator !=(Vector3DInt left, Vector3DInt right) => left.X != right.X || left.Y != right.Y || left.Z != right.Z; + + public static implicit operator Vector3DInt(Vector3D vector) => new(vector.X.RoundToInt(), vector.Y.RoundToInt(), vector.Z.RoundToInt()); + public static implicit operator Vector3DInt(Vector2DInt vector) => new(vector.X, vector.Y, 0); + + /// + /// Calculates the length of the . + /// + /// The . + /// The length of the . + public static float Length(Vector3DInt vector) => Math.Sqrt(LengthSquared(vector)); + + /// + /// Calculates the squared length of the . + /// + /// The . + /// The squared length of the . + public static float LengthSquared(Vector3DInt vector) => vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z; + + /// + /// Calculates the distance between two s. + /// + /// The start . + /// The end . + /// The distance between the two s. + public static float Distance(Vector3DInt from, Vector3DInt to) => Length(FromTo(from, to)); + + /// + /// Inverts the direction of the . + /// + /// The . + /// The inverted . + public static Vector3DInt Invert(Vector3DInt vector) => -vector; + + /// + /// Adds two s. + /// + /// The first . + /// The second . + /// The sum of the two s. + public static Vector3DInt Add(Vector3DInt left, Vector3DInt right) => left + right; + + /// + /// Subtracts one from another. + /// + /// The to subtract from. + /// The to subtract. + /// The result of subtracting the second from the first. + public static Vector3DInt Subtract(Vector3DInt left, Vector3DInt right) => left - right; + + /// + /// Multiplies a by a scalar value. + /// + /// The . + /// The scalar value. + /// The result of multiplying the by the scalar value. + public static Vector3DInt Multiply(Vector3DInt vector, int value) => vector * value; + + /// + /// Divides a by a scalar value. + /// + /// The . + /// The scalar value. + /// The result of dividing the by the scalar value. + public static Vector3DInt Divide(Vector3DInt vector, int value) => vector / value; + + /// + /// Calculates the absolute value of each component of the vector. + /// + /// The . + /// The with each component's absolute value. + public static Vector3DInt Abs(Vector3DInt vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y), Math.Abs(vector.Z)); + + /// + /// Calculates the from one point to another. + /// + /// The starting point. + /// The ending point. + /// The from the starting point to the ending point. + public static Vector3DInt FromTo(Vector3DInt from, Vector3DInt to) => to - from; + + /// + /// Scales a by another component-wise. + /// + /// The to scale. + /// The containing the scaling factors for each component. + /// The scaled . + public static Vector3DInt Scale(Vector3DInt vector, Vector3DInt scale) => new(vector.X * scale.X, vector.Y * scale.Y, vector.Z * scale.Z); + + /// + /// Returns the component-wise minimum of two s. + /// + /// The first . + /// The second . + /// The containing the minimum components from both input s. + public static Vector3DInt Min(Vector3DInt left, Vector3DInt right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y, (left.Z < right.Z) ? left.Z : right.Z); + + /// + /// Returns the component-wise maximum of two s. + /// + /// The first . + /// The second . + /// The containing the maximum components from both input s. + public static Vector3DInt Max(Vector3DInt left, Vector3DInt right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y, (left.Z > right.Z) ? left.Z : right.Z); + + /// + /// Clamps each component of a between the corresponding component of two other s. + /// + /// The to clamp. + /// The representing the minimum values for each component. + /// The representing the maximum values for each component. + /// A with each component clamped between the corresponding components of the min and max s. + public static Vector3DInt Clamp(Vector3DInt vector, Vector3DInt min, Vector3DInt max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y), Math.Clamp(vector.Z, min.Z, max.Z)); + + /// + /// Performs linear interpolation between two s. + /// + /// The starting (t = 0). + /// The ending (t = 1). + /// The interpolation parameter. + /// The interpolated . + public static Vector3DInt Lerp(Vector3DInt from, Vector3DInt to, int t) => from + FromTo(from, to) * t; + + /// + /// Calculates the cross product of two s. + /// + /// The first . + /// The second . + /// The cross product of the two s. + public static Vector3DInt Cross(Vector3DInt left, Vector3DInt right) => new(left.Y * right.Z - left.Z * right.Y, left.Z * right.X - left.X * right.Z, left.X * right.Y - left.Y * right.X); + + /// + /// Calculates the angle between two s. + /// + /// The first . + /// The second . + /// The angle between the two s in radians. + public static float Angle(Vector3DInt left, Vector3DInt right) => Math.Acos(Dot(left, right) / (Length(left) * Length(right))); + + /// + /// Calculates the dot product of two s. + /// + /// The first . + /// The second . + /// The dot product of the two s. + public static int Dot(Vector3DInt left, Vector3DInt right) => left.X * right.X + left.Y * right.Y + left.Z * right.Z; + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// if the specified object is equal to the current ; otherwise, . + public override bool Equals(object? obj) => obj is Vector3DInt vector3D && this == vector3D; + public bool Equals(Vector3DInt other) => this == other; + + /// + /// Generates a hash code for the . + /// + /// A hash code for the . + public override int GetHashCode() => System.HashCode.Combine(X, Y, Z); + + /// + /// Converts the to its string representation. + /// + /// A string representation of the . + public override string ToString() => $"{nameof(Vector3DInt)}({X}, {Y}, {Z})"; +} + +/// +/// Provides extension methods for type. +/// +public static class Vector3DIntExtensions +{ + /// + public static float Length(this Vector3DInt vector) => Vector3DInt.Length(vector); + + /// + public static float LengthSquared(this Vector3DInt vector) => Vector3DInt.LengthSquared(vector); + + /// + public static float Distance(this Vector3DInt from, Vector3DInt to) => Vector3DInt.Distance(from, to); + + /// + public static Vector3DInt Invert(this Vector3DInt vector) => Vector3DInt.Invert(vector); + + /// + public static Vector3DInt Add(this Vector3DInt vector, Vector3DInt vectorToAdd) => Vector3DInt.Add(vector, vectorToAdd); + + /// + public static Vector3DInt Subtract(this Vector3DInt vector, Vector3DInt vectorToSubtract) => Vector3DInt.Subtract(vector, vectorToSubtract); + + /// + public static Vector3DInt Multiply(this Vector3DInt vector, int value) => Vector3DInt.Multiply(vector, value); + + /// + public static Vector3DInt Divide(this Vector3DInt vector, int value) => Vector3DInt.Divide(vector, value); + + /// + public static Vector3DInt Abs(this Vector3DInt vector) => Vector3DInt.Abs(vector); + + /// + public static Vector3DInt FromTo(this Vector3DInt from, Vector3DInt to) => Vector3DInt.FromTo(from, to); + + /// + public static Vector3DInt Scale(this Vector3DInt vector, Vector3DInt scale) => Vector3DInt.Scale(vector, scale); + + /// + public static Vector3DInt Min(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Min(left, right); + + /// + public static Vector3DInt Max(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Max(left, right); + + /// + public static Vector3DInt Clamp(this Vector3DInt vector, Vector3DInt min, Vector3DInt max) => Vector3DInt.Clamp(vector, min, max); + + /// + public static Vector3DInt Lerp(this Vector3DInt from, Vector3DInt to, int t) => Vector3DInt.Lerp(from, to, t); + + /// + public static Vector3DInt Cross(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Cross(left, right); + + /// + public static float AngleBetween(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Angle(left, right); + + /// + public static int Dot(this Vector3DInt left, Vector3DInt right) => Vector3DInt.Dot(left, right); +} diff --git a/Engine.Core/Serialization/Attributes/IgnoreSerializationAttribute.cs b/Engine.Core/Serialization/Attributes/IgnoreSerializationAttribute.cs index f0dcb16..a8f9207 100644 --- a/Engine.Core/Serialization/Attributes/IgnoreSerializationAttribute.cs +++ b/Engine.Core/Serialization/Attributes/IgnoreSerializationAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core.Serialization; +namespace Engine.Core.Serialization; [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class)] public class IgnoreSerializationAttribute : Attribute; diff --git a/Engine.Core/Serialization/Attributes/SerializeAllAttribute.cs b/Engine.Core/Serialization/Attributes/SerializeAllAttribute.cs index fa6e8a1..6b142f3 100644 --- a/Engine.Core/Serialization/Attributes/SerializeAllAttribute.cs +++ b/Engine.Core/Serialization/Attributes/SerializeAllAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core.Serialization; +namespace Engine.Core.Serialization; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class SerializeAllAttribute : Attribute; diff --git a/Engine.Core/Serialization/Attributes/SerializeAttribute.cs b/Engine.Core/Serialization/Attributes/SerializeAttribute.cs index 6f94860..32bebe0 100644 --- a/Engine.Core/Serialization/Attributes/SerializeAttribute.cs +++ b/Engine.Core/Serialization/Attributes/SerializeAttribute.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core.Serialization; +namespace Engine.Core.Serialization; [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class SerializeAttribute : Attribute; diff --git a/Engine.Core/Serialization/EntityReference.cs b/Engine.Core/Serialization/EntityReference.cs index 8ad0bd6..07f1fc8 100644 --- a/Engine.Core/Serialization/EntityReference.cs +++ b/Engine.Core/Serialization/EntityReference.cs @@ -1,3 +1,3 @@ -namespace Syntriax.Engine.Core.Serialization; +namespace Engine.Core.Serialization; public record class EntityReference(string? Id = null); diff --git a/Engine.Core/Serialization/EntityRegistry.cs b/Engine.Core/Serialization/EntityRegistry.cs index c02b9d2..6339ba1 100644 --- a/Engine.Core/Serialization/EntityRegistry.cs +++ b/Engine.Core/Serialization/EntityRegistry.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Syntriax.Engine.Core.Serialization; +namespace Engine.Core.Serialization; public class EntityRegistry { diff --git a/Engine.Core/Serialization/ISerializer.cs b/Engine.Core/Serialization/ISerializer.cs index 0ef6962..0ada93c 100644 --- a/Engine.Core/Serialization/ISerializer.cs +++ b/Engine.Core/Serialization/ISerializer.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core.Serialization; +namespace Engine.Core.Serialization; public interface ISerializer { diff --git a/Engine.Core/Serialization/SerializedClass.cs b/Engine.Core/Serialization/SerializedClass.cs index 992f772..803c83b 100644 --- a/Engine.Core/Serialization/SerializedClass.cs +++ b/Engine.Core/Serialization/SerializedClass.cs @@ -2,9 +2,9 @@ using System; using System.Collections.Generic; using System.Reflection; -using Syntriax.Engine.Core.Factory; +using Engine.Core.Factory; -namespace Syntriax.Engine.Core.Serialization; +namespace Engine.Core.Serialization; public class SerializedClass { diff --git a/Engine.Core/Serialization/TypeContainer.cs b/Engine.Core/Serialization/TypeContainer.cs index 7179745..4b5a359 100644 --- a/Engine.Core/Serialization/TypeContainer.cs +++ b/Engine.Core/Serialization/TypeContainer.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core.Serialization; +namespace Engine.Core.Serialization; public class TypeContainer { diff --git a/Engine.Core/Serialization/Utils.cs b/Engine.Core/Serialization/Utils.cs index 6bf6eb1..9c5f145 100644 --- a/Engine.Core/Serialization/Utils.cs +++ b/Engine.Core/Serialization/Utils.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -namespace Syntriax.Engine.Core.Serialization; +namespace Engine.Core.Serialization; public static class Utils { diff --git a/Engine.Core/StateEnable.cs b/Engine.Core/StateEnable.cs index 2b4fc9e..fd1d89a 100644 --- a/Engine.Core/StateEnable.cs +++ b/Engine.Core/StateEnable.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public class StateEnable : IStateEnable { diff --git a/Engine.Core/Static/Internal/Constants.cs b/Engine.Core/Static/Internal/Constants.cs index 535e17d..800b02b 100644 --- a/Engine.Core/Static/Internal/Constants.cs +++ b/Engine.Core/Static/Internal/Constants.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; internal static class Constants { diff --git a/Engine.Core/Systems/Abstract/IDraw.cs b/Engine.Core/Systems/Abstract/IDraw.cs index 696e551..0f1d430 100644 --- a/Engine.Core/Systems/Abstract/IDraw.cs +++ b/Engine.Core/Systems/Abstract/IDraw.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a to be notified when the draw phase of the occurs. diff --git a/Engine.Core/Systems/Abstract/IEnterUniverse.cs b/Engine.Core/Systems/Abstract/IEnterUniverse.cs new file mode 100644 index 0000000..63703ae --- /dev/null +++ b/Engine.Core/Systems/Abstract/IEnterUniverse.cs @@ -0,0 +1,6 @@ +namespace Engine.Core; + +public interface IEnterUniverse : IBehaviour +{ + void EnterUniverse(IUniverse universe); +} diff --git a/Engine.Core/Systems/Abstract/IExitUniverse.cs b/Engine.Core/Systems/Abstract/IExitUniverse.cs new file mode 100644 index 0000000..8bc3ba3 --- /dev/null +++ b/Engine.Core/Systems/Abstract/IExitUniverse.cs @@ -0,0 +1,6 @@ +namespace Engine.Core; + +public interface IExitUniverse : IBehaviour +{ + void ExitUniverse(IUniverse universe); +} diff --git a/Engine.Core/Systems/Abstract/IFirstFrameUpdate.cs b/Engine.Core/Systems/Abstract/IFirstFrameUpdate.cs index d3abd57..8a2e5b4 100644 --- a/Engine.Core/Systems/Abstract/IFirstFrameUpdate.cs +++ b/Engine.Core/Systems/Abstract/IFirstFrameUpdate.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; public interface IFirstFrameUpdate : IBehaviour { diff --git a/Engine.Core/Systems/Abstract/ILastFrameUpdate.cs b/Engine.Core/Systems/Abstract/ILastFrameUpdate.cs new file mode 100644 index 0000000..c3e5132 --- /dev/null +++ b/Engine.Core/Systems/Abstract/ILastFrameUpdate.cs @@ -0,0 +1,6 @@ +namespace Engine.Core; + +public interface ILastFrameUpdate : IBehaviour +{ + void LastActiveFrame(); +} diff --git a/Engine.Core/Systems/Abstract/IPostDraw.cs b/Engine.Core/Systems/Abstract/IPostDraw.cs index 38003b2..ce4ec03 100644 --- a/Engine.Core/Systems/Abstract/IPostDraw.cs +++ b/Engine.Core/Systems/Abstract/IPostDraw.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a to be notified after the draw phase of the occurs. diff --git a/Engine.Core/Systems/Abstract/IPostUpdate.cs b/Engine.Core/Systems/Abstract/IPostUpdate.cs index 3802437..4654dde 100644 --- a/Engine.Core/Systems/Abstract/IPostUpdate.cs +++ b/Engine.Core/Systems/Abstract/IPostUpdate.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a to be notified after the update phase of the occurs. diff --git a/Engine.Core/Systems/Abstract/IPreDraw.cs b/Engine.Core/Systems/Abstract/IPreDraw.cs index 7d43801..b13226d 100644 --- a/Engine.Core/Systems/Abstract/IPreDraw.cs +++ b/Engine.Core/Systems/Abstract/IPreDraw.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a to be notified before the draw phase of the occurs. diff --git a/Engine.Core/Systems/Abstract/IPreUpdate.cs b/Engine.Core/Systems/Abstract/IPreUpdate.cs index af3a453..f07d009 100644 --- a/Engine.Core/Systems/Abstract/IPreUpdate.cs +++ b/Engine.Core/Systems/Abstract/IPreUpdate.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a to be notified before the update phase of the occurs. diff --git a/Engine.Core/Systems/Abstract/IUpdate.cs b/Engine.Core/Systems/Abstract/IUpdate.cs index 1e34989..b574148 100644 --- a/Engine.Core/Systems/Abstract/IUpdate.cs +++ b/Engine.Core/Systems/Abstract/IUpdate.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Core; +namespace Engine.Core; /// /// Represents a to be notified when the update phase of the occurs. diff --git a/Engine.Core/CoroutineManager.cs b/Engine.Core/Systems/CoroutineManager.cs similarity index 96% rename from Engine.Core/CoroutineManager.cs rename to Engine.Core/Systems/CoroutineManager.cs index 3b60152..16f9390 100644 --- a/Engine.Core/CoroutineManager.cs +++ b/Engine.Core/Systems/CoroutineManager.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public class CoroutineManager : Behaviour, IUpdate { diff --git a/Engine.Core/CoroutineYield.cs b/Engine.Core/Systems/CoroutineYield.cs similarity index 85% rename from Engine.Core/CoroutineYield.cs rename to Engine.Core/Systems/CoroutineYield.cs index 014a1ee..44e3f5b 100644 --- a/Engine.Core/CoroutineYield.cs +++ b/Engine.Core/Systems/CoroutineYield.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public class CoroutineYield(Func condition) : ICoroutineYield { diff --git a/Engine.Core/Systems/DrawManager.cs b/Engine.Core/Systems/DrawManager.cs index cd7fef0..13706ae 100644 --- a/Engine.Core/Systems/DrawManager.cs +++ b/Engine.Core/Systems/DrawManager.cs @@ -1,15 +1,16 @@ using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public class DrawManager : Behaviour { // We use Descending order because draw calls are running from last to first - private static Comparer SortByDescendingPriority() => Comparer.Create((x, y) => y.Priority.CompareTo(x.Priority)); + private static Comparer SortByDescendingPriority() => Comparer.Create((x, y) => y.CompareTo(x)); + private static System.Func GetPriority() => (b) => b.Priority; - private readonly ActiveBehaviourCollectorSorted preDrawEntities = new() { SortBy = SortByDescendingPriority() }; - private readonly ActiveBehaviourCollectorSorted drawEntities = new() { SortBy = SortByDescendingPriority() }; - private readonly ActiveBehaviourCollectorSorted postDrawEntities = new() { SortBy = SortByDescendingPriority() }; + private readonly ActiveBehaviourCollectorOrdered preDrawEntities = new(GetPriority(), SortByDescendingPriority()); + private readonly ActiveBehaviourCollectorOrdered drawEntities = new(GetPriority(), SortByDescendingPriority()); + private readonly ActiveBehaviourCollectorOrdered postDrawEntities = new(GetPriority(), SortByDescendingPriority()); private void OnPreDraw(IUniverse sender) { diff --git a/Engine.Core/Systems/UniverseEntranceManager.cs b/Engine.Core/Systems/UniverseEntranceManager.cs new file mode 100644 index 0000000..502c6a6 --- /dev/null +++ b/Engine.Core/Systems/UniverseEntranceManager.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; + +namespace Engine.Core; + +public class UniverseEntranceManager : Behaviour +{ + // We use Ascending order because we are using reverse for loop to call them + private static Comparer SortByAscendingPriority() => Comparer.Create((x, y) => x.CompareTo(y)); + private static System.Func GetPriority() => (b) => b.Priority; + + private readonly ActiveBehaviourCollectorOrdered enterUniverses = new(GetPriority(), SortByAscendingPriority()); + private readonly ActiveBehaviourCollectorOrdered exitUniverses = new(GetPriority(), SortByAscendingPriority()); + + private readonly List toCallEnterUniverses = new(32); + private readonly List toCallExitUniverses = new(32); + + protected override void OnEnteredUniverse(IUniverse universe) + { + enterUniverses.Assign(universe); + + foreach (IUniverseObject universeObject in universe.UniverseObjects) + OnUniverseObjectRegistered(universe, new(universeObject)); + + universe.OnUniverseObjectRegistered.AddListener(OnUniverseObjectRegistered); + universe.OnUniverseObjectUnRegistered.AddListener(OnUniverseObjectUnRegistered); + } + + protected override void OnExitedUniverse(IUniverse universe) + { + enterUniverses.Unassign(); + + foreach (IUniverseObject universeObject in universe.UniverseObjects) + OnUniverseObjectUnRegistered(universe, new(universeObject)); + + universe.OnUniverseObjectRegistered.RemoveListener(OnUniverseObjectRegistered); + universe.OnUniverseObjectUnRegistered.RemoveListener(OnUniverseObjectUnRegistered); + } + + private void OnUniverseObjectUnRegistered(IUniverse sender, IUniverse.UniverseObjectUnRegisteredArguments args) + { + for (int i = toCallExitUniverses.Count - 1; i >= 0; i--) + { + IExitUniverse exitUniverse = toCallExitUniverses[i]; + toCallExitUniverses.RemoveAt(i); + exitUniverse.ExitUniverse(Universe); + } + } + + private void OnUniverseObjectRegistered(IUniverse sender, IUniverse.UniverseObjectRegisteredArguments args) + { + for (int i = toCallEnterUniverses.Count - 1; i >= 0; i--) + { + IEnterUniverse enterUniverse = toCallEnterUniverses[i]; + toCallEnterUniverses.RemoveAt(i); + enterUniverse.EnterUniverse(Universe); + } + } + + private void OnEnterUniverseCollected(IBehaviourCollector sender, IBehaviourCollector.BehaviourCollectedArguments args) + { + toCallEnterUniverses.Add(args.BehaviourCollected); + } + + private void OnExitUniverseCollected(IBehaviourCollector sender, IBehaviourCollector.BehaviourCollectedArguments args) + { + toCallExitUniverses.Add(args.BehaviourCollected); + } + + public UniverseEntranceManager() + { + enterUniverses.OnCollected.AddListener(OnEnterUniverseCollected); + exitUniverses.OnCollected.AddListener(OnExitUniverseCollected); + } +} diff --git a/Engine.Core/Systems/UpdateManager.cs b/Engine.Core/Systems/UpdateManager.cs index dcf8070..f44076a 100644 --- a/Engine.Core/Systems/UpdateManager.cs +++ b/Engine.Core/Systems/UpdateManager.cs @@ -1,26 +1,30 @@ using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; public class UpdateManager : Behaviour { - // We use Ascending order because draw calls are running from last to first - private static Comparer SortByAscendingPriority() => Comparer.Create((x, y) => x.Priority.CompareTo(y.Priority)); + // We use Ascending order because we are using reverse for loop to call them + private static Comparer SortByAscendingPriority() => Comparer.Create((x, y) => x.CompareTo(y)); + private static System.Func GetPriority() => (b) => b.Priority; - private readonly ActiveBehaviourCollectorSorted firstFrameUpdates = new() { SortBy = SortByAscendingPriority() }; - private readonly ActiveBehaviourCollectorSorted preUpdateEntities = new() { SortBy = SortByAscendingPriority() }; - private readonly ActiveBehaviourCollectorSorted updateEntities = new() { SortBy = SortByAscendingPriority() }; - private readonly ActiveBehaviourCollectorSorted postUpdateEntities = new() { SortBy = SortByAscendingPriority() }; + private readonly ActiveBehaviourCollectorOrdered firstFrameUpdates = new(GetPriority(), SortByAscendingPriority()); + private readonly ActiveBehaviourCollector lastFrameUpdates = new(); + private readonly ActiveBehaviourCollectorOrdered preUpdateEntities = new(GetPriority(), SortByAscendingPriority()); + private readonly ActiveBehaviourCollectorOrdered updateEntities = new(GetPriority(), SortByAscendingPriority()); + private readonly ActiveBehaviourCollectorOrdered postUpdateEntities = new(GetPriority(), SortByAscendingPriority()); private readonly List toCallFirstFrameUpdates = new(32); protected override void OnEnteredUniverse(IUniverse universe) { firstFrameUpdates.Assign(universe); + lastFrameUpdates.Assign(universe); preUpdateEntities.Assign(universe); updateEntities.Assign(universe); postUpdateEntities.Assign(universe); + universe.OnPreUpdate.AddListener(OnFirstUpdate, int.MaxValue); universe.OnPreUpdate.AddListener(OnPreUpdate); universe.OnUpdate.AddListener(OnUpdate); universe.OnPostUpdate.AddListener(OnPostUpdate); @@ -29,23 +33,28 @@ public class UpdateManager : Behaviour protected override void OnExitedUniverse(IUniverse universe) { firstFrameUpdates.Unassign(); + lastFrameUpdates.Unassign(); preUpdateEntities.Unassign(); updateEntities.Unassign(); postUpdateEntities.Unassign(); + universe.OnPreUpdate.RemoveListener(OnFirstUpdate); universe.OnPreUpdate.RemoveListener(OnPreUpdate); universe.OnUpdate.RemoveListener(OnUpdate); universe.OnPostUpdate.RemoveListener(OnPostUpdate); } - private void OnPreUpdate(IUniverse sender, IUniverse.UpdateArguments args) + private void OnFirstUpdate(IUniverse sender, IUniverse.UpdateArguments args) { for (int i = toCallFirstFrameUpdates.Count - 1; i >= 0; i--) { toCallFirstFrameUpdates[i].FirstActiveFrame(); toCallFirstFrameUpdates.RemoveAt(i); } + } + private void OnPreUpdate(IUniverse sender, IUniverse.UpdateArguments args) + { for (int i = preUpdateEntities.Count - 1; i >= 0; i--) preUpdateEntities[i].PreUpdate(); } @@ -67,8 +76,21 @@ public class UpdateManager : Behaviour toCallFirstFrameUpdates.Add(args.BehaviourCollected); } + private void OnLastFrameRemoved(IBehaviourCollector sender, IBehaviourCollector.BehaviourRemovedArguments args) + { + args.BehaviourRemoved.LastActiveFrame(); + } + + private void CallLastFramesBeforeExit(object? sender, System.EventArgs e) + { + for (int i = lastFrameUpdates.Count - 1; i >= 0; i--) + lastFrameUpdates[i].LastActiveFrame(); + } + public UpdateManager() { firstFrameUpdates.OnCollected.AddListener(OnFirstFrameCollected); + lastFrameUpdates.OnRemoved.AddListener(OnLastFrameRemoved); + System.AppDomain.CurrentDomain.ProcessExit += CallLastFramesBeforeExit; } } diff --git a/Engine.Core/Transform2D.cs b/Engine.Core/Transform2D.cs index 7d14fa9..1c2a8bc 100644 --- a/Engine.Core/Transform2D.cs +++ b/Engine.Core/Transform2D.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core.Serialization; +using Engine.Core.Serialization; -namespace Syntriax.Engine.Core; +namespace Engine.Core; [System.Diagnostics.DebuggerDisplay("Name: {UniverseObject.Name, nq} Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")] public class Transform2D : Behaviour, ITransform2D diff --git a/Engine.Core/Universe.cs b/Engine.Core/Universe.cs index f0f82a4..8e3c28f 100644 --- a/Engine.Core/Universe.cs +++ b/Engine.Core/Universe.cs @@ -2,7 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; [System.Diagnostics.DebuggerDisplay("UniverseObject Count: {_universeObjects.Count}")] public class Universe : BaseEntity, IUniverse @@ -13,14 +13,16 @@ public class Universe : BaseEntity, IUniverse public Event OnPreDraw { get; } = new(); public Event OnDraw { get; } = new(); public Event OnPostDraw { get; } = new(); + public Event OnPreUniverseObjectRegistered { get; } = new(); public Event OnUniverseObjectRegistered { get; } = new(); + public Event OnPreUniverseObjectUnRegistered { get; } = new(); public Event OnUniverseObjectUnRegistered { get; } = new(); public Event OnTimeScaleChanged { get; } = new(); private readonly Event.EventHandler delegateOnUniverseObjectFinalize = null!; private readonly Event.EventHandler delegateOnUniverseObjectExitedUniverse = null!; - private readonly List _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL); + private readonly FastList _universeObjects = new(Constants.UNIVERSE_OBJECTS_SIZE_INITIAL); private float _timeScale = 1f; public Universe() @@ -53,6 +55,8 @@ public class Universe : BaseEntity, IUniverse if (_universeObjects.Contains(universeObject)) throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is already registered to the {nameof(Universe)}."); + OnPreUniverseObjectRegistered?.Invoke(this, new(universeObject)); + universeObject.OnFinalized.AddListener(delegateOnUniverseObjectFinalize); universeObject.OnExitedUniverse.AddListener(delegateOnUniverseObjectExitedUniverse); @@ -79,7 +83,7 @@ public class Universe : BaseEntity, IUniverse public void Remove(IUniverseObject universeObject) { - universeObject.SetParent(null); + universeObject.Parent = null; RemoveIncursive(universeObject); } @@ -88,6 +92,8 @@ public class Universe : BaseEntity, IUniverse if (!_universeObjects.Contains(universeObject)) throw new Exception($"{nameof(IUniverseObject)} named {universeObject.Name} is not registered to the {nameof(Universe)}."); + OnPreUniverseObjectUnRegistered?.Invoke(this, new(universeObject)); + universeObject.OnFinalized.RemoveListener(delegateOnUniverseObjectFinalize); universeObject.OnExitedUniverse.RemoveListener(delegateOnUniverseObjectExitedUniverse); @@ -114,8 +120,8 @@ public class Universe : BaseEntity, IUniverse protected override void FinalizeInternal() { base.FinalizeInternal(); - for (int i = UniverseObjects.Count; i >= 0; i--) - UniverseObjects[i].Finalize(); + for (int i = UniverseObjects.Count - 1; i >= 0; i--) + Remove(UniverseObjects[i]); } public void Update(UniverseTime engineTime) @@ -125,9 +131,10 @@ public class Universe : BaseEntity, IUniverse UnscaledTime = engineTime; Time = new(TimeSpan.FromTicks((long)(Time.TimeSinceStart.Ticks + engineTime.DeltaSpan.Ticks * TimeScale)), TimeSpan.FromTicks((long)(engineTime.DeltaSpan.Ticks * TimeScale))); - OnPreUpdate?.Invoke(this, new(Time)); - OnUpdate?.Invoke(this, new(Time)); - OnPostUpdate?.Invoke(this, new(Time)); + IUniverse.UpdateArguments args = new(Time); + OnPreUpdate?.Invoke(this, args); + OnUpdate?.Invoke(this, args); + OnPostUpdate?.Invoke(this, args); } public void Draw() diff --git a/Engine.Core/UniverseObject.cs b/Engine.Core/UniverseObject.cs index 94ff25a..e653f1c 100644 --- a/Engine.Core/UniverseObject.cs +++ b/Engine.Core/UniverseObject.cs @@ -1,7 +1,6 @@ -using System.Collections; using System.Collections.Generic; -namespace Syntriax.Engine.Core; +namespace Engine.Core; [System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")] public class UniverseObject : BaseEntity, IUniverseObject @@ -20,9 +19,9 @@ public class UniverseObject : BaseEntity, IUniverseObject private IUniverse _universe = null!; private IBehaviourController _behaviourController = null!; private bool _isActive = false; - private readonly List _children = []; + private readonly FastList _children = []; + private IUniverseObject? _parent = null; - public IUniverseObject? Parent { get; private set; } = null; public IReadOnlyList Children => _children; public IBehaviourController BehaviourController => _behaviourController; public IUniverse Universe => _universe; @@ -42,6 +41,40 @@ public class UniverseObject : BaseEntity, IUniverseObject } } + public IUniverseObject? Parent + { + get => _parent; + set + { + if (value == this) + throw new Exceptions.AssignFailedException($"{Name} can not parent itself"); + + if (_parent == value) + return; + + IUniverseObject? previousParent = Parent; + if (previousParent is not null) + { + previousParent.RemoveChild(this); + previousParent.OnActiveChanged.RemoveListener(OnParentActiveChanged); + } + + _parent = value; + + if (value is not null) + { + if (value.IsInUniverse && !IsInUniverse) + value.Universe.Register(this); + + value.AddChild(this); + value.OnActiveChanged.AddListener(OnParentActiveChanged); + } + + UpdateActive(); + OnParentChanged?.Invoke(this, new(previousParent, value)); + } + } + protected virtual void OnEnteringUniverse(IUniverse universe) { } bool IUniverseObject.EnterUniverse(IUniverse universe) { @@ -67,44 +100,14 @@ public class UniverseObject : BaseEntity, IUniverseObject return true; } - public void SetParent(IUniverseObject? parent) + public void AddChild(IUniverseObject child) { - if (parent == this) - throw new Exceptions.AssignFailedException($"{Name} can not parent itself"); - - if (Parent == parent) + if (_children.Contains(child)) return; - IUniverseObject? previousParent = Parent; - if (previousParent is not null) - { - previousParent.RemoveChild(this); - previousParent.OnActiveChanged.RemoveListener(OnParentActiveChanged); - } - - Parent = parent; - - if (parent is not null) - { - if (parent.IsInUniverse && !IsInUniverse) - parent.Universe.Register(this); - - parent.AddChild(this); - parent.OnActiveChanged.AddListener(OnParentActiveChanged); - } - - UpdateActive(); - OnParentChanged?.Invoke(this, new(previousParent, parent)); - } - - public void AddChild(IUniverseObject parent) - { - if (_children.Contains(parent)) - return; - - _children.Add(parent); - parent.SetParent(this); - OnChildrenAdded?.Invoke(this, new(parent)); + _children.Add(child); + child.Parent = this; + OnChildrenAdded?.Invoke(this, new(child)); } public void RemoveChild(IUniverseObject child) @@ -112,7 +115,7 @@ public class UniverseObject : BaseEntity, IUniverseObject if (!_children.Remove(child)) return; - child.SetParent(null); + child.Parent = null; OnChildrenRemoved?.Invoke(this, new(child)); } @@ -133,6 +136,7 @@ public class UniverseObject : BaseEntity, IUniverseObject base.OnAssign(stateEnable); stateEnable.OnEnabledChanged.AddListener(OnStateEnabledChanged); + UpdateActive(); } private void OnParentActiveChanged(IActive sender, IActive.ActiveChangedArguments args) => UpdateActive(); @@ -164,7 +168,4 @@ public class UniverseObject : BaseEntity, IUniverseObject { _name = GetType().Name; } - - public IEnumerator GetEnumerator() => _children.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => _children.GetEnumerator(); } diff --git a/Engine.Core/UniverseTime.cs b/Engine.Core/UniverseTime.cs index ed73de7..460ed8a 100644 --- a/Engine.Core/UniverseTime.cs +++ b/Engine.Core/UniverseTime.cs @@ -1,8 +1,8 @@ using System; -namespace Syntriax.Engine.Core; +namespace Engine.Core; -public readonly struct UniverseTime(TimeSpan TimeSinceStart, TimeSpan TimeDelta) +public readonly record struct UniverseTime(TimeSpan TimeSinceStart, TimeSpan TimeDelta) { public readonly TimeSpan TimeSinceStart = TimeSinceStart; public readonly TimeSpan DeltaSpan = TimeDelta; diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Engine.Integration.LiteNetLib.csproj b/Engine.Integration/Engine.Integration.LiteNetLib/Engine.Integration.LiteNetLib.csproj new file mode 100644 index 0000000..f5f119a --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Engine.Integration.LiteNetLib.csproj @@ -0,0 +1,19 @@ + + + + net9.0 + enable + enable + Engine.Systems.Network + Engine.Integration.LiteNetLib + + + + + + + + + + + diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs new file mode 100644 index 0000000..55887e6 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClient.cs @@ -0,0 +1,85 @@ +using System.Net.Sockets; + +using LiteNetLib; +using LiteNetLib.Utils; + +using Engine.Core; +using Engine.Core.Debug; + +namespace Engine.Systems.Network; + +public class LiteNetLibClient : LiteNetLibCommunicatorBase, INetworkCommunicatorClient +{ + private readonly NetDataWriter netDataWriter = new(); + + private CancellationTokenSource? cancellationTokenSource = null; + + protected override IConnection GetConnection(NetPeer peer) => new LiteNetLibServerConnection(peer); + + public INetworkCommunicatorClient Connect(string address, int port, string? password = null) + { + if (!UniverseObject.IsInUniverse) + throw new($"{nameof(LiteNetLibClient)} must be in an universe to connect"); + + password ??= string.Empty; + + // Okay, for some reason sometimes LiteNetLib goes dumb when the server hostname has IPv6 address as well as IPv4 + // but the client doesn't support IPv6, it still tries to use the v6 unless we explicitly tell the ip to connect to, + // which fails to connect... So for the time being I am preferring IPv4 below over IPv6 for clients. + // TODO: I think this is something that happens on Linux only? I need to check on Windows as well just to be sure. + System.Net.IPAddress[] addresses = System.Net.Dns.GetHostAddresses(address); + string connectionAddress = addresses.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork, addresses[0]).ToString(); + + logger?.Log(this, $"Connecting to server at '{address}:{port}' with password '{password}'"); + logger?.Log(this, $"Resolved address for {address}: {connectionAddress}"); + + Manager.Start(); + Manager.Connect(connectionAddress, port, password); + + return this; + } + + public INetworkCommunicatorClient SendToServer(T packet, PacketDelivery packetDelivery) where T : class, new() + { + netDataWriter.Reset(); + netPacketProcessor.Write(netDataWriter, packet); + + switch (packetDelivery) + { + case PacketDelivery.ReliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; + case PacketDelivery.UnreliableInOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Sequenced); break; + case PacketDelivery.ReliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableUnordered); break; + case PacketDelivery.UnreliableOutOfOrder: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.Unreliable); break; + default: Manager.FirstPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; + } + + return this; + } + + protected override void OnEnteredUniverse(IUniverse universe) + { + base.OnEnteredUniverse(universe); + + cancellationTokenSource = new CancellationTokenSource(); + PollEvents(cancellationTokenSource.Token); + } + + protected override void OnExitedUniverse(IUniverse universe) + { + base.OnExitedUniverse(universe); + cancellationTokenSource?.Cancel(); + } + + /// + /// Client needs to send everything as soon as possible so + /// the events are polled a separate thread running constantly + /// + private async void PollEvents(CancellationToken cancellationToken) => await Task.Run(() => + { + while (true) + { + Manager.PollEvents(); + Thread.Sleep(1); + } + }, cancellationToken); +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClientConnection.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClientConnection.cs new file mode 100644 index 0000000..5ce46e0 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibClientConnection.cs @@ -0,0 +1,16 @@ +using LiteNetLib; + +namespace Engine.Systems.Network; + +public record class LiteNetLibClientConnection(NetPeer NetPeer) : IConnection +{ + public string Id { get; } = NetPeer.Id.ToString(); + + public float Ping => NetPeer.Ping * .001f; + public float RoundTrip => NetPeer.RoundTripTime * .001f; + + public int PingMs => NetPeer.Ping; + public int RoundTripMs => NetPeer.RoundTripTime; + + public override string ToString() => $"Connection({Id})"; +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs new file mode 100644 index 0000000..7691103 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibCommunicatorBase.cs @@ -0,0 +1,185 @@ +using System.Reflection; + +using LiteNetLib; +using LiteNetLib.Utils; + +using Engine.Core; +using Engine.Core.Debug; + +namespace Engine.Systems.Network; + +public abstract class LiteNetLibCommunicatorBase : Behaviour, INetworkCommunicator +{ + protected readonly NetPacketProcessor netPacketProcessor = new(); + + private readonly Dictionary> listeners = []; + private readonly Dictionary _connections = []; + + private readonly Dictionary localPeerIdToConnectionDictionary = []; + + protected ILogger? logger = null; + + public IReadOnlyDictionary Connections => _connections; + public EventBasedNetListener Listener { get; private set; } = null!; + public NetManager Manager { get; private set; } = null!; + + public Event OnConnectionEstablished { get; } = new(); + public Event OnConnectionAbolished { get; } = new(); + + public INetworkCommunicator Stop() + { + Manager.Stop(); + return this; + } + + protected override void OnEnteredUniverse(IUniverse universe) + { + base.OnEnteredUniverse(universe); + logger = universe.FindBehaviour(); + } + + protected override void OnExitedUniverse(IUniverse universe) + { + base.OnExitedUniverse(universe); + logger = null; + Stop(); + } + + protected virtual void OnPacketArrived(T packet, NetPeer peer) where T : INetworkPacket + { + if (!listeners.TryGetValue(typeof(T), out Event? @event)) + return; + + @event.Invoke(localPeerIdToConnectionDictionary[peer.Id], packet); + } + + private void NetworkReceiveEvent(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) + { + try { netPacketProcessor.ReadAllPackets(reader, peer); } + catch (Exception exception) { logger?.LogException(this, exception, force: true); } + } + + protected abstract IConnection GetConnection(NetPeer peer); + private void ConnectionEstablished(NetPeer peer) + { + IConnection connection = GetConnection(peer); + + localPeerIdToConnectionDictionary.Add(peer.Id, connection); + _connections.Add(connection.Id, connection); + + logger?.Log(this, $"Connection established with ip '{peer.Address}' and id '{connection.Id}'"); + OnConnectionEstablished.Invoke(this, connection); + } + + private void ConnectionAbolished(NetPeer peer, DisconnectInfo disconnectInfo) + { + if (!localPeerIdToConnectionDictionary.TryGetValue(peer.Id, out IConnection? connection)) + return; + + localPeerIdToConnectionDictionary.Remove(peer.Id); + _connections.Remove(connection.Id); + + logger?.Log(this, $"Connection abolished with ip '{peer.Address}' and id '{connection.Id}'"); + OnConnectionAbolished.Invoke(this, connection); + } + + private void SetupPackets() + { + // Find network packets implementing INetworkPacket + IEnumerable packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) + .Where(t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && !t.IsGenericType); + + MethodInfo subscribeReusableMethod = typeof(NetPacketProcessor) + .GetMethods() + .FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.SubscribeReusable) && + m.GetParameters().Length == 1 && + m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Action<,>) + )!; + + MethodInfo registerNestedTypeMethod = typeof(NetPacketProcessor) + .GetMethods() + .FirstOrDefault(m => m.Name == nameof(NetPacketProcessor.RegisterNestedType) && + m.GetParameters().Length == 0 + )!; + + MethodInfo onPacketArrivedMethod = typeof(LiteNetLibCommunicatorBase) + .GetMethod(nameof(OnPacketArrived), BindingFlags.NonPublic | BindingFlags.Instance)!; + + // Register all network packets by calling the methods bellow where T is our found network packet type + // NetPacketProcessor.SubscribeReusable(Action onReceive) + // NetPacketProcessor.RegisterNestedType() + foreach (Type packetType in packetTypes) + { + if (!packetType.IsClass) + { + MethodInfo genericRegisterNestedTypeMethod = registerNestedTypeMethod.MakeGenericMethod(packetType); + genericRegisterNestedTypeMethod.Invoke(netPacketProcessor, []); + continue; + } + + MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType); + + Type delegateType = typeof(Action<,>).MakeGenericType(packetType, typeof(NetPeer)); + Delegate delegateHandler = Delegate.CreateDelegate(delegateType, this, genericOnPacketArrivedMethod); + + MethodInfo genericSubscribeReusableMethod = subscribeReusableMethod.MakeGenericMethod(packetType, typeof(NetPeer)); + genericSubscribeReusableMethod.Invoke(netPacketProcessor, [delegateHandler]); + } + } + + public LiteNetLibCommunicatorBase() + { + Listener = new EventBasedNetListener(); + Manager = new NetManager(Listener); + + Listener.NetworkReceiveEvent += NetworkReceiveEvent; + Listener.PeerConnectedEvent += ConnectionEstablished; + Listener.PeerDisconnectedEvent += ConnectionAbolished; + + SetupEnginePackets(); + SetupPackets(); + } + + private void SetupEnginePackets() + { + // I know, ugly af. I need to find a better way + netPacketProcessor.RegisterNestedType(AABBNetPacker.Write, AABBNetPacker.Read); + netPacketProcessor.RegisterNestedType(CircleNetPacker.Write, CircleNetPacker.Read); + netPacketProcessor.RegisterNestedType(ColorHSVNetPacker.Write, ColorHSVNetPacker.Read); + netPacketProcessor.RegisterNestedType(ColorRGBANetPacker.Write, ColorRGBANetPacker.Read); + netPacketProcessor.RegisterNestedType(ColorRGBNetPacker.Write, ColorRGBNetPacker.Read); + netPacketProcessor.RegisterNestedType(Line2DEquationNetPacker.Write, Line2DEquationNetPacker.Read); + netPacketProcessor.RegisterNestedType(Line2DNetPacker.Write, Line2DNetPacker.Read); + netPacketProcessor.RegisterNestedType(Projection1DNetPacker.Write, Projection1DNetPacker.Read); + netPacketProcessor.RegisterNestedType(QuaternionNetPacker.Write, QuaternionNetPacker.Read); + netPacketProcessor.RegisterNestedType(Shape2DNetPacker.Write, Shape2DNetPacker.Read); + netPacketProcessor.RegisterNestedType(TriangleNetPacker.Write, TriangleNetPacker.Read); + netPacketProcessor.RegisterNestedType(Vector2DNetPacker.Write, Vector2DNetPacker.Read); + netPacketProcessor.RegisterNestedType(Vector3DNetPacker.Write, Vector3DNetPacker.Read); + } + + public INetworkCommunicator SubscribeToPackets(Event.EventHandler callback) + { + Type type = typeof(T); + if (!listeners.TryGetValue(type, out Event? @event)) + { + @event = new(); + listeners.Add(type, @event); + } + + @event.AddListener(EventDelegateWrapper(callback)); + return this; + } + + public INetworkCommunicator UnsubscribeFromPackets(Event.EventHandler callback) + { + Type type = typeof(T); + if (!listeners.TryGetValue(type, out Event? @event)) + return this; + + @event.RemoveListener(EventDelegateWrapper(callback)); + return this; + } + + private static Event.EventHandler EventDelegateWrapper(Event.EventHandler callback) => (sender, @object) => callback.Invoke(sender, (T)@object); +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs new file mode 100644 index 0000000..f77d1e3 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServer.cs @@ -0,0 +1,102 @@ +using LiteNetLib; +using LiteNetLib.Utils; + +using Engine.Core; +using Engine.Core.Debug; + +namespace Engine.Systems.Network; + +public class LiteNetLibServer : LiteNetLibCommunicatorBase, INetworkCommunicatorServer +{ + public string Password { get; private set; } = string.Empty; + public int MaxConnectionCount { get; private set; } = 2; + public int Port { get; private set; } = 8888; + + private readonly NetDataWriter netDataWriter = new(); + + public LiteNetLibServer() : this(8888, 2) { } + public LiteNetLibServer(int port, int maxConnectionCount) : base() + { + MaxConnectionCount = maxConnectionCount; + Port = port; + + Listener.ConnectionRequestEvent += OnConnectionRequest; + } + + protected override IConnection GetConnection(NetPeer peer) => new LiteNetLibClientConnection(peer); + + private void OnConnectionRequest(ConnectionRequest request) + { + logger?.Log(this, $"Connection request from ip {request.RemoteEndPoint}"); + logger?.Log(this, $"Current connection count: {Connections.Count}"); + if (Manager.ConnectedPeersCount < MaxConnectionCount) + request.AcceptIfKey(Password); + else + request.Reject(); + } + + public INetworkCommunicatorServer Start(int port, int maxConnectionCount, string? password = null) + { + if (!UniverseObject.IsInUniverse) + throw new($"{nameof(LiteNetLibServer)} must be in an universe to start"); + + Password = password ?? string.Empty; + MaxConnectionCount = maxConnectionCount; + Port = port; + + logger?.Log(this, $"Starting server on port '{port}' with password '{Password}' and max connection count '{maxConnectionCount}'"); + logger?.Log(this, $"Server status: {(Manager.Start(port) ? "Active" : "Failed")}"); + + return this; + } + + public INetworkCommunicatorServer SendToClient(IConnection connection, T packet, PacketDelivery packetDelivery) where T : class, new() + { + netDataWriter.Reset(); + netPacketProcessor.Write(netDataWriter, packet); + + if (Manager.ConnectedPeerList.FirstOrDefault(p => p.Id.CompareTo(connection.Id) == 0) is not NetPeer netPeer) + throw new($"Peer {connection} couldn't be found."); + + switch (packetDelivery) + { + case PacketDelivery.ReliableInOrder: netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; + case PacketDelivery.UnreliableInOrder: netPeer.Send(netDataWriter, DeliveryMethod.Sequenced); break; + case PacketDelivery.ReliableOutOfOrder: netPeer.Send(netDataWriter, DeliveryMethod.ReliableUnordered); break; + case PacketDelivery.UnreliableOutOfOrder: netPeer.Send(netDataWriter, DeliveryMethod.Unreliable); break; + default: netPeer.Send(netDataWriter, DeliveryMethod.ReliableOrdered); break; + } + + return this; + } + + public INetworkCommunicatorServer SendToAll(T packet, PacketDelivery packetDelivery) where T : class, new() + { + netDataWriter.Reset(); + netPacketProcessor.Write(netDataWriter, packet); + + switch (packetDelivery) + { + case PacketDelivery.ReliableInOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); break; + case PacketDelivery.UnreliableInOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.Sequenced); break; + case PacketDelivery.ReliableOutOfOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableUnordered); break; + case PacketDelivery.UnreliableOutOfOrder: Manager.SendToAll(netDataWriter, DeliveryMethod.Unreliable); break; + default: Manager.SendToAll(netDataWriter, DeliveryMethod.ReliableOrdered); break; + } + return this; + } + + private void PollEvents(IUniverse sender, IUniverse.UpdateArguments args) => Manager.PollEvents(); + + protected override void OnEnteredUniverse(IUniverse universe) + { + base.OnEnteredUniverse(universe); + universe.OnPostUpdate.AddListener(PollEvents); + } + + protected override void OnExitedUniverse(IUniverse universe) + { + base.OnExitedUniverse(universe); + universe.OnPostUpdate.RemoveListener(PollEvents); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServerConnection.cs b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServerConnection.cs new file mode 100644 index 0000000..a215b74 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/LiteNetLibServerConnection.cs @@ -0,0 +1,16 @@ +using LiteNetLib; + +namespace Engine.Systems.Network; + +public record class LiteNetLibServerConnection(NetPeer NetPeer) : IConnection +{ + public string Id => NetPeer.RemoteId.ToString(); + + public float Ping => NetPeer.Ping * .001f; + public float RoundTrip => NetPeer.RoundTripTime * .001f; + + public int PingMs => NetPeer.Ping; + public int RoundTripMs => NetPeer.RoundTripTime; + + public override string ToString() => $"Connection({Id})"; +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/AABBNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/AABBNetPacker.cs new file mode 100644 index 0000000..0af57aa --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/AABBNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class AABBNetPacker +{ + internal static void Write(NetDataWriter writer, AABB data) + { + Vector2DNetPacker.Write(writer, data.LowerBoundary); + Vector2DNetPacker.Write(writer, data.UpperBoundary); + } + + internal static AABB Read(NetDataReader reader) + { + Vector2D lowerBoundary = Vector2DNetPacker.Read(reader); + Vector2D upperBoundary = Vector2DNetPacker.Read(reader); + + return new AABB(lowerBoundary, upperBoundary); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/CircleNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/CircleNetPacker.cs new file mode 100644 index 0000000..9cd748f --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/CircleNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class CircleNetPacker +{ + internal static void Write(NetDataWriter writer, Circle data) + { + Vector2DNetPacker.Write(writer, data.Center); + writer.Put(data.Radius); + } + + internal static Circle Read(NetDataReader reader) + { + Vector2D center = Vector2DNetPacker.Read(reader); + float radius = reader.GetFloat(); + + return new Circle(center, radius); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorHSVNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorHSVNetPacker.cs new file mode 100644 index 0000000..a7a24be --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorHSVNetPacker.cs @@ -0,0 +1,24 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class ColorHSVNetPacker +{ + internal static void Write(NetDataWriter writer, ColorHSV data) + { + writer.Put(data.Hue); + writer.Put(data.Saturation); + writer.Put(data.Value); + } + + internal static ColorHSV Read(NetDataReader reader) + { + float hue = reader.GetFloat(); + float saturation = reader.GetFloat(); + float value = reader.GetFloat(); + + return new ColorHSV(hue, saturation, value); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBANetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBANetPacker.cs new file mode 100644 index 0000000..ea5f409 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBANetPacker.cs @@ -0,0 +1,26 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class ColorRGBANetPacker +{ + internal static void Write(NetDataWriter writer, ColorRGBA data) + { + writer.Put(data.R); + writer.Put(data.G); + writer.Put(data.B); + writer.Put(data.A); + } + + internal static ColorRGBA Read(NetDataReader reader) + { + byte red = reader.GetByte(); + byte green = reader.GetByte(); + byte blue = reader.GetByte(); + byte alpha = reader.GetByte(); + + return new ColorRGBA(red, green, blue, alpha); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBNetPacker.cs new file mode 100644 index 0000000..4243143 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/ColorRGBNetPacker.cs @@ -0,0 +1,24 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class ColorRGBNetPacker +{ + internal static void Write(NetDataWriter writer, ColorRGB data) + { + writer.Put(data.R); + writer.Put(data.G); + writer.Put(data.B); + } + + internal static ColorRGB Read(NetDataReader reader) + { + byte red = reader.GetByte(); + byte green = reader.GetByte(); + byte blue = reader.GetByte(); + + return new ColorRGB(red, green, blue); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DEquationNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DEquationNetPacker.cs new file mode 100644 index 0000000..e3c2228 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DEquationNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class Line2DEquationNetPacker +{ + internal static void Write(NetDataWriter writer, Line2DEquation data) + { + writer.Put(data.Slope); + writer.Put(data.OffsetY); + } + + internal static Line2DEquation Read(NetDataReader reader) + { + float slope = reader.GetFloat(); + float offsetY = reader.GetFloat(); + + return new Line2DEquation(slope, offsetY); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DNetPacker.cs new file mode 100644 index 0000000..4340670 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Line2DNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class Line2DNetPacker +{ + internal static void Write(NetDataWriter writer, Line2D data) + { + Vector2DNetPacker.Write(writer, data.From); + Vector2DNetPacker.Write(writer, data.To); + } + + internal static Line2D Read(NetDataReader reader) + { + Vector2D from = Vector2DNetPacker.Read(reader); + Vector2D to = Vector2DNetPacker.Read(reader); + + return new Line2D(from, to); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Projection1DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Projection1DNetPacker.cs new file mode 100644 index 0000000..753f037 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Projection1DNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class Projection1DNetPacker +{ + internal static void Write(NetDataWriter writer, Projection1D data) + { + writer.Put(data.Min); + writer.Put(data.Max); + } + + internal static Projection1D Read(NetDataReader reader) + { + float min = reader.GetFloat(); + float max = reader.GetFloat(); + + return new Projection1D(min, max); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/QuaternionNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/QuaternionNetPacker.cs new file mode 100644 index 0000000..fbda6ce --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/QuaternionNetPacker.cs @@ -0,0 +1,26 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class QuaternionNetPacker +{ + internal static void Write(NetDataWriter writer, Quaternion data) + { + writer.Put(data.X); + writer.Put(data.Y); + writer.Put(data.Z); + writer.Put(data.W); + } + + internal static Quaternion Read(NetDataReader reader) + { + float x = reader.GetFloat(); + float y = reader.GetFloat(); + float z = reader.GetFloat(); + float w = reader.GetFloat(); + + return new Quaternion(x, y, z, w); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Shape2DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Shape2DNetPacker.cs new file mode 100644 index 0000000..97ceea8 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Shape2DNetPacker.cs @@ -0,0 +1,26 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class Shape2DNetPacker +{ + internal static void Write(NetDataWriter writer, Shape2D data) + { + writer.Put(data.Vertices.Count); + foreach (Vector2D vertex in data.Vertices) + Vector2DNetPacker.Write(writer, vertex); + } + + internal static Shape2D Read(NetDataReader reader) + { + int verticesCount = reader.GetInt(); + List vertices = new(verticesCount); + + for (int i = 0; i < verticesCount; i++) + vertices.Add(Vector2DNetPacker.Read(reader)); + + return new Shape2D(vertices); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/TriangleNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/TriangleNetPacker.cs new file mode 100644 index 0000000..53b3481 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/TriangleNetPacker.cs @@ -0,0 +1,24 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class TriangleNetPacker +{ + internal static void Write(NetDataWriter writer, Triangle data) + { + Vector2DNetPacker.Write(writer, data.A); + Vector2DNetPacker.Write(writer, data.B); + Vector2DNetPacker.Write(writer, data.C); + } + + internal static Triangle Read(NetDataReader reader) + { + Vector2D a = Vector2DNetPacker.Read(reader); + Vector2D b = Vector2DNetPacker.Read(reader); + Vector2D c = Vector2DNetPacker.Read(reader); + + return new Triangle(a, b, c); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector2DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector2DNetPacker.cs new file mode 100644 index 0000000..93f60e1 --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector2DNetPacker.cs @@ -0,0 +1,22 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class Vector2DNetPacker +{ + internal static void Write(NetDataWriter writer, Vector2D data) + { + writer.Put(data.X); + writer.Put(data.Y); + } + + internal static Vector2D Read(NetDataReader reader) + { + float x = reader.GetFloat(); + float y = reader.GetFloat(); + + return new Vector2D(x, y); + } +} diff --git a/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector3DNetPacker.cs b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector3DNetPacker.cs new file mode 100644 index 0000000..393152c --- /dev/null +++ b/Engine.Integration/Engine.Integration.LiteNetLib/Packers/Vector3DNetPacker.cs @@ -0,0 +1,24 @@ +using LiteNetLib.Utils; + +using Engine.Core; + +namespace Engine.Systems.Network; + +internal static class Vector3DNetPacker +{ + internal static void Write(NetDataWriter writer, Vector3D data) + { + writer.Put(data.X); + writer.Put(data.Y); + writer.Put(data.Z); + } + + internal static Vector3D Read(NetDataReader reader) + { + float x = reader.GetFloat(); + float y = reader.GetFloat(); + float z = reader.GetFloat(); + + return new Vector3D(x, y, z); + } +} diff --git a/Engine.Integration/Engine.Integration.MonoGame/Abstract/IDrawableSprite.cs b/Engine.Integration/Engine.Integration.MonoGame/Abstract/IDrawableSprite.cs index 2e39ff7..a0e2211 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Abstract/IDrawableSprite.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Abstract/IDrawableSprite.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public interface IDrawableSprite : IBehaviour { diff --git a/Engine.Integration/Engine.Integration.MonoGame/Abstract/IDrawableTriangle.cs b/Engine.Integration/Engine.Integration.MonoGame/Abstract/IDrawableTriangle.cs index 937d559..7d3372d 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Abstract/IDrawableTriangle.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Abstract/IDrawableTriangle.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public interface IDrawableTriangle : IBehaviour { diff --git a/Engine.Integration/Engine.Integration.MonoGame/Abstract/ILoadContent.cs b/Engine.Integration/Engine.Integration.MonoGame/Abstract/ILoadContent.cs new file mode 100644 index 0000000..d838772 --- /dev/null +++ b/Engine.Integration/Engine.Integration.MonoGame/Abstract/ILoadContent.cs @@ -0,0 +1,10 @@ +using Microsoft.Xna.Framework.Content; + +using Engine.Core; + +namespace Engine.Integration.MonoGame; + +public interface ILoadContent : IBehaviour +{ + void LoadContent(ContentManager content); +} diff --git a/Engine.Integration/Engine.Integration.MonoGame/Abstract/ISpriteBatch.cs b/Engine.Integration/Engine.Integration.MonoGame/Abstract/ISpriteBatch.cs index 88a4f82..0939141 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Abstract/ISpriteBatch.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Abstract/ISpriteBatch.cs @@ -3,9 +3,9 @@ using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public interface ISpriteBatch { diff --git a/Engine.Integration/Engine.Integration.MonoGame/Abstract/ITriangleBatch.cs b/Engine.Integration/Engine.Integration.MonoGame/Abstract/ITriangleBatch.cs index 08f190a..1de8f80 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Abstract/ITriangleBatch.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Abstract/ITriangleBatch.cs @@ -1,8 +1,8 @@ using Microsoft.Xna.Framework; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public interface ITriangleBatch { diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/DrawableShapeBehaviour.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/DrawableShape.cs similarity index 61% rename from Engine.Integration/Engine.Integration.MonoGame/Behaviours/DrawableShapeBehaviour.cs rename to Engine.Integration/Engine.Integration.MonoGame/Behaviours/DrawableShape.cs index dbfef7e..0078088 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/DrawableShapeBehaviour.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/DrawableShape.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; -public class DrawableShapeBehaviour : Behaviour2D, IDrawableTriangle, IPreDraw +public class DrawableShape : Behaviour2D, IDrawableTriangle, IPreDraw { private readonly Shape2D shape = new([]); private readonly List worldTriangles = []; @@ -23,7 +23,7 @@ public class DrawableShapeBehaviour : Behaviour2D, IDrawableTriangle, IPreDraw protected void UpdateWorldShape() => shape.Transform(Transform, worldShape); - public DrawableShapeBehaviour() => shape = Shape2D.Triangle; - public DrawableShapeBehaviour(Shape2D shape) => this.shape = shape; - public DrawableShapeBehaviour(Shape2D shape, ColorRGB color) { this.shape = shape; this.color = color; } + public DrawableShape() => shape = Shape2D.Triangle; + public DrawableShape(Shape2D shape) => this.shape = shape; + public DrawableShape(Shape2D shape, ColorRGB color) { this.shape = shape; this.color = color; } } diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/KeyboardInputsBehaviour.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/KeyboardInputs.cs similarity index 54% rename from Engine.Integration/Engine.Integration.MonoGame/Behaviours/KeyboardInputsBehaviour.cs rename to Engine.Integration/Engine.Integration.MonoGame/Behaviours/KeyboardInputs.cs index 944fcc1..5e333e6 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/KeyboardInputsBehaviour.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/KeyboardInputs.cs @@ -3,18 +3,18 @@ using System.Collections.Generic; using Microsoft.Xna.Framework.Input; -using Syntriax.Engine.Core; -using Syntriax.Engine.Systems.Input; +using Engine.Core; +using Engine.Systems.Input; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; -public class KeyboardInputsBehaviour : Behaviour, IButtonInputs, IUpdate +public class KeyboardInputs : Behaviour, IButtonInputs, IUpdate { - public Event, IButtonInputs.ButtonCallbackArguments> OnAnyButtonPressed { get; } = new(); - public Event, IButtonInputs.ButtonCallbackArguments> OnAnyButtonReleased { get; } = new(); + public IButtonInputs.InputEvent OnAnyButtonPressed { get; } = new(); + public IButtonInputs.InputEvent OnAnyButtonReleased { get; } = new(); - private readonly Dictionary, IButtonInputs.ButtonCallbackArguments>> OnPressed = new(256); - private readonly Dictionary, IButtonInputs.ButtonCallbackArguments>> OnReleased = new(256); + private readonly Dictionary.InputEvent> OnPressed = new(256); + private readonly Dictionary.InputEvent> OnReleased = new(256); private int cachePressedCurrentlyCount = 0; private readonly Keys[] cachePressedCurrently = new Keys[256]; @@ -22,9 +22,9 @@ public class KeyboardInputsBehaviour : Behaviour, IButtonInputs, IUpdate private int cachePressedPreviouslyCount = 0; private readonly Keys[] cachePressedPreviously = new Keys[256]; - public void RegisterOnPress(Keys key, Event, IButtonInputs.ButtonCallbackArguments>.EventHandler callback) + public void RegisterOnPress(Keys key, IButtonInputs.InputEvent.EventHandler callback) { - if (!OnPressed.TryGetValue(key, out Event, IButtonInputs.ButtonCallbackArguments>? delegateCallback)) + if (!OnPressed.TryGetValue(key, out IButtonInputs.InputEvent? delegateCallback)) { delegateCallback = new(); OnPressed.Add(key, delegateCallback); @@ -33,15 +33,15 @@ public class KeyboardInputsBehaviour : Behaviour, IButtonInputs, IUpdate delegateCallback.AddListener(callback); } - public void UnregisterOnPress(Keys key, Event, IButtonInputs.ButtonCallbackArguments>.EventHandler callback) + public void UnregisterOnPress(Keys key, IButtonInputs.InputEvent.EventHandler callback) { - if (OnPressed.TryGetValue(key, out Event, IButtonInputs.ButtonCallbackArguments>? delegateCallback)) + if (OnPressed.TryGetValue(key, out IButtonInputs.InputEvent? delegateCallback)) delegateCallback.RemoveListener(callback); } - public void RegisterOnRelease(Keys key, Event, IButtonInputs.ButtonCallbackArguments>.EventHandler callback) + public void RegisterOnRelease(Keys key, IButtonInputs.InputEvent.EventHandler callback) { - if (!OnReleased.TryGetValue(key, out Event, IButtonInputs.ButtonCallbackArguments>? delegateCallback)) + if (!OnReleased.TryGetValue(key, out IButtonInputs.InputEvent? delegateCallback)) { delegateCallback = new(); OnReleased.Add(key, delegateCallback); @@ -50,9 +50,9 @@ public class KeyboardInputsBehaviour : Behaviour, IButtonInputs, IUpdate delegateCallback.AddListener(callback); } - public void UnregisterOnRelease(Keys key, Event, IButtonInputs.ButtonCallbackArguments>.EventHandler callback) + public void UnregisterOnRelease(Keys key, IButtonInputs.InputEvent.EventHandler callback) { - if (OnReleased.TryGetValue(key, out Event, IButtonInputs.ButtonCallbackArguments>? delegateCallback)) + if (OnReleased.TryGetValue(key, out IButtonInputs.InputEvent? delegateCallback)) delegateCallback.RemoveListener(callback); } @@ -69,7 +69,7 @@ public class KeyboardInputsBehaviour : Behaviour, IButtonInputs, IUpdate if (WasPressed(currentlyPressedKey)) continue; - if (OnPressed.TryGetValue(currentlyPressedKey, out Event, IButtonInputs.ButtonCallbackArguments>? callback)) + if (OnPressed.TryGetValue(currentlyPressedKey, out IButtonInputs.InputEvent? callback)) callback?.Invoke(this, new(currentlyPressedKey)); OnAnyButtonPressed?.Invoke(this, new(currentlyPressedKey)); @@ -82,7 +82,7 @@ public class KeyboardInputsBehaviour : Behaviour, IButtonInputs, IUpdate if (IsPressed(previouslyPressedKey)) continue; - if (OnReleased.TryGetValue(previouslyPressedKey, out Event, IButtonInputs.ButtonCallbackArguments>? callback)) + if (OnReleased.TryGetValue(previouslyPressedKey, out IButtonInputs.InputEvent? callback)) callback?.Invoke(this, new(previouslyPressedKey)); OnAnyButtonReleased?.Invoke(this, new(previouslyPressedKey)); diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/LoadContentManager.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/LoadContentManager.cs new file mode 100644 index 0000000..18fd80c --- /dev/null +++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/LoadContentManager.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; + +using Engine.Core; + +namespace Engine.Integration.MonoGame; + +public class LoadContentManager : Behaviour, IFirstFrameUpdate +{ + // We use Ascending order because we are using reverse for loop to call them + private static Comparer SortByAscendingPriority() => Comparer.Create((x, y) => x.CompareTo(y)); + private static System.Func GetPriority() => (b) => b.Priority; + + private readonly ActiveBehaviourCollectorOrdered loadContents = new(GetPriority(), SortByAscendingPriority()); + private readonly List toCallLoadContents = new(32); + + private MonoGameWindowContainer monoGameWindowContainer = null!; + + public void FirstActiveFrame() + { + monoGameWindowContainer = Universe.FindRequiredBehaviour(); + } + + protected override void OnEnteredUniverse(IUniverse universe) + { + loadContents.Assign(universe); + + universe.OnPreUpdate.AddListener(OnPreUpdate); + } + + protected override void OnExitedUniverse(IUniverse universe) + { + loadContents.Unassign(); + + universe.OnPreUpdate.RemoveListener(OnPreUpdate); + } + + private void OnPreUpdate(IUniverse sender, IUniverse.UpdateArguments args) + { + for (int i = toCallLoadContents.Count - 1; i >= 0; i--) + { + toCallLoadContents[i].LoadContent(monoGameWindowContainer.Window.Content); + toCallLoadContents.RemoveAt(i); + } + } + + private void OnFirstFrameCollected(IBehaviourCollector sender, IBehaviourCollector.BehaviourCollectedArguments args) + { + toCallLoadContents.Add(args.BehaviourCollected); + } + + public LoadContentManager() + { + loadContents.OnCollected.AddListener(OnFirstFrameCollected); + } +} diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2DBehaviour.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs similarity index 86% rename from Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2DBehaviour.cs rename to Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs index 553c843..0f0dc85 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2DBehaviour.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/MonoGameCamera2D.cs @@ -1,11 +1,11 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; -public class MonoGameCamera2DBehaviour : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPreDraw +public class MonoGameCamera2D : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPreDraw { public event MatrixTransformChangedArguments? OnMatrixTransformChanged = null; public event ViewportChangedArguments? OnViewportChanged = null; @@ -56,7 +56,7 @@ public class MonoGameCamera2DBehaviour : BehaviourBase, ICamera2D, IFirstFrameUp get => _zoom; set { - float newValue = Syntriax.Engine.Core.Math.Max(0.1f, value); + float newValue = Engine.Core.Math.Max(0.1f, value); if (_zoom == newValue) return; @@ -94,7 +94,7 @@ public class MonoGameCamera2DBehaviour : BehaviourBase, ICamera2D, IFirstFrameUp { MatrixTransform = Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) * - Matrix.CreateRotationZ(Rotation * Syntriax.Engine.Core.Math.DegreeToRadian) * + Matrix.CreateRotationZ(Rotation * Engine.Core.Math.DegreeToRadian) * Matrix.CreateScale(Transform.Scale.X.Max(Transform.Scale.Y)) * Matrix.CreateScale(Zoom) * Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f)); @@ -103,7 +103,7 @@ public class MonoGameCamera2DBehaviour : BehaviourBase, ICamera2D, IFirstFrameUp protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour(); protected sealed override void FinalizeInternal() => Transform = null!; - public delegate void MatrixTransformChangedArguments(MonoGameCamera2DBehaviour sender); - public delegate void ViewportChangedArguments(MonoGameCamera2DBehaviour sender); - public delegate void ZoomChangedArguments(MonoGameCamera2DBehaviour sender); + public delegate void MatrixTransformChangedArguments(MonoGameCamera2D sender); + public delegate void ViewportChangedArguments(MonoGameCamera2D sender); + public delegate void ZoomChangedArguments(MonoGameCamera2D sender); } diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/SpriteBatchWrapper.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/SpriteBatchWrapper.cs index 9fa617b..7e40376 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/SpriteBatchWrapper.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/SpriteBatchWrapper.cs @@ -3,9 +3,9 @@ using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public class SpriteBatchWrapper(GraphicsDevice graphicsDevice) : ISpriteBatch { diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/SpriteBatcher.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/SpriteBatcher.cs index 88b13ac..9f39e1d 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/SpriteBatcher.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/SpriteBatcher.cs @@ -1,22 +1,23 @@ using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public class SpriteBatcher : BehaviourBase, IFirstFrameUpdate, IDraw { - private static Comparer SortByPriority() => Comparer.Create((x, y) => y.Priority.CompareTo(x.Priority)); + private static Comparer SortByPriority() => Comparer.Create((x, y) => y.CompareTo(x)); + private static System.Func GetPriority() => (b) => b.Priority; private ISpriteBatch spriteBatch = null!; - private MonoGameCamera2DBehaviour camera2D = null!; + private MonoGameCamera2D camera2D = null!; - private readonly ActiveBehaviourCollectorSorted drawableSprites = new() { SortBy = SortByPriority() }; + private readonly ActiveBehaviourCollectorOrdered drawableSprites = new(GetPriority(), SortByPriority()); public void FirstActiveFrame() { MonoGameWindowContainer windowContainer = Universe.FindRequiredBehaviour(); - camera2D = Universe.FindRequiredBehaviour(); + camera2D = Universe.FindRequiredBehaviour(); spriteBatch = new SpriteBatchWrapper(windowContainer.Window.GraphicsDevice); drawableSprites.Unassign(); diff --git a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/TriangleBatcher.cs b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/TriangleBatcher.cs index 4215384..40827a3 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Behaviours/TriangleBatcher.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/Behaviours/TriangleBatcher.cs @@ -2,22 +2,24 @@ using System.Collections.Generic; using Microsoft.Xna.Framework; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public class TriangleBatcher : BehaviourBase, ITriangleBatch, IFirstFrameUpdate, IDraw { - private static Comparer SortByAscendingPriority() => Comparer.Create((x, y) => x.Priority.CompareTo(y.Priority)); + private static Comparer SortByAscendingPriority() => Comparer.Create((x, y) => x.CompareTo(y)); + private static System.Func GetPriority() => (b) => b.Priority; private TriangleBatch triangleBatch = null!; - private MonoGameCamera2DBehaviour camera2D = null!; - private readonly ActiveBehaviourCollectorSorted drawableShapes = new() { SortBy = SortByAscendingPriority() }; + private MonoGameCamera2D camera2D = null!; + + private readonly ActiveBehaviourCollectorOrdered drawableShapes = new(GetPriority(), SortByAscendingPriority()); public void FirstActiveFrame() { MonoGameWindowContainer windowContainer = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour(); - camera2D = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour(); + camera2D = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour(); triangleBatch = new(windowContainer.Window.GraphicsDevice); drawableShapes.Unassign(); diff --git a/Engine.Integration/Engine.Integration.MonoGame/Engine.Integration.MonoGame.csproj b/Engine.Integration/Engine.Integration.MonoGame/Engine.Integration.MonoGame.csproj index cd49b65..4f66f9a 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/Engine.Integration.MonoGame.csproj +++ b/Engine.Integration/Engine.Integration.MonoGame/Engine.Integration.MonoGame.csproj @@ -3,7 +3,8 @@ net9.0 disable enable - Syntriax.Engine.Integration.MonoGame + Engine.Integration.MonoGame + Engine.Integration.MonoGame diff --git a/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs b/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs index a5d047d..b1c408f 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/EngineConverter.cs @@ -3,9 +3,9 @@ using System.Runtime.CompilerServices; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public static class EngineConverterExtensions { @@ -18,7 +18,16 @@ public static class EngineConverterExtensions public static Vector2D ToVector2D(this Vector2 vector) => new(vector.X, vector.Y); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Color ToColor(this ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B, rgba.A); + public static Color ToColor(this ColorRGBA color) => new(color.R, color.G, color.B, color.A); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Color ToPreMultipliedColor(this ColorRGBA color) + { + float alphaMultiplier = color.A / 255f; + return new((byte)(color.R * alphaMultiplier), (byte)(color.G * alphaMultiplier), (byte)(color.B * alphaMultiplier), color.A); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ColorRGBA ToColorRGBA(this Color color) => new(color.R, color.G, color.B, color.A); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2D ToVector2D(this Point point) => new(point.X, point.Y); diff --git a/Engine.Integration/Engine.Integration.MonoGame/MonoGameWindow.cs b/Engine.Integration/Engine.Integration.MonoGame/MonoGameWindow.cs index 62e9ae5..7decf16 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/MonoGameWindow.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/MonoGameWindow.cs @@ -1,9 +1,9 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public class MonoGameWindow : Game { @@ -21,6 +21,9 @@ public class MonoGameWindow : Game Universe.InstantiateUniverseObject().SetUniverseObject("Window Container") .BehaviourController.AddBehaviour(this); + + Universe.InstantiateUniverseObject().SetUniverseObject("Content Loader") + .BehaviourController.AddBehaviour(); } protected override void Initialize() diff --git a/Engine.Integration/Engine.Integration.MonoGame/TriangleBatch.cs b/Engine.Integration/Engine.Integration.MonoGame/TriangleBatch.cs index a03c852..fd98064 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/TriangleBatch.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/TriangleBatch.cs @@ -1,9 +1,9 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; public class TriangleBatch : ITriangleBatch { @@ -14,13 +14,14 @@ public class TriangleBatch : ITriangleBatch private Matrix _view; private Matrix _projection; private readonly BasicEffect basicEffect; + private readonly RasterizerState rasterizerState = new() { CullMode = CullMode.None }; public TriangleBatch(GraphicsDevice graphicsDevice) { this.graphicsDevice = graphicsDevice; - this.graphicsDevice.RasterizerState = new RasterizerState() { CullMode = CullMode.None }; basicEffect = new(graphicsDevice); basicEffect.VertexColorEnabled = true; + vertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionColor), 1024, BufferUsage.WriteOnly); } public void Draw(Triangle triangle, ColorRGBA colorRGBA) @@ -58,9 +59,12 @@ public class TriangleBatch : ITriangleBatch private void Flush() { + if (verticesIndex == 0) + return; + + graphicsDevice.RasterizerState = rasterizerState; basicEffect.Projection = _projection; basicEffect.View = _view; - vertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionColor), 1024, BufferUsage.WriteOnly); vertexBuffer.SetData(vertices); graphicsDevice.SetVertexBuffer(vertexBuffer); diff --git a/Engine.Integration/Engine.Integration.MonoGame/UniverseObjects/MonoGameWindowContainer.cs b/Engine.Integration/Engine.Integration.MonoGame/UniverseObjects/MonoGameWindowContainer.cs index eba7e18..f4b8016 100644 --- a/Engine.Integration/Engine.Integration.MonoGame/UniverseObjects/MonoGameWindowContainer.cs +++ b/Engine.Integration/Engine.Integration.MonoGame/UniverseObjects/MonoGameWindowContainer.cs @@ -1,7 +1,7 @@ -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; -namespace Syntriax.Engine.Integration.MonoGame; +namespace Engine.Integration.MonoGame; [IgnoreSerialization] public class MonoGameWindowContainer(MonoGameWindow GameWindow) : BehaviourBase diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs similarity index 68% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs index e9812d9..873b796 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Abstract/IEngineTypeYamlConverter.cs @@ -1,9 +1,9 @@ -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public interface IEngineTypeYamlConverter : IYamlTypeConverter { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/BehaviourControllerConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourControllerConverter.cs similarity index 96% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/BehaviourControllerConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourControllerConverter.cs index 770f9f1..97ed522 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/BehaviourControllerConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourControllerConverter.cs @@ -2,14 +2,14 @@ using System; using System.Collections.Generic; using System.Linq; -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class BehaviourControllerConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/BehaviourConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourConverter.cs similarity index 96% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/BehaviourConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourConverter.cs index eebfdd1..abc3b97 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/BehaviourConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/BehaviourConverter.cs @@ -1,13 +1,13 @@ using System; -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class BehaviourConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/EngineTypeYamlConverterBase.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/EngineTypeYamlConverterBase.cs similarity index 89% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/EngineTypeYamlConverterBase.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/EngineTypeYamlConverterBase.cs index 4e8d731..134e922 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/EngineTypeYamlConverterBase.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/EngineTypeYamlConverterBase.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; using YamlDotNet.Core; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public abstract class EngineTypeYamlSerializerBase : IEngineTypeYamlConverter { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/AABBConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/AABBConverter.cs similarity index 95% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/AABBConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/AABBConverter.cs index bfc0001..d09150d 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/AABBConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/AABBConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class AABBConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/CircleConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/CircleConverter.cs similarity index 95% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/CircleConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/CircleConverter.cs index 32ad922..d06bc53 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/CircleConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/CircleConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class CircleConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/ColorHSVConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/ColorHSVConverter.cs similarity index 92% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/ColorHSVConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/ColorHSVConverter.cs index b5bb4ca..a03f490 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/ColorHSVConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/ColorHSVConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class ColorHSVConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/ColorRGBAConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/ColorRGBAConverter.cs similarity index 92% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/ColorRGBAConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/ColorRGBAConverter.cs index 8243a0b..50ec7aa 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/ColorRGBAConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/ColorRGBAConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class ColorRGBAConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/ColorRGBConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/ColorRGBConverter.cs similarity index 92% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/ColorRGBConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/ColorRGBConverter.cs index 809db2a..8133885 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/ColorRGBConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/ColorRGBConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class ColorRGBConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Line2DConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Line2DConverter.cs similarity index 94% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Line2DConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Line2DConverter.cs index 85551c0..1d45331 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Line2DConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Line2DConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class Line2DConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Line2DEquationConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Line2DEquationConverter.cs similarity index 95% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Line2DEquationConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Line2DEquationConverter.cs index da70ea2..ed13cc4 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Line2DEquationConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Line2DEquationConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class Line2DEquationConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Projection1DConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Projection1DConverter.cs similarity index 95% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Projection1DConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Projection1DConverter.cs index 9bad9bf..b1ea9fe 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Projection1DConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Projection1DConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class Projection1DConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/QuaternionConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/QuaternionConverter.cs similarity index 93% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/QuaternionConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/QuaternionConverter.cs index 66e2a0d..76317b3 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/QuaternionConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/QuaternionConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class QuaternionConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Shape2DConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Shape2DConverter.cs similarity index 95% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Shape2DConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Shape2DConverter.cs index 239546e..cd88d96 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Shape2DConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Shape2DConverter.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class Shape2DConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/TriangleConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/TriangleConverter.cs similarity index 96% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/TriangleConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/TriangleConverter.cs index d98efbb..c544b37 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/TriangleConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/TriangleConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class TriangleConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Vector2DConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Vector2DConverter.cs similarity index 92% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Vector2DConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Vector2DConverter.cs index 1c1b420..654416c 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Vector2DConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Vector2DConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class Vector2DConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Vector3DConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Vector3DConverter.cs similarity index 92% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Vector3DConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Vector3DConverter.cs index 64f49c2..4bdca07 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/Primitives/Vector3DConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/Primitives/Vector3DConverter.cs @@ -1,12 +1,12 @@ using System; -using Syntriax.Engine.Core; +using Engine.Core; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class Vector3DConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/SerializedClassConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/SerializedClassConverter.cs similarity index 96% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/SerializedClassConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/SerializedClassConverter.cs index 2dd9455..aef2822 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/SerializedClassConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/SerializedClassConverter.cs @@ -2,15 +2,15 @@ using System; using System.Collections.Generic; using System.Linq; -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Factory; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Factory; +using Engine.Core.Serialization; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class SerializedClassConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/StateEnableConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/StateEnableConverter.cs similarity index 95% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/StateEnableConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/StateEnableConverter.cs index 1135bf5..9c317b0 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/StateEnableConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/StateEnableConverter.cs @@ -1,13 +1,13 @@ using System; -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class StateEnableConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/TypeContainerConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/TypeContainerConverter.cs similarity index 92% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/TypeContainerConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/TypeContainerConverter.cs index 0b7a665..94f045c 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/TypeContainerConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/TypeContainerConverter.cs @@ -1,13 +1,13 @@ using System; -using Syntriax.Engine.Core.Serialization; -using Syntriax.Engine.Core.Factory; +using Engine.Core.Serialization; +using Engine.Core.Factory; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class TypeContainerConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/UniverseConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseConverter.cs similarity index 97% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/UniverseConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/UniverseConverter.cs index 1fbf978..6fdf87e 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/UniverseConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseConverter.cs @@ -2,14 +2,14 @@ using System; using System.Collections.Generic; using System.Linq; -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class UniverseConverter : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Converters/UniverseObjectConverter.cs b/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseObjectConverter.cs similarity index 97% rename from Engine.Serializers/Engine.Serializers.Yaml/Converters/UniverseObjectConverter.cs rename to Engine.Integration/Engine.Integration.Yaml/Converters/UniverseObjectConverter.cs index 527e2b0..76268fe 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Converters/UniverseObjectConverter.cs +++ b/Engine.Integration/Engine.Integration.Yaml/Converters/UniverseObjectConverter.cs @@ -2,14 +2,14 @@ using System; using System.Collections.Generic; using System.Linq; -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class UniverseObjectSerializer : EngineTypeYamlSerializerBase { diff --git a/Engine.Serializers/Engine.Serializers.Yaml/Engine.Serializers.Yaml.csproj b/Engine.Integration/Engine.Integration.Yaml/Engine.Integration.Yaml.csproj similarity index 76% rename from Engine.Serializers/Engine.Serializers.Yaml/Engine.Serializers.Yaml.csproj rename to Engine.Integration/Engine.Integration.Yaml/Engine.Integration.Yaml.csproj index c56d15f..4878b8e 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/Engine.Serializers.Yaml.csproj +++ b/Engine.Integration/Engine.Integration.Yaml/Engine.Integration.Yaml.csproj @@ -4,7 +4,8 @@ net9.0 disable enable - Syntriax.Engine.Serializers.Yaml + Engine.Integration.Yaml + Engine.Integration.Yaml diff --git a/Engine.Serializers/Engine.Serializers.Yaml/SerializerInProgressException.cs b/Engine.Integration/Engine.Integration.Yaml/SerializerInProgressException.cs similarity index 76% rename from Engine.Serializers/Engine.Serializers.Yaml/SerializerInProgressException.cs rename to Engine.Integration/Engine.Integration.Yaml/SerializerInProgressException.cs index 2b03c0b..7ca2cc3 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/SerializerInProgressException.cs +++ b/Engine.Integration/Engine.Integration.Yaml/SerializerInProgressException.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; [Serializable] public class SerializerInProgressException() : Exception("There's already a running deserialization in progress."); diff --git a/Engine.Serializers/Engine.Serializers.Yaml/YamlSerializer.cs b/Engine.Integration/Engine.Integration.Yaml/YamlSerializer.cs similarity index 97% rename from Engine.Serializers/Engine.Serializers.Yaml/YamlSerializer.cs rename to Engine.Integration/Engine.Integration.Yaml/YamlSerializer.cs index 15d5f2f..1dfadd1 100644 --- a/Engine.Serializers/Engine.Serializers.Yaml/YamlSerializer.cs +++ b/Engine.Integration/Engine.Integration.Yaml/YamlSerializer.cs @@ -4,13 +4,13 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; -namespace Syntriax.Engine.Serializers.Yaml; +namespace Engine.Serializers.Yaml; public class YamlSerializer : Core.Serialization.ISerializer { diff --git a/Engine.Integration/YamlDotNet b/Engine.Integration/YamlDotNet new file mode 160000 index 0000000..b8ac2a9 --- /dev/null +++ b/Engine.Integration/YamlDotNet @@ -0,0 +1 @@ +Subproject commit b8ac2a98ffcc12434eff6c6abb75b38ad1b1ab04 diff --git a/Engine.Physics2D/Abstract/ICircleCollider2D.cs b/Engine.Physics2D/Abstract/ICircleCollider2D.cs index fff33bd..3384f1c 100644 --- a/Engine.Physics2D/Abstract/ICircleCollider2D.cs +++ b/Engine.Physics2D/Abstract/ICircleCollider2D.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a with the shape of a . diff --git a/Engine.Physics2D/Abstract/ICollider2D.cs b/Engine.Physics2D/Abstract/ICollider2D.cs index fdc1ffd..38f0c75 100644 --- a/Engine.Physics2D/Abstract/ICollider2D.cs +++ b/Engine.Physics2D/Abstract/ICollider2D.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a 2D collider. diff --git a/Engine.Physics2D/Abstract/ICollisionDetector2D.cs b/Engine.Physics2D/Abstract/ICollisionDetector2D.cs index ed6e307..9f11739 100644 --- a/Engine.Physics2D/Abstract/ICollisionDetector2D.cs +++ b/Engine.Physics2D/Abstract/ICollisionDetector2D.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a 2D collision detector. diff --git a/Engine.Physics2D/Abstract/ICollisionResolver2D.cs b/Engine.Physics2D/Abstract/ICollisionResolver2D.cs index e5b10ff..9d7851f 100644 --- a/Engine.Physics2D/Abstract/ICollisionResolver2D.cs +++ b/Engine.Physics2D/Abstract/ICollisionResolver2D.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a 2D collision resolver. diff --git a/Engine.Physics2D/Abstract/IPhysicsEngine2D.cs b/Engine.Physics2D/Abstract/IPhysicsEngine2D.cs index 323a31e..f281349 100644 --- a/Engine.Physics2D/Abstract/IPhysicsEngine2D.cs +++ b/Engine.Physics2D/Abstract/IPhysicsEngine2D.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a 2D physics engine. diff --git a/Engine.Physics2D/Abstract/IPhysicsMaterial2D.cs b/Engine.Physics2D/Abstract/IPhysicsMaterial2D.cs index 4bfc716..1605eb3 100644 --- a/Engine.Physics2D/Abstract/IPhysicsMaterial2D.cs +++ b/Engine.Physics2D/Abstract/IPhysicsMaterial2D.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a 2D physics object's responsive attributes. diff --git a/Engine.Physics2D/Abstract/IRaycastResolver2D.cs b/Engine.Physics2D/Abstract/IRaycastResolver2D.cs index 28fc459..190d5d1 100644 --- a/Engine.Physics2D/Abstract/IRaycastResolver2D.cs +++ b/Engine.Physics2D/Abstract/IRaycastResolver2D.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a 2D raycast resolver. diff --git a/Engine.Physics2D/Abstract/IRigidBody2D.cs b/Engine.Physics2D/Abstract/IRigidBody2D.cs index b7292cd..871fc0e 100644 --- a/Engine.Physics2D/Abstract/IRigidBody2D.cs +++ b/Engine.Physics2D/Abstract/IRigidBody2D.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a 2D rigid body in the engine. diff --git a/Engine.Physics2D/Abstract/IShapeCollider2D.cs b/Engine.Physics2D/Abstract/IShapeCollider2D.cs index 925751a..99a3802 100644 --- a/Engine.Physics2D/Abstract/IShapeCollider2D.cs +++ b/Engine.Physics2D/Abstract/IShapeCollider2D.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a with a custom . diff --git a/Engine.Physics2D/Abstract/RaycastResult.cs b/Engine.Physics2D/Abstract/RaycastResult.cs index 55a56f2..2e6f236 100644 --- a/Engine.Physics2D/Abstract/RaycastResult.cs +++ b/Engine.Physics2D/Abstract/RaycastResult.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public readonly struct RaycastResult(Ray2D ray, ICollider2D collider2D, Vector2D position, Vector2D normal) { diff --git a/Engine.Physics2D/Abstract/Updates/IPhysicsIteration.cs b/Engine.Physics2D/Abstract/Updates/IPhysicsIteration.cs index 103837a..c1b3737 100644 --- a/Engine.Physics2D/Abstract/Updates/IPhysicsIteration.cs +++ b/Engine.Physics2D/Abstract/Updates/IPhysicsIteration.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a that listens to physics simulation update phase. diff --git a/Engine.Physics2D/Abstract/Updates/IPhysicsUpdate.cs b/Engine.Physics2D/Abstract/Updates/IPhysicsUpdate.cs index f447dfa..b596965 100644 --- a/Engine.Physics2D/Abstract/Updates/IPhysicsUpdate.cs +++ b/Engine.Physics2D/Abstract/Updates/IPhysicsUpdate.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a that listens to physics simulation update phase. diff --git a/Engine.Physics2D/Abstract/Updates/IPostPhysicsUpdate.cs b/Engine.Physics2D/Abstract/Updates/IPostPhysicsUpdate.cs index e6edb47..31e74ba 100644 --- a/Engine.Physics2D/Abstract/Updates/IPostPhysicsUpdate.cs +++ b/Engine.Physics2D/Abstract/Updates/IPostPhysicsUpdate.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a that listens to the phase after the physics simulation phase. diff --git a/Engine.Physics2D/Abstract/Updates/IPrePhysicsUpdate.cs b/Engine.Physics2D/Abstract/Updates/IPrePhysicsUpdate.cs index 4de7d66..dfd9f7c 100644 --- a/Engine.Physics2D/Abstract/Updates/IPrePhysicsUpdate.cs +++ b/Engine.Physics2D/Abstract/Updates/IPrePhysicsUpdate.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; /// /// Represents a that listens to the phase before the physics simulation phase. diff --git a/Engine.Physics2D/Collider2DBehaviourBase.cs b/Engine.Physics2D/Collider2DBase.cs similarity index 96% rename from Engine.Physics2D/Collider2DBehaviourBase.cs rename to Engine.Physics2D/Collider2DBase.cs index 209d860..b0aef94 100644 --- a/Engine.Physics2D/Collider2DBehaviourBase.cs +++ b/Engine.Physics2D/Collider2DBase.cs @@ -1,8 +1,8 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; -public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D +public abstract class Collider2DBase : Behaviour2D, ICollider2D { public Event OnCollisionDetected { get; } = new(); public Event OnCollisionResolved { get; } = new(); @@ -18,7 +18,7 @@ public abstract class Collider2DBehaviourBase : Behaviour2D, ICollider2D protected bool NeedsRecalculation { get; set; } = true; protected IRigidBody2D? _rigidBody2D = null; - protected Collider2DBehaviourBase() + protected Collider2DBase() { delegateOnBehaviourAddedToController = OnBehaviourAddedToController; delegateOnBehaviourRemovedFromController = OnBehaviourRemovedFromController; diff --git a/Engine.Physics2D/Collider2DCircleBehaviour.cs b/Engine.Physics2D/Collider2DCircle.cs similarity index 60% rename from Engine.Physics2D/Collider2DCircleBehaviour.cs rename to Engine.Physics2D/Collider2DCircle.cs index 7fb9fb0..8291b08 100644 --- a/Engine.Physics2D/Collider2DCircleBehaviour.cs +++ b/Engine.Physics2D/Collider2DCircle.cs @@ -1,8 +1,8 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; -public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollider2D +public class Collider2DCircle : Collider2DBase, ICircleCollider2D { private Circle _circleLocal = Circle.UnitCircle; @@ -19,6 +19,6 @@ public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollide public override void CalculateCollider() => CircleWorld = Transform.Transform(_circleLocal); - public Collider2DCircleBehaviour() { } - public Collider2DCircleBehaviour(Circle circle) => CircleLocal = circle; + public Collider2DCircle() { } + public Collider2DCircle(Circle circle) => CircleLocal = circle; } diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShape.cs similarity index 60% rename from Engine.Physics2D/Collider2DShapeBehaviour.cs rename to Engine.Physics2D/Collider2DShape.cs index 2ba213e..10113f4 100644 --- a/Engine.Physics2D/Collider2DShapeBehaviour.cs +++ b/Engine.Physics2D/Collider2DShape.cs @@ -1,8 +1,8 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; -public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2D +public class Collider2DShape : Collider2DBase, IShapeCollider2D { public Shape2D ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; } public Shape2D ShapeLocal @@ -15,13 +15,13 @@ public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2 } } - private Shape2D _shapeWorld = Shape2D.Square.CreateCopy(); + private Shape2D _shapeWorld = Shape2D.Square; private Shape2D _shapeLocal = Shape2D.Square; public override void CalculateCollider() => ShapeLocal.Transform(Transform, _shapeWorld); - public Collider2DShapeBehaviour() { } - public Collider2DShapeBehaviour(Shape2D shape) + public Collider2DShape() { } + public Collider2DShape(Shape2D shape) { ShapeLocal = shape; } diff --git a/Engine.Physics2D/CollisionDetectionInformation.cs b/Engine.Physics2D/CollisionDetectionInformation.cs index 6476df0..62e564a 100644 --- a/Engine.Physics2D/CollisionDetectionInformation.cs +++ b/Engine.Physics2D/CollisionDetectionInformation.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; [System.Diagnostics.DebuggerDisplay("Normal: {Normal.ToString(), nq}, Penetration: {Penetration}")] public readonly struct CollisionDetectionInformation diff --git a/Engine.Physics2D/CollisionDetector2D.cs b/Engine.Physics2D/CollisionDetector2D.cs index 8b1508b..760c086 100644 --- a/Engine.Physics2D/CollisionDetector2D.cs +++ b/Engine.Physics2D/CollisionDetector2D.cs @@ -1,7 +1,7 @@ -using Syntriax.Engine.Core; -using Syntriax.Engine.Physics2D; +using Engine.Core; +using Engine.Physics2D; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public class CollisionDetector2D : ICollisionDetector2D { diff --git a/Engine.Physics2D/CollisionResolver2D.cs b/Engine.Physics2D/CollisionResolver2D.cs index 4918a59..90341be 100644 --- a/Engine.Physics2D/CollisionResolver2D.cs +++ b/Engine.Physics2D/CollisionResolver2D.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public class CollisionResolver2D : ICollisionResolver2D { diff --git a/Engine.Physics2D/Engine.Physics2D.csproj b/Engine.Physics2D/Engine.Physics2D.csproj index ba7ce86..7119153 100644 --- a/Engine.Physics2D/Engine.Physics2D.csproj +++ b/Engine.Physics2D/Engine.Physics2D.csproj @@ -4,7 +4,8 @@ net9.0 disable enable - Syntriax.Engine.Physics2D + Engine.Physics2D + Engine.Physics2D diff --git a/Engine.Physics2D/Physics2D.cs b/Engine.Physics2D/Physics2D.cs index edb3335..1dd6b0b 100644 --- a/Engine.Physics2D/Physics2D.cs +++ b/Engine.Physics2D/Physics2D.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public static class Physics2D { diff --git a/Engine.Physics2D/PhysicsCoroutineManager.cs b/Engine.Physics2D/PhysicsCoroutineManager.cs index 47fb742..573a5d4 100644 --- a/Engine.Physics2D/PhysicsCoroutineManager.cs +++ b/Engine.Physics2D/PhysicsCoroutineManager.cs @@ -1,9 +1,9 @@ using System.Collections; using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public class PhysicsCoroutineManager : Behaviour, IPhysicsUpdate { diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index 4e84b5b..d2071c3 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public class PhysicsEngine2D : Behaviour, IPreUpdate, IPhysicsEngine2D { @@ -17,19 +17,20 @@ public class PhysicsEngine2D : Behaviour, IPreUpdate, IPhysicsEngine2D protected readonly ICollisionResolver2D collisionResolver = null!; protected readonly IRaycastResolver2D raycastResolver = null!; - private static Comparer SortByPriority() => Comparer.Create((x, y) => y.Priority.CompareTo(x.Priority)); - protected ActiveBehaviourCollectorSorted physicsPreUpdateCollector = new() { SortBy = SortByPriority() }; - protected ActiveBehaviourCollectorSorted physicsUpdateCollector = new() { SortBy = SortByPriority() }; - protected ActiveBehaviourCollectorSorted physicsIterationCollector = new() { SortBy = SortByPriority() }; - protected ActiveBehaviourCollectorSorted physicsPostUpdateCollector = new() { SortBy = SortByPriority() }; + private static Comparer SortByPriority() => Comparer.Create((x, y) => y.CompareTo(x)); + private static System.Func GetPriority() => (b) => b.Priority; + protected ActiveBehaviourCollectorOrdered physicsPreUpdateCollector = new(GetPriority(), SortByPriority()); + protected ActiveBehaviourCollectorOrdered physicsUpdateCollector = new(GetPriority(), SortByPriority()); + protected ActiveBehaviourCollectorOrdered physicsIterationCollector = new(GetPriority(), SortByPriority()); + protected ActiveBehaviourCollectorOrdered physicsPostUpdateCollector = new(GetPriority(), SortByPriority()); protected BehaviourCollector rigidBodyCollector = new(); protected BehaviourCollector colliderCollector = new(); - private readonly ListPool colliderPool = new(() => new(32)); - private readonly ListPool prePhysicsUpdatePool = new(() => new(32)); - private readonly ListPool physicsUpdatePool = new(() => new(32)); - private readonly ListPool physicsIterationPool = new(() => new(32)); - private readonly ListPool postPhysicsUpdatePool = new(() => new(32)); + private readonly ListPool colliderPool = new(); + private readonly ListPool prePhysicsUpdatePool = new(); + private readonly ListPool physicsUpdatePool = new(); + private readonly ListPool physicsIterationPool = new(); + private readonly ListPool postPhysicsUpdatePool = new(); public int IterationPerStep { get => _iterationPerStep; set => _iterationPerStep = value < 1 ? 1 : value; } public float IterationPeriod { get => _iterationPeriod; set => _iterationPeriod = value.Max(0.0001f); } diff --git a/Engine.Physics2D/PhysicsEngine2DStandalone.cs b/Engine.Physics2D/PhysicsEngine2DStandalone.cs index dc16d4c..11c313c 100644 --- a/Engine.Physics2D/PhysicsEngine2DStandalone.cs +++ b/Engine.Physics2D/PhysicsEngine2DStandalone.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public class PhysicsEngine2DStandalone : IPhysicsEngine2D { @@ -21,6 +21,12 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D private readonly ICollisionResolver2D collisionResolver = null!; private readonly IRaycastResolver2D raycastResolver = null!; + private readonly ListPool colliderPool = new(); + private readonly ListPool prePhysicsUpdatePool = new(); + private readonly ListPool physicsUpdatePool = new(); + private readonly ListPool physicsIterationPool = new(); + private readonly ListPool postPhysicsUpdatePool = new(); + public int IterationPerStep { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; } public void AddRigidBody(IRigidBody2D rigidBody) @@ -112,11 +118,11 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D { float intervalDeltaTime = deltaTime / IterationPerStep; - List childColliders = []; - List physicsPreUpdates = []; - List physicsUpdates = []; - List physicsIterations = []; - List physicsPostUpdates = []; + List childColliders = colliderPool.Get(); + List physicsPreUpdates = prePhysicsUpdatePool.Get(); + List physicsUpdates = physicsUpdatePool.Get(); + List physicsIterations = physicsIterationPool.Get(); + List physicsPostUpdates = postPhysicsUpdatePool.Get(); rigidBody.BehaviourController.GetBehavioursInChildren(childColliders); rigidBody.BehaviourController.GetBehavioursInChildren(physicsPreUpdates); @@ -160,6 +166,12 @@ public class PhysicsEngine2DStandalone : IPhysicsEngine2D for (int i = physicsPostUpdates.Count - 1; i >= 0; i--) physicsPostUpdates[i].PostPhysicsUpdate(deltaTime); + + colliderPool.Return(childColliders); + prePhysicsUpdatePool.Return(physicsPreUpdates); + physicsUpdatePool.Return(physicsUpdates); + physicsIterationPool.Return(physicsIterations); + postPhysicsUpdatePool.Return(physicsPostUpdates); } private void ResolveColliders(ICollider2D colliderX, ICollider2D colliderY) diff --git a/Engine.Physics2D/PhysicsMaterial2D.cs b/Engine.Physics2D/PhysicsMaterial2D.cs index 616c26f..a3875ca 100644 --- a/Engine.Physics2D/PhysicsMaterial2D.cs +++ b/Engine.Physics2D/PhysicsMaterial2D.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public readonly struct PhysicsMaterial2D(float Friction, float Restitution) : IPhysicsMaterial2D { diff --git a/Engine.Physics2D/PhysicsMaterial2DDefault.cs b/Engine.Physics2D/PhysicsMaterial2DDefault.cs index faf0e31..1ed446e 100644 --- a/Engine.Physics2D/PhysicsMaterial2DDefault.cs +++ b/Engine.Physics2D/PhysicsMaterial2DDefault.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public readonly struct PhysicsMaterial2DDefault : IPhysicsMaterial2D { diff --git a/Engine.Physics2D/Preserver.cs b/Engine.Physics2D/Preserver.cs index be2dfe1..a446f3f 100644 --- a/Engine.Physics2D/Preserver.cs +++ b/Engine.Physics2D/Preserver.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Physics2D +namespace Engine.Physics2D { // This is pretty much so the assembly gets loaded automatically because // the builds include the assembly but sometimes doesn't link load it at startup. diff --git a/Engine.Physics2D/RaycastResolver2D.cs b/Engine.Physics2D/RaycastResolver2D.cs index 881e4ad..e933e66 100644 --- a/Engine.Physics2D/RaycastResolver2D.cs +++ b/Engine.Physics2D/RaycastResolver2D.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public class RaycastResolver2D : IRaycastResolver2D { - private readonly ListPool lineCacheQueue = new(() => new(4)); + private readonly ListPool lineCacheQueue = new(initialListCapacity: 4); RaycastResult? IRaycastResolver2D.RaycastAgainst(T shape, Ray2D ray, float length) { diff --git a/Engine.Physics2D/RigidBody2D.cs b/Engine.Physics2D/RigidBody2D.cs index c5584eb..8d9c1f9 100644 --- a/Engine.Physics2D/RigidBody2D.cs +++ b/Engine.Physics2D/RigidBody2D.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Physics2D; +namespace Engine.Physics2D; public class RigidBody2D : Behaviour2D, IRigidBody2D { diff --git a/Engine.Systems/Engine.Systems.csproj b/Engine.Systems/Engine.Systems.csproj index 967f31f..11a1757 100644 --- a/Engine.Systems/Engine.Systems.csproj +++ b/Engine.Systems/Engine.Systems.csproj @@ -4,7 +4,8 @@ net9.0 disable enable - Syntriax.Engine.Systems + Engine.Systems + Engine.Systems diff --git a/Engine.Systems/Input/IButtonInputs.cs b/Engine.Systems/Input/IButtonInputs.cs index 75cd678..da41a93 100644 --- a/Engine.Systems/Input/IButtonInputs.cs +++ b/Engine.Systems/Input/IButtonInputs.cs @@ -1,18 +1,20 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Input; +namespace Engine.Systems.Input; public interface IButtonInputs : IHasStateEnable { - Event, ButtonCallbackArguments> OnAnyButtonPressed { get; } - Event, ButtonCallbackArguments> OnAnyButtonReleased { get; } + InputEvent OnAnyButtonPressed { get; } + InputEvent OnAnyButtonReleased { get; } - void RegisterOnPress(T button, Event, ButtonCallbackArguments>.EventHandler callback); - void UnregisterOnPress(T button, Event, ButtonCallbackArguments>.EventHandler callback); - void RegisterOnRelease(T button, Event, ButtonCallbackArguments>.EventHandler callback); - void UnregisterOnRelease(T button, Event, ButtonCallbackArguments>.EventHandler callback); + void RegisterOnPress(T button, InputEvent.EventHandler callback); + void UnregisterOnPress(T button, InputEvent.EventHandler callback); + void RegisterOnRelease(T button, InputEvent.EventHandler callback); + void UnregisterOnRelease(T button, InputEvent.EventHandler callback); bool IsPressed(T button); readonly record struct ButtonCallbackArguments(T Button); + + class InputEvent : Event, ButtonCallbackArguments>; } diff --git a/Engine.Systems/Network/Abstract/IConnection.cs b/Engine.Systems/Network/Abstract/IConnection.cs new file mode 100644 index 0000000..f43a87c --- /dev/null +++ b/Engine.Systems/Network/Abstract/IConnection.cs @@ -0,0 +1,12 @@ +namespace Engine.Systems.Network; + +public interface IConnection +{ + string Id { get; } + + float Ping { get; } + float RoundTrip { get; } + + int PingMs { get; } + int RoundTripMs { get; } +} diff --git a/Engine.Systems/Network/Abstract/IEntityNetworkPacket.cs b/Engine.Systems/Network/Abstract/IEntityNetworkPacket.cs new file mode 100644 index 0000000..5f05e16 --- /dev/null +++ b/Engine.Systems/Network/Abstract/IEntityNetworkPacket.cs @@ -0,0 +1,6 @@ +namespace Engine.Systems.Network; + +public interface IEntityNetworkPacket : INetworkPacket +{ + string EntityId { get; } +} diff --git a/Engine.Systems/Network/Abstract/INetworkCommunicator.cs b/Engine.Systems/Network/Abstract/INetworkCommunicator.cs new file mode 100644 index 0000000..413eae2 --- /dev/null +++ b/Engine.Systems/Network/Abstract/INetworkCommunicator.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +using Engine.Core; + +namespace Engine.Systems.Network; + +public interface INetworkCommunicator +{ + Event OnConnectionEstablished { get; } + Event OnConnectionAbolished { get; } + + IReadOnlyDictionary Connections { get; } + + INetworkCommunicator Stop(); + + INetworkCommunicator SubscribeToPackets(Event.EventHandler callback); + INetworkCommunicator UnsubscribeFromPackets(Event.EventHandler callback); +} diff --git a/Engine.Systems/Network/Abstract/INetworkCommunicatorClient.cs b/Engine.Systems/Network/Abstract/INetworkCommunicatorClient.cs new file mode 100644 index 0000000..414287c --- /dev/null +++ b/Engine.Systems/Network/Abstract/INetworkCommunicatorClient.cs @@ -0,0 +1,8 @@ +namespace Engine.Systems.Network; + +public interface INetworkCommunicatorClient : INetworkCommunicator +{ + INetworkCommunicatorClient Connect(string address, int port, string? password = null); + + INetworkCommunicatorClient SendToServer(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new(); +} diff --git a/Engine.Systems/Network/Abstract/INetworkCommunicatorServer.cs b/Engine.Systems/Network/Abstract/INetworkCommunicatorServer.cs new file mode 100644 index 0000000..73175d4 --- /dev/null +++ b/Engine.Systems/Network/Abstract/INetworkCommunicatorServer.cs @@ -0,0 +1,13 @@ +namespace Engine.Systems.Network; + +public interface INetworkCommunicatorServer : INetworkCommunicator +{ + string Password { get; } + int MaxConnectionCount { get; } + int Port { get; } + + INetworkCommunicatorServer Start(int port, int maxConnectionCount, string? password = null); + + INetworkCommunicatorServer SendToClient(IConnection connection, T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new(); + INetworkCommunicatorServer SendToAll(T packet, PacketDelivery packetDelivery = PacketDelivery.ReliableInOrder) where T : class, new(); +} diff --git a/Engine.Systems/Network/Abstract/INetworkEntity.cs b/Engine.Systems/Network/Abstract/INetworkEntity.cs new file mode 100644 index 0000000..3baa040 --- /dev/null +++ b/Engine.Systems/Network/Abstract/INetworkEntity.cs @@ -0,0 +1,5 @@ +using Engine.Core; + +namespace Engine.Systems.Network; + +public interface INetworkEntity : IEntity; diff --git a/Engine.Systems/Network/Abstract/INetworkManager.cs b/Engine.Systems/Network/Abstract/INetworkManager.cs new file mode 100644 index 0000000..306f001 --- /dev/null +++ b/Engine.Systems/Network/Abstract/INetworkManager.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +using Engine.Core; + +namespace Engine.Systems.Network; + +public interface INetworkManager +{ + IReadOnlyDictionary NetworkEntities { get; } + IBehaviourCollector NetworkEntityCollector { get; } +} diff --git a/Engine.Systems/Network/Abstract/INetworkPacket.cs b/Engine.Systems/Network/Abstract/INetworkPacket.cs new file mode 100644 index 0000000..25d9438 --- /dev/null +++ b/Engine.Systems/Network/Abstract/INetworkPacket.cs @@ -0,0 +1,3 @@ +namespace Engine.Systems.Network; + +public interface INetworkPacket; diff --git a/Engine.Systems/Network/Abstract/IPacketListenerClient.cs b/Engine.Systems/Network/Abstract/IPacketListenerClient.cs new file mode 100644 index 0000000..39576fa --- /dev/null +++ b/Engine.Systems/Network/Abstract/IPacketListenerClient.cs @@ -0,0 +1,6 @@ +namespace Engine.Systems.Network; + +public interface IPacketListenerClient : INetworkEntity +{ + void OnClientPacketArrived(IConnection sender, T packet); +} diff --git a/Engine.Systems/Network/Abstract/IPacketListenerServer.cs b/Engine.Systems/Network/Abstract/IPacketListenerServer.cs new file mode 100644 index 0000000..6508db4 --- /dev/null +++ b/Engine.Systems/Network/Abstract/IPacketListenerServer.cs @@ -0,0 +1,6 @@ +namespace Engine.Systems.Network; + +public interface IPacketListenerServer : INetworkEntity +{ + void OnServerPacketArrived(IConnection sender, T packet); +} diff --git a/Engine.Systems/Network/Abstract/PacketDelivery.cs b/Engine.Systems/Network/Abstract/PacketDelivery.cs new file mode 100644 index 0000000..a35d9a2 --- /dev/null +++ b/Engine.Systems/Network/Abstract/PacketDelivery.cs @@ -0,0 +1,9 @@ +namespace Engine.Systems.Network; + +public enum PacketDelivery +{ + ReliableInOrder, + ReliableOutOfOrder, + UnreliableInOrder, + UnreliableOutOfOrder, +}; diff --git a/Engine.Systems/Network/NetworkManager.cs b/Engine.Systems/Network/NetworkManager.cs new file mode 100644 index 0000000..53d9c5c --- /dev/null +++ b/Engine.Systems/Network/NetworkManager.cs @@ -0,0 +1,332 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +using Engine.Core; + +namespace Engine.Systems.Network; + +/// +/// Intermediary manager that looks up in it's hierarchy for a to route/broadcast it's received packets to their destinations. +/// +public class NetworkManager : Behaviour, INetworkManager +{ + private readonly Dictionary>> clientPacketArrivalMethods = []; + private readonly Dictionary>> serverPacketArrivalMethods = []; + + private readonly Dictionary> clientPacketRouters = []; + private readonly Dictionary> serverPacketRouters = []; + + private readonly List<(Type packetType, Delegate @delegate)> packetRetrievalDelegates = []; + + private readonly Dictionary clearRoutesMethods = []; + private readonly Dictionary registerPacketListenersMethods = []; + + private readonly Dictionary _networkEntities = []; + public IReadOnlyDictionary NetworkEntities => _networkEntities; + + private readonly BehaviourCollector _networkEntityCollector = new(); + public IBehaviourCollector NetworkEntityCollector => _networkEntityCollector; + + private INetworkCommunicator _networkCommunicator = null!; + public INetworkCommunicator NetworkCommunicator + { + get => _networkCommunicator; + set + { + if (_networkCommunicator == value) + return; + + INetworkCommunicator? previousCommunicator = _networkCommunicator; + _networkCommunicator = value; + + if (previousCommunicator is not null) UnsubscribeCommunicatorMethods(previousCommunicator); + if (_networkCommunicator is not null) SubscribeCommunicatorMethods(_networkCommunicator); + } + } + + #region Communicator Subscriptions + private void SubscribeCommunicatorMethods(INetworkCommunicator networkCommunicator) + { + MethodInfo subscribeToPacketsMethod = typeof(INetworkCommunicator) + .GetMethod(nameof(INetworkCommunicator.SubscribeToPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!; + + foreach ((Type packetType, Delegate @delegate) in packetRetrievalDelegates) + { + MethodInfo genericSubscribeMethod = subscribeToPacketsMethod.MakeGenericMethod(packetType); + genericSubscribeMethod.Invoke(networkCommunicator, [@delegate]); + } + } + + private void UnsubscribeCommunicatorMethods(INetworkCommunicator networkCommunicator) + { + MethodInfo unsubscribeFromPacketsMethod = typeof(INetworkCommunicator) + .GetMethod(nameof(INetworkCommunicator.UnsubscribeFromPackets), BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)!; + + foreach ((Type packetType, Delegate @delegate) in packetRetrievalDelegates) + { + MethodInfo genericUnsubscribeMethod = unsubscribeFromPacketsMethod.MakeGenericMethod(packetType); + genericUnsubscribeMethod.Invoke(networkCommunicator, [@delegate]); + } + } + #endregion + + #region Packet Routing + private void OnPacketReceived(IConnection sender, T entityDataPacket) + { + if (entityDataPacket is IEntityNetworkPacket entityPacket) + RoutePacket(sender, entityDataPacket, entityPacket); + else + BroadcastPacket(sender, entityDataPacket); + } + + private void RoutePacket(IConnection sender, T entityDataPacket, IEntityNetworkPacket entityPacket) + { + if (NetworkCommunicator is INetworkCommunicatorClient) + RoutePacket(clientPacketRouters, entityPacket.EntityId, sender, entityDataPacket); + if (NetworkCommunicator is INetworkCommunicatorServer) + RoutePacket(serverPacketRouters, entityPacket.EntityId, sender, entityDataPacket); + } + + private void BroadcastPacket(IConnection sender, T entityDataPacket) + { + if (NetworkCommunicator is INetworkCommunicatorClient) + BroadcastPacket(clientPacketRouters, sender, entityDataPacket); + if (NetworkCommunicator is INetworkCommunicatorServer) + BroadcastPacket(serverPacketRouters, sender, entityDataPacket); + } + + private static void BroadcastPacket( + Dictionary> packetRouters, + IConnection sender, + T entityDataPacket) + { + if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary? routers)) + return; + + foreach ((string behaviourId, object routerEventReference) in routers) + { + Event routerEvent = (Event)routerEventReference; + routerEvent.Invoke(sender, entityDataPacket!); + } + } + + private static void RoutePacket( + Dictionary> packetRouters, + string entityId, + IConnection sender, + T entityDataPacket) + { + if (!packetRouters.TryGetValue(entityDataPacket!.GetType(), out Dictionary? routers)) + return; + + if (!routers.TryGetValue(entityId, out object? routerEventReference)) + return; + + Event routerEvent = (Event)routerEventReference; + routerEvent.Invoke(sender, entityDataPacket!); + } + #endregion + + #region Packet Routers + private void RegisterPacketRoutersFor( + INetworkEntity behaviour, + Dictionary> packetRouters, + Dictionary>> packetArrivalMethods, + NetworkType networkType) + { + if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary>? arrivalMethods)) + return; + + foreach ((Type packetType, List methods) in arrivalMethods) + foreach (MethodInfo receiveMethod in methods) + { + if (!packetRouters.TryGetValue(packetType, out Dictionary? routers)) + { + routers = []; + packetRouters.Add(packetType, routers); + } + + object packetListenerEvent = CreateEventAndRegister(packetType, behaviour, networkType); + routers.Add(behaviour.Id, packetListenerEvent); + } + } + + private object CreateEventAndRegister(Type packetType, INetworkEntity behaviour, NetworkType networkType) + { + Type genericEventType = typeof(Event<,>).MakeGenericType(typeof(IConnection), packetType); + object packetListenerEvent = Activator.CreateInstance(genericEventType)!; + + if (!registerPacketListenersMethods.TryGetValue(packetType, out MethodInfo? registerPacketListenerMethod)) + throw new($"{nameof(RegisterPacketListenerEvent)} for {packetType.Name} has not been cached."); + + registerPacketListenerMethod.Invoke(this, [behaviour, packetListenerEvent, networkType]); + return packetListenerEvent; + } + + private static void RegisterPacketListenerEvent( + INetworkEntity behaviour, + Event packetListenerEvent, + NetworkType networkType) + { + switch (networkType) + { + case NetworkType.Client: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerClient)behaviour).OnClientPacketArrived(sender, packet)); break; + case NetworkType.Server: packetListenerEvent.AddListener((sender, packet) => ((IPacketListenerServer)behaviour).OnServerPacketArrived(sender, packet)); break; + } + } + + private void UnregisterPacketRoutersFor( + INetworkEntity behaviour, + Dictionary> packetRouters, + Dictionary>> packetArrivalMethods) + { + if (!packetArrivalMethods.TryGetValue(behaviour.GetType(), out Dictionary>? arrivalMethods)) + return; + + foreach ((Type packetType, List methods) in arrivalMethods) + { + if (!packetRouters.TryGetValue(packetType, out Dictionary? routers)) + continue; + + if (!routers.TryGetValue(behaviour.Id, out object? routerEventReference)) + continue; + + if (!clearRoutesMethods.TryGetValue(packetType, out MethodInfo? clearRouterMethod)) + continue; + + clearRouterMethod.Invoke(this, [routerEventReference]); + } + } + + private static void ClearRouter(object routerEventReference) + { + Event routerEvent = (Event)routerEventReference; + routerEvent.Clear(); + } + + #endregion + + #region Engine Callbacks + private void OnCollected(IBehaviourCollector sender, IBehaviourCollector.BehaviourCollectedArguments args) + { + INetworkEntity collectedBehaviour = args.BehaviourCollected; + + if (!_networkEntities.TryAdd(collectedBehaviour.Id, collectedBehaviour)) + throw new($"Unable to add {collectedBehaviour.Id} to {nameof(NetworkManager)}"); + + RegisterPacketRoutersFor(collectedBehaviour, clientPacketRouters, clientPacketArrivalMethods, NetworkType.Client); + RegisterPacketRoutersFor(collectedBehaviour, serverPacketRouters, serverPacketArrivalMethods, NetworkType.Server); + } + + private void OnRemoved(IBehaviourCollector sender, IBehaviourCollector.BehaviourRemovedArguments args) + { + INetworkEntity removedBehaviour = args.BehaviourRemoved; + if (!_networkEntities.Remove(args.BehaviourRemoved.Id)) + return; + + UnregisterPacketRoutersFor(removedBehaviour, clientPacketRouters, clientPacketArrivalMethods); + UnregisterPacketRoutersFor(removedBehaviour, serverPacketRouters, serverPacketArrivalMethods); + } + + protected override void OnExitedUniverse(IUniverse universe) => _networkEntityCollector.Unassign(); + protected override void OnEnteredUniverse(IUniverse universe) + { + _networkEntityCollector.Assign(universe); + NetworkCommunicator = BehaviourController.GetRequiredBehaviourInParent(); + } + #endregion + + #region Initialization + public NetworkManager() + { + CachePacketRetrievalDelegates(); + CacheRegistrationMethods(); + CachePacketArrivalMethods(); + + _networkEntityCollector.OnCollected.AddListener(OnCollected); + _networkEntityCollector.OnRemoved.AddListener(OnRemoved); + } + + private void CachePacketRetrievalDelegates() + { + IEnumerable packetTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()) + .Where(t => typeof(INetworkPacket).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && !t.IsGenericType); + + MethodInfo onPacketArrivedMethod = GetType() + .GetMethod(nameof(OnPacketReceived), BindingFlags.NonPublic | BindingFlags.Instance)!; + + foreach (Type packetType in packetTypes) + { + MethodInfo genericOnPacketArrivedMethod = onPacketArrivedMethod.MakeGenericMethod(packetType); + + Type genericDelegateType = typeof(Event<,>.EventHandler).MakeGenericType(typeof(IConnection), packetType); + Delegate genericPacketReceivedDelegate = Delegate.CreateDelegate(genericDelegateType, this, genericOnPacketArrivedMethod); + + packetRetrievalDelegates.Add((packetType, genericPacketReceivedDelegate)); + } + } + + private void CacheRegistrationMethods() + { + CacheRegistrationMethods(registerPacketListenersMethods, nameof(RegisterPacketListenerEvent)); + CacheRegistrationMethods(clearRoutesMethods, nameof(ClearRouter)); + } + + private void CacheRegistrationMethods(Dictionary registrationMethods, string methodName) + { + MethodInfo registerPacketMethod = typeof(NetworkManager).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static)!; + foreach ((Type packetType, Delegate @delegate) in packetRetrievalDelegates) + { + MethodInfo genericMethod = registerPacketMethod.MakeGenericMethod(packetType); + registrationMethods.Add(packetType, genericMethod); + } + } + + private void CachePacketArrivalMethods() + { + CachePacketArrivalMethods(clientPacketArrivalMethods, typeof(IPacketListenerClient<>), nameof(IPacketListenerClient.OnClientPacketArrived)); + CachePacketArrivalMethods(serverPacketArrivalMethods, typeof(IPacketListenerServer<>), nameof(IPacketListenerServer.OnServerPacketArrived)); + } + + private static void CachePacketArrivalMethods(Dictionary>> packetArrivalMethods, Type listenerType, string packetArrivalMethodName) + { + foreach (Type listenerClass in GetGenericsWith(listenerType)) + { + Dictionary> packetRouters = []; + packetArrivalMethods.Add(listenerClass, packetRouters); + + foreach (Type packetListener in GetGenericInterfacesWith(listenerType, listenerClass)) + { + Type packetType = packetListener.GetGenericArguments().First(); + + List arrivalMethods = packetListener + .GetMethods() + .Where(m => m.Name == packetArrivalMethodName) + .ToList(); + + packetRouters.Add(packetType, arrivalMethods); + } + } + } + + private static IEnumerable GetGenericsWith(Type type) + => AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(a => + a.GetTypes().Where( + t => t.GetInterfaces().Any( + i => i.IsGenericType && i.GetGenericTypeDefinition() == type + ) + ) + ); + + private static IEnumerable GetGenericInterfacesWith(Type interfaceType, Type type) + => type.GetInterfaces().Where( + i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType + ); + #endregion + + private enum NetworkType { Client, Server } +} diff --git a/Engine.Systems/Network/TypeHasher.cs b/Engine.Systems/Network/TypeHasher.cs new file mode 100644 index 0000000..82451d4 --- /dev/null +++ b/Engine.Systems/Network/TypeHasher.cs @@ -0,0 +1,27 @@ +namespace Engine.Systems.Network; + +public static class TypeHasher +{ + private static long _fnv1a = 0; + public static long FNV1a + { + get + { + if (_fnv1a == 0) + unchecked + { + const long fnvPrime = 1099511628211; + _fnv1a = 1469598103934665603; + + string typeName = typeof(T).FullName ?? typeof(T).Name; + foreach (char c in typeName) + { + _fnv1a ^= c; + _fnv1a *= fnvPrime; + } + } + + return _fnv1a; + } + } +} diff --git a/Engine.Systems/Preserver.cs b/Engine.Systems/Preserver.cs index dbfab6f..92ac222 100644 --- a/Engine.Systems/Preserver.cs +++ b/Engine.Systems/Preserver.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Systems +namespace Engine.Systems { // This is pretty much so the assembly gets loaded automatically because // the builds include the assembly but sometimes doesn't link load it at startup. diff --git a/Engine.Systems/StateMachine/IState.cs b/Engine.Systems/StateMachine/IState.cs index f30b260..c29b0bc 100644 --- a/Engine.Systems/StateMachine/IState.cs +++ b/Engine.Systems/StateMachine/IState.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.StateMachine; +namespace Engine.Systems.StateMachine; public interface IState : IEntity, INameable { diff --git a/Engine.Systems/StateMachine/State.cs b/Engine.Systems/StateMachine/State.cs index 86b030e..0c34a56 100644 --- a/Engine.Systems/StateMachine/State.cs +++ b/Engine.Systems/StateMachine/State.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.StateMachine; +namespace Engine.Systems.StateMachine; public class State : BaseEntity, IState { diff --git a/Engine.Systems/StateMachine/StateBehaviourBase.cs b/Engine.Systems/StateMachine/StateBehaviourBase.cs index efc6c9a..1ee7790 100644 --- a/Engine.Systems/StateMachine/StateBehaviourBase.cs +++ b/Engine.Systems/StateMachine/StateBehaviourBase.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.StateMachine; +namespace Engine.Systems.StateMachine; public abstract class StateBehaviourBase : Behaviour, IState { diff --git a/Engine.Systems/StateMachine/StateMachine.cs b/Engine.Systems/StateMachine/StateMachine.cs index 28f97b3..e8f009b 100644 --- a/Engine.Systems/StateMachine/StateMachine.cs +++ b/Engine.Systems/StateMachine/StateMachine.cs @@ -1,7 +1,7 @@ -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Serialization; +using Engine.Core; +using Engine.Core.Serialization; -namespace Syntriax.Engine.Systems.StateMachine; +namespace Engine.Systems.StateMachine; public class StateMachine : Behaviour, IUpdate { diff --git a/Engine.Systems/StateMachine/StateTransition.cs b/Engine.Systems/StateMachine/StateTransition.cs index ab02331..48dff73 100644 --- a/Engine.Systems/StateMachine/StateTransition.cs +++ b/Engine.Systems/StateMachine/StateTransition.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace Syntriax.Engine.Systems.StateMachine; +namespace Engine.Systems.StateMachine; public readonly record struct StateTransition(IState State, IReadOnlyList> Conditions) { diff --git a/Engine.Systems/Time/IReadOnlyStopwatch.cs b/Engine.Systems/Time/IReadOnlyStopwatch.cs index ea28155..e702c19 100644 --- a/Engine.Systems/Time/IReadOnlyStopwatch.cs +++ b/Engine.Systems/Time/IReadOnlyStopwatch.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; public interface IReadOnlyStopwatch { diff --git a/Engine.Systems/Time/IReadOnlyTicker.cs b/Engine.Systems/Time/IReadOnlyTicker.cs index 665671f..6d7d799 100644 --- a/Engine.Systems/Time/IReadOnlyTicker.cs +++ b/Engine.Systems/Time/IReadOnlyTicker.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; public interface IReadOnlyTicker : IReadOnlyStopwatch { diff --git a/Engine.Systems/Time/IReadOnlyTimer.cs b/Engine.Systems/Time/IReadOnlyTimer.cs index bca3ab7..cb1ca5e 100644 --- a/Engine.Systems/Time/IReadOnlyTimer.cs +++ b/Engine.Systems/Time/IReadOnlyTimer.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; public interface IReadOnlyTimer { diff --git a/Engine.Systems/Time/IStopwatch.cs b/Engine.Systems/Time/IStopwatch.cs index 46e3cbc..aca070a 100644 --- a/Engine.Systems/Time/IStopwatch.cs +++ b/Engine.Systems/Time/IStopwatch.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; public interface IStopwatch : IReadOnlyStopwatch { diff --git a/Engine.Systems/Time/ITicker.cs b/Engine.Systems/Time/ITicker.cs index 1d88e4a..ae18ab8 100644 --- a/Engine.Systems/Time/ITicker.cs +++ b/Engine.Systems/Time/ITicker.cs @@ -1,8 +1,8 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; -public interface ITicker : IStopwatch +public interface ITicker { Event OnTick { get; } diff --git a/Engine.Systems/Time/ITimer.cs b/Engine.Systems/Time/ITimer.cs index 8e313f9..31f4e2f 100644 --- a/Engine.Systems/Time/ITimer.cs +++ b/Engine.Systems/Time/ITimer.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; public interface ITimer : IReadOnlyTimer { diff --git a/Engine.Systems/Time/StopwatchBehaviour.cs b/Engine.Systems/Time/Stopwatch.cs similarity index 94% rename from Engine.Systems/Time/StopwatchBehaviour.cs rename to Engine.Systems/Time/Stopwatch.cs index 2eab89d..90e92f8 100644 --- a/Engine.Systems/Time/StopwatchBehaviour.cs +++ b/Engine.Systems/Time/Stopwatch.cs @@ -1,8 +1,8 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; -public class StopwatchBehaviour : Behaviour, IUpdate, IStopwatch +public class Stopwatch : Behaviour, IUpdate, IStopwatch { public Event OnStarted { get; } = new(); public Event OnDelta { get; } = new(); diff --git a/Engine.Systems/Time/TickerBehaviour.cs b/Engine.Systems/Time/TickerStopwatch.cs similarity index 75% rename from Engine.Systems/Time/TickerBehaviour.cs rename to Engine.Systems/Time/TickerStopwatch.cs index d7ada18..c41f5b7 100644 --- a/Engine.Systems/Time/TickerBehaviour.cs +++ b/Engine.Systems/Time/TickerStopwatch.cs @@ -1,15 +1,15 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; -public class TickerBehaviour : StopwatchBehaviour, ITicker +public class TickerStopwatch : Stopwatch, ITicker { public Event OnTick { get; } = new(); public double Period { get; set; } = 1f; public int TickCounter { get; private set; } = 0; - private double nextTick = 0f; + private double nextTick = double.MaxValue; public override void Start() { @@ -22,7 +22,7 @@ public class TickerBehaviour : StopwatchBehaviour, ITicker { base.Update(); - while (Time > nextTick) + while (Time >= nextTick) { nextTick += Period; TickCounter++; diff --git a/Engine.Systems/Time/TickerTimer.cs b/Engine.Systems/Time/TickerTimer.cs new file mode 100644 index 0000000..4e17115 --- /dev/null +++ b/Engine.Systems/Time/TickerTimer.cs @@ -0,0 +1,40 @@ +using Engine.Core; + +namespace Engine.Systems.Time; + +public class TickerTimer : Timer, ITicker +{ + public Event OnTick { get; } = new(); + + public double Period { get; set; } = 1f; + public int TickCounter { get; private set; } = 0; + + private double nextTick = double.MinValue; + + public override void Start(double time) + { + TickCounter = 0; + base.Start(time); + nextTick = Remaining - Period; + } + + public override void Update() + { + base.Update(); + + while (Remaining <= nextTick) + { + nextTick -= Period; + TickCounter++; + OnTick?.Invoke(this); + } + } + + protected override void OnFinalize() + { + base.OnFinalize(); + + TickCounter = 0; + nextTick = 0f; + } +} diff --git a/Engine.Systems/Time/TimerBehaviour.cs b/Engine.Systems/Time/Timer.cs similarity index 94% rename from Engine.Systems/Time/TimerBehaviour.cs rename to Engine.Systems/Time/Timer.cs index 314afac..42d2e03 100644 --- a/Engine.Systems/Time/TimerBehaviour.cs +++ b/Engine.Systems/Time/Timer.cs @@ -1,8 +1,8 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; -public class TimerBehaviour : Behaviour, IUpdate, ITimer +public class Timer : Behaviour, IUpdate, ITimer { public Event OnStarted { get; } = new(); public Event OnDelta { get; } = new(); @@ -53,7 +53,7 @@ public class TimerBehaviour : Behaviour, IUpdate, ITimer OnStopped?.Invoke(this); } - public void Update() + public virtual void Update() { if (State is not TimerState.Ticking) return; diff --git a/Engine.Systems/Time/TimerState.cs b/Engine.Systems/Time/TimerState.cs index 4bda41a..97b7f11 100644 --- a/Engine.Systems/Time/TimerState.cs +++ b/Engine.Systems/Time/TimerState.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Systems.Time; +namespace Engine.Systems.Time; public enum TimerState { diff --git a/Engine.Systems/Tween/Easings.cs b/Engine.Systems/Tween/Easings.cs index a629c1f..46a031d 100644 --- a/Engine.Systems/Tween/Easings.cs +++ b/Engine.Systems/Tween/Easings.cs @@ -1,16 +1,16 @@ // Reference: https://easings.net -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; internal static class EaseConstants { internal const float c1 = 1.70158f; internal const float c2 = c1 * 1.525f; internal const float c3 = c1 + 1f; - internal const float c4 = 2f * Math.PI / 3f; - internal const float c5 = 2f * Math.PI / 4.5f; + internal const float c4 = 2f * Math.Pi / 3f; + internal const float c5 = 2f * Math.Pi / 4.5f; } public abstract class EasingBase where T : IEasing, new() { public static readonly T Instance = new(); } @@ -33,9 +33,9 @@ public class EaseInQuint : EasingBase, IEasing { public float Evalu public class EaseOutQuint : EasingBase, IEasing { public float Evaluate(float x) => 1f - Math.Pow(1f - x, 5f); } public class EaseInOutQuint : EasingBase, IEasing { public float Evaluate(float x) => x < .5f ? 16f * x * x * x * x * x : 1f - Math.Pow(-2f * x + 2f, 5f) * .5f; } -public class EaseInSine : EasingBase, IEasing { public float Evaluate(float x) => 1f - Math.Cos(x * Math.PI * .5f); } -public class EaseOutSine : EasingBase, IEasing { public float Evaluate(float x) => Math.Sin(x * Math.PI * .5f); } -public class EaseInOutSine : EasingBase, IEasing { public float Evaluate(float x) => -(Math.Cos(Math.PI * x) - 1f) * .5f; } +public class EaseInSine : EasingBase, IEasing { public float Evaluate(float x) => 1f - Math.Cos(x * Math.Pi * .5f); } +public class EaseOutSine : EasingBase, IEasing { public float Evaluate(float x) => Math.Sin(x * Math.Pi * .5f); } +public class EaseInOutSine : EasingBase, IEasing { public float Evaluate(float x) => -(Math.Cos(Math.Pi * x) - 1f) * .5f; } public class EaseInExpo : EasingBase, IEasing { public float Evaluate(float x) => x == 0f ? 0f : Math.Pow(2f, 10f * x - 10f); } public class EaseOutExpo : EasingBase, IEasing { public float Evaluate(float x) => x == 1f ? 1f : 1f - Math.Pow(2f, -10f * x); } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenAABBExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenAABBExtensions.cs index d6c19bd..bfedd4f 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenAABBExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenAABBExtensions.cs @@ -1,10 +1,24 @@ -using System; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenAABBExtensions { - public static ITween TweenAABB(this AABB initialAABB, ITweenManager tweenManager, float duration, AABB targetAABB, Action setMethod) - => tweenManager.StartTween(duration, t => setMethod?.Invoke(new AABB(initialAABB.LowerBoundary.Lerp(targetAABB.LowerBoundary, t), initialAABB.UpperBoundary.Lerp(targetAABB.UpperBoundary, t)))); + private static readonly BoxedPool boxedAABBPool = new(2); + + public static ITween TweenAABB(this AABB initialAABB, ITweenManager tweenManager, float duration, AABB targetAABB, System.Action setMethod) + { + Boxed boxedInitial = boxedAABBPool.Get(initialAABB); + Boxed boxedTarget = boxedAABBPool.Get(targetAABB); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(new AABB(boxedInitial.Value.LowerBoundary.Lerp(boxedTarget.Value.LowerBoundary, t), boxedInitial.Value.UpperBoundary.Lerp(boxedTarget.Value.UpperBoundary, t)))); + + tween.OnComplete(() => + { + boxedAABBPool.Return(boxedInitial); + boxedAABBPool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenCamera2DExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenCamera2DExtensions.cs index 1644789..d8d3059 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenCamera2DExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenCamera2DExtensions.cs @@ -1,12 +1,24 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenCamera2DExtensions { + private static readonly BoxedPool boxedFloatPool = new(2); + public static ITween TweenZoom(this ICamera2D camera2D, ITweenManager tweenManager, float duration, float targetZoom) { - float initialZoom = camera2D.Zoom; - return tweenManager.StartTween(duration, t => camera2D.Zoom = initialZoom.Lerp(targetZoom, t)); + Boxed boxedInitial = boxedFloatPool.Get(camera2D.Zoom); + Boxed boxedTarget = boxedFloatPool.Get(targetZoom); + + ITween tween = tweenManager.StartTween(duration, t => camera2D.Zoom = boxedInitial.Value.Lerp(boxedTarget.Value, t)); + + tween.OnComplete(() => + { + boxedFloatPool.Return(boxedInitial); + boxedFloatPool.Return(boxedTarget); + }); + + return tween; } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenCircleExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenCircleExtensions.cs index c92f8fd..f0253b6 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenCircleExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenCircleExtensions.cs @@ -1,16 +1,31 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenCircleExtensions { + private static readonly BoxedPool boxedCirclePool = new(2); + public static ITween TweenCircle(this Circle initialCircle, ITweenManager tweenManager, float duration, Circle targetCircle, System.Action setMethod) - => tweenManager.StartTween(duration, + { + Boxed boxedInitial = boxedCirclePool.Get(initialCircle); + Boxed boxedTarget = boxedCirclePool.Get(targetCircle); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke( new Circle( - initialCircle.Center.Lerp(targetCircle.Center, t), - initialCircle.Diameter.Lerp(targetCircle.Diameter, t) + boxedInitial.Value.Center.Lerp(boxedTarget.Value.Center, t), + boxedInitial.Value.Diameter.Lerp(boxedTarget.Value.Diameter, t) ) ) ); + + tween.OnComplete(() => + { + boxedCirclePool.Return(boxedInitial); + boxedCirclePool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenColorExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenColorExtensions.cs index 9a3b886..3bf9216 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenColorExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenColorExtensions.cs @@ -1,15 +1,47 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenColorExtensions { + private static readonly BoxedPool boxedColorHSVPool = new(2); + private static readonly BoxedPool boxedColorHSVAPool = new(2); + public static ITween TweenColor(this ColorRGB initialColorRGB, ITweenManager tweenManager, float duration, ColorRGB targetColorRGB, System.Action setMethod) - => tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorRGB.Lerp(targetColorRGB, t))); + => TweenColor((ColorHSV)initialColorRGB, tweenManager, duration, (ColorHSV)targetColorRGB, color => setMethod?.Invoke(color)); public static ITween TweenColor(this ColorRGBA initialColorRGBA, ITweenManager tweenManager, float duration, ColorRGBA targetColorRGBA, System.Action setMethod) - => tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorRGBA.Lerp(targetColorRGBA, t))); + => TweenColor((ColorHSVA)initialColorRGBA, tweenManager, duration, (ColorHSVA)targetColorRGBA, color => setMethod?.Invoke(color)); public static ITween TweenColor(this ColorHSV initialColorHSV, ITweenManager tweenManager, float duration, ColorHSV targetColorHSV, System.Action setMethod) - => tweenManager.StartTween(duration, t => setMethod?.Invoke(initialColorHSV.Lerp(targetColorHSV, t))); + { + Boxed boxedInitial = boxedColorHSVPool.Get(initialColorHSV); + Boxed boxedTarget = boxedColorHSVPool.Get(targetColorHSV); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t))); + + tween.OnComplete(() => + { + boxedColorHSVPool.Return(boxedInitial); + boxedColorHSVPool.Return(boxedTarget); + }); + + return tween; + } + + public static ITween TweenColor(this ColorHSVA initialColorHSVA, ITweenManager tweenManager, float duration, ColorHSVA targetColorHSVA, System.Action setMethod) + { + Boxed boxedInitial = boxedColorHSVAPool.Get(initialColorHSVA); + Boxed boxedTarget = boxedColorHSVAPool.Get(targetColorHSVA); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t))); + + tween.OnComplete(() => + { + boxedColorHSVAPool.Return(boxedInitial); + boxedColorHSVAPool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenLine2DEquationExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenLine2DEquationExtensions.cs index bf8d91f..51fc9e6 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenLine2DEquationExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenLine2DEquationExtensions.cs @@ -1,16 +1,31 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenLine2DEquationExtensions { + private static readonly BoxedPool boxedLine2DEquationPool = new(2); + public static ITween TweenLine2DEquation(this Line2DEquation initialLine2DEquation, ITweenManager tweenManager, float duration, Line2DEquation targetLine2DEquation, System.Action setMethod) - => tweenManager.StartTween(duration, + { + Boxed boxedInitial = boxedLine2DEquationPool.Get(initialLine2DEquation); + Boxed boxedTarget = boxedLine2DEquationPool.Get(targetLine2DEquation); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke( new Line2DEquation( - initialLine2DEquation.Slope.Lerp(targetLine2DEquation.Slope, t), - initialLine2DEquation.OffsetY.Lerp(targetLine2DEquation.OffsetY, t) + boxedInitial.Value.Slope.Lerp(boxedTarget.Value.Slope, t), + boxedInitial.Value.OffsetY.Lerp(boxedTarget.Value.OffsetY, t) ) ) ); + + tween.OnComplete(() => + { + boxedLine2DEquationPool.Return(boxedInitial); + boxedLine2DEquationPool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenLine2DExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenLine2DExtensions.cs index eaaed71..a34ec4d 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenLine2DExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenLine2DExtensions.cs @@ -1,16 +1,31 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenLine2DExtensions { + private static readonly BoxedPool boxedLine2DPool = new(2); + public static ITween TweenLine2D(this Line2D initialLine2D, ITweenManager tweenManager, float duration, Line2D targetLine2D, System.Action setMethod) - => tweenManager.StartTween(duration, + { + Boxed boxedInitial = boxedLine2DPool.Get(initialLine2D); + Boxed boxedTarget = boxedLine2DPool.Get(targetLine2D); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke( new Line2D( - initialLine2D.From.Lerp(targetLine2D.From, t), - initialLine2D.To.Lerp(targetLine2D.To, t) + boxedInitial.Value.From.Lerp(boxedTarget.Value.From, t), + boxedInitial.Value.To.Lerp(boxedTarget.Value.To, t) ) ) ); + + tween.OnComplete(() => + { + boxedLine2DPool.Return(boxedInitial); + boxedLine2DPool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenPrimitiveExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenPrimitiveExtensions.cs new file mode 100644 index 0000000..1dc857f --- /dev/null +++ b/Engine.Systems/Tween/EngineExtensions/TweenPrimitiveExtensions.cs @@ -0,0 +1,41 @@ +using Engine.Core; + +namespace Engine.Systems.Tween; + +public static class TweenPrimitiveExtensions +{ + private static readonly BoxedPool boxedFloatPool = new(2); + private static readonly BoxedPool boxedIntPool = new(2); + + public static ITween TweenFloat(this float initialFloat, ITweenManager tweenManager, float duration, float targetFloat, System.Action setMethod) + { + Boxed boxedInitial = boxedFloatPool.Get(initialFloat); + Boxed boxedTarget = boxedFloatPool.Get(targetFloat); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t))); + + tween.OnComplete(() => + { + boxedFloatPool.Return(boxedInitial); + boxedFloatPool.Return(boxedTarget); + }); + + return tween; + } + + public static ITween TweenInt(this int initialInt, ITweenManager tweenManager, float duration, int targetInt, System.Action setMethod) + { + Boxed boxedInitial = boxedIntPool.Get(initialInt); + Boxed boxedTarget = boxedIntPool.Get(targetInt); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value + (boxedTarget.Value - boxedInitial.Value) * t)); + + tween.OnComplete(() => + { + boxedIntPool.Return(boxedInitial); + boxedIntPool.Return(boxedTarget); + }); + + return tween; + } +} diff --git a/Engine.Systems/Tween/EngineExtensions/TweenProjection1DExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenProjection1DExtensions.cs index f6755bf..b18399e 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenProjection1DExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenProjection1DExtensions.cs @@ -1,17 +1,31 @@ -using System; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenProjection1DExtensions { - public static ITween TweenProjection1D(this Projection1D initialProjection1D, ITweenManager tweenManager, float duration, Projection1D targetProjection1D, Action setMethod) - => tweenManager.StartTween(duration, + private static readonly BoxedPool boxedProjection1DPool = new(2); + + public static ITween TweenProjection1D(this Projection1D initialProjection1D, ITweenManager tweenManager, float duration, Projection1D targetProjection1D, System.Action setMethod) + { + Boxed boxedInitial = boxedProjection1DPool.Get(initialProjection1D); + Boxed boxedTarget = boxedProjection1DPool.Get(targetProjection1D); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke( new Projection1D( - initialProjection1D.Min.Lerp(targetProjection1D.Min, t), - initialProjection1D.Max.Lerp(targetProjection1D.Max, t) + boxedInitial.Value.Min.Lerp(boxedTarget.Value.Min, t), + boxedInitial.Value.Max.Lerp(boxedTarget.Value.Max, t) ) ) ); + + tween.OnComplete(() => + { + boxedProjection1DPool.Return(boxedInitial); + boxedProjection1DPool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenQuaternionExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenQuaternionExtensions.cs index a0c10fc..976c2d4 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenQuaternionExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenQuaternionExtensions.cs @@ -1,10 +1,24 @@ -using System; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenQuaternionExtensions { - public static ITween TweenQuaternion(this Quaternion initialQuaternion, ITweenManager tweenManager, float duration, Quaternion targetQuaternion, Action setMethod) - => tweenManager.StartTween(duration, t => setMethod?.Invoke(initialQuaternion.SLerp(targetQuaternion, t))); + private static readonly BoxedPool boxedQuaternionPool = new(2); + + public static ITween TweenQuaternion(this Quaternion initialQuaternion, ITweenManager tweenManager, float duration, Quaternion targetQuaternion, System.Action setMethod) + { + Boxed boxedInitial = boxedQuaternionPool.Get(initialQuaternion); + Boxed boxedTarget = boxedQuaternionPool.Get(targetQuaternion); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.SLerp(boxedTarget.Value, t))); + + tween.OnComplete(() => + { + boxedQuaternionPool.Return(boxedInitial); + boxedQuaternionPool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenShape2DExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenShape2DExtensions.cs index 00393dd..6c9bb97 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenShape2DExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenShape2DExtensions.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenShape2DExtensions { diff --git a/Engine.Systems/Tween/EngineExtensions/TweenTransform2DExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenTransform2DExtensions.cs index f4ca833..62a2aac 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenTransform2DExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenTransform2DExtensions.cs @@ -1,44 +1,27 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenTransform2DExtensions { public static ITween TweenPosition(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetPosition) - { - Vector2D initialPosition = transform2D.Position; - return tweenManager.StartTween(duration, t => transform2D.Position = initialPosition.Lerp(targetPosition, t)); - } + => transform2D.Position.TweenVector2D(tweenManager, duration, targetPosition, x => transform2D.Position = x); public static ITween TweenScale(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetScale) - { - Vector2D initialScale = transform2D.Scale; - return tweenManager.StartTween(duration, t => transform2D.Scale = initialScale.Lerp(targetScale, t)); - } + => transform2D.Scale.TweenVector2D(tweenManager, duration, targetScale, x => transform2D.Scale = x); public static ITween TweenRotation(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float targetRotation) - { - float initialRotation = transform2D.Rotation; - return tweenManager.StartTween(duration, t => transform2D.Rotation = initialRotation.Lerp(targetRotation, t)); - } + => transform2D.Rotation.TweenFloat(tweenManager, duration, targetRotation, x => transform2D.Rotation = x); public static ITween TweenLocalPosition(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetLocalPosition) - { - Vector2D initialLocalPosition = transform2D.LocalPosition; - return tweenManager.StartTween(duration, t => transform2D.LocalPosition = initialLocalPosition.Lerp(targetLocalPosition, t)); - } + => transform2D.LocalPosition.TweenVector2D(tweenManager, duration, targetLocalPosition, x => transform2D.LocalPosition = x); public static ITween TweenLocalScale(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D targetLocalScale) - { - Vector2D initialLocalScale = transform2D.LocalScale; - return tweenManager.StartTween(duration, t => transform2D.LocalScale = initialLocalScale.Lerp(targetLocalScale, t)); - } + => transform2D.LocalScale.TweenVector2D(tweenManager, duration, targetLocalScale, x => transform2D.LocalScale = x); public static ITween TweenLocalRotation(this ITransform2D transform2D, ITweenManager tweenManager, float duration, float targetLocalRotation) - { - float initialLocalRotation = transform2D.LocalRotation; - return tweenManager.StartTween(duration, t => transform2D.LocalRotation = initialLocalRotation.Lerp(targetLocalRotation, t)); - } + => transform2D.LocalRotation.TweenFloat(tweenManager, duration, targetLocalRotation, x => transform2D.LocalRotation = x); + public static ITween TweenPositionAdditive(this ITransform2D transform2D, ITweenManager tweenManager, float duration, Vector2D additivePosition) { Vector2D progressedPosition = Vector2D.Zero; diff --git a/Engine.Systems/Tween/EngineExtensions/TweenTriangleExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenTriangleExtensions.cs index d91475e..c1dfd1e 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenTriangleExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenTriangleExtensions.cs @@ -1,18 +1,31 @@ -using System; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenTriangleExtensions { - public static ITween TweenTriangle(this Triangle initialTriangle, ITweenManager tweenManager, float duration, Triangle targetTriangle, Action setMethod) - => tweenManager.StartTween(duration, - t => setMethod?.Invoke( + private static readonly BoxedPool boxedTrianglePool = new(2); + + public static ITween TweenTriangle(this Triangle initialTriangle, ITweenManager tweenManager, float duration, Triangle targetTriangle, System.Action setMethod) + { + Boxed boxedInitial = boxedTrianglePool.Get(initialTriangle); + Boxed boxedTarget = boxedTrianglePool.Get(targetTriangle); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke( new Triangle( - initialTriangle.A.Lerp(targetTriangle.A, t), - initialTriangle.B.Lerp(targetTriangle.B, t), - initialTriangle.C.Lerp(targetTriangle.C, t) + boxedInitial.Value.A.Lerp(boxedTarget.Value.A, t), + boxedInitial.Value.B.Lerp(boxedTarget.Value.B, t), + boxedInitial.Value.C.Lerp(boxedTarget.Value.C, t) ) ) ); + + tween.OnComplete(() => + { + boxedTrianglePool.Return(boxedInitial); + boxedTrianglePool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenVector2DExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenVector2DExtensions.cs index e230349..49d620f 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenVector2DExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenVector2DExtensions.cs @@ -1,9 +1,24 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenVector2DExtensions { + private static readonly BoxedPool boxedVector2DPool = new(2); + public static ITween TweenVector2D(this Vector2D initialVector2D, ITweenManager tweenManager, float duration, Vector2D targetVector2D, System.Action setMethod) - => tweenManager.StartTween(duration, t => setMethod?.Invoke(initialVector2D.Lerp(targetVector2D, t))); + { + Boxed boxedInitial = boxedVector2DPool.Get(initialVector2D); + Boxed boxedTarget = boxedVector2DPool.Get(targetVector2D); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t))); + + tween.OnComplete(() => + { + boxedVector2DPool.Return(boxedInitial); + boxedVector2DPool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/EngineExtensions/TweenVector3DExtensions.cs b/Engine.Systems/Tween/EngineExtensions/TweenVector3DExtensions.cs index 3ef7026..3853634 100644 --- a/Engine.Systems/Tween/EngineExtensions/TweenVector3DExtensions.cs +++ b/Engine.Systems/Tween/EngineExtensions/TweenVector3DExtensions.cs @@ -1,9 +1,24 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenVector3DExtensions { + private static readonly BoxedPool boxedVector3DPool = new(2); + public static ITween TweenVector3D(this Vector3D initialVector3D, ITweenManager tweenManager, float duration, Vector3D targetVector3D, System.Action setMethod) - => tweenManager.StartTween(duration, t => setMethod?.Invoke(initialVector3D.Lerp(targetVector3D, t))); + { + Boxed boxedInitial = boxedVector3DPool.Get(initialVector3D); + Boxed boxedTarget = boxedVector3DPool.Get(targetVector3D); + + ITween tween = tweenManager.StartTween(duration, t => setMethod?.Invoke(boxedInitial.Value.Lerp(boxedTarget.Value, t))); + + tween.OnComplete(() => + { + boxedVector3DPool.Return(boxedInitial); + boxedVector3DPool.Return(boxedTarget); + }); + + return tween; + } } diff --git a/Engine.Systems/Tween/Helpers/Boxed.cs b/Engine.Systems/Tween/Helpers/Boxed.cs new file mode 100644 index 0000000..e02242d --- /dev/null +++ b/Engine.Systems/Tween/Helpers/Boxed.cs @@ -0,0 +1,12 @@ +using Engine.Core; + +namespace Engine.Systems.Tween; + +public class Boxed where T : struct +{ + public Event, BoxedValueChangedArguments> OnValueChanged { get; } = new(); + + public T Value { get; set; } = default; + + public readonly record struct BoxedValueChangedArguments(T PreviousValue, T CurrentValue); +} diff --git a/Engine.Systems/Tween/Helpers/BoxedPool.cs b/Engine.Systems/Tween/Helpers/BoxedPool.cs new file mode 100644 index 0000000..37fd270 --- /dev/null +++ b/Engine.Systems/Tween/Helpers/BoxedPool.cs @@ -0,0 +1,14 @@ +using Engine.Core; + +namespace Engine.Systems.Tween; + +public class BoxedPool(int initialCapacity = 1) : Pool>(() => new(), initialCapacity) where T : struct; +public static class BoxedPoolExtensions +{ + public static Boxed Get(this BoxedPool boxedPool, T value) where T : struct + { + Boxed boxed = boxedPool.Get(); + boxed.Value = value; + return boxed; + } +} diff --git a/Engine.Systems/Tween/IEasing.cs b/Engine.Systems/Tween/IEasing.cs index a81331f..acf1502 100644 --- a/Engine.Systems/Tween/IEasing.cs +++ b/Engine.Systems/Tween/IEasing.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public interface IEasing { diff --git a/Engine.Systems/Tween/ITween.cs b/Engine.Systems/Tween/ITween.cs index f4d881b..559d3ad 100644 --- a/Engine.Systems/Tween/ITween.cs +++ b/Engine.Systems/Tween/ITween.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public interface ITween { diff --git a/Engine.Systems/Tween/ITweenManager.cs b/Engine.Systems/Tween/ITweenManager.cs index 2dc52d9..f1739fc 100644 --- a/Engine.Systems/Tween/ITweenManager.cs +++ b/Engine.Systems/Tween/ITweenManager.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public interface ITweenManager { diff --git a/Engine.Systems/Tween/Tween.cs b/Engine.Systems/Tween/Tween.cs index c92aa94..a8afc42 100644 --- a/Engine.Systems/Tween/Tween.cs +++ b/Engine.Systems/Tween/Tween.cs @@ -1,6 +1,6 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; internal class Tween : ITween { diff --git a/Engine.Systems/Tween/TweenExtensions.cs b/Engine.Systems/Tween/TweenExtensions.cs index 4b02cac..e0ffc09 100644 --- a/Engine.Systems/Tween/TweenExtensions.cs +++ b/Engine.Systems/Tween/TweenExtensions.cs @@ -1,6 +1,6 @@ using System; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public static class TweenExtensions { diff --git a/Engine.Systems/Tween/TweenManager.cs b/Engine.Systems/Tween/TweenManager.cs index 4c0b599..53f830d 100644 --- a/Engine.Systems/Tween/TweenManager.cs +++ b/Engine.Systems/Tween/TweenManager.cs @@ -1,9 +1,9 @@ using System.Collections; using System.Collections.Generic; -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public class TweenManager : Behaviour, ITweenManager { diff --git a/Engine.Systems/Tween/TweenState.cs b/Engine.Systems/Tween/TweenState.cs index 166e925..df010c0 100644 --- a/Engine.Systems/Tween/TweenState.cs +++ b/Engine.Systems/Tween/TweenState.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public enum TweenState { diff --git a/Engine.Systems/Tween/Yields/WaitForTweenCompleteCoroutineYield.cs b/Engine.Systems/Tween/Yields/WaitForTweenCompleteCoroutineYield.cs index 0a8c290..b574530 100644 --- a/Engine.Systems/Tween/Yields/WaitForTweenCompleteCoroutineYield.cs +++ b/Engine.Systems/Tween/Yields/WaitForTweenCompleteCoroutineYield.cs @@ -1,5 +1,5 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public class WaitForTweenCompleteCoroutineYield(ITween tween) : CoroutineYield(() => tween.State == TweenState.Completed); diff --git a/Engine.Systems/Tween/Yields/WaitForTweenDoneCoroutineYield.cs b/Engine.Systems/Tween/Yields/WaitForTweenDoneCoroutineYield.cs index b34330b..3ab3d78 100644 --- a/Engine.Systems/Tween/Yields/WaitForTweenDoneCoroutineYield.cs +++ b/Engine.Systems/Tween/Yields/WaitForTweenDoneCoroutineYield.cs @@ -1,5 +1,5 @@ -using Syntriax.Engine.Core; +using Engine.Core; -namespace Syntriax.Engine.Systems.Tween; +namespace Engine.Systems.Tween; public class WaitWhileTweenActiveCoroutineYield(ITween tween) : CoroutineYield(() => tween.State.CheckFlag(TweenState.Completed | TweenState.Cancelled)); diff --git a/Engine.sln b/Engine.sln index 4f5da96..59b17e5 100644 --- a/Engine.sln +++ b/Engine.sln @@ -11,15 +11,17 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Systems", "Engine.Sy EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{58AE79C1-9203-44AE-8022-AA180F0A71DC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine.Serializers", "Engine.Serializers", "{F88E129A-9A47-4D27-96EE-6EC02F79594B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine.Integration", "Engine.Integration", "{F88E129A-9A47-4D27-96EE-6EC02F79594B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Serializers.Yaml", "Engine.Serializers\Engine.Serializers.Yaml\Engine.Serializers.Yaml.csproj", "{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Integration.Yaml", "Engine.Integration\Engine.Integration.Yaml\Engine.Integration.Yaml.csproj", "{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YamlDotNet", "Engine.Serializers\YamlDotNet\YamlDotNet\YamlDotNet.csproj", "{3D852C92-BC14-4893-AEF2-50612DAFCD8F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YamlDotNet", "Engine.Integration\YamlDotNet\YamlDotNet\YamlDotNet.csproj", "{3D852C92-BC14-4893-AEF2-50612DAFCD8F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{823D4020-332D-2C13-F261-6F510F11A57E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame", "Engine.Integration\Engine.Integration.MonoGame\Engine.Integration.MonoGame.csproj", "{C3438D33-0879-44E4-9DF0-D29F5621C44C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Integration.MonoGame", "Engine.Integration\Engine.Integration.MonoGame\Engine.Integration.MonoGame.csproj", "{C3438D33-0879-44E4-9DF0-D29F5621C44C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Integration.LiteNetLib", "Engine.Integration\Engine.Integration.LiteNetLib\Engine.Integration.LiteNetLib.csproj", "{121A7C66-1691-4DA5-B070-A681A83779AC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -115,6 +117,18 @@ Global {C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x64.Build.0 = Release|Any CPU {C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.ActiveCfg = Release|Any CPU {C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.Build.0 = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|x64.ActiveCfg = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|x64.Build.0 = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|x86.ActiveCfg = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Debug|x86.Build.0 = Debug|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|Any CPU.Build.0 = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|x64.ActiveCfg = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|x64.Build.0 = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|x86.ActiveCfg = Release|Any CPU + {121A7C66-1691-4DA5-B070-A681A83779AC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Engine/Engine.csproj b/Engine/Engine.csproj index be663c5..c3763fb 100644 --- a/Engine/Engine.csproj +++ b/Engine/Engine.csproj @@ -4,7 +4,8 @@ net9.0 disable enable - Syntriax.Engine + Engine + Engine diff --git a/Engine/Preserver.cs b/Engine/Preserver.cs index a272d7e..f26a7ac 100644 --- a/Engine/Preserver.cs +++ b/Engine/Preserver.cs @@ -1,4 +1,4 @@ -namespace Syntriax.Engine +namespace Engine { // This is pretty much so the assembly gets loaded automatically because // the builds include the assembly but sometimes doesn't link load it at startup.