diff options
Diffstat (limited to 'modules/multiplayer/scene_multiplayer.cpp')
| -rw-r--r-- | modules/multiplayer/scene_multiplayer.cpp | 163 | 
1 files changed, 157 insertions, 6 deletions
diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp index 3fc1eef366..f94f8ef658 100644 --- a/modules/multiplayer/scene_multiplayer.cpp +++ b/modules/multiplayer/scene_multiplayer.cpp @@ -67,12 +67,20 @@ Error SceneMultiplayer::poll() {  		const uint8_t *packet;  		int len; +		int channel = multiplayer_peer->get_packet_channel(); +		MultiplayerPeer::TransferMode mode = multiplayer_peer->get_packet_mode(); +  		Error err = multiplayer_peer->get_packet(&packet, len);  		ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error getting packet! %d", err)); -		remote_sender_id = sender; -		_process_packet(sender, packet, len); -		remote_sender_id = 0; +		if (len && (packet[0] & CMD_MASK) == NETWORK_COMMAND_SYS) { +			// Sys messages are processed separately since they might call _process_packet themselves. +			_process_sys(sender, packet, len, mode, channel); +		} else { +			remote_sender_id = sender; +			_process_packet(sender, packet, len); +			remote_sender_id = 0; +		}  		if (!multiplayer_peer.is_valid()) {  			return OK; // It's also possible that a packet or RPC caused a disconnection, so also check here. @@ -86,6 +94,7 @@ void SceneMultiplayer::clear() {  	connected_peers.clear();  	packet_cache.clear();  	cache->clear(); +	relay_buffer->clear();  }  void SceneMultiplayer::set_root_path(const NodePath &p_path) { @@ -166,10 +175,123 @@ void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int  		case NETWORK_COMMAND_SYNC: {  			replicator->on_sync_receive(p_from, p_packet, p_packet_len);  		} break; +		default: { +			ERR_FAIL_MSG("Invalid network command from " + itos(p_from)); +		} break; +	} +} + +Error SceneMultiplayer::send_command(int p_to, const uint8_t *p_packet, int p_packet_len) { +	if (server_relay && get_unique_id() != 1 && p_to != 1 && multiplayer_peer->is_server_relay_supported()) { +		// Send relay packet. +		relay_buffer->seek(0); +		relay_buffer->put_u8(NETWORK_COMMAND_SYS); +		relay_buffer->put_u8(SYS_COMMAND_RELAY); +		relay_buffer->put_32(p_to); // Set the destination. +		relay_buffer->put_data(p_packet, p_packet_len); +		multiplayer_peer->set_target_peer(1); +		const Vector<uint8_t> data = relay_buffer->get_data_array(); +		return multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position()); +	} +	if (p_to < 0) { +		for (const int &pid : connected_peers) { +			if (pid == -p_to) { +				continue; +			} +			multiplayer_peer->set_target_peer(pid); +			multiplayer_peer->put_packet(p_packet, p_packet_len); +		} +		return OK; +	} else { +		multiplayer_peer->set_target_peer(p_to); +		return multiplayer_peer->put_packet(p_packet, p_packet_len); +	} +} + +void SceneMultiplayer::_process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel) { +	ERR_FAIL_COND_MSG(p_packet_len < SYS_CMD_SIZE, "Invalid packet received. Size too small."); +	uint8_t sys_cmd_type = p_packet[1]; +	int32_t peer = int32_t(decode_uint32(&p_packet[2])); +	switch (sys_cmd_type) { +		case SYS_COMMAND_ADD_PEER: { +			ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1); +			_add_peer(peer); +		} break; +		case SYS_COMMAND_DEL_PEER: { +			ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1); +			_del_peer(peer); +		} break; +		case SYS_COMMAND_RELAY: { +			ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported()); +			ERR_FAIL_COND(p_packet_len < SYS_CMD_SIZE + 1); +			const uint8_t *packet = p_packet + SYS_CMD_SIZE; +			int len = p_packet_len - SYS_CMD_SIZE; +			bool should_process = false; +			if (get_unique_id() == 1) { // I am the server. +				// Direct messages to server should not go through relay. +				ERR_FAIL_COND(peer > 0 && !connected_peers.has(peer)); +				// Send relay packet. +				relay_buffer->seek(0); +				relay_buffer->put_u8(NETWORK_COMMAND_SYS); +				relay_buffer->put_u8(SYS_COMMAND_RELAY); +				relay_buffer->put_32(p_from); // Set the source. +				relay_buffer->put_data(packet, len); +				const Vector<uint8_t> data = relay_buffer->get_data_array(); +				multiplayer_peer->set_transfer_mode(p_mode); +				multiplayer_peer->set_transfer_channel(p_channel); +				if (peer > 0) { +					multiplayer_peer->set_target_peer(peer); +					multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position()); +				} else { +					for (const int &P : connected_peers) { +						// Not to sender, nor excluded. +						if (P == p_from || (peer < 0 && P != -peer)) { +							continue; +						} +						multiplayer_peer->set_target_peer(P); +						multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position()); +					} +				} +				if (peer == 0 || peer == -1) { +					should_process = true; +					peer = p_from; // Process as the source. +				} +			} else { +				ERR_FAIL_COND(p_from != 1); // Bug. +				should_process = true; +			} +			if (should_process) { +				remote_sender_id = peer; +				_process_packet(peer, packet, len); +				remote_sender_id = 0; +			} +		} break; +		default: { +			ERR_FAIL(); +		}  	}  }  void SceneMultiplayer::_add_peer(int p_id) { +	if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) { +		// Notify others of connection, and send connected peers to newly connected one. +		uint8_t buf[SYS_CMD_SIZE]; +		buf[0] = NETWORK_COMMAND_SYS; +		buf[1] = SYS_COMMAND_ADD_PEER; +		multiplayer_peer->set_transfer_channel(0); +		multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); +		for (const int &P : connected_peers) { +			// Send new peer to already connected. +			encode_uint32(p_id, &buf[2]); +			multiplayer_peer->set_target_peer(P); +			multiplayer_peer->put_packet(buf, sizeof(buf)); +			// Send already connected to new peer. +			encode_uint32(P, &buf[2]); +			multiplayer_peer->set_target_peer(p_id); +			multiplayer_peer->put_packet(buf, sizeof(buf)); +		} +	} +  	connected_peers.insert(p_id);  	cache->on_peer_change(p_id, true);  	replicator->on_peer_change(p_id, true); @@ -177,6 +299,23 @@ void SceneMultiplayer::_add_peer(int p_id) {  }  void SceneMultiplayer::_del_peer(int p_id) { +	if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) { +		// Notify others of disconnection. +		uint8_t buf[SYS_CMD_SIZE]; +		buf[0] = NETWORK_COMMAND_SYS; +		buf[1] = SYS_COMMAND_DEL_PEER; +		multiplayer_peer->set_transfer_channel(0); +		multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); +		encode_uint32(p_id, &buf[2]); +		for (const int &P : connected_peers) { +			if (P == p_id) { +				continue; +			} +			multiplayer_peer->set_target_peer(P); +			multiplayer_peer->put_packet(buf, sizeof(buf)); +		} +	} +  	replicator->on_peer_change(p_id, false);  	cache->on_peer_change(p_id, false);  	connected_peers.erase(p_id); @@ -209,11 +348,9 @@ Error SceneMultiplayer::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer  	packet_cache.write[0] = NETWORK_COMMAND_RAW;  	memcpy(&packet_cache.write[1], &r[0], p_data.size()); -	multiplayer_peer->set_target_peer(p_to);  	multiplayer_peer->set_transfer_channel(p_channel);  	multiplayer_peer->set_transfer_mode(p_mode); - -	return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); +	return send_command(p_to, packet_cache.ptr(), p_data.size() + 1);  }  void SceneMultiplayer::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { @@ -303,6 +440,15 @@ Error SceneMultiplayer::object_configuration_remove(Object *p_obj, Variant p_con  	return ERR_INVALID_PARAMETER;  } +void SceneMultiplayer::set_server_relay_enabled(bool p_enabled) { +	ERR_FAIL_COND_MSG(multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_DISCONNECTED, "Cannot change the server relay option while the multiplayer peer is active."); +	server_relay = p_enabled; +} + +bool SceneMultiplayer::is_server_relay_enabled() const { +	return server_relay; +} +  void SceneMultiplayer::_bind_methods() {  	ClassDB::bind_method(D_METHOD("set_root_path", "path"), &SceneMultiplayer::set_root_path);  	ClassDB::bind_method(D_METHOD("get_root_path"), &SceneMultiplayer::get_root_path); @@ -311,17 +457,22 @@ void SceneMultiplayer::_bind_methods() {  	ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &SceneMultiplayer::is_refusing_new_connections);  	ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &SceneMultiplayer::set_allow_object_decoding);  	ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &SceneMultiplayer::is_object_decoding_allowed); +	ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &SceneMultiplayer::set_server_relay_enabled); +	ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &SceneMultiplayer::is_server_relay_enabled);  	ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &SceneMultiplayer::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0));  	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");  	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");  	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); +	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled"); +  	ADD_PROPERTY_DEFAULT("refuse_new_connections", false);  	ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet")));  }  SceneMultiplayer::SceneMultiplayer() { +	relay_buffer.instantiate();  	replicator = Ref<SceneReplicationInterface>(memnew(SceneReplicationInterface(this)));  	rpc = Ref<SceneRPCInterface>(memnew(SceneRPCInterface(this)));  	cache = Ref<SceneCacheInterface>(memnew(SceneCacheInterface(this)));  |