Initial commit
This commit is contained in:
327
Include/ZSimulation/ZSimulation.hpp
Normal file
327
Include/ZSimulation/ZSimulation.hpp
Normal file
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
ZSimulation.hpp
|
||||
Author: James Russell <jcrussell@762studios.com>
|
||||
Created: 2/4/2013
|
||||
|
||||
Purpose:
|
||||
|
||||
Simulation base class for the ZSimulation project. Simulation is intended to be a server
|
||||
that handles the simulation of a large number of interacting entities. Entities will
|
||||
interact with each other via a messaging queue which will have handlers defined per
|
||||
message type.
|
||||
|
||||
Per this simulation, Actors are simply Entities with a 'think' function defined.
|
||||
|
||||
The nominal difference between server and client instances is that client instances
|
||||
can only ever connect to a single server instance, whereas server instances listen
|
||||
for client connections and can connect to other server instances to share the
|
||||
workload.
|
||||
|
||||
License:
|
||||
|
||||
Copyright 2013, 762 Studios
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ZSIMULATION_HPP
|
||||
#define ZSIMULATION_HPP
|
||||
|
||||
#include <SST/SST_Concurrency.h>
|
||||
|
||||
#include <ZSTL/ZArray.hpp>
|
||||
#include <ZSTL/ZArrayAlgo.hpp>
|
||||
|
||||
#include <ZUtil/ZUtil.hpp>
|
||||
|
||||
#include "ZSimulationDefs.hpp"
|
||||
#include "ZMessageStream.hpp"
|
||||
#include "ZEntity.hpp"
|
||||
#include "ZPropertyBuffer.hpp"
|
||||
#include "ZNetworkService.hpp"
|
||||
|
||||
/*
|
||||
Simulation class, which handles the following sphere of responsibilities:
|
||||
|
||||
* Fixed Timestep and Entity Update
|
||||
* Entity Message Passing
|
||||
* Entity Lifetime and Memory Management
|
||||
* Entity Property Allocation
|
||||
* Entity Property Network Synchronization
|
||||
* Entity Type Declaration
|
||||
* Entity State Declaration
|
||||
*/
|
||||
class ZSimulation : public ZThread
|
||||
{
|
||||
public:
|
||||
// c'tor
|
||||
ZSimulation();
|
||||
|
||||
// d'tor
|
||||
virtual ~ZSimulation();
|
||||
|
||||
// initialization methods
|
||||
bool InitAsServer(bool start = true);
|
||||
bool InitAsClient(bool start = true);
|
||||
|
||||
// shutdown method (this will deallocate entities)
|
||||
void Shutdown();
|
||||
|
||||
// getters
|
||||
uint32_t GetCurrentEntityCount() const { return CurrentEntityCount; }
|
||||
const ZEntity* GetEntity(eID entityId) const { return Entities[entityId]; }
|
||||
ZEntity* GetEntity(eID entityId) { return Entities[entityId]; }
|
||||
ZArray<ZEntity*>& GetBaseEntities() { return BaseEntities; }
|
||||
ZArray<ZEntity*>& GetActorEntities() { return ActorEntities; }
|
||||
uint64_t GetCurrentFrame() const { return CurrentFrame; }
|
||||
double GetCurrentFrameStartTime() const { return CurrentFrameStartTime; }
|
||||
int16_t GetSimulationHz() const { return SimulationHz; }
|
||||
double GetSimulationTimestep() const { return 1.0 / (double)SimulationHz; }
|
||||
size_t GetSimulationThreadCount() const { return ThreadCount; }
|
||||
ZPropertyBuffer& GetPropertyBuffer() { return PropertyBuffer; }
|
||||
ZRandomGenerator& GetPRNG() { return PRNG; }
|
||||
uint16_t GetListenPort() { return ListenPort; }
|
||||
uint8_t GetSyncRate() { return SyncRate; }
|
||||
ZNetworkService& GetNetworkService() { return NetworkService; }
|
||||
ZNetworkUpdateStream& GetNetworkUpdateStream() { return NetworkUpdateStream; }
|
||||
uint8_t GetNetworkSendRate() { return NetSendRate; }
|
||||
ZString& GetNetworkName() { return NetName; }
|
||||
bool IsServer() { return bIsServer; }
|
||||
|
||||
// function getters (can return NULL)
|
||||
ZEntity::InitFunc GetEntityInitFunc(const ZName& type);
|
||||
ZEntity::InitFunc GetEntityInitFunc(ZHashValue type);
|
||||
ZEntity::StateFunc GetEntityStateFunc(const ZName& name);
|
||||
ZEntity::StateFunc GetEntityStateFunc(ZHashValue name);
|
||||
|
||||
// setters
|
||||
void SetHz(int16_t simHz) { SimulationHz = simHz; }
|
||||
void SetThreadCount(int16_t threadCount) { ThreadCount = threadCount; }
|
||||
void SetListenPort(uint16_t port) { ListenPort = port; }
|
||||
void SetSyncRate(uint8_t rate) { SyncRate = rate; }
|
||||
void SetNetworkName(const ZString& name) { NetName = name; }
|
||||
|
||||
/*
|
||||
Connection method. Connects to another simulation instance. Returns the network request
|
||||
object which can be used to check status of the connection attempt. Delete the request
|
||||
when done.
|
||||
*/
|
||||
ZNetworkRequest* Connect(const char* remoteHost);
|
||||
|
||||
/*
|
||||
Declares an entity and initialization function. All entity initialization functions
|
||||
must be declared in order to ensure proper allocation of properties on all simulations,
|
||||
client and server.
|
||||
|
||||
It is recommended that all mapping be done during simulation initialization - no locking
|
||||
or concurrent access mechanisms are used when looking up mapped data.
|
||||
|
||||
The initialization function can be NULL, which will create an empty entity.
|
||||
|
||||
Ensure when declaring entity types that the name provided has its string representation.
|
||||
|
||||
@assert - on name collision (name hash resolves to same or entry already exists)
|
||||
*/
|
||||
void DeclareEntityType(const ZName& type, ZEntity::InitFunc init);
|
||||
|
||||
/*
|
||||
Declares an entity state function. Entity states are looked up by name to ensure recognition
|
||||
on all simulations, client and server.
|
||||
|
||||
It is recommended that all state mapping be done during simulation initialization - no locking
|
||||
or concurrent access mechanisms are used when looking up mapped data.
|
||||
|
||||
State functions cannot be NULL.
|
||||
|
||||
@assert - on name collision (name hash resolves to same or entry already exists)
|
||||
*/
|
||||
void DeclareEntityState(const ZName& name, ZEntity::StateFunc state);
|
||||
|
||||
/*
|
||||
Given the name, gives a string name for an entity type or state. These exist because names
|
||||
can be constructed without a string representation (such as when used for network communication).
|
||||
*/
|
||||
ZString LookupEntityTypeName(const ZName& type);
|
||||
ZString LookupEntityStateName(const ZName& name);
|
||||
|
||||
/*
|
||||
Spawn methods which add an entity to the simulation, returning the eID (0 if failure).
|
||||
|
||||
Variant which takes an eID request that an entity with a specific eID be added (returns
|
||||
false if unable to create).
|
||||
|
||||
@assert - if the entity type is undefined
|
||||
*/
|
||||
eID SpawnEntity(const ZName& type);
|
||||
bool SpawnEntity(const ZName& type, eID id);
|
||||
|
||||
/*
|
||||
Kill method, which removes entities from the simulation. This call cleans up the entity and any
|
||||
memory it has allocated.
|
||||
*/
|
||||
void KillEntity(eID id);
|
||||
|
||||
/*
|
||||
Sends a message through the simulation message queue, which will copy data from
|
||||
a struct into the message for you (MT must be a POD type). Local messages are not
|
||||
reflected across the network.
|
||||
*/
|
||||
template <typename MT> void SendLocalMessage(mID type, MT payload) const
|
||||
{ const_cast<ZSimulation*>(this)->MessageQueue.Send(type, ZSIM_EID_SYSTEM, ZSIM_EID_SYSTEM, payload); }
|
||||
|
||||
template <typename MT> void SendLocalMessage(mID type, eID sender, eID target, MT payload) const
|
||||
{ const_cast<ZSimulation*>(this)->MessageQueue.Send(type, sender, target, payload); }
|
||||
|
||||
/*
|
||||
Sends a message across the network, which will be processed by the message queue of
|
||||
another connected simulation. MT must be POD type and in order to ensure proper
|
||||
processing it should not contain pointers as these will be invalid on the other side.
|
||||
*/
|
||||
template <typename MT> void SendNetworkMessage(mID type, MT payload, cID connection) const
|
||||
{ const_cast<ZSimulation*>(this)->NetworkUpdateStream.EnqueueOutgoingEvent(new (std::nothrow) ZMessageEvent(type, ZSIM_EID_SYSTEM, ZSIM_EID_SYSTEM, payload, connection)); }
|
||||
|
||||
template <typename MT> void SendNetworkMessage(mID type, eID sender, eID target, MT payload, cID connection) const
|
||||
{ const_cast<ZSimulation*>(this)->NetworkUpdateStream.EnqueueOutgoingEvent(new (std::nothrow) ZMessageEvent(type, sender, target, payload, connection)); }
|
||||
|
||||
/*
|
||||
Pushes a task onto the task stream. This task will be executed by the simulation after
|
||||
simulation tasks have completed.
|
||||
*/
|
||||
void PushTask(ZPtr<ZTask> task);
|
||||
void PushTasks(ZArray<ZPtr<ZTask>>& tasks);
|
||||
|
||||
/*
|
||||
Pushes a future onto the task stream, which can be used to evaluate task progress.
|
||||
*/
|
||||
template <typename RT, typename AT> void PushFuture(ZPtr<ZFuture<RT, AT>> future) {
|
||||
TaskStream.PushFuture(future);
|
||||
}
|
||||
|
||||
/*
|
||||
Pushes or Pops a message handler onto the simulation. These message handlers are used for
|
||||
messages that are directed at the simulation or for messages that do not have a valid
|
||||
target entity.
|
||||
*/
|
||||
void PopHandler(mID type);
|
||||
void PushHandler(mID type, ZMessageHandler handler);
|
||||
|
||||
/*
|
||||
These methods notify the simulation that a targeted message was handled correctly or was
|
||||
unhandled. The simulation will return these messages to the message pool. These functions
|
||||
are primarily used by entities as they handle their own message queues.
|
||||
*/
|
||||
void HandledMessage(ZMessage* msg) const;
|
||||
void UnhandledMessage(ZMessage* msg) const;
|
||||
|
||||
/*
|
||||
This method notifies the simulation that an entity has had one or more of it's properties
|
||||
deleted. It's not terribly efficient to delete entity properties - use sparingly.
|
||||
*/
|
||||
void PropertyDeleted(eID id);
|
||||
|
||||
protected:
|
||||
int16_t SimulationHz; // current simulation Hz (tick rate per second)
|
||||
size_t ThreadCount; // number of concurrent simulation threads
|
||||
|
||||
double CurrentTime; // current tick count (most recent)
|
||||
double TotalTime; // total number of ticks that have been processed
|
||||
double Accumulator; // tick accumulator, used to help fix the timestep
|
||||
double LastSync; // the time we last did a network sync
|
||||
|
||||
uint64_t CurrentFrame; // number of simulation frames that have passed
|
||||
double CurrentFrameStartTime; // time at the start of the current frame
|
||||
|
||||
uint32_t CurrentEntityCount; // number of entities we have spawned
|
||||
size_t CurrentEntityIndex; // next entity we will hand out for simulation entities
|
||||
ZEntity* Entities[ZSIM_EID_MAX + 1]; // array mapping of entity id -> entity
|
||||
|
||||
ZArray<ZEntity*> BaseEntities; // base entities that have been created (no think state)
|
||||
ZArray<ZEntity*> ActorEntities; // actor entities that have been created (has a think state)
|
||||
|
||||
ZMessageStream MessageQueue; // message queue for the simulation
|
||||
ZTaskStream TaskStream; // task stream for the simulation, which concurrently processes actors
|
||||
ZRandomGenerator PRNG; // PRNG for the simulation
|
||||
|
||||
ZNetworkService NetworkService; // network service for this simulation
|
||||
ZNetworkUpdateStream NetworkUpdateStream; // the network update stream that processes sync and events
|
||||
bool bIsServer; // set by which init method we call
|
||||
uint16_t ListenPort; // listen port for this simulation server (0 = don't listen)
|
||||
uint8_t SyncRate; // the number of times per second we should send network updates
|
||||
uint8_t NetSendRate; // the rate at which the buffers are synchronized (updates per second)
|
||||
ZString NetName; // the name of this server / client on the network
|
||||
|
||||
ZPropertyBuffer PropertyBuffer; // allocator and sync manager for entity properties
|
||||
ZArray<eID> PropertyDeletedStack; // list of entities that have had a property deleted
|
||||
ZMutex PropertyDeletedLock; // lock for manipulating PropertyDeletedStack
|
||||
|
||||
ZHashMap<ZName, ZEntity::InitFunc> EntityTypeMap; // map of entity name to initialization functions
|
||||
ZHashMap<ZName, ZEntity::StateFunc> EntityStateMap; // map of entity name to initialization functions
|
||||
|
||||
// function to get the next entity id available
|
||||
eID GetNextEntityId()
|
||||
{
|
||||
size_t startIndex = CurrentEntityIndex;
|
||||
|
||||
ZEntity* ent = Entities[CurrentEntityIndex];
|
||||
|
||||
while (ent != NULL)
|
||||
{
|
||||
CurrentEntityIndex++;
|
||||
|
||||
if (CurrentEntityIndex > ZSIM_EID_MAX)
|
||||
CurrentEntityIndex = 0;
|
||||
|
||||
ZASSERT_RUNTIME(CurrentEntityIndex != startIndex, "No available entity Id's! Also, wow, that's a lot of entities.");
|
||||
|
||||
ent = Entities[CurrentEntityIndex];
|
||||
}
|
||||
|
||||
return (eID)CurrentEntityIndex;
|
||||
}
|
||||
|
||||
// Entity allocation methods (override in subclass to use specialized memory management)
|
||||
virtual ZEntity* AllocEntity(eID entityId);
|
||||
virtual ZEntity* AllocEntity(eID entityId, ZEntity::StateFunc startState);
|
||||
virtual void DeallocEntity(ZEntity* entity);
|
||||
|
||||
// Thread initialization method and shutdown method (be sure to call ZSimulation::initThread and ZSimulation::shutdownThread if subclassed)
|
||||
virtual void initThread();
|
||||
virtual void shutdownThread();
|
||||
|
||||
// Overridden 'Run' Method
|
||||
virtual ZThreadReturn run(uint64_t dt);
|
||||
|
||||
// Exposed Init and Shutdown methods for subclass simulations to implement
|
||||
virtual bool SubInitServer() { return true; }
|
||||
virtual bool SubInitClient() { return true; }
|
||||
virtual void SubShutdown() { }
|
||||
|
||||
// Exposed 'Simulation Tick' method for subclass simulations to implement
|
||||
virtual void Tick(double dt) { URFP(dt); }
|
||||
|
||||
// Some private static functions for handling the base message types
|
||||
static void HandleNetworkSystemShutdown(eID sender, eID target, void* payload, ZSimulation& sim); //
|
||||
static void HandleConnectionEstablished(eID sender, eID target, void* payload, ZSimulation& sim); //
|
||||
static void HandleConnectionFailed(eID sender, eID target, void* payload, ZSimulation& sim); //
|
||||
static void HandleConnectionLost(eID sender, eID target, void* payload, ZSimulation& sim); //
|
||||
static void HandleServerNodeGained(eID sender, eID target, void* payload, ZSimulation& sim); //
|
||||
static void HandleServerNodeLost(eID sender, eID target, void* payload, ZSimulation& sim); //
|
||||
|
||||
static void HandleSpawnEntity(eID sender, eID target, void* payload, ZSimulation& sim); // spawns an entity of a specific type
|
||||
static void HandleGainState(eID sender, eID target, void* payload, ZSimulation& sim); // transitions entity to 'actor' status
|
||||
static void HandleLoseState(eID sender, eID target, void* payload, ZSimulation& sim); // transitions from 'actor' to 'base entity' status
|
||||
static void HandleKill(eID sender, eID target, void* payload, ZSimulation& sim); // removes an entity from the simulation
|
||||
|
||||
static void HandleShutdownSimulation(eID sender, eID target, void* payload, ZSimulation& sim); // handles a shutdown message (shuts down this simulation)
|
||||
|
||||
static void HandleSetInt(eID sender, eID target, void* payload, ZSimulation& sim); // sets the integer value of a property
|
||||
static void HandleSetDbl(eID sender, eID target, void* payload, ZSimulation& sim); // sets the real number value of a property
|
||||
static void HandleSetVec(eID sender, eID target, void* payload, ZSimulation& sim); // sets the vector value of a property
|
||||
static void HandleSetMat(eID sender, eID target, void* payload, ZSimulation& sim); // sets the matrix value of a property
|
||||
static void HandleSetStr(eID sender, eID target, void* payload, ZSimulation& sim); // sets the string value of a property
|
||||
|
||||
private:
|
||||
DISABLE_COPY_AND_ASSIGN(ZSimulation);
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user