diff options
author | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2022-07-12 23:12:42 +0200 |
---|---|---|
committer | Fabio Alessandrelli <fabio.alessandrelli@gmail.com> | 2022-07-26 09:31:12 +0200 |
commit | ca7d572908c58c587214b8f65bdd4078d0185ab2 (patch) | |
tree | efd1507df5b3cd45e20b36bfc22818fcfadc5c85 /scene | |
parent | c3dc887c410c5fa01eba197acba3afd8ce357ada (diff) |
[Net] Modularize multiplayer, expose MultiplayerAPI to extensions.
- 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.
Diffstat (limited to 'scene')
24 files changed, 1059 insertions, 3180 deletions
diff --git a/scene/SCsub b/scene/SCsub index a7b23af598..92288211bb 100644 --- a/scene/SCsub +++ b/scene/SCsub @@ -9,7 +9,6 @@ env.add_source_files(env.scene_sources, "*.cpp") # Chain load SCsubs SConscript("main/SCsub") -SConscript("multiplayer/SCsub") SConscript("gui/SCsub") if not env["disable_3d"]: SConscript("3d/SCsub") 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; diff --git a/scene/multiplayer/SCsub b/scene/multiplayer/SCsub deleted file mode 100644 index fc61250247..0000000000 --- a/scene/multiplayer/SCsub +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python - -Import("env") - -env.add_source_files(env.scene_sources, "*.cpp") diff --git a/scene/multiplayer/multiplayer_spawner.cpp b/scene/multiplayer/multiplayer_spawner.cpp deleted file mode 100644 index 8363d05e54..0000000000 --- a/scene/multiplayer/multiplayer_spawner.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/*************************************************************************/ -/* multiplayer_spawner.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_spawner.h" - -#include "core/io/marshalls.h" -#include "core/multiplayer/multiplayer_api.h" -#include "scene/main/window.h" -#include "scene/scene_string_names.h" - -#ifdef TOOLS_ENABLED -/* This is editor only */ -bool MultiplayerSpawner::_set(const StringName &p_name, const Variant &p_value) { - if (p_name == "_spawnable_scene_count") { - spawnable_scenes.resize(p_value); - notify_property_list_changed(); - return true; - } else { - String ns = p_name; - if (ns.begins_with("scenes/")) { - uint32_t index = ns.get_slicec('/', 1).to_int(); - ERR_FAIL_UNSIGNED_INDEX_V(index, spawnable_scenes.size(), false); - spawnable_scenes[index].path = p_value; - return true; - } - } - return false; -} - -bool MultiplayerSpawner::_get(const StringName &p_name, Variant &r_ret) const { - if (p_name == "_spawnable_scene_count") { - r_ret = spawnable_scenes.size(); - return true; - } else { - String ns = p_name; - if (ns.begins_with("scenes/")) { - uint32_t index = ns.get_slicec('/', 1).to_int(); - ERR_FAIL_UNSIGNED_INDEX_V(index, spawnable_scenes.size(), false); - r_ret = spawnable_scenes[index].path; - return true; - } - } - return false; -} - -void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::INT, "_spawnable_scene_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Auto Spawn List,scenes/")); - List<String> exts; - ResourceLoader::get_recognized_extensions_for_type("PackedScene", &exts); - String ext_hint; - for (const String &E : exts) { - if (!ext_hint.is_empty()) { - ext_hint += ","; - } - ext_hint += "*." + E; - } - for (uint32_t i = 0; i < spawnable_scenes.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, "scenes/" + itos(i), PROPERTY_HINT_FILE, ext_hint, PROPERTY_USAGE_EDITOR)); - } -} -#endif -void MultiplayerSpawner::add_spawnable_scene(const String &p_path) { - SpawnableScene sc; - sc.path = p_path; - if (Engine::get_singleton()->is_editor_hint()) { - ERR_FAIL_COND(!FileAccess::exists(p_path)); - } else { - sc.cache = ResourceLoader::load(p_path); - ERR_FAIL_COND_MSG(sc.cache.is_null(), "Invalid spawnable scene: " + p_path); - } - spawnable_scenes.push_back(sc); -} -int MultiplayerSpawner::get_spawnable_scene_count() const { - return spawnable_scenes.size(); -} -String MultiplayerSpawner::get_spawnable_scene(int p_idx) const { - return spawnable_scenes[p_idx].path; -} -void MultiplayerSpawner::clear_spawnable_scenes() { - spawnable_scenes.clear(); -} - -Vector<String> MultiplayerSpawner::_get_spawnable_scenes() const { - Vector<String> ss; - ss.resize(spawnable_scenes.size()); - for (int i = 0; i < ss.size(); i++) { - ss.write[i] = spawnable_scenes[i].path; - } - return ss; -} - -void MultiplayerSpawner::_set_spawnable_scenes(const Vector<String> &p_scenes) { - clear_spawnable_scenes(); - for (int i = 0; i < p_scenes.size(); i++) { - add_spawnable_scene(p_scenes[i]); - } -} - -void MultiplayerSpawner::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_spawnable_scene", "path"), &MultiplayerSpawner::add_spawnable_scene); - ClassDB::bind_method(D_METHOD("get_spawnable_scene_count"), &MultiplayerSpawner::get_spawnable_scene_count); - ClassDB::bind_method(D_METHOD("get_spawnable_scene", "path"), &MultiplayerSpawner::get_spawnable_scene); - ClassDB::bind_method(D_METHOD("clear_spawnable_scenes"), &MultiplayerSpawner::clear_spawnable_scenes); - - ClassDB::bind_method(D_METHOD("_get_spawnable_scenes"), &MultiplayerSpawner::_get_spawnable_scenes); - ClassDB::bind_method(D_METHOD("_set_spawnable_scenes", "scenes"), &MultiplayerSpawner::_set_spawnable_scenes); - - ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "_spawnable_scenes", PROPERTY_HINT_NONE, "", (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL)), "_set_spawnable_scenes", "_get_spawnable_scenes"); - - ClassDB::bind_method(D_METHOD("spawn", "data"), &MultiplayerSpawner::spawn, DEFVAL(Variant())); - - ClassDB::bind_method(D_METHOD("get_spawn_path"), &MultiplayerSpawner::get_spawn_path); - ClassDB::bind_method(D_METHOD("set_spawn_path", "path"), &MultiplayerSpawner::set_spawn_path); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "spawn_path", PROPERTY_HINT_NONE, ""), "set_spawn_path", "get_spawn_path"); - - ClassDB::bind_method(D_METHOD("get_spawn_limit"), &MultiplayerSpawner::get_spawn_limit); - ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit); - ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit"); - - GDVIRTUAL_BIND(_spawn_custom, "data"); - - ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); -} - -void MultiplayerSpawner::_update_spawn_node() { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - if (spawn_node.is_valid()) { - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)); - if (node && node->is_connected("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added))) { - node->disconnect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added)); - } - } - Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path); - if (node) { - spawn_node = node->get_instance_id(); - if (get_spawnable_scene_count() && !GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom)) { - node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added)); - } - } else { - spawn_node = ObjectID(); - } -} - -void MultiplayerSpawner::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_POST_ENTER_TREE: { - _update_spawn_node(); - } break; - - case NOTIFICATION_EXIT_TREE: { - _update_spawn_node(); - - for (const KeyValue<ObjectID, SpawnInfo> &E : tracked_nodes) { - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E.key)); - ERR_CONTINUE(!node); - node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit)); - // This is unlikely, but might still crash the engine. - if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) { - node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready)); - } - get_multiplayer()->despawn(node, this); - } - tracked_nodes.clear(); - } break; - } -} - -void MultiplayerSpawner::_node_added(Node *p_node) { - if (!get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority()) { - return; - } - if (tracked_nodes.has(p_node->get_instance_id())) { - return; - } - const Node *parent = get_spawn_node(); - if (!parent || p_node->get_parent() != parent) { - return; - } - int id = find_spawnable_scene_index_from_path(p_node->get_scene_file_path()); - if (id == INVALID_ID) { - return; - } - const String name = p_node->get_name(); - ERR_FAIL_COND_MSG(name.validate_node_name() != name, vformat("Unable to auto-spawn node with reserved name: %s. Make sure to add your replicated scenes via 'add_child(node, true)' to produce valid names.", name)); - _track(p_node, Variant(), id); -} - -NodePath MultiplayerSpawner::get_spawn_path() const { - return spawn_path; -} - -void MultiplayerSpawner::set_spawn_path(const NodePath &p_path) { - spawn_path = p_path; - _update_spawn_node(); -} - -void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_scene_id) { - ObjectID oid = p_node->get_instance_id(); - if (!tracked_nodes.has(oid)) { - tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id); - p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit), varray(p_node->get_instance_id()), CONNECT_ONESHOT); - p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready), varray(p_node->get_instance_id()), CONNECT_ONESHOT); - } -} - -void MultiplayerSpawner::_node_ready(ObjectID p_id) { - get_multiplayer()->spawn(ObjectDB::get_instance(p_id), this); -} - -void MultiplayerSpawner::_node_exit(ObjectID p_id) { - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id)); - ERR_FAIL_COND(!node); - if (tracked_nodes.has(p_id)) { - tracked_nodes.erase(p_id); - get_multiplayer()->despawn(node, this); - } -} - -int MultiplayerSpawner::find_spawnable_scene_index_from_path(const String &p_scene) const { - for (uint32_t i = 0; i < spawnable_scenes.size(); i++) { - if (spawnable_scenes[i].path == p_scene) { - return i; - } - } - return INVALID_ID; -} - -int MultiplayerSpawner::find_spawnable_scene_index_from_object(const ObjectID &p_id) const { - const SpawnInfo *info = tracked_nodes.getptr(p_id); - return info ? info->id : INVALID_ID; -} - -const Variant MultiplayerSpawner::get_spawn_argument(const ObjectID &p_id) const { - const SpawnInfo *info = tracked_nodes.getptr(p_id); - return info ? info->args : Variant(); -} - -Node *MultiplayerSpawner::instantiate_scene(int p_id) { - ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); - ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_id, spawnable_scenes.size(), nullptr); - Ref<PackedScene> scene = spawnable_scenes[p_id].cache; - ERR_FAIL_COND_V(scene.is_null(), nullptr); - return scene->instantiate(); -} - -Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) { - ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); - Object *obj = nullptr; - Node *node = nullptr; - if (GDVIRTUAL_CALL(_spawn_custom, p_data, obj)) { - node = Object::cast_to<Node>(obj); - } - return node; -} - -Node *MultiplayerSpawner::spawn(const Variant &p_data) { - ERR_FAIL_COND_V(!is_inside_tree() || !get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority(), nullptr); - ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!"); - ERR_FAIL_COND_V_MSG(!GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom), nullptr, "Custom spawn requires the '_spawn_custom' virtual method to be implemented via script."); - - Node *parent = get_spawn_node(); - ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node."); - - Node *node = instantiate_custom(p_data); - ERR_FAIL_COND_V_MSG(!node, nullptr, "The '_spawn_custom' implementation must return a valid Node."); - - _track(node, p_data); - parent->add_child(node, true); - return node; -} diff --git a/scene/multiplayer/multiplayer_spawner.h b/scene/multiplayer/multiplayer_spawner.h deleted file mode 100644 index 2c0eb9a2f0..0000000000 --- a/scene/multiplayer/multiplayer_spawner.h +++ /dev/null @@ -1,117 +0,0 @@ -/*************************************************************************/ -/* multiplayer_spawner.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_SPAWNER_H -#define MULTIPLAYER_SPAWNER_H - -#include "scene/main/node.h" - -#include "core/templates/local_vector.h" -#include "core/variant/typed_array.h" -#include "scene/resources/packed_scene.h" -#include "scene/resources/scene_replication_config.h" - -class MultiplayerSpawner : public Node { - GDCLASS(MultiplayerSpawner, Node); - -public: - enum { - INVALID_ID = 0xFF, - }; - -private: - struct SpawnableScene { - String path; - Ref<PackedScene> cache; - }; - - LocalVector<SpawnableScene> spawnable_scenes; - - HashSet<ResourceUID::ID> spawnable_ids; - NodePath spawn_path; - - struct SpawnInfo { - Variant args; - int id = INVALID_ID; - SpawnInfo(Variant p_args, int p_id) { - id = p_id; - args = p_args; - } - SpawnInfo() {} - }; - - ObjectID spawn_node; - HashMap<ObjectID, SpawnInfo> tracked_nodes; - uint32_t spawn_limit = 0; - - void _update_spawn_node(); - void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID); - void _node_added(Node *p_node); - void _node_exit(ObjectID p_id); - void _node_ready(ObjectID p_id); - - Vector<String> _get_spawnable_scenes() const; - void _set_spawnable_scenes(const Vector<String> &p_scenes); - -protected: - static void _bind_methods(); - void _notification(int p_what); - -#ifdef TOOLS_ENABLED - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; -#endif -public: - Node *get_spawn_node() const { return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr; } - - void add_spawnable_scene(const String &p_path); - int get_spawnable_scene_count() const; - String get_spawnable_scene(int p_idx) const; - void clear_spawnable_scenes(); - - NodePath get_spawn_path() const; - void set_spawn_path(const NodePath &p_path); - uint32_t get_spawn_limit() const { return spawn_limit; } - void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; } - - const Variant get_spawn_argument(const ObjectID &p_id) const; - int find_spawnable_scene_index_from_object(const ObjectID &p_id) const; - int find_spawnable_scene_index_from_path(const String &p_path) const; - Node *spawn(const Variant &p_data = Variant()); - Node *instantiate_custom(const Variant &p_data); - Node *instantiate_scene(int p_idx); - - GDVIRTUAL1R(Object *, _spawn_custom, const Variant &); - - MultiplayerSpawner() {} -}; - -#endif // MULTIPLAYER_SPAWNER_H diff --git a/scene/multiplayer/multiplayer_synchronizer.cpp b/scene/multiplayer/multiplayer_synchronizer.cpp deleted file mode 100644 index e1b7433968..0000000000 --- a/scene/multiplayer/multiplayer_synchronizer.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/*************************************************************************/ -/* multiplayer_synchronizer.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_synchronizer.h" - -#include "core/config/engine.h" -#include "core/multiplayer/multiplayer_api.h" - -Object *MultiplayerSynchronizer::_get_prop_target(Object *p_obj, const NodePath &p_path) { - if (p_path.get_name_count() == 0) { - return p_obj; - } - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V_MSG(!node || !node->has_node(p_path), nullptr, vformat("Node '%s' not found.", p_path)); - return node->get_node(p_path); -} - -void MultiplayerSynchronizer::_stop() { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr; - if (node) { - get_multiplayer()->replication_stop(node, this); - } -} - -void MultiplayerSynchronizer::_start() { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr; - if (node) { - get_multiplayer()->replication_start(node, this); - _update_process(); - } -} - -void MultiplayerSynchronizer::_update_process() { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr; - if (!node) { - return; - } - set_process_internal(false); - set_physics_process_internal(false); - if (!visibility_filters.size()) { - return; - } - switch (visibility_update_mode) { - case VISIBILITY_PROCESS_IDLE: - set_process_internal(true); - break; - case VISIBILITY_PROCESS_PHYSICS: - set_physics_process_internal(true); - break; - case VISIBILITY_PROCESS_NONE: - break; - } -} - -Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs) { - ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER); - r_variant.resize(p_properties.size()); - r_variant_ptrs.resize(r_variant.size()); - int i = 0; - for (const NodePath &prop : p_properties) { - bool valid = false; - const Object *obj = _get_prop_target(p_obj, prop); - ERR_FAIL_COND_V(!obj, FAILED); - r_variant.write[i] = obj->get(prop.get_concatenated_subnames(), &valid); - r_variant_ptrs.write[i] = &r_variant[i]; - ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop)); - i++; - } - return OK; -} - -Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state) { - ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER); - int i = 0; - for (const NodePath &prop : p_properties) { - Object *obj = _get_prop_target(p_obj, prop); - ERR_FAIL_COND_V(!obj, FAILED); - obj->set(prop.get_concatenated_subnames(), p_state[i]); - i += 1; - } - return OK; -} - -bool MultiplayerSynchronizer::is_visibility_public() const { - return peer_visibility.has(0); -} - -void MultiplayerSynchronizer::set_visibility_public(bool p_visible) { - set_visibility_for(0, p_visible); -} - -bool MultiplayerSynchronizer::is_visible_to(int p_peer) { - if (visibility_filters.size()) { - Variant arg = p_peer; - const Variant *argv[1] = { &arg }; - for (Callable filter : visibility_filters) { - Variant ret; - Callable::CallError err; - filter.call(argv, 1, ret, err); - ERR_FAIL_COND_V(err.error != Callable::CallError::CALL_OK || ret.get_type() != Variant::BOOL, false); - if (!ret.operator bool()) { - return false; - } - } - } - return peer_visibility.has(0) || peer_visibility.has(p_peer); -} - -void MultiplayerSynchronizer::add_visibility_filter(Callable p_callback) { - visibility_filters.insert(p_callback); - _update_process(); -} - -void MultiplayerSynchronizer::remove_visibility_filter(Callable p_callback) { - visibility_filters.erase(p_callback); - _update_process(); -} - -void MultiplayerSynchronizer::set_visibility_for(int p_peer, bool p_visible) { - if (peer_visibility.has(p_peer) == p_visible) { - return; - } - if (p_visible) { - peer_visibility.insert(p_peer); - } else { - peer_visibility.erase(p_peer); - } - update_visibility(p_peer); -} - -bool MultiplayerSynchronizer::get_visibility_for(int p_peer) const { - return peer_visibility.has(p_peer); -} - -void MultiplayerSynchronizer::set_visibility_update_mode(VisibilityUpdateMode p_mode) { - visibility_update_mode = p_mode; - _update_process(); -} - -MultiplayerSynchronizer::VisibilityUpdateMode MultiplayerSynchronizer::get_visibility_update_mode() const { - return visibility_update_mode; -} - -void MultiplayerSynchronizer::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerSynchronizer::set_root_path); - ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerSynchronizer::get_root_path); - - ClassDB::bind_method(D_METHOD("set_replication_interval", "milliseconds"), &MultiplayerSynchronizer::set_replication_interval); - ClassDB::bind_method(D_METHOD("get_replication_interval"), &MultiplayerSynchronizer::get_replication_interval); - - ClassDB::bind_method(D_METHOD("set_replication_config", "config"), &MultiplayerSynchronizer::set_replication_config); - ClassDB::bind_method(D_METHOD("get_replication_config"), &MultiplayerSynchronizer::get_replication_config); - - ClassDB::bind_method(D_METHOD("set_visibility_update_mode", "mode"), &MultiplayerSynchronizer::set_visibility_update_mode); - ClassDB::bind_method(D_METHOD("get_visibility_update_mode"), &MultiplayerSynchronizer::get_visibility_update_mode); - ClassDB::bind_method(D_METHOD("update_visibility", "for_peer"), &MultiplayerSynchronizer::update_visibility, DEFVAL(0)); - - ClassDB::bind_method(D_METHOD("set_visibility_public", "visible"), &MultiplayerSynchronizer::set_visibility_public); - ClassDB::bind_method(D_METHOD("is_visibility_public"), &MultiplayerSynchronizer::is_visibility_public); - - ClassDB::bind_method(D_METHOD("add_visibility_filter", "filter"), &MultiplayerSynchronizer::add_visibility_filter); - ClassDB::bind_method(D_METHOD("remove_visibility_filter", "filter"), &MultiplayerSynchronizer::remove_visibility_filter); - ClassDB::bind_method(D_METHOD("set_visibility_for", "peer", "visible"), &MultiplayerSynchronizer::set_visibility_for); - ClassDB::bind_method(D_METHOD("get_visibility_for", "peer"), &MultiplayerSynchronizer::get_visibility_for); - - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001,suffix:s"), "set_replication_interval", "get_replication_interval"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replication_config", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig", PROPERTY_USAGE_NO_EDITOR), "set_replication_config", "get_replication_config"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,None"), "set_visibility_update_mode", "get_visibility_update_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "public_visibility"), "set_visibility_public", "is_visibility_public"); - - BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_IDLE); - BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_PHYSICS); - BIND_ENUM_CONSTANT(VISIBILITY_PROCESS_NONE); - - ADD_SIGNAL(MethodInfo("visibility_changed", PropertyInfo(Variant::INT, "for_peer"))); -} - -void MultiplayerSynchronizer::_notification(int p_what) { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - if (root_path.is_empty()) { - return; - } - - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - _start(); - } break; - - case NOTIFICATION_EXIT_TREE: { - _stop(); - } break; - - case NOTIFICATION_INTERNAL_PROCESS: - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - update_visibility(0); - } break; - } -} - -void MultiplayerSynchronizer::set_replication_interval(double p_interval) { - ERR_FAIL_COND_MSG(p_interval < 0, "Interval must be greater or equal to 0 (where 0 means default)"); - interval_msec = uint64_t(p_interval * 1000); -} - -double MultiplayerSynchronizer::get_replication_interval() const { - return double(interval_msec) / 1000.0; -} - -uint64_t MultiplayerSynchronizer::get_replication_interval_msec() const { - return interval_msec; -} - -void MultiplayerSynchronizer::set_replication_config(Ref<SceneReplicationConfig> p_config) { - replication_config = p_config; -} - -Ref<SceneReplicationConfig> MultiplayerSynchronizer::get_replication_config() { - return replication_config; -} - -void MultiplayerSynchronizer::update_visibility(int p_for_peer) { -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - return; - } -#endif - Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr; - if (node && get_multiplayer()->has_multiplayer_peer() && is_multiplayer_authority()) { - emit_signal(SNAME("visibility_changed"), p_for_peer); - } -} - -void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) { - _stop(); - root_path = p_path; - _start(); -} - -NodePath MultiplayerSynchronizer::get_root_path() const { - return root_path; -} - -void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_recursive) { - Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr; - if (!node) { - Node::set_multiplayer_authority(p_peer_id, p_recursive); - return; - } - get_multiplayer()->replication_stop(node, this); - Node::set_multiplayer_authority(p_peer_id, p_recursive); - get_multiplayer()->replication_start(node, this); -} - -MultiplayerSynchronizer::MultiplayerSynchronizer() { - // Publicly visible by default. - peer_visibility.insert(0); -} diff --git a/scene/multiplayer/multiplayer_synchronizer.h b/scene/multiplayer/multiplayer_synchronizer.h deleted file mode 100644 index 77c23b336d..0000000000 --- a/scene/multiplayer/multiplayer_synchronizer.h +++ /dev/null @@ -1,96 +0,0 @@ -/*************************************************************************/ -/* multiplayer_synchronizer.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_SYNCHRONIZER_H -#define MULTIPLAYER_SYNCHRONIZER_H - -#include "scene/main/node.h" - -#include "scene/resources/scene_replication_config.h" - -class MultiplayerSynchronizer : public Node { - GDCLASS(MultiplayerSynchronizer, Node); - -public: - enum VisibilityUpdateMode { - VISIBILITY_PROCESS_IDLE, - VISIBILITY_PROCESS_PHYSICS, - VISIBILITY_PROCESS_NONE, - }; - -private: - Ref<SceneReplicationConfig> replication_config; - NodePath root_path = NodePath(".."); // Start with parent, like with AnimationPlayer. - uint64_t interval_msec = 0; - VisibilityUpdateMode visibility_update_mode = VISIBILITY_PROCESS_IDLE; - HashSet<Callable> visibility_filters; - HashSet<int> peer_visibility; - - static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop); - void _start(); - void _stop(); - void _update_process(); - -protected: - static void _bind_methods(); - void _notification(int p_what); - -public: - static Error get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs); - static Error set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state); - - void set_replication_interval(double p_interval); - double get_replication_interval() const; - uint64_t get_replication_interval_msec() const; - - void set_replication_config(Ref<SceneReplicationConfig> p_config); - Ref<SceneReplicationConfig> get_replication_config(); - - void set_root_path(const NodePath &p_path); - NodePath get_root_path() const; - virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true) override; - - bool is_visibility_public() const; - void set_visibility_public(bool p_public); - bool is_visible_to(int p_peer); - void set_visibility_for(int p_peer, bool p_visible); - bool get_visibility_for(int p_peer) const; - void update_visibility(int p_for_peer); - void set_visibility_update_mode(VisibilityUpdateMode p_mode); - void add_visibility_filter(Callable p_callback); - void remove_visibility_filter(Callable p_callback); - VisibilityUpdateMode get_visibility_update_mode() const; - - MultiplayerSynchronizer(); -}; - -VARIANT_ENUM_CAST(MultiplayerSynchronizer::VisibilityUpdateMode); - -#endif // MULTIPLAYER_SYNCHRONIZER_H diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp deleted file mode 100644 index 79a7dc2d5a..0000000000 --- a/scene/multiplayer/scene_cache_interface.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/*************************************************************************/ -/* scene_cache_interface.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 "scene_cache_interface.h" - -#include "core/io/marshalls.h" -#include "scene/main/node.h" -#include "scene/main/window.h" - -MultiplayerCacheInterface *SceneCacheInterface::_create(MultiplayerAPI *p_multiplayer) { - return memnew(SceneCacheInterface(p_multiplayer)); -} - -void SceneCacheInterface::make_default() { - MultiplayerAPI::create_default_cache_interface = _create; -} - -void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) { - if (p_connected) { - path_get_cache.insert(p_id, PathGetCache()); - } else { - // Cleanup get cache. - path_get_cache.erase(p_id); - // Cleanup sent cache. - // Some refactoring is needed to make this faster and do paths GC. - for (const KeyValue<NodePath, PathSentCache> &E : path_send_cache) { - PathSentCache *psc = path_send_cache.getptr(E.key); - psc->confirmed_peers.erase(p_id); - } - } -} - -void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); - ERR_FAIL_COND(!root_node); - ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); - int ofs = 1; - - String methods_md5; - methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); - ofs += 33; - - int id = decode_uint32(&p_packet[ofs]); - ofs += 4; - - String paths; - paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); - - NodePath path = paths; - - if (!path_get_cache.has(p_from)) { - path_get_cache[p_from] = PathGetCache(); - } - - Node *node = root_node->get_node(path); - ERR_FAIL_COND(node == nullptr); - const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5; - if (valid_rpc_checksum == false) { - ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); - } - - PathGetCache::NodeInfo ni; - ni.path = path; - - path_get_cache[p_from].nodes[id] = ni; - - // Encode path to send ack. - CharString pname = String(path).utf8(); - int len = encode_cstring(pname.get_data(), nullptr); - - Vector<uint8_t> packet; - - packet.resize(1 + 1 + len); - packet.write[0] = MultiplayerAPI::NETWORK_COMMAND_CONFIRM_PATH; - packet.write[1] = valid_rpc_checksum; - encode_cstring(pname.get_data(), &packet.write[2]); - - Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); - ERR_FAIL_COND(multiplayer_peer.is_null()); - -#ifdef DEBUG_ENABLED - multiplayer->profile_bandwidth("out", packet.size()); -#endif - - multiplayer_peer->set_transfer_channel(0); - multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - multiplayer_peer->set_target_peer(p_from); - multiplayer_peer->put_packet(packet.ptr(), packet.size()); -} - -void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); - - const bool valid_rpc_checksum = p_packet[1]; - - String paths; - paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); - - NodePath path = paths; - - if (valid_rpc_checksum == false) { - ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); - } - - PathSentCache *psc = path_send_cache.getptr(path); - ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); - - HashMap<int, bool>::Iterator E = psc->confirmed_peers.find(p_from); - ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path."); - E->value = true; -} - -Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers) { - // Encode function name. - const CharString path = String(p_path).utf8(); - const int path_len = encode_cstring(path.get_data(), nullptr); - - // Extract MD5 from rpc methods list. - const String methods_md5 = multiplayer->get_rpc_md5(p_node); - const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. - - Vector<uint8_t> packet; - packet.resize(1 + 4 + path_len + methods_md5_len); - int ofs = 0; - - packet.write[ofs] = MultiplayerAPI::NETWORK_COMMAND_SIMPLIFY_PATH; - ofs += 1; - - ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); - - ofs += encode_uint32(psc->id, &packet.write[ofs]); - - ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - - Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer(); - ERR_FAIL_COND_V(multiplayer_peer.is_null(), ERR_BUG); - -#ifdef DEBUG_ENABLED - multiplayer->profile_bandwidth("out", packet.size() * p_peers.size()); -#endif - - Error err = OK; - for (int peer_id : p_peers) { - multiplayer_peer->set_target_peer(peer_id); - multiplayer_peer->set_transfer_channel(0); - multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE); - err = multiplayer_peer->put_packet(packet.ptr(), packet.size()); - ERR_FAIL_COND_V(err != OK, err); - // Insert into confirmed, but as false since it was not confirmed. - psc->confirmed_peers.insert(peer_id, false); - } - return err; -} - -bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) { - const PathSentCache *psc = path_send_cache.getptr(p_path); - ERR_FAIL_COND_V(!psc, false); - HashMap<int, bool>::ConstIterator F = psc->confirmed_peers.find(p_peer); - ERR_FAIL_COND_V(!F, false); // Should never happen. - return F->value; -} - -int SceneCacheInterface::make_object_cache(Object *p_obj) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node, -1); - NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path()); - // See if the path is cached. - PathSentCache *psc = path_send_cache.getptr(for_path); - if (!psc) { - // Path is not cached, create. - path_send_cache[for_path] = PathSentCache(); - psc = path_send_cache.getptr(for_path); - psc->id = last_send_cache_id++; - } - return psc->id; -} - -bool SceneCacheInterface::send_object_cache(Object *p_obj, int p_peer_id, int &r_id) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node, false); - - r_id = make_object_cache(p_obj); - ERR_FAIL_COND_V(r_id < 0, false); - NodePath for_path = multiplayer->get_root_path().rel_path_to(node->get_path()); - PathSentCache *psc = path_send_cache.getptr(for_path); - - bool has_all_peers = true; - List<int> peers_to_add; // If one is missing, take note to add it. - - if (p_peer_id > 0) { - // Fast single peer check. - HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(p_peer_id); - if (!F) { - peers_to_add.push_back(p_peer_id); // Need to also be notified. - has_all_peers = false; - } else if (!F->value) { - has_all_peers = false; - } - } else { - // Long and painful. - for (const int &E : multiplayer->get_connected_peers()) { - if (p_peer_id < 0 && E == -p_peer_id) { - continue; // Continue, excluded. - } - if (p_peer_id > 0 && E != p_peer_id) { - continue; // Continue, not for this peer. - } - - HashMap<int, bool>::Iterator F = psc->confirmed_peers.find(E); - if (!F) { - peers_to_add.push_back(E); // Need to also be notified. - has_all_peers = false; - } else if (!F->value) { - has_all_peers = false; - } - } - } - - if (peers_to_add.size()) { - _send_confirm_path(node, for_path, psc, peers_to_add); - } - - return has_all_peers; -} - -Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id) { - Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); - ERR_FAIL_COND_V(!root_node, nullptr); - HashMap<int, PathGetCache>::Iterator E = path_get_cache.find(p_from); - ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from)); - - HashMap<int, PathGetCache::NodeInfo>::Iterator F = E->value.nodes.find(p_cache_id); - ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_cache_id, p_from)); - - PathGetCache::NodeInfo *ni = &F->value; - Node *node = root_node->get_node(ni->path); - if (!node) { - ERR_PRINT("Failed to get cached path: " + String(ni->path) + "."); - } - return node; -} - -void SceneCacheInterface::clear() { - path_get_cache.clear(); - path_send_cache.clear(); - last_send_cache_id = 1; -} diff --git a/scene/multiplayer/scene_cache_interface.h b/scene/multiplayer/scene_cache_interface.h deleted file mode 100644 index 6bfd683cf4..0000000000 --- a/scene/multiplayer/scene_cache_interface.h +++ /dev/null @@ -1,83 +0,0 @@ -/*************************************************************************/ -/* scene_cache_interface.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 SCENE_CACHE_INTERFACE_H -#define SCENE_CACHE_INTERFACE_H - -#include "core/multiplayer/multiplayer_api.h" - -class SceneCacheInterface : public MultiplayerCacheInterface { - GDCLASS(SceneCacheInterface, MultiplayerCacheInterface); - -private: - MultiplayerAPI *multiplayer = nullptr; - - //path sent caches - struct PathSentCache { - HashMap<int, bool> confirmed_peers; - int id; - }; - - //path get caches - struct PathGetCache { - struct NodeInfo { - NodePath path; - ObjectID instance; - }; - - HashMap<int, NodeInfo> nodes; - }; - - HashMap<NodePath, PathSentCache> path_send_cache; - HashMap<int, PathGetCache> path_get_cache; - int last_send_cache_id = 1; - -protected: - Error _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, const List<int> &p_peers); - static MultiplayerCacheInterface *_create(MultiplayerAPI *p_multiplayer); - -public: - static void make_default(); - - virtual void clear() override; - virtual void on_peer_change(int p_id, bool p_connected) override; - virtual void process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) override; - virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) override; - - // Returns true if all peers have cached path. - virtual bool send_object_cache(Object *p_obj, int p_target, int &p_id) override; - virtual int make_object_cache(Object *p_obj) override; - virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) override; - virtual bool is_cache_confirmed(NodePath p_path, int p_peer) override; - - SceneCacheInterface(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; } -}; - -#endif // SCENE_CACHE_INTERFACE_H diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp deleted file mode 100644 index c616c5bb85..0000000000 --- a/scene/multiplayer/scene_replication_interface.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/*************************************************************************/ -/* scene_replication_interface.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 "scene_replication_interface.h" - -#include "core/io/marshalls.h" -#include "scene/main/node.h" -#include "scene/multiplayer/multiplayer_spawner.h" -#include "scene/multiplayer/multiplayer_synchronizer.h" - -#define MAKE_ROOM(m_amount) \ - if (packet_cache.size() < m_amount) \ - packet_cache.resize(m_amount); - -MultiplayerReplicationInterface *SceneReplicationInterface::_create(MultiplayerAPI *p_multiplayer) { - return memnew(SceneReplicationInterface(p_multiplayer)); -} - -void SceneReplicationInterface::make_default() { - MultiplayerAPI::create_default_replication_interface = _create; -} - -void SceneReplicationInterface::_free_remotes(int p_id) { - const HashMap<uint32_t, ObjectID> remotes = rep_state->peer_get_remotes(p_id); - for (const KeyValue<uint32_t, ObjectID> &E : remotes) { - Node *node = rep_state->get_node(E.value); - ERR_CONTINUE(!node); - node->queue_delete(); - } -} - -void SceneReplicationInterface::on_peer_change(int p_id, bool p_connected) { - if (p_connected) { - rep_state->on_peer_change(p_id, p_connected); - for (const ObjectID &oid : rep_state->get_spawned_nodes()) { - _update_spawn_visibility(p_id, oid); - } - for (const ObjectID &oid : rep_state->get_synced_nodes()) { - MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid); - ERR_CONTINUE(!sync); // ERR_BUG - if (sync->is_multiplayer_authority()) { - _update_sync_visibility(p_id, oid); - } - } - } else { - _free_remotes(p_id); - rep_state->on_peer_change(p_id, p_connected); - } -} - -void SceneReplicationInterface::on_reset() { - for (int pid : rep_state->get_peers()) { - _free_remotes(pid); - } - rep_state->reset(); -} - -void SceneReplicationInterface::on_network_process() { - uint64_t msec = OS::get_singleton()->get_ticks_msec(); - for (int peer : rep_state->get_peers()) { - _send_sync(peer, msec); - } -} - -Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); - MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object()); - ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER); - Error err = rep_state->config_add_spawn(node, spawner); - ERR_FAIL_COND_V(err != OK, err); - const ObjectID oid = node->get_instance_id(); - if (multiplayer->has_multiplayer_peer() && spawner->is_multiplayer_authority()) { - rep_state->ensure_net_id(oid); - _update_spawn_visibility(0, oid); - } - ERR_FAIL_COND_V(err != OK, err); - return OK; -} - -Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); - MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object()); - ERR_FAIL_COND_V(!p_obj || !spawner, ERR_INVALID_PARAMETER); - // Forcibly despawn to all peers that knowns me. - int len = 0; - Error err = _make_despawn_packet(node, len); - ERR_FAIL_COND_V(err != OK, ERR_BUG); - const ObjectID oid = p_obj->get_instance_id(); - for (int pid : rep_state->get_peers()) { - if (!rep_state->is_peer_spawn(pid, oid)) { - continue; - } - _send_raw(packet_cache.ptr(), len, pid, true); - } - // Also remove spawner tracking from the replication state. - return rep_state->config_del_spawn(node, spawner); -} - -Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); - MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object()); - ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER); - - // Add to synchronizer list and setup visibility. - rep_state->config_add_sync(node, sync); - const ObjectID oid = node->get_instance_id(); - sync->connect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed), varray(oid)); - if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) { - _update_sync_visibility(0, oid); - } - - // Try to apply initial state if spawning (hack to apply if before ready). - if (pending_spawn == p_obj->get_instance_id()) { - pending_spawn = ObjectID(); // Make sure this only happens once. - const List<NodePath> props = sync->get_replication_config()->get_spawn_properties(); - Vector<Variant> vars; - vars.resize(props.size()); - int consumed; - Error err = MultiplayerAPI::decode_and_decompress_variants(vars, pending_buffer, pending_buffer_size, consumed); - ERR_FAIL_COND_V(err, err); - err = MultiplayerSynchronizer::set_state(props, node, vars); - ERR_FAIL_COND_V(err, err); - } - return OK; -} - -Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_config) { - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER); - MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object()); - ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER); - sync->disconnect("visibility_changed", callable_mp(this, &SceneReplicationInterface::_visibility_changed)); - return rep_state->config_del_sync(node, sync); -} - -void SceneReplicationInterface::_visibility_changed(int p_peer, ObjectID p_oid) { - if (rep_state->is_spawned_node(p_oid)) { - _update_spawn_visibility(p_peer, p_oid); - } - if (rep_state->is_synced_node(p_oid)) { - _update_sync_visibility(p_peer, p_oid); - } -} - -Error SceneReplicationInterface::_update_sync_visibility(int p_peer, const ObjectID &p_oid) { - MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid); - ERR_FAIL_COND_V(!sync || !sync->is_multiplayer_authority(), ERR_BUG); - bool is_visible = sync->is_visible_to(p_peer); - if (p_peer == 0) { - for (int pid : rep_state->get_peers()) { - // Might be visible to this specific peer. - is_visible = is_visible || sync->is_visible_to(pid); - if (rep_state->is_peer_sync(pid, p_oid) == is_visible) { - continue; - } - if (is_visible) { - rep_state->peer_add_sync(pid, p_oid); - } else { - rep_state->peer_del_sync(pid, p_oid); - } - } - return OK; - } else { - if (is_visible == rep_state->is_peer_sync(p_peer, p_oid)) { - return OK; - } - if (is_visible) { - return rep_state->peer_add_sync(p_peer, p_oid); - } else { - return rep_state->peer_del_sync(p_peer, p_oid); - } - } -} - -Error SceneReplicationInterface::_update_spawn_visibility(int p_peer, const ObjectID &p_oid) { - MultiplayerSpawner *spawner = rep_state->get_spawner(p_oid); - MultiplayerSynchronizer *sync = rep_state->get_synchronizer(p_oid); - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_oid)); - ERR_FAIL_COND_V(!node || !spawner || !spawner->is_multiplayer_authority(), ERR_BUG); - bool is_visible = !sync || sync->is_visible_to(p_peer); - // Spawn (and despawn) when needed. - HashSet<int> to_spawn; - HashSet<int> to_despawn; - if (p_peer) { - if (is_visible == rep_state->is_peer_spawn(p_peer, p_oid)) { - return OK; - } - if (is_visible) { - to_spawn.insert(p_peer); - } else { - to_despawn.insert(p_peer); - } - } else { - // Check visibility for each peers. - for (int pid : rep_state->get_peers()) { - bool peer_visible = is_visible || sync->is_visible_to(pid); - if (peer_visible == rep_state->is_peer_spawn(pid, p_oid)) { - continue; - } - if (peer_visible) { - to_spawn.insert(pid); - } else { - to_despawn.insert(pid); - } - } - } - if (to_spawn.size()) { - int len = 0; - _make_spawn_packet(node, len); - for (int pid : to_spawn) { - int path_id; - multiplayer->send_object_cache(spawner, pid, path_id); - _send_raw(packet_cache.ptr(), len, pid, true); - rep_state->peer_add_spawn(pid, p_oid); - } - } - if (to_despawn.size()) { - int len = 0; - _make_despawn_packet(node, len); - for (int pid : to_despawn) { - rep_state->peer_del_spawn(pid, p_oid); - _send_raw(packet_cache.ptr(), len, pid, true); - } - } - return OK; -} - -Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) { - ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED); - ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED); - -#ifdef DEBUG_ENABLED - multiplayer->profile_bandwidth("out", p_size); -#endif - - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - peer->set_target_peer(p_peer); - peer->set_transfer_channel(0); - peer->set_transfer_mode(p_reliable ? Multiplayer::TRANSFER_MODE_RELIABLE : Multiplayer::TRANSFER_MODE_UNRELIABLE); - return peer->put_packet(p_buffer, p_size); -} - -Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, int &r_len) { - ERR_FAIL_COND_V(!multiplayer, ERR_BUG); - - const ObjectID oid = p_node->get_instance_id(); - MultiplayerSpawner *spawner = rep_state->get_spawner(oid); - ERR_FAIL_COND_V(!spawner || !p_node, ERR_BUG); - - uint32_t nid = rep_state->get_net_id(oid); - ERR_FAIL_COND_V(!nid, ERR_UNCONFIGURED); - - // Prepare custom arg and scene_id - uint8_t scene_id = spawner->find_spawnable_scene_index_from_object(oid); - bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID; - Variant spawn_arg = spawner->get_spawn_argument(oid); - int spawn_arg_size = 0; - if (is_custom) { - Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, nullptr, spawn_arg_size, false); - ERR_FAIL_COND_V(err, err); - } - - // Prepare spawn state. - int state_size = 0; - Vector<Variant> state_vars; - Vector<const Variant *> state_varp; - MultiplayerSynchronizer *synchronizer = rep_state->get_synchronizer(oid); - if (synchronizer) { - ERR_FAIL_COND_V(synchronizer->get_replication_config().is_null(), ERR_BUG); - const List<NodePath> props = synchronizer->get_replication_config()->get_spawn_properties(); - Error err = MultiplayerSynchronizer::get_state(props, p_node, state_vars, state_varp); - ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state."); - err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), nullptr, state_size); - ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state."); - } - - // Encode scene ID, path ID, net ID, node name. - int path_id = multiplayer->make_object_cache(spawner); - CharString cname = p_node->get_name().operator String().utf8(); - int nlen = encode_cstring(cname.get_data(), nullptr); - MAKE_ROOM(1 + 1 + 4 + 4 + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = (uint8_t)MultiplayerAPI::NETWORK_COMMAND_SPAWN; - ptr[1] = scene_id; - int ofs = 2; - ofs += encode_uint32(path_id, &ptr[ofs]); - ofs += encode_uint32(nid, &ptr[ofs]); - ofs += encode_uint32(nlen, &ptr[ofs]); - ofs += encode_cstring(cname.get_data(), &ptr[ofs]); - // Write args - if (is_custom) { - ofs += encode_uint32(spawn_arg_size, &ptr[ofs]); - Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, &ptr[ofs], spawn_arg_size, false); - ERR_FAIL_COND_V(err, err); - ofs += spawn_arg_size; - } - // Write state. - if (state_size) { - Error err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), &ptr[ofs], state_size); - ERR_FAIL_COND_V(err, err); - ofs += state_size; - } - r_len = ofs; - return OK; -} - -Error SceneReplicationInterface::_make_despawn_packet(Node *p_node, int &r_len) { - const ObjectID oid = p_node->get_instance_id(); - MAKE_ROOM(5); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = (uint8_t)MultiplayerAPI::NETWORK_COMMAND_DESPAWN; - int ofs = 1; - uint32_t nid = rep_state->get_net_id(oid); - ofs += encode_uint32(nid, &ptr[ofs]); - r_len = ofs; - return OK; -} - -Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { - ERR_FAIL_COND_V_MSG(p_buffer_len < 14, ERR_INVALID_DATA, "Invalid spawn packet received"); - int ofs = 1; // The spawn/despawn command. - uint8_t scene_id = p_buffer[ofs]; - ofs += 1; - uint32_t node_target = decode_uint32(&p_buffer[ofs]); - ofs += 4; - MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(multiplayer->get_cached_object(p_from, node_target)); - ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST); - ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED); - - uint32_t net_id = decode_uint32(&p_buffer[ofs]); - ofs += 4; - uint32_t name_len = decode_uint32(&p_buffer[ofs]); - ofs += 4; - ERR_FAIL_COND_V_MSG(name_len > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA, vformat("Invalid spawn packet size: %d, wants: %d", p_buffer_len, ofs + name_len)); - ERR_FAIL_COND_V_MSG(name_len < 1, ERR_INVALID_DATA, "Zero spawn name size."); - - // We need to make sure no trickery happens here, but we want to allow autogenerated ("@") node names. - const String name = String::utf8((const char *)&p_buffer[ofs], name_len); - ERR_FAIL_COND_V_MSG(name.validate_node_name() != name, ERR_INVALID_DATA, vformat("Invalid node name received: '%s'. Make sure to add nodes via 'add_child(node, true)' remotely.", name)); - ofs += name_len; - - // Check that we can spawn. - Node *parent = spawner->get_node_or_null(spawner->get_spawn_path()); - ERR_FAIL_COND_V(!parent, ERR_UNCONFIGURED); - ERR_FAIL_COND_V(parent->has_node(name), ERR_INVALID_DATA); - - Node *node = nullptr; - if (scene_id == MultiplayerSpawner::INVALID_ID) { - // Custom spawn. - ERR_FAIL_COND_V(p_buffer_len - ofs < 4, ERR_INVALID_DATA); - uint32_t arg_size = decode_uint32(&p_buffer[ofs]); - ofs += 4; - ERR_FAIL_COND_V(arg_size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA); - Variant v; - Error err = MultiplayerAPI::decode_and_decompress_variant(v, &p_buffer[ofs], arg_size, nullptr, false); - ERR_FAIL_COND_V(err != OK, err); - ofs += arg_size; - node = spawner->instantiate_custom(v); - } else { - // Scene based spawn. - node = spawner->instantiate_scene(scene_id); - } - ERR_FAIL_COND_V(!node, ERR_UNAUTHORIZED); - node->set_name(name); - rep_state->peer_add_remote(p_from, net_id, node, spawner); - // The initial state will be applied during the sync config (i.e. before _ready). - int state_len = p_buffer_len - ofs; - if (state_len) { - pending_spawn = node->get_instance_id(); - pending_buffer = &p_buffer[ofs]; - pending_buffer_size = state_len; - } - parent->add_child(node); - pending_spawn = ObjectID(); - pending_buffer = nullptr; - pending_buffer_size = 0; - return OK; -} - -Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { - ERR_FAIL_COND_V_MSG(p_buffer_len < 5, ERR_INVALID_DATA, "Invalid spawn packet received"); - int ofs = 1; // The spawn/despawn command. - uint32_t net_id = decode_uint32(&p_buffer[ofs]); - ofs += 4; - Node *node = nullptr; - Error err = rep_state->peer_del_remote(p_from, net_id, &node); - ERR_FAIL_COND_V(err != OK, err); - ERR_FAIL_COND_V(!node, ERR_BUG); - if (node->get_parent() != nullptr) { - node->get_parent()->remove_child(node); - } - node->queue_delete(); - return OK; -} - -void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) { - const HashSet<ObjectID> &to_sync = rep_state->get_peer_sync_nodes(p_peer); - if (to_sync.is_empty()) { - return; - } - MAKE_ROOM(sync_mtu); - uint8_t *ptr = packet_cache.ptrw(); - ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC; - int ofs = 1; - ofs += encode_uint16(rep_state->peer_sync_next(p_peer), &ptr[1]); - // Can only send updates for already notified nodes. - // This is a lazy implementation, we could optimize much more here with by grouping by replication config. - for (const ObjectID &oid : to_sync) { - if (!rep_state->update_sync_time(oid, p_msec)) { - continue; // nothing to sync. - } - MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid); - ERR_CONTINUE(!sync || !sync->get_replication_config().is_valid()); - Node *node = rep_state->get_node(oid); - ERR_CONTINUE(!node); - uint32_t net_id = rep_state->get_net_id(oid); - if (net_id == 0 || (net_id & 0x80000000)) { - int path_id = 0; - bool verified = multiplayer->send_object_cache(sync, p_peer, path_id); - ERR_CONTINUE_MSG(path_id < 0, "This should never happen!"); - if (net_id == 0) { - // First time path based ID. - net_id = path_id | 0x80000000; - rep_state->set_net_id(oid, net_id | 0x80000000); - } - if (!verified) { - // The path based sync is not yet confirmed, skipping. - continue; - } - } - int size; - Vector<Variant> vars; - Vector<const Variant *> varp; - const List<NodePath> props = sync->get_replication_config()->get_sync_properties(); - Error err = MultiplayerSynchronizer::get_state(props, node, vars, varp); - ERR_CONTINUE_MSG(err != OK, "Unable to retrieve sync state."); - err = MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), nullptr, size); - ERR_CONTINUE_MSG(err != OK, "Unable to encode sync state."); - // TODO Handle single state above MTU. - ERR_CONTINUE_MSG(size > 3 + 4 + 4 + sync_mtu, vformat("Node states bigger then MTU will not be sent (%d > %d): %s", size, sync_mtu, node->get_path())); - if (ofs + 4 + 4 + size > sync_mtu) { - // Send what we got, and reset write. - _send_raw(packet_cache.ptr(), ofs, p_peer, false); - ofs = 3; - } - if (size) { - ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]); - ofs += encode_uint32(size, &ptr[ofs]); - MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size); - ofs += size; - } - } - if (ofs > 3) { - // Got some left over to send. - _send_raw(packet_cache.ptr(), ofs, p_peer, false); - } -} - -Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { - ERR_FAIL_COND_V_MSG(p_buffer_len < 11, ERR_INVALID_DATA, "Invalid sync packet received"); - uint16_t time = decode_uint16(&p_buffer[1]); - int ofs = 3; - rep_state->peer_sync_recv(p_from, time); - while (ofs + 8 < p_buffer_len) { - uint32_t net_id = decode_uint32(&p_buffer[ofs]); - ofs += 4; - uint32_t size = decode_uint32(&p_buffer[ofs]); - ofs += 4; - Node *node = nullptr; - if (net_id & 0x80000000) { - MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_cached_object(p_from, net_id & 0x7FFFFFFF)); - ERR_FAIL_COND_V(!sync || sync->get_multiplayer_authority() != p_from, ERR_UNAUTHORIZED); - node = sync->get_node(sync->get_root_path()); - } else { - node = rep_state->peer_get_remote(p_from, net_id); - } - if (!node) { - // Not received yet. - ofs += size; - continue; - } - const ObjectID oid = node->get_instance_id(); - if (!rep_state->update_last_node_sync(oid, time)) { - // State is too old. - ofs += size; - continue; - } - MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid); - ERR_FAIL_COND_V(!sync, ERR_BUG); - ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_BUG); - const List<NodePath> props = sync->get_replication_config()->get_sync_properties(); - Vector<Variant> vars; - vars.resize(props.size()); - int consumed; - Error err = MultiplayerAPI::decode_and_decompress_variants(vars, &p_buffer[ofs], size, consumed); - ERR_FAIL_COND_V(err, err); - err = MultiplayerSynchronizer::set_state(props, node, vars); - ERR_FAIL_COND_V(err, err); - ofs += size; - } - return OK; -} diff --git a/scene/multiplayer/scene_replication_interface.h b/scene/multiplayer/scene_replication_interface.h deleted file mode 100644 index 9ddab2d383..0000000000 --- a/scene/multiplayer/scene_replication_interface.h +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************/ -/* scene_replication_interface.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 SCENE_REPLICATION_INTERFACE_H -#define SCENE_REPLICATION_INTERFACE_H - -#include "core/multiplayer/multiplayer_api.h" - -#include "scene/multiplayer/scene_replication_state.h" - -class SceneReplicationInterface : public MultiplayerReplicationInterface { - GDCLASS(SceneReplicationInterface, MultiplayerReplicationInterface); - -private: - void _send_sync(int p_peer, uint64_t p_msec); - Error _make_spawn_packet(Node *p_node, int &r_len); - Error _make_despawn_packet(Node *p_node, int &r_len); - Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable); - - void _visibility_changed(int p_peer, ObjectID p_oid); - Error _update_sync_visibility(int p_peer, const ObjectID &p_oid); - Error _update_spawn_visibility(int p_peer, const ObjectID &p_oid); - void _free_remotes(int p_peer); - - Ref<SceneReplicationState> rep_state; - MultiplayerAPI *multiplayer = nullptr; - PackedByteArray packet_cache; - int sync_mtu = 1350; // Highly dependent on underlying protocol. - - // An hack to apply the initial state before ready. - ObjectID pending_spawn; - const uint8_t *pending_buffer = nullptr; - int pending_buffer_size = 0; - -protected: - static MultiplayerReplicationInterface *_create(MultiplayerAPI *p_multiplayer); - -public: - static void make_default(); - - virtual void on_reset() override; - virtual void on_peer_change(int p_id, bool p_connected) override; - - virtual Error on_spawn(Object *p_obj, Variant p_config) override; - virtual Error on_despawn(Object *p_obj, Variant p_config) override; - virtual Error on_replication_start(Object *p_obj, Variant p_config) override; - virtual Error on_replication_stop(Object *p_obj, Variant p_config) override; - virtual void on_network_process() override; - - virtual Error on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override; - virtual Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override; - virtual Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override; - - SceneReplicationInterface(MultiplayerAPI *p_multiplayer) { - rep_state.instantiate(); - multiplayer = p_multiplayer; - } -}; - -#endif // SCENE_REPLICATION_INTERFACE_H diff --git a/scene/multiplayer/scene_replication_state.cpp b/scene/multiplayer/scene_replication_state.cpp deleted file mode 100644 index f6a51ff9c7..0000000000 --- a/scene/multiplayer/scene_replication_state.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/*************************************************************************/ -/* scene_replication_state.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 "scene/multiplayer/scene_replication_state.h" - -#include "core/multiplayer/multiplayer_api.h" -#include "scene/multiplayer/multiplayer_spawner.h" -#include "scene/multiplayer/multiplayer_synchronizer.h" -#include "scene/scene_string_names.h" - -SceneReplicationState::TrackedNode &SceneReplicationState::_track(const ObjectID &p_id) { - if (!tracked_nodes.has(p_id)) { - tracked_nodes[p_id] = TrackedNode(p_id); - Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id)); - node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack), varray(p_id), Node::CONNECT_ONESHOT); - } - return tracked_nodes[p_id]; -} - -void SceneReplicationState::_untrack(const ObjectID &p_id) { - if (tracked_nodes.has(p_id)) { - uint32_t net_id = tracked_nodes[p_id].net_id; - uint32_t peer = tracked_nodes[p_id].remote_peer; - tracked_nodes.erase(p_id); - // If it was spawned by a remote, remove it from the received nodes. - if (peer && peers_info.has(peer)) { - peers_info[peer].recv_nodes.erase(net_id); - } - // If we spawned or synced it, we need to remove it from any peer it was sent to. - if (net_id || peer == 0) { - for (KeyValue<int, PeerInfo> &E : peers_info) { - E.value.sync_nodes.erase(p_id); - E.value.spawn_nodes.erase(p_id); - } - } - } -} - -const HashMap<uint32_t, ObjectID> SceneReplicationState::peer_get_remotes(int p_peer) const { - return peers_info.has(p_peer) ? peers_info[p_peer].recv_nodes : HashMap<uint32_t, ObjectID>(); -} - -bool SceneReplicationState::update_last_node_sync(const ObjectID &p_id, uint16_t p_time) { - TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND_V(!tnode, false); - if (p_time <= tnode->last_sync && tnode->last_sync - p_time < 32767) { - return false; - } - tnode->last_sync = p_time; - return true; -} - -bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_msec) { - TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND_V(!tnode, false); - MultiplayerSynchronizer *sync = get_synchronizer(p_id); - if (!sync) { - return false; - } - if (tnode->last_sync_msec == p_msec) { - return true; - } - if (p_msec >= tnode->last_sync_msec + sync->get_replication_interval_msec()) { - tnode->last_sync_msec = p_msec; - return true; - } - return false; -} - -uint32_t SceneReplicationState::get_net_id(const ObjectID &p_id) const { - const TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND_V(!tnode, 0); - return tnode->net_id; -} - -void SceneReplicationState::set_net_id(const ObjectID &p_id, uint32_t p_net_id) { - TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND(!tnode); - tnode->net_id = p_net_id; -} - -uint32_t SceneReplicationState::ensure_net_id(const ObjectID &p_id) { - TrackedNode *tnode = tracked_nodes.getptr(p_id); - ERR_FAIL_COND_V(!tnode, 0); - if (tnode->net_id == 0) { - tnode->net_id = ++last_net_id; - } - return tnode->net_id; -} - -void SceneReplicationState::on_peer_change(int p_peer, bool p_connected) { - if (p_connected) { - peers_info[p_peer] = PeerInfo(); - known_peers.insert(p_peer); - } else { - peers_info.erase(p_peer); - known_peers.erase(p_peer); - } -} - -void SceneReplicationState::reset() { - peers_info.clear(); - known_peers.clear(); - // Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned. - for (KeyValue<ObjectID, TrackedNode> &E : tracked_nodes) { - TrackedNode &tobj = E.value; - tobj.net_id = 0; - tobj.remote_peer = 0; - tobj.last_sync = 0; - } -} - -Error SceneReplicationState::config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner) { - const ObjectID oid = p_node->get_instance_id(); - TrackedNode &tobj = _track(oid); - ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE); - tobj.spawner = p_spawner->get_instance_id(); - spawned_nodes.insert(oid); - return OK; -} - -Error SceneReplicationState::config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner) { - const ObjectID oid = p_node->get_instance_id(); - ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER); - TrackedNode &tobj = _track(oid); - ERR_FAIL_COND_V(tobj.spawner != p_spawner->get_instance_id(), ERR_INVALID_PARAMETER); - tobj.spawner = ObjectID(); - spawned_nodes.erase(oid); - for (KeyValue<int, PeerInfo> &E : peers_info) { - E.value.spawn_nodes.erase(oid); - } - return OK; -} - -Error SceneReplicationState::config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync) { - const ObjectID oid = p_node->get_instance_id(); - TrackedNode &tobj = _track(oid); - ERR_FAIL_COND_V(tobj.synchronizer != ObjectID(), ERR_ALREADY_IN_USE); - tobj.synchronizer = p_sync->get_instance_id(); - synced_nodes.insert(oid); - return OK; -} - -Error SceneReplicationState::config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync) { - const ObjectID oid = p_node->get_instance_id(); - ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER); - TrackedNode &tobj = _track(oid); - ERR_FAIL_COND_V(tobj.synchronizer != p_sync->get_instance_id(), ERR_INVALID_PARAMETER); - tobj.synchronizer = ObjectID(); - synced_nodes.erase(oid); - for (KeyValue<int, PeerInfo> &E : peers_info) { - E.value.sync_nodes.erase(oid); - } - return OK; -} - -Error SceneReplicationState::peer_add_sync(int p_peer, const ObjectID &p_id) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); - peers_info[p_peer].sync_nodes.insert(p_id); - return OK; -} - -Error SceneReplicationState::peer_del_sync(int p_peer, const ObjectID &p_id) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); - peers_info[p_peer].sync_nodes.erase(p_id); - return OK; -} - -const HashSet<ObjectID> SceneReplicationState::get_peer_sync_nodes(int p_peer) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>()); - return peers_info[p_peer].sync_nodes; -} - -bool SceneReplicationState::is_peer_sync(int p_peer, const ObjectID &p_id) const { - ERR_FAIL_COND_V(!peers_info.has(p_peer), false); - return peers_info[p_peer].sync_nodes.has(p_id); -} - -Error SceneReplicationState::peer_add_spawn(int p_peer, const ObjectID &p_id) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); - peers_info[p_peer].spawn_nodes.insert(p_id); - return OK; -} - -Error SceneReplicationState::peer_del_spawn(int p_peer, const ObjectID &p_id) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER); - peers_info[p_peer].spawn_nodes.erase(p_id); - return OK; -} - -const HashSet<ObjectID> SceneReplicationState::get_peer_spawn_nodes(int p_peer) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), HashSet<ObjectID>()); - return peers_info[p_peer].spawn_nodes; -} - -bool SceneReplicationState::is_peer_spawn(int p_peer, const ObjectID &p_id) const { - ERR_FAIL_COND_V(!peers_info.has(p_peer), false); - return peers_info[p_peer].spawn_nodes.has(p_id); -} - -Node *SceneReplicationState::peer_get_remote(int p_peer, uint32_t p_net_id) { - PeerInfo *info = peers_info.getptr(p_peer); - return info && info->recv_nodes.has(p_net_id) ? Object::cast_to<Node>(ObjectDB::get_instance(info->recv_nodes[p_net_id])) : nullptr; -} - -Error SceneReplicationState::peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner) { - ERR_FAIL_COND_V(!p_node || !p_spawner, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAVAILABLE); - PeerInfo &pinfo = peers_info[p_peer]; - ObjectID oid = p_node->get_instance_id(); - TrackedNode &tobj = _track(oid); - tobj.spawner = p_spawner->get_instance_id(); - tobj.net_id = p_net_id; - tobj.remote_peer = p_peer; - tobj.last_sync = pinfo.last_recv_sync; - // Also track as a remote. - ERR_FAIL_COND_V(pinfo.recv_nodes.has(p_net_id), ERR_ALREADY_IN_USE); - pinfo.recv_nodes[p_net_id] = oid; - return OK; -} - -Error SceneReplicationState::peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAUTHORIZED); - PeerInfo &info = peers_info[p_peer]; - ERR_FAIL_COND_V(!info.recv_nodes.has(p_net_id), ERR_UNAUTHORIZED); - *r_node = Object::cast_to<Node>(ObjectDB::get_instance(info.recv_nodes[p_net_id])); - info.recv_nodes.erase(p_net_id); - return OK; -} - -uint16_t SceneReplicationState::peer_sync_next(int p_peer) { - ERR_FAIL_COND_V(!peers_info.has(p_peer), 0); - PeerInfo &info = peers_info[p_peer]; - return ++info.last_sent_sync; -} - -void SceneReplicationState::peer_sync_recv(int p_peer, uint16_t p_time) { - ERR_FAIL_COND(!peers_info.has(p_peer)); - peers_info[p_peer].last_recv_sync = p_time; -} diff --git a/scene/multiplayer/scene_replication_state.h b/scene/multiplayer/scene_replication_state.h deleted file mode 100644 index 01b70b11b8..0000000000 --- a/scene/multiplayer/scene_replication_state.h +++ /dev/null @@ -1,132 +0,0 @@ -/*************************************************************************/ -/* scene_replication_state.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 SCENE_REPLICATION_STATE_H -#define SCENE_REPLICATION_STATE_H - -#include "core/object/ref_counted.h" - -class MultiplayerSpawner; -class MultiplayerSynchronizer; -class Node; - -class SceneReplicationState : public RefCounted { -private: - struct TrackedNode { - ObjectID id; - uint32_t net_id = 0; - uint32_t remote_peer = 0; - ObjectID spawner; - ObjectID synchronizer; - uint16_t last_sync = 0; - uint64_t last_sync_msec = 0; - - bool operator==(const ObjectID &p_other) { return id == p_other; } - - Node *get_node() const { return id.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(id)) : nullptr; } - MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; } - MultiplayerSynchronizer *get_synchronizer() const { return synchronizer.is_valid() ? Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(synchronizer)) : nullptr; } - TrackedNode() {} - TrackedNode(const ObjectID &p_id) { id = p_id; } - TrackedNode(const ObjectID &p_id, uint32_t p_net_id) { - id = p_id; - net_id = p_net_id; - } - }; - - struct PeerInfo { - HashSet<ObjectID> sync_nodes; - HashSet<ObjectID> spawn_nodes; - HashMap<uint32_t, ObjectID> recv_nodes; - uint16_t last_sent_sync = 0; - uint16_t last_recv_sync = 0; - }; - - HashSet<int> known_peers; - uint32_t last_net_id = 0; - HashMap<ObjectID, TrackedNode> tracked_nodes; - HashMap<int, PeerInfo> peers_info; - HashSet<ObjectID> spawned_nodes; - HashSet<ObjectID> synced_nodes; - - TrackedNode &_track(const ObjectID &p_id); - void _untrack(const ObjectID &p_id); - bool is_tracked(const ObjectID &p_id) const { return tracked_nodes.has(p_id); } - -public: - const HashSet<int> get_peers() const { return known_peers; } - const HashSet<ObjectID> &get_spawned_nodes() const { return spawned_nodes; } - bool is_spawned_node(const ObjectID &p_id) const { return spawned_nodes.has(p_id); } - const HashSet<ObjectID> &get_synced_nodes() const { return synced_nodes; } - bool is_synced_node(const ObjectID &p_id) const { return synced_nodes.has(p_id); } - - MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; } - MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; } - Node *get_node(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_node() : nullptr; } - bool update_last_node_sync(const ObjectID &p_id, uint16_t p_time); - bool update_sync_time(const ObjectID &p_id, uint64_t p_msec); - - uint32_t get_net_id(const ObjectID &p_id) const; - void set_net_id(const ObjectID &p_id, uint32_t p_net_id); - uint32_t ensure_net_id(const ObjectID &p_id); - - void reset(); - void on_peer_change(int p_peer, bool p_connected); - - Error config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner); - Error config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner); - - Error config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync); - Error config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync); - - Error peer_add_sync(int p_peer, const ObjectID &p_id); - Error peer_del_sync(int p_peer, const ObjectID &p_id); - - const HashSet<ObjectID> get_peer_sync_nodes(int p_peer); - bool is_peer_sync(int p_peer, const ObjectID &p_id) const; - - Error peer_add_spawn(int p_peer, const ObjectID &p_id); - Error peer_del_spawn(int p_peer, const ObjectID &p_id); - - const HashSet<ObjectID> get_peer_spawn_nodes(int p_peer); - bool is_peer_spawn(int p_peer, const ObjectID &p_id) const; - - const HashMap<uint32_t, ObjectID> peer_get_remotes(int p_peer) const; - Node *peer_get_remote(int p_peer, uint32_t p_net_id); - Error peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner); - Error peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node); - - uint16_t peer_sync_next(int p_peer); - void peer_sync_recv(int p_peer, uint16_t p_time); - - SceneReplicationState() {} -}; - -#endif // SCENE_REPLICATION_STATE_H diff --git a/scene/multiplayer/scene_rpc_interface.cpp b/scene/multiplayer/scene_rpc_interface.cpp deleted file mode 100644 index 144a10c665..0000000000 --- a/scene/multiplayer/scene_rpc_interface.cpp +++ /dev/null @@ -1,509 +0,0 @@ -/*************************************************************************/ -/* scene_rpc_interface.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 "scene/multiplayer/scene_rpc_interface.h" - -#include "core/debugger/engine_debugger.h" -#include "core/io/marshalls.h" -#include "core/multiplayer/multiplayer_api.h" -#include "scene/main/node.h" -#include "scene/main/window.h" - -MultiplayerRPCInterface *SceneRPCInterface::_create(MultiplayerAPI *p_multiplayer) { - return memnew(SceneRPCInterface(p_multiplayer)); -} - -void SceneRPCInterface::make_default() { - MultiplayerAPI::create_default_rpc_interface = _create; -} - -#ifdef DEBUG_ENABLED -_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) { - if (EngineDebugger::is_profiling("rpc")) { - Array values; - values.push_back(p_id); - values.push_back(p_what); - EngineDebugger::profiler_add_frame_data("rpc", values); - } -} -#else -_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {} -#endif - -// Returns the packet size stripping the node path added when the node is not yet cached. -int get_packet_len(uint32_t p_node_target, int p_packet_len) { - if (p_node_target & 0x80000000) { - int ofs = p_node_target & 0x7FFFFFFF; - return p_packet_len - (p_packet_len - ofs); - } else { - return p_packet_len; - } -} - -const Multiplayer::RPCConfig _get_rpc_config(const Node *p_node, const StringName &p_method, uint16_t &r_id) { - const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods(); - for (int i = 0; i < node_config.size(); i++) { - if (node_config[i].name == p_method) { - r_id = ((uint16_t)i) | (1 << 15); - return node_config[i]; - } - } - if (p_node->get_script_instance()) { - const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods(); - for (int i = 0; i < script_config.size(); i++) { - if (script_config[i].name == p_method) { - r_id = (uint16_t)i; - return script_config[i]; - } - } - } - return Multiplayer::RPCConfig(); -} - -const Multiplayer::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_id) { - Vector<Multiplayer::RPCConfig> config; - uint16_t id = p_id; - if (id & (1 << 15)) { - id = id & ~(1 << 15); - config = p_node->get_node_rpc_methods(); - } else if (p_node->get_script_instance()) { - config = p_node->get_script_instance()->get_rpc_methods(); - } - if (id < config.size()) { - return config[id]; - } - return Multiplayer::RPCConfig(); -} - -_FORCE_INLINE_ bool _can_call_mode(Node *p_node, Multiplayer::RPCMode mode, int p_remote_id) { - switch (mode) { - case Multiplayer::RPC_MODE_DISABLED: { - return false; - } break; - case Multiplayer::RPC_MODE_ANY_PEER: { - return true; - } break; - case Multiplayer::RPC_MODE_AUTHORITY: { - return !p_node->is_multiplayer_authority() && p_remote_id == p_node->get_multiplayer_authority(); - } break; - } - - return false; -} - -String SceneRPCInterface::get_rpc_md5(const Object *p_obj) const { - const Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND_V(!node, ""); - String rpc_list; - const Vector<Multiplayer::RPCConfig> node_config = node->get_node_rpc_methods(); - for (int i = 0; i < node_config.size(); i++) { - rpc_list += String(node_config[i].name); - } - if (node->get_script_instance()) { - const Vector<Multiplayer::RPCConfig> script_config = node->get_script_instance()->get_rpc_methods(); - for (int i = 0; i < script_config.size(); i++) { - rpc_list += String(script_config[i].name); - } - } - return rpc_list.md5_text(); -} - -Node *SceneRPCInterface::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { - Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path()); - ERR_FAIL_COND_V(!root_node, nullptr); - Node *node = nullptr; - - if (p_node_target & 0x80000000) { - // Use full path (not cached yet). - int ofs = p_node_target & 0x7FFFFFFF; - - ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared."); - - String paths; - paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); - - NodePath np = paths; - - node = root_node->get_node(np); - - if (!node) { - ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); - } - return node; - } else { - // Use cached path. - return Object::cast_to<Node>(multiplayer->get_cached_object(p_from, p_node_target)); - } -} - -void SceneRPCInterface::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) { - // Extract packet meta - int packet_min_size = 1; - int name_id_offset = 1; - ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); - // Compute the meta size, which depends on the compression level. - int node_id_compression = (p_packet[0] & NODE_ID_COMPRESSION_FLAG) >> NODE_ID_COMPRESSION_SHIFT; - int name_id_compression = (p_packet[0] & NAME_ID_COMPRESSION_FLAG) >> NAME_ID_COMPRESSION_SHIFT; - - switch (node_id_compression) { - case NETWORK_NODE_ID_COMPRESSION_8: - packet_min_size += 1; - name_id_offset += 1; - break; - case NETWORK_NODE_ID_COMPRESSION_16: - packet_min_size += 2; - name_id_offset += 2; - break; - case NETWORK_NODE_ID_COMPRESSION_32: - packet_min_size += 4; - name_id_offset += 4; - break; - default: - ERR_FAIL_MSG("Was not possible to extract the node id compression mode."); - } - switch (name_id_compression) { - case NETWORK_NAME_ID_COMPRESSION_8: - packet_min_size += 1; - break; - case NETWORK_NAME_ID_COMPRESSION_16: - packet_min_size += 2; - break; - default: - ERR_FAIL_MSG("Was not possible to extract the name id compression mode."); - } - ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); - - uint32_t node_target = 0; - switch (node_id_compression) { - case NETWORK_NODE_ID_COMPRESSION_8: - node_target = p_packet[1]; - break; - case NETWORK_NODE_ID_COMPRESSION_16: - node_target = decode_uint16(p_packet + 1); - break; - case NETWORK_NODE_ID_COMPRESSION_32: - node_target = decode_uint32(p_packet + 1); - break; - default: - // Unreachable, checked before. - CRASH_NOW(); - } - - Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len); - ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found."); - - uint16_t name_id = 0; - switch (name_id_compression) { - case NETWORK_NAME_ID_COMPRESSION_8: - name_id = p_packet[name_id_offset]; - break; - case NETWORK_NAME_ID_COMPRESSION_16: - name_id = decode_uint16(p_packet + name_id_offset); - break; - default: - // Unreachable, checked before. - CRASH_NOW(); - } - - const int packet_len = get_packet_len(node_target, p_packet_len); - _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); -} - -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) { - ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small."); - - // Check that remote can call the RPC on this node. - const Multiplayer::RPCConfig config = _get_rpc_config_by_id(p_node, p_rpc_method_id); - ERR_FAIL_COND(config.name == StringName()); - - bool can_call = _can_call_mode(p_node, config.rpc_mode, p_from); - 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()) + "."); - - int argc = 0; - - const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG; - if (byte_only_or_no_args) { - if (p_offset < p_packet_len) { - // This packet contains only bytes. - argc = 1; - } - } else { - // Normal variant, takes the argument count from the packet. - ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); - argc = p_packet[p_offset]; - p_offset += 1; - } - - Vector<Variant> args; - Vector<const Variant *> argp; - args.resize(argc); - argp.resize(argc); - -#ifdef DEBUG_ENABLED - _profile_node_data("rpc_in", p_node->get_instance_id()); -#endif - - int out; - 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()); - for (int i = 0; i < argc; i++) { - argp.write[i] = &args[i]; - } - - Callable::CallError ce; - - p_node->callp(config.name, (const Variant **)argp.ptr(), argc, ce); - if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(p_node, config.name, (const Variant **)argp.ptr(), argc, ce); - error = "RPC - " + error; - ERR_PRINT(error); - } -} - -void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer."); - - ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to call RPC while multiplayer peer is not connected yet."); - - ERR_FAIL_COND_MSG(peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to call RPC while multiplayer peer is disconnected."); - - ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments (>255)."); - - if (p_to != 0 && !multiplayer->get_connected_peers().has(ABS(p_to))) { - ERR_FAIL_COND_MSG(p_to == peer->get_unique_id(), "Attempt to call RPC on yourself! Peer unique ID: " + itos(peer->get_unique_id()) + "."); - - ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + "."); - } - - // See if all peers have cached path (if so, call can be fast). - int psc_id; - const bool has_all_peers = multiplayer->send_object_cache(p_from, p_to, psc_id); - - // Create base packet, lots of hardcode because it must be tight. - - int ofs = 0; - -#define MAKE_ROOM(m_amount) \ - if (packet_cache.size() < m_amount) \ - packet_cache.resize(m_amount); - - // Encode meta. - uint8_t command_type = MultiplayerAPI::NETWORK_COMMAND_REMOTE_CALL; - uint8_t node_id_compression = UINT8_MAX; - uint8_t name_id_compression = UINT8_MAX; - bool byte_only_or_no_args = false; - - MAKE_ROOM(1); - // The meta is composed along the way, so just set 0 for now. - packet_cache.write[0] = 0; - ofs += 1; - - // Encode Node ID. - if (has_all_peers) { - // Compress the node ID only if all the target peers already know it. - if (psc_id >= 0 && psc_id <= 255) { - // We can encode the id in 1 byte - node_id_compression = NETWORK_NODE_ID_COMPRESSION_8; - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = static_cast<uint8_t>(psc_id); - ofs += 1; - } else if (psc_id >= 0 && psc_id <= 65535) { - // We can encode the id in 2 bytes - node_id_compression = NETWORK_NODE_ID_COMPRESSION_16; - MAKE_ROOM(ofs + 2); - encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs])); - ofs += 2; - } else { - // Too big, let's use 4 bytes. - node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; - MAKE_ROOM(ofs + 4); - encode_uint32(psc_id, &(packet_cache.write[ofs])); - ofs += 4; - } - } else { - // The targets don't know the node yet, so we need to use 32 bits int. - node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; - MAKE_ROOM(ofs + 4); - encode_uint32(psc_id, &(packet_cache.write[ofs])); - ofs += 4; - } - - // Encode method ID - if (p_rpc_id <= UINT8_MAX) { - // The ID fits in 1 byte - name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = static_cast<uint8_t>(p_rpc_id); - ofs += 1; - } else { - // The ID is larger, let's use 2 bytes - name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; - MAKE_ROOM(ofs + 2); - encode_uint16(p_rpc_id, &(packet_cache.write[ofs])); - ofs += 2; - } - - int len; - Error err = MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, nullptr, len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed()); - ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC arguments. THIS IS LIKELY A BUG IN THE ENGINE!"); - if (byte_only_or_no_args) { - MAKE_ROOM(ofs + len); - } else { - MAKE_ROOM(ofs + 1 + len); - packet_cache.write[ofs] = p_argcount; - ofs += 1; - } - if (len) { - MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, &packet_cache.write[ofs], len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed()); - ofs += len; - } - - ERR_FAIL_COND(command_type > 7); - ERR_FAIL_COND(node_id_compression > 3); - ERR_FAIL_COND(name_id_compression > 1); - - // We can now set the meta - 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); - -#ifdef DEBUG_ENABLED - multiplayer->profile_bandwidth("out", ofs); -#endif - - // Take chance and set transfer mode, since all send methods will use it. - peer->set_transfer_channel(p_config.channel); - peer->set_transfer_mode(p_config.transfer_mode); - - if (has_all_peers) { - // They all have verified paths, so send fast. - peer->set_target_peer(p_to); // To all of you. - peer->put_packet(packet_cache.ptr(), ofs); // A message with love. - } else { - // Unreachable because the node ID is never compressed if the peers doesn't know it. - CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); - - // Not all verified path, so send one by one. - - // Append path at the end, since we will need it for some packets. - NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path()); - CharString pname = String(from_path).utf8(); - int path_len = encode_cstring(pname.get_data(), nullptr); - MAKE_ROOM(ofs + path_len); - encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); - - for (const int &P : multiplayer->get_connected_peers()) { - if (p_to < 0 && P == -p_to) { - continue; // Continue, excluded. - } - - if (p_to > 0 && P != p_to) { - continue; // Continue, not for this peer. - } - - bool confirmed = multiplayer->is_cache_confirmed(from_path, P); - - peer->set_target_peer(P); // To this one specifically. - - if (confirmed) { - // This one confirmed path, so use id. - encode_uint32(psc_id, &(packet_cache.write[1])); - peer->put_packet(packet_cache.ptr(), ofs); - } else { - // This one did not confirm path yet, so use entire path (sorry!). - encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. - peer->put_packet(packet_cache.ptr(), ofs + path_len); - } - } - } -} - -void SceneRPCInterface::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) { - Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - ERR_FAIL_COND_MSG(!peer.is_valid(), "Trying to call an RPC while no multiplayer peer is active."); - Node *node = Object::cast_to<Node>(p_obj); - ERR_FAIL_COND(!node); - ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); - ERR_FAIL_COND_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a multiplayer peer which is not connected."); - - int node_id = peer->get_unique_id(); - bool call_local_native = false; - bool call_local_script = false; - uint16_t rpc_id = UINT16_MAX; - const Multiplayer::RPCConfig config = _get_rpc_config(node, p_method, rpc_id); - ERR_FAIL_COND_MSG(config.name == StringName(), - 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())); - if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { - if (rpc_id & (1 << 15)) { - call_local_native = config.call_local; - } else { - call_local_script = config.call_local; - } - } - - if (p_peer_id != node_id) { -#ifdef DEBUG_ENABLED - _profile_node_data("rpc_out", node->get_instance_id()); -#endif - - _send_rpc(node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount); - } - - if (call_local_native) { - Callable::CallError ce; - - multiplayer->set_remote_sender_override(peer->get_unique_id()); - node->callp(p_method, p_arg, p_argcount, ce); - multiplayer->set_remote_sender_override(0); - - if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in local call: - " + error + "."; - ERR_PRINT(error); - return; - } - } - - if (call_local_script) { - Callable::CallError ce; - ce.error = Callable::CallError::CALL_OK; - - multiplayer->set_remote_sender_override(peer->get_unique_id()); - node->get_script_instance()->callp(p_method, p_arg, p_argcount, ce); - multiplayer->set_remote_sender_override(0); - - if (ce.error != Callable::CallError::CALL_OK) { - String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in script local call: - " + error + "."; - ERR_PRINT(error); - return; - } - } - - ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.call_local, "RPC '" + p_method + "' on yourself is not allowed by selected mode."); -} diff --git a/scene/multiplayer/scene_rpc_interface.h b/scene/multiplayer/scene_rpc_interface.h deleted file mode 100644 index 86e1d0d280..0000000000 --- a/scene/multiplayer/scene_rpc_interface.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************/ -/* scene_rpc_interface.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 SCENE_RPC_INTERFACE_H -#define SCENE_RPC_INTERFACE_H - -#include "core/multiplayer/multiplayer.h" -#include "core/multiplayer/multiplayer_api.h" - -class SceneRPCInterface : public MultiplayerRPCInterface { - GDCLASS(SceneRPCInterface, MultiplayerRPCInterface); - -private: - enum NetworkNodeIdCompression { - NETWORK_NODE_ID_COMPRESSION_8 = 0, - NETWORK_NODE_ID_COMPRESSION_16, - NETWORK_NODE_ID_COMPRESSION_32, - }; - - enum NetworkNameIdCompression { - NETWORK_NAME_ID_COMPRESSION_8 = 0, - NETWORK_NAME_ID_COMPRESSION_16, - }; - - // The RPC meta is composed by a single byte that contains (starting from the least significant bit): - // - `NetworkCommands` in the first four bits. - // - `NetworkNodeIdCompression` in the next 2 bits. - // - `NetworkNameIdCompression` in the next 1 bit. - // - `byte_only_or_no_args` in the next 1 bit. - enum { - NODE_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT, // 2 bits for this. - NAME_ID_COMPRESSION_SHIFT = MultiplayerAPI::CMD_FLAG_2_SHIFT, - BYTE_ONLY_OR_NO_ARGS_SHIFT = MultiplayerAPI::CMD_FLAG_3_SHIFT, - }; - - enum { - NODE_ID_COMPRESSION_FLAG = (1 << NODE_ID_COMPRESSION_SHIFT) | (1 << (NODE_ID_COMPRESSION_SHIFT + 1)), // 2 bits for this. - NAME_ID_COMPRESSION_FLAG = (1 << NAME_ID_COMPRESSION_SHIFT), - BYTE_ONLY_OR_NO_ARGS_FLAG = (1 << BYTE_ONLY_OR_NO_ARGS_SHIFT), - }; - - MultiplayerAPI *multiplayer = nullptr; - Vector<uint8_t> packet_cache; - -protected: - static MultiplayerRPCInterface *_create(MultiplayerAPI *p_multiplayer); - - _FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id); - void _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); - - void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount); - Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); - -public: - static void make_default(); - - virtual void rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override; - virtual void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) override; - virtual String get_rpc_md5(const Object *p_obj) const override; - - SceneRPCInterface(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; } -}; - -#endif // SCENE_RPC_INTERFACE_H diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 09a283ea53..89e2327fc8 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -130,16 +130,12 @@ #include "scene/main/http_request.h" #include "scene/main/instance_placeholder.h" #include "scene/main/missing_node.h" +#include "scene/main/multiplayer_api.h" #include "scene/main/resource_preloader.h" #include "scene/main/scene_tree.h" #include "scene/main/timer.h" #include "scene/main/viewport.h" #include "scene/main/window.h" -#include "scene/multiplayer/multiplayer_spawner.h" -#include "scene/multiplayer/multiplayer_synchronizer.h" -#include "scene/multiplayer/scene_cache_interface.h" -#include "scene/multiplayer/scene_replication_interface.h" -#include "scene/multiplayer/scene_rpc_interface.h" #include "scene/resources/animation_library.h" #include "scene/resources/audio_stream_sample.h" #include "scene/resources/bit_map.h" @@ -322,9 +318,13 @@ void register_scene_types() { GDREGISTER_ABSTRACT_CLASS(Viewport); GDREGISTER_CLASS(SubViewport); GDREGISTER_CLASS(ViewportTexture); + + GDREGISTER_ABSTRACT_CLASS(MultiplayerPeer); + GDREGISTER_CLASS(MultiplayerPeerExtension); + GDREGISTER_ABSTRACT_CLASS(MultiplayerAPI); + GDREGISTER_CLASS(MultiplayerAPIExtension); + GDREGISTER_CLASS(HTTPRequest); - GDREGISTER_CLASS(MultiplayerSpawner); - GDREGISTER_CLASS(MultiplayerSynchronizer); GDREGISTER_CLASS(Timer); GDREGISTER_CLASS(CanvasLayer); GDREGISTER_CLASS(CanvasModulate); @@ -875,8 +875,6 @@ void register_scene_types() { GDREGISTER_CLASS(LabelSettings); - GDREGISTER_CLASS(SceneReplicationConfig); - GDREGISTER_CLASS(TextLine); GDREGISTER_CLASS(TextParagraph); @@ -1117,9 +1115,6 @@ void register_scene_types() { } SceneDebugger::initialize(); - SceneReplicationInterface::make_default(); - SceneRPCInterface::make_default(); - SceneCacheInterface::make_default(); } void initialize_theme() { diff --git a/scene/resources/scene_replication_config.cpp b/scene/resources/scene_replication_config.cpp deleted file mode 100644 index 6789f9f7d5..0000000000 --- a/scene/resources/scene_replication_config.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/*************************************************************************/ -/* scene_replication_config.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 "scene_replication_config.h" - -#include "core/multiplayer/multiplayer_api.h" -#include "scene/main/node.h" - -bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_value) { - String name = p_name; - - if (name.begins_with("properties/")) { - int idx = name.get_slicec('/', 1).to_int(); - String what = name.get_slicec('/', 2); - - if (properties.size() == idx && what == "path") { - ERR_FAIL_COND_V(p_value.get_type() != Variant::NODE_PATH, false); - NodePath path = p_value; - ERR_FAIL_COND_V(path.is_empty() || path.get_subname_count() == 0, false); - add_property(path); - return true; - } - ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false); - ERR_FAIL_INDEX_V(idx, properties.size(), false); - ReplicationProperty &prop = properties[idx]; - if (what == "sync") { - prop.sync = p_value; - if (prop.sync) { - sync_props.push_back(prop.name); - } else { - sync_props.erase(prop.name); - } - return true; - } else if (what == "spawn") { - prop.spawn = p_value; - if (prop.spawn) { - spawn_props.push_back(prop.name); - } else { - spawn_props.erase(prop.name); - } - return true; - } - } - return false; -} - -bool SceneReplicationConfig::_get(const StringName &p_name, Variant &r_ret) const { - String name = p_name; - - if (name.begins_with("properties/")) { - int idx = name.get_slicec('/', 1).to_int(); - String what = name.get_slicec('/', 2); - ERR_FAIL_INDEX_V(idx, properties.size(), false); - const ReplicationProperty &prop = properties[idx]; - if (what == "path") { - r_ret = prop.name; - return true; - } else if (what == "sync") { - r_ret = prop.sync; - return true; - } else if (what == "spawn") { - r_ret = prop.spawn; - return true; - } - } - return false; -} - -void SceneReplicationConfig::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < properties.size(); i++) { - p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/spawn", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); - p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); - } -} - -TypedArray<NodePath> SceneReplicationConfig::get_properties() const { - TypedArray<NodePath> paths; - for (const ReplicationProperty &prop : properties) { - paths.push_back(prop.name); - } - return paths; -} - -void SceneReplicationConfig::add_property(const NodePath &p_path, int p_index) { - ERR_FAIL_COND(properties.find(p_path)); - - if (p_index < 0 || p_index == properties.size()) { - properties.push_back(ReplicationProperty(p_path)); - return; - } - - ERR_FAIL_INDEX(p_index, properties.size()); - - List<ReplicationProperty>::Element *I = properties.front(); - int c = 0; - while (c < p_index) { - I = I->next(); - c++; - } - properties.insert_before(I, ReplicationProperty(p_path)); -} - -void SceneReplicationConfig::remove_property(const NodePath &p_path) { - properties.erase(p_path); -} - -bool SceneReplicationConfig::has_property(const NodePath &p_path) const { - for (int i = 0; i < properties.size(); i++) { - if (properties[i].name == p_path) { - return true; - } - } - return false; -} - -int SceneReplicationConfig::property_get_index(const NodePath &p_path) const { - for (int i = 0; i < properties.size(); i++) { - if (properties[i].name == p_path) { - return i; - } - } - ERR_FAIL_V(-1); -} - -bool SceneReplicationConfig::property_get_spawn(const NodePath &p_path) { - List<ReplicationProperty>::Element *E = properties.find(p_path); - ERR_FAIL_COND_V(!E, false); - return E->get().spawn; -} - -void SceneReplicationConfig::property_set_spawn(const NodePath &p_path, bool p_enabled) { - List<ReplicationProperty>::Element *E = properties.find(p_path); - ERR_FAIL_COND(!E); - if (E->get().spawn == p_enabled) { - return; - } - E->get().spawn = p_enabled; - spawn_props.clear(); - for (const ReplicationProperty &prop : properties) { - if (prop.spawn) { - spawn_props.push_back(p_path); - } - } -} - -bool SceneReplicationConfig::property_get_sync(const NodePath &p_path) { - List<ReplicationProperty>::Element *E = properties.find(p_path); - ERR_FAIL_COND_V(!E, false); - return E->get().sync; -} - -void SceneReplicationConfig::property_set_sync(const NodePath &p_path, bool p_enabled) { - List<ReplicationProperty>::Element *E = properties.find(p_path); - ERR_FAIL_COND(!E); - if (E->get().sync == p_enabled) { - return; - } - E->get().sync = p_enabled; - sync_props.clear(); - for (const ReplicationProperty &prop : properties) { - if (prop.sync) { - sync_props.push_back(p_path); - } - } -} - -void SceneReplicationConfig::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_properties"), &SceneReplicationConfig::get_properties); - ClassDB::bind_method(D_METHOD("add_property", "path", "index"), &SceneReplicationConfig::add_property, DEFVAL(-1)); - ClassDB::bind_method(D_METHOD("has_property", "path"), &SceneReplicationConfig::has_property); - ClassDB::bind_method(D_METHOD("remove_property", "path"), &SceneReplicationConfig::remove_property); - ClassDB::bind_method(D_METHOD("property_get_index", "path"), &SceneReplicationConfig::property_get_index); - ClassDB::bind_method(D_METHOD("property_get_spawn", "path"), &SceneReplicationConfig::property_get_spawn); - ClassDB::bind_method(D_METHOD("property_set_spawn", "path", "enabled"), &SceneReplicationConfig::property_set_spawn); - ClassDB::bind_method(D_METHOD("property_get_sync", "path"), &SceneReplicationConfig::property_get_sync); - ClassDB::bind_method(D_METHOD("property_set_sync", "path", "enabled"), &SceneReplicationConfig::property_set_sync); -} diff --git a/scene/resources/scene_replication_config.h b/scene/resources/scene_replication_config.h deleted file mode 100644 index ab3658d2a7..0000000000 --- a/scene/resources/scene_replication_config.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************/ -/* scene_replication_config.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 SCENE_REPLICATION_CONFIG_H -#define SCENE_REPLICATION_CONFIG_H - -#include "core/io/resource.h" - -#include "core/variant/typed_array.h" - -class SceneReplicationConfig : public Resource { - GDCLASS(SceneReplicationConfig, Resource); - OBJ_SAVE_TYPE(SceneReplicationConfig); - RES_BASE_EXTENSION("repl"); - -private: - struct ReplicationProperty { - NodePath name; - bool spawn = true; - bool sync = true; - - bool operator==(const ReplicationProperty &p_to) { - return name == p_to.name; - } - - ReplicationProperty() {} - - ReplicationProperty(const NodePath &p_name) { - name = p_name; - } - }; - - List<ReplicationProperty> properties; - List<NodePath> spawn_props; - List<NodePath> sync_props; - -protected: - static void _bind_methods(); - - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; - -public: - TypedArray<NodePath> get_properties() const; - - void add_property(const NodePath &p_path, int p_index = -1); - void remove_property(const NodePath &p_path); - bool has_property(const NodePath &p_path) const; - - int property_get_index(const NodePath &p_path) const; - bool property_get_spawn(const NodePath &p_path); - void property_set_spawn(const NodePath &p_path, bool p_enabled); - - bool property_get_sync(const NodePath &p_path); - void property_set_sync(const NodePath &p_path, bool p_enabled); - - const List<NodePath> &get_spawn_properties() { return spawn_props; } - const List<NodePath> &get_sync_properties() { return sync_props; } - - SceneReplicationConfig() {} -}; - -#endif // SCENE_REPLICATION_CONFIG_H |