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))); |