From e304b4e43e5d2f5027ab0c475b3f2530e81db207 Mon Sep 17 00:00:00 2001 From: Max Aller Date: Sun, 21 Dec 2025 12:00:36 -0800 Subject: [PATCH] GDScript: Fix incorrect default transfer_mode for `@rpc` annotation The `@rpc` annotation was registered with "unreliable" as the default transfer_mode, but the runtime (SceneRPCInterface) defaults to TRANSFER_MODE_RELIABLE. This caused a mismatch between the documented default and the actual behavior when using Node.rpc_config(). Changed the parser default to "reliable" to match the runtime behavior and C# RpcAttribute. Added comments in all three locations to help prevent future drift. Fixes godotengine/godot-docs#8874 Related docs fix: godotengine/godot-docs#11554 --- modules/gdscript/doc_classes/@GDScript.xml | 4 ++-- modules/gdscript/gdscript_parser.cpp | 4 +++- modules/multiplayer/scene_rpc_interface.cpp | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 00e743544b8..28b0ca554a2 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -788,7 +788,7 @@ - + Mark the following method for remote procedure calls. See [url=$DOCS_URL/tutorials/networking/high_level_multiplayer.html]High-level multiplayer[/url]. @@ -804,7 +804,7 @@ @rpc("any_peer", "unreliable_ordered") func fn_update_pos(): pass - @rpc("authority", "call_remote", "unreliable", 0) # Equivalent to @rpc + @rpc("authority", "call_remote", "reliable", 0) # Equivalent to @rpc func fn_default(): pass [/codeblock] [b]Note:[/b] Methods annotated with [annotation @rpc] cannot receive objects which define required parameters in [method Object._init]. See [method Object._init] for more details. diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 6e955969ba3..d34079447de 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -186,7 +186,8 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@warning_ignore_start", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::STANDALONE, &GDScriptParser::warning_ignore_region_annotations, varray(), true); register_annotation(MethodInfo("@warning_ignore_restore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::STANDALONE, &GDScriptParser::warning_ignore_region_annotations, varray(), true); // Networking. - register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0)); + // Keep in sync with `rpc_annotation()` and `SceneRPCInterface::_parse_rpc_config()`. + register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "reliable", 0)); } #ifdef DEBUG_ENABLED @@ -5199,6 +5200,7 @@ bool GDScriptParser::rpc_annotation(AnnotationNode *p_annotation, Node *p_target return false; } + // Default values should match the annotation registration defaults and `SceneRPCInterface::_parse_rpc_config()`. Dictionary rpc_config; rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY; if (!p_annotation->resolved_arguments.is_empty()) { diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp index da9df1e8c76..68e177918a5 100644 --- a/modules/multiplayer/scene_rpc_interface.cpp +++ b/modules/multiplayer/scene_rpc_interface.cpp @@ -84,6 +84,7 @@ void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_no ERR_CONTINUE(config[name].get_type() != Variant::DICTIONARY); ERR_CONTINUE(!config[name].operator Dictionary().has("rpc_mode")); Dictionary dict = config[name]; + // Default values should match GDScript `@rpc` annotation registration and `rpc_annotation()`. RPCConfig cfg; cfg.name = name; cfg.rpc_mode = ((MultiplayerAPI::RPCMode)dict.get("rpc_mode", MultiplayerAPI::RPC_MODE_AUTHORITY).operator int());