1597 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			1597 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C#
		
	
	
| using NUnit.Framework;
 | |
| using FluentAssertions;
 | |
| 
 | |
| using Encompass;
 | |
| 
 | |
| using System;
 | |
| using System.Linq;
 | |
| using System.Collections.Generic;
 | |
| using Encompass.Exceptions;
 | |
| 
 | |
| namespace Tests
 | |
| {
 | |
|     struct MockComponent : IComponent
 | |
|     {
 | |
|         public int myInt;
 | |
|         public string myString;
 | |
|     }
 | |
| 
 | |
|     public class EngineTest
 | |
|     {
 | |
|         static List<MockComponent> resultComponents;
 | |
|         static MockComponent resultComponent;
 | |
| 
 | |
|         static List<MockMessage> resultMessages = new List<MockMessage>();
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         public class ReadComponentsTestEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 resultComponents = ReadComponents<MockComponent>().ToList();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static List<(MockComponent, Entity)> resultComponentsIncludingEntity;
 | |
|         static (MockComponent, Entity) resultComponentIncludingEntity;
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         public class ReadComponentsIncludingEntityEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 resultComponentsIncludingEntity = ReadComponentsIncludingEntity<MockComponent>().ToList();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         public class ReadComponentTestEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 resultComponent = ReadComponent<MockComponent>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         public class ReadComponentIncludingEntityEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 resultComponentIncludingEntity = ReadComponentIncludingEntity<MockComponent>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void ReadComponents()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadComponentsTestEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             var entityB = worldBuilder.CreateEntity();
 | |
| 
 | |
|             MockComponent mockComponent;
 | |
|             mockComponent.myInt = 0;
 | |
|             mockComponent.myString = "hello";
 | |
| 
 | |
|             MockComponent mockComponentB;
 | |
|             mockComponentB.myInt = 1;
 | |
|             mockComponentB.myString = "howdy";
 | |
| 
 | |
|             worldBuilder.SetComponent(entity, mockComponent);
 | |
|             worldBuilder.SetComponent(entityB, mockComponentB);
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01f);
 | |
| 
 | |
|             resultComponents.Should().Contain(mockComponent);
 | |
|             resultComponents.Should().Contain(mockComponentB);
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void ReadComponentsIncludingEntity()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadComponentsIncludingEntityEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             var entityB = worldBuilder.CreateEntity();
 | |
| 
 | |
|             MockComponent mockComponent;
 | |
|             mockComponent.myInt = 0;
 | |
|             mockComponent.myString = "hello";
 | |
| 
 | |
|             MockComponent mockComponentB;
 | |
|             mockComponentB.myInt = 1;
 | |
|             mockComponentB.myString = "howdy";
 | |
| 
 | |
|             worldBuilder.SetComponent(entity, mockComponent);
 | |
|             worldBuilder.SetComponent(entityB, mockComponentB);
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01f);
 | |
| 
 | |
|             var resultComponentValues = resultComponentsIncludingEntity.Select((kv) => kv.Item1);
 | |
|             resultComponentValues.Should().Contain(mockComponent);
 | |
|             resultComponentValues.Should().Contain(mockComponentB);
 | |
| 
 | |
|             var resultEntities = resultComponentsIncludingEntity.Select((kv) => kv.Item2);
 | |
|             resultEntities.Should().Contain(entity);
 | |
|             resultEntities.Should().Contain(entityB);
 | |
| 
 | |
|             resultComponentsIncludingEntity.Should().Contain((mockComponent, entity));
 | |
|             resultComponentsIncludingEntity.Should().Contain((mockComponentB, entityB));
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void ReadComponent()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadComponentTestEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
| 
 | |
|             MockComponent mockComponent;
 | |
|             mockComponent.myInt = 0;
 | |
|             mockComponent.myString = "hello";
 | |
| 
 | |
|             worldBuilder.SetComponent(entity, mockComponent);
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01f);
 | |
| 
 | |
|             Assert.AreEqual(mockComponent, resultComponent);
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void ReadComponentWhenMultipleComponents()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadComponentTestEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             var entityB = worldBuilder.CreateEntity();
 | |
| 
 | |
|             MockComponent mockComponent;
 | |
|             mockComponent.myInt = 0;
 | |
|             mockComponent.myString = "hello";
 | |
| 
 | |
|             MockComponent mockComponentB;
 | |
|             mockComponentB.myInt = 1;
 | |
|             mockComponentB.myString = "howdy";
 | |
| 
 | |
|             worldBuilder.SetComponent(entity, mockComponent);
 | |
|             worldBuilder.SetComponent(entityB, mockComponentB);
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01);
 | |
| 
 | |
|             Assert.That(resultComponent, Is.EqualTo(mockComponent).Or.EqualTo(mockComponentB));
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void ReadComponentWithEntity()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadComponentIncludingEntityEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
| 
 | |
|             MockComponent mockComponent;
 | |
|             mockComponent.myInt = 0;
 | |
|             mockComponent.myString = "hello";
 | |
| 
 | |
|             worldBuilder.SetComponent(entity, mockComponent);
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01f);
 | |
| 
 | |
|             (mockComponent, entity).Should().BeEquivalentTo(resultComponentIncludingEntity);
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         [Writes(typeof(MockComponent))]
 | |
|         public class UpdateComponentTestEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 var (component, entity) = ReadComponentIncludingEntity<MockComponent>();
 | |
| 
 | |
|                 component.myInt = 420;
 | |
