summaryrefslogtreecommitdiff
path: root/Source/Android/app/src/main/java/org
diff options
context:
space:
mode:
authorTom Pratt <tom.pratt@outlook.com>2026-04-15 22:23:02 +0200
committerTom Pratt <tompratt@squareup.com>2026-05-19 12:02:56 +0200
commit65e4c6015860c650d6afa844e86ca6f1ecd2c15a (patch)
treee0ca2dbf2aed5d282f74c32a740da868efcada35 /Source/Android/app/src/main/java/org
parente49451b28e9356e9378bf841ffdefa2ac3aa1b75 (diff)
Max buffer
Diffstat (limited to 'Source/Android/app/src/main/java/org')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/Netplay.kt11
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplayViewModel.kt14
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt3
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayScreen.kt150
4 files changed, 177 insertions, 1 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/Netplay.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/Netplay.kt
index 88baa9f495..319929e71e 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/Netplay.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/Netplay.kt
@@ -149,6 +149,9 @@ object Netplay {
external fun sendMessage(message: String)
@JvmStatic
+ external fun adjustPadBufferSize(buffer: Int)
+
+ @JvmStatic
private external fun ReleaseNetplayClient()
private fun mergeMessages(): Flow<NetplayMessage> = merge(
@@ -193,6 +196,8 @@ object Netplay {
@JvmStatic
fun onPadBufferChanged(buffer: Int) {
+ // Only for remote pad buffer settings. Ignore local max buffer changes.
+ if (_hostInputAuthorityEnabled.replayCache.firstOrNull() == true) return
_padBuffer.tryEmit(buffer)
}
@@ -240,6 +245,12 @@ object Netplay {
@JvmStatic
external fun getIndexPassword(): String
+ @JvmStatic
+ external fun getClientBufferSize(): Int
+
+ @JvmStatic
+ external fun setClientBufferSize(buffer: Int)
+
suspend fun saveSetup(
nickname: String,
connectionType: ConnectionType,
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 cc9dc00ae0..9da56d0c48 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
@@ -10,7 +10,9 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -32,6 +34,12 @@ class NetplayViewModel : ViewModel() {
val game = Netplay.game
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "")
+ val hostInputAuthority = Netplay.hostInputAuthorityEnabled
+ .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
+
+ private val _maxBuffer = MutableStateFlow(Netplay.getClientBufferSize())
+ val maxBuffer = _maxBuffer.asStateFlow()
+
init {
if (!Netplay.isClientConnected()) {
_goBack.trySend(Unit)
@@ -47,6 +55,12 @@ class NetplayViewModel : ViewModel() {
Netplay.sendMessage(trimmedMessage)
}
+ fun setMaxBuffer(buffer: Int) {
+ _maxBuffer.value = buffer
+ Netplay.setClientBufferSize(buffer)
+ Netplay.adjustPadBufferSize(buffer)
+ }
+
@OptIn(DelicateCoroutinesApi::class)
override fun onCleared() {
super.onCleared()
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 c4d0ded675..fec0823456 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
@@ -48,6 +48,9 @@ class NetplayActivity : AppCompatActivity(), ThemeProvider {
onSendMessage = viewModel::sendMessage,
game = viewModel.game.collectAsState().value,
players = viewModel.players.collectAsState().value,
+ hostInputAuthorityEnabled = viewModel.hostInputAuthority.collectAsState().value,
+ maxBuffer = viewModel.maxBuffer.collectAsState().value,
+ onMaxBufferChanged = viewModel::setMaxBuffer,
)
}
}
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 528c0d8a0b..73db806337 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
@@ -8,25 +8,32 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
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.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MediumTopAppBar
import androidx.compose.material3.ModalBottomSheet
@@ -48,8 +55,12 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+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 org.dolphinemu.dolphinemu.R
@@ -67,6 +78,9 @@ fun NetplayScreen(
messages: List<NetplayMessage>,
onSendMessage: (String) -> Unit,
game: String,
+ hostInputAuthorityEnabled: Boolean,
+ maxBuffer: Int,
+ onMaxBufferChanged: (Int) -> Unit,
players: List<Player>,
) {
Scaffold(
@@ -95,6 +109,9 @@ fun NetplayScreen(
onSendMessage = onSendMessage,
game = game,
players = players,
+ hostInputAuthorityEnabled = hostInputAuthorityEnabled,
+ maxBuffer = maxBuffer,
+ onMaxBufferChanged = onMaxBufferChanged,
modifier = modifier
)
} else {
@@ -103,6 +120,9 @@ fun NetplayScreen(
onSendMessage = onSendMessage,
game = game,
players = players,
+ hostInputAuthorityEnabled = hostInputAuthorityEnabled,
+ maxBuffer = maxBuffer,
+ onMaxBufferChanged = onMaxBufferChanged,
modifier = modifier
)
}
@@ -115,6 +135,9 @@ private fun PortraitContent(
onSendMessage: (String) -> Unit,
game: String,
players: List<Player>,
+ hostInputAuthorityEnabled: Boolean,
+ maxBuffer: Int,
+ onMaxBufferChanged: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
Column(
@@ -134,9 +157,12 @@ private fun PortraitContent(
PlayersAndSettings(
game = game,
players = players,
+ hostInputAuthorityEnabled = hostInputAuthorityEnabled,
+ maxBuffer = maxBuffer,
+ onMaxBufferChanged = onMaxBufferChanged,
modifier = Modifier
.weight(1f)
- .padding(horizontal = DolphinTheme.scaffoldPadding)
+ .padding(horizontal = DolphinTheme.scaffoldPadding),
)
}
}
@@ -147,6 +173,9 @@ private fun LandscapeContent(
onSendMessage: (String) -> Unit,
game: String,
players: List<Player>,
+ hostInputAuthorityEnabled: Boolean,
+ maxBuffer: Int,
+ onMaxBufferChanged: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
Row(
@@ -164,6 +193,9 @@ private fun LandscapeContent(
PlayersAndSettings(
game = game,
players = players,
+ hostInputAuthorityEnabled = hostInputAuthorityEnabled,
+ maxBuffer = maxBuffer,
+ onMaxBufferChanged = onMaxBufferChanged,
modifier = Modifier
.weight(1f)
.padding(horizontal = DolphinTheme.scaffoldPadding)
@@ -175,6 +207,9 @@ private fun LandscapeContent(
private fun PlayersAndSettings(
game: String,
players: List<Player>,
+ hostInputAuthorityEnabled: Boolean,
+ maxBuffer: Int,
+ onMaxBufferChanged: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
Column(
@@ -211,6 +246,16 @@ private fun PlayersAndSettings(
.fillMaxWidth()
)
}
+
+ if (hostInputAuthorityEnabled) {
+ MenuSpacer()
+
+ BufferInput(
+ value = maxBuffer,
+ onValueChange = onMaxBufferChanged,
+ label = stringResource(R.string.netplay_max_buffer),
+ )
+ }
}
}
@@ -353,6 +398,106 @@ private fun PlayersTable(
}
}
+@Composable
+private fun BufferInput(
+ value: Int,
+ onValueChange: (Int) -> Unit,
+ label: String,
+) {
+ val range = 0..99
+ var maybeEmptyValue by remember(value) {
+ mutableStateOf("$value")
+ }
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .fillMaxWidth()
+ ) {
+ OutlinedTextField(
+ value = TextFieldValue(
+ text = maybeEmptyValue,
+ selection = TextRange(maybeEmptyValue.length)
+ ),
+ onValueChange = { newValue ->
+ if (newValue.text.isEmpty()) {
+ maybeEmptyValue = newValue.text
+ return@OutlinedTextField
+ }
+ newValue.text.toIntOrNull()?.let {
+ if (it in range) {
+ onValueChange(it)
+ }
+ }
+ },
+ label = { Text(label) },
+ textStyle = LocalTextStyle.current.copy(
+ textAlign = TextAlign.Center
+ ),
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Number,
+ ),
+ singleLine = true,
+ modifier = Modifier
+ .weight(1f)
+ )
+
+ Spacer(modifier = Modifier.width(12.dp))
+
+ Button(
+ onClick = {
+ if (maybeEmptyValue.isEmpty()) {
+ maybeEmptyValue = "0"
+ onValueChange(0)
+ } else {
+ val newValue = value - 1
+ if (newValue in range) {
+ onValueChange(newValue)
+ }
+ }
+ },
+ shape = RoundedCornerShape(
+ topStartPercent = 50,
+ topEndPercent = 0,
+ bottomEndPercent = 0,
+ bottomStartPercent = 50,
+ ),
+ modifier = Modifier
+ .height(60.dp)
+ .padding(top = 8.dp)
+ ) {
+ Icon(Icons.Filled.Remove, contentDescription = "Back")
+ }
+
+ Spacer(modifier = Modifier.width(2.dp))
+
+ Button(
+ onClick = {
+ if (maybeEmptyValue.isEmpty()) {
+ maybeEmptyValue = "0"
+ onValueChange(0)
+ } else {
+ val newValue = value + 1
+ if (newValue in range) {
+ onValueChange(newValue)
+ }
+ }
+ },
+ shape = RoundedCornerShape(
+ topStartPercent = 0,
+ topEndPercent = 50,
+ bottomEndPercent = 50,
+ bottomStartPercent = 0,
+ ),
+ modifier = Modifier
+ .height(60.dp)
+ .padding(top = 8.dp)
+ ) {
+ Icon(Icons.Filled.Add, contentDescription = "Back")
+ }
+ }
+}
+
@Preview
@Composable
private fun NetplayScreenPreview() {
@@ -419,5 +564,8 @@ private fun PreviewNetplayScreen() {
},
onSendMessage = {},
game = "Game name",
+ hostInputAuthorityEnabled = true,
+ maxBuffer = 10,
+ onMaxBufferChanged = {},
)
}