BeardedManStudios / ForgeNetworkingRemastered

See various forks, also please join the Forge Community fork of Forge Alloy! -> https://github.com/ForgeAlloyCommunity/ForgeAlloy
https://twitter.com/FarrisFaulds
Apache License 2.0
1.49k stars 311 forks source link

Implement TAP-based request-response flow on top of RPCs #399

Open NoTuxNoBux opened 3 years ago

NoTuxNoBux commented 3 years ago

Summary

This introduces requests with responses, which are just RPCs that can send back a value. Requests are also just layered on top of RPCs and don't introduce any new special treatment, they are just some convenience sugar on top of RPCs where you want to send back a response.

Sending a request will return a Task already known from TAP [1], so you can easily await the response to your request from the receiver. It works similar to HTTP requests.

Context

I needed a way to send back information from one party to another after sending an RPC, and the only way to do that seemed to be two RPCs: one to send the request, and an RPC on the sender that the receiver can in turn invoke to send the response back. This approach resulted in rather entangled code where I would have two handler classes, one for each RPC, that needed to do bookkeeping about what was sent and what response went with what request.

This moves that bookkeeping into Forge, so senders can just await the response and don't have to worry about anything else.

Examples

networkObject.RegisterRequest<SayHelloRequest,SayHelloResponse>("SayHello", async (context) => {
    await DoSomethingElseAsync();

    // You can do more async stuff here, and even return a Task yourself. The response won't be sent back until
    // the returned task resolves and will use the value produced by the returned task.

    return new SayHelloResponse("You said: '" + context.Data.Message + "', hello back to you!");
});
var response = await networkObject.SendRequest<SayHelloResponse>(someOtherClient, "SayHello", new SayHelloRequest("Good day!"));

var theOtherPartyReplied = response.Message;

// theOtherPartyReplied = "You said: 'Good Day!', hello back to you!"

Limitations

NOTE: This doesn't necessarily need to be merged, but I wanted to upstream it anyway, to contribute back to the Forge community. Similar support for TAP may also be interesting to port over to Alloy as it's just one message going out and another coming back, so this could act as a reference implementation.

[1] https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap