1 | /**************************************************************************/ |
2 | /* scene_rpc_interface.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "scene_rpc_interface.h" |
32 | |
33 | #include "scene_multiplayer.h" |
34 | |
35 | #include "core/debugger/engine_debugger.h" |
36 | #include "core/io/marshalls.h" |
37 | #include "scene/main/multiplayer_api.h" |
38 | #include "scene/main/node.h" |
39 | #include "scene/main/window.h" |
40 | |
41 | // The RPC meta is composed by a single byte that contains (starting from the least significant bit): |
42 | // - `NetworkCommands` in the first four bits. |
43 | // - `NetworkNodeIdCompression` in the next 2 bits. |
44 | // - `NetworkNameIdCompression` in the next 1 bit. |
45 | // - `byte_only_or_no_args` in the next 1 bit. |
46 | #define NODE_ID_COMPRESSION_SHIFT SceneMultiplayer::CMD_FLAG_0_SHIFT |
47 | #define NAME_ID_COMPRESSION_SHIFT SceneMultiplayer::CMD_FLAG_2_SHIFT |
48 | #define BYTE_ONLY_OR_NO_ARGS_SHIFT SceneMultiplayer::CMD_FLAG_3_SHIFT |
49 | |
50 | #define NODE_ID_COMPRESSION_FLAG ((1 << NODE_ID_COMPRESSION_SHIFT) | (1 << (NODE_ID_COMPRESSION_SHIFT + 1))) |
51 | #define NAME_ID_COMPRESSION_FLAG (1 << NAME_ID_COMPRESSION_SHIFT) |
52 | #define BYTE_ONLY_OR_NO_ARGS_FLAG (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT) |
53 | |
54 | #ifdef DEBUG_ENABLED |
55 | _FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id, int p_size) { |
56 | if (EngineDebugger::is_profiling("multiplayer:rpc" )) { |
57 | Array values; |
58 | values.push_back(p_what); |
59 | values.push_back(p_id); |
60 | values.push_back(p_size); |
61 | EngineDebugger::profiler_add_frame_data("multiplayer:rpc" , values); |
62 | } |
63 | } |
64 | #endif |
65 | |
66 | // Returns the packet size stripping the node path added when the node is not yet cached. |
67 | int get_packet_len(uint32_t p_node_target, int p_packet_len) { |
68 | if (p_node_target & 0x80000000) { |
69 | int ofs = p_node_target & 0x7FFFFFFF; |
70 | return p_packet_len - (p_packet_len - ofs); |
71 | } else { |
72 | return p_packet_len; |
73 | } |
74 | } |
75 | |
76 | void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_node, RPCConfigCache &r_cache) { |
77 | if (p_config.get_type() == Variant::NIL) { |
78 | return; |
79 | } |
80 | ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY); |
81 | const Dictionary config = p_config; |
82 | Array names = config.keys(); |
83 | names.sort(); // Ensure ID order |
84 | for (int i = 0; i < names.size(); i++) { |
85 | ERR_CONTINUE(names[i].get_type() != Variant::STRING && names[i].get_type() != Variant::STRING_NAME); |
86 | String name = names[i].operator String(); |
87 | ERR_CONTINUE(config[name].get_type() != Variant::DICTIONARY); |
88 | ERR_CONTINUE(!config[name].operator Dictionary().has("rpc_mode" )); |
89 | Dictionary dict = config[name]; |
90 | RPCConfig cfg; |
91 | cfg.name = name; |
92 | cfg.rpc_mode = ((MultiplayerAPI::RPCMode)dict.get("rpc_mode" , MultiplayerAPI::RPC_MODE_AUTHORITY).operator int()); |
93 | cfg.transfer_mode = ((MultiplayerPeer::TransferMode)dict.get("transfer_mode" , MultiplayerPeer::TRANSFER_MODE_RELIABLE).operator int()); |
94 | cfg.call_local = dict.get("call_local" , false).operator bool(); |
95 | cfg.channel = dict.get("channel" , 0).operator int(); |
96 | uint16_t id = ((uint16_t)i); |
97 | if (p_for_node) { |
98 | id |= (1 << 15); |
99 | } |
100 | r_cache.configs[id] = cfg; |
101 | r_cache.ids[name] = id; |
102 | } |
103 | } |
104 | |
105 | const SceneRPCInterface::RPCConfigCache &SceneRPCInterface::_get_node_config(const Node *p_node) { |
106 | const ObjectID oid = p_node->get_instance_id(); |
107 | if (rpc_cache.has(oid)) { |
108 | return rpc_cache[oid]; |
109 | } |
110 | RPCConfigCache cache; |
111 | _parse_rpc_config(p_node->get_node_rpc_config(), true, cache); |
112 | if (p_node->get_script_instance()) { |
113 | _parse_rpc_config(p_node->get_script_instance()->get_rpc_config(), false, cache); |
114 | } |
115 | rpc_cache[oid] = cache; |
116 | return rpc_cache[oid]; |
117 | } |
118 | |
119 | _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { |
120 | switch (mode) { |
121 | case MultiplayerAPI::RPC_MODE_DISABLED: { |
122 | return false; |
123 | } break; |
124 | case MultiplayerAPI::RPC_MODE_ANY_PEER: { |
125 | return true; |
126 | } break; |
127 | case MultiplayerAPI::RPC_MODE_AUTHORITY: { |
128 | return !p_node->is_multiplayer_authority() && p_remote_id == p_node->get_multiplayer_authority(); |
129 | } break; |
130 | } |
131 | |
132 | return false; |
133 | } |
134 | |
135 | String SceneRPCInterface::get_rpc_md5(const Object *p_obj) { |
136 | const Node *node = Object::cast_to<Node>(p_obj); |
137 | ERR_FAIL_COND_V(!node, "" ); |
138 | const RPCConfigCache cache = _get_node_config(node); |
139 | String rpc_list; |
140 | for (const KeyValue<uint16_t, RPCConfig> &config : cache.configs) { |
141 | rpc_list += String(config.value.name); |
142 | } |
143 | return rpc_list.md5_text(); |
144 | } |
145 | |
146 | Node *SceneRPCInterface::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { |
147 | Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); |
148 | ERR_FAIL_COND_V(!root_node, nullptr); |
149 | Node *node = nullptr; |
150 | |
151 | if (p_node_target & 0x80000000) { |
152 | // Use full path (not cached yet). |
153 | int ofs = p_node_target & 0x7FFFFFFF; |
154 | |
155 | ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared." ); |
156 | |
157 | String paths; |
158 | paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); |
159 | |
160 | NodePath np = paths; |
161 | |
162 | node = root_node->get_node(np); |
163 | |
164 | if (!node) { |
165 | ERR_PRINT("Failed to get path from RPC: " + String(np) + "." ); |
166 | } |
167 | return node; |
168 | } else { |
169 | // Use cached path. |
170 | return Object::cast_to<Node>(multiplayer->get_path_cache()->get_cached_object(p_from, p_node_target)); |
171 | } |
172 | } |
173 | |
174 | void SceneRPCInterface::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) { |
175 | // Extract packet meta |
176 | int packet_min_size = 1; |
177 | int name_id_offset = 1; |
178 | ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small." ); |
179 | // Compute the meta size, which depends on the compression level. |
180 | int node_id_compression = (p_packet[0] & NODE_ID_COMPRESSION_FLAG) >> NODE_ID_COMPRESSION_SHIFT; |
181 | int name_id_compression = (p_packet[0] & NAME_ID_COMPRESSION_FLAG) >> NAME_ID_COMPRESSION_SHIFT; |
182 | |
183 | switch (node_id_compression) { |
184 | case NETWORK_NODE_ID_COMPRESSION_8: |
185 | packet_min_size += 1; |
186 | name_id_offset += 1; |
187 | break; |
188 | case NETWORK_NODE_ID_COMPRESSION_16: |
189 | packet_min_size += 2; |
190 | name_id_offset += 2; |
191 | break; |
192 | case NETWORK_NODE_ID_COMPRESSION_32: |
193 | packet_min_size += 4; |
194 | name_id_offset += 4; |
195 | break; |
196 | default: |
197 | ERR_FAIL_MSG("Was not possible to extract the node id compression mode." ); |
198 | } |
199 | switch (name_id_compression) { |
200 | case NETWORK_NAME_ID_COMPRESSION_8: |
201 | packet_min_size += 1; |
202 | break; |
203 | case NETWORK_NAME_ID_COMPRESSION_16: |
204 | packet_min_size += 2; |
205 | break; |
206 | default: |
207 | ERR_FAIL_MSG("Was not possible to extract the name id compression mode." ); |
208 | } |
209 | ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small." ); |
210 | |
211 | uint32_t node_target = 0; |
212 | switch (node_id_compression) { |
213 | case NETWORK_NODE_ID_COMPRESSION_8: |
214 | node_target = p_packet[1]; |
215 | break; |
216 | case NETWORK_NODE_ID_COMPRESSION_16: |
217 | node_target = decode_uint16(p_packet + 1); |
218 | break; |
219 | case NETWORK_NODE_ID_COMPRESSION_32: |
220 | node_target = decode_uint32(p_packet + 1); |
221 | break; |
222 | default: |
223 | // Unreachable, checked before. |
224 | CRASH_NOW(); |
225 | } |
226 | |
227 | Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len); |
228 | ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found." ); |
229 | |
230 | uint16_t name_id = 0; |
231 | switch (name_id_compression) { |
232 | case NETWORK_NAME_ID_COMPRESSION_8: |
233 | name_id = p_packet[name_id_offset]; |
234 | break; |
235 | case NETWORK_NAME_ID_COMPRESSION_16: |
236 | name_id = decode_uint16(p_packet + name_id_offset); |
237 | break; |
238 | default: |
239 | // Unreachable, checked before. |
240 | CRASH_NOW(); |
241 | } |
242 | |
243 | const int packet_len = get_packet_len(node_target, p_packet_len); |
244 | _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); |
245 | } |
246 | |
247 | void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { |
248 | ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small." ); |
249 | |
250 | // Check that remote can call the RPC on this node. |
251 | const RPCConfigCache &cache_config = _get_node_config(p_node); |
252 | ERR_FAIL_COND(!cache_config.configs.has(p_rpc_method_id)); |
253 | const RPCConfig &config = cache_config.configs[p_rpc_method_id]; |
254 | |
255 | bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from); |
256 | ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + "." ); |
257 | |
258 | int argc = 0; |
259 | |
260 | const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG; |
261 | if (byte_only_or_no_args) { |
262 | if (p_offset < p_packet_len) { |
263 | // This packet contains only bytes. |
264 | argc = 1; |
265 | } |
266 | } else { |
267 | // Normal variant, takes the argument count from the packet. |
268 | ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small." ); |
269 | argc = p_packet[p_offset]; |
270 | p_offset += 1; |
271 | } |
272 | |
273 | Vector<Variant> args; |
274 | Vector<const Variant *> argp; |
275 | args.resize(argc); |
276 | argp.resize(argc); |
277 | |
278 | #ifdef DEBUG_ENABLED |
279 | _profile_node_data("rpc_in" , p_node->get_instance_id(), p_packet_len); |
280 | #endif |
281 | |
282 | int out; |
283 | MultiplayerAPI::decode_and_decompress_variants(args, &p_packet[p_offset], p_packet_len - p_offset, out, byte_only_or_no_args, multiplayer->is_object_decoding_allowed()); |
284 | for (int i = 0; i < argc; i++) { |
285 | argp.write[i] = &args[i]; |
286 | } |
287 | |
288 | Callable::CallError ce; |
289 | |
290 | p_node->callp(config.name, (const Variant **)argp.ptr(), argc, ce); |
291 | if (ce.error != Callable::CallError::CALL_OK) { |
292 | String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce); |
293 | error = "RPC - " + error; |
294 | ERR_PRINT(error); |
295 | } |
296 | } |
297 | |
298 | void SceneRPCInterface::_send_rpc(Node *p_node, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { |
299 | Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); |
300 | ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer." ); |
301 | |
302 | ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to call RPC while multiplayer peer is not connected yet." ); |
303 | |
304 | ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to call RPC while multiplayer peer is disconnected." ); |
305 | |
306 | ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255)." ); |
307 | |
308 | if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) { |
309 | ERR_FAIL_COND_MSG(p_to == multiplayer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(multiplayer->get_unique_id()) + "." ); |
310 | |
311 | ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + "." ); |
312 | } |
313 | |
314 | // See if all peers have cached path (if so, call can be fast) while building the RPC target list. |
315 | HashSet<int> targets; |
316 | Ref<SceneCacheInterface> cache = multiplayer->get_path_cache(); |
317 | int psc_id = -1; |
318 | bool has_all_peers = true; |
319 | const ObjectID oid = p_node->get_instance_id(); |
320 | if (p_to > 0) { |
321 | ERR_FAIL_COND_MSG(!multiplayer->get_replicator()->is_rpc_visible(oid, p_to), "Attempt to call an RPC to a peer that cannot see this node. Peer ID: " + itos(p_to)); |
322 | targets.insert(p_to); |
323 | has_all_peers = cache->send_object_cache(p_node, p_to, psc_id); |
324 | } else { |
325 | bool restricted = !multiplayer->get_replicator()->is_rpc_visible(oid, 0); |
326 | for (const int &P : multiplayer->get_connected_peers()) { |
327 | if (p_to < 0 && P == -p_to) { |
328 | continue; // Excluded peer. |
329 | } |
330 | if (restricted && !multiplayer->get_replicator()->is_rpc_visible(oid, P)) { |
331 | continue; // Not visible to this peer. |
332 | } |
333 | targets.insert(P); |
334 | bool has_peer = cache->send_object_cache(p_node, P, psc_id); |
335 | has_all_peers = has_all_peers && has_peer; |
336 | } |
337 | } |
338 | if (targets.is_empty()) { |
339 | return; // No one in sight. |
340 | } |
341 | |
342 | // Create base packet, lots of hardcode because it must be tight. |
343 | int ofs = 0; |
344 | |
345 | #define MAKE_ROOM(m_amount) \ |
346 | if (packet_cache.size() < m_amount) \ |
347 | packet_cache.resize(m_amount); |
348 | |
349 | // Encode meta. |
350 | uint8_t command_type = SceneMultiplayer::NETWORK_COMMAND_REMOTE_CALL; |
351 | uint8_t node_id_compression = UINT8_MAX; |
352 | uint8_t name_id_compression = UINT8_MAX; |
353 | bool byte_only_or_no_args = false; |
354 | |
355 | MAKE_ROOM(1); |
356 | // The meta is composed along the way, so just set 0 for now. |
357 | packet_cache.write[0] = 0; |
358 | ofs += 1; |
359 | |
360 | // Encode Node ID. |
361 | if (has_all_peers) { |
362 | // Compress the node ID only if all the target peers already know it. |
363 | if (psc_id >= 0 && psc_id <= 255) { |
364 | // We can encode the id in 1 byte |
365 | node_id_compression = NETWORK_NODE_ID_COMPRESSION_8; |
366 | MAKE_ROOM(ofs + 1); |
367 | packet_cache.write[ofs] = static_cast<uint8_t>(psc_id); |
368 | ofs += 1; |
369 | } else if (psc_id >= 0 && psc_id <= 65535) { |
370 | // We can encode the id in 2 bytes |
371 | node_id_compression = NETWORK_NODE_ID_COMPRESSION_16; |
372 | MAKE_ROOM(ofs + 2); |
373 | encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs])); |
374 | ofs += 2; |
375 | } else { |
376 | // Too big, let's use 4 bytes. |
377 | node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; |
378 | MAKE_ROOM(ofs + 4); |
379 | encode_uint32(psc_id, &(packet_cache.write[ofs])); |
380 | ofs += 4; |
381 | } |
382 | } else { |
383 | // The targets don't know the node yet, so we need to use 32 bits int. |
384 | node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; |
385 | MAKE_ROOM(ofs + 4); |
386 | encode_uint32(psc_id, &(packet_cache.write[ofs])); |
387 | ofs += 4; |
388 | } |
389 | |
390 | // Encode method ID |
391 | if (p_rpc_id <= UINT8_MAX) { |
392 | // The ID fits in 1 byte |
393 | name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; |
394 | MAKE_ROOM(ofs + 1); |
395 | packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id); |
396 | ofs += 1; |
397 | } else { |
398 | // The ID is larger, let's use 2 bytes |
399 | name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; |
400 | MAKE_ROOM(ofs + 2); |
401 | encode_uint16(p_rpc_id, &(packet_cache.write[ofs])); |
402 | ofs += 2; |
403 | } |
404 | |
405 | int len; |
406 | Error err = MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, nullptr, len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed()); |
407 | ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC arguments. THIS IS LIKELY A BUG IN THE ENGINE!" ); |
408 | if (byte_only_or_no_args) { |
409 | MAKE_ROOM(ofs + len); |
410 | } else { |
411 | MAKE_ROOM(ofs + 1 + len); |
412 | packet_cache.write[ofs] = p_argcount; |
413 | ofs += 1; |
414 | } |
415 | if (len) { |
416 | MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, &packet_cache.write[ofs], len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed()); |
417 | ofs += len; |
418 | } |
419 | |
420 | ERR_FAIL_COND(command_type > 7); |
421 | ERR_FAIL_COND(node_id_compression > 3); |
422 | ERR_FAIL_COND(name_id_compression > 1); |
423 | |
424 | #ifdef DEBUG_ENABLED |
425 | _profile_node_data("rpc_out" , p_node->get_instance_id(), ofs); |
426 | #endif |
427 | |
428 | // We can now set the meta |
429 | packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + (byte_only_or_no_args ? BYTE_ONLY_OR_NO_ARGS_FLAG : 0); |
430 | |
431 | // Take chance and set transfer mode, since all send methods will use it. |
432 | peer->set_transfer_channel(p_config.channel); |
433 | peer->set_transfer_mode(p_config.transfer_mode); |
434 | |
435 | if (has_all_peers) { |
436 | for (const int P : targets) { |
437 | multiplayer->send_command(P, packet_cache.ptr(), ofs); |
438 | } |
439 | } else { |
440 | // Unreachable because the node ID is never compressed if the peers doesn't know it. |
441 | CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); |
442 | |
443 | // Not all verified path, so send one by one. |
444 | |
445 | // Append path at the end, since we will need it for some packets. |
446 | NodePath from_path = multiplayer->get_root_path().rel_path_to(p_node->get_path()); |
447 | CharString pname = String(from_path).utf8(); |
448 | int path_len = encode_cstring(pname.get_data(), nullptr); |
449 | MAKE_ROOM(ofs + path_len); |
450 | encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); |
451 | |
452 | // Not all verified path, so check which needs the longer packet. |
453 | for (const int P : targets) { |
454 | bool confirmed = multiplayer->get_path_cache()->is_cache_confirmed(from_path, P); |
455 | if (confirmed) { |
456 | // This one confirmed path, so use id. |
457 | encode_uint32(psc_id, &(packet_cache.write[1])); |
458 | multiplayer->send_command(P, packet_cache.ptr(), ofs); |
459 | } else { |
460 | // This one did not confirm path yet, so use entire path (sorry!). |
461 | encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. |
462 | multiplayer->send_command(P, packet_cache.ptr(), ofs + path_len); |
463 | } |
464 | } |
465 | } |
466 | } |
467 | |
468 | Error SceneRPCInterface::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { |
469 | Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); |
470 | ERR_FAIL_COND_V_MSG(!peer.is_valid(), ERR_UNCONFIGURED, "Trying to call an RPC while no multiplayer peer is active." ); |
471 | Node *node = Object::cast_to<Node>(p_obj); |
472 | ERR_FAIL_COND_V_MSG(!node || !node->is_inside_tree(), ERR_INVALID_PARAMETER, "The object must be a valid Node inside the SceneTree" ); |
473 | ERR_FAIL_COND_V_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_CONNECTION_ERROR, "Trying to call an RPC via a multiplayer peer which is not connected." ); |
474 | |
475 | int caller_id = multiplayer->get_unique_id(); |
476 | bool call_local_native = false; |
477 | bool call_local_script = false; |
478 | const RPCConfigCache &config_cache = _get_node_config(node); |
479 | uint16_t rpc_id = config_cache.ids.has(p_method) ? config_cache.ids[p_method] : UINT16_MAX; |
480 | ERR_FAIL_COND_V_MSG(rpc_id == UINT16_MAX, ERR_INVALID_PARAMETER, |
481 | vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is missing or not marked for RPCs in the local script." , p_method, node->get_path())); |
482 | const RPCConfig &config = config_cache.configs[rpc_id]; |
483 | |
484 | ERR_FAIL_COND_V_MSG(p_peer_id == caller_id && !config.call_local, ERR_INVALID_PARAMETER, "RPC '" + p_method + "' on yourself is not allowed by selected mode." ); |
485 | |
486 | if (p_peer_id == 0 || p_peer_id == caller_id || (p_peer_id < 0 && p_peer_id != -caller_id)) { |
487 | if (rpc_id & (1 << 15)) { |
488 | call_local_native = config.call_local; |
489 | } else { |
490 | call_local_script = config.call_local; |
491 | } |
492 | } |
493 | |
494 | if (p_peer_id != caller_id) { |
495 | _send_rpc(node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount); |
496 | } |
497 | |
498 | if (call_local_native) { |
499 | Callable::CallError ce; |
500 | |
501 | multiplayer->set_remote_sender_override(multiplayer->get_unique_id()); |
502 | node->callp(p_method, p_arg, p_argcount, ce); |
503 | multiplayer->set_remote_sender_override(0); |
504 | |
505 | if (ce.error != Callable::CallError::CALL_OK) { |
506 | String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce); |
507 | error = "rpc() aborted in local call: - " + error + "." ; |
508 | ERR_PRINT(error); |
509 | return FAILED; |
510 | } |
511 | } |
512 | |
513 | if (call_local_script) { |
514 | Callable::CallError ce; |
515 | ce.error = Callable::CallError::CALL_OK; |
516 | |
517 | multiplayer->set_remote_sender_override(multiplayer->get_unique_id()); |
518 | node->get_script_instance()->callp(p_method, p_arg, p_argcount, ce); |
519 | multiplayer->set_remote_sender_override(0); |
520 | |
521 | if (ce.error != Callable::CallError::CALL_OK) { |
522 | String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce); |
523 | error = "rpc() aborted in script local call: - " + error + "." ; |
524 | ERR_PRINT(error); |
525 | return FAILED; |
526 | } |
527 | } |
528 | return OK; |
529 | } |
530 | |