1
0
mirror of https://github.com/godotengine/godot.git synced 2026-01-06 19:41:11 +00:00

Merge pull request #63049 from Faless/mp/4.x_as_module

This commit is contained in:
Rémi Verschelde
2022-07-28 20:46:31 +02:00
committed by GitHub
80 changed files with 1819 additions and 1388 deletions

View File

@@ -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");
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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();
}
// 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);
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;
}
}
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);

View File

@@ -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;

View File

@@ -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;