/* ZEntity.hpp Author: James Russell Created: 2/5/2013 Purpose: Entity class for the ZSimulation framework. All objects affecting the simulation will exist as an entity. Entities can either be created with state or without state. Entities with and without state can be messaged by other entities within the simulation. Entities with state have their state function called once per tick, and this state function can modify the entity state, making the entity an autonomous state machine. // ENTITY MESSAGING // Entities are created in order to be processed in a threaded fashion. When entities have their current state function called, they should be expected to be able to process this in a thread-safe manner, able to call their own non-const methods but only able to affect other entities via messaging. After the state functions are called, remaining messages are delivered to the entity 'mailbox'. Any action that could mutate an entity that is not done from within the call to the state function must be handled via a message. These delivered messages are then processed in a multi threaded manner, but each entity has it's messages processed serially, meaning that modifications can take place as per normal. Entity messages are processed by deferring to the handler installed for a particular message type on an entity. If no message handler is installed for that message type on an entity, the message handler installed for that type on the simulation message stream will be called. If no message handler is installed there, the message is dropped. // ENTITY PROPERTIES // Entities hold their state via allocation into the simulation 'property buffer', which allows for contiguous space allocation for properties to be stored. The property buffer is a raw memory allocator and does not care what the underlying data actually is. Entity properties can be allocated as 'local' or 'synchronized'. Data stored locally is not updated to other simulation instances. Data stored in a 'synchronized' state is mirrored to other simulation instances. License: Copyright 2013, 762 Studios */ #pragma once #ifndef ZENTITY_HPP #define ZENTITY_HPP #include #include #include "ZSimulationDefs.hpp" #include "ZPropertyBuffer.hpp" #include "ZStringBuffer.hpp" // forward decl class ZSimulation; struct ZMessage; /* Entity class. */ class ZEntity { public: /* This is the entity state typedef, which corresponds to 'AI State'. The current think state is called once per simulation tick. @param entity - the entity who is thinking @param sim - the running simulation @param dt - the time (expressed in seconds) since last call of this function */ typedef void (*StateFunc)(ZEntity& entity, const ZSimulation& sim, double dt); /* This is the entity message handler typedef, which corresponds to 'Behavior'. If there is no message handler installed for a given message type, the default handler installed in the message queue is used. @param entity - the entity receiving the message @param sim - the running simulation @param sender - the sending entity @param payload - the payload of the message */ typedef void (*MessageFunc)(ZEntity& entity, const ZSimulation& sim, eID sender, void* payload); /* This is the entity initialization function typedef, which can be passed to the simulation to initialize the entity upon creation. @param entity - the entity to initialize @param sim - the running simulation */ typedef void (*InitFunc)(ZEntity& entity, const ZSimulation& sim); /* Entity property class. An ease of access class for reading and writing property data. This class does not handle type coercion on read. Types will change when values are set. */ struct Property { // we'll be using this a lot typedef ZStringBuffer::StringKey StringKey; enum { BOOL, // boolean type INT, // integer type FLOAT, // floating point type DOUBLE, // double precision floating point type STRING, // string type (stored in string buffer or dynamic, limited size) VECTOR, // vector type (4 floating point values) MATRIX, // matrix type (16 floating point values) NONE // untyped data } Type; // the type of property ZString LocalString; // local storage for strings uint8_t Local[64]; // local storage for simple types void* Data; // data for more complex types size_t Size; // size of data (in bytes) // default constructor, which initializes an empty string Property(); // generic and array constructor, which takes data and size Property(void* data, size_t size); // value constructors Property(const Property& other); Property(const bool val); Property(const int val); Property(const float val); Property(const double val); Property(const char* val); // checks to see if this string has a string key, if not makes a dynamic string Property(const StringKey val); // quicker method of directly creating a static string type Property(const SST_Vec4f& val); Property(const SST_Vec3f& val); // convenience for SST_Vec4f(x, y, z, 0.0) Property(const SST_Vec2f& val); // convenience for SST_Vec4f(x, y, 0.0, 0.0) Property(const SST_Mat44f& val); // assignment operators Property& operator = (const Property& other); Property& operator = (const bool& val); Property& operator = (const int& val); Property& operator = (const float& val); Property& operator = (const double& al); Property& operator = (const char* val); Property& operator = (const StringKey val); // quicker method of using a static string type Property& operator = (const SST_Vec4f& val); Property& operator = (const SST_Vec3f& val); // convenience for SST_Vec4f(x, y, z, 0.0) Property& operator = (const SST_Vec2f& val); // convenience for SST_Vec4f(x, y, 0.0, 0.0) Property& operator = (const SST_Mat44f& val); // cast operators operator bool () const; operator int () const; operator float () const; operator double () const; operator char* () const; operator bool* () const; operator int* () const; operator float* () const; operator double* () const; operator SST_Vec4f () const; operator SST_Vec3f () const; // gets [x, y, z] only operator SST_Vec2f () const; // gets [x, y] only operator SST_Mat44f () const; }; // c'tor ZEntity(eID entityId, ZSimulation& sim); // parameterized c'tor (creates an actor) ZEntity(eID entityId, ZSimulation& sim, StateFunc startState); // d'tor ~ZEntity(); ////////////////////////////////////////////////////////////////////////// // Member Data Getter Functions eID GetId() const; ZRandomGenerator& GetPRNG(); ////////////////////////////////////////////////////////////////////////// // Property Manipulation Functions /* Property access functions. Used to create, remove, read, and write properties and their values. The 'HasProperty' function will return true if the named property exists on this entity. The 'CreateSyncProperty' functions is used to declare a synchronized property on this entity. Synchronized properties are automatically mirrored across the network system whenever a change is made. If the 'owner' boolean is set to true, modifications to the entity on this simulation are synchronized to other simulations. If the owner boolean is set to false, modifications are not synchronized, and the property will be updated periodically from other simulations. Only one simulation should be the owner of an entity, or they will update over each other. The 'GetProperty' function will fill out a Property instance that references the read value of the named property. If the named property does not exist on this entity, the function will return false. The 'SetProperty' function will set the write value of the property to be the value provided. If the named property does not exist, it will be created as a non-synchronized property. Note that the read value is not updated until the next simulation tick. The 'EraseProperty' function will queue the property for removal at the end of this tick, which allows 'get' operations to function as normal until the next tick. The template 'Get' function is a shortcut to directly get the value of a specific property. Note that if the property does not exist, this will return a default constructed property value. */ bool HasProperty(const ZName& name) const; void CreateSyncProperty(const ZName& name, const Property& prop, bool owner); bool GetProperty(const ZName& name, Property& val) const; void SetProperty(const ZName& name, const Property& val); void EraseProperty(const ZName& name); template T Get(const ZName& name) const { Property prop; GetProperty(name, prop); return (T)prop; } template void Set(const ZName& name, const T val) { Property prop(val); SetProperty(name, prop); } /*************************************************************************/ /* State Management Functions */ /* State functions. Used to get information about the current state and manipulate the state stack, as well as call the current state function. Push and Pop operate as you would expect for a state stack. The maximum number of states is defined by ZENT_STACK_SIZE. SetState sets the current state to the given state, replacing it. ResetState clears the state stack and sets the current state to be the provided one. */ StateFunc GetCurrentState() const; double GetTimeInCurrentState() const; void PushState(StateFunc state); void PopState(); void SetState(StateFunc state); void ResetState(StateFunc state); /* State property stack manipulation functions. Used to read and write information to the state property stack, which can be used to pass transient information from one state function to another. */ size_t GetStatePropertyCount() const; Property PopStateProperty(); void PushStateProperty(const Property& prop); void ClearStatePropertyStack(); /*************************************************************************/ /* Messaging Management Functions */ /* Messages, once posted to the message queue, can be 'delivered' to entities for sorting purposes to be later processed in a convenient fashion. When processing delivered messages, order of delivery is maintained. Any messages that do not have a corresponding handler type installed will be handled by the handler installed in the message stream. If no corresponding handler is installed there, the message is dropped, and the simulation is notified. */ void DeliverMessage(ZMessage* msg); void ProcessMessages(); void PushMessageHandler(mID type, MessageFunc handler); void PopMessageHandler(mID type); void ClearMessageHandlers(); protected: /* Typedef for the property map used by ZEntity. It has a bucket size of 32, local storage for the buckets of 32, 10 local nodes for each hash-chain, 10 local storage for each property array, and will not resize based on load factor. Uses a 64-bit hash value. */ typedef ZHashMap, 0> PropMap; /* Stack used to indicate which properties have been modified since last swap of properties. */ typedef ZArray> PropModifiedStack; /* Stack used to indicate which properties have been deleted since last swap of properties. */ typedef ZArray PropDeletedStack; /* Stack used for state properties, which can be used to send data from one state to another. */ typedef ZArray StatePropStack; /* Buffer used as an entity mailbox - targeted messages are delivered here. */ typedef ZRingBuffer Mailbox; /* Typedef for the handler map used by ZEntity, which maps message type to a stack of handlers. Only keeps two local nodes in the allocator, and will not resize based on load factor. */ typedef ZHashMap, int64_t, ZHasher, 0, ZListPooledAllocator, int64_t>, 2 >> HandlerMap; /* Member Data */ ZSimulation& Sim; // the simulation running this thing const eID Id; // the id of the entity ZRandomGenerator PRNG; // entity PRNG, kept so there are no thread conflicts with PRNG access /* Property Management */ PropMap PropertyMap; // entity properties map PropModifiedStack PropertyModifiedStack; // properties that need to be mirrored into the read properties map PropDeletedStack PropertyDeletedStack; // properties have been deleted /* State Management */ int StateIndex; // index to our current state (-1 indicates no state) StateFunc StateStack[ZSIM_ENT_STATE_STACK_SIZE]; // stack of states (StateStack[StateIndex] is our current) StatePropStack StatePropertyStack; // stack of properties so that states can pass data to each other double StateTime; // amount of time we have spent in our current state /* Message Management */ Mailbox MessageMailbox; // entity 'Mailbox', which is where messages get delivered to HandlerMap MessageHandlers; // entity handlers, used to process received messages private: friend class ZSimulation; friend class ActorTickTask; DISABLE_COPY_AND_ASSIGN(ZEntity); // allocates a new property // TODO // called by the simulation to tick the current state void StateTick(double dt); // called by the simulation to dealloc properties void HandlePropertyDelete(); }; #endif