Sites

Menu

Remote interactions

The remote interaction service is in alpha and may have breaking changes before stabilizing. For more information on maturity stages see Maturity stages.

Introduction

In many different situations, an online service or other administration tool for your game needs to interact with the world state of a deployment. Examples of such situation are:

  • To start a planned maintenance routine in your game logic without stopping the deployment.
  • To query your deployment’s health and game status.

In the following examples, we walk you through how to use the remote interaction service that the Platform SDK provides to invoke RPC-like interactions on an arbitrary running deployment.

Note: The semantics of all the interactions above, in terms of ordering and timeouts, are identical to a worker instance sending the commands.

Find out how to download the Platform SDK from the setup guide.

The alpha version of the remote interaction service requires the use of (de)serialisation logic. You must use the C# Worker SDK in release 14.0.0 or later to access the corresponding functionality. This constraint will be relaxed in future releases of the service.

Instantiating a client

In order to instantiate a client for the remote interaction service you can use:

private readonly RemoteInteractionServiceClient _remoteInteractionServiceClient = RemoteInteractionServiceClient.Create();

By default, it uses your refresh token saved locally by the SpatialOS CLI:

  • Windows: C:\Users\<username>\AppData\Local\.improbable\oauth2\oauth2_refresh_token\
  • Mac: ~/.improbable/oauth2/oauth2_refresh_token

If you want to provide a refresh token, you can instantiate a PlatformRefreshTokenCredential object and supply it to Create:

var credentialsWithProvidedToken = new PlatformRefreshTokenCredential("my_refresh_token");
private readonly RemoteInteractionServiceClient _remoteInteractionServiceClient = RemoteInteractionServiceClient.Create(credentials: credentialsWithProvidedToken);

Example: creating and deleting entities

Create an entity using the remote interaction service

Note: To create the initial entity snapshot, you follow the same steps as when using the C# Worker SDK. The difference is that instead of calling SendCreateEntityRequest on the worker connection, you need to serialize the newly created entity and call the Platform SDK.

Entity newEntity;

// Entity snapshot initialisation.
// [...]

CreateEntityResponse createEntityResponse;
try
{
    // Make a blocking call to the service creating the entity.
    createEntityResponse = _remoteInteractionServiceClient.CreateEntity(new CreateEntityRequest
    {
        Target = new Deployment { ProjectName = "your-cloud-project-name", DeploymentName = "your-deployment-name" },
        Binary = ByteString.CopyFrom(newEntity.ToEntityState()),
    });
}
catch (RpcException e)
{
    Console.Error.WriteLine($"Create entity request failed with code {e.Status.StatusCode} - {e.Status.Detail}")
    return;
}
if (response.Status.StatusCode != CommandStatus.Types.Code.Ok)
{
    Console.Error.WriteLine($"Create entity request failed with code {response.Status.StatusCode} with reason: {response.Status.Message}.");
}
else
{
    Console.WriteLine($"Entity was created with ID {response.EntityId}");
}

Delete the entity. You need the corresponding entity ID.

long entityIdToDelete;
DeleteEntityResponse deleteEntityResponse;
try
{
    // Make a blocking call to the service deleting the entity.
    deleteEntityResponse = _remoteInteractionServiceClient.DeleteEntity(new DeleteEntityRequest
    {
        Target = new Deployment { ProjectName = "your-cloud-project-name", DeploymentName = "your-deployment-name" },
        EntityId = entityIdToDelete,
    });
}
catch (RpcException e)
{
    Console.Error.WriteLine($"Delete entity request failed with code {e.Status.StatusCode} - {e.Status.Detail}")
    return;
}
if (response.Status.StatusCode != CommandStatus.Types.Code.Ok)
{
    Console.Error.WriteLine($"Delete entity request failed with code {response.Status.StatusCode} with reason: {response.Status.Message}.");
}
else
{
    Console.WriteLine($"Entity with ID {entityIdToDelete} was successfully deleted.");
}

Example: invoking an entity command

Invoke an entity command.

Note: You must specify both a worker type and ID when you send a command via the remote interaction service, where:

  • The worker type needs to correspond to one that you’ve defined in your project’s configuration.
  • The provided worker ID will be made available to the worker handling the command via the CallerWorkerId field in the CommandRequestOp.
long targetEntityId;
var commandRequest = new MyCustomCommandRequest
{
    fieldA = "valueA",
    fieldB = 10,
};

EntityCommandResponse entityCommandResponse;
try
{
    // Make a blocking call to the service running the command on the targeted entity / component.
    entityCommandResponse = _remoteInteractionServiceClient.EntityCommand(new EntityCommandRequest
    {
        Target = new Deployment { ProjectName = "your-cloud-project-name", DeploymentName = "your-deployment-name" },
        WorkerType = "my-worker-type",
        WorkerId = "my-worker-type-remote",
        ComponentTarget = new EntityCommandTarget
        {
            EntityId = targetEntityId
            ComponentId = MyCustomData.Metaclass.ComponentId,
            CommandIdx = MyCustomData.Commands.MyCustomCommand.Metaclass.CommandIdx,
        },
        Binary = ByteString.CopyFrom(commandRequest.ToBinaryEncodedSchemaRepresentation()),
    });
}
catch (RpcException e)
{
    Console.Error.WriteLine($"Entity command request failed with code {e.Status.StatusCode} - {e.Status.Detail}")
    return;
}
if (response.Status.StatusCode != CommandStatus.Types.Code.Ok)
{
    Console.Error.WriteLine($"Entity command request failed with code {response.Status.StatusCode} with reason: {response.Status.Message}.");
    return;
}
var commandResponse = MyCustomCommandResponse.FromBinaryEncodedSchemaRepresentation(entityCommandResponse.Binary.ToByteArray());

Example: querying the deployment

Running a one-off query is similar to sending entity commands. The difference is when you run it, you serialize the query expression in the request, and entity states are deserialized from the response. Here’s an example where you query all entities with the MyCustomData component to return their Position components.

EntityQueryResponse entityQueryResponse;
try
{
    // Make a blocking call to the service running the one-off query.
    entityQueryResponse = _remoteInteractionServiceClient.EntityQuery(new EntityQueryRequest
    {
        Target = new Deployment { ProjectName = "your-cloud-project-name", DeploymentName = "your-deployment-name" },
        Binary = ByteString.CopyFrom(new ComponentInterest.Query(
            new ComponentInterest.QueryConstraint { componentInterest = MyCustomData.Metaclass.ComponentId },
            false,
            new Improbable.Collections.List<uint> { Position.ComponentId },
            null
        ).ToBinaryEncodedSchemaRepresentation()),
    });
}
catch (Grpc.Core.RpcException e)
{
    Console.Error.WriteLine($"Entity query request failed with code {e.Status.StatusCode} - {e.Status.Detail}.");
    return;
}
if (response.Status.StatusCode != CommandStatus.Types.Code.Ok)
{
    Console.Error.WriteLine($"Entity query request failed with code {response.Status.StatusCode} for reason: {response.Status.Message}.");
    return;
}

var entityList = new Dictionary<long, Entity>();
foreach (var result in response.Result)
{
    entityList[result.Id] = Entity.FromEntityState(result.Binary.ToByteArray());
}

2019-10-4 Page added with limited editorial review

Search results

Was this page helpful?

Thanks for letting us know!

Thanks for your feedback

Need more help? Ask on the forums