Files
libsst/Include/ZSimulation/ZEntity.hpp
2026-04-03 00:22:39 -05:00

380 lines
13 KiB
C++

/*
ZEntity.hpp
Author: James Russell <jcrussell@762studios.com>
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 <ZUtil/ZRandomGenerator.hpp>
#include <ZUtil/ZName.hpp>
#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 <typename T>
T Get(const ZName& name) const
{ Property prop; GetProperty(name, prop); return (T)prop; }
template <typename T>
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<ZName, ZPropertyBuffer::Property, int64_t, ZHasher<ZName, int64_t>, 0> PropMap;
/*
Stack used to indicate which properties have been modified since last swap of properties.
*/
typedef ZArray<ZPair<ZName, ZPropertyBuffer::Property>> PropModifiedStack;
/*
Stack used to indicate which properties have been deleted since last swap of properties.
*/
typedef ZArray<ZName> PropDeletedStack;
/*
Stack used for state properties, which can be used to send data from one state to another.
*/
typedef ZArray<Property> StatePropStack;
/*
Buffer used as an entity mailbox - targeted messages are delivered here.
*/
typedef ZRingBuffer<ZMessage*> 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<mID, ZArray<MessageFunc>, int64_t, ZHasher<ZName, int64_t>, 0,
ZListPooledAllocator<ZHashNode<mID, ZArray<MessageFunc>, 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