Sites

Menu
These are the docs for 14.0, 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 →

Creating and deleting entities

All code examples on this page assume import improbable.worker.*; and import example.*; (the generated code namespace).

Note: In order to create or delete an entity, a worker must have permission to do so. For more information, see the Worker permissions page.

Using the improbable.worker.Connection, a worker can request SpatialOS to:

  • reserve a range of entity IDs using sendReserveEntityIdsRequest
  • create an entity using sendCreateEntityRequest
  • delete an entity, by using the methods sendDeleteEntityRequest

For all of these methods:

  • They return an improbable.worker.RequestId<C> request ID, which can be used to match a request with its response. The type parameter C depends on the type of request being sent.
  • The response is received via a callback registered with the improbable.worker.Dispatcher using onReserveEntityIdResponse, onReserveEntityIdsResponse, onCreateEntityResponse, and onDeleteEntityResponse, respectively.
  • The request can fail, so the statusCode field in the worker.Ops.ReserveEntityIdResponse, worker.Ops.ReserveEntityIdsResponse, worker.Ops.CreateEntityResponse, or worker.Ops.DeleteEntityResponse argument of the respective callback should be checked, and the command can be retried as necessary. The caller will always get a response callback, but it might be due to a timeout.

Entity ID Reservations

It is possible to reserve entity IDs before creating entities.

Note: Entity ID reservations can expire at an arbitrary time in the future, without warning. Therefore, it is not guaranteed that the reserved entity ID will be still be valid by the time you create an entity with it. For this reason, we recommend creating entities without reserving IDs, and instead using the automatically assigned entity ID returned in [worker.Ops.CreateEntityResponse].

Reserve a range of entity IDs using the Connection method sendReserveEntityIdsRequest. sendReserveEntityIdsRequest takes an optional timeout. If the operation succeeds, the response contains an entity ID, which is the first in a contiguous range guaranteed to be unused in the current deployment, and the corresponding number of entity IDs in the reserved range. If the operation fails, the entity ID is not set and the number of entity IDs in the reserved range is 0.

Create an Entity

Create an entity using the Connection method sendCreateEntityRequest, which takes:

  • a improbable.worker.Entity representing the initial state of the entity
  • an optional entity ID (which, if provided, must have been obtained by a previous call to sendReserveEntityIdRequest)
  • an optional timeout

If the operation succeeds, the response contains the ID of the newly created entity.

Delete an Entity

Delete an entity using sendDeleteEntityRequest takes an entity ID and an optional timeout. The response contains no additional data.

Example

Here’s an example of reserving an entity ID, creating an entity with that ID and some initial state, and finally deleting it:

private static void createDeleteEntity(Dispatcher dispatcher, Connection connection) {
  final String entityType = "Player";
  final improbable.collections.Option<Integer> timeoutMillis = improbable.collections.Option.of(500);

  class RequestIds {
    private RequestId<CreateEntityRequest> entityCreationRequest;
    private RequestId<DeleteEntityRequest> entityDeletionRequest;
  }

  final RequestIds requestIds = new RequestIds();

  // Reserve an entity ID.
  RequestId<ReserveEntityIdsRequest> entityIdReservationRequestId =
      connection.sendReserveEntityIdsRequest(1, timeoutMillis);

  // When the reservation succeeds, create an entity with the reserved ID.
  dispatcher.onReserveEntityIdsResponse(op -> {
    if (op.requestId.equals(entityIdReservationRequestId) && op.statusCode == StatusCode.SUCCESS) {
      Entity entity = new Entity();
      entity.add(improbable.Metadata.COMPONENT, new improbable.MetadataData(entityType));
      // Empty ACL - should be customised.
      entity.add(improbable.EntityAcl.COMPONENT,
                 new improbable.EntityAclData(new improbable.WorkerRequirementSet(new java.util.ArrayList<>()),
                                              new java.util.HashMap<>()));
      // Needed for the entity to be persisted in snapshots.
      entity.add(improbable.Persistence.COMPONENT, new improbable.PersistenceData());
      entity.add(improbable.Position.COMPONENT, new improbable.PositionData(new improbable.Coordinates(1, 2, 3)));
      requestIds.entityCreationRequest = connection.sendCreateEntityRequest(entity, op.firstEntityId, timeoutMillis);
    }
  });

  // When the creation succeeds, delete the entity.
  dispatcher.onCreateEntityResponse(op -> {
    if (op.requestId.equals(requestIds.entityCreationRequest) && op.statusCode == StatusCode.SUCCESS) {
      requestIds.entityDeletionRequest = connection.sendDeleteEntityRequest(op.entityId.get(), timeoutMillis);
    }
  });

  // When the deletion succeeds, we're done.
  dispatcher.onDeleteEntityResponse(op -> {
    if (op.requestId.equals(requestIds.entityDeletionRequest) && op.statusCode == StatusCode.SUCCESS) {
      System.out.println("Successfully created and deleted entity.");
    }
  });
}