|                 component.myString = "blaze it";
 | |
|                 SetComponent(entity, component);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // this test needs to be improved...
 | |
| 
 | |
|         [Test]
 | |
|         public void UpdateComponent()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|             worldBuilder.AddEngine(new UpdateComponentTestEngine());
 | |
|             worldBuilder.AddEngine(new ReadComponentTestEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
| 
 | |
|             MockComponent mockComponent;
 | |
|             mockComponent.myInt = 0;
 | |
|             mockComponent.myString = "hello";
 | |
| 
 | |
|             worldBuilder.SetComponent(entity, mockComponent);
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01);
 | |
|             world.Update(0.01);
 | |
| 
 | |
|             Assert.AreEqual(420, resultComponent.myInt);
 | |
|             Assert.AreEqual("blaze it", resultComponent.myString);
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         public class UndeclaredUpdateComponentTestEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 var (component, entity) = ReadComponentIncludingEntity<MockComponent>();
 | |
| 
 | |
|                 component.myInt = 420;
 | |
|                 component.myString = "blaze it";
 | |
|                 SetComponent(entity, component);
 | |
| 
 | |
|                 component = ReadComponent<MockComponent>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void UpdateUndeclaredComponent()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new UndeclaredUpdateComponentTestEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
| 
 | |
|             MockComponent mockComponent;
 | |
|             mockComponent.myInt = 0;
 | |
|             mockComponent.myString = "hello";
 | |
| 
 | |
|             worldBuilder.SetComponent(entity, mockComponent);
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             var ex = Assert.Throws<IllegalWriteException>(() => world.Update(0.01f));
 | |
|             Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredUpdateComponentTestEngine tried to update undeclared Component MockComponent"));
 | |
|         }
 | |
| 
 | |
|         struct MockMessage : IMessage
 | |
|         {
 | |
|             public string myString;
 | |
|         }
 | |
| 
 | |
|         [Sends(typeof(MockMessage))]
 | |
|         public class MessageEmitEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 MockMessage message;
 | |
|                 message.myString = "howdy";
 | |
| 
 | |
|                 this.SendMessage(message);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Receives(typeof(MockMessage))]
 | |
|         public class MessageReadEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 resultMessages = this.ReadMessages<MockMessage>().ToList();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void EmitAndReadMessage()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new MessageEmitEngine());
 | |
|             worldBuilder.AddEngine(new MessageReadEngine());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01f);
 | |
| 
 | |
|             Assert.AreEqual(resultMessages.First().myString, "howdy");
 | |
|         }
 | |
| 
 | |
|         public class UndeclaredMessageEmitEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 MockMessage message;
 | |
|                 message.myString = "howdy";
 | |
| 
 | |
|                 this.SendMessage(message);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static IEnumerable<MockMessage> emptyReadMessagesResult;
 | |
| 
 | |
|         [Receives(typeof(MockMessage))]
 | |
|         class ReadMessagesWhenNoneExistEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 emptyReadMessagesResult = ReadMessages<MockMessage>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void ReadMessagesWhenNoneHaveBeenEmitted()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadMessagesWhenNoneExistEngine());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01f);
 | |
| 
 | |
|             emptyReadMessagesResult.Should().BeEmpty();
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void EmitUndeclaredMessage()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new UndeclaredMessageEmitEngine());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             var ex = Assert.Throws<IllegalSendException>(() => world.Update(0.01f));
 | |
|             Assert.That(ex.Message, Is.EqualTo("Engine UndeclaredMessageEmitEngine tried to send undeclared Message MockMessage"));
 | |
|         }
 | |
| 
 | |
|         static bool someTest;
 | |
| 
 | |
|         [Sends(typeof(MockMessage))]
 | |
|         class EmitMockMessageEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 MockMessage message;
 | |
|                 message.myString = "howdy";
 | |
| 
 | |
|                 this.SendMessage(message);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Receives(typeof(MockMessage))]
 | |
|         class SomeMessageTestEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 someTest = this.SomeMessage<MockMessage>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void SomeMessage()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new EmitMockMessageEngine());
 | |
|             worldBuilder.AddEngine(new SomeMessageTestEngine());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01f);
 | |
| 
 | |
|             Assert.That(someTest, Is.True);
 | |
|         }
 | |
| 
 | |
|         class UndeclaredSomeMessageEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 someTest = this.SomeMessage<MockMessage>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void UndeclaredSomeMessage()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new EmitMockMessageEngine());
 | |
|             worldBuilder.AddEngine(new UndeclaredSomeMessageEngine());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             Assert.Throws<IllegalReadException>(() => world.Update(0.01f));
 | |
|         }
 | |
| 
 | |
|         class SomeComponentTestEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 Assert.IsTrue(SomeComponent<MockComponent>());
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void SomeComponent()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01);
 | |
|         }
 | |
| 
 | |
|         static (MockComponent, Entity) pairA;
 | |
|         static (MockComponent, Entity) pairB;
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         class SameValueComponentReadEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 var components = ReadComponentsIncludingEntity<MockComponent>();
 | |
| 
 | |
|                 pairA = components.First();
 | |
|                 pairB = components.Last();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Tests that components with identical values should be distinguishable by their entities
 | |
|         [Test]
 | |
|         public void SameValueComponents()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new SameValueComponentReadEngine());
 | |
| 
 | |
|             MockComponent componentA;
 | |
|             componentA.myInt = 20;
 | |
|             componentA.myString = "hello";
 | |
