From 57be9b0a0cccc11fd81be1163a501a758da89f55 Mon Sep 17 00:00:00 2001 From: Ken Schosinsky Date: Mon, 4 Nov 2019 10:53:03 +0100 Subject: [PATCH] add hook for default base aspect applied to all aspects Fixes: #371 --- .../java/com/artemis/ArchetypeBuilder.java | 50 ++- .../src/main/java/com/artemis/Aspect.java | 44 ++- .../artemis/AspectSubscriptionManager.java | 6 +- .../java/com/artemis/ComponentManager.java | 72 ++++- .../java/com/artemis/EntityTransmuter.java | 2 +- .../java/com/artemis/WorldConfiguration.java | 21 +- .../artemis/WorldConfigurationBuilder.java | 13 + .../artemis/annotations/AspectDescriptor.java | 5 + .../java/com/artemis/annotations/Exclude.java | 5 + .../injection/AspectFieldResolver.java | 19 +- .../test/java/com/artemis/ArchetypeTest.java | 47 ++- .../com/artemis/AspectFieldHandlerTest.java | 306 +++++++++++++++--- .../AspectSubscriptionManagerTest.java | 20 ++ .../src/test/java/com/artemis/AspectTest.java | 202 ++++++++++++ .../java/com/artemis/ComponentMapperTest.java | 3 +- .../java/com/artemis/EntitySystemTest.java | 49 +++ .../com/artemis/EntityTransmuterTest.java | 102 +++++- .../com/artemis/component/ComponentZ.java | 6 + 18 files changed, 892 insertions(+), 80 deletions(-) create mode 100644 artemis-core/artemis/src/test/java/com/artemis/AspectTest.java create mode 100644 artemis-core/artemis/src/test/java/com/artemis/component/ComponentZ.java diff --git a/artemis-core/artemis/src/main/java/com/artemis/ArchetypeBuilder.java b/artemis-core/artemis/src/main/java/com/artemis/ArchetypeBuilder.java index 2cb1956d6..cce621166 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/ArchetypeBuilder.java +++ b/artemis-core/artemis/src/main/java/com/artemis/ArchetypeBuilder.java @@ -19,6 +19,7 @@ */ public class ArchetypeBuilder { private final Bag> classes; + private boolean defaults; /** * Constructs an archetype builder containing the composition of the specified parent. @@ -69,6 +70,21 @@ public ArchetypeBuilder add(Class... types) { return this; } + + /** + * Ensure this builder includes the specified component types. + * + * @param types + * @return This instance for chaining. + */ + public ArchetypeBuilder add(Iterable> types) { + for (Class type : types) { + if (!classes.contains(type)) + classes.add(type); + } + + return this; + } /** * Remove the specified component from this builder, if it is present (optional operation). @@ -94,6 +110,34 @@ public ArchetypeBuilder remove(Class... types) { return this; } + + /** + * Remove the specified component from this builder, if it is present (optional operation). + * + * @param types + * @return This instance for chaining. + */ + public ArchetypeBuilder remove(Iterable> types) { + for (Class type : types) { + classes.remove(type); + } + + return this; + } + + /** + * Changes whether the default aspect will be applied to this archetype. + * + * @param defaults + * whether to apply default aspect + * + * @return an archetype that will have the default aspect applied to it + * depending on the value. + */ + public ArchetypeBuilder defaults(boolean defaults) { + this.defaults = defaults; + return this; + } /** * Create a new world specific instance of Archetype based on the current state. @@ -113,9 +157,13 @@ public Archetype build(World world) { * @return new Archetype based on current state */ public Archetype build(World world, String name) { + ComponentManager cm = world.getComponentManager(); + if (defaults) { + add(cm.defaultAspectBuilder.allTypes); + } + ComponentType[] types = resolveTypes(world); - ComponentManager cm = world.getComponentManager(); ComponentMapper[] mappers = new ComponentMapper[types.length]; for (int i = 0, s = mappers.length; s > i; i++) { mappers[i] = cm.getMapper(types[i].getType()); diff --git a/artemis-core/artemis/src/main/java/com/artemis/Aspect.java b/artemis-core/artemis/src/main/java/com/artemis/Aspect.java index 39b73f219..d76322139 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/Aspect.java +++ b/artemis-core/artemis/src/main/java/com/artemis/Aspect.java @@ -206,14 +206,28 @@ public static Aspect.Builder one(Class... types) { public static Aspect.Builder one(Collection> types) { return new Builder().one(types); } + + /** + * Changes whether the default aspect will be applied to this aspect. + * + * @param defaults + * whether to apply default aspect + * + * @return an aspect that will have the default aspect applied to it + * depending on the value. + */ + public static Aspect.Builder defaults(boolean defaults) { + return new Builder().defaults(defaults); + } /** * Constructs instances of {@link Aspect}. */ public static class Builder { - private final Bag> allTypes; - private final Bag> exclusionTypes; - private final Bag> oneTypes; + final Bag> allTypes; + final Bag> exclusionTypes; + final Bag> oneTypes; + boolean defaults = true; private Builder() { allTypes = new Bag>(); @@ -258,7 +272,7 @@ public Builder copy() { * * @return an aspect that can be matched against entities */ - public Builder all(Collection> types) { + public Builder all(Iterable> types) { for (Class t : types) { allTypes.add(t); } @@ -292,7 +306,7 @@ public final Builder one(Class... types) { * * @return an aspect that can be matched against entities */ - public Builder one(Collection> types) { + public Builder one(Iterable> types) { for (Class t : types) oneTypes.add(t); @@ -332,12 +346,27 @@ public final Builder exclude(Class... types) { * * @return an aspect that can be matched against entities */ - public Builder exclude(Collection> types) { + public Builder exclude(Iterable> types) { for (Class t : types) exclusionTypes.add(t); return this; } + + /** + * Changes whether the default aspect will be applied to this aspect. + * + * @param defaults + * whether to apply default aspect + * + * @return an aspect that will have the default aspect applied to it + * depending on the value. + */ + public Builder defaults(boolean defaults) { + this.defaults = defaults; + + return this; + } /** * Bake an aspect. @@ -351,7 +380,7 @@ public Aspect build(World world) { associate(tf, exclusionTypes, aspect.exclusionSet); associate(tf, oneTypes, aspect.oneSet); - return aspect; + return defaults ? world.getComponentManager().applyDefaultAspect(aspect) : aspect; } private static void associate(ComponentTypeFactory tf, Bag> types, BitVector componentBits) { @@ -391,6 +420,7 @@ public String toString() { "all=" + append(allTypes) + ", one=" + append(oneTypes) + ", exclude=" + append(exclusionTypes) + + ", defaults=" + defaults + ']'; } diff --git a/artemis-core/artemis/src/main/java/com/artemis/AspectSubscriptionManager.java b/artemis-core/artemis/src/main/java/com/artemis/AspectSubscriptionManager.java index ff340dd6b..6c38d84a6 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/AspectSubscriptionManager.java +++ b/artemis-core/artemis/src/main/java/com/artemis/AspectSubscriptionManager.java @@ -41,9 +41,9 @@ protected void setWorld(World world) { super.setWorld(world); // making sure the first subscription matches all entities - get(all()); + get(all().defaults(false)); } - + /** *

Gets the entity subscription for the {@link Aspect}. * Subscriptions are only created once per aspect.

