auto static_args = arguments.to_octree_params<octree_traits>();
static_args.build_user_state = [](
const aether_cell_state<octree_traits> &aether_state
) -> std::unique_ptr<user_state<octree_traits>> {
using user_state_type = entity_store_wrapper<entity_store_traits<octree_traits>>;
user_state_type::params_type params{};
// initialise_world handles any global setup, e.g. creating our prey
params.initialise_world = &initialise;
// initialise_cell handles per-cell setup, including ECS configuration
params.initialise_cell = &initialise_cell;
// handle_events allows the simulation to respond to player input
params.handle_events = &handle_events;
// cell_tick is the called per cell, per tick and advances the simulation
params.cell_tick = &cell_tick;
// serialize_to_client handles serializing the cell state to be sent to
// clients. The packets generated by this function will be sent to the
// muxer, which can choose what needs to be forwarded to which client
params.serialize_to_client = &cell_state_serialize;
// estimate_load is a hook to allow workers to indicate if they are being
// over or under utilised.
params.estimate_load = &estimate_load;
// deinitialise_cell allows for any per-cell cleanup
params.deinitialise_cell = &deinitialise_cell;
// One of the Aether Engine core concepts is that any agents within the
// simulation have a handover radius that defines how close to the edge
// of a neighbouring cell that agent would need to be before the neighbouring
// cell is made aware of the agents presence.
// Here we query the components the agent has from the ECS to decide what
// the handover radius should be.
params.agent_aabb = [](const auto &aether_state, const user_cell_state&, user_cell_state::agent_reference agent) -> auto {
if( agent.has<c_base>() )
pos = agent.get_dynamic<c_base>()->position;
const float reach = agent.has<c_prey>() ? PREY_HANDOVER_RADIUS : PREDATOR_HANDOVER_RADIUS;
return encoder<2>::encode_aabb(pos, reach);
// In order to judge how close the entity is to neighbouring cells we need a
// single point, and this function is the one that defines that point.
params.agent_center = [](const auto &aether_state, const user_cell_state&, user_cell_state::agent_reference agent) -> auto {
if( agent.has<c_base>() )
pos = agent.get_dynamic<c_base>()->position;
return encoder<2>::encode_position(pos);
return std::unique_ptr<user_state<octree_traits>>(
new user_state_type(params, aether_state)