Merge pull request #36568 from AndreaCatania/optimized_rpc_with_bytes
Optimized rpc packet size when only the `PackedByteArray` is sent.
This commit is contained in:
commit
c1abfd437e
1 changed files with 83 additions and 28 deletions
|
@ -34,6 +34,10 @@
|
||||||
#include "scene/main/node.h"
|
#include "scene/main/node.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
|
||||||
|
@ -168,6 +172,16 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
|
||||||
return network_peer;
|
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) {
|
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.");
|
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;
|
int name_id_offset = 1;
|
||||||
ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small.");
|
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.
|
// Compute the meta size, which depends on the compression level.
|
||||||
int node_id_compression = (p_packet[0] & 24) >> 3;
|
int node_id_compression = (p_packet[0] & 24) >> NODE_ID_COMPRESSION_SHIFT;
|
||||||
int name_id_compression = (p_packet[0] & 32) >> 5;
|
int name_id_compression = (p_packet[0] & 32) >> NAME_ID_COMPRESSION_SHIFT;
|
||||||
|
|
||||||
switch (node_id_compression) {
|
switch (node_id_compression) {
|
||||||
case NETWORK_NODE_ID_COMPRESSION_8:
|
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.
|
// Unreachable, checked before.
|
||||||
CRASH_NOW();
|
CRASH_NOW();
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len);
|
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.");
|
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();
|
CRASH_NOW();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int packet_len = get_packet_len(node_target, p_packet_len);
|
||||||
if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
|
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 {
|
} 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;
|
} 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) {
|
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.
|
// Check that remote can call the RPC on this node.
|
||||||
StringName name = p_node->get_node_rpc_method(p_rpc_method_id);
|
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);
|
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()) + ".");
|
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<Variant> args;
|
||||||
Vector<const Variant *> argp;
|
Vector<const Variant *> argp;
|
||||||
args.resize(argc);
|
args.resize(argc);
|
||||||
argp.resize(argc);
|
argp.resize(argc);
|
||||||
|
|
||||||
p_offset++;
|
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
if (profiling) {
|
if (profiling) {
|
||||||
ObjectID id = p_node->get_instance_id();
|
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
|
#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;
|
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];
|
||||||
p_offset += vlen;
|
p_offset += vlen;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Callable::CallError ce;
|
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.
|
// - `NetworkCommands` in the first three bits.
|
||||||
// - `NetworkNodeIdCompression` in the next 2 bits.
|
// - `NetworkNodeIdCompression` in the next 2 bits.
|
||||||
// - `NetworkNameIdCompression` in the next 1 bit.
|
// - `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 command_type = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
|
||||||
uint8_t node_id_compression = UINT8_MAX;
|
uint8_t node_id_compression = UINT8_MAX;
|
||||||
uint8_t name_id_compression = UINT8_MAX;
|
uint8_t name_id_compression = UINT8_MAX;
|
||||||
|
bool byte_only_or_no_args = false;
|
||||||
|
|
||||||
MAKE_ROOM(1);
|
MAKE_ROOM(1);
|
||||||
// The meta is composed along the way, so just set 0 for now.
|
// 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;
|
ofs += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call arguments.
|
if (p_argcount == 0) {
|
||||||
MAKE_ROOM(ofs + 1);
|
byte_only_or_no_args = true;
|
||||||
packet_cache.write[ofs] = p_argcount;
|
} else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
|
||||||
ofs += 1;
|
byte_only_or_no_args = true;
|
||||||
for (int i = 0; i < p_argcount; i++) {
|
// Special optimization when only the byte vector is sent.
|
||||||
int len(0);
|
const Vector<uint8_t> data = *p_arg[0];
|
||||||
Error err = _encode_and_compress_variant(*p_arg[i], NULL, len);
|
MAKE_ROOM(ofs + data.size());
|
||||||
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
|
copymem(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
|
||||||
MAKE_ROOM(ofs + len);
|
ofs += data.size();
|
||||||
_encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
|
} else {
|
||||||
ofs += len;
|
// 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);
|
ERR_FAIL_COND(name_id_compression > 1);
|
||||||
|
|
||||||
// We can now set the meta
|
// 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
|
#ifdef DEBUG_ENABLED
|
||||||
if (profiling) {
|
if (profiling) {
|
||||||
|
|
Loading…
Reference in a new issue