| 
 | |
|             MockComponent componentB;
 | |
|             componentB.myInt = 20;
 | |
|             componentB.myString = "hello";
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, componentA);
 | |
| 
 | |
|             var entityB = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entityB, componentB);
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
|             world.Update(0.01f);
 | |
| 
 | |
|             Assert.That(EngineTest.pairA, Is.Not.EqualTo(EngineTest.pairB));
 | |
|             Assert.That(EngineTest.pairA.Item1, Is.EqualTo(EngineTest.pairB.Item1));
 | |
|         }
 | |
| 
 | |
|         static IEnumerable<(MockComponent, Entity)> emptyComponentReadResult;
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         class ReadEmptyMockComponentsEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 emptyComponentReadResult = ReadComponentsIncludingEntity<MockComponent>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void ReadComponentsOfTypeWhereNoneExist()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadEmptyMockComponentsEngine());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
|             world.Update(0.01f);
 | |
| 
 | |
|             Assert.That(emptyComponentReadResult, Is.Empty);
 | |
|         }
 | |
| 
 | |
|         struct DestroyerComponent : IComponent { }
 | |
| 
 | |
|         [Reads(typeof(DestroyerComponent))]
 | |
|         class DestroyerEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var (component, entity) in ReadComponentsIncludingEntity<DestroyerComponent>())
 | |
|                 {
 | |
|                     Destroy(entity);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static List<(MockComponent, Entity)> results;
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         class ReaderEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 results = ReadComponentsIncludingEntity<MockComponent>().ToList();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void DestroyEntity()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new DestroyerEngine());
 | |
|             worldBuilder.AddEngine(new ReaderEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             var entityB = worldBuilder.CreateEntity();
 | |
|             var entityC = worldBuilder.CreateEntity();
 | |
| 
 | |
|             DestroyerComponent destroyerComponent;
 | |
|             MockComponent mockComponent;
 | |
|             mockComponent.myInt = 2;
 | |
|             mockComponent.myString = "blah";
 | |
| 
 | |
|             worldBuilder.SetComponent(entity, destroyerComponent);
 | |
|             worldBuilder.SetComponent(entity, mockComponent);
 | |
| 
 | |
|             worldBuilder.SetComponent(entityB, destroyerComponent);
 | |
|             worldBuilder.SetComponent(entityB, mockComponent);
 | |
| 
 | |
|             worldBuilder.SetComponent(entityC, mockComponent);
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01);
 | |
|             world.Update(0.01);
 | |
| 
 | |
|             Assert.That(results, Does.Not.Contain((mockComponent, entity)));
 | |
|             Assert.That(results, Does.Not.Contain((mockComponent, entityB)));
 | |
|             Assert.That(results, Does.Contain((mockComponent, entityC)));
 | |
|         }
 | |
| 
 | |
|         [Receives(typeof(DestroyComponentMessage))]
 | |
|         class DestroyEntityEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var message in ReadMessages<DestroyComponentMessage>())
 | |
|                 {
 | |
|                     Destroy(message.entity);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void DestroyEntityWithoutID()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new AddComponentEngine());
 | |
|             worldBuilder.AddEngine(new DestroyEntityEngine());
 | |
|             worldBuilder.AddEngine(new ReaderEngine());
 | |
| 
 | |
|             var mockComponent = new MockComponent { };
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, mockComponent);
 | |
|             worldBuilder.SendMessage(new DestroyComponentMessage { entity = entity });
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
|             world.Update(0.01);
 | |
| 
 | |
|             Assert.DoesNotThrow(() => world.Update(0.01));
 | |
|             Assert.That(results, Does.Not.Contain((mockComponent, entity)));
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(DestroyerComponent))]
 | |
|         [Writes(typeof(MockComponent))]
 | |
|         class DestroyAndAddComponentEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var (componentPair, entity) in ReadComponentsIncludingEntity<DestroyerComponent>())
 | |
|                 {
 | |
|                     RemoveComponent<MockComponent>(entity);
 | |
|                     Destroy(entity);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void DestroyEntityWhileRemovingComponent()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new DestroyAndAddComponentEngine());
 | |
|             worldBuilder.AddEngine(new ReaderEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
| 
 | |
|             worldBuilder.SetComponent(entity, new DestroyerComponent());
 | |
|             worldBuilder.SetComponent(entity, new MockComponent());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             Assert.DoesNotThrow(() => world.Update(0.01));
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         [WritesImmediate(typeof(MockComponent))]
 | |
|         [Writes(typeof(MockComponent))]
 | |
|         class AddAndRemoveMockComponentEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var (mockComponent, entity) in ReadComponentsIncludingEntity<MockComponent>())
 | |
|                 {
 | |
|                     RemoveComponent<MockComponent>(entity);
 | |
|                     SetComponent(entity, new MockComponent());
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static Entity entityResult;
 | |
| 
 | |
|         [ReadsImmediate(typeof(MockComponent))]
 | |
|         class GetEntityFromImmediateReadComponents : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 var (_, entity) = ReadComponentIncludingEntity<MockComponent>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void GetEntityFromImmediateComponentID()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new AddAndRemoveMockComponentEngine());
 | |
|             worldBuilder.AddEngine(new GetEntityFromImmediateReadComponents());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             Assert.DoesNotThrow(() => world.Update(0.01));
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         [Writes(typeof(MockComponent))]
 | |
|         class DelayedMessageEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
 | |
