Optimized rpc packet size when only the PackedByteArray
is sent.
This work has been kindly sponsored by IMVU.
This commit is contained in:
parent
1e57b558f2
commit
1afd60d8f5
1 changed files with 83 additions and 28 deletions
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue