godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.11k stars 69 forks source link

Add "MultiplayerPeerCustom" to allow creating High-Level Multiplayer API backends in GDScript #4873

Closed dsnopek closed 2 years ago

dsnopek commented 2 years ago

Describe the project you are working on

Implementing a High-Level Multiplayer API backend that uses Nakama (on behalf of Heroic Labs)

Describe the problem or limitation you are having in your project

In order to use the High-Level Multiplayer API over Nakama, the game developer first has to somehow authenticate with Nakama, and create a Nakama socket, so that a match can be setup (via one of the ~4 methods to create a match that Nakama allows) to relay the RPC's sent by Godot. They probably also want to have the ability to send other messages over this match or use other Nakama features on the same connection. This means the developer needs to be able to use a full Nakama client.

The Nakama client library for Godot is written in GDScript. But currently, it's only possible to add a new High-Level Multiplayer API backend in C++ (via GDNative/GDExtension or a module).

Of course, it would be possible to implement a full Nakama client library for Godot in C++, but it wouldn't be able to have the same API as the GDScript version (it currently does lots of clever things with yield and GDScriptFunctionState and fake namespaces). Also, the Heroic Labs folks want something to just "drop in" to the existing client and get this functionality.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

If there was a way to implement a custom MultiplayerPeer from GDScript, then this could easily be added to the existing Nakama client library for Godot written in GDScript.

Using GDNative (in Godot 3.x) and GDExtension (in Godot 4.x), I made a NakamaMultiplayerPeer class for this purpose and it works pretty good! You can find the code here:

However, looking at the code, there's nothing Nakama-specific about it! It really only facilitates doing all the important stuff from GDScript. And it's so small and simple, that I hoped that it could maybe get added to the engine, so that GDNative/GDExtension isn't necessary.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

I propose adding a MultiplayerPeerCustom class, that exposes the following additional methods to GDScript:

And the following signal:

So, once the connection is setup, the GDScript code will to the rpc_generated signal and pass that data into its connection, and any data that it receives on that connection will get passed to deliver_rpc().

The GDScript code also needs to emit other signals on the MultiplayerPeerCustom object (from the parent MultiplayerPeer class), like peer_connected and peer_disconnected as it finds out from it's connect that new peers have connected or disconnected.

I'll make a draft PR with this proposed implementation soon.

If this enhancement will not be used often, can it be worked around with a few lines of script?

It's not possible to implement a new backend for the High-Level Multiplayer API purely from GDScript, it requires GDNative/GDExtension or a custom module.

Is there a reason why this should be core and not an add-on in the asset library?

Like I said above, it is 100% possible to make a GDNative/GDExtension that adds this class (I've already done it!).

However, this additional class would be very small and very simple, not adding very many bytes to the engine. And it's a shame that GDNative/GDExtension is necessary just to facilitate doing something from GDScript.

Faless commented 2 years ago

I like the scope of the proposal, but I don't think this is tackling the problem in the correct place (also see https://github.com/godotengine/godot/pull/62870#issuecomment-1179732935).

I admit greatly over designing the original concept, and I've been working on a rewrite and modularization branch as suggested in the PR above, but the gist is that most of those functions should be extensible via the MultiplayerAPI, not in MultiplayerPeer.

I'll come up with a more formal implementation proposal in the next few hours.

Faless commented 2 years ago

So, these are the functions you would be able to implement in GDScript, and you would be in charge of emitting peer_connected/peer_disconnected/etc yourself.

# Connection
void _set_multiplayer_peer(MultiplayerPeer) virtual
MultiplayerPeer _get_multiplayer_peer() virtual const
PackedInt32Array _get_peer_ids() virtual const
int _get_unique_id() virtual const
int _poll() virtual
# RPC
int _get_remote_sender_id() virtual const
Variant _rpc(peer: int, object: Object, method: StringName, args: Array) virtual
# TBD Generic object configurations (e.g. replication API).
int _object_configuration_add(object: Object, config: Variant) virtual
int _object_configuration_remove(object: Object, config: Variant) virtual

I'm not sure if pre-encoding the arguments in a PackedByteArray is always desired, maybe we could add an extra utility method for doing that.

Ideally we could also change the MultiplayerPeer interface to return Variant in get_unique_id and in its signals and do you could do your own mapping if needed.

dsnopek commented 2 years ago

Thanks, @Faless!

For Godot 4, that sounds like a much nicer way to make a new backend for the High-Level Multiplayer API. I'll take a deeper look into your PR, and see if I can implement my Nakama backend with it!

But for Godot 3, would it be possible to add something like the MultiplayerPeerCustom that I describe in my proposal?

For reference, the GDNative/GDExtension code that I wrote for this is now public:

Faless commented 2 years ago

@dsnopek It looks like that the repository is still private, but in general having a MultiplayerPeerCustom with the functionalities you describe in the proposal could be really good for 3.x :+1: , so since it seem you've done most of the work already feel free to open a pull request on the 3.x branch and I'll review it ASAP, so we can better discuss potential changes.

dsnopek commented 2 years ago

@Faless:

It looks like that the repository is still private

Ack! Sorry :-) Hopefully, it'll really be public sometime soon

feel free to open a pull request on the 3.x branch and I'll review it ASAP, so we can better discuss potential changes

Here you go: https://github.com/godotengine/godot/pull/63163

Thanks!

Faless commented 2 years ago

Closed via godotengine/godot#63163 (3.x) and godotengine/godot#63262 (4.x)