diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index e8ff6da8c0..45391cb972 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -98,6 +98,10 @@ MSG_HASH( MSG_NETPLAY_CANNOT_PLAY, "Cannot switch to play mode." ) +MSG_HASH( + MSG_NETPLAY_PEER_PAUSED, + "Netplay peer \"%s\" paused." + ) MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_SHARED_CONTEXT, "Give hardware-rendered cores their own private context. Avoids having to assume hardware state changes inbetween frames." diff --git a/msg_hash.h b/msg_hash.h index 1bb53899e1..4e57829709 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -160,6 +160,7 @@ enum msg_hash_enums MSG_NETPLAY_CANNOT_PLAY_UNPRIVILEGED, MSG_NETPLAY_CANNOT_PLAY_NO_SLOTS, MSG_NETPLAY_CANNOT_PLAY, + MSG_NETPLAY_PEER_PAUSED, MSG_AUTODETECT, MSG_AUDIO_VOLUME, MSG_LIBRETRO_FRONTEND, diff --git a/network/netplay/README b/network/netplay/README index a02f4823af..ad6932bc3e 100644 --- a/network/netplay/README +++ b/network/netplay/README @@ -255,11 +255,18 @@ Description: serialized state is zlib compressed. Otherwise it is uncompressed. Command: PAUSE -Payload: None +Payload: + { + nickname: char[32] + } +Description: Indicates that the core is paused. The receiving peer should also pause. + The server should pass it on, using the known correct name rather than the + provided name. Command: RESUME Payload: None +Description: Indicates that the core is no longer paused. Command: CHEATS diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index 524c5e814f..ff68b1daee 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -467,6 +467,7 @@ static void netplay_flip_users(netplay_t *netplay) static void netplay_frontend_paused(netplay_t *netplay, bool paused) { size_t i; + uint32_t paused_ct; /* Nothing to do if we already knew this */ if (netplay->local_paused == paused) @@ -474,18 +475,33 @@ static void netplay_frontend_paused(netplay_t *netplay, bool paused) netplay->local_paused = paused; - /* If other connections are paused, nothing to say */ - if (netplay->remote_paused) + /* Communicating this is a bit odd: If exactly one other connection is + * paused, then we must tell them that we're unpaused, as from their + * perspective we are. If more than one other connection is paused, then our + * status as proxy means we are NOT unpaused to either of them. */ + paused_ct = 0; + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + if (connection->active && connection->paused) + paused_ct++; + } + if (paused_ct > 1) return; - /* Have to send manually because every buffer must be flushed immediately */ + /* Send our unpaused status. Must send manually because we must immediately + * flush the buffer: If we're paused, we won't be polled. */ for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED) { - netplay_send_raw_cmd(netplay, connection, - paused ? NETPLAY_CMD_PAUSE : NETPLAY_CMD_RESUME, NULL, 0); + if (paused) + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PAUSE, + netplay->nick, NETPLAY_NICK_LEN); + else + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_RESUME, + NULL, 0); /* We're not going to be polled, so we need to flush this command now */ netplay_send_flush(&connection->send_packet_buffer, connection->fd, true); diff --git a/network/netplay/netplay_io.c b/network/netplay/netplay_io.c index 781f2f5b13..fdeda81f1d 100644 --- a/network/netplay/netplay_io.c +++ b/network/netplay/netplay_io.c @@ -1095,10 +1095,43 @@ static bool netplay_get_cmd(netplay_t *netplay, } case NETPLAY_CMD_PAUSE: - connection->paused = true; - netplay->remote_paused = true; - netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_PAUSE, NULL, 0); - break; + { + char msg[512], nick[NETPLAY_NICK_LEN]; + msg[sizeof(msg)-1] = '\0'; + + /* Read in the paused nick */ + if (cmd_size != sizeof(nick)) + { + RARCH_ERR("NETPLAY_CMD_PAUSE received invalid payload size.\n"); + return netplay_cmd_nak(netplay, connection); + } + RECV(nick, sizeof(nick)) + { + RARCH_ERR("Failed to receive paused nickname.\n"); + return netplay_cmd_nak(netplay, connection); + } + nick[sizeof(nick)-1] = '\0'; + + /* We outright ignore pausing from spectators */ + if (connection->mode != NETPLAY_CONNECTION_PLAYING) + break; + + connection->paused = true; + netplay->remote_paused = true; + if (netplay->is_server) + { + snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_PEER_PAUSED), connection->nick); + netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_PAUSE, + connection->nick, NETPLAY_NICK_LEN); + } + else + { + snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_PEER_PAUSED), nick); + } + RARCH_LOG("%s\n", msg); + runloop_msg_queue_push(msg, 1, 180, false); + break; + } case NETPLAY_CMD_RESUME: remote_unpaused(netplay, connection);