Commits

Michael Ludwig  committed 86ccda8

Let users disable auto-versioning for properties.

  • Participants
  • Parent commits 3c326e0

Comments (0)

Files changed (9)

                         <source>1.7</source>
                         <target>1.7</target>
                         <showWarnings>true</showWarnings>
+                        <useIncrementalCompilation>false</useIncrementalCompilation>
                     </configuration>
 
                     <executions>
 
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-resources-plugin</artifactId>
+                    <version>2.6</version>
+                </plugin>
+
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-site-plugin</artifactId>
                     <version>3.1</version>
                 </plugin>

File src/main/java/com/lhkbob/entreri/NoAutoVersion.java

+/*
+ * Entreri, an entity-component framework in Java
+ *
+ * Copyright (c) 2013, Michael Ludwig
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *     Redistributions of source code must retain the above copyright notice,
+ *         this list of conditions and the following disclaimer.
+ *     Redistributions in binary form must reproduce the above copyright notice,
+ *         this list of conditions and the following disclaimer in the
+ *         documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.lhkbob.entreri;
+
+import java.lang.annotation.*;
+
+/**
+ * By default, all modifications to properties declared in a component will automatically increment that
+ * component's version. This is useful when components are largely unchanging and the version change can be
+ * used to kick off a more expensive computation.  However, when a property stores a cached value that
+ * frequently changes or is not part of the common identify of the component, it can be useful to disable this
+ * behavior.
+ * <p/>
+ * It is highly recommended to not use this annotation until performance profiling suggests that disabling the
+ * version increment is necessary to take advantage of it.  An example of this is in Ferox, the Renderable
+ * component has its geometry's local bounds, but it also caches the world bounds. The world bounds is a
+ * cached value that combines the local bounds and the matrix from the Transform component. The Transform's
+ * version can change very often, which would force the Renderable's version to change when the world bounds
+ * was updated to match. By disabling the auto-update on the world bounds, the rest of the Renderable could be
+ * easily versioned.
+ * <p/>
+ * The annotation must be placed on the getter, it is ignored when placed on the setter.
+ *
+ * @author Michael Ludwig
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NoAutoVersion {
+}

File src/main/java/com/lhkbob/entreri/impl/CollectionComponentIteratorImpl.java

  */
 public class CollectionComponentIteratorImpl implements ComponentIterator {
     private final EntitySystemImpl system;
-    private final Iterable<Entity> entities;
+    private Iterable<Entity> entities;
 
     private AbstractComponent<?>[] required; // all required except primary
     private AbstractComponent<?>[] optional;
     private Iterator<Entity> currentIterator;
 
     public CollectionComponentIteratorImpl(EntitySystemImpl system, Iterable<Entity> entities) {
+        if (entities == null) {
+            throw new NullPointerException("Entity collection cannot be null");
+        }
         this.entities = entities;
         this.system = system;
 

File src/main/java/com/lhkbob/entreri/impl/ComponentFactoryProvider.java

         }
 
         // implement the body
+        boolean needsUpdate = false;
         for (PropertyDeclaration p : params) {
             int idx = properties.indexOf(p);
+            if (p.isAutoVersionEnabled()) {
+                needsUpdate = true;
+            }
             sb.append("\t\t").append(PROPERTY_FIELD_PREFIX).append(idx).append(".set(")
               .append(INDEX_FIELD_NAME).append(", ").append(SETTER_PARAM_PREFIX).append(idx).append(");\n");
         }
 
-        sb.append("\t\t").append(REPO_FIELD_NAME).append('.').append(UPDATE_VERSION_METHOD).append("(")
-          .append(INDEX_FIELD_NAME).append(");\n");
+        if (needsUpdate) {
+            sb.append("\t\t").append(REPO_FIELD_NAME).append('.').append(UPDATE_VERSION_METHOD).append("(")
+              .append(INDEX_FIELD_NAME).append(");\n");
+        }
 
         // return this component if we're not a void setter
         if (returnComponent) {

File src/main/java/com/lhkbob/entreri/impl/MirrorComponentSpecification.java

         private final String getter;
         private final boolean isSharedInstance;
         private final boolean isGeneric;
+        private final boolean isVersioned;
 
         private final String type;
         private final String propertyType;
             setterReturnsComponent = !setter.getReturnType().getKind().equals(TypeKind.VOID);
 
             isSharedInstance = getter.getAnnotation(SharedInstance.class) != null;
+            isVersioned = getter.getAnnotation(NoAutoVersion.class) == null;
             isGeneric = propertyType.getAnnotation(GenericProperty.class) != null;
 
             List<Annotation> annots = new ArrayList<>();
         }
 
         @Override
+        public boolean isAutoVersionEnabled() {
+            return isVersioned;
+        }
+
+        @Override
         public PropertyFactory<?> getPropertyFactory() {
             throw new UnsupportedOperationException("Cannot create PropertyFactory with mirror API");
         }

File src/main/java/com/lhkbob/entreri/impl/PropertyDeclaration.java

     public boolean isShared();
 
     /**
+     * Get whether or not modifying this property should have updateVersion() called during the setter.
+     *
+     * @return True unless {@link com.lhkbob.entreri.NoAutoVersion} is present on getter
+     */
+    public boolean isAutoVersionEnabled();
+
+    /**
      * Get the PropertyFactory instance that was configured for this property. It must be used to instantiate
      * the property objects that will be assignable to the type returned by {@link
      * #getPropertyImplementation()}, and will be configured by all attribute annotations applied to the

File src/main/java/com/lhkbob/entreri/impl/ReflectionComponentSpecification.java

         private final Method getter;
         private final boolean isSharedInstance;
         private final boolean isGeneric;
+        private final boolean isVersioned;
 
         private final Class<? extends Property> propertyType;
         private final List<Annotation> validationAnnotations;
             this.setter = setter;
             this.setterParameter = setterParameter;
             isSharedInstance = getter.getAnnotation(SharedInstance.class) != null;
+            isVersioned = getter.getAnnotation(NoAutoVersion.class) == null;
 
             propertyType = getCreatedType((Class<? extends PropertyFactory<?>>) factory.getClass());
             isGeneric = propertyType.getAnnotation(GenericProperty.class) != null;
         }
 
         @Override
+        public boolean isAutoVersionEnabled() {
+            return isVersioned;
+        }
+
+        @Override
         public String getPropertyImplementation() {
             return propertyType.getCanonicalName();
         }

File src/test/java/com/lhkbob/entreri/ComponentTest.java

     }
 
     @Test
+    public void testAutomaticVersionDisabled() {
+        EntitySystem system = EntitySystem.Factory.create();
+        Entity e = system.addEntity();
+        FloatComponent cd = e.add(FloatComponent.class);
+
+        int originalVersion = cd.getVersion();
+        cd.setFloat(500);
+        Assert.assertEquals(originalVersion, cd.getVersion());
+    }
+
+    @Test
     public void testUniqueVersionUpdate() {
         EntitySystem system = EntitySystem.Factory.create();
         IntComponent cd1 = system.addEntity().add(IntComponent.class);

File src/test/java/com/lhkbob/entreri/components/FloatComponent.java

 package com.lhkbob.entreri.components;
 
 import com.lhkbob.entreri.Component;
+import com.lhkbob.entreri.NoAutoVersion;
 import com.lhkbob.entreri.Within;
 
 /**
  * @author Michael Ludwig
  */
 public interface FloatComponent extends Component {
+    @NoAutoVersion
     public float getFloat();
 
     public void setFloat(@Within(min = -12, max = 4500) float value);