|                 {
 | |
|                     RemoveComponent<MockComponent>(entity);
 | |
|                     SendMessage(new MockMessage { }, 1);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void EngineSendMessageDelayed()
 | |
|         {
 | |
|             resultMessages.Clear();
 | |
| 
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ActivateTimeDilationEngine());
 | |
|             worldBuilder.AddEngine(new DelayedMessageEngine());
 | |
|             worldBuilder.AddEngine(new MessageReadEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent { });
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01);
 | |
| 
 | |
|             resultMessages.Should().BeEmpty();
 | |
| 
 | |
|             world.Update(0.5);
 | |
| 
 | |
|             resultMessages.Should().BeEmpty();
 | |
| 
 | |
|             world.Update(0.5);
 | |
| 
 | |
|             resultMessages.Should().BeEmpty();
 | |
| 
 | |
|             world.Update(2);
 | |
| 
 | |
|             resultMessages.Should().NotBeEmpty();
 | |
|             resultMessages.First().Should().BeOfType<MockMessage>();
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         [Writes(typeof(MockComponent))]
 | |
|         class DelayedMessageIgnoringTimeDilationEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
 | |
|                 {
 | |
|                     RemoveComponent<MockComponent>(entity);
 | |
|                     SendMessageIgnoringTimeDilation(new MockMessage { }, 1);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void EngineSendMessageDelayedIgnoringTimeDilation()
 | |
|         {
 | |
|             resultMessages.Clear();
 | |
| 
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ActivateTimeDilationEngine());
 | |
|             worldBuilder.AddEngine(new DelayedMessageIgnoringTimeDilationEngine());
 | |
|             worldBuilder.AddEngine(new MessageReadEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent { });
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01);
 | |
| 
 | |
|             resultMessages.Should().BeEmpty();
 | |
| 
 | |
|             world.Update(0.5);
 | |
| 
 | |
|             resultMessages.Should().BeEmpty();
 | |
| 
 | |
|             world.Update(0.5);
 | |
| 
 | |
|             resultMessages.Should().NotBeEmpty();
 | |
|             resultMessages.First().Should().BeOfType<MockMessage>();
 | |
|         }
 | |
| 
 | |
|         [Receives(typeof(MockMessage))]
 | |
|         [WritesImmediate(typeof(MockComponent))]
 | |
|         [Writes(typeof(MockComponent), 1)]
 | |
|         class ActivateComponentEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var message in ReadMessages<MockMessage>())
 | |
|                 {
 | |
|                     var entity = CreateEntity();
 | |
|                     SetComponent(entity, new MockComponent { });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [ReadsImmediate(typeof(MockComponent))]
 | |
|         [Writes(typeof(MockComponent), 0)]
 | |
|         class RemoveComponentEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var entity in ReadEntities<MockComponent>())
 | |
|                 {
 | |
|                     RemoveComponent<MockComponent>(entity);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void EngineAddAndRemoveComponentSameFrameWithRemovePriority()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ActivateComponentEngine());
 | |
|             worldBuilder.AddEngine(new RemoveComponentEngine());
 | |
|             worldBuilder.AddEngine(new ReadComponentsTestEngine());
 | |
| 
 | |
|             worldBuilder.SendMessage(new MockMessage { });
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             Assert.DoesNotThrow(() => world.Update(0.01));
 | |
|             world.Update(0.01); // update again for the read
 | |
|             resultComponents.Should().BeEmpty();
 | |
|         }
 | |
| 
 | |
|         struct DestroyComponentMessage : IMessage { public Entity entity; }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         [Writes(typeof(MockComponent))]
 | |
|         class AddComponentEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var entity in ReadEntities<MockComponent>())
 | |
|                 {
 | |
|                     SetComponent(entity, new MockComponent { });
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static Entity readEntity;
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         class ReadEntityByComponentTypeEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 readEntity = ReadEntity<MockComponent>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void GetEntityByComponentType()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadEntityByComponentTypeEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent { });
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
|             world.Update(0.01);
 | |
| 
 | |
|             entity.Should().BeEquivalentTo(readEntity);
 | |
|         }
 | |
| 
 | |
| 
 | |
| 
 | |
|         static Entity[] readEntities;
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         class ReadEntitiesWithComponentTypeEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 readEntities = ReadEntities<MockComponent>().ToArray();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void ReadEntities()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
 | |
|             worldBuilder.AddEngine(new DestroyAllWithEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent { });
 | |
| 
 | |
|             var entityB = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entityB, new MockComponent { });
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
|             world.Update(0.01);
 | |
| 
 | |
|             readEntities.Should().Contain(entity);
 | |
|             readEntities.Should().Contain(entityB);
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         class DestroyWithEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 if (SomeComponent<MockComponent>())
 | |
|                 {
 | |
|                     DestroyWith<MockComponent>();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void DestroyWith()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
 | |
|             worldBuilder.AddEngine(new DestroyWithEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent { });
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
|             world.Update(0.01);
 | |
|             world.Update(0.01); // update twice so the read happens after destroy
 | |
| 
 | |
|             readEntities.Should().BeEmpty();
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         class DestroyAllWithEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 DestroyAllWith<MockComponent>();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void DestroyAllWith()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadEntitiesWithComponentTypeEngine());
 | |
|             worldBuilder.AddEngine(new DestroyAllWithEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent { });
 | |
| 
 | |
|             var entityB = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entityB, new MockComponent { });
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
|             world.Update(0.01);
 | |
