|
@@ -32,15 +32,11 @@
|
|
|
|
|
|
#include "core/debugger/engine_debugger.h"
|
|
#include "core/debugger/engine_debugger.h"
|
|
#include "core/io/marshalls.h"
|
|
#include "core/io/marshalls.h"
|
|
|
|
+#include "core/io/multiplayer_replicator.h"
|
|
#include "scene/main/node.h"
|
|
#include "scene/main/node.h"
|
|
-#include "scene/resources/packed_scene.h"
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
#include <stdint.h>
|
|
|
|
|
|
-#define NODE_ID_COMPRESSION_SHIFT 3
|
|
|
|
-#define NAME_ID_COMPRESSION_SHIFT 5
|
|
|
|
-#define BYTE_ONLY_OR_NO_ARGS_SHIFT 6
|
|
|
|
-
|
|
|
|
#ifdef DEBUG_ENABLED
|
|
#ifdef DEBUG_ENABLED
|
|
#include "core/os/os.h"
|
|
#include "core/os/os.h"
|
|
#endif
|
|
#endif
|
|
@@ -147,7 +143,7 @@ void MultiplayerAPI::poll() {
|
|
}
|
|
}
|
|
|
|
|
|
void MultiplayerAPI::clear() {
|
|
void MultiplayerAPI::clear() {
|
|
- replicated_nodes.clear();
|
|
|
|
|
|
+ replicator->clear();
|
|
connected_peers.clear();
|
|
connected_peers.clear();
|
|
path_get_cache.clear();
|
|
path_get_cache.clear();
|
|
path_send_cache.clear();
|
|
path_send_cache.clear();
|
|
@@ -325,10 +321,10 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
|
|
_process_raw(p_from, p_packet, p_packet_len);
|
|
_process_raw(p_from, p_packet, p_packet_len);
|
|
} break;
|
|
} break;
|
|
case NETWORK_COMMAND_SPAWN: {
|
|
case NETWORK_COMMAND_SPAWN: {
|
|
- _process_spawn_despawn(p_from, p_packet, p_packet_len, true);
|
|
|
|
|
|
+ replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true);
|
|
} break;
|
|
} break;
|
|
case NETWORK_COMMAND_DESPAWN: {
|
|
case NETWORK_COMMAND_DESPAWN: {
|
|
- _process_spawn_despawn(p_from, p_packet, p_packet_len, false);
|
|
|
|
|
|
+ replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false);
|
|
} break;
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -338,7 +334,6 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uin
|
|
|
|
|
|
if (p_node_target & 0x80000000) {
|
|
if (p_node_target & 0x80000000) {
|
|
// Use full path (not cached yet).
|
|
// Use full path (not cached yet).
|
|
-
|
|
|
|
int ofs = p_node_target & 0x7FFFFFFF;
|
|
int ofs = p_node_target & 0x7FFFFFFF;
|
|
|
|
|
|
ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
|
|
ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared.");
|
|
@@ -353,25 +348,11 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uin
|
|
if (!node) {
|
|
if (!node) {
|
|
ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
|
|
ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
|
|
}
|
|
}
|
|
|
|
+ return node;
|
|
} else {
|
|
} else {
|
|
// Use cached path.
|
|
// Use cached path.
|
|
- int id = p_node_target;
|
|
|
|
-
|
|
|
|
- Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
|
|
|
|
- ERR_FAIL_COND_V_MSG(!E, nullptr, "Invalid packet received. Requests invalid peer cache.");
|
|
|
|
-
|
|
|
|
- Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id);
|
|
|
|
- ERR_FAIL_COND_V_MSG(!F, nullptr, "Invalid packet received. Unabled to find requested cached node.");
|
|
|
|
-
|
|
|
|
- PathGetCache::NodeInfo *ni = &F->get();
|
|
|
|
- // Do proper caching later.
|
|
|
|
-
|
|
|
|
- node = root_node->get_node(ni->path);
|
|
|
|
- if (!node) {
|
|
|
|
- ERR_PRINT("Failed to get cached path from RPC: " + String(ni->path) + ".");
|
|
|
|
- }
|
|
|
|
|
|
+ return get_cached_node(p_from, p_node_target);
|
|
}
|
|
}
|
|
- return node;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
|
|
void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
|
|
@@ -425,7 +406,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id,
|
|
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
|
|
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
|
|
|
|
|
|
int vlen;
|
|
int vlen;
|
|
- Error err = _decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
|
|
|
|
|
|
+ Error err = decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
|
|
ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
|
|
ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
|
|
|
|
|
|
argp.write[i] = &args[i];
|
|
argp.write[i] = &args[i];
|
|
@@ -514,64 +495,6 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet,
|
|
E->get() = true;
|
|
E->get() = true;
|
|
}
|
|
}
|
|
|
|
|
|
-void MultiplayerAPI::_process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn) {
|
|
|
|
- ERR_FAIL_COND_MSG(p_packet_len < 18, "Invalid spawn packet received");
|
|
|
|
- int ofs = 1;
|
|
|
|
- ResourceUID::ID id = decode_uint64(&p_packet[ofs]);
|
|
|
|
- ofs += 8;
|
|
|
|
- ERR_FAIL_COND_MSG(!spawnables.has(id), "Invalid spawn ID received " + itos(id));
|
|
|
|
-
|
|
|
|
- uint32_t node_target = decode_uint32(&p_packet[ofs]);
|
|
|
|
- Node *parent = _process_get_node(p_from, nullptr, node_target, 0);
|
|
|
|
- ofs += 4;
|
|
|
|
- ERR_FAIL_COND_MSG(parent == nullptr, "Invalid packet received. Requested node was not found.");
|
|
|
|
-
|
|
|
|
- uint32_t name_len = decode_uint32(&p_packet[ofs]);
|
|
|
|
- ofs += 4;
|
|
|
|
- ERR_FAIL_COND_MSG(name_len > uint32_t(p_packet_len - ofs), vformat("Invalid spawn packet size: %d, wants: %d", p_packet_len, ofs + name_len));
|
|
|
|
- ERR_FAIL_COND_MSG(name_len < 1, "Zero spawn name size.");
|
|
|
|
-
|
|
|
|
- const String name = String::utf8((const char *)&p_packet[ofs], name_len);
|
|
|
|
- // We need to make sure no trickery happens here (e.g. despawning a subpath), but we want to allow autogenerated ("@") node names.
|
|
|
|
- ERR_FAIL_COND_MSG(name.validate_node_name() != name.replace("@", ""), vformat("Invalid node name received: '%s'", name));
|
|
|
|
- ofs += name_len;
|
|
|
|
- PackedByteArray data;
|
|
|
|
- if (p_packet_len > ofs) {
|
|
|
|
- data.resize(p_packet_len - ofs);
|
|
|
|
- memcpy(data.ptrw(), &p_packet[ofs], data.size());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- SpawnMode mode = spawnables[id];
|
|
|
|
- if (mode == SPAWN_MODE_SERVER && p_from == 1) {
|
|
|
|
- String scene_path = ResourceUID::get_singleton()->get_id_path(id);
|
|
|
|
- if (p_spawn) {
|
|
|
|
- ERR_FAIL_COND_MSG(parent->has_node(name), vformat("Unable to spawn node. Node already exists: %s/%s", parent->get_path(), name));
|
|
|
|
- RES res = ResourceLoader::load(scene_path);
|
|
|
|
- ERR_FAIL_COND_MSG(!res.is_valid(), "Unable to load scene to spawn at path: " + scene_path);
|
|
|
|
- PackedScene *scene = Object::cast_to<PackedScene>(res.ptr());
|
|
|
|
- ERR_FAIL_COND(!scene);
|
|
|
|
- Node *node = scene->instantiate();
|
|
|
|
- ERR_FAIL_COND(!node);
|
|
|
|
- replicated_nodes[node->get_instance_id()] = id;
|
|
|
|
- parent->_add_child_nocheck(node, name);
|
|
|
|
- emit_signal(SNAME("network_spawn"), p_from, id, node, data);
|
|
|
|
- } else {
|
|
|
|
- ERR_FAIL_COND_MSG(!parent->has_node(name), vformat("Path not found: %s/%s", parent->get_path(), name));
|
|
|
|
- Node *node = parent->get_node(name);
|
|
|
|
- ERR_FAIL_COND_MSG(!replicated_nodes.has(node->get_instance_id()), vformat("Trying to despawn a Node that was not replicated: %s/%s", parent->get_path(), name));
|
|
|
|
- emit_signal(SNAME("network_despawn"), p_from, id, node, data);
|
|
|
|
- replicated_nodes.erase(node->get_instance_id());
|
|
|
|
- node->queue_delete();
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- if (p_spawn) {
|
|
|
|
- emit_signal(SNAME("network_spawn_request"), p_from, id, parent, name, data);
|
|
|
|
- } else {
|
|
|
|
- emit_signal(SNAME("network_despawn_request"), p_from, id, parent, name, data);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) {
|
|
bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) {
|
|
bool has_all_peers = true;
|
|
bool has_all_peers = true;
|
|
List<int> peers_to_add; // If one is missing, take note to add it.
|
|
List<int> peers_to_add; // If one is missing, take note to add it.
|
|
@@ -647,7 +570,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC
|
|
#define ENCODE_16 1 << 5
|
|
#define ENCODE_16 1 << 5
|
|
#define ENCODE_32 2 << 5
|
|
#define ENCODE_32 2 << 5
|
|
#define ENCODE_64 3 << 5
|
|
#define ENCODE_64 3 << 5
|
|
-Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) {
|
|
|
|
|
|
+Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) {
|
|
// Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
|
|
// Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
|
|
CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
|
|
CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
|
|
|
|
|
|
@@ -722,7 +645,7 @@ Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uin
|
|
return OK;
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
|
|
-Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) {
|
|
|
|
|
|
+Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) {
|
|
const uint8_t *buf = p_buffer;
|
|
const uint8_t *buf = p_buffer;
|
|
int len = p_len;
|
|
int len = p_len;
|
|
|
|
|
|
@@ -906,10 +829,10 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const
|
|
ofs += 1;
|
|
ofs += 1;
|
|
for (int i = 0; i < p_argcount; i++) {
|
|
for (int i = 0; i < p_argcount; i++) {
|
|
int len(0);
|
|
int len(0);
|
|
- Error err = _encode_and_compress_variant(*p_arg[i], nullptr, len);
|
|
|
|
|
|
+ Error err = encode_and_compress_variant(*p_arg[i], nullptr, len);
|
|
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
|
|
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
|
|
MAKE_ROOM(ofs + len);
|
|
MAKE_ROOM(ofs + len);
|
|
- _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
|
|
|
|
|
|
+ encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
|
|
ofs += len;
|
|
ofs += len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -976,14 +899,7 @@ void MultiplayerAPI::_add_peer(int p_id) {
|
|
connected_peers.insert(p_id);
|
|
connected_peers.insert(p_id);
|
|
path_get_cache.insert(p_id, PathGetCache());
|
|
path_get_cache.insert(p_id, PathGetCache());
|
|
if (is_network_server()) {
|
|
if (is_network_server()) {
|
|
- for (const KeyValue<ObjectID, ResourceUID::ID> &E : replicated_nodes) {
|
|
|
|
- // Only server mode adds to replicated_nodes, no need to check it.
|
|
|
|
- Object *obj = ObjectDB::get_instance(E.key);
|
|
|
|
- ERR_CONTINUE(!obj);
|
|
|
|
- Node *node = Object::cast_to<Node>(obj);
|
|
|
|
- ERR_CONTINUE(!node);
|
|
|
|
- _send_spawn_despawn(p_id, E.value, node->get_path(), nullptr, 0, true);
|
|
|
|
- }
|
|
|
|
|
|
+ replicator->spawn_all(p_id);
|
|
}
|
|
}
|
|
emit_signal(SNAME("network_peer_connected"), p_id);
|
|
emit_signal(SNAME("network_peer_connected"), p_id);
|
|
}
|
|
}
|
|
@@ -1000,10 +916,6 @@ void MultiplayerAPI::_del_peer(int p_id) {
|
|
PathSentCache *psc = path_send_cache.getptr(E);
|
|
PathSentCache *psc = path_send_cache.getptr(E);
|
|
psc->confirmed_peers.erase(p_id);
|
|
psc->confirmed_peers.erase(p_id);
|
|
}
|
|
}
|
|
- if (p_id == 1) {
|
|
|
|
- // Erase server replicated nodes, but do not queue them for deletion.
|
|
|
|
- replicated_nodes.clear();
|
|
|
|
- }
|
|
|
|
emit_signal(SNAME("network_peer_disconnected"), p_id);
|
|
emit_signal(SNAME("network_peer_disconnected"), p_id);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1109,6 +1021,36 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac
|
|
emit_signal(SNAME("network_peer_packet"), p_from, out);
|
|
emit_signal(SNAME("network_peer_packet"), p_from, out);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+bool MultiplayerAPI::send_confirm_path(Node *p_node, NodePath p_path, int p_peer_id, int &r_id) {
|
|
|
|
+ // See if the path is cached.
|
|
|
|
+ PathSentCache *psc = path_send_cache.getptr(p_path);
|
|
|
|
+ if (!psc) {
|
|
|
|
+ // Path is not cached, create.
|
|
|
|
+ path_send_cache[p_path] = PathSentCache();
|
|
|
|
+ psc = path_send_cache.getptr(p_path);
|
|
|
|
+ psc->id = last_send_cache_id++;
|
|
|
|
+ }
|
|
|
|
+ r_id = psc->id;
|
|
|
|
+
|
|
|
|
+ // See if all peers have cached path (if so, call can be fast).
|
|
|
|
+ return _send_confirm_path(p_node, p_path, psc, p_peer_id);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+Node *MultiplayerAPI::get_cached_node(int p_from, uint32_t p_node_id) {
|
|
|
|
+ Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
|
|
|
|
+ ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from));
|
|
|
|
+
|
|
|
|
+ Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(p_node_id);
|
|
|
|
+ ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_node_id, p_from));
|
|
|
|
+
|
|
|
|
+ PathGetCache::NodeInfo *ni = &F->get();
|
|
|
|
+ Node *node = root_node->get_node(ni->path);
|
|
|
|
+ if (!node) {
|
|
|
|
+ ERR_PRINT("Failed to get cached path: " + String(ni->path) + ".");
|
|
|
|
+ }
|
|
|
|
+ return node;
|
|
|
|
+}
|
|
|
|
+
|
|
int MultiplayerAPI::get_network_unique_id() const {
|
|
int MultiplayerAPI::get_network_unique_id() const {
|
|
ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID.");
|
|
ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID.");
|
|
return network_peer->get_unique_id();
|
|
return network_peer->get_unique_id();
|
|
@@ -1147,97 +1089,12 @@ bool MultiplayerAPI::is_object_decoding_allowed() const {
|
|
return allow_object_decoding;
|
|
return allow_object_decoding;
|
|
}
|
|
}
|
|
|
|
|
|
-Error MultiplayerAPI::spawnable_config(const ResourceUID::ID &p_id, SpawnMode p_mode) {
|
|
|
|
- ERR_FAIL_COND_V(p_mode < SPAWN_MODE_NONE || p_mode > SPAWN_MODE_CUSTOM, ERR_INVALID_PARAMETER);
|
|
|
|
- ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER);
|
|
|
|
-#ifdef TOOLS_ENABLED
|
|
|
|
- String path = ResourceUID::get_singleton()->get_id_path(p_id);
|
|
|
|
- RES res = ResourceLoader::load(path);
|
|
|
|
- ERR_FAIL_COND_V(!res->is_class("PackedScene"), ERR_INVALID_PARAMETER);
|
|
|
|
-#endif
|
|
|
|
- if (p_mode == SPAWN_MODE_NONE) {
|
|
|
|
- if (spawnables.has(p_id)) {
|
|
|
|
- spawnables.erase(p_id);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- spawnables[p_id] = p_mode;
|
|
|
|
- }
|
|
|
|
- return OK;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-Error MultiplayerAPI::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data) {
|
|
|
|
- return _send_spawn_despawn(p_peer_id, p_scene_id, p_path, p_data.ptr(), p_data.size(), false);
|
|
|
|
|
|
+MultiplayerReplicator *MultiplayerAPI::get_replicator() const {
|
|
|
|
+ return replicator;
|
|
}
|
|
}
|
|
|
|
|
|
-Error MultiplayerAPI::send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data) {
|
|
|
|
- return _send_spawn_despawn(p_peer_id, p_scene_id, p_path, p_data.ptr(), p_data.size(), true);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-Error MultiplayerAPI::_send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const uint8_t *p_data, int p_data_len, bool p_spawn) {
|
|
|
|
- ERR_FAIL_COND_V(!root_node, ERR_UNCONFIGURED);
|
|
|
|
- ERR_FAIL_COND_V_MSG(!spawnables.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
|
|
|
|
-
|
|
|
|
- NodePath rel_path = (root_node->get_path()).rel_path_to(p_path);
|
|
|
|
- const Vector<StringName> names = rel_path.get_names();
|
|
|
|
- ERR_FAIL_COND_V(names.size() < 2, ERR_INVALID_PARAMETER);
|
|
|
|
-
|
|
|
|
- NodePath parent = NodePath(names.subarray(0, names.size() - 2), false);
|
|
|
|
- ERR_FAIL_COND_V_MSG(!root_node->has_node(parent), ERR_INVALID_PARAMETER, "Path not found: " + parent);
|
|
|
|
-
|
|
|
|
- // See if the path is cached.
|
|
|
|
- PathSentCache *psc = path_send_cache.getptr(parent);
|
|
|
|
- if (!psc) {
|
|
|
|
- // Path is not cached, create.
|
|
|
|
- path_send_cache[parent] = PathSentCache();
|
|
|
|
- psc = path_send_cache.getptr(parent);
|
|
|
|
- psc->id = last_send_cache_id++;
|
|
|
|
- }
|
|
|
|
- _send_confirm_path(root_node->get_node(parent), parent, psc, p_peer_id);
|
|
|
|
-
|
|
|
|
- const CharString cname = String(names[names.size() - 1]).utf8();
|
|
|
|
- int nlen = encode_cstring(cname.get_data(), nullptr);
|
|
|
|
- MAKE_ROOM(1 + 8 + 4 + 4 + nlen + p_data_len);
|
|
|
|
- uint8_t *ptr = packet_cache.ptrw();
|
|
|
|
- ptr[0] = p_spawn ? NETWORK_COMMAND_SPAWN : NETWORK_COMMAND_DESPAWN;
|
|
|
|
- int ofs = 1;
|
|
|
|
- ofs += encode_uint64(p_scene_id, &ptr[ofs]);
|
|
|
|
- ofs += encode_uint32(psc->id, &ptr[ofs]);
|
|
|
|
- ofs += encode_uint32(nlen, &ptr[ofs]);
|
|
|
|
- ofs += encode_cstring(cname.get_data(), &ptr[ofs]);
|
|
|
|
- memcpy(&ptr[ofs], p_data, p_data_len);
|
|
|
|
- network_peer->set_target_peer(p_peer_id);
|
|
|
|
- network_peer->set_transfer_channel(0);
|
|
|
|
- network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE);
|
|
|
|
- return network_peer->put_packet(ptr, ofs + p_data_len);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, const Node *p_node, bool p_enter) {
|
|
|
|
- if (!has_network_peer()) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- ERR_FAIL_COND(!p_node || !p_node->get_parent() || !root_node);
|
|
|
|
- NodePath path = (root_node->get_path()).rel_path_to(p_node->get_parent()->get_path());
|
|
|
|
- if (path.is_empty()) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- ResourceUID::ID id = ResourceLoader::get_resource_uid(p_scene);
|
|
|
|
- if (!spawnables.has(id)) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- SpawnMode mode = spawnables[id];
|
|
|
|
- if (p_enter) {
|
|
|
|
- if (mode == SPAWN_MODE_SERVER && is_network_server()) {
|
|
|
|
- replicated_nodes[p_node->get_instance_id()] = id;
|
|
|
|
- _send_spawn_despawn(0, id, p_node->get_path(), nullptr, 0, true);
|
|
|
|
- }
|
|
|
|
- emit_signal(SNAME("network_spawnable_added"), id, p_node);
|
|
|
|
- } else {
|
|
|
|
- if (mode == SPAWN_MODE_SERVER && is_network_server() && replicated_nodes.has(p_node->get_instance_id())) {
|
|
|
|
- replicated_nodes.erase(p_node->get_instance_id());
|
|
|
|
- _send_spawn_despawn(0, id, p_node->get_path(), nullptr, 0, false);
|
|
|
|
- }
|
|
|
|
- emit_signal(SNAME("network_spawnable_removed"), id, p_node);
|
|
|
|
- }
|
|
|
|
|
|
+void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) {
|
|
|
|
+ replicator->scene_enter_exit_notify(p_scene, p_node, p_enter);
|
|
}
|
|
}
|
|
|
|
|
|
void MultiplayerAPI::_bind_methods() {
|
|
void MultiplayerAPI::_bind_methods() {
|
|
@@ -1258,15 +1115,14 @@ void MultiplayerAPI::_bind_methods() {
|
|
ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections);
|
|
ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections);
|
|
ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding);
|
|
ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding);
|
|
ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed);
|
|
ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed);
|
|
- ClassDB::bind_method(D_METHOD("spawnable_config", "scene_id", "spawn_mode"), &MultiplayerAPI::spawnable_config);
|
|
|
|
- ClassDB::bind_method(D_METHOD("send_despawn", "peer_id", "scene_id", "path", "data"), &MultiplayerAPI::send_despawn, DEFVAL(PackedByteArray()));
|
|
|
|
- ClassDB::bind_method(D_METHOD("send_spawn", "peer_id", "scene_id", "path", "data"), &MultiplayerAPI::send_spawn, DEFVAL(PackedByteArray()));
|
|
|
|
|
|
+ ClassDB::bind_method(D_METHOD("get_replicator"), &MultiplayerAPI::get_replicator);
|
|
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections");
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections");
|
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_peer");
|
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_peer");
|
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node");
|
|
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node");
|
|
ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false);
|
|
ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false);
|
|
|
|
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator");
|
|
|
|
|
|
ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id")));
|
|
ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id")));
|
|
ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id")));
|
|
ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id")));
|
|
@@ -1274,27 +1130,19 @@ void MultiplayerAPI::_bind_methods() {
|
|
ADD_SIGNAL(MethodInfo("connected_to_server"));
|
|
ADD_SIGNAL(MethodInfo("connected_to_server"));
|
|
ADD_SIGNAL(MethodInfo("connection_failed"));
|
|
ADD_SIGNAL(MethodInfo("connection_failed"));
|
|
ADD_SIGNAL(MethodInfo("server_disconnected"));
|
|
ADD_SIGNAL(MethodInfo("server_disconnected"));
|
|
- ADD_SIGNAL(MethodInfo("network_despawn", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
|
|
|
|
- ADD_SIGNAL(MethodInfo("network_spawn", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
|
|
|
|
- ADD_SIGNAL(MethodInfo("network_despawn_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
|
|
|
|
- ADD_SIGNAL(MethodInfo("network_spawn_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
|
|
|
|
- ADD_SIGNAL(MethodInfo("network_spawnable_added", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
|
|
|
- ADD_SIGNAL(MethodInfo("network_spawnable_removed", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
|
|
|
|
|
|
|
BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
|
|
BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
|
|
BIND_ENUM_CONSTANT(RPC_MODE_REMOTE);
|
|
BIND_ENUM_CONSTANT(RPC_MODE_REMOTE);
|
|
BIND_ENUM_CONSTANT(RPC_MODE_MASTER);
|
|
BIND_ENUM_CONSTANT(RPC_MODE_MASTER);
|
|
BIND_ENUM_CONSTANT(RPC_MODE_PUPPET);
|
|
BIND_ENUM_CONSTANT(RPC_MODE_PUPPET);
|
|
-
|
|
|
|
- BIND_ENUM_CONSTANT(SPAWN_MODE_NONE);
|
|
|
|
- BIND_ENUM_CONSTANT(SPAWN_MODE_SERVER);
|
|
|
|
- BIND_ENUM_CONSTANT(SPAWN_MODE_CUSTOM);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
MultiplayerAPI::MultiplayerAPI() {
|
|
MultiplayerAPI::MultiplayerAPI() {
|
|
|
|
+ replicator = memnew(MultiplayerReplicator(this));
|
|
clear();
|
|
clear();
|
|
}
|
|
}
|
|
|
|
|
|
MultiplayerAPI::~MultiplayerAPI() {
|
|
MultiplayerAPI::~MultiplayerAPI() {
|
|
clear();
|
|
clear();
|
|
|
|
+ memdelete(replicator);
|
|
}
|
|
}
|