Skip to content
This repository has been archived by the owner on Jan 18, 2022. It is now read-only.

Typed command ids #1372

Merged
merged 7 commits into from
May 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- `WorkerConnector.HandleWorkerConnectionFailure` has been removed and `WorkerConnector.Connect` now throws exceptions for connection errors instead. [#1365](https://github.com/spatialos/gdk-for-unity/pull/1365)
- `WorkerConnector` no longer destroys itself in `Dispose`. [#1365](https://github.com/spatialos/gdk-for-unity/pull/1365)
- `MultiThreadedSpatialOSConnectionHandler` and `SpatialOSConnectionHandlerBuilder.SetThreadingMode` have been removed. [#1367](https://github.com/spatialos/gdk-for-unity/pull/1367)
- Command request IDs are now typed as `CommandRequestID` instead of `long`. [#1372](https://github.com/spatialos/gdk-for-unity/pull/1372)

### Added

Expand Down
7 changes: 7 additions & 0 deletions UPGRADE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## From `0.3.5` to `0.3.6`

### Command Request ID's are now typed

The return value of `CommandSystem.SendCommand` has changed from `long` to `CommandRequestID`.
A similar change has been made to all APIs that take request IDs as a parameter.

Anywhere you store request IDs, you need to change the type from `long` to `CommandRequestId`.

### Multithreaded connection removed

The `MultiThreadedSpatialOSConnectionHandler` has been removed and the method `SpatialOSConnectionHandlerBuilder.SetThreadingMode` has been removed to reflect this change.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static CodeWriter Generate(UnityComponentDetails componentDetails)
{
cgw.UsingDirectives(
"Improbable.Gdk.Core",
"Improbable.Gdk.Core.Commands",
"Improbable.Worker.CInterop"
);

Expand Down Expand Up @@ -60,8 +61,9 @@ public void AddResponseToDiff(CommandResponseOp op, ViewDiff diff, CommandMetaDa
rawResponse = {command.FqnResponseType}.Serialization.Deserialize(op.Response.SchemaData.Value.GetObject());
}}

var commandContext = commandMetaData.GetContext<{command.FqnRequestType}>(ComponentId, {command.CommandIndex}, op.RequestId);
commandMetaData.RemoveRequest(ComponentId, {command.CommandIndex}, op.RequestId);
var internalRequestId = new InternalCommandRequestId(op.RequestId);
var commandContext = commandMetaData.GetContext<{command.FqnRequestType}>(ComponentId, {command.CommandIndex}, internalRequestId);
commandMetaData.RemoveRequest(ComponentId, {command.CommandIndex}, internalRequestId);

var response = new {command.PascalCaseName}.ReceivedResponse(
commandContext.SendingEntity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public static CodeWriter Generate(UnityComponentDetails componentDetails)
return CodeWriter.Populate(cgw =>
{
cgw.UsingDirectives(
"Improbable.Gdk.Core"
"Improbable.Gdk.Core",
"Improbable.Gdk.Core.Commands"
);

cgw.Namespace(componentDetails.Namespace, ns =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public Response(long requestId, string failureMessage)
public readonly {commandDetails.FqnResponseType}? ResponsePayload;
public readonly {commandDetails.FqnRequestType} RequestPayload;
public readonly global::System.Object Context;
public readonly long RequestId;
public readonly CommandRequestId RequestId;

public ReceivedResponse(
Unity.Entities.Entity sendingEntity,
Expand All @@ -125,7 +125,7 @@ public ReceivedResponse(
{commandDetails.FqnResponseType}? response,
{commandDetails.FqnRequestType} request,
global::System.Object context,
long requestId)
CommandRequestId requestId)
{{
SendingEntity = sendingEntity;
EntityId = entityId;
Expand All @@ -137,7 +137,7 @@ public ReceivedResponse(
RequestId = requestId;
}}

long IReceivedCommandResponse.RequestId => RequestId;
CommandRequestId IReceivedCommandResponse.RequestId => RequestId;
}}

public readonly struct RawReceivedResponse : IRawReceivedCommandResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ internal static class ReceivedMessageListExtensions
{
// binary search for the command for given request ID
// invariant: lower <= target <= upper
public static int? GetResponseIndex<T>(this MessageList<T> list, long requestId)
public static int? GetResponseIndex<T>(this MessageList<T> list, CommandRequestId requestId)
where T : struct, IReceivedCommandResponse
{
var targetId = requestId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public interface IReceivedCommandResponse : IReceivedMessage
/// Gets the request ID from the request. For use in generic methods.
/// </summary>
/// <returns> The request ID associated with the request </returns>
long RequestId { get; }
CommandRequestId RequestId { get; }
}

public interface IRawReceivedCommandResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public MessagesSpan<TResponse> GetResponses()
return responseStorage.Slice();
}

public MessagesSpan<TResponse> GetResponse(long requestId)
public MessagesSpan<TResponse> GetResponse(CommandRequestId requestId)
{
if (!responsesSorted)
{
Expand All @@ -83,15 +83,15 @@ public MessagesSpan<TResponse> GetResponse(long requestId)
: MessagesSpan<TResponse>.Empty();
}

private class RequestComparer<T> : IComparer<T> where T : struct, IReceivedCommandRequest
private sealed class RequestComparer<T> : IComparer<T> where T : struct, IReceivedCommandRequest
{
public int Compare(T x, T y)
{
return x.EntityId.Id.CompareTo(y.EntityId.Id);
}
}

private class ResponseComparer<T> : IComparer<T> where T : struct, IReceivedCommandResponse
private sealed class ResponseComparer<T> : IComparer<T> where T : struct, IReceivedCommandResponse
{
public int Compare(T x, T y)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
using System.Linq;
using Unity.Entities;

namespace Improbable.Gdk.Core
namespace Improbable.Gdk.Core.Commands
{
public readonly struct CommandContext<T>
{
public readonly Entity SendingEntity;
public readonly T Request;
public readonly object Context;
public readonly long RequestId;
public readonly CommandRequestId RequestId;

public CommandContext(Entity sendingEntity, T request, object context, long requestId)
public CommandContext(Entity sendingEntity, T request, object context, CommandRequestId requestId)
{
SendingEntity = sendingEntity;
Request = request;
Expand All @@ -24,24 +24,24 @@ public CommandContext(Entity sendingEntity, T request, object context, long requ
public class CommandMetaData
{
// Cache the types needed to instantiate a new CommandMetaData
private static List<(uint componentId, Type command)> storageTypes;
private static readonly List<(uint componentId, Type command)> StorageTypes;

private readonly HashSet<long> internalRequestIds = new HashSet<long>();
private readonly HashSet<InternalCommandRequestId> internalRequestIds = new HashSet<InternalCommandRequestId>();

private readonly Dictionary<(uint componentId, uint commandId), ICommandMetaDataStorage> componentCommandToStorage =
new Dictionary<(uint componentId, uint commandId), ICommandMetaDataStorage>();

public CommandMetaData()
static CommandMetaData()
{
if (storageTypes == null)
{
storageTypes = ComponentDatabase.Metaclasses
.SelectMany(type => type.Value.Commands
.Select(c => (componentId: type.Value.ComponentId, command: c.MetaDataStorage)))
.ToList();
}
StorageTypes = ComponentDatabase.Metaclasses
.SelectMany(type => type.Value.Commands
.Select(c => (componentId: type.Value.ComponentId, command: c.MetaDataStorage)))
.ToList();
}

foreach (var (componentId, type) in storageTypes)
public CommandMetaData()
{
foreach (var (componentId, type) in StorageTypes)
{
var instance = (ICommandMetaDataStorage) Activator.CreateInstance(type);
componentCommandToStorage.Add((componentId, instance.CommandId), instance);
Expand All @@ -50,7 +50,7 @@ public CommandMetaData()
componentCommandToStorage.Add((0, 0), new WorldCommandMetaDataStorage());
}

public void RemoveRequest(uint componentId, uint commandId, long internalRequestId)
public void RemoveRequest(uint componentId, uint commandId, InternalCommandRequestId internalRequestId)
{
var commandMetaDataStorage = GetCommandDiffStorage(componentId, commandId);
commandMetaDataStorage.RemoveMetaData(internalRequestId);
Expand All @@ -63,14 +63,14 @@ public void AddRequest<T>(uint componentId, uint commandId, in CommandContext<T>
commandPayloadStorage.AddRequest(in context);
}

public void AddInternalRequestId(uint componentId, uint commandId, long requestId, long internalRequestId)
public void AddInternalRequestId(uint componentId, uint commandId, CommandRequestId requestId, InternalCommandRequestId internalRequestId)
{
internalRequestIds.Add(internalRequestId);
var commandMetaDataStorage = GetCommandDiffStorage(componentId, commandId);
commandMetaDataStorage.SetInternalRequestId(internalRequestId, requestId);
}

public CommandContext<T> GetContext<T>(uint componentId, uint commandId, long internalRequestId)
public CommandContext<T> GetContext<T>(uint componentId, uint commandId, InternalCommandRequestId internalRequestId)
{
var commandPayloadStorage = (ICommandPayloadStorage<T>) GetCommandDiffStorage(componentId, commandId);
return commandPayloadStorage.GetPayload(internalRequestId);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using System;

namespace Improbable.Gdk.Core.Commands
{
public readonly struct CommandRequestId : IEquatable<CommandRequestId>, IComparable<CommandRequestId>
{
public readonly long Raw;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... does this need to be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For us no, but I wanted to keep this public in case someone used the command ID's for something.

Copy link
Contributor

@jamiebrynes7 jamiebrynes7 May 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But then they can use this right? It has equality and comparison operators. Like why would you want the raw number over the wrapper struct?


public CommandRequestId(long requestId)
{
Raw = requestId;
}

public static bool operator ==(CommandRequestId left, CommandRequestId right)
{
return left.Equals(right);
}

public static bool operator !=(CommandRequestId left, CommandRequestId right)
{
return !left.Equals(right);
}

public static bool operator <(CommandRequestId left, CommandRequestId right)
{
return left.CompareTo(right) < 0;
}

public static bool operator >(CommandRequestId left, CommandRequestId right)
{
return left.CompareTo(right) > 0;
}

public static bool operator <=(CommandRequestId left, CommandRequestId right)
{
return left.CompareTo(right) <= 0;
}

public static bool operator >=(CommandRequestId left, CommandRequestId right)
{
return left.CompareTo(right) >= 0;
}

public bool Equals(CommandRequestId other)
{
return Raw == other.Raw;
}

public override bool Equals(object obj)
{
return obj is CommandRequestId other && Equals(other);
}

public override int GetHashCode()
{
return Raw.GetHashCode();
}

public int CompareTo(CommandRequestId other)
{
return Raw.CompareTo(other.Raw);
}
}

public readonly struct InternalCommandRequestId : IEquatable<InternalCommandRequestId>, IComparable<InternalCommandRequestId>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between InternalCommandRequestId and CommandRequestId? Is it that they're two different wrappers for two different types of commands and could theoretically deviate from each other? Or are you trying to create type safety?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are the same data wise, but 1 is an id the GDK generates when you schedule a command to be send. And the internal ID is the ID the worker SDK returns when the command is actually send over the network.
Using 2 different types here ensures they aren't accidentally mixed up,

{
public readonly long Raw;

public InternalCommandRequestId(long requestId)
{
Raw = requestId;
}

public static bool operator ==(InternalCommandRequestId left, InternalCommandRequestId right)
{
return left.Equals(right);
}

public static bool operator !=(InternalCommandRequestId left, InternalCommandRequestId right)
{
return !left.Equals(right);
}

public static bool operator <(InternalCommandRequestId left, InternalCommandRequestId right)
{
return left.CompareTo(right) < 0;
}

public static bool operator >(InternalCommandRequestId left, InternalCommandRequestId right)
{
return left.CompareTo(right) > 0;
}

public static bool operator <=(InternalCommandRequestId left, InternalCommandRequestId right)
{
return left.CompareTo(right) <= 0;
}

public static bool operator >=(InternalCommandRequestId left, InternalCommandRequestId right)
{
return left.CompareTo(right) >= 0;
}

public bool Equals(InternalCommandRequestId other)
{
return Raw == other.Raw;
}

public override bool Equals(object obj)
{
return obj is InternalCommandRequestId other && Equals(other);
}

public override int GetHashCode()
{
return Raw.GetHashCode();
}

public int CompareTo(InternalCommandRequestId other)
{
return Raw.CompareTo(other.Raw);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public void Clear()
responseStorage.Clear();
}

public void AddRequest(TRequest request, Entity entity, long requestId)
public void AddRequest(TRequest request, Entity entity, CommandRequestId requestId)
{
requestStorage.Add(new CommandRequestWithMetaData<TRequest>(request, entity, requestId));
}
Expand All @@ -43,4 +43,18 @@ internal MessageList<TResponse> GetResponses()
return responseStorage;
}
}

internal readonly struct CommandRequestWithMetaData<T>
{
public readonly T Request;
public readonly Entity SendingEntity;
public readonly CommandRequestId RequestId;

public CommandRequestWithMetaData(T request, Entity sendingEntity, CommandRequestId requestId)
{
Request = request;
SendingEntity = sendingEntity;
RequestId = requestId;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Improbable.Worker.CInterop;

namespace Improbable.Gdk.Core.Commands
{
public interface ICommandDiffDeserializer
{
void AddRequestToDiff(CommandRequestOp op, ViewDiff diff);
void AddResponseToDiff(CommandResponseOp op, ViewDiff diff, CommandMetaData commandMetaData);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading