Implement OnConnectionLost, show a dialog on the main netplay screen forcing the user to exit

This commit is contained in:
Tom Pratt 2026-04-25 17:40:40 +02:00
parent b21cbc63f7
commit ee1271e5b2
8 changed files with 51 additions and 3 deletions

View File

@ -47,6 +47,9 @@ object Netplay {
private val _stopGame = Channel<Unit>(Channel.CONFLATED)
val stopGame = _stopGame.receiveAsFlow()
private val _connectionLost = Channel<Unit>(Channel.CONFLATED)
val connectionLost = _connectionLost.receiveAsFlow()
private val _connectionErrors = Channel<String>(Channel.BUFFERED)
val connectionErrors = _connectionErrors.receiveAsFlow()
@ -63,7 +66,7 @@ object Netplay {
val players = _players.asSharedFlow().distinctUntilChanged()
private val _chatMessages = MutableSharedFlow<String>(
replay = 1,
extraBufferCapacity = 32,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val chatMessages = _chatMessages.asSharedFlow()
@ -180,6 +183,12 @@ object Netplay {
_stopGame.trySend(Unit)
}
@Keep
@JvmStatic
fun onConnectionLost() {
_connectionLost.trySend(Unit)
}
@JvmStatic
fun onConnectionError(message: String) {
_connectionErrors.trySend(message)

View File

@ -25,6 +25,8 @@ class NetplayViewModel : ViewModel() {
private val _goBack = Channel<Unit>(CONFLATED)
val goBack = _goBack.receiveAsFlow()
val connectionLost = Netplay.connectionLost
val players = Netplay.players
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())

View File

@ -44,6 +44,7 @@ class NetplayActivity : AppCompatActivity(), ThemeProvider {
DolphinTheme {
NetplayScreen(
onBackClicked = { finish() },
connectionLost = viewModel.connectionLost,
messages = viewModel.messages.collectAsState().value,
onSendMessage = viewModel::sendMessage,
game = viewModel.game.collectAsState().value,

View File

@ -28,6 +28,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Remove
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
@ -43,6 +44,7 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
@ -63,6 +65,8 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.features.netplay.model.NetplayMessage
import org.dolphinemu.dolphinemu.features.netplay.model.Player
@ -75,6 +79,7 @@ import org.dolphinemu.dolphinemu.ui.theme.PreviewTheme
@Composable
fun NetplayScreen(
onBackClicked: () -> Unit,
connectionLost: Flow<Unit>,
messages: List<NetplayMessage>,
onSendMessage: (String) -> Unit,
game: String,
@ -126,6 +131,22 @@ fun NetplayScreen(
modifier = modifier
)
}
var showConnectionLostDialog by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
connectionLost.collect { showConnectionLostDialog = true }
}
if (showConnectionLostDialog) {
AlertDialog(
text = { Text(stringResource(R.string.netplay_connection_lost)) },
confirmButton = {
TextButton(onClick = onBackClicked) {
Text(stringResource(R.string.ok))
}
},
onDismissRequest = onBackClicked,
)
}
}
}
@ -246,7 +267,7 @@ private fun PLayersAndSettings(
.fillMaxWidth()
)
}
if (hostInputAuthorityEnabled) {
MenuSpacer()
@ -539,6 +560,7 @@ private fun LandscapeNetplayScreenDarkPreview() {
private fun PreviewNetplayScreen() {
NetplayScreen(
onBackClicked = {},
connectionLost = emptyFlow(),
players = listOf(
Player(
pid = 1,

View File

@ -1008,4 +1008,5 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="netplay_players_ping">Ping</string>
<string name="netplay_players_mapping">Mapping</string>
<string name="netplay_max_buffer">Max buffer</string>
<string name="netplay_connection_lost">Netplay connection lost</string>
</resources>

View File

@ -33,6 +33,7 @@ static jfieldID s_net_play_client_pointer;
static jfieldID s_netplay_boot_session_data_pointer;
static jmethodID s_netplay_on_boot_game;
static jmethodID s_netplay_on_stop_game;
static jmethodID s_netplay_on_connection_lost;
static jmethodID s_netplay_on_connection_error;
static jmethodID s_netplay_on_game_changed;
static jmethodID s_netplay_on_host_input_authority_changed;
@ -273,6 +274,11 @@ jmethodID GetNetplayOnStopGame()
return s_netplay_on_stop_game;
}
jmethodID GetNetplayOnConnectionLost()
{
return s_netplay_on_connection_lost;
}
jmethodID GetNetplayOnConnectionError()
{
return s_netplay_on_connection_error;
@ -724,6 +730,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
s_netplay_boot_session_data_pointer = env->GetStaticFieldID(netplay_class, "bootSessionDataPointer", "J");
s_netplay_on_boot_game = env->GetStaticMethodID(netplay_class, "onBootGame", "(Ljava/lang/String;J)V");
s_netplay_on_stop_game = env->GetStaticMethodID(netplay_class, "onStopGame", "()V");
s_netplay_on_connection_lost = env->GetStaticMethodID(netplay_class, "onConnectionLost", "()V");
s_netplay_on_connection_error = env->GetStaticMethodID(netplay_class, "onConnectionError", "(Ljava/lang/String;)V");
s_netplay_on_game_changed =
env->GetStaticMethodID(netplay_class, "onGameChanged", "(Ljava/lang/String;)V");

View File

@ -36,6 +36,7 @@ jfieldID GetNetPlayClientPointer();
jfieldID GetNetplayBootSessionDataPointer();
jmethodID GetNetplayOnBootGame();
jmethodID GetNetplayOnStopGame();
jmethodID GetNetplayOnConnectionLost();
jmethodID GetNetplayOnConnectionError();
jmethodID GetNetplayOnGameChanged();
jmethodID GetNetplayOnHostInputAuthorityChanged();

View File

@ -135,7 +135,12 @@ void NetPlayUICallbacks::OnHostInputAuthorityChanged(bool enabled)
}
void NetPlayUICallbacks::OnDesync(u32, const std::string&) {}
void NetPlayUICallbacks::OnConnectionLost() {}
void NetPlayUICallbacks::OnConnectionLost()
{
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNetplayClass(), IDCache::GetNetplayOnConnectionLost());
}
void NetPlayUICallbacks::OnConnectionError(const std::string& message)
{