summaryrefslogtreecommitdiff
path: root/scene/main
diff options
context:
space:
mode:
Diffstat (limited to 'scene/main')
-rw-r--r--scene/main/multiplayer_api.cpp416
-rw-r--r--scene/main/multiplayer_api.h115
-rw-r--r--scene/main/multiplayer_peer.cpp303
-rw-r--r--scene/main/multiplayer_peer.h157
-rw-r--r--scene/main/node.cpp84
-rw-r--r--scene/main/node.h30
-rw-r--r--scene/main/scene_tree.cpp18
7 files changed, 1052 insertions, 71 deletions
diff --git a/scene/main/multiplayer_api.cpp b/scene/main/multiplayer_api.cpp
new file mode 100644
index 0000000000..95574042a8
--- /dev/null
+++ b/scene/main/multiplayer_api.cpp
@@ -0,0 +1,416 @@
+/*************************************************************************/
+/* multiplayer_api.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_api.h"
+
+#include "core/debugger/engine_debugger.h"
+#include "core/io/marshalls.h"
+
+#include <stdint.h>
+
+#ifdef DEBUG_ENABLED
+#include "core/os/os.h"
+#endif
+
+StringName MultiplayerAPI::default_interface = StringName();
+
+void MultiplayerAPI::set_default_interface(const StringName &p_interface) {
+ ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_interface, MultiplayerAPI::get_class_static()), vformat("Can't make %s the default multiplayer interface since it does not extend MultiplayerAPI.", p_interface));
+ default_interface = p_interface;
+}
+
+StringName MultiplayerAPI::get_default_interface() {
+ return default_interface;
+}
+
+Ref<MultiplayerAPI> MultiplayerAPI::create_default_interface() {
+ if (default_interface != StringName()) {
+ return Ref<MultiplayerAPI>(Object::cast_to<MultiplayerAPI>(ClassDB::instantiate(default_interface)));
+ }
+ return Ref<MultiplayerAPI>(memnew(MultiplayerAPIExtension));
+}
+
+// The variant is compressed and encoded; The first byte contains all the meta
+// information and the format is:
+// - The first LSB 5 bits are used for the variant type.
+// - The next two bits are used to store the encoding mode.
+// - The most significant is used to store the boolean value.
+#define VARIANT_META_TYPE_MASK 0x1F
+#define VARIANT_META_EMODE_MASK 0x60
+#define VARIANT_META_BOOL_MASK 0x80
+#define ENCODE_8 0 << 5
+#define ENCODE_16 1 << 5
+#define ENCODE_32 2 << 5
+#define ENCODE_64 3 << 5
+Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_allow_object_decoding) {
+ // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
+ CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
+
+ uint8_t *buf = r_buffer;
+ r_len = 0;
+ uint8_t encode_mode = 0;
+
+ switch (p_variant.get_type()) {
+ case Variant::BOOL: {
+ if (buf) {
+ // We still have 1 free bit in the meta, so let's use it.
+ buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0;
+ buf[0] |= encode_mode | p_variant.get_type();
+ }
+ r_len += 1;
+ } break;
+ case Variant::INT: {
+ if (buf) {
+ // Reserve the first byte for the meta.
+ buf += 1;
+ }
+ r_len += 1;
+ int64_t val = p_variant;
+ if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
+ // Use 8 bit
+ encode_mode = ENCODE_8;
+ if (buf) {
+ buf[0] = val;
+ }
+ r_len += 1;
+ } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
+ // Use 16 bit
+ encode_mode = ENCODE_16;
+ if (buf) {
+ encode_uint16(val, buf);
+ }
+ r_len += 2;
+ } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
+ // Use 32 bit
+ encode_mode = ENCODE_32;
+ if (buf) {
+ encode_uint32(val, buf);
+ }
+ r_len += 4;
+ } else {
+ // Use 64 bit
+ encode_mode = ENCODE_64;
+ if (buf) {
+ encode_uint64(val, buf);
+ }
+ r_len += 8;
+ }
+ // Store the meta
+ if (buf) {
+ buf -= 1;
+ buf[0] = encode_mode | p_variant.get_type();
+ }
+ } break;
+ default:
+ // Any other case is not yet compressed.
+ Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding);
+ if (err != OK) {
+ return err;
+ }
+ if (r_buffer) {
+ // The first byte is not used by the marshalling, so store the type
+ // so we know how to decompress and decode this variant.
+ r_buffer[0] = p_variant.get_type();
+ }
+ }
+
+ return OK;
+}
+
+Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding) {
+ const uint8_t *buf = p_buffer;
+ int len = p_len;
+
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
+ uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
+
+ ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
+
+ switch (type) {
+ case Variant::BOOL: {
+ bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
+ r_variant = val;
+ if (r_len) {
+ *r_len = 1;
+ }
+ } break;
+ case Variant::INT: {
+ buf += 1;
+ len -= 1;
+ if (r_len) {
+ *r_len = 1;
+ }
+ if (encode_mode == ENCODE_8) {
+ // 8 bits.
+ ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
+ int8_t val = buf[0];
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 1;
+ }
+ } else if (encode_mode == ENCODE_16) {
+ // 16 bits.
+ ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
+ int16_t val = decode_uint16(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 2;
+ }
+ } else if (encode_mode == ENCODE_32) {
+ // 32 bits.
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+ int32_t val = decode_uint32(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 4;
+ }
+ } else {
+ // 64 bits.
+ ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
+ int64_t val = decode_uint64(buf);
+ r_variant = val;
+ if (r_len) {
+ (*r_len) += 8;
+ }
+ }
+ } break;
+ default:
+ Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding);
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int size = 0;
+
+ if (p_count == 0) {
+ if (r_raw) {
+ *r_raw = true;
+ }
+ return OK;
+ }
+
+ // Try raw encoding optimization.
+ if (r_raw && p_count == 1) {
+ *r_raw = false;
+ const Variant &v = *(p_variants[0]);
+ if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
+ *r_raw = true;
+ const PackedByteArray pba = v;
+ if (p_buffer) {
+ memcpy(p_buffer, pba.ptr(), pba.size());
+ }
+ r_len += pba.size();
+ } else {
+ encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+ }
+
+ // Regular encoding.
+ for (int i = 0; i < p_count; i++) {
+ const Variant &v = *(p_variants[i]);
+ encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+}
+
+Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int argc = r_variants.size();
+ if (argc == 0 && p_raw) {
+ return OK;
+ }
+ ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
+ if (p_raw) {
+ r_len = p_len;
+ PackedByteArray pba;
+ pba.resize(p_len);
+ memcpy(pba.ptrw(), p_buffer, p_len);
+ r_variants.write[0] = pba;
+ return OK;
+ }
+
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
+
+ int vlen;
+ Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
+ r_len += vlen;
+ }
+ return OK;
+}
+
+Error MultiplayerAPI::_rpc_bind(int p_peer, Object *p_object, const StringName &p_method, Array p_args) {
+ Vector<Variant> args;
+ Vector<const Variant *> argsp;
+ args.resize(p_args.size());
+ argsp.resize(p_args.size());
+ Variant *ptr = args.ptrw();
+ const Variant **pptr = argsp.ptrw();
+ for (int i = 0; i < p_args.size(); i++) {
+ ptr[i] = p_args[i];
+ pptr[i] = &ptr[i];
+ }
+ return rpcp(p_object, p_peer, p_method, argsp.size() ? argsp.ptrw() : nullptr, argsp.size());
+}
+
+void MultiplayerAPI::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("set_multiplayer_peer", "peer"), &MultiplayerAPI::set_multiplayer_peer);
+ ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerAPI::get_unique_id);
+ ClassDB::bind_method(D_METHOD("is_server"), &MultiplayerAPI::is_server);
+ ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id);
+ ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
+ ClassDB::bind_method(D_METHOD("rpc", "peer", "object", "method", "arguments"), &MultiplayerAPI::_rpc_bind, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("object_configuration_add", "object", "configuration"), &MultiplayerAPI::object_configuration_add);
+ ClassDB::bind_method(D_METHOD("object_configuration_remove", "object", "configuration"), &MultiplayerAPI::object_configuration_remove);
+
+ ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer");
+
+ ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("set_default_interface", "interface_name"), &MultiplayerAPI::set_default_interface);
+ ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("get_default_interface"), &MultiplayerAPI::get_default_interface);
+ ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("create_default_interface"), &MultiplayerAPI::create_default_interface);
+
+ ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("connected_to_server"));
+ ADD_SIGNAL(MethodInfo("connection_failed"));
+ ADD_SIGNAL(MethodInfo("server_disconnected"));
+
+ BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(RPC_MODE_ANY_PEER);
+ BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY);
+}
+
+/// MultiplayerAPIExtension
+
+Error MultiplayerAPIExtension::poll() {
+ int err;
+ if (GDVIRTUAL_CALL(_poll, err)) {
+ return (Error)err;
+ }
+ return OK;
+}
+
+void MultiplayerAPIExtension::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
+ GDVIRTUAL_CALL(_set_multiplayer_peer, p_peer);
+}
+
+Ref<MultiplayerPeer> MultiplayerAPIExtension::get_multiplayer_peer() {
+ Ref<MultiplayerPeer> peer;
+ if (GDVIRTUAL_CALL(_get_multiplayer_peer, peer)) {
+ return peer;
+ }
+ return nullptr;
+}
+
+int MultiplayerAPIExtension::get_unique_id() {
+ int id;
+ if (GDVIRTUAL_CALL(_get_unique_id, id)) {
+ return id;
+ }
+ return 1;
+}
+
+Vector<int> MultiplayerAPIExtension::get_peer_ids() {
+ Vector<int> ids;
+ if (GDVIRTUAL_CALL(_get_peer_ids, ids)) {
+ return ids;
+ }
+ return Vector<int>();
+}
+
+Error MultiplayerAPIExtension::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ if (!GDVIRTUAL_IS_OVERRIDDEN(_rpc)) {
+ return ERR_UNAVAILABLE;
+ }
+ Array args;
+ for (int i = 0; i < p_argcount; i++) {
+ args.push_back(*p_arg[i]);
+ }
+ int ret;
+ if (GDVIRTUAL_CALL(_rpc, p_peer_id, p_obj, p_method, args, ret)) {
+ return (Error)ret;
+ }
+ return FAILED;
+}
+
+int MultiplayerAPIExtension::get_remote_sender_id() {
+ int id;
+ if (GDVIRTUAL_CALL(_get_remote_sender_id, id)) {
+ return id;
+ }
+ return 0;
+}
+
+Error MultiplayerAPIExtension::object_configuration_add(Object *p_object, Variant p_config) {
+ int err;
+ if (GDVIRTUAL_CALL(_object_configuration_add, p_object, p_config, err)) {
+ return (Error)err;
+ }
+ return ERR_UNAVAILABLE;
+}
+
+Error MultiplayerAPIExtension::object_configuration_remove(Object *p_object, Variant p_config) {
+ int err;
+ if (GDVIRTUAL_CALL(_object_configuration_remove, p_object, p_config, err)) {
+ return (Error)err;
+ }
+ return ERR_UNAVAILABLE;
+}
+
+void MultiplayerAPIExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_poll);
+ GDVIRTUAL_BIND(_set_multiplayer_peer, "multiplayer_peer");
+ GDVIRTUAL_BIND(_get_multiplayer_peer);
+ GDVIRTUAL_BIND(_get_unique_id);
+ GDVIRTUAL_BIND(_get_peer_ids);
+ GDVIRTUAL_BIND(_rpc, "peer", "object", "method", "args");
+ GDVIRTUAL_BIND(_get_remote_sender_id);
+ GDVIRTUAL_BIND(_object_configuration_add, "object", "configuration");
+ GDVIRTUAL_BIND(_object_configuration_remove, "object", "configuration");
+}
diff --git a/scene/main/multiplayer_api.h b/scene/main/multiplayer_api.h
new file mode 100644
index 0000000000..c1d90d651e
--- /dev/null
+++ b/scene/main/multiplayer_api.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* multiplayer_api.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_API_H
+#define MULTIPLAYER_API_H
+
+#include "core/object/ref_counted.h"
+#include "scene/main/multiplayer_peer.h"
+
+class MultiplayerAPI : public RefCounted {
+ GDCLASS(MultiplayerAPI, RefCounted);
+
+private:
+ static StringName default_interface;
+
+protected:
+ static void _bind_methods();
+ Error _rpc_bind(int p_peer, Object *p_obj, const StringName &p_method, Array args = Array());
+
+public:
+ enum RPCMode {
+ RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
+ RPC_MODE_ANY_PEER, // Any peer can call this RPC
+ RPC_MODE_AUTHORITY, // Only the node's multiplayer authority (server by default) can call this RPC
+ };
+
+ static Ref<MultiplayerAPI> create_default_interface();
+ static void set_default_interface(const StringName &p_interface);
+ static StringName get_default_interface();
+
+ static Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len, bool p_allow_object_decoding);
+ static Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding);
+ static Error encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr, bool p_allow_object_decoding = false);
+ static Error decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false, bool p_allow_object_decoding = false);
+
+ virtual Error poll() = 0;
+ virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) = 0;
+ virtual Ref<MultiplayerPeer> get_multiplayer_peer() = 0;
+ virtual int get_unique_id() = 0;
+ virtual Vector<int> get_peer_ids() = 0;
+
+ virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) = 0;
+ virtual int get_remote_sender_id() = 0;
+
+ virtual Error object_configuration_add(Object *p_object, Variant p_config) = 0;
+ virtual Error object_configuration_remove(Object *p_object, Variant p_config) = 0;
+
+ bool has_multiplayer_peer() { return get_multiplayer_peer().is_valid(); }
+ bool is_server() { return get_unique_id() == MultiplayerPeer::TARGET_PEER_SERVER; }
+
+ MultiplayerAPI() {}
+ virtual ~MultiplayerAPI() {}
+};
+
+VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode);
+
+class MultiplayerAPIExtension : public MultiplayerAPI {
+ GDCLASS(MultiplayerAPIExtension, MultiplayerAPI);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Error poll() override;
+ virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) override;
+ virtual Ref<MultiplayerPeer> get_multiplayer_peer() override;
+ virtual int get_unique_id() override;
+ virtual Vector<int> get_peer_ids() override;
+
+ virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override;
+ virtual int get_remote_sender_id() override;
+
+ virtual Error object_configuration_add(Object *p_object, Variant p_config) override;
+ virtual Error object_configuration_remove(Object *p_object, Variant p_config) override;
+
+ // Extensions
+ GDVIRTUAL0R(int, _poll);
+ GDVIRTUAL1(_set_multiplayer_peer, Ref<MultiplayerPeer>);
+ GDVIRTUAL0R(Ref<MultiplayerPeer>, _get_multiplayer_peer);
+ GDVIRTUAL0RC(int, _get_unique_id);
+ GDVIRTUAL0RC(PackedInt32Array, _get_peer_ids);
+ GDVIRTUAL4R(int, _rpc, int, Object *, StringName, Array);
+ GDVIRTUAL0RC(int, _get_remote_sender_id);
+ GDVIRTUAL2R(int, _object_configuration_add, Object *, Variant);
+ GDVIRTUAL2R(int, _object_configuration_remove, Object *, Variant);
+};
+
+#endif // MULTIPLAYER_API_H
diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp
new file mode 100644
index 0000000000..aad5baccab
--- /dev/null
+++ b/scene/main/multiplayer_peer.cpp
@@ -0,0 +1,303 @@
+/*************************************************************************/
+/* multiplayer_peer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_peer.h"
+
+#include "core/os/os.h"
+
+uint32_t MultiplayerPeer::generate_unique_id() const {
+ uint32_t hash = 0;
+
+ while (hash == 0 || hash == 1) {
+ hash = hash_murmur3_one_32(
+ (uint32_t)OS::get_singleton()->get_ticks_usec());
+ hash = hash_murmur3_one_32(
+ (uint32_t)OS::get_singleton()->get_unix_time(), hash);
+ hash = hash_murmur3_one_32(
+ (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
+ hash = hash_murmur3_one_32(
+ (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
+ hash = hash_murmur3_one_32(
+ (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
+
+ hash = hash_fmix32(hash);
+ hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
+ }
+
+ return hash;
+}
+
+void MultiplayerPeer::set_transfer_channel(int p_channel) {
+ transfer_channel = p_channel;
+}
+
+int MultiplayerPeer::get_transfer_channel() const {
+ return transfer_channel;
+}
+
+void MultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
+ transfer_mode = p_mode;
+}
+
+MultiplayerPeer::TransferMode MultiplayerPeer::get_transfer_mode() const {
+ return transfer_mode;
+}
+
+void MultiplayerPeer::set_refuse_new_connections(bool p_enable) {
+ refuse_connections = p_enable;
+}
+
+bool MultiplayerPeer::is_refusing_new_connections() const {
+ return refuse_connections;
+}
+
+void MultiplayerPeer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel);
+ ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel);
+ ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode);
+ ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode);
+ ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer);
+
+ ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer);
+
+ ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
+
+ ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status);
+ ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id);
+ ClassDB::bind_method(D_METHOD("generate_unique_id"), &MultiplayerPeer::generate_unique_id);
+
+ ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections);
+ ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel");
+
+ BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);
+ BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);
+ BIND_ENUM_CONSTANT(CONNECTION_CONNECTED);
+
+ BIND_CONSTANT(TARGET_PEER_BROADCAST);
+ BIND_CONSTANT(TARGET_PEER_SERVER);
+
+ BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE);
+ BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED);
+ BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE);
+
+ ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("server_disconnected"));
+ ADD_SIGNAL(MethodInfo("connection_succeeded"));
+ ADD_SIGNAL(MethodInfo("connection_failed"));
+}
+
+/*************/
+
+int MultiplayerPeerExtension::get_available_packet_count() const {
+ int count;
+ if (GDVIRTUAL_CALL(_get_available_packet_count, count)) {
+ return count;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_available_packet_count is unimplemented!");
+ return -1;
+}
+
+Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
+ return (Error)err;
+ }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_get_packet_script)) {
+ if (!GDVIRTUAL_CALL(_get_packet_script, script_buffer)) {
+ return FAILED;
+ }
+
+ if (script_buffer.size() == 0) {
+ return Error::ERR_UNAVAILABLE;
+ }
+
+ *r_buffer = script_buffer.ptr();
+ r_buffer_size = script_buffer.size();
+
+ return Error::OK;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!");
+ return FAILED;
+}
+
+Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
+ return (Error)err;
+ }
+ if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) {
+ PackedByteArray a;
+ a.resize(p_buffer_size);
+ memcpy(a.ptrw(), p_buffer, p_buffer_size);
+
+ if (!GDVIRTUAL_CALL(_put_packet_script, a, err)) {
+ return FAILED;
+ }
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!");
+ return FAILED;
+}
+
+int MultiplayerPeerExtension::get_max_packet_size() const {
+ int size;
+ if (GDVIRTUAL_CALL(_get_max_packet_size, size)) {
+ return size;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_max_packet_size is unimplemented!");
+ return 0;
+}
+
+void MultiplayerPeerExtension::set_transfer_channel(int p_channel) {
+ if (GDVIRTUAL_CALL(_set_transfer_channel, p_channel)) {
+ return;
+ }
+ MultiplayerPeer::set_transfer_channel(p_channel);
+}
+
+int MultiplayerPeerExtension::get_transfer_channel() const {
+ int channel;
+ if (GDVIRTUAL_CALL(_get_transfer_channel, channel)) {
+ return channel;
+ }
+ return MultiplayerPeer::get_transfer_channel();
+}
+
+void MultiplayerPeerExtension::set_transfer_mode(TransferMode p_mode) {
+ if (GDVIRTUAL_CALL(_set_transfer_mode, p_mode)) {
+ return;
+ }
+ MultiplayerPeer::set_transfer_mode(p_mode);
+}
+
+MultiplayerPeer::TransferMode MultiplayerPeerExtension::get_transfer_mode() const {
+ int mode;
+ if (GDVIRTUAL_CALL(_get_transfer_mode, mode)) {
+ return (MultiplayerPeer::TransferMode)mode;
+ }
+ return MultiplayerPeer::get_transfer_mode();
+}
+
+void MultiplayerPeerExtension::set_target_peer(int p_peer_id) {
+ if (GDVIRTUAL_CALL(_set_target_peer, p_peer_id)) {
+ return;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_set_target_peer is unimplemented!");
+}
+
+int MultiplayerPeerExtension::get_packet_peer() const {
+ int peer;
+ if (GDVIRTUAL_CALL(_get_packet_peer, peer)) {
+ return peer;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_peer is unimplemented!");
+ return 0;
+}
+
+bool MultiplayerPeerExtension::is_server() const {
+ bool server;
+ if (GDVIRTUAL_CALL(_is_server, server)) {
+ return server;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_is_server is unimplemented!");
+ return false;
+}
+
+void MultiplayerPeerExtension::poll() {
+ int err;
+ if (GDVIRTUAL_CALL(_poll, err)) {
+ return;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_poll is unimplemented!");
+}
+
+int MultiplayerPeerExtension::get_unique_id() const {
+ int id;
+ if (GDVIRTUAL_CALL(_get_unique_id, id)) {
+ return id;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_unique_id is unimplemented!");
+ return 0;
+}
+
+void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) {
+ if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) {
+ return;
+ }
+ MultiplayerPeer::set_refuse_new_connections(p_enable);
+}
+
+bool MultiplayerPeerExtension::is_refusing_new_connections() const {
+ bool refusing;
+ if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) {
+ return refusing;
+ }
+ return MultiplayerPeer::is_refusing_new_connections();
+}
+
+MultiplayerPeer::ConnectionStatus MultiplayerPeerExtension::get_connection_status() const {
+ int status;
+ if (GDVIRTUAL_CALL(_get_connection_status, status)) {
+ return (ConnectionStatus)status;
+ }
+ WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_connection_status is unimplemented!");
+ return CONNECTION_DISCONNECTED;
+}
+
+void MultiplayerPeerExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
+ GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
+ GDVIRTUAL_BIND(_get_available_packet_count);
+ GDVIRTUAL_BIND(_get_max_packet_size);
+
+ GDVIRTUAL_BIND(_get_packet_script)
+ GDVIRTUAL_BIND(_put_packet_script, "p_buffer");
+
+ GDVIRTUAL_BIND(_set_transfer_channel, "p_channel");
+ GDVIRTUAL_BIND(_get_transfer_channel);
+
+ GDVIRTUAL_BIND(_set_transfer_mode, "p_mode");
+ GDVIRTUAL_BIND(_get_transfer_mode);
+
+ GDVIRTUAL_BIND(_set_target_peer, "p_peer");
+
+ GDVIRTUAL_BIND(_get_packet_peer);
+ GDVIRTUAL_BIND(_is_server);
+ GDVIRTUAL_BIND(_poll);
+ GDVIRTUAL_BIND(_get_unique_id);
+ GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable");
+ GDVIRTUAL_BIND(_is_refusing_new_connections);
+ GDVIRTUAL_BIND(_get_connection_status);
+}
diff --git a/scene/main/multiplayer_peer.h b/scene/main/multiplayer_peer.h
new file mode 100644
index 0000000000..8a012d7520
--- /dev/null
+++ b/scene/main/multiplayer_peer.h
@@ -0,0 +1,157 @@
+/*************************************************************************/
+/* multiplayer_peer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_PEER_H
+#define MULTIPLAYER_PEER_H
+
+#include "core/io/packet_peer.h"
+
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
+#include "core/variant/native_ptr.h"
+
+class MultiplayerPeer : public PacketPeer {
+ GDCLASS(MultiplayerPeer, PacketPeer);
+
+public:
+ enum TransferMode {
+ TRANSFER_MODE_UNRELIABLE,
+ TRANSFER_MODE_UNRELIABLE_ORDERED,
+ TRANSFER_MODE_RELIABLE
+ };
+
+protected:
+ static void _bind_methods();
+
+private:
+ int transfer_channel = 0;
+ TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
+ bool refuse_connections = false;
+
+public:
+ enum {
+ TARGET_PEER_BROADCAST = 0,
+ TARGET_PEER_SERVER = 1
+ };
+
+ enum ConnectionStatus {
+ CONNECTION_DISCONNECTED,
+ CONNECTION_CONNECTING,
+ CONNECTION_CONNECTED,
+ };
+
+ virtual void set_transfer_channel(int p_channel);
+ virtual int get_transfer_channel() const;
+ virtual void set_transfer_mode(TransferMode p_mode);
+ virtual TransferMode get_transfer_mode() const;
+ virtual void set_refuse_new_connections(bool p_enable);
+ virtual bool is_refusing_new_connections() const;
+
+ virtual void set_target_peer(int p_peer_id) = 0;
+
+ virtual int get_packet_peer() const = 0;
+
+ virtual bool is_server() const = 0;
+
+ virtual void poll() = 0;
+
+ virtual int get_unique_id() const = 0;
+
+ virtual ConnectionStatus get_connection_status() const = 0;
+
+ uint32_t generate_unique_id() const;
+
+ MultiplayerPeer() {}
+};
+
+VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus);
+VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode);
+
+class MultiplayerPeerExtension : public MultiplayerPeer {
+ GDCLASS(MultiplayerPeerExtension, MultiplayerPeer);
+
+protected:
+ static void _bind_methods();
+
+ PackedByteArray script_buffer;
+
+public:
+ /* PacketPeer */
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ virtual int get_max_packet_size() const override;
+
+ /* MultiplayerPeer */
+ virtual void set_transfer_channel(int p_channel) override;
+ virtual int get_transfer_channel() const override;
+ virtual void set_transfer_mode(TransferMode p_mode) override;
+ virtual TransferMode get_transfer_mode() const override;
+ virtual void set_target_peer(int p_peer_id) override;
+
+ virtual int get_packet_peer() const override;
+
+ virtual bool is_server() const override;
+
+ virtual void poll() override;
+
+ virtual int get_unique_id() const override;
+
+ virtual void set_refuse_new_connections(bool p_enable) override;
+ virtual bool is_refusing_new_connections() const override;
+
+ virtual ConnectionStatus get_connection_status() const override;
+
+ /* PacketPeer GDExtension */
+ GDVIRTUAL0RC(int, _get_available_packet_count);
+ GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>);
+ GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int);
+ GDVIRTUAL0RC(int, _get_max_packet_size);
+
+ /* PacketPeer GDScript */
+ GDVIRTUAL0R(PackedByteArray, _get_packet_script);
+ GDVIRTUAL1R(int, _put_packet_script, PackedByteArray);
+
+ /* MultiplayerPeer GDExtension */
+ GDVIRTUAL1(_set_transfer_channel, int);
+ GDVIRTUAL0RC(int, _get_transfer_channel);
+ GDVIRTUAL1(_set_transfer_mode, int);
+ GDVIRTUAL0RC(int, _get_transfer_mode);
+ GDVIRTUAL1(_set_target_peer, int);
+ GDVIRTUAL0RC(int, _get_packet_peer);
+ GDVIRTUAL0RC(bool, _is_server);
+ GDVIRTUAL0R(int, _poll);
+ GDVIRTUAL0RC(int, _get_unique_id);
+ GDVIRTUAL1(_set_refuse_new_connections, bool);
+ GDVIRTUAL0RC(bool, _is_refusing_new_connections);
+ GDVIRTUAL0RC(int, _get_connection_status);
+};
+
+#endif // MULTIPLAYER_PEER_H
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index b4701637a4..4bf1936a65 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -33,12 +33,12 @@
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
-#include "core/multiplayer/multiplayer_api.h"
#include "core/object/message_queue.h"
#include "core/string/print_string.h"
#include "instance_placeholder.h"
#include "scene/animation/tween.h"
#include "scene/debugger/scene_debugger.h"
+#include "scene/main/multiplayer_api.h"
#include "scene/resources/packed_scene.h"
#include "scene/scene_string_names.h"
#include "viewport.h"
@@ -582,35 +582,30 @@ bool Node::is_multiplayer_authority() const {
/***** RPC CONFIG ********/
-uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
- for (int i = 0; i < data.rpc_methods.size(); i++) {
- if (data.rpc_methods[i].name == p_method) {
- Multiplayer::RPCConfig &nd = data.rpc_methods.write[i];
- nd.rpc_mode = p_rpc_mode;
- nd.transfer_mode = p_transfer_mode;
- nd.call_local = p_call_local;
- nd.channel = p_channel;
- return i | (1 << 15);
- }
+void Node::rpc_config(const StringName &p_method, const Variant &p_config) {
+ if (data.rpc_config.get_type() != Variant::DICTIONARY) {
+ data.rpc_config = Dictionary();
+ }
+ Dictionary node_config = data.rpc_config;
+ if (p_config.get_type() == Variant::NIL) {
+ node_config.erase(p_method);
+ } else {
+ ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY);
+ node_config[p_method] = p_config;
}
- // New method
- Multiplayer::RPCConfig nd;
- nd.name = p_method;
- nd.rpc_mode = p_rpc_mode;
- nd.transfer_mode = p_transfer_mode;
- nd.channel = p_channel;
- nd.call_local = p_call_local;
- data.rpc_methods.push_back(nd);
- return ((uint16_t)data.rpc_methods.size() - 1) | (1 << 15);
+}
+
+const Variant Node::get_node_rpc_config() const {
+ return data.rpc_config;
}
/***** RPC FUNCTIONS ********/
-void Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Error Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 1) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 1;
- return;
+ return ERR_INVALID_PARAMETER;
}
Variant::Type type = p_args[0]->get_type();
@@ -618,28 +613,28 @@ void Node::_rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::STRING_NAME;
- return;
+ return ERR_INVALID_PARAMETER;
}
StringName method = (*p_args[0]).operator StringName();
- rpcp(0, method, &p_args[1], p_argcount - 1);
-
+ Error err = rpcp(0, method, &p_args[1], p_argcount - 1);
r_error.error = Callable::CallError::CALL_OK;
+ return err;
}
-void Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Error Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 2) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = 2;
- return;
+ return ERR_INVALID_PARAMETER;
}
if (p_args[0]->get_type() != Variant::INT) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::INT;
- return;
+ return ERR_INVALID_PARAMETER;
}
Variant::Type type = p_args[1]->get_type();
@@ -647,20 +642,35 @@ void Node::_rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallEr
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
r_error.expected = Variant::STRING_NAME;
- return;
+ return ERR_INVALID_PARAMETER;
}
int peer_id = *p_args[0];
StringName method = (*p_args[1]).operator StringName();
- rpcp(peer_id, method, &p_args[2], p_argcount - 2);
-
+ Error err = rpcp(peer_id, method, &p_args[2], p_argcount - 2);
r_error.error = Callable::CallError::CALL_OK;
+ return err;
}
-void Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
- ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
+template <typename... VarArgs>
+Error Node::rpc(const StringName &p_method, VarArgs... p_args) {
+ return rpc_id(0, p_method, p_args...);
+}
+
+template <typename... VarArgs>
+Error Node::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];
+ }
+ return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+}
+
+Error Node::rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
+ return get_multiplayer()->rpcp(this, p_peer_id, p_method, p_arg, p_argcount);
}
Ref<MultiplayerAPI> Node::get_multiplayer() const {
@@ -670,10 +680,6 @@ Ref<MultiplayerAPI> Node::get_multiplayer() const {
return get_tree()->get_multiplayer(get_path());
}
-Vector<Multiplayer::RPCConfig> Node::get_node_rpc_methods() const {
- return data.rpc_methods;
-}
-
//////////// end of rpc
bool Node::can_process_notification(int p_what) const {
@@ -2888,7 +2894,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_multiplayer_authority"), &Node::is_multiplayer_authority);
ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
- ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("rpc_config", "method", "config"), &Node::rpc_config);
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description);
diff --git a/scene/main/node.h b/scene/main/node.h
index d978c03442..0645c68eb9 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -127,7 +127,7 @@ private:
Node *process_owner = nullptr;
int multiplayer_authority = 1; // Server by default.
- Vector<Multiplayer::RPCConfig> rpc_methods;
+ Variant rpc_config;
// Variables used to properly sort the node when processing, ignored otherwise.
// TODO: Should move all the stuff below to bits.
@@ -183,8 +183,8 @@ private:
TypedArray<Node> _get_children(bool p_include_internal = true) const;
Array _get_groups() const;
- void _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- void _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
_FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; }
_FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; }
@@ -491,30 +491,16 @@ public:
int get_multiplayer_authority() const;
bool is_multiplayer_authority() const;
- uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local = false, Multiplayer::TransferMode p_transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0); // config a local method for RPC
- Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const;
+ void rpc_config(const StringName &p_method, const Variant &p_config); // config a local method for RPC
+ const Variant get_node_rpc_config() const;
template <typename... VarArgs>
- void rpc(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(0, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
- }
+ Error rpc(const StringName &p_method, VarArgs... p_args);
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);
- void rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
+ Error rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
Ref<MultiplayerAPI> get_multiplayer() const;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 66482f65dc..644fb3e9cc 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -37,7 +37,6 @@
#include "core/io/image_loader.h"
#include "core/io/marshalls.h"
#include "core/io/resource_loader.h"
-#include "core/multiplayer/multiplayer_api.h"
#include "core/object/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -45,6 +44,7 @@
#include "node.h"
#include "scene/animation/tween.h"
#include "scene/debugger/scene_debugger.h"
+#include "scene/main/multiplayer_api.h"
#include "scene/main/viewport.h"
#include "scene/resources/font.h"
#include "scene/resources/material.h"
@@ -1213,19 +1213,17 @@ void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePat
if (p_root_path.is_empty()) {
ERR_FAIL_COND(!p_multiplayer.is_valid());
if (multiplayer.is_valid()) {
- multiplayer->set_root_path(NodePath());
+ multiplayer->object_configuration_remove(nullptr, NodePath("/" + root->get_name()));
}
multiplayer = p_multiplayer;
- multiplayer->set_root_path("/" + root->get_name());
+ multiplayer->object_configuration_add(nullptr, NodePath("/" + root->get_name()));
} else {
+ if (custom_multiplayers.has(p_root_path)) {
+ custom_multiplayers[p_root_path]->object_configuration_remove(nullptr, p_root_path);
+ }
if (p_multiplayer.is_valid()) {
custom_multiplayers[p_root_path] = p_multiplayer;
- p_multiplayer->set_root_path(p_root_path);
- } else {
- if (custom_multiplayers.has(p_root_path)) {
- custom_multiplayers[p_root_path]->set_root_path(NodePath());
- custom_multiplayers.erase(p_root_path);
- }
+ p_multiplayer->object_configuration_add(nullptr, p_root_path);
}
}
}
@@ -1415,7 +1413,7 @@ SceneTree::SceneTree() {
#endif // _3D_DISABLED
// Initialize network state.
- set_multiplayer(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
+ set_multiplayer(MultiplayerAPI::create_default_interface());
root->set_as_audio_listener_2d(true);
current_scene = nullptr;