|             world.Update(0.01); // update twice so the read happens after destroy
 | |
| 
 | |
|             readEntities.Should().BeEmpty();
 | |
|         }
 | |
| 
 | |
|         [Reads(typeof(MockComponent))]
 | |
|         [Writes(typeof(MockComponent))]
 | |
|         class RemoveComponentByTypeEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 foreach (var (_, entity) in ReadComponentsIncludingEntity<MockComponent>())
 | |
|                 {
 | |
|                     RemoveComponent<MockComponent>(entity);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void RemoveComponentByType()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadComponentsTestEngine());
 | |
|             worldBuilder.AddEngine(new RemoveComponentByTypeEngine());
 | |
| 
 | |
|             var entity = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent { });
 | |
| 
 | |
|             var entityB = worldBuilder.CreateEntity();
 | |
|             worldBuilder.SetComponent(entity, new MockComponent { });
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01);
 | |
|             world.Update(0.01);
 | |
| 
 | |
|             resultComponents.Should().BeEmpty();
 | |
|         }
 | |
| 
 | |
|         static double dilatedDeltaTime;
 | |
| 
 | |
|         class ActivateTimeDilationEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 if (!TimeDilationActive)
 | |
|                 {
 | |
|                     ActivateTimeDilation(0.2, 1, 1, 1);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     dilatedDeltaTime = dt;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void ActivateTimeDilation()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ActivateTimeDilationEngine());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01); // activate time dilation
 | |
| 
 | |
|             world.Update(0.5);
 | |
| 
 | |
|             dilatedDeltaTime.Should().BeApproximately(0.3, 0.01);
 | |
| 
 | |
|             world.Update(0.5);
 | |
| 
 | |
|             dilatedDeltaTime.Should().BeApproximately(0.1, 0.01);
 | |
| 
 | |
|             world.Update(1);
 | |
| 
 | |
|             world.Update(0.5);
 | |
| 
 | |
|             dilatedDeltaTime.Should().BeApproximately(0.3, 0.01);
 | |
|         }
 | |
| 
 | |
|         class ActivateTimeDilationLowerFactorEngine : Engine
 | |
|         {
 | |
|             private bool activated = false;
 | |
| 
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 if (!activated)
 | |
|                 {
 | |
|                     ActivateTimeDilation(0.2, 1, 1, 1);
 | |
|                     activated = true;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         class ActivateTimeDilationHigherFactorEngine : Engine
 | |
|         {
 | |
|             private bool activated = false;
 | |
| 
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 if (!activated)
 | |
|                 {
 | |
|                     ActivateTimeDilation(0.5, 1, 1, 1);
 | |
|                     activated = true;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static bool timeDilationActive;
 | |
|         class ReadDilatedDeltaTimeEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 dilatedDeltaTime = dt;
 | |
|                 timeDilationActive = TimeDilationActive;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void MultipleActivateTimeDilation()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ReadDilatedDeltaTimeEngine());
 | |
|             worldBuilder.AddEngine(new ActivateTimeDilationLowerFactorEngine());
 | |
|             worldBuilder.AddEngine(new ActivateTimeDilationHigherFactorEngine());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01); // activate time dilation
 | |
| 
 | |
|             world.Update(0.5); // 0.3 and 0.375
 | |
| 
 | |
|             dilatedDeltaTime.Should().BeApproximately(0.3375, 0.01);
 | |
|             timeDilationActive.Should().BeTrue();
 | |
| 
 | |
|             world.Update(5);
 | |
| 
 | |
|             dilatedDeltaTime.Should().BeApproximately(5, 0.01);
 | |
|             timeDilationActive.Should().BeFalse();
 | |
|         }
 | |
| 
 | |
|         static double undilatedDeltaTime;
 | |
| 
 | |
|         [IgnoresTimeDilation]
 | |
|         class IgnoresTimeDilationEngine : Engine
 | |
|         {
 | |
|             public override void Update(double dt)
 | |
|             {
 | |
|                 undilatedDeltaTime = dt;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Test]
 | |
|         public void IgnoresTimeDilation()
 | |
|         {
 | |
|             var worldBuilder = new WorldBuilder();
 | |
|             worldBuilder.AddEngine(new ActivateTimeDilationEngine());
 | |
|             worldBuilder.AddEngine(new IgnoresTimeDilationEngine());
 | |
| 
 | |
|             var world = worldBuilder.Build();
 | |
| 
 | |
|             world.Update(0.01); // activate time dilation
 | |
| 
 | |
|             world.Update(0.5);
 | |
| 
 | |
|             undilatedDeltaTime.Should().Be(0.5);
 | |
|         }
 | |
| 
 | |
|         public class QueryTests
 | |
|         {
 | |
|             struct MockComponentB : IComponent { }
 | |
|             struct MockComponentC : IComponent { }
 | |
|             struct MockComponentD : IComponent { }
 | |
| 
 | |
|             [Reads(typeof(MockComponent), typeof(MockComponentB))]
 | |
|             [QueryWith(typeof(MockComponent), typeof(MockComponentB))]
 | |
|             class EntityQueryWithComponentsEngine : Engine
 | |
|             {
 | |
|                 private List<Entity> entities;
 | |
| 
 | |
|                 public EntityQueryWithComponentsEngine(List<Entity> entities)
 | |
|                 {
 | |
|                     this.entities = entities;
 | |
|                 }
 | |
| 
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     entities.Clear();
 | |
|                     foreach (var entity in TrackedEntities)
 | |
|                     {
 | |
|                         entities.Add(entity);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void EntitiesWithComponents()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 var entityB = worldBuilder.CreateEntity();
 | |
|                 var entityC = worldBuilder.CreateEntity();
 | |
| 
 | |
|                 worldBuilder.SetComponent(entity, new MockComponent());
 | |
|                 worldBuilder.SetComponent(entity, new MockComponentB());
 | |
| 
 | |
|                 worldBuilder.SetComponent(entityB, new MockComponent());
 | |
|                 worldBuilder.SetComponent(entityB, new MockComponentB());
 | |
| 
 | |
|                 worldBuilder.SetComponent(entityC, new MockComponentB());
 | |
| 
 | |
|                 var queriedEntities = new List<Entity>();
 | |
|                 worldBuilder.AddEngine(new EntityQueryWithComponentsEngine(queriedEntities));
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
| 
 | |
|                 world.Update(0.01);
 | |
| 
 | |
|                 queriedEntities.Should().BeEquivalentTo(new Entity[] { entity, entityB });
 | |
|             }
 | |
| 
 | |
|             [Reads(typeof(MockComponent))]
 | |
|             [QueryWithout(typeof(MockComponent))]
 | |
|             class EntityQueryWithoutComponentsEngine : Engine
 | |
|             {
 | |
|                 private List<Entity> entities;
 | |
| 
 | |
|                 public EntityQueryWithoutComponentsEngine(List<Entity> entities)
 | |
|                 {
 | |
|                     this.entities = entities;
 | |
|                 }
 | |
| 
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     entities.Clear();
 | |
|                     entities.AddRange(TrackedEntities);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void EntitiesWithoutComponents()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 var entityB = worldBuilder.CreateEntity();
 | |
|                 var entityC = worldBuilder.CreateEntity();
 | |
| 
 | |
|                 worldBuilder.SetComponent(entity, new MockComponent());
 | |
|                 worldBuilder.SetComponent(entity, new MockComponentB());
 | |
| 
 | |
|                 worldBuilder.SetComponent(entityB, new MockComponent());
 | |
|                 worldBuilder.SetComponent(entityB, new MockComponentB());
 | |
| 
 | |
|                 worldBuilder.SetComponent(entityC, new MockComponentB());
 | |
| 
 | |
|                 var queriedEntities = new List<Entity>();
 | |
|                 worldBuilder.AddEngine(new EntityQueryWithoutComponentsEngine(queriedEntities));
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
| 
 | |
|                 world.Update(0.01);
 | |
| 
 | |
|                 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityC });
 | |
|             }
 | |
| 
 | |
|             [Reads(typeof(MockComponent), typeof(MockComponentB), typeof(MockComponentD))]
 | |
|             [QueryWith(typeof(MockComponent), typeof(MockComponentB))]
 | |
|             [QueryWithout(typeof(MockComponentD))]
 | |
|             class EntityQueryWithandWithoutComponentsEngine : Engine
 | |
|             {
 | |
|                 private List<Entity> entities;
 | |
| 
 | |
|                 public EntityQueryWithandWithoutComponentsEngine(List<Entity> entities)
 | |
|                 {
 | |
|                     this.entities = entities;
 | |
|                 }
 | |
| 
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     entities.Clear();
 | |
| 
 | |
|                     entities.AddRange(TrackedEntities);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void EntitiesWithAndWithoutComponents()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 var entityB = worldBuilder.CreateEntity();
 | |
|                 var entityC = worldBuilder.CreateEntity();
 | |
|                 var entityD = worldBuilder.CreateEntity();
 | |
| 
 | |
|                 worldBuilder.SetComponent(entity, new MockComponent());
 | |
|                 worldBuilder.SetComponent(entity, new MockComponentB());
 | |
|                 worldBuilder.SetComponent(entity, new MockComponentD());
 | |
| 
 | |
|                 worldBuilder.SetComponent(entityB, new MockComponent());
 | |
| 
 | |
|                 worldBuilder.SetComponent(entityC, new MockComponent());
 | |
|                 worldBuilder.SetComponent(entityC, new MockComponentB());
 | |
|                 worldBuilder.SetComponent(entityC, new MockComponentC());
 | |
|                 worldBuilder.SetComponent(entityC, new MockComponentD());
 | |
| 
 | |
|                 worldBuilder.SetComponent(entityD, new MockComponent());
 | |
|                 worldBuilder.SetComponent(entityD, new MockComponentB());
 | |
|                 worldBuilder.SetComponent(entityD, new MockComponentC());
 | |
| 
 | |
|                 var queriedEntities = new List<Entity>();
 | |
|                 worldBuilder.AddEngine(new EntityQueryWithandWithoutComponentsEngine(queriedEntities));
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
| 
 | |
|                 world.Update(0.01);
 | |
| 
 | |
|                 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityD });
 | |
|             }
 | |
| 
 | |
|             [Reads(typeof(MockComponent))]
 | |
|             [WritesImmediate(typeof(MockComponentB))]
 | |
|             [Writes(typeof(MockComponentB), 0)]
 | |
|             class AddImmediateComponentEngine : Engine
 | |
|             {
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     foreach (var entity in ReadEntities<MockComponent>())
 | |
|                     {
 | |
|                         SetComponent(entity, new MockComponentB());
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [ReadsImmediate(typeof(MockComponentB))]
 | |
|             [QueryWith(typeof(MockComponentB))]
 | |
|             class EntityQueryWithImmediateComponentsEngine : Engine
 | |
|             {
 | |
|                 private List<Entity> entities;
 | |
| 
 | |
|                 public EntityQueryWithImmediateComponentsEngine(List<Entity> entities)
 | |
|                 {
 | |
|                     this.entities = entities;
 | |
|                 }
 | |
| 
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     entities.Clear();
 | |
|                     entities.AddRange(TrackedEntities);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void EntitiesWithImmediateComponents()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 var entityB = worldBuilder.CreateEntity();
 | |
| 
 | |
|                 worldBuilder.SetComponent(entity, new MockComponent());
 | |
| 
 | |
|                 var queriedEntities = new List<Entity>();
 | |
|                 worldBuilder.AddEngine(new AddImmediateComponentEngine());
 | |
|                 worldBuilder.AddEngine(new EntityQueryWithImmediateComponentsEngine(queriedEntities));
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
| 
 | |
|                 world.Update(0.01);
 | |
| 
 | |
|                 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entity });
 | |
|             }
 | |
| 
 | |
|             [ReadsImmediate(typeof(MockComponentB))]
 | |
|             [QueryWithout(typeof(MockComponentB))]
 | |
|             class EntityQueryWithoutImmediateComponentsEngine : Engine
 | |
|             {
 | |
|                 private List<Entity> entities;
 | |
| 
 | |
|                 public EntityQueryWithoutImmediateComponentsEngine(List<Entity> entities)
 | |
|                 {
 | |
|                     this.entities = entities;
 | |
|                 }
 | |
| 
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     entities.Clear();
 | |
|                     entities.AddRange(TrackedEntities);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void EntitiesWithoutImmediateComponents()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 var entityB = worldBuilder.CreateEntity();
 | |
| 
 | |
|                 worldBuilder.SetComponent(entity, new MockComponent());
 | |
| 
 | |
|                 var queriedEntities = new List<Entity>();
 | |
|                 worldBuilder.AddEngine(new AddImmediateComponentEngine());
 | |
|                 worldBuilder.AddEngine(new EntityQueryWithoutImmediateComponentsEngine(queriedEntities));
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
| 
 | |
|                 world.Update(0.01);
 | |
| 
 | |
|                 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
 | |
|             }
 | |
| 
 | |
|             [Reads(typeof(MockComponentC), typeof(MockComponentD))]
 | |
|             [WritesImmediate(typeof(MockComponent), typeof(MockComponentB))]
 | |
|             [Writes(typeof(MockComponent), 0)]
 | |
|             [Writes(typeof(MockComponentB), 0)]
 | |
|             class ConditionallyAddImmediateComponentsEngine : Engine
 | |
|             {
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     foreach (var entity in ReadEntities<MockComponentC>())
 | |
|                     {
 | |
|                         SetComponent(entity, new MockComponent());
 | |
|                     }
 | |
| 
 | |
|                     foreach (var entity in ReadEntities<MockComponentD>())
 | |
|                     {
 | |
|                         SetComponent(entity, new MockComponent());
 | |
|                         SetComponent(entity, new MockComponentB());
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [ReadsImmediate(typeof(MockComponent), typeof(MockComponentB))]
 | |
|             [QueryWith(typeof(MockComponent))]
 | |
|             [QueryWithout(typeof(MockComponentB))]
 | |
|             class EntityQueryWithAndWithoutImmediateComponentsEngine : Engine
 | |
|             {
 | |
|                 private List<Entity> entities;
 | |
| 
 | |
|                 public EntityQueryWithAndWithoutImmediateComponentsEngine(List<Entity> entities)
 | |
|                 {
 | |
|                     this.entities = entities;
 | |
|                 }
 | |
| 
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     entities.Clear();
 | |
| 
 | |
|                     entities.AddRange(TrackedEntities);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void EntitiesWithAndWithoutImmediateComponents()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 var entityB = worldBuilder.CreateEntity();
 | |
|                 var entityC = worldBuilder.CreateEntity();
 | |
| 
 | |
|                 worldBuilder.SetComponent(entityB, new MockComponentC());
 | |
|                 worldBuilder.SetComponent(entityC, new MockComponentD());
 | |
| 
 | |
|                 var queriedEntities = new List<Entity>();
 | |
|                 worldBuilder.AddEngine(new ConditionallyAddImmediateComponentsEngine());
 | |
|                 worldBuilder.AddEngine(new EntityQueryWithAndWithoutImmediateComponentsEngine(queriedEntities));
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
| 
 | |
|                 world.Update(0.01);
 | |
| 
 | |
|                 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
 | |
|             }
 | |
| 
 | |
|             [Reads(typeof(MockComponentC))]
 | |
|             [WritesImmediate(typeof(MockComponentB))]
 | |
|             [Writes(typeof(MockComponentB), 0)]
 | |
|             class ConditionallyAddImmediateComponentEngine : Engine
 | |
|             {
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     foreach (var entity in ReadEntities<MockComponentC>())
 | |
|                     {
 | |
|                         SetComponent(entity, new MockComponentB());
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [ReadsImmediate(typeof(MockComponentB))]
 | |
|             [Reads(typeof(MockComponent))]
 | |
|             [QueryWith(typeof(MockComponent), typeof(MockComponentB))]
 | |
|             class EntityQueryWithImmediateAndNonImmediateComponents : Engine
 | |
|             {
 | |
|                 private List<Entity> entities;
 | |
| 
 | |
|                 public EntityQueryWithImmediateAndNonImmediateComponents(List<Entity> entities)
 | |
|                 {
 | |
|                     this.entities = entities;
 | |
|                 }
 | |
| 
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     entities.Clear();
 | |
|                     entities.AddRange(TrackedEntities);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void EntitiesWithImmediateAndNonImmediateComponents()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 var entityB = worldBuilder.CreateEntity();
 | |
|                 var entityC = worldBuilder.CreateEntity();
 | |
| 
 | |
|                 worldBuilder.SetComponent(entityB, new MockComponent());
 | |
|                 worldBuilder.SetComponent(entityB, new MockComponentC());
 | |
|                 worldBuilder.SetComponent(entityC, new MockComponentD());
 | |
| 
 | |
|                 var queriedEntities = new List<Entity>();
 | |
|                 worldBuilder.AddEngine(new ConditionallyAddImmediateComponentEngine());
 | |
|                 worldBuilder.AddEngine(new EntityQueryWithImmediateAndNonImmediateComponents(queriedEntities));
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
| 
 | |
|                 world.Update(0.01);
 | |
| 
 | |
|                 queriedEntities.ToArray().Should().BeEquivalentTo(new Entity[] { entityB });
 | |
|             }
 | |
| 
 | |
|             [ReadsImmediate(typeof(MockComponentB))]
 | |
|             class ReadImmediateComponentsEngine : Engine
 | |
|             {
 | |
|                 private List<MockComponentB> _components;
 | |
| 
 | |
|                 public ReadImmediateComponentsEngine(List<MockComponentB> components)
 | |
|                 {
 | |
|                     _components = components;
 | |
|                 }
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     _components.AddRange(ReadComponents<MockComponentB>());
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void ReadImmediateComponents()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var _components = new List<MockComponentB>();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 worldBuilder.SetComponent(entity, new MockComponent());
 | |
| 
 | |
|                 worldBuilder.AddEngine(new AddImmediateComponentEngine());
 | |
|                 worldBuilder.AddEngine(new ReadImmediateComponentsEngine(_components));
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
|                 world.Update(0.01);
 | |
| 
 | |
|                 _components.Should().NotBeEmpty();
 | |
|             }
 | |
| 
 | |
|             [ReadsImmediate(typeof(MockComponentB))]
 | |
|             [Reads(typeof(MockComponent))]
 | |
|             class HasAndGetImmediateComponentEngine : Engine
 | |
|             {
 | |
|                 private List<MockComponentB> _components;
 | |
| 
 | |
|                 public HasAndGetImmediateComponentEngine(List<MockComponentB> components)
 | |
|                 {
 | |
|                     _components = components;
 | |
|                 }
 | |
| 
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     foreach (var (component, entity) in ReadComponentsIncludingEntity<MockComponent>())
 | |
|                     {
 | |
|                         if (HasComponent<MockComponentB>(entity))
 | |
|                         {
 | |
|                             _components.Add(GetComponent<MockComponentB>(entity));
 | |
| 
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void HasAndGetImmediateComponent()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var _components = new List<MockComponentB>();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 worldBuilder.SetComponent(entity, new MockComponent());
 | |
| 
 | |
|                 worldBuilder.AddEngine(new AddImmediateComponentEngine());
 | |
|                 worldBuilder.AddEngine(new HasAndGetImmediateComponentEngine(_components));
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
|                 world.Update(0.01);
 | |
| 
 | |
|                 _components.Should().NotBeEmpty();
 | |
|             }
 | |
| 
 | |
|             struct MockTimerComponent : IComponent
 | |
|             {
 | |
|                 public MockTimerComponent(double time)
 | |
|                 {
 | |
|                     Timer = time;
 | |
|                 }
 | |
| 
 | |
|                 public double Timer { get; set; }
 | |
|             }
 | |
| 
 | |
|             [Reads(typeof(MockTimerComponent))]
 | |
|             [Writes(typeof(MockTimerComponent))]
 | |
|             class ReadWhileRemovingComponentsEngine : Engine
 | |
|             {
 | |
|                 public override void Update(double dt)
 | |
|                 {
 | |
|                     foreach (var (component, entity) in ReadComponentsIncludingEntity<MockTimerComponent>())
 | |
|                     {
 | |
|                         var updatedComponent = component;
 | |
|                         updatedComponent.Timer -= dt;
 | |
| 
 | |
|                         if (updatedComponent.Timer <= 0)
 | |
|                         {
 | |
|                             RemoveComponent<MockTimerComponent>(entity);
 | |
| 
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             SetComponent<MockTimerComponent>(entity, updatedComponent);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             [Test]
 | |
|             public void ReadWhileRemovingComponents()
 | |
|             {
 | |
|                 var worldBuilder = new WorldBuilder();
 | |
| 
 | |
|                 var entity = worldBuilder.CreateEntity();
 | |
|                 worldBuilder.SetComponent(entity, new MockTimerComponent(0.5));
 | |
| 
 | |
|                 var entityB = worldBuilder.CreateEntity();
 | |
|                 worldBuilder.SetComponent(entityB, new MockTimerComponent(0.4));
 | |
| 
 | |
|                 worldBuilder.AddEngine(new ReadWhileRemovingComponentsEngine());
 | |
| 
 | |
|                 var world = worldBuilder.Build();
 | |
|                 Assert.DoesNotThrow(() => world.Update(0.2));
 | |
|                 Assert.DoesNotThrow(() => world.Update(0.25));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |