These are the docs for 13.3, an old version of SpatialOS.
The docs for this version are frozen: we do not correct, update or republish them.
14.2 is the newest →
Using the Dynamic class
The Improbable.Worker.Dynamic
class is a
way to execute arbitrary component-related logic in a type-safe way even when the component is not known statically.
Dynamic.ForComponent
will invoke
a custom handler for a component with a specific ID.
Dynamic.ForEachComponent
will
invoke a custom handler for every known component.
These handlers have enough information to register Dispatcher callbacks, manipulate component updates and data, and so on.
Example of usage
Dynamic
allows you to implement, for example, a
custom View
with any desired semantics from scratch:
using Improbable;
using Improbable.Collections;
using Improbable.Worker;
public sealed class CustomView : Dispatcher
{
// Maintain a Map of the entities in this worker's view.
private readonly Map<EntityId, Entity> entities = new Map<EntityId, Entity>();
private readonly Map<EntityId, Map<uint, Authority>> authorityMap =
new Map<EntityId, Map<uint, Authority>>();
// Current component data for all entities in the worker's view.
public Map<EntityId, Entity> Entities
{
get { return entities; }
}
// Current authority delegations.
public Map<EntityId, Map<uint, Authority>> AuthorityMap
{
get { return authorityMap; }
}
// Helper function that checks if the worker has authority over a particular component of a
// particular entity.
public Authority GetAuthority<C>(EntityId entityId) where C : IComponentMetaclass, new()
{
Map<uint, Authority> entityAuthority;
if (!authorityMap.TryGetValue(entityId, out entityAuthority))
{
return Authority.NotAuthoritative;
}
Authority componentAuthority;
return entityAuthority.TryGetValue(Dynamic.GetComponentId<C>(), out componentAuthority) ? componentAuthority : Authority.NotAuthoritative;
}
// An implementation of Dynamic.Handler to provide type-safe behaviour for
// the components.
private class TrackComponent : Dynamic.Handler
{
private readonly CustomView view;
public TrackComponent(CustomView view)
{
this.view = view;
}
// Define the type-safe behaviour in the Accept method.
// Here, we are specifying callbacks to update our view's entities map
// when components are added or removed, when the worker's authority
// over an entity changes or when we receive component updates.
public void Accept<C>(C metaclass) where C : IComponentMetaclass
{
view.OnAddComponent<C>(op =>
{
if (view.Entities.ContainsKey(op.EntityId))
{
view.Entities[op.EntityId].Add<C>(op.Data);
}
if (view.AuthorityMap.ContainsKey(op.EntityId))
{
view.AuthorityMap[op.EntityId][metaclass.ComponentId] = Authority.NotAuthoritative;
}
});
view.OnRemoveComponent<C>(op =>
{
if (view.Entities.ContainsKey(op.EntityId))
{
view.Entities[op.EntityId].Remove<C>();
}
if (view.AuthorityMap.ContainsKey(op.EntityId))
{
view.AuthorityMap[op.EntityId].Remove(metaclass.ComponentId);
}
});
view.OnAuthorityChange<C>(op =>
{
if (view.AuthorityMap.ContainsKey(op.EntityId))
{
view.AuthorityMap[op.EntityId][metaclass.ComponentId] = op.Authority;
}
});
view.OnComponentUpdate<C>(op =>
{
if (view.Entities.ContainsKey(op.EntityId))
{
view.Entities[op.EntityId].Update<C>(op.Update);
}
});
}
}
public CustomView()
{
// When entities are added or removed, the Entities map is updated.
OnAddEntity(op =>
{
Entities.Add(op.EntityId, new Entity());
AuthorityMap.Add(op.EntityId, new Map<uint, Authority>());
});
OnRemoveEntity(op =>
{
Entities.Remove(op.EntityId);
AuthorityMap.Remove(op.EntityId);
});
// Invoke the TrackComponent handler for every component.
Dynamic.ForEachComponent(new TrackComponent(this));
}
}