Mimicking "Request" and "Response" in Coherence

In coherence, users cannot get the return value of the commands. However, it is sometimes important to get some sort of “response” or “callback” after a command is sent. For example, suppose some chat messages are stored in the simulator and I want to “download” them only once to the client. In this case, it will be convenient if I can send a “DownloadChatMessage” command to the simulator and get the data in a callback. Is there a way that coherence team recommends?
Two ways come to my mind:

  1. Create a unique ID and send it to the simulator as command parameter, then send the response from the simulator to the client in another command along with the ID:
        public event Action<object, string> OnResponse;
        public bool Request<T>(string methodName, Action<object> callback = null, params object[] args) {
            var id = Guid.NewGuid().ToString(); // UUID
            Action<object, string> onResponseAction = null;
            onResponseAction = delegate(object response, string responseId) {
                    if (responseId == id) {
                        OnResponse -= onResponseAction;
                        if (callback != null) callback(response);
                    }
            }; 
            OnResponse += onResponseAction;
            var t = typeof(T).ToString();
            var requestArgs = new object[] {id, t, methodName};
            requestArgs = requestArgs.Concat(args).ToArray();
            if (!sync.HasStateAuthority) {
                bool wasSent = sync.SendCommand(this.GetType(), "OnRequest", Coherence.MessageTarget.AuthorityOnly, requestArgs);
                return wasSent;
            } else {
                OnRequest(requestArgs[0], requestArgs[1], requestArgs[2], requestArgs.Skip(3).ToArray());
                return true;
            }
        }

        public void OnRequest(object idObj, object tObj, object methodNameObj, params object[] args) {
            var id = (string)idObj;
            var t = (string)tObj;
            var methodName = (string)methodNameObj;
            var component = (MonoBehaviour)GetComponent(t);
            var methodInfo = component.GetType().GetMethod(methodName);
            // I think I need to add a filter here to make sure that the user can only call "allowed" methods?
            var response = methodInfo.Invoke(component, args);
            // Then somehow send the response in a new command to the requester.
        }

There are 2 issues in this approach: I believe that there are some size limitations for the arguments. Also, how do I send the response to the requester only and not to all other clients?

  1. Upload the response along with an ID to a Redis database then download it on the requester device.

Any thoughts?

Great explanation of your needs. A request/response is a pattern very well establish - I will forward this to the SDK team and see if they have any recommendations using our current capabilities.

I will say, in addition to large data sync and list sync as I mentioned in the other thread, another item on our priority list is the concept of a KV (Key Value) store that has granular permissions available to a project. So, e.g. in your scenario a client can simply request and get a response for static data like chat history.

We currently have a per-player Key Value store, but that is not useful in the context that you mention. We have seen many requests and recognize the need for a project-level Key Value store, and again although I can’t give any sense of timeline I can say that it’s high on our radar to make coherence even more useful.

Hey, you’re right, there’s no nice way to send back a message to the requester only. An option to do so is on the roadmap, but in the mean time here are some suggestions:

  1. send the response to MessageType.Other and have the other clients ignore it if they didn’t send the message initially (a waste of bandwidth)
  2. you could set up a slightly complex relay using ClientConnections where the request includes the sender’s ClientID and then the response is sent through the ClientConnection CoherenceSync object including the UUID and the CoherenceSync Reference of the entity that originally sent the request so the response can be passed to the correct CoherenceSync object on the receiving client. (much better bandwidth use but more complex)

With commands, you’re limited to a total of 511 bytes for all arguments but 509 if you send them in one big byte array. Byte arrays and string arguments have a little overhead that the other primitive types do not. And, in general, you want to send as little data in commands as possible anyway.