summaryrefslogtreecommitdiff
path: root/Source/Android/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Android/app/src')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplaySession.kt58
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/GameDigestProgress.kt14
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplayViewModel.kt2
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt1
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt97
-rw-r--r--Source/Android/app/src/main/res/values/strings.xml3
6 files changed, 173 insertions, 2 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplaySession.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplaySession.kt
index fbeba1510c..e0531a6477 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplaySession.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplaySession.kt
@@ -24,6 +24,7 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.runningFold
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
+import org.dolphinemu.dolphinemu.features.netplay.model.GameDigestProgress
import org.dolphinemu.dolphinemu.features.netplay.model.NetplayMessage
import org.dolphinemu.dolphinemu.features.netplay.model.Player
import org.dolphinemu.dolphinemu.features.netplay.model.SaveTransferProgress
@@ -104,6 +105,9 @@ class NetplaySession(
private val _saveTransferProgress = MutableStateFlow<SaveTransferProgress?>(null)
val saveTransferProgress = _saveTransferProgress.asStateFlow()
+ private val _gameDigestProgress = MutableStateFlow<GameDigestProgress?>(null)
+ val gameDigestProgress = _gameDigestProgress.asStateFlow()
+
suspend fun join(): Boolean = withContext(Dispatchers.IO) {
if (isClosed) throw IllegalStateException("Cannot join a closed session")
@@ -282,6 +286,60 @@ class NetplaySession(
fun onHideChunkedProgressDialog() {
_saveTransferProgress.value = null
}
+
+ @Keep
+ fun onShowGameDigestDialog(title: String) {
+ val players = _players.replayCache.firstOrNull()
+ _gameDigestProgress.value = GameDigestProgress(
+ title = title,
+ playerProgresses = players?.map { player ->
+ GameDigestProgress.PlayerProgress(
+ playerId = player.pid,
+ name = player.name,
+ progress = 0,
+ result = null,
+ )
+ } ?: emptyList(),
+ matches = null,
+ )
+ }
+
+ @Keep
+ fun onSetGameDigestProgress(playerId: Int, progress: Int) {
+ val current = _gameDigestProgress.value ?: return
+ _gameDigestProgress.value = current.copy(
+ playerProgresses = current.playerProgresses.map {
+ if (it.playerId == playerId) it.copy(progress = progress) else it
+ }
+ )
+ }
+
+ @Keep
+ fun onSetGameDigestResult(playerId: Int, result: String) {
+ val current = _gameDigestProgress.value ?: return
+ val updated = current.copy(
+ playerProgresses = current.playerProgresses.map {
+ if (it.playerId == playerId) it.copy(result = result) else it
+ }
+ )
+ val finished = updated.playerProgresses.all { it.result != null }
+ _gameDigestProgress.value = if (finished) {
+ val results = updated.playerProgresses.map { it.result }
+ updated.copy(matches = results.distinct().size == 1)
+ } else {
+ updated
+ }
+ }
+
+ /**
+ * Hosts send this when they dismiss their dialog even in a successful scenario. Ensuring
+ * that the value is cleared before a new game digest is started. Without this, StateFlow
+ * would not be a good choice.
+ */
+ @Keep
+ fun onAbortGameDigest() {
+ _gameDigestProgress.value = null
+ }
}
private fun <T> Channel<T>.flush() {
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/GameDigestProgress.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/GameDigestProgress.kt
new file mode 100644
index 0000000000..57a9cae78b
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/GameDigestProgress.kt
@@ -0,0 +1,14 @@
+package org.dolphinemu.dolphinemu.features.netplay.model
+
+data class GameDigestProgress(
+ val title: String,
+ val playerProgresses: List<PlayerProgress>,
+ val matches: Boolean?,
+) {
+ data class PlayerProgress(
+ val playerId: Int,
+ val name: String,
+ val progress: Int,
+ val result: String?,
+ )
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplayViewModel.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplayViewModel.kt
index 96f9d7328b..d0f86ed3f6 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplayViewModel.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplayViewModel.kt
@@ -41,6 +41,8 @@ class NetplayViewModel(
val saveTransferProgress = netplaySession.saveTransferProgress
+ val gameDigestProgress = netplaySession.gameDigestProgress
+
fun sendMessage(message: String) {
val trimmedMessage = message.trim()
if (trimmedMessage.isEmpty()) {
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt
index f2acf50107..d99c4da57a 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt
@@ -56,6 +56,7 @@ class NetplayActivity : AppCompatActivity(), ThemeProvider {
maxBuffer = viewModel.maxBuffer.collectAsState().value,
onMaxBufferChanged = viewModel::setMaxBuffer,
saveTransferProgress = viewModel.saveTransferProgress.collectAsState().value,
+ gameDigestProgress = viewModel.gameDigestProgress.collectAsState().value,
)
}
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt
index 40fe1fe5ec..71595ddb43 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt
@@ -50,6 +50,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -69,6 +70,7 @@ 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.GameDigestProgress
import org.dolphinemu.dolphinemu.features.netplay.model.NetplayMessage
import org.dolphinemu.dolphinemu.features.netplay.model.Player
import org.dolphinemu.dolphinemu.features.netplay.model.SaveTransferProgress
@@ -91,6 +93,7 @@ fun NetplayScreen(
onMaxBufferChanged: (Int) -> Unit,
players: List<Player>,
saveTransferProgress: SaveTransferProgress?,
+ gameDigestProgress: GameDigestProgress?,
) {
Scaffold(
topBar = {
@@ -136,16 +139,21 @@ fun NetplayScreen(
)
}
- var showConnectionLostDialog by remember { mutableStateOf(false) }
+ var showConnectionLostDialog by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(Unit) {
connectionLost.collect { showConnectionLostDialog = true }
}
- var dismissSaveTransferProgressDialog by remember { mutableStateOf(false) }
+ var dismissSaveTransferProgressDialog by rememberSaveable { mutableStateOf(false) }
if (saveTransferProgress == null) {
dismissSaveTransferProgressDialog = false
}
+ var dismissGameDigestDialog by rememberSaveable { mutableStateOf(false) }
+ if (gameDigestProgress == null) {
+ dismissGameDigestDialog = false
+ }
+
when {
showConnectionLostDialog -> {
AlertDialog(
@@ -165,6 +173,13 @@ fun NetplayScreen(
onDismiss = { dismissSaveTransferProgressDialog = true },
)
}
+
+ gameDigestProgress != null && !dismissGameDigestDialog -> {
+ GameDigestProgressDialog(
+ gameDigestProgress = gameDigestProgress,
+ onDismiss = { dismissGameDigestDialog = true },
+ )
+ }
}
}
}
@@ -548,6 +563,7 @@ private fun SaveTransferProgressDialog(
text = {
Column(
verticalArrangement = Arrangement.spacedBy(12.dp),
+ modifier = Modifier.verticalScroll(rememberScrollState()),
) {
saveTransferProgress.playerProgresses.forEachIndexed { index, playerProgress ->
SaveTransferProgressRow(
@@ -600,6 +616,82 @@ private fun SaveTransferProgressRow(
}
}
+@Composable
+private fun GameDigestProgressDialog(
+ gameDigestProgress: GameDigestProgress,
+ onDismiss: () -> Unit,
+) {
+ AlertDialog(
+ title = { Text(gameDigestProgress.title) },
+ text = {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ modifier = Modifier.verticalScroll(rememberScrollState()),
+ ) {
+ gameDigestProgress.playerProgresses.forEachIndexed { index, playerProgress ->
+ GameDigestPlayerRow(playerProgress)
+ if (index < gameDigestProgress.playerProgresses.lastIndex) {
+ HorizontalDivider()
+ }
+ }
+ if (gameDigestProgress.matches != null) {
+ Spacer(modifier = Modifier.height(4.dp))
+ Text(
+ text = stringResource(
+ if (gameDigestProgress.matches) {
+ R.string.netplay_game_digest_match
+ } else {
+ R.string.netplay_game_digest_mismatch
+ }
+ ),
+ style = MaterialTheme.typography.bodyLarge,
+ fontWeight = FontWeight.Medium,
+ )
+ }
+ }
+ },
+ confirmButton = {
+ if (gameDigestProgress.matches != null) {
+ TextButton(onClick = onDismiss) {
+ Text(stringResource(R.string.netplay_game_digest_close))
+ }
+ }
+ },
+ onDismissRequest = { onDismiss() },
+ )
+}
+
+@Composable
+private fun GameDigestPlayerRow(
+ playerProgress: GameDigestProgress.PlayerProgress,
+) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ LinearProgressIndicator(
+ progress = { playerProgress.progress / 100f },
+ modifier = Modifier.fillMaxWidth(),
+ )
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ if (playerProgress.result == null) {
+ Text(
+ text = playerProgress.name,
+ )
+ Spacer(modifier = Modifier.weight(1f))
+ Text(
+ text = "${playerProgress.progress}%",
+ )
+ } else {
+ Text(
+ text = "${playerProgress.name}:\u00A0${playerProgress.result}",
+ )
+ }
+ }
+ }
+}
+
@Preview
@Composable
private fun NetplayScreenPreview() {
@@ -671,6 +763,7 @@ private fun PreviewNetplayScreen() {
maxBuffer = 10,
onMaxBufferChanged = {},
saveTransferProgress = null,
+ gameDigestProgress = null,
// saveTransferProgress = SaveTransferProgress(
// title = "Title",
// totalSize = 1024L,
diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml
index 4e9c977faa..ef103432aa 100644
--- a/Source/Android/app/src/main/res/values/strings.xml
+++ b/Source/Android/app/src/main/res/values/strings.xml
@@ -1011,4 +1011,7 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="netplay_max_buffer">Max buffer</string>
<string name="netplay_connection_lost">Netplay connection lost</string>
<string name="netplay_save_transfer_progress_close">Close</string>
+ <string name="netplay_game_digest_match">The hashes match</string>
+ <string name="netplay_game_digest_mismatch">The hashes do not match</string>
+ <string name="netplay_game_digest_close">Close</string>
</resources>