You've already forked godot
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:
416
scene/main/multiplayer_api.cpp
Normal file
416
scene/main/multiplayer_api.cpp
Normal 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");
|
||||
}
|
||||
115
scene/main/multiplayer_api.h
Normal file
115
scene/main/multiplayer_api.h
Normal 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
|
||||
303
scene/main/multiplayer_peer.cpp
Normal file
303
scene/main/multiplayer_peer.cpp
Normal 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);
|
||||
}
|
||||
157
scene/main/multiplayer_peer.h
Normal file
157
scene/main/multiplayer_peer.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user