1 /* 2 * Entreri, an entity-component framework in Java 3 * 4 * Copyright (c) 2013, Michael Ludwig 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without modification, 8 * are permitted provided that the following conditions are met: 9 * 10 * Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 package com.lhkbob.entreri; 28 29 /** 30 * <p/> 31 * Component represents a grouping of reusable and related states that are added to an 32 * {@link Entity}. An Entity's behavior is defined by the union of all components attached 33 * to it, and the system's configured tasks that process the entities. Tasks must be 34 * implemented and used in order to take advantage of a particular component 35 * configuration. 36 * <p/> 37 * An entity can be considered as a collection of aspects represented by component 38 * instances. An entity can have at most one instance of a component type at a time, 39 * although over its lifetime the specific component instance may change. 40 * <p/> 41 * Logically a component definition is a set of named and typed properties, and a 42 * method-based API to get and set the values of each property. Specific types of 43 * component are defined by creating a sub-interface of Component. Using the {@link 44 * com.lhkbob.entreri.property.Named Named}, {@link com.lhkbob.entreri.property.SharedInstance 45 * SharedInstance}, {@link com.lhkbob.entreri.property.Factory Factory} and custom {@link 46 * com.lhkbob.entreri.property.Attribute Attribute} annotations defined by {@link 47 * com.lhkbob.entreri.property.Property Property} implementations, the data properties of 48 * the component type are specified in the sub-interface. A declaration model similar to 49 * the Java Bean model is used and is outlined below: 50 * <p/> 51 * <ol> <li>Non-void, zero-argument methods starting with 'get', 'is', and 'has' declare a 52 * property. The property type is inspected from the return type of the method. The 53 * property name is the method name minus the 'get'/'is'/'has' prefix with its first 54 * letter made lower-case. The {@link com.lhkbob.entreri.property.Named Named} annotation 55 * can be used to override the name.</li> <li>Single-argument methods starting with 'set' 56 * are assumed to be a setter corresponding to a property. The single parameter's type 57 * must equal the type the getter. The {@link com.lhkbob.entreri.property.Named Named} 58 * annotation can be applied to either the setter or the parameter to specify the property 59 * name.</li> <li>Multi-argument methods starting with 'set' are assumed to be a setter 60 * that assigns values to multiple property, one for each argument. Each argument must be 61 * annotated with {@link com.lhkbob.entreri.property.Named Named} to specify the property, 62 * and the argument type must equal the type of the matching property.</li> <li>Setter 63 * methods must return void or return the components type, in which case the proxy will 64 * return itself to allow for method chaining.</li> <li>Getters with void return types or 65 * more than 0 arguments, setters with an invalid return type or no arguments, and any 66 * other method not matching the conventions above will cause the system to throw an 67 * {@link IllegalComponentDefinitionException}.</li> </ol> 68 * <p/> 69 * Internally, the entity system will generate proxy implementations of the component 70 * interfaces that implement the property getters and setters but store all of the values 71 * in {@link com.lhkbob.entreri.property.Property} instances of a compatible type. This 72 * allows iteration over components to have much better cache locality if the component is 73 * defined in terms of primitives or types that have specialized Property implementations 74 * that can pack and unpack an instance. The {@link com.lhkbob.entreri.property.SharedInstance 75 * SharedInstance} annotation can be added to the getter method of a property to specify 76 * that the {@link com.lhkbob.entreri.property.ShareableProperty ShareableProperty} API 77 * should be leveraged by the generated class. 78 * <p/> 79 * Additional attribute annotations can be added to the getter method to influence the 80 * behavior of the {@link com.lhkbob.entreri.property.PropertyFactory PropertyFactory} 81 * used for each property in the component definition. Besides using the Factory 82 * annotation to specify the factory type, a property implementation can be associated 83 * with a type with canonical name <var>C</var> by adding the file 84 * META-INF/entreri/mapping/C to the classpath, where its contents must be: 85 * <pre> 86 * <BINARY NAME OF PROPERTY> 87 * </pre> 88 * where the value is suitable for passing into {@link Class#forName(String)}. 89 * <p/> 90 * Attribute annotations provided by the default property implementations are outlined 91 * below: <ul> <li>{@link com.lhkbob.entreri.property.BooleanProperty.DefaultBoolean 92 * DefaultBoolean} - set value for boolean properties</li> <li>{@link 93 * com.lhkbob.entreri.property.ByteProperty.DefaultByte DefaultByte} - set value for byte 94 * properties</li> <li>{@link com.lhkbob.entreri.property.ShortProperty.DefaultShort 95 * DefaultShort} - set value for short properties</li> <li>{@link 96 * com.lhkbob.entreri.property.IntProperty.DefaultInt DefaultInt} - set value for int 97 * properties</li> <li>{@link com.lhkbob.entreri.property.LongProperty.DefaultLong 98 * DefaultLong} - set value for long properties</li> <li>{@link 99 * com.lhkbob.entreri.property.FloatProperty.DefaultFloat DefaultFloat} - set value for 100 * float properties</li> <li>{@link com.lhkbob.entreri.property.DoubleProperty.DefaultDouble 101 * DefaultDouble} - set value for double properties</li> <li>{@link 102 * com.lhkbob.entreri.property.CharProperty.DefaultChar DefaultChar} - set value for char 103 * properties</li> <li>{@link com.lhkbob.entreri.property.Clone Clone} - specify clone 104 * policy used with entity or component templates</li> </ul> Note that there are no 105 * annotations that work with general object types. This is because the scope of that 106 * problem is intractable. The default ObjectProperty implementation assumes null values 107 * are allowed and that the default value is null. 108 * <p/> 109 * The generated proxies will implement equals() and hashCode() based on their type and 110 * the id of their owning entity. The {@link ComponentIterator} class creates flyweight 111 * component instances whose identity changes as iteration proceeds; equals() and 112 * hashCode() will behave appropriately. This means that flyweight components should not 113 * be stored in collections that depend on equality or hashes. Flyweight components are 114 * used for performance reasons when iterating over the entities in a system with specific 115 * component configurations because the JVM does not need to load each unique component 116 * instance into the cache. 117 * <p/> 118 * Component implements both {@link Ownable} and {@link Owner}. This can be used to create 119 * hierarchies of both components and entities that share a lifetime. When a component is 120 * removed from an entity, all of its owned objects are disowned. If any of them were 121 * entities or components, they are also removed from the system. 122 * 123 * @author Michael Ludwig 124 */ 125 public interface Component extends Owner, Ownable { 126 /** 127 * @return The EntitySystem that created this component 128 */ 129 public EntitySystem getEntitySystem(); 130 131 /** 132 * Get the entity that this component is attached to. If the component has been 133 * removed from the entity, or is otherwise not live, this will return null. 134 * 135 * @return The owning entity, or null 136 */ 137 public Entity getEntity(); 138 139 /** 140 * <p/> 141 * Get whether or not the component is still attached to an alive entity in an entity 142 * system. This will return false if it or its entity has been removed. If the 143 * component is a flyweight object used in iteration, it can also return false when 144 * iteration has terminated. 145 * <p/> 146 * Using property getters or setters on a dead component produces undefined behavior. 147 * 148 * @return True if the component is still attached to an entity in the entity system, 149 * or false if it or its entity has been removed 150 */ 151 public boolean isAlive(); 152 153 /** 154 * <p/> 155 * Get whether or not this particular instance is a flyweight component object. A 156 * flyweight instance is used by ComponentIterators to efficiently view the underlying 157 * component data iteratively. It is safe to set the owner of a flyweight component or 158 * to set the flyweight as an owner of another object. It will correctly register the 159 * canonical component as the owner. 160 * <p/> 161 * Components returned by {@link Entity#add(Class)}, {@link Entity#get(Class)}, and 162 * its other component returning functions will always return non-flyweight 163 * components. These components are the 'canonical' instances for that component type 164 * and entity. 165 * 166 * @return True if this component is a flyweight instance iterating over a list of 167 * components 168 */ 169 public boolean isFlyweight(); 170 171 /** 172 * <p/> 173 * Get the underlying index of this component used to access its properties. For most 174 * uses, you should not need to use this method. As an EntitySystem manages and 175 * compacts the component data storage, a component's index can change. The index 176 * should be used to access decorated properties, which will be kept in-sync with any 177 * compactions. 178 * <p/> 179 * Additionally, some component instances may be flyweight objects used for iteration. 180 * In this case, every iteration will update its index and the component object's 181 * identity will change as well. See {@link ComponentIterator} for more information. 182 * 183 * @return The current index of component 184 */ 185 public int getIndex(); 186 187 /** 188 * <p/> 189 * Get the current version of the data of this component. When a component's data is 190 * mutated, implementations increment its version so comparing a previously cached 191 * version number can be used to determine when changes have been made. 192 * <p/> 193 * Within a component type for an entity system, version values will be unique across 194 * component instances. Thus, {@code entity.as(T.class).getVersion() != cachedVersion} 195 * will correctly detect changes to the original component instance as well as the 196 * removal and replacement by a new component of type T. 197 * 198 * @return The current version, or a negative number if the data is invalid 199 */ 200 public int getVersion(); 201 202 /** 203 * Increment the version of the component accessed by this instance. This will be 204 * automatically called by all exposed setters by the generated proxies, but if 205 * necessary it can be invoked manually as well. 206 * 207 * @see #getVersion() 208 */ 209 public void updateVersion(); 210 211 /** 212 * Get the class identifier for this component. This will be the specific 213 * sub-interface of Component that this object is an instance of. getType() should be 214 * used instead of {@link Object#getClass()} because that will return a specific and 215 * most likely generated proxy implementation of the component type. 216 * 217 * @return The component type 218 */ 219 public Class<? extends Component> getType(); 220 }