summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrea Catania <info@andreacatania.com>2020-02-27 08:19:59 +0100
committerAndrea Catania <info@andreacatania.com>2020-02-27 08:19:59 +0100
commit1afd60d8f5ecb30f024d3eedc3e83c08dd1e9447 (patch)
treed671014289717c5b7367fd8acce0b2391d791a7c
parent1e57b558f215dd4920768e9567b6f55825877c89 (diff)
Optimized rpc packet size when only the `PackedByteArray` is sent.
This work has been kindly sponsored by IMVU.
-rw-r--r--core/io/multiplayer_api.cpp111
1 files changed, 83 insertions, 28 deletions
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
index 6a0eeea513..2aef0a3274 100644
--- a/core/io/multiplayer_api.cpp
+++ b/core/io/multiplayer_api.cpp
@@ -34,6 +34,10 @@
#include "scene/main/node.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
#include "core/os/os.h"
#endif
@@ -168,6 +172,16 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
return network_peer;
}
+// Returns the packet size stripping the node path added when the node is not yet cached.
+int get_packet_len(uint32_t p_node_target, int p_packet_len) {
+ if (p_node_target & 0x80000000) {
+ int ofs = p_node_target & 0x7FFFFFFF;
+ return p_packet_len - (p_packet_len - ofs);
+ } else {
+ return p_packet_len;
+ }
+}
+
void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
ERR_FAIL_COND_MSG(root_node == NULL, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it.");
@@ -204,8 +218,8 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
int name_id_offset = 1;
ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
// Compute the meta size, which depends on the compression level.
- int node_id_compression = (p_packet[0] & 24) >> 3;
- int name_id_compression = (p_packet[0] & 32) >> 5;
+ int node_id_compression = (p_packet[0] & 24) >> NODE_ID_COMPRESSION_SHIFT;
+ int name_id_compression = (p_packet[0] & 32) >> NAME_ID_COMPRESSION_SHIFT;
switch (node_id_compression) {
case NETWORK_NODE_ID_COMPRESSION_8:
@@ -250,6 +264,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
// Unreachable, checked before.
CRASH_NOW();
}
+
Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
ERR_FAIL_COND_MSG(node == NULL, "Invalid packet received. Requested node was not found.");
@@ -266,13 +281,14 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
CRASH_NOW();
}
+ const int packet_len = get_packet_len(node_target, p_packet_len);
if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
- _process_rpc(node, name_id, p_from, p_packet, p_packet_len, packet_min_size);
+ _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
} else {
- _process_rset(node, name_id, p_from, p_packet, p_packet_len, packet_min_size);
+ _process_rset(node, name_id, p_from, p_packet, packet_len, packet_min_size);
}
} break;
@@ -326,7 +342,7 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uin
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) {
- 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.");
// Check that remote can call the RPC on this node.
StringName name = p_node->get_node_rpc_method(p_rpc_method_id);
@@ -340,14 +356,30 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id,
bool can_call = _can_call_mode(p_node, rpc_mode, p_from);
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
- int argc = p_packet[p_offset];
+ int argc = 0;
+ bool byte_only = false;
+
+ const bool byte_only_or_no_args = ((p_packet[0] & 64) >> BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1;
+ if (byte_only_or_no_args) {
+ if (p_offset < p_packet_len) {
+ // This packet contains only bytes.
+ argc = 1;
+ byte_only = true;
+ } else {
+ // This rpc calls a method without parameters.
+ }
+ } else {
+ // Normal variant, takes the argument count from the packet.
+ ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
+ argc = p_packet[p_offset];
+ p_offset += 1;
+ }
+
Vector<Variant> args;
Vector<const Variant *> argp;
args.resize(argc);
argp.resize(argc);
- p_offset++;
-
#ifdef DEBUG_ENABLED
if (profiling) {
ObjectID id = p_node->get_instance_id();
@@ -356,16 +388,26 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id,
}
#endif
- for (int i = 0; i < argc; i++) {
+ if (byte_only) {
+ Vector<uint8_t> pure_data;
+ const int len = p_packet_len - p_offset;
+ pure_data.resize(len);
+ memcpy(pure_data.ptrw(), &p_packet[p_offset], len);
+ args.write[0] = pure_data;
+ argp.write[0] = &args[0];
+ p_offset += len;
+ } else {
+ for (int i = 0; i < argc; i++) {
- 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;
- 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.");
+ int 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.");
- argp.write[i] = &args[i];
- p_offset += vlen;
+ argp.write[i] = &args[i];
+ p_offset += vlen;
+ }
}
Callable::CallError ce;
@@ -742,10 +784,12 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
// - `NetworkCommands` in the first three bits.
// - `NetworkNodeIdCompression` in the next 2 bits.
// - `NetworkNameIdCompression` in the next 1 bit.
- // - So we still have the last two bits free!
+ // - `byte_only_or_no_args` in the next 1 bit.
+ // - So we still have the last bit free!
uint8_t command_type = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
uint8_t node_id_compression = UINT8_MAX;
uint8_t name_id_compression = UINT8_MAX;
+ bool byte_only_or_no_args = false;
MAKE_ROOM(1);
// The meta is composed along the way, so just set 0 for now.
@@ -835,17 +879,28 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
ofs += 2;
}
- // Call arguments.
- MAKE_ROOM(ofs + 1);
- packet_cache.write[ofs] = p_argcount;
- ofs += 1;
- for (int i = 0; i < p_argcount; i++) {
- int len(0);
- Error err = _encode_and_compress_variant(*p_arg[i], NULL, len);
- ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
- MAKE_ROOM(ofs + len);
- _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
- ofs += len;
+ if (p_argcount == 0) {
+ byte_only_or_no_args = true;
+ } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
+ byte_only_or_no_args = true;
+ // Special optimization when only the byte vector is sent.
+ const Vector<uint8_t> data = *p_arg[0];
+ MAKE_ROOM(ofs + data.size());
+ copymem(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
+ ofs += data.size();
+ } else {
+ // Arguments
+ MAKE_ROOM(ofs + 1);
+ packet_cache.write[ofs] = p_argcount;
+ ofs += 1;
+ for (int i = 0; i < p_argcount; i++) {
+ int len(0);
+ Error err = _encode_and_compress_variant(*p_arg[i], NULL, len);
+ ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
+ MAKE_ROOM(ofs + len);
+ _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
+ ofs += len;
+ }
}
}
@@ -854,7 +909,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
ERR_FAIL_COND(name_id_compression > 1);
// We can now set the meta
- packet_cache.write[0] = command_type + (node_id_compression << 3) + (name_id_compression << 5);
+ packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + ((byte_only_or_no_args ? 1 : 0) << BYTE_ONLY_OR_NO_ARGS_SHIFT);
#ifdef DEBUG_ENABLED
if (profiling) {