-
-
Notifications
You must be signed in to change notification settings - Fork 21.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Net] Modularize multiplayer, expose MultiplayerAPI to extensions. #63049
Conversation
One other thing to note is that we could also move |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only looked a bit into the C# side.
Thanks a lot for the review @raulsntos , I fixed the typo, and did the renaming. |
That's because the C# attribute uses the RPC enums that used to be core constants and now are enums in MultiplayerAPI and MultiplayerPeer. This should fix it: diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
index 0a1c8322d7..fb37838ffa 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
@@ -5,8 +5,8 @@ namespace Godot
/// <summary>
/// Attribute that changes the RPC mode for the annotated <c>method</c> to the given <see cref="Mode"/>,
/// optionally specifying the <see cref="TransferMode"/> and <see cref="TransferChannel"/> (on supported peers).
- /// See <see cref="RPCMode"/> and <see cref="TransferMode"/>. By default, methods are not exposed to networking
- /// (and RPCs).
+ /// See <see cref="MultiplayerAPI.RPCMode"/> and <see cref="MultiplayerPeer.TransferModeEnum"/>.
+ /// By default, methods are not exposed to networking (and RPCs).
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class RPCAttribute : Attribute
@@ -14,7 +14,7 @@ namespace Godot
/// <summary>
/// RPC mode for the annotated method.
/// </summary>
- public RPCMode Mode { get; } = RPCMode.Disabled;
+ public MultiplayerAPI.RPCMode Mode { get; } = MultiplayerAPI.RPCMode.Disabled;
/// <summary>
/// If the method will also be called locally; otherwise, it is only called remotely.
@@ -24,7 +24,7 @@ namespace Godot
/// <summary>
/// Transfer mode for the annotated method.
/// </summary>
- public TransferMode TransferMode { get; set; } = TransferMode.Reliable;
+ public MultiplayerPeer.TransferModeEnum TransferMode { get; set; } = MultiplayerPeer.TransferModeEnum.Reliable;
/// <summary>
/// Transfer channel for the annotated mode.
@@ -35,7 +35,7 @@ namespace Godot
/// Constructs a <see cref="RPCAttribute"/> instance.
/// </summary>
/// <param name="mode">The RPC mode to use.</param>
- public RPCAttribute(RPCMode mode = RPCMode.Authority)
+ public RPCAttribute(MultiplayerAPI.RPCMode mode = MultiplayerAPI.RPCMode.Authority)
{
Mode = mode;
} |
Thanks a lot ❤️ . This should do it then, I had some issues with |
@Faless This is almost enough to implement what I was looking for in godotengine/godot-proposals#4873 My goal (as described in more detail there) is for RPC's (and any other messages that Extending Given this refactor, it still seems like implementing a custom But maybe further refactoring to Unless I'm totally missing/misunderstanding something? |
@dsnopek I think you are right and after seeing you implementation I think I misunderstood the scope of your original proposal. I think we should also have something like you describe in I'm a bit busy this week, but I'll try to look into it, it should be really just those 2 functions as far as I can tell since the other methods should be already extendable in GDScript. |
Yeah, I think it is just By extending, do you mean making a new class (for example, Given a direction, I could try and make a PR for it! |
@dsnopek well, I had both in mind, not sure which one is better. If we just modify if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet)) {
// current fast route.
} else if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) {
// Make PackedByteArray and GDVIRTUAL_CALL _put_packet_script
} Which is nice, because we don't need a new class, but we add a bunch of checks to every packet sent (probably fine). Actually, I think we could likely also simplify it to just: if (GDVIRTUAL_CALL(_put_packet)) { // should return false when not overridden
// current fast route.
} else if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) { // check is_overridden only in the gdscript route.
// Make PackedByteArray and GDVIRTUAL_CALL _put_packet_script
} I think if this works and do not spam errors about the first call we should go this route.
Sure, go ahead and ping me for review, thanks :) |
78b226b
to
acfd780
Compare
Okay, I think this is generally ready for review. I've left out a few things which probably should go in their own PRs:
I've added documentation for P.S.: Some stats: $ git diff HEAD~1 --stat -- core/ scene/ editor/
39 files changed, 638 insertions(+), 4893 deletions(-)
$ git diff HEAD~1 --stat -- modules/
51 files changed, 4862 insertions(+), 137 deletions(-) |
- RPC configurations are now dictionaries. - Script.get_rpc_methods renamed to Script.get_rpc_config. - Node.rpc[_id] and Callable.rpc now return an Error. - Refactor MultiplayerAPI to allow extension. - New MultiplayerAPI.rpc method with Array argument (for scripts). - Move the default MultiplayerAPI implementation to a module.
Thanks! |
Are we sure this PR has enough topic labels? 😆 |
template <typename... VarArgs> | ||
void rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) { | ||
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. | ||
const Variant *argptrs[sizeof...(p_args) + 1]; | ||
for (uint32_t i = 0; i < sizeof...(p_args); i++) { | ||
argptrs[i] = &args[i]; | ||
} | ||
rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); | ||
} | ||
Error rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm getting a linker error when using this method in a module, but it works if I move the code back into the header. I've tested that it's broken on macOS and Linux, both GCC and Clang. Any ideas? GameNetworking/NetworkSynchronizer#16 (comment)
If I move these back into the headers, it compiles fine: https://github.com/aaronfranke/godot/tree/node-rpc-header
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened #63820
Would this new addition allow for an interchangable transport implementation (usage of HLAPI but with custom low-level LAN/Thirdparty P2P transport as an example)? Because if it would, then this is a huge breakthrough, since there are only two other engines that provides this flexibility out of the box by having HLAPI seperate from interchangable LLAPI (LAN/Third-party P2P) transport implementation: Source and Unreal. In every other engine that is available right now you have to build your own complete (LLAPI + HLAPI) networking codebase from scratch (Which I had to do for Godot, Unity and a bunch of other engines because of this) around this missing low level piece that not a lot of people seem to realise is missing until you are trying to make a multiplayer like Left 4 Dead 2, where LAN and P2P are both valid working options for a play session. |
@ClockRate It should always have been possible to implement a custom This change in this PR opens up the possibility for taking over more of what the High-Level Multiplayer API does, at a higher level. If we're eventually able to use this to assign something other than integers for peer ids, that would definitely make that process easier! However, if you're looking for a way to implement a custom |
@dsnopek Thanks for the heads up, I tried using NetworkedMultiplayerPeer with GDNative in the past, but I could not figure out how to override some of the methods that were essential for implementation of the LLAPI (writing/reading packets), maybe stuff has changed in the 3.X branch since then, I'll give it a shot. |
TL; DR;
Most of the Multiplayer code has been extracted from
core
/scene
tomodules/multiplayer
which can be compiled out (see below for details).A new
MultiplayerAPIExtension
class allows extending theMultiplayerAPI
by overriding specific methods to manage peers, handling RPCs, and even replacing the replication behavior (via the extra configuration methods_object_configuration_add
/_object_configuration_remove
).Script.get_rpc_config
(get_rpc_methods
) now returns aVariant
so we can keep compatibility in the future (the module expects aDictionary
for now).In code
And the main:
Review considerations
Script.get_rpc_config
(get_rpc_methods
) now returns aVariant
and should probably be exposed to scripting, the current implementation expects a Dictionary, and I should have implemented them properly in both gdscript/mono (CC @vnen @neikeq ).MultiplayerAPI
should probably be moved toscene/main/
(andEDIT: same forMultiplayerPeer
moved back tocore/io
)MultiplayerPeer
(cc @reduz ).Deserves its own PRMultiplayerPeer
should probably useVariant
instead ofint
in methods and signals (so it supports hashes etc) and the mapping should just be implemented by theMultiplayerAPI
.Expose. Also deserves its own PRMultiplayer.[de|en]code_variant[s]
(cc @dsnopek )Expose a faster RPC callback in extensions/languages that support variable arguments (callable?)Needs more discussion.MultiplayerAPI
and the newMultiplayerAPIExtension
.Supersedes (closes #62870)
Partly address #38294