@@ -60,6 +60,8 @@ protected void setWorld(World world) { * @return {@link EntitySubscription} for aspect. */ public EntitySubscription get(Aspect.Builder builder) { + world.getComponentManager().applyDefaultAspect(builder); + EntitySubscription subscription = subscriptionMap.get(builder); return (subscription != null) ? subscription : createSubscription(builder); } diff --git a/artemis-core/artemis/src/main/java/com/artemis/ComponentManager.java b/artemis-core/artemis/src/main/java/com/artemis/ComponentManager.java index 1428b39f1..fd755bae3 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/ComponentManager.java +++ b/artemis-core/artemis/src/main/java/com/artemis/ComponentManager.java @@ -32,6 +32,12 @@ public class ComponentManager extends BaseSystem { final ShortBag entityToIdentity; protected final ComponentTypeFactory typeFactory; + Aspect defaultAspect; + Aspect.Builder defaultAspectBuilder; + private final BitVector tmpVector = new BitVector(); + private final Bag> tmpComponentBag = new Bag<>(); + + /** * Creates a new instance of {@link ComponentManager}. */ @@ -74,7 +80,6 @@ static T newInstance(Class componentClass) { } } - /** * Removes all components from deleted entities. * @@ -274,4 +279,69 @@ int allocateIdentity(BitVector componentBits, ComponentManager cm) { return compositionBits.size() - 1; } } + + public Aspect.Builder applyDefaultAspect(Aspect.Builder builder) { + if (defaultAspectBuilder == null || !builder.defaults) { + return builder; + } + + tmpComponentBag.clear(); + tmpComponentBag.addAll(defaultAspectBuilder.allTypes); + tmpComponentBag.removeAll(builder.allTypes); + tmpComponentBag.removeAll(builder.oneTypes); + tmpComponentBag.removeAll(builder.exclusionTypes); + builder.all(tmpComponentBag); + + tmpComponentBag.clear(); + tmpComponentBag.addAll(defaultAspectBuilder.oneTypes); + tmpComponentBag.removeAll(builder.allTypes); + tmpComponentBag.removeAll(builder.oneTypes); + tmpComponentBag.removeAll(builder.exclusionTypes); + builder.one(tmpComponentBag); + + tmpComponentBag.clear(); + tmpComponentBag.addAll(defaultAspectBuilder.exclusionTypes); + tmpComponentBag.removeAll(builder.allTypes); + tmpComponentBag.removeAll(builder.oneTypes); + tmpComponentBag.removeAll(builder.exclusionTypes); + builder.exclude(tmpComponentBag); + + return builder; + } + + public Aspect applyDefaultAspect(Aspect aspect) { + if (defaultAspect == null) { + return aspect; + } + + tmpVector.clear(); + tmpVector.or(defaultAspect.allSet); + tmpVector.andNot(aspect.oneSet); + tmpVector.andNot(aspect.exclusionSet); + aspect.allSet.or(tmpVector); + + tmpVector.clear(); + tmpVector.or(defaultAspect.oneSet); + tmpVector.andNot(aspect.allSet); + tmpVector.andNot(aspect.exclusionSet); + aspect.oneSet.or(tmpVector); + + tmpVector.clear(); + tmpVector.or(defaultAspect.exclusionSet); + tmpVector.andNot(aspect.allSet); + tmpVector.andNot(aspect.oneSet); + aspect.exclusionSet.or(tmpVector); + + return aspect; + } + + void setDefaultAspect(Aspect.Builder aspect) { + if (aspect != null) { + this.defaultAspectBuilder = aspect; + this.defaultAspect = aspect.build(world); + tmpVector.ensureCapacity(defaultAspect.allSet.length()); + tmpVector.ensureCapacity(defaultAspect.oneSet.length()); + tmpVector.ensureCapacity(defaultAspect.exclusionSet.length()); + } + } } diff --git a/artemis-core/artemis/src/main/java/com/artemis/EntityTransmuter.java b/artemis-core/artemis/src/main/java/com/artemis/EntityTransmuter.java index 6377c9394..b32a25c93 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/EntityTransmuter.java +++ b/artemis-core/artemis/src/main/java/com/artemis/EntityTransmuter.java @@ -27,7 +27,7 @@ public final class EntityTransmuter { private final ShortBag entityToIdentity; public EntityTransmuter(World world, Aspect.Builder aspect) { - this(world, world.getAspectSubscriptionManager().get(aspect).getAspect()); + this(world, world.getAspectSubscriptionManager().get(aspect.defaults(false)).getAspect()); } EntityTransmuter(World world, Aspect aspect) { diff --git a/artemis-core/artemis/src/main/java/com/artemis/WorldConfiguration.java b/artemis-core/artemis/src/main/java/com/artemis/WorldConfiguration.java index a0952fae8..a800cfba2 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/WorldConfiguration.java +++ b/artemis-core/artemis/src/main/java/com/artemis/WorldConfiguration.java @@ -38,6 +38,7 @@ public final class WorldConfiguration { private boolean alwaysDelayComponentRemoval = false; private Set> registered = new HashSet>(); + private Aspect.Builder defaultAspect; public WorldConfiguration() { // reserving space for core managers @@ -162,7 +163,11 @@ void initialize(World world, Injector injector, AspectSubscriptionManager asm) { world.invocationStrategy = invocationStrategy; - systems.set(COMPONENT_MANAGER_IDX, world.getComponentManager()); + ComponentManager cm = world.getComponentManager(); + cm.setWorld(world); + cm.setDefaultAspect(defaultAspect); + + systems.set(COMPONENT_MANAGER_IDX, cm); systems.set(ENTITY_MANAGER_IDX, world.getEntityManager()); systems.set(ASPECT_SUBSCRIPTION_MANAGER_IDX, asm); @@ -172,7 +177,7 @@ void initialize(World world, Injector injector, AspectSubscriptionManager asm) { if (ClassReflection.isInstance(Manager.class, system)) { ((Manager) system).registerManager(); } - } + } injector.initialize(world, injectables); @@ -222,4 +227,16 @@ public boolean isAlwaysDelayComponentRemoval() { public void setAlwaysDelayComponentRemoval(boolean value) { this.alwaysDelayComponentRemoval = value; } + + /** + * Sets the default aspect to be applied to all aspects unless explicitly set to not do so. + * + * @param aspect default aspect + * @return + * @return this + */ + public WorldConfiguration setDefaultAspect(Aspect.Builder aspect) { + this.defaultAspect = aspect; + return this; + } } diff --git a/artemis-core/artemis/src/main/java/com/artemis/WorldConfigurationBuilder.java b/artemis-core/artemis/src/main/java/com/artemis/WorldConfigurationBuilder.java index 977cf88b3..cfb4212d6 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/WorldConfigurationBuilder.java +++ b/artemis-core/artemis/src/main/java/com/artemis/WorldConfigurationBuilder.java @@ -28,6 +28,7 @@ public class WorldConfigurationBuilder { private ArtemisPlugin activePlugin; private final InjectionCache cache; private SystemInvocationStrategy invocationStrategy; + private Aspect.Builder defaultAspect; public WorldConfigurationBuilder() { reset(); @@ -46,6 +47,7 @@ public WorldConfiguration build() { registerFieldResolvers(config); registerInvocationStrategies(config); config.setAlwaysDelayComponentRemoval(alwaysDelayComponentRemoval); + config.setDefaultAspect(defaultAspect); reset(); return config; } @@ -266,6 +268,17 @@ public WorldConfigurationBuilder with(ArtemisPlugin... plugins) { addPlugins(plugins); return this; } + + /** + * Sets the default aspect to be applied to all aspects unless explicitly set to not do so. + * + * @param aspect default aspect + * @return this + */ + public WorldConfigurationBuilder defaultAspect(Aspect.Builder aspect) { + this.defaultAspect = aspect; + return this; + } /** * helper to queue systems for registration. diff --git a/artemis-core/artemis/src/main/java/com/artemis/annotations/AspectDescriptor.java b/artemis-core/artemis/src/main/java/com/artemis/annotations/AspectDescriptor.java index c861d1423..5e11be22e 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/annotations/AspectDescriptor.java +++ b/artemis-core/artemis/src/main/java/com/artemis/annotations/AspectDescriptor.java @@ -41,4 +41,9 @@ * @return excluding types */ Class[] exclude() default {}; + + /** + * @return whether to apply default aspect + */ + boolean defaults() default true; } diff --git a/artemis-core/artemis/src/main/java/com/artemis/annotations/Exclude.java b/artemis-core/artemis/src/main/java/com/artemis/annotations/Exclude.java index 875b6615a..f3ec542ae 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/annotations/Exclude.java +++ b/artemis-core/artemis/src/main/java/com/artemis/annotations/Exclude.java @@ -44,4 +44,9 @@ */ Class[] value() default {}; + /** + * @return whether to exclude default aspect + */ + boolean excludeDefaults() default false; + } diff --git a/artemis-core/artemis/src/main/java/com/artemis/injection/AspectFieldResolver.java b/artemis-core/artemis/src/main/java/com/artemis/injection/AspectFieldResolver.java index ed4769493..fd6f99d7d 100644 --- a/artemis-core/artemis/src/main/java/com/artemis/injection/AspectFieldResolver.java +++ b/artemis-core/artemis/src/main/java/com/artemis/injection/AspectFieldResolver.java @@ -26,12 +26,14 @@ public class AspectFieldResolver implements FieldResolver { private static final Class[] EMPTY_COMPONENT_CLASS_ARRAY = new Class[0]; private World world; + private ComponentManager cm; private IdentityHashMap fields = new IdentityHashMap(); @Override public void initialize(World world) { this.world = world; + this.cm = world.getComponentManager(); } @Override @@ -46,7 +48,7 @@ public Object resolve(Object target, Class fieldType, Field field) { } else if (Aspect.Builder.class == fieldType) { return aspect; } else if (EntityTransmuter.class == fieldType) { - return new EntityTransmuter(world, aspect); + return new EntityTransmuter(world, aspect.defaults(false)); } else if (EntitySubscription.class == fieldType) { return world.getAspectSubscriptionManager().get(aspect); } else if (Archetype.class == fieldType) { @@ -88,13 +90,22 @@ private AspectDescriptor descriptor(Field field) { } private Aspect.Builder toAspect(AspectDescriptor ad) { - return all(ad.all()).one(ad.one()).exclude(ad.exclude()); + Aspect.Builder aspect = all(ad.all()).one(ad.one()).exclude(ad.exclude()).defaults(ad.defaults()); + + return ad.defaults() + ? cm.applyDefaultAspect(aspect) + : aspect; } private Aspect.Builder toAspect(All all, One one, Exclude exclude) { - return all(all != null ? all.value() : EMPTY_COMPONENT_CLASS_ARRAY) + Aspect.Builder aspect = all(all != null ? all.value() : EMPTY_COMPONENT_CLASS_ARRAY) .one(one != null ? one.value() : EMPTY_COMPONENT_CLASS_ARRAY) - .exclude(exclude != null ? exclude.value() : EMPTY_COMPONENT_CLASS_ARRAY); + .exclude(exclude != null ? exclude.value() : EMPTY_COMPONENT_CLASS_ARRAY) + .defaults(exclude == null || !exclude.excludeDefaults()); + + return exclude == null || !exclude.excludeDefaults() + ? cm.applyDefaultAspect(aspect) + : aspect; } private Class[] allComponents(Field field) { diff --git a/artemis-core/artemis/src/test/java/com/artemis/ArchetypeTest.java b/artemis-core/artemis/src/test/java/com/artemis/ArchetypeTest.java index e9258ec63..8e15f1cce 100644 --- a/artemis-core/artemis/src/test/java/com/artemis/ArchetypeTest.java +++ b/artemis-core/artemis/src/test/java/com/artemis/ArchetypeTest.java @@ -11,21 +11,26 @@ import com.artemis.component.ComponentX; import com.artemis.component.ComponentY; +import com.artemis.component.ComponentZ; import com.artemis.systems.EntityProcessingSystem; public class ArchetypeTest { private World world; private Es1 es1; private Es2 es2; + private EsDefaults esDefaults; private Archetype arch1; private Archetype arch2; private Archetype arch3; + private Archetype archDefaults; @Before public void init() { world = new World(new WorldConfiguration() .setSystem(new Es1()) - .setSystem(new Es2())); + .setSystem(new Es2()) + .setSystem(new EsDefaults()) + .setDefaultAspect(Aspect.all(ComponentZ.class))); world.inject(this); arch1 = new ArchetypeBuilder() @@ -33,9 +38,15 @@ public void init() { arch2 = new ArchetypeBuilder() .add(ComponentX.class) .add(ComponentY.class) + .defaults(false) .build(world); arch3 = new ArchetypeBuilder() .add(ComponentX.class) + .defaults(false) + .build(world); + archDefaults = new ArchetypeBuilder() + .add(ComponentX.class) + .defaults(true) .build(world); } @@ -44,17 +55,20 @@ public void test_composition_id() { assertEquals(0, arch1.transmuter.compositionId); assertEquals(1, arch2.transmuter.compositionId); assertEquals(2, arch3.transmuter.compositionId); + assertEquals(3, archDefaults.transmuter.compositionId); } @Test public void test_inherited_archetypes_and_composition_resolution() { - Archetype arch4 = new ArchetypeBuilder(arch2).build(world); - Archetype arch5 = new ArchetypeBuilder(arch2).remove(ComponentY.class).build(world); - Archetype arch6 = new ArchetypeBuilder(arch2).remove(ComponentX.class).build(world); + Archetype arch4 = new ArchetypeBuilder(arch2).defaults(false).build(world); + Archetype arch5 = new ArchetypeBuilder(arch2).remove(ComponentY.class).defaults(false).build(world); + Archetype arch6 = new ArchetypeBuilder(arch2).remove(ComponentX.class).defaults(false).build(world); + Archetype arch7 = new ArchetypeBuilder(arch2).remove(ComponentY.class).defaults(true).build(world); assertEquals(arch2.transmuter.compositionId, arch4.transmuter.compositionId); assertEquals(arch3.transmuter.compositionId, arch5.transmuter.compositionId); - assertEquals(3, arch6.transmuter.compositionId); + assertEquals(4, arch6.transmuter.compositionId); + assertEquals(archDefaults.transmuter.compositionId, arch7.transmuter.compositionId); } @Test @@ -62,11 +76,13 @@ public void test_adding_to_systems() { archetypeEntity(arch1, 2); // never inserted archetypeEntity(arch2, 4); // es1 archetypeEntity(arch3, 8); // es1 + 2 + archetypeEntity(archDefaults, 16); // esDefaults world.process(); assertEquals(12, es1.getSubscription().getEntities().size()); assertEquals(8, es2.getSubscription().getEntities().size()); + assertEquals(16, esDefaults.getSubscription().getEntities().size()); world.process(); } @@ -128,7 +144,7 @@ private static class Es1 extends EntityProcessingSystem { @SuppressWarnings("unchecked") public Es1() { - super(Aspect.all(ComponentX.class)); + super(Aspect.all(ComponentX.class).exclude(ComponentZ.class)); } @Override @@ -143,12 +159,29 @@ private static class Es2 extends EntityProcessingSystem { @SuppressWarnings("unchecked") public Es2() { - super(Aspect.all(ComponentX.class).exclude(ComponentY.class)); + super(Aspect.all(ComponentX.class).exclude(ComponentY.class, ComponentZ.class)); + } + + @Override + protected void process(Entity e) { + assertNotNull(componentXMapper.get(e)); + } + } + + private static class EsDefaults extends EntityProcessingSystem { + + private ComponentMapper componentXMapper; + private ComponentMapper componentZMapper; + + @SuppressWarnings("unchecked") + public EsDefaults() { + super(Aspect.all(ComponentX.class, ComponentZ.class).exclude(ComponentY.class)); } @Override protected void process(Entity e) { assertNotNull(componentXMapper.get(e)); + assertNotNull(componentZMapper.get(e)); } } } diff --git a/artemis-core/artemis/src/test/java/com/artemis/AspectFieldHandlerTest.java b/artemis-core/artemis/src/test/java/com/artemis/AspectFieldHandlerTest.java index 582b1b737..2c8c243a2 100644 --- a/artemis-core/artemis/src/test/java/com/artemis/AspectFieldHandlerTest.java +++ b/artemis-core/artemis/src/test/java/com/artemis/AspectFieldHandlerTest.java @@ -14,8 +14,14 @@ public class AspectFieldHandlerTest { private Aspect.Builder reference = all(ComponentX.class, ComponentY.class) - .one(ReusedComponent.class, EntityHolder.class) - .exclude(PooledString.class); + .one(ReusedComponent.class, EntityHolder.class) + .exclude(PooledString.class) + .defaults(false); + + private Aspect.Builder referenceDefaults = all(ComponentX.class, ComponentY.class, ComponentZ.class) + .one(ReusedComponent.class, EntityHolder.class) + .exclude(PooledString.class) + .defaults(false); @Test public void inject_aspect_fields_pojo() { @@ -42,6 +48,51 @@ public void inject_aspect_fields_pojo() { checkArchetype(world, withAspectFields.archetypeAll); } + @Test + public void inject_aspect_fields_pojo_defaults() { + WorldConfiguration worldConfiguration = new WorldConfiguration() + .setSystem(new SomeSystem()) + .setDefaultAspect(Aspect.all(ComponentZ.class)) + .register(new Object()); + World world = new World(worldConfiguration); + + // no defaults + ObjectAspectFields withAspectFields = new ObjectAspectFields(); + world.inject(withAspectFields); + + assertEquals(reference, withAspectFields.ab); + assertNotNull(withAspectFields.aspect); + assertEquals(reference, withAspectFields.sub.getAspectBuilder()); + assertNotNull(withAspectFields.transmuter); + + checkArchetype(world, withAspectFields.archetype); + + assertEquals(reference, withAspectFields.abAllOneExclude); + assertNotNull(withAspectFields.aspectAllOneExclude); + assertEquals(reference, withAspectFields.subAllOneExclude.getAspectBuilder()); + assertNotNull(withAspectFields.transmuterAllOneExclude); + + checkArchetype(world, withAspectFields.archetypeAll); + + // defaults + ObjectAspectFieldsDefaults withAspectFieldsDefaults = new ObjectAspectFieldsDefaults(); + world.inject(withAspectFieldsDefaults); + + assertEquals(referenceDefaults, withAspectFieldsDefaults.abDefaults); + assertNotNull(withAspectFieldsDefaults.aspectDefaults); + assertEquals(referenceDefaults, withAspectFieldsDefaults.subDefaults.getAspectBuilder()); + assertNotNull(withAspectFieldsDefaults.transmuterDefaults); + + checkArchetypeDefaults(world, withAspectFieldsDefaults.archetypeDefaults); + + assertEquals(referenceDefaults, withAspectFieldsDefaults.abAllOneExcludeDefaults); + assertNotNull(withAspectFieldsDefaults.aspectAllOneExcludeDefaults); + assertEquals(referenceDefaults, withAspectFieldsDefaults.subAllOneExcludeDefaults.getAspectBuilder()); + assertNotNull(withAspectFieldsDefaults.transmuterAllOneExcludeDefaults); + + checkArchetypeDefaults(world, withAspectFieldsDefaults.archetypeAllDefaults); + } + @Test public void inject_aspect_fields_system() { WorldConfiguration worldConfiguration = new WorldConfiguration() @@ -59,120 +110,285 @@ public void inject_aspect_fields_system() { checkArchetype(world, withAspectFields.archetype); } + @Test + public void inject_aspect_fields_system_defaults() { + WorldConfiguration worldConfiguration = new WorldConfiguration() + .setSystem(new SomeSystem()) + .setSystem(new SomeSystemDefaults()) + .setDefaultAspect(Aspect.all(ComponentZ.class)) + .register(new Object()); + World world = new World(worldConfiguration); + + // no defaults + SomeSystem withAspectFields = world.getSystem(SomeSystem.class); + + assertEquals(reference, withAspectFields.ab); + assertNotNull(withAspectFields.aspect); + assertEquals(reference, withAspectFields.sub.getAspectBuilder()); + assertNotNull(withAspectFields.transmuter); + + checkArchetype(world, withAspectFields.archetype); + + // defaults + SomeSystemDefaults withAspectFieldsDefaults = world.getSystem(SomeSystemDefaults.class); + + assertEquals(referenceDefaults, withAspectFieldsDefaults.abDefaults); + assertNotNull(withAspectFieldsDefaults.aspectDefaults); + assertEquals(referenceDefaults, withAspectFieldsDefaults.subDefaults.getAspectBuilder()); + assertNotNull(withAspectFieldsDefaults.transmuterDefaults); + + checkArchetypeDefaults(world, withAspectFieldsDefaults.archetypeDefaults); + } + private static void checkArchetype(World world, Archetype archetype) { Entity e = world.getEntity(world.create(archetype)); assertNotNull(e.getComponent(ComponentX.class)); assertNotNull(e.getComponent(ReusedComponent.class)); assertNull(e.getComponent(ComponentY.class)); } + + private static void checkArchetypeDefaults(World world, Archetype archetype) { + Entity e = world.getEntity(world.create(archetype)); + assertNotNull(e.getComponent(ComponentX.class)); + assertNotNull(e.getComponent(ReusedComponent.class)); + assertNull(e.getComponent(ComponentY.class)); + assertNull(e.getComponent(ComponentZ.class)); + } private static class ObjectAspectFields { @AspectDescriptor( - all = {ComponentX.class, ComponentY.class}, - exclude = PooledString.class, - one = {ReusedComponent.class, EntityHolder.class}) + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = false) public EntitySubscription sub; - + @AspectDescriptor( - all = {ComponentX.class, ComponentY.class}, - exclude = PooledString.class, - one = {ReusedComponent.class, EntityHolder.class}) + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = false) public EntityTransmuter transmuter; - + @AspectDescriptor( - all = {ComponentX.class, ComponentY.class}, - exclude = PooledString.class, - one = {ReusedComponent.class, EntityHolder.class}) + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = false) public Aspect aspect; - + @AspectDescriptor( - all = {ComponentX.class, ComponentY.class}, - exclude = PooledString.class, - one = {ReusedComponent.class, EntityHolder.class}) + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = false) public Aspect.Builder ab; @AspectDescriptor( - all = {ComponentX.class, ReusedComponent.class}) + all = {ComponentX.class, ReusedComponent.class}, + defaults = false) public Archetype archetype; + + @All({ ComponentX.class, ComponentY.class }) + @Exclude(value = PooledString.class, excludeDefaults = true) + @One({ ReusedComponent.class, EntityHolder.class }) + public EntitySubscription subAllOneExclude; + + @All({ ComponentX.class, ComponentY.class }) + @Exclude(value = PooledString.class, excludeDefaults = true) + @One({ ReusedComponent.class, EntityHolder.class }) + public EntityTransmuter transmuterAllOneExclude; + + @All({ ComponentX.class, ComponentY.class }) + @Exclude(value = PooledString.class, excludeDefaults = true) + @One({ ReusedComponent.class, EntityHolder.class }) + public Aspect aspectAllOneExclude; + + @All({ ComponentX.class, ComponentY.class }) + @Exclude(value = PooledString.class, excludeDefaults = true) + @One({ ReusedComponent.class, EntityHolder.class }) + public Aspect.Builder abAllOneExclude; + + @All({ ComponentX.class, ReusedComponent.class }) + @Exclude(excludeDefaults = true) + public Archetype archetypeAll; + } + + private static class ObjectAspectFieldsDefaults { + @AspectDescriptor( + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}) + public EntitySubscription subDefaults; + + @AspectDescriptor( + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}) + public EntityTransmuter transmuterDefaults; + + @AspectDescriptor( + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}) + public Aspect aspectDefaults; + + @AspectDescriptor( + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}) + public Aspect.Builder abDefaults; + + @AspectDescriptor( + all = {ComponentX.class, ReusedComponent.class}) + public Archetype archetypeDefaults; @All({ ComponentX.class, ComponentY.class }) @Exclude(PooledString.class) @One({ ReusedComponent.class, EntityHolder.class }) - public EntitySubscription subAllOneExclude; + public EntitySubscription subAllOneExcludeDefaults; @All({ ComponentX.class, ComponentY.class }) @Exclude(PooledString.class) @One({ ReusedComponent.class, EntityHolder.class }) - public EntityTransmuter transmuterAllOneExclude; + public EntityTransmuter transmuterAllOneExcludeDefaults; @All({ ComponentX.class, ComponentY.class }) @Exclude(PooledString.class) @One({ ReusedComponent.class, EntityHolder.class }) - public Aspect aspectAllOneExclude; + public Aspect aspectAllOneExcludeDefaults; @All({ ComponentX.class, ComponentY.class }) @Exclude(PooledString.class) @One({ ReusedComponent.class, EntityHolder.class }) - public Aspect.Builder abAllOneExclude; + public Aspect.Builder abAllOneExcludeDefaults; @All({ ComponentX.class, ReusedComponent.class }) - public Archetype archetypeAll; + public Archetype archetypeAllDefaults; } private static class SomeSystem extends BaseSystem { @AspectDescriptor( - all = {ComponentX.class, ComponentY.class}, - exclude = PooledString.class, - one = {ReusedComponent.class, EntityHolder.class}) - public EntitySubscription sub; + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = false) + public EntitySubscription sub; @AspectDescriptor( all = {ComponentX.class, ComponentY.class}, exclude = PooledString.class, - one = {ReusedComponent.class, EntityHolder.class}) + one = {ReusedComponent.class, EntityHolder.class}, + defaults = false) public EntityTransmuter transmuter; - + @AspectDescriptor( - all = {ComponentX.class, ComponentY.class}, - exclude = PooledString.class, - one = {ReusedComponent.class, EntityHolder.class}) - public Aspect aspect; - + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = false) + public Aspect aspect; + @AspectDescriptor( - all = {ComponentX.class, ComponentY.class}, - exclude = PooledString.class, - one = {ReusedComponent.class, EntityHolder.class}) - public Aspect.Builder ab; - + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = false) + public Aspect.Builder ab; + @AspectDescriptor( - all = {ComponentX.class, ReusedComponent.class}) - public Archetype archetype; + all = {ComponentX.class, ReusedComponent.class}, + defaults = false) + public Archetype archetype; @All({ ComponentX.class, ComponentY.class }) - @Exclude(PooledString.class) + @Exclude(value = PooledString.class, excludeDefaults = true) @One({ ReusedComponent.class, EntityHolder.class }) public EntitySubscription subAllOneExclude; @All({ ComponentX.class, ComponentY.class }) - @Exclude(PooledString.class) + @Exclude(value = PooledString.class, excludeDefaults = true) @One({ ReusedComponent.class, EntityHolder.class }) public EntityTransmuter transmuterAllOneExclude; @All({ ComponentX.class, ComponentY.class }) - @Exclude(PooledString.class) + @Exclude(value = PooledString.class, excludeDefaults = true) @One({ ReusedComponent.class, EntityHolder.class }) public Aspect aspectAllOneExclude; @All({ ComponentX.class, ComponentY.class }) - @Exclude(PooledString.class) + @Exclude(value = PooledString.class, excludeDefaults = true) @One({ ReusedComponent.class, EntityHolder.class }) public Aspect.Builder abAllOneExclude; @All({ ComponentX.class, ReusedComponent.class }) + @Exclude(excludeDefaults = true) public Archetype archetypeAll; @Override protected void processSystem() {} } + private static class SomeSystemDefaults extends BaseSystem { + @AspectDescriptor( + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = true) + public EntitySubscription subDefaults; + + @AspectDescriptor( + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = true) + public EntityTransmuter transmuterDefaults; + + @AspectDescriptor( + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = true) + public Aspect aspectDefaults; + + @AspectDescriptor( + all = {ComponentX.class, ComponentY.class}, + exclude = PooledString.class, + one = {ReusedComponent.class, EntityHolder.class}, + defaults = true) + public Aspect.Builder abDefaults; + + @AspectDescriptor( + all = {ComponentX.class, ReusedComponent.class}, + defaults = true) + public Archetype archetypeDefaults; + + @All({ ComponentX.class, ComponentY.class }) + @Exclude(value = PooledString.class, excludeDefaults = false) + @One({ ReusedComponent.class, EntityHolder.class }) + public EntitySubscription subAllOneExcludeDefaults; + + @All({ ComponentX.class, ComponentY.class }) + @Exclude(value = PooledString.class, excludeDefaults = false) + @One({ ReusedComponent.class, EntityHolder.class }) + public EntityTransmuter transmuterAllOneExcludeDefaults; + + @All({ ComponentX.class, ComponentY.class }) + @Exclude(value = PooledString.class, excludeDefaults = false) + @One({ ReusedComponent.class, EntityHolder.class }) + public Aspect aspectAllOneExcludeDefaults; + + @All({ ComponentX.class, ComponentY.class }) + @Exclude(value = PooledString.class, excludeDefaults = false) + @One({ ReusedComponent.class, EntityHolder.class }) + public Aspect.Builder abAllOneExcludeDefaults; + + @All({ ComponentX.class, ReusedComponent.class }) + @Exclude(excludeDefaults = false) + public Archetype archetypeAllDefaults; + + @Override + protected void processSystem() {} + } + } diff --git a/artemis-core/artemis/src/test/java/com/artemis/AspectSubscriptionManagerTest.java b/artemis-core/artemis/src/test/java/com/artemis/AspectSubscriptionManagerTest.java index ade06d48c..7fd24631e 100644 --- a/artemis-core/artemis/src/test/java/com/artemis/AspectSubscriptionManagerTest.java +++ b/artemis-core/artemis/src/test/java/com/artemis/AspectSubscriptionManagerTest.java @@ -2,6 +2,7 @@ import com.artemis.component.ComponentX; import com.artemis.component.ComponentY; +import com.artemis.component.ComponentZ; import com.artemis.systems.IteratingSystem; import com.artemis.utils.IntBag; import org.junit.Before; @@ -122,6 +123,25 @@ public void create_delete_same_tick() { w.getEntity(es.replacedEntityId).getCompositionId()); } + @Test + public void asm_adds_default_aspect() { + world = new World(new WorldConfiguration() + .setSystem(new BootstrappingManager()) + .setDefaultAspect(Aspect.all(ComponentZ.class))); + AspectSubscriptionManager asm = world.getAspectSubscriptionManager(); + EntitySubscription sub = asm.get(all(ComponentX.class)); + SubListener listener = new SubListener(); + sub.addSubscriptionListener(listener); + + entity(ComponentX.class); + world.process(); + assertEquals(0, listener.totalInserted); + + entity(ComponentX.class, ComponentZ.class); + world.process(); + assertEquals(1, listener.totalInserted); + } + public static class CreateInremoveSystem extends IteratingSystem { private ComponentMapper componentXMapper; diff --git a/artemis-core/artemis/src/test/java/com/artemis/AspectTest.java b/artemis-core/artemis/src/test/java/com/artemis/AspectTest.java new file mode 100644 index 000000000..cc114a8fd --- /dev/null +++ b/artemis-core/artemis/src/test/java/com/artemis/AspectTest.java @@ -0,0 +1,202 @@ +package com.artemis; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.artemis.component.ComponentX; +import com.artemis.component.ComponentY; +import com.artemis.component.ComponentZ; + +public class AspectTest { + + private ComponentMapper xM; + private ComponentMapper yM; + private ComponentMapper zM; + + @Test + public void testNoDefaultAspect() { + World world = world(null); + ComponentTypeFactory ctf = world.getComponentManager().getTypeFactory(); + int indexX = ctf.getIndexFor(ComponentX.class); + int indexY = ctf.getIndexFor(ComponentY.class); + int indexZ = ctf.getIndexFor(ComponentZ.class); + + Aspect defaultAspect = Aspect.all().build(world); + assertFalse(defaultAspect.allSet.unsafeGet(indexX)); + assertFalse(defaultAspect.allSet.unsafeGet(indexY)); + assertFalse(defaultAspect.allSet.unsafeGet(indexZ)); + assertFalse(defaultAspect.oneSet.unsafeGet(indexX)); + assertFalse(defaultAspect.oneSet.unsafeGet(indexY)); + assertFalse(defaultAspect.oneSet.unsafeGet(indexZ)); + assertFalse(defaultAspect.exclusionSet.unsafeGet(indexX)); + assertFalse(defaultAspect.exclusionSet.unsafeGet(indexY)); + assertFalse(defaultAspect.exclusionSet.unsafeGet(indexZ)); + + Aspect aspect = Aspect.all(ComponentX.class).one(ComponentY.class).exclude(ComponentZ.class).build(world); + assertTrue(aspect.allSet.unsafeGet(indexX)); + assertFalse(aspect.allSet.unsafeGet(indexY)); + assertFalse(aspect.allSet.unsafeGet(indexZ)); + assertFalse(aspect.oneSet.unsafeGet(indexX)); + assertTrue(aspect.oneSet.unsafeGet(indexY)); + assertFalse(aspect.oneSet.unsafeGet(indexZ)); + assertFalse(aspect.exclusionSet.unsafeGet(indexX)); + assertFalse(aspect.exclusionSet.unsafeGet(indexY)); + assertTrue(aspect.exclusionSet.unsafeGet(indexZ)); + } + + @Test + public void testDefaultAspect() { + World world = world(Aspect.all(ComponentX.class).one(ComponentY.class).exclude(ComponentZ.class)); + ComponentTypeFactory ctf = world.getComponentManager().getTypeFactory(); + int indexX = ctf.getIndexFor(ComponentX.class); + int indexY = ctf.getIndexFor(ComponentY.class); + int indexZ = ctf.getIndexFor(ComponentZ.class); + + Aspect defaultAspect = Aspect.all().build(world); + assertTrue(defaultAspect.allSet.unsafeGet(indexX)); + assertFalse(defaultAspect.allSet.unsafeGet(indexY)); + assertFalse(defaultAspect.allSet.unsafeGet(indexZ)); + assertFalse(defaultAspect.oneSet.unsafeGet(indexX)); + assertTrue(defaultAspect.oneSet.unsafeGet(indexY)); + assertFalse(defaultAspect.oneSet.unsafeGet(indexZ)); + assertFalse(defaultAspect.exclusionSet.unsafeGet(indexX)); + assertFalse(defaultAspect.exclusionSet.unsafeGet(indexY)); + assertTrue(defaultAspect.exclusionSet.unsafeGet(indexZ)); + } + + @Test + public void testDefaultAspectDisabled() { + World world = world(Aspect.all(ComponentX.class).one(ComponentY.class).exclude(ComponentZ.class)); + ComponentTypeFactory ctf = world.getComponentManager().getTypeFactory(); + int indexX = ctf.getIndexFor(ComponentX.class); + int indexY = ctf.getIndexFor(ComponentY.class); + int indexZ = ctf.getIndexFor(ComponentZ.class); + + Aspect defaultAspect = Aspect.defaults(false).build(world); + assertFalse(defaultAspect.allSet.unsafeGet(indexX)); + assertFalse(defaultAspect.allSet.unsafeGet(indexY)); + assertFalse(defaultAspect.allSet.unsafeGet(indexZ)); + assertFalse(defaultAspect.oneSet.unsafeGet(indexX)); + assertFalse(defaultAspect.oneSet.unsafeGet(indexY)); + assertFalse(defaultAspect.oneSet.unsafeGet(indexZ)); + assertFalse(defaultAspect.exclusionSet.unsafeGet(indexX)); + assertFalse(defaultAspect.exclusionSet.unsafeGet(indexY)); + assertFalse(defaultAspect.exclusionSet.unsafeGet(indexZ)); + } + + @Test + public void testDefaultAspect_ComponentsSkippedIfUsed() { + World world = world(Aspect.all(ComponentX.class).one(ComponentY.class).exclude(ComponentZ.class)); + ComponentTypeFactory ctf = world.getComponentManager().getTypeFactory(); + int indexX = ctf.getIndexFor(ComponentX.class); + int indexY = ctf.getIndexFor(ComponentY.class); + int indexZ = ctf.getIndexFor(ComponentZ.class); + + Aspect aspect = Aspect.all(ComponentY.class).one(ComponentZ.class).exclude(ComponentX.class).build(world); + assertFalse(aspect.allSet.unsafeGet(indexX)); + assertTrue(aspect.allSet.unsafeGet(indexY)); + assertFalse(aspect.allSet.unsafeGet(indexZ)); + assertFalse(aspect.oneSet.unsafeGet(indexX)); + assertFalse(aspect.oneSet.unsafeGet(indexY)); + assertTrue(aspect.oneSet.unsafeGet(indexZ)); + assertTrue(aspect.exclusionSet.unsafeGet(indexX)); + assertFalse(aspect.exclusionSet.unsafeGet(indexY)); + assertFalse(aspect.exclusionSet.unsafeGet(indexZ)); + } + + @Test + public void testDefaultAspect_ComponentsSkippedIfUsed_All() { + World world = world(Aspect.all(ComponentX.class).one(ComponentY.class).exclude(ComponentZ.class)); + ComponentTypeFactory ctf = world.getComponentManager().getTypeFactory(); + int indexX = ctf.getIndexFor(ComponentX.class); + int indexY = ctf.getIndexFor(ComponentY.class); + int indexZ = ctf.getIndexFor(ComponentZ.class); + + Aspect aspect = Aspect.all(ComponentY.class, ComponentZ.class).build(world); + assertTrue(aspect.allSet.unsafeGet(indexX)); + assertTrue(aspect.allSet.unsafeGet(indexY)); + assertTrue(aspect.allSet.unsafeGet(indexZ)); + assertFalse(aspect.oneSet.unsafeGet(indexX)); + assertFalse(aspect.oneSet.unsafeGet(indexY)); + assertFalse(aspect.oneSet.unsafeGet(indexZ)); + assertFalse(aspect.exclusionSet.unsafeGet(indexX)); + assertFalse(aspect.exclusionSet.unsafeGet(indexY)); + assertFalse(aspect.exclusionSet.unsafeGet(indexZ)); + } + + @Test + public void testDefaultAspect_ComponentsSkippedIfUsed_One() { + World world = world(Aspect.all(ComponentX.class).one(ComponentY.class).exclude(ComponentZ.class)); + ComponentTypeFactory ctf = world.getComponentManager().getTypeFactory(); + int indexX = ctf.getIndexFor(ComponentX.class); + int indexY = ctf.getIndexFor(ComponentY.class); + int indexZ = ctf.getIndexFor(ComponentZ.class); + + Aspect aspect = Aspect.one(ComponentX.class, ComponentZ.class).build(world); + assertFalse(aspect.allSet.unsafeGet(indexX)); + assertFalse(aspect.allSet.unsafeGet(indexY)); + assertFalse(aspect.allSet.unsafeGet(indexZ)); + assertTrue(aspect.oneSet.unsafeGet(indexX)); + assertTrue(aspect.oneSet.unsafeGet(indexY)); + assertTrue(aspect.oneSet.unsafeGet(indexZ)); + assertFalse(aspect.exclusionSet.unsafeGet(indexX)); + assertFalse(aspect.exclusionSet.unsafeGet(indexY)); + assertFalse(aspect.exclusionSet.unsafeGet(indexZ)); + } + + @Test + public void testDefaultAspect_ComponentsSkippedIfUsed_Exclude() { + World world = world(Aspect.all(ComponentX.class).one(ComponentY.class).exclude(ComponentZ.class)); + ComponentTypeFactory ctf = world.getComponentManager().getTypeFactory(); + int indexX = ctf.getIndexFor(ComponentX.class); + int indexY = ctf.getIndexFor(ComponentY.class); + int indexZ = ctf.getIndexFor(ComponentZ.class); + + Aspect aspect = Aspect.exclude(ComponentX.class, ComponentY.class).build(world); + assertFalse(aspect.allSet.unsafeGet(indexX)); + assertFalse(aspect.allSet.unsafeGet(indexY)); + assertFalse(aspect.allSet.unsafeGet(indexZ)); + assertFalse(aspect.oneSet.unsafeGet(indexX)); + assertFalse(aspect.oneSet.unsafeGet(indexY)); + assertFalse(aspect.oneSet.unsafeGet(indexZ)); + assertTrue(aspect.exclusionSet.unsafeGet(indexX)); + assertTrue(aspect.exclusionSet.unsafeGet(indexY)); + assertTrue(aspect.exclusionSet.unsafeGet(indexZ)); + } + + @Test + public void testDefaultAspectEntityTest() { + World world = world(Aspect.all(ComponentX.class).one(ComponentY.class).exclude(ComponentZ.class)); + world.inject(this); + + Entity entity = world.createEntity(); + + EntitySubscription subscription = world.getAspectSubscriptionManager().get(Aspect.all(ComponentY.class)); + Aspect aspect = subscription.getAspect(); + + world.process(); + assertEquals(false, aspect.isInterested(entity)); + + world.process(); + xM.create(entity); + assertEquals(false, aspect.isInterested(entity)); + + world.process(); + yM.create(entity); + assertEquals(true, aspect.isInterested(entity)); + + world.process(); + zM.create(entity); + assertEquals(false, aspect.isInterested(entity)); + } + + private World world(Aspect.Builder aspect) { + return new World(new WorldConfigurationBuilder() + .defaultAspect(aspect) + .build()); + } + +} diff --git a/artemis-core/artemis/src/test/java/com/artemis/ComponentMapperTest.java b/artemis-core/artemis/src/test/java/com/artemis/ComponentMapperTest.java index 5ab16a989..365679b97 100644 --- a/artemis-core/artemis/src/test/java/com/artemis/ComponentMapperTest.java +++ b/artemis-core/artemis/src/test/java/com/artemis/ComponentMapperTest.java @@ -3,6 +3,7 @@ import com.artemis.annotations.Wire; import com.artemis.component.ComponentX; import com.artemis.component.ComponentY; +import com.artemis.component.ComponentZ; import com.artemis.systems.EntityProcessingSystem; import org.junit.Assert; import org.junit.Test; @@ -197,7 +198,7 @@ protected void process(Entity e) { } protected void createAndProcessWorld(BaseSystem system) { - final World world = new World(new WorldConfiguration().setSystem(system)); + final World world = new World(new WorldConfiguration().setSystem(system).setDefaultAspect(Aspect.all(ComponentZ.class))); world.createEntity().edit().create(TestMarker.class); world.process(); } diff --git a/artemis-core/artemis/src/test/java/com/artemis/EntitySystemTest.java b/artemis-core/artemis/src/test/java/com/artemis/EntitySystemTest.java index 015fd3746..6f1e2b797 100644 --- a/artemis-core/artemis/src/test/java/com/artemis/EntitySystemTest.java +++ b/artemis-core/artemis/src/test/java/com/artemis/EntitySystemTest.java @@ -2,6 +2,7 @@ import static org.junit.Assert.assertEquals; +import com.artemis.annotations.All; import com.artemis.systems.EntityProcessingSystem; import org.junit.Test; @@ -34,7 +35,40 @@ public void aspect_exclude_only() { w.process(); assertEquals(1, es1.getSubscription().getEntities().size()); + assertEquals(1, es1.subscription.getEntities().size()); assertEquals(1, es2.getSubscription().getEntities().size()); + assertEquals(1, es2.subscription.getEntities().size()); + } + + @Test + public void default_aspect() { + DefaultAspectSystem es = new DefaultAspectSystem(); + AnnotatedDefaultAspectSystem esAnnotated = new AnnotatedDefaultAspectSystem(); + World w = new World(new WorldConfigurationBuilder() + .with(es, esAnnotated) + .defaultAspect(Aspect.exclude(C2.class)) + .build()); + + EntityEdit e = w.createEntity().edit(); + w.process(); + assertEquals(0, es.getSubscription().getEntities().size()); + assertEquals(0, es.subscription.getEntities().size()); + assertEquals(0, esAnnotated.getSubscription().getEntities().size()); + assertEquals(0, esAnnotated.subscription.getEntities().size()); + + e.add(new C()); + w.process(); + assertEquals(1, es.getSubscription().getEntities().size()); + assertEquals(1, es.subscription.getEntities().size()); + assertEquals(1, esAnnotated.getSubscription().getEntities().size()); + assertEquals(1, esAnnotated.subscription.getEntities().size()); + + e.add(new C2()); + w.process(); + assertEquals(0, es.getSubscription().getEntities().size()); + assertEquals(0, es.subscription.getEntities().size()); + assertEquals(0, esAnnotated.getSubscription().getEntities().size()); + assertEquals(0, esAnnotated.subscription.getEntities().size()); } public static class C extends Component {} @@ -78,4 +112,19 @@ public EmptySystem() { @Override protected void process(Entity e) {} } + + public static class DefaultAspectSystem extends BaseEntitySystem { + public DefaultAspectSystem() { + super(Aspect.all(C.class)); + } + + @Override + protected void processSystem() {} + } + + @All(C.class) + public static class AnnotatedDefaultAspectSystem extends BaseEntitySystem { + @Override + protected void processSystem() {} + } } diff --git a/artemis-core/artemis/src/test/java/com/artemis/EntityTransmuterTest.java b/artemis-core/artemis/src/test/java/com/artemis/EntityTransmuterTest.java index 8e13e5c02..6510af933 100644 --- a/artemis-core/artemis/src/test/java/com/artemis/EntityTransmuterTest.java +++ b/artemis-core/artemis/src/test/java/com/artemis/EntityTransmuterTest.java @@ -2,6 +2,7 @@ import com.artemis.component.ComponentX; import com.artemis.component.ComponentY; +import com.artemis.component.ComponentZ; import com.artemis.component.Packed; import com.artemis.component.ReusedComponent; import com.artemis.systems.EntityProcessingSystem; @@ -19,11 +20,13 @@ public class EntityTransmuterTest { private ES1 es; private EntityTransmuter transmuter1; private EntityTransmuter transmuter3; + private EntityTransmuter transmuter4; @Before public void init() { world = new World(new WorldConfiguration() - .setSystem(new ES1())); + .setSystem(new ES1()) + .setDefaultAspect(Aspect.all(ComponentZ.class))); world.inject(this); transmuter3 = new EntityTransmuterFactory(world) @@ -38,10 +41,12 @@ public void init() { .remove(Packed.class) .remove(ReusedComponent.class) .build(); + + transmuter4 = new EntityTransmuter(world, Aspect.all(ComponentX.class, Packed.class).exclude(ComponentY.class)); } @Test - public void transmute_deleted_entity_does_not_cancel_delete() { + public void factory_transmute_deleted_entity_does_not_cancel_delete() { EntityManager em = world.getEntityManager(); int id = world.create(); @@ -54,9 +59,24 @@ public void transmute_deleted_entity_does_not_cancel_delete() { world.process(); assertFalse(em.isActive(id)); } + + @Test + public void direct_transmute_deleted_entity_does_not_cancel_delete() { + EntityManager em = world.getEntityManager(); + int id = world.create(); + + world.process(); + assertTrue(em.isActive(id)); + + world.delete(id); + transmuter4.transmute(id); + + world.process(); + assertFalse(em.isActive(id)); + } @Test - public void transmuting_entities() { + public void factory_transmuting_entities() { Entity e1 = createEntity(ComponentY.class, ReusedComponent.class); Entity e2 = createEntity(ComponentY.class, ReusedComponent.class); world.process(); @@ -80,14 +100,49 @@ public void transmuting_entities() { assertNotNull(e1.getComponent(Packed.class)); assertNotNull(e1.getComponent(ReusedComponent.class)); assertNull(e1.getComponent(ComponentY.class)); + assertNull(e1.getComponent(ComponentZ.class)); + assertNotNull(e2.getComponent(ComponentX.class)); + assertNotNull(e2.getComponent(Packed.class)); + assertNotNull(e2.getComponent(ReusedComponent.class)); + assertNull(e2.getComponent(ComponentY.class)); + assertNull(e2.getComponent(ComponentZ.class)); + } + + @Test + public void direct_transmuting_entities() { + Entity e1 = createEntity(ComponentY.class, ReusedComponent.class); + Entity e2 = createEntity(ComponentY.class, ReusedComponent.class); + world.process(); + assertEquals(2, e1.getCompositionId()); + + transmuter4.transmute(e1); + + // manually applying transmuter to e2 + EntityEdit edit = e2.edit(); + edit.create(ComponentX.class); + edit.create(Packed.class); + edit.remove(ComponentY.class); + + world.process(); + world.process(); + + assertTrue("compositionId=" + e2.getCompositionId(), 2 != e2.getCompositionId()); + assertEquals(e1.getCompositionId(), e2.getCompositionId()); + + assertNotNull(e1.getComponent(ComponentX.class)); + assertNotNull(e1.getComponent(Packed.class)); + assertNotNull(e1.getComponent(ReusedComponent.class)); + assertNull(e1.getComponent(ComponentY.class)); + assertNull(e1.getComponent(ComponentZ.class)); assertNotNull(e2.getComponent(ComponentX.class)); assertNotNull(e2.getComponent(Packed.class)); assertNotNull(e2.getComponent(ReusedComponent.class)); assertNull(e2.getComponent(ComponentY.class)); + assertNull(e2.getComponent(ComponentZ.class)); } @Test - public void transmute_twice() { + public void factory_transmute_twice() { Entity e = createEntity(ComponentY.class, ReusedComponent.class); world.process(); @@ -99,10 +154,24 @@ public void transmute_twice() { transmuter3.transmute(e); assertEquals(3, e.getCompositionId()); } + + @Test + public void direct_transmute_twice() { + Entity e = createEntity(ComponentY.class, ReusedComponent.class); + world.process(); + + assertEquals(2, e.getCompositionId()); + + transmuter1.transmute(e); + assertEquals(0, e.getCompositionId()); + + transmuter4.transmute(e); + assertEquals(3, e.getCompositionId()); + } @Test - public void entity_insertion_removal() { + public void factory_entity_insertion_removal() { Entity e = world.createEntity(); world.process(); transmuter3.transmute(e); @@ -115,6 +184,21 @@ public void entity_insertion_removal() { assertEquals(0, es.getSubscription().getEntities().size()); } + + @Test + public void direct_entity_insertion_removal() { + Entity e = world.createEntity(); + world.process(); + transmuter4.transmute(e); + world.process(); + + assertEquals(1, es.getSubscription().getEntities().size()); + + transmuter1.transmute(e); + world.process(); + + assertEquals(0, es.getSubscription().getEntities().size()); + } @Test public void toggle_entities_single_component() { @@ -195,7 +279,7 @@ public static class SysTransmuter extends IteratingSystem { private EntityTransmuter transmuter; public SysTransmuter() { - super(all(ComponentX.class)); + super(all(ComponentX.class).defaults(false)); } @Override @@ -215,7 +299,7 @@ protected void process(int entityId) { public static class SysSubscriber extends BaseEntitySystem { public SysSubscriber() { - super(all(ComponentY.class)); + super(all(ComponentY.class).defaults(false)); } @Override @@ -230,7 +314,7 @@ protected void inserted(int entityId) { private static class ES1 extends EntityProcessingSystem { public ES1() { - super(Aspect.all(ComponentX.class)); + super(Aspect.all(ComponentX.class).defaults(false)); } @Override @@ -243,7 +327,7 @@ private static class ES2 extends EntityProcessingSystem { private EntityTransmuter removeX; public ES2() { - super(Aspect.all(ReusedComponent.class)); + super(Aspect.all(ReusedComponent.class).defaults(false)); } @Override diff --git a/artemis-core/artemis/src/test/java/com/artemis/component/ComponentZ.java b/artemis-core/artemis/src/test/java/com/artemis/component/ComponentZ.java new file mode 100644 index 000000000..013767783 --- /dev/null +++ b/artemis-core/artemis/src/test/java/com/artemis/component/ComponentZ.java @@ -0,0 +1,6 @@ +package com.artemis.component; + +import com.artemis.Component; + +public class ComponentZ extends Component { +}