You've already forked godot
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:
@@ -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)));
|
||||
|
||||
Reference in New Issue
Block a user