1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

[MP] Let MultiplayerAPI handle packet relaying and peer signaling.

MultiplayerPeer changes:

- Adds is_server_relay_supported virtual method

Informs the upper MultiplayerAPI layer if it can signal peers connected
to the server to other clients, and perform packet relaying among them.

- Adds get_packet_channel and get_packet_mode virtual methods

Allows the MultiplayerAPI to retrieve the channel and transfer modes to
use when relaying the last received packet.

SceneMultiplayerPeer changes:

- Implement peer signaling and packet relaying when the MultiplayerPeer
  advertise they are supported.

ENet, WebRTC, WebSocket changes:

- Removed custom code for relaying from WebSocket and ENet, and let it
  be handled by the upper layer.
- Update WebRTC to split create_client, create_server, and create_mesh,
  with the latter behaving like the old initialize with
  "server_compatibility = false", and the first two supporting the upper
  layer relaying protocol.
This commit is contained in:
Fabio Alessandrelli
2022-10-08 20:50:19 +02:00
parent 03e5de37ae
commit 7536d15fe3
17 changed files with 465 additions and 446 deletions

View File

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