Răsfoiți Sursa

Merge pull request #36568 from AndreaCatania/optimized_rpc_with_bytes

Optimized rpc packet size when only the `PackedByteArray` is sent.
Rémi Verschelde 5 ani în urmă
părinte
comite
c1abfd437e
1 a modificat fișierele cu 83 adăugiri și 28 ștergeri
  1. 83 28
      core/io/multiplayer_api.cpp

+ 83 - 28
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) {