public IObservableGroup GetObservableGroup(IGroup group, params int[] collectionIds) { var requiredComponents = ComponentTypeLookup.GetComponentTypes(group.RequiredComponents); var excludedComponents = ComponentTypeLookup.GetComponentTypes(group.ExcludedComponents); var lookupGroup = new LookupGroup(requiredComponents, excludedComponents); var observableGroupToken = new ObservableGroupToken(lookupGroup, collectionIds); if (_observableGroups.Contains(observableGroupToken)) { return(_observableGroups[observableGroupToken]); } var entityMatches = GetEntitiesFor(lookupGroup, collectionIds); var configuration = new ObservableGroupConfiguration { ObservableGroupToken = observableGroupToken, InitialEntities = entityMatches }; if (collectionIds != null && collectionIds.Length > 0) { configuration.NotifyingCollections = _collections.Where(x => collectionIds.Contains(x.Id)); } else { configuration.NotifyingCollections = new [] { this }; } var observableGroup = ObservableGroupFactory.Create(configuration); _observableGroups.Add(observableGroup); return(_observableGroups[observableGroupToken]); }
public IObservableGroup GetObservableGroup(IGroup group, string collectionName = null) { var requiredComponents = ComponentTypeLookup.GetComponentTypes(group.RequiredComponents); var excludedComponents = ComponentTypeLookup.GetComponentTypes(group.ExcludedComponents); var lookupGroup = new LookupGroup(requiredComponents, excludedComponents); var observableGroupToken = new ObservableGroupToken(lookupGroup, collectionName); if (_observableGroups.ContainsKey(observableGroupToken)) { return(_observableGroups[observableGroupToken]); } var entityMatches = GetEntitiesFor(lookupGroup, collectionName); var configuration = new ObservableGroupConfiguration { ObservableGroupToken = observableGroupToken, InitialEntities = entityMatches }; if (collectionName != null) { configuration.NotifyingCollection = _collections[collectionName]; } else { configuration.NotifyingCollection = this; } var observableGroup = ObservableGroupFactory.Create(configuration); _observableGroups.Add(observableGroupToken, observableGroup); return(_observableGroups[observableGroupToken]); }
public void should_add_entity_and_raise_event_when_components_match_group() { var collectionName = "default"; var accessorToken = new ObservableGroupToken(new[] { 1 }, new [] { 2 }, collectionName); var mockCollection = Substitute.For <IEntityCollection>(); mockCollection.Name.Returns(collectionName); var applicableEntity = Substitute.For <IEntity>(); applicableEntity.Id.Returns(1); var mockCollectionNotifier = Substitute.For <INotifyingEntityCollection>(); var componentRemoved = new Subject <ComponentsChangedEvent>(); mockCollectionNotifier.EntityAdded.Returns(Observable.Empty <CollectionEntityEvent>()); mockCollectionNotifier.EntityRemoved.Returns(Observable.Empty <CollectionEntityEvent>()); mockCollectionNotifier.EntityComponentsAdded.Returns(Observable.Empty <ComponentsChangedEvent>()); mockCollectionNotifier.EntityComponentsRemoving.Returns(Observable.Empty <ComponentsChangedEvent>()); mockCollectionNotifier.EntityComponentsRemoved.Returns(componentRemoved); var observableGroup = new ObservableGroup(accessorToken, new IEntity[0], mockCollectionNotifier); var wasCalled = 0; observableGroup.OnEntityAdded.Subscribe(x => wasCalled++); applicableEntity.HasAllComponents(accessorToken.LookupGroup.RequiredComponents).Returns(true); applicableEntity.HasAnyComponents(accessorToken.LookupGroup.ExcludedComponents).Returns(false); componentRemoved.OnNext(new ComponentsChangedEvent(mockCollection, applicableEntity, null)); Assert.Contains(applicableEntity, observableGroup.CachedEntities.Values); Assert.Equal(1, wasCalled); }
public void should_not_cache_applicable_entity_when_added_to_different_collection() { var mockEventSystem = Substitute.For <IEventSystem>(); var collectionName = "defaut"; var accessorToken = new ObservableGroupToken(new[] { typeof(TestComponentOne), typeof(TestComponentTwo) }, "some-other-entityCollection-name"); var mockCollection = Substitute.For <IEntityCollection>(); mockCollection.Name.Returns(collectionName); var applicableEntity = new Entity(Guid.NewGuid(), mockEventSystem); applicableEntity.AddComponent <TestComponentOne>(); applicableEntity.AddComponent <TestComponentTwo>(); var unapplicableEntity = new Entity(Guid.NewGuid(), mockEventSystem); unapplicableEntity.AddComponent <TestComponentOne>(); var underlyingEvent = new ReactiveProperty <EntityAddedEvent>(new EntityAddedEvent(applicableEntity, mockCollection)); mockEventSystem.Receive <EntityAddedEvent>().Returns(underlyingEvent); mockEventSystem.Receive <EntityRemovedEvent>().Returns(Observable.Empty <EntityRemovedEvent>()); mockEventSystem.Receive <ComponentsAddedEvent>().Returns(Observable.Empty <ComponentsAddedEvent>()); mockEventSystem.Receive <ComponentsRemovedEvent>().Returns(Observable.Empty <ComponentsRemovedEvent>()); var cacheableGroupAccessor = new ObservableGroup(mockEventSystem, accessorToken, new IEntity[] { }); underlyingEvent.SetValueAndForceNotify(new EntityAddedEvent(unapplicableEntity, mockCollection)); Assert.Empty(cacheableGroupAccessor.CachedEntities); }
public void should_correctly_notify_when_matching_component_removed() { var componentTypes = new[] { typeof(TestComponentOne), typeof(TestComponentTwo) }; var fakeEventSystem = new EventSystem(new MessageBroker()); var accessorToken = new ObservableGroupToken(componentTypes, "default"); var fakeEntity1 = new Entity(Guid.Empty, fakeEventSystem); fakeEntity1.AddComponent <TestComponentOne>(); fakeEntity1.AddComponent <TestComponentTwo>(); fakeEntity1.AddComponent <TestComponentThree>(); var fakeEntity2 = new Entity(Guid.Empty, fakeEventSystem); fakeEntity2.AddComponent <TestComponentOne>(); fakeEntity2.AddComponent <TestComponentThree>(); var timesCalled = 0; var observableGroup = new ObservableGroup(fakeEventSystem, accessorToken, new IEntity[] { fakeEntity1 }); ObservableExtensions.Subscribe <IEntity>(observableGroup.OnEntityRemoved, x => { Assert.Equal <IEntity>(fakeEntity1, x); timesCalled++; }); fakeEntity1.RemoveComponent <TestComponentThree>(); fakeEntity1.RemoveComponent <TestComponentTwo>(); fakeEntity2.RemoveComponent <TestComponentThree>(); Assert.Equal(1, timesCalled); }
public void should_only_add_entity_when_components_match_group() { var mockEventSystem = Substitute.For <IEventSystem>(); var accessorToken = new ObservableGroupToken(new[] { typeof(TestComponentOne), typeof(TestComponentTwo) }, "default"); var existingEntityOne = new Entity(Guid.NewGuid(), mockEventSystem); var componentToAdd = new TestComponentOne(); existingEntityOne.AddComponent <TestComponentTwo>(); var existingEntityTwo = new Entity(Guid.NewGuid(), mockEventSystem); var unapplicableComponent = new TestComponentThree(); existingEntityTwo.AddComponent <TestComponentOne>(); var dummyEventToSeedMock = new ComponentsAddedEvent(new Entity(Guid.NewGuid(), mockEventSystem), new[] { new TestComponentOne() }); var underlyingEvent = new ReactiveProperty <ComponentsAddedEvent>(dummyEventToSeedMock); mockEventSystem.Receive <ComponentsAddedEvent>().Returns(underlyingEvent); mockEventSystem.Receive <ComponentsRemovedEvent>().Returns(Observable.Empty <ComponentsRemovedEvent>()); mockEventSystem.Receive <EntityAddedEvent>().Returns(Observable.Empty <EntityAddedEvent>()); mockEventSystem.Receive <EntityRemovedEvent>().Returns(Observable.Empty <EntityRemovedEvent>()); var cacheableGroupAccessor = new ObservableGroup(mockEventSystem, accessorToken, new IEntity[] {}); existingEntityOne.AddComponent(componentToAdd); underlyingEvent.SetValueAndForceNotify(new ComponentsAddedEvent(existingEntityOne, new[] { componentToAdd })); existingEntityTwo.AddComponent(unapplicableComponent); underlyingEvent.SetValueAndForceNotify(new ComponentsAddedEvent(existingEntityTwo, new[] { unapplicableComponent })); Assert.Equal(1, cacheableGroupAccessor.CachedEntities.Count); Assert.Equal <IEntity>(existingEntityOne, cacheableGroupAccessor.CachedEntities[existingEntityOne.Id]); }
public void should_include_entity_snapshot_on_creation() { var mockEventSystem = Substitute.For <IEventSystem>(); var accessorToken = new ObservableGroupToken(new Type[] { }, "default"); mockEventSystem.Receive <EntityAddedEvent>().Returns(Observable.Empty <EntityAddedEvent>()); mockEventSystem.Receive <EntityRemovedEvent>().Returns(Observable.Empty <EntityRemovedEvent>()); mockEventSystem.Receive <ComponentsAddedEvent>().Returns(Observable.Empty <ComponentsAddedEvent>()); mockEventSystem.Receive <ComponentsRemovedEvent>().Returns(Observable.Empty <ComponentsRemovedEvent>()); var dummyEntitySnapshot = new List <IEntity> { new Entity(Guid.NewGuid(), mockEventSystem), new Entity(Guid.NewGuid(), mockEventSystem), new Entity(Guid.NewGuid(), mockEventSystem) }; var cacheableGroupAccessor = new ObservableGroup(mockEventSystem, accessorToken, dummyEntitySnapshot); Assert.Equal(3, cacheableGroupAccessor.CachedEntities.Count); Assert.Equal(dummyEntitySnapshot[0], cacheableGroupAccessor.CachedEntities[dummyEntitySnapshot[0].Id]); Assert.Equal(dummyEntitySnapshot[1], cacheableGroupAccessor.CachedEntities[dummyEntitySnapshot[1].Id]); Assert.Equal(dummyEntitySnapshot[2], cacheableGroupAccessor.CachedEntities[dummyEntitySnapshot[2].Id]); cacheableGroupAccessor.Dispose(); }
public void should_only_remove_applicable_entity_when_entity_removed() { var mockEventSystem = Substitute.For <IEventSystem>(); var accessorToken = new ObservableGroupToken(new[] { typeof(TestComponentOne), typeof(TestComponentTwo) }, "default"); var mockCollection = Substitute.For <IEntityCollection>(); var existingEntityOne = new Entity(Guid.NewGuid(), mockEventSystem); existingEntityOne.AddComponent <TestComponentOne>(); existingEntityOne.AddComponent <TestComponentTwo>(); var existingEntityTwo = new Entity(Guid.NewGuid(), mockEventSystem); existingEntityTwo.AddComponent <TestComponentOne>(); existingEntityTwo.AddComponent <TestComponentTwo>(); var unapplicableEntity = new Entity(Guid.NewGuid(), mockEventSystem); unapplicableEntity.AddComponent <TestComponentOne>(); var underlyingEvent = new ReactiveProperty <EntityRemovedEvent>(new EntityRemovedEvent(unapplicableEntity, mockCollection)); mockEventSystem.Receive <EntityRemovedEvent>().Returns(underlyingEvent); mockEventSystem.Receive <EntityAddedEvent>().Returns(Observable.Empty <EntityAddedEvent>()); mockEventSystem.Receive <ComponentsAddedEvent>().Returns(Observable.Empty <ComponentsAddedEvent>()); mockEventSystem.Receive <ComponentsRemovedEvent>().Returns(Observable.Empty <ComponentsRemovedEvent>()); var cacheableGroupAccessor = new ObservableGroup(mockEventSystem, accessorToken, new IEntity[] { existingEntityOne, existingEntityTwo }); underlyingEvent.SetValueAndForceNotify(new EntityRemovedEvent(existingEntityOne, mockCollection)); Assert.Equal(1, cacheableGroupAccessor.CachedEntities.Count); Assert.Equal <IEntity>(existingEntityTwo, cacheableGroupAccessor.CachedEntities[existingEntityTwo.Id]); }
public void should_remove_entity_and_raise_events_when_entity_removed_with_components() { var collectionName = "default"; var accessorToken = new ObservableGroupToken(new[] { 1, 2 }, new int[0], collectionName); var mockCollection = Substitute.For <IEntityCollection>(); mockCollection.Name.Returns(collectionName); var applicableEntity = Substitute.For <IEntity>(); applicableEntity.Id.Returns(1); applicableEntity.HasComponent(Arg.Is <int>(x => accessorToken.LookupGroup.RequiredComponents.Contains(x))).Returns(true); applicableEntity.HasComponent(Arg.Is <int>(x => accessorToken.LookupGroup.ExcludedComponents.Contains(x))).Returns(false); var mockCollectionNotifier = Substitute.For <INotifyingEntityCollection>(); var entityRemoved = new Subject <CollectionEntityEvent>(); var componentRemoving = new Subject <ComponentsChangedEvent>(); mockCollectionNotifier.EntityRemoved.Returns(entityRemoved); mockCollectionNotifier.EntityAdded.Returns(Observable.Empty <CollectionEntityEvent>()); mockCollectionNotifier.EntityComponentsAdded.Returns(Observable.Empty <ComponentsChangedEvent>()); mockCollectionNotifier.EntityComponentsRemoving.Returns(componentRemoving); mockCollectionNotifier.EntityComponentsRemoved.Returns(Observable.Empty <ComponentsChangedEvent>()); var observableGroup = new ObservableGroup(accessorToken, new[] { applicableEntity }, mockCollectionNotifier); var wasRemovingCalled = 0; observableGroup.OnEntityRemoving.Subscribe(x => wasRemovingCalled++); var wasRemovedCalled = 0; observableGroup.OnEntityRemoved.Subscribe(x => wasRemovedCalled++); componentRemoving.OnNext(new ComponentsChangedEvent(null, applicableEntity, new[] { 1 })); Assert.Contains(applicableEntity, observableGroup.CachedEntities.Values); Assert.Equal(1, wasRemovingCalled); Assert.Equal(0, wasRemovedCalled); wasRemovingCalled = wasRemovedCalled = 0; entityRemoved.OnNext(new CollectionEntityEvent(applicableEntity, null)); Assert.DoesNotContain(applicableEntity, observableGroup.CachedEntities.Values); Assert.Equal(0, wasRemovingCalled); Assert.Equal(1, wasRemovedCalled); }
public void should_add_entity_and_raise_event_when_applicable_entity_added() { var collectionName = "default"; var accessorToken = new ObservableGroupToken(new[] { 1, 2 }, new int[0], collectionName); var mockCollection = Substitute.For <IEntityCollection>(); mockCollection.Name.Returns(collectionName); var applicableEntity = Substitute.For <IEntity>(); applicableEntity.Id.Returns(1); applicableEntity.HasComponent(Arg.Is <int>(x => accessorToken.LookupGroup.RequiredComponents.Contains(x))).Returns(true); var unapplicableEntity = Substitute.For <IEntity>(); unapplicableEntity.Id.Returns(2); unapplicableEntity.HasComponent(Arg.Is <int>(x => accessorToken.LookupGroup.RequiredComponents.Contains(x))).Returns(false); var mockCollectionNotifier = Substitute.For <INotifyingEntityCollection>(); var entityAddedSub = new Subject <CollectionEntityEvent>(); mockCollectionNotifier.EntityAdded.Returns(entityAddedSub); mockCollectionNotifier.EntityRemoved.Returns(Observable.Empty <CollectionEntityEvent>()); mockCollectionNotifier.EntityComponentsAdded.Returns(Observable.Empty <ComponentsChangedEvent>()); mockCollectionNotifier.EntityComponentsRemoving.Returns(Observable.Empty <ComponentsChangedEvent>()); mockCollectionNotifier.EntityComponentsRemoved.Returns(Observable.Empty <ComponentsChangedEvent>()); var observableGroup = new ObservableGroup(accessorToken, new IEntity[0], mockCollectionNotifier); var wasCalled = 0; observableGroup.OnEntityAdded.Subscribe(x => wasCalled++); entityAddedSub.OnNext(new CollectionEntityEvent(unapplicableEntity, mockCollection)); Assert.Empty(observableGroup.CachedEntities); entityAddedSub.OnNext(new CollectionEntityEvent(applicableEntity, mockCollection)); Assert.Equal(1, observableGroup.CachedEntities.Count); Assert.Equal(applicableEntity, observableGroup.CachedEntities[applicableEntity.Id]); Assert.Equal(1, wasCalled); }
public IObservableGroup CreateObservableGroup(IGroup group, string collectionName = null) { var groupAccessorToken = new ObservableGroupToken(group.MatchesComponents.ToArray(), collectionName); if (_groupAccessors.ContainsKey(groupAccessorToken)) { return(_groupAccessors[groupAccessorToken]); } var entityMatches = GetEntitiesFor(group, collectionName); var groupAccessor = ObservableGroupFactory.Create(new ObservableGroupConfiguration { ObservableGroupToken = groupAccessorToken, InitialEntities = entityMatches }); _groupAccessors.Add(groupAccessorToken, groupAccessor); return(_groupAccessors[groupAccessorToken]); }
public void should_correctly_notify_when_matching_entity_removed() { var componentTypes = new[] { typeof(TestComponentOne), typeof(TestComponentTwo) }; var mockEventSystem = Substitute.For <IEventSystem>(); var mockCollection = Substitute.For <IEntityCollection>(); var accessorToken = new ObservableGroupToken(componentTypes, "default"); var fakeEntity1 = new Entity(Guid.Empty, mockEventSystem); fakeEntity1.AddComponent <TestComponentOne>(); fakeEntity1.AddComponent <TestComponentTwo>(); var fakeEntity2 = new Entity(Guid.Empty, mockEventSystem); fakeEntity2.AddComponent <TestComponentOne>(); fakeEntity2.AddComponent <TestComponentThree>(); var timesCalled = 0; mockCollection.Name.Returns("default"); var underlyingEvent = new Subject <EntityRemovedEvent>(); mockEventSystem.Receive <ComponentsAddedEvent>().Returns(Observable.Empty <ComponentsAddedEvent>()); mockEventSystem.Receive <ComponentsRemovedEvent>().Returns(Observable.Empty <ComponentsRemovedEvent>()); mockEventSystem.Receive <EntityAddedEvent>().Returns(Observable.Empty <EntityAddedEvent>()); mockEventSystem.Receive <EntityRemovedEvent>().Returns(underlyingEvent); var observableGroup = new ObservableGroup(mockEventSystem, accessorToken, new IEntity[] { fakeEntity1 }); ObservableExtensions.Subscribe <IEntity>(observableGroup.OnEntityRemoved, x => { Assert.Equal <IEntity>(fakeEntity1, x); timesCalled++; }); underlyingEvent.OnNext(new EntityRemovedEvent(fakeEntity1, mockCollection)); underlyingEvent.OnNext(new EntityRemovedEvent(fakeEntity2, mockCollection)); Assert.Equal(1, timesCalled); }
public void should_include_entity_snapshot_on_creation() { var mockCollectionNotifier = Substitute.For <INotifyingEntityCollection>(); var accessorToken = new ObservableGroupToken(new[] { 1 }, new int[0], "default"); var applicableEntity1 = Substitute.For <IEntity>(); var applicableEntity2 = Substitute.For <IEntity>(); var notApplicableEntity1 = Substitute.For <IEntity>(); applicableEntity1.Id.Returns(1); applicableEntity2.Id.Returns(2); notApplicableEntity1.Id.Returns(3); applicableEntity1.HasComponent(Arg.Any <int>()).Returns(true); applicableEntity2.HasComponent(Arg.Any <int>()).Returns(true); notApplicableEntity1.HasComponent(Arg.Any <int>()).Returns(false); var dummyEntitySnapshot = new List <IEntity> { applicableEntity1, applicableEntity2, notApplicableEntity1 }; mockCollectionNotifier.EntityAdded.Returns(Observable.Empty <CollectionEntityEvent>()); mockCollectionNotifier.EntityRemoved.Returns(Observable.Empty <CollectionEntityEvent>()); mockCollectionNotifier.EntityComponentsAdded.Returns(Observable.Empty <ComponentsChangedEvent>()); mockCollectionNotifier.EntityComponentsRemoving.Returns(Observable.Empty <ComponentsChangedEvent>()); mockCollectionNotifier.EntityComponentsRemoved.Returns(Observable.Empty <ComponentsChangedEvent>()); var observableGroup = new ObservableGroup(accessorToken, dummyEntitySnapshot, mockCollectionNotifier); Assert.Equal(2, observableGroup.CachedEntities.Count); Assert.Contains(applicableEntity1, observableGroup.CachedEntities.Values); Assert.Contains(applicableEntity2, observableGroup.CachedEntities.Values); }
public void should_correctly_differentiate_tokens() { var defaultRequired = new[] { 1 }; var defaultExcluded = new int[0]; var token1 = new ObservableGroupToken(defaultRequired, defaultExcluded); var token2 = new ObservableGroupToken(defaultRequired, defaultExcluded, 1); var token3 = new ObservableGroupToken(defaultRequired, defaultExcluded, 1, 2); Assert.NotEqual(token1, token2); Assert.NotEqual(token1, token3); Assert.NotEqual(token2, token3); Assert.NotEqual(token1.GetHashCode(), token2.GetHashCode()); Assert.NotEqual(token1.GetHashCode(), token3.GetHashCode()); Assert.NotEqual(token2.GetHashCode(), token3.GetHashCode()); Assert.Equal(token1, token1); Assert.Equal(token2, token2); Assert.Equal(token3, token3); Assert.Equal(token1.GetHashCode(), token1.GetHashCode()); Assert.Equal(token2.GetHashCode(), token2.GetHashCode()); Assert.Equal(token3.GetHashCode(), token3.GetHashCode()); }