Entity ACLs

Entity ACLs are exposed to the worker as a component, and can be manipulated as any other component. ACLs can be set at entity creation time, or modified dynamically at runtime by the worker that has write access authority over the EntityAcl component.

This example adds an EntityAcl to an Entity given a CommandRequestOp to make a specific worker (as opposed to, potentially, a set of workers) qualify for write access authority of a component. Specifically:

  • The entity will be visible to workers that have the “client” or “physics” worker attribute (in other words, workers that are in the “client” or “physics” layer).
  • Any “physics” worker (i.e. a worker with “physics” as its attribute) can have write access authority over the entity’s Position and EntityAcl components.
  • The worker which issued the Ops.CommandRequest is the only worker that can have write access authority over the PlayerControls component.

This can be used as part of creating a new entity in response to a command request.

public static void addComponentDelegations(CommandRequestOp<?, ?> op, Entity entity) {
  // This requirement set matches only the command caller, i.e. the worker that issued the command,
  // since callerWorkerAttributes includes the caller's unique attribute.
  improbable.WorkerAttributeSet callerWorkerAttributeSet =
      new improbable.WorkerAttributeSet(java.util.Collections.singletonList("workerId:" + op.callerWorkerId));
  improbable.WorkerRequirementSet callerWorkerRequirementSet =
      new improbable.WorkerRequirementSet(java.util.Collections.singletonList(callerWorkerAttributeSet));

  // Worker attribute set of a physics worker
  improbable.WorkerAttributeSet physicsWorkerAttributeSet =
      new improbable.WorkerAttributeSet(java.util.Collections.singletonList("physics"));
  // Worker attribute set of a client worker
  improbable.WorkerAttributeSet clientWorkerAttributeSet =
      new improbable.WorkerAttributeSet(java.util.Collections.singletonList("client"));

  // This requirement set matches any worker with the attribute "physics".
  improbable.WorkerRequirementSet physicsWorkerRequirementSet =
      new improbable.WorkerRequirementSet(java.util.Collections.singletonList(physicsWorkerAttributeSet));

  // This requirement set matches any worker with the attribute "client" or "physics".
  java.util.List<improbable.WorkerAttributeSet> clientOrPhysicsAttributeSets = new java.util.LinkedList<>();
  clientOrPhysicsAttributeSets.add(clientWorkerAttributeSet);
  clientOrPhysicsAttributeSets.add(physicsWorkerAttributeSet);

  improbable.WorkerRequirementSet clientOrPhysicsRequirementSet =
      new improbable.WorkerRequirementSet(clientOrPhysicsAttributeSets);

  // Give authority over Position and EntityAcl to any physics worker, and over PlayerControls to the caller worker.
  java.util.Map<Integer, improbable.WorkerRequirementSet> componentAcl = new java.util.HashMap<>();
  componentAcl.put(improbable.Position.COMPONENT_ID, physicsWorkerRequirementSet);
  componentAcl.put(improbable.EntityAcl.COMPONENT_ID, physicsWorkerRequirementSet);
  componentAcl.put(PlayerControls.COMPONENT_ID, callerWorkerRequirementSet);

  entity.add(improbable.EntityAcl.COMPONENT,
             new improbable.EntityAclData(
                 /* read */ clientOrPhysicsRequirementSet, /* write */ componentAcl));
}

The worker that has write access authority over the EntityAcl component can later decide to give the write access authority over position (or any other component) to a different worker, e.g. the client. In order to do this, EntityAclData needs to be modified in order to map Position.COMPONENT_ID to callerWorkerRequirementSet (created above). This change can be made using the method below:

public static improbable.EntityAclData delegateComponent(improbable.EntityAclData currentAcl, Integer componentId,
                                                         improbable.WorkerRequirementSet requirementSet) {
  // Take a deep copy, so that this does not modify the current EntityAcl.
  improbable.EntityAclData newAcl = currentAcl.deepCopy();
  // Set the write ACL for the specified component to the specified attribute set.
  newAcl.getComponentWriteAcl().put(componentId, requirementSet);
  return newAcl;
}

For these changes to take effect, the worker that has write access authority over the EntityAcl component needs send the changes through a component update.

See Worker attributes and worker requirement sets for more information.

Search results

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums