From 6e000492b003373a2fb8d6df5bd7208739784180 Mon Sep 17 00:00:00 2001 From: Brychan Dempsey Date: Tue, 8 Jun 2021 17:08:57 +1200 Subject: [PATCH] .. --- javaecs/pom.xml | 14 ++-- .../nz/ac/massey/javaecs/ComponentArray.java | 6 +- .../main/java/nz/ac/massey/javaecs/ECS.java | 77 +++++++++++++++---- .../nz/ac/massey/javaecs/EntityManager.java | 38 ++++++--- .../nz/ac/massey/javaecs/SystemManager.java | 2 +- .../java/nz/ac/massey/javaecs/AppTest.java | 73 +++++++++++++++++- 6 files changed, 176 insertions(+), 34 deletions(-) diff --git a/javaecs/pom.xml b/javaecs/pom.xml index 6227329..eaf0334 100644 --- a/javaecs/pom.xml +++ b/javaecs/pom.xml @@ -2,7 +2,7 @@ 4.0.0 nz.ac.massey.javaecs javaecs - 1.1-SNAPSHOT + 1.2-PRERELEASE 1.8 1.8 @@ -17,10 +17,10 @@ 3.0.0 4.3.0 - 0% - 0% - 20 - 5 + 75% + 50% + 30 + 10 @@ -134,7 +134,7 @@ report - + diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java b/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java index 6fae0d9..f6ce8fd 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java @@ -34,7 +34,7 @@ class ComponentArray{ public void removeData(int entity){ if (!entityComponentDataMap.containsKey(entity)){ - System.err.println("Attempted to remove non-existent entity"); + ECS.writeErr("Attempted to remove non-existent entity"); return; } // Get the componentData index of the entity @@ -55,7 +55,7 @@ class ComponentArray{ public void insertData(int entity, Object component){ if (entityComponentDataMap.containsKey(entity)){ - System.err.println("Entity is already subscribed to the component"); + ECS.writeErr("Entity is already subscribed to the component"); return; } int index = componentArray.size(); @@ -78,7 +78,7 @@ class ComponentArray{ public Object getData(int entity){ if (!entityComponentDataMap.containsKey(entity)){ - System.err.println("Attempted to retrieve non-existent data"); + ECS.writeErr("Attempted to retrieve non-existent data"); return null; } diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/ECS.java b/javaecs/src/main/java/nz/ac/massey/javaecs/ECS.java index b4dfd54..de0a6ae 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/ECS.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/ECS.java @@ -12,6 +12,7 @@ package nz.ac.massey.javaecs; * */ +import java.io.PrintStream; import java.lang.reflect.Type; import java.util.BitSet; /** @@ -21,6 +22,10 @@ import java.util.BitSet; * for documentation and more information. */ public class ECS { + // All internal functions write error messages to errorStream; which defaults to System.err + // Can be set to a different PrintStream, to allow errors to be logged elsewhere + protected static PrintStream errorStream = System.err; + // References to the manager classes protected EntityManager entityManager; protected ComponentManager componentManager; protected SystemManager systemManager; @@ -36,7 +41,7 @@ public class ECS { } /** - * Initialises the ECS with the specified value(s) + * Initialises the ECS with the specified value * @param maxEntities the maximum number of entities to allow */ public ECS(int maxEntities){ @@ -80,8 +85,8 @@ public class ECS { * Registers the specified name in the component manager * @param name the name to register. Should be the component class name or a suitable name for primitive types */ - void registerComponent(Type type){ - componentManager.registerComponent(type); + boolean registerComponent(Type type){ + return componentManager.registerComponent(type); } Integer getComponentIndex(Type type){ @@ -94,10 +99,14 @@ public class ECS { * @param componentName the class name of the component to add * @param component the actual component data */ - void addComponent(int entity, Type componentName, Object component){ - componentManager.addComponentToEntity(componentName, component, entity); - entityManager.registerComponent(componentManager.getComponentIndex(componentName), entity); - systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity)); + boolean addComponent(int entity, Type componentName, Object component){ + if (entityManager.registerComponent(componentManager.getComponentIndex(componentName), entity)) + { + systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity)); + componentManager.addComponentToEntity(componentName, component, entity); + return true; + } + else return false; } /** @@ -105,10 +114,14 @@ public class ECS { * @param entity the entity to remove the component from * @param componentName the class name of the component */ - void removeComponent(int entity, Type componentType){ - componentManager.removeComponentFromEntity(componentType, entity); - entityManager.unregisterComponent(componentManager.getComponentIndex(componentType), entity); - systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity)); + boolean removeComponent(int entity, Type componentType){ + if (entityManager.unregisterComponent(componentManager.getComponentIndex(componentType), entity)) + { + componentManager.removeComponentFromEntity(componentType, entity); + systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity)); + return true; + } + else return false; } /** @@ -127,8 +140,8 @@ public class ECS { * @param systemName * @param action */ - void registerSystem(Type systemType, ECSSystem action){ - systemManager.registerSystem(systemType, action); + boolean registerSystem(Type systemType, ECSSystem action){ + return systemManager.registerSystem(systemType, action); } /** @@ -142,4 +155,42 @@ public class ECS { Integer getMaxEntities(){ return entityManager.currentSize; } + + // Encapsulate syserr writes so they may be redirected out of the lib + + /** + * Writes an error message to the set PrintStream + *

+ * Default System.err + * @param message the message to write + */ + static void writeErr(String message){ + errorStream.println(message); + } + + /** + * Writes an object to the set PrintStream + *

+ * Default System.err + * @param obj the object to write + */ + static void writeErr(Object obj){ + errorStream.println(obj); + } + + /** + * Sets the error PrintStream to the provided PrintStream + * @param newErr the PrintStream to set as the write destination + */ + static void setErr(PrintStream newErr){ + errorStream = newErr; + } + + /** + * Gets the current error PrintStream + * @return the current PrintStream that errors are written to + */ + static PrintStream getErr(){ + return errorStream; + } } \ No newline at end of file diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java b/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java index 58889dc..70c8537 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java @@ -53,7 +53,7 @@ class EntityManager{ public Integer addEntity(){ if (unusedEntities.size() == 0){ - System.err.println("No available space to create a new entity"); + ECS.writeErr("No available space to create a new entity"); return -1; } return unusedEntities.remove(); @@ -64,16 +64,36 @@ class EntityManager{ entityRegistrations.get(entity).clear(); } - public void registerComponent(int component, int entity){ + public boolean registerComponent(int component, int entity){ if (entity >= currentSize){ - System.err.println("Attempted to assign a component to non-existent entity: " + entity); - return; + ECS.writeErr("Attempted to assign a component to non-existent entity: " + entity); + return false; + } + else if (entityRegistrations.get(entity).get(component)) + { + ECS.writeErr("Entity is already assigned to the component"); + return false; + } + else{ + entityRegistrations.get(entity).set(component); + return true; } - entityRegistrations.get(entity).set(component); } - public void unregisterComponent(int component, int entity){ - entityRegistrations.get(entity).clear(component); + public boolean unregisterComponent(int component, int entity){ + try{ + if (entityRegistrations.get(entity).get(component)) + { + entityRegistrations.get(entity).clear(component); + return true; + } + else return false; + } + catch (IndexOutOfBoundsException e) + { + return false; + } + } public BitSet getRegistrations(int entity){ @@ -86,11 +106,11 @@ class EntityManager{ public boolean resize(int newSize, SystemManager systemManager, ComponentManager componentManager){ if (newSize < currentSize - unusedEntities.size()){ - System.err.println("Attempted to resize the maximum entity count to a number smaller than the current assigned entity count."); + ECS.writeErr("Attempted to resize the maximum entity count to a number smaller than the current assigned entity count."); return false; } else if (newSize == currentSize){ - System.err.println("Attempted to set the newSize to the current size"); + ECS.writeErr("Attempted to set the newSize to the current size"); return true; } else{ diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java b/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java index d610d6b..388014a 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java @@ -29,7 +29,7 @@ class SystemManager{ // table. public boolean registerSystem(Type systemType, ECSSystem system){ if (systems.containsKey(systemType)){ - System.err.println("System already registered"); + ECS.writeErr("System already registered"); return false; } systems.put(systemType, system); diff --git a/javaecs/src/test/java/nz/ac/massey/javaecs/AppTest.java b/javaecs/src/test/java/nz/ac/massey/javaecs/AppTest.java index 13e75f0..b708c33 100644 --- a/javaecs/src/test/java/nz/ac/massey/javaecs/AppTest.java +++ b/javaecs/src/test/java/nz/ac/massey/javaecs/AppTest.java @@ -2,12 +2,18 @@ package nz.ac.massey.javaecs; import org.junit.jupiter.api.Test; - +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.nio.charset.Charset; import java.util.BitSet; import org.junit.jupiter.api.BeforeEach; @@ -21,6 +27,10 @@ class AppTest { TestSystem system; class TestObject{ + TestObject() {} + TestObject(int i){ + this.i = i; + } int i = 1; } @@ -161,6 +171,67 @@ class AppTest { } + @Test + void testEntityManagerConstructor(){ + gameEngine = new ECS(5); + assertEquals(5, gameEngine.getMaxEntities()); + } + + @Test + void testAssignToNonExistentEntity(){ + assertFalse(gameEngine.addComponent(1025, TestObject.class, new TestObject())); + } + + @Test + void testRemoveComponent(){ + int entity = gameEngine.createEntity(); + gameEngine.addComponent(entity, TestObject.class, new TestObject()); + gameEngine.removeComponent(entity, TestObject.class); + assertFalse(gameEngine.entityManager.getRegistrations(entity).get(gameEngine.getComponentIndex(TestObject.class))); + } + + @Test + void testEqualResize(){ + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + PrintStream orig = System.err; + PrintStream newErr = new PrintStream(bytes); + System.setErr(newErr); + assertTrue(gameEngine.resizeMaximum(1024)); + System.setErr(orig); + newErr.flush(); // ensure the bytes are recieved + byte[] errBytes = bytes.toByteArray(); + String result = new String(errBytes); + System.err.println("Captured in redirect: " + result); + assertTrue(result.trim().equals("Attempted to set the newSize to the current size")); + } + + @Test + void testLargerResize(){ + assertTrue(gameEngine.resizeMaximum(2048)); + assertEquals(2048, gameEngine.getMaxEntities()); + } + + @Test + void testComponentAlreadyRegistered(){ + assertFalse(gameEngine.registerComponent(TestObject.class)); + } + + @Test + void testRemoveNonExistentComponentData(){ + assertFalse(gameEngine.removeComponent(1, TestObject.class)); + } + + @Test + void testAssignComponentAlreadyAssigned(){ + int entity = gameEngine.createEntity(); + gameEngine.addComponent(entity, TestObject.class, new TestObject()); + assertFalse(gameEngine.addComponent(entity, TestObject.class, new TestObject())); + } + + @Test + void testRegisterExistingSystem(){ + assertFalse(gameEngine.registerSystem(TestSystem.class, new TestSystem())); + } /** * Establish a simple ECS, with a single system and component