Add network mode picker for host

This commit is contained in:
Tom Pratt
2026-05-06 09:46:33 +02:00
committed by Tom Pratt
parent 1c2806dfe9
commit 76ae445694
8 changed files with 118 additions and 1 deletions

View File

@@ -162,6 +162,8 @@ class NetplaySession(
nativeSendMessage(message)
}
fun setHostInputAuthority(enable: Boolean) = nativeSetHostInputAuthority(enable)
fun adjustClientPadBufferSize(buffer: Int) = nativeAdjustClientPadBufferSize(buffer)
fun adjustServerPadBufferSize(buffer: Int) = nativeAdjustServerPadBufferSize(buffer)
@@ -243,6 +245,8 @@ class NetplaySession(
private external fun nativeSendMessage(message: String)
private external fun nativeSetHostInputAuthority(enable: Boolean)
private external fun nativeAdjustClientPadBufferSize(buffer: Int)
private external fun nativeAdjustServerPadBufferSize(buffer: Int)

View File

@@ -65,6 +65,11 @@ class NetplayViewModel(
val hostInputAuthority = netplaySession.hostInputAuthorityEnabled
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
private val _networkMode = MutableStateFlow(
NetworkMode.fromConfigValue(StringSetting.NETPLAY_NETWORK_MODE.string)
)
val networkMode = _networkMode.asStateFlow()
private val _buffer = MutableStateFlow(IntSetting.NETPLAY_BUFFER_SIZE.int)
val buffer = _buffer.asStateFlow()
@@ -107,6 +112,12 @@ class NetplayViewModel(
netplaySession.sendMessage(trimmedMessage)
}
fun setNetworkMode(mode: NetworkMode) {
_networkMode.value = mode
StringSetting.NETPLAY_NETWORK_MODE.setString(NativeConfig.LAYER_BASE, mode.configValue)
netplaySession.setHostInputAuthority(mode.isHostInputAuthority)
}
fun setBuffer(value: Int) {
_buffer.value = value
IntSetting.NETPLAY_BUFFER_SIZE.setInt(NativeConfig.LAYER_BASE, value)

View File

@@ -0,0 +1,18 @@
package org.dolphinemu.dolphinemu.features.netplay.model
import androidx.annotation.StringRes
import org.dolphinemu.dolphinemu.R
enum class NetworkMode(val configValue: String, @StringRes val labelId: Int) {
FAIR_INPUT_DELAY("fixeddelay", R.string.netplay_network_mode_fair_input_delay),
HOST_INPUT_AUTHORITY("hostinputauthority", R.string.netplay_network_mode_host_input_authority),
GOLF("golf", R.string.netplay_network_mode_golf);
val isHostInputAuthority: Boolean
get() = this == HOST_INPUT_AUTHORITY || this == GOLF
companion object {
fun fromConfigValue(value: String): NetworkMode =
entries.find { it.configValue == value } ?: FAIR_INPUT_DELAY
}
}

View File

@@ -60,6 +60,8 @@ class NetplayActivity : AppCompatActivity(), ThemeProvider {
gameFiles = viewModel.gameFiles.collectAsState().value,
players = viewModel.players.collectAsState().value,
hostInputAuthorityEnabled = viewModel.hostInputAuthority.collectAsState().value,
networkMode = viewModel.networkMode.collectAsState().value,
onNetworkModeChanged = viewModel::setNetworkMode,
buffer = viewModel.buffer.collectAsState().value,
onBufferChanged = viewModel::setBuffer,
clientBuffer = viewModel.clientBuffer.collectAsState().value,

View File

@@ -95,6 +95,7 @@ import org.dolphinemu.dolphinemu.features.netplay.model.GameDigestProgress
import org.dolphinemu.dolphinemu.features.netplay.model.JoinAddress
import org.dolphinemu.dolphinemu.features.netplay.model.JoinInfoType
import org.dolphinemu.dolphinemu.features.netplay.model.NetplayMessage
import org.dolphinemu.dolphinemu.features.netplay.model.NetworkMode
import org.dolphinemu.dolphinemu.features.netplay.model.Player
import org.dolphinemu.dolphinemu.features.netplay.model.SaveTransferProgress
import org.dolphinemu.dolphinemu.features.netplay.model.TraversalState
@@ -121,6 +122,8 @@ fun NetplayScreen(
onGameSelected: (GameFile) -> Unit,
gameFiles: List<GameFile>,
hostInputAuthorityEnabled: Boolean,
networkMode: NetworkMode,
onNetworkModeChanged: (NetworkMode) -> Unit,
buffer: Int,
onBufferChanged: (Int) -> Unit,
clientBuffer: Int,
@@ -178,6 +181,8 @@ fun NetplayScreen(
onShowGamePickerChanged = { showGamePicker = it },
players = players,
hostInputAuthorityEnabled = hostInputAuthorityEnabled,
networkMode = networkMode,
onNetworkModeChanged = onNetworkModeChanged,
buffer = buffer,
onBufferChanged = onBufferChanged,
clientBuffer = clientBuffer,
@@ -201,6 +206,8 @@ fun NetplayScreen(
onShowGamePickerChanged = { showGamePicker = it },
players = players,
hostInputAuthorityEnabled = hostInputAuthorityEnabled,
networkMode = networkMode,
onNetworkModeChanged = onNetworkModeChanged,
buffer = buffer,
onBufferChanged = onBufferChanged,
clientBuffer = clientBuffer,
@@ -290,6 +297,8 @@ private fun PortraitContent(
onShowGamePickerChanged: (Boolean) -> Unit,
players: List<Player>,
hostInputAuthorityEnabled: Boolean,
networkMode: NetworkMode,
onNetworkModeChanged: (NetworkMode) -> Unit,
buffer: Int,
onBufferChanged: (Int) -> Unit,
clientBuffer: Int,
@@ -324,6 +333,8 @@ private fun PortraitContent(
onShowGamePickerChanged = onShowGamePickerChanged,
players = players,
hostInputAuthorityEnabled = hostInputAuthorityEnabled,
networkMode = networkMode,
onNetworkModeChanged = onNetworkModeChanged,
buffer = buffer,
onBufferChanged = onBufferChanged,
clientBuffer = clientBuffer,
@@ -356,6 +367,8 @@ private fun LandscapeContent(
onShowGamePickerChanged: (Boolean) -> Unit,
players: List<Player>,
hostInputAuthorityEnabled: Boolean,
networkMode: NetworkMode,
onNetworkModeChanged: (NetworkMode) -> Unit,
buffer: Int,
onBufferChanged: (Int) -> Unit,
clientBuffer: Int,
@@ -394,6 +407,8 @@ private fun LandscapeContent(
onShowGamePickerChanged = onShowGamePickerChanged,
players = players,
hostInputAuthorityEnabled = hostInputAuthorityEnabled,
networkMode = networkMode,
onNetworkModeChanged = onNetworkModeChanged,
buffer = buffer,
onBufferChanged = onBufferChanged,
clientBuffer = clientBuffer,
@@ -421,6 +436,8 @@ private fun PlayersAndSettings(
onShowGamePickerChanged: (Boolean) -> Unit,
players: List<Player>,
hostInputAuthorityEnabled: Boolean,
networkMode: NetworkMode,
onNetworkModeChanged: (NetworkMode) -> Unit,
buffer: Int,
onBufferChanged: (Int) -> Unit,
clientBuffer: Int,
@@ -475,6 +492,15 @@ private fun PlayersAndSettings(
)
}
if (isHosting) {
MenuSpacer()
NetworkModeDropdown(
networkMode = networkMode,
onNetworkModeChanged = onNetworkModeChanged,
)
}
if (isHosting && !hostInputAuthorityEnabled) {
MenuSpacer()
@@ -902,6 +928,47 @@ private fun PlayersTable(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun NetworkModeDropdown(
networkMode: NetworkMode,
onNetworkModeChanged: (NetworkMode) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it },
) {
OutlinedTextField(
value = stringResource(networkMode.labelId),
onValueChange = {},
readOnly = true,
label = { Text(stringResource(R.string.netplay_network_mode_label)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
modifier = Modifier
.menuAnchor(MenuAnchorType.PrimaryNotEditable)
.fillMaxWidth(),
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
// No golf mode for now since it requires in game UI.
listOf(NetworkMode.FAIR_INPUT_DELAY, NetworkMode.HOST_INPUT_AUTHORITY).forEach { mode ->
DropdownMenuItem(
text = { Text(stringResource(mode.labelId)) },
onClick = {
onNetworkModeChanged(mode)
expanded = false
},
)
}
}
}
}
@Composable
private fun BufferInput(
value: Int,
@@ -1214,6 +1281,8 @@ private fun PreviewNetplayScreen() {
onGameSelected = {},
gameFiles = emptyList(),
hostInputAuthorityEnabled = true,
networkMode = NetworkMode.HOST_INPUT_AUTHORITY,
onNetworkModeChanged = {},
buffer = 5,
onBufferChanged = {},
clientBuffer = 10,

View File

@@ -116,7 +116,8 @@ enum class StringSetting(
),
NETPLAY_ADDRESS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_NETPLAY, "Address", "127.0.0.1"),
NETPLAY_NICKNAME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_NETPLAY, "Nickname", "Player"),
NETPLAY_GAME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_NETPLAY, "Game", "");
NETPLAY_GAME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_NETPLAY, "Game", ""),
NETPLAY_NETWORK_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_NETPLAY, "NetworkMode", "fixeddelay");
override val isOverridden: Boolean
get() = NativeConfig.isOverridden(file, section, key)

View File

@@ -1008,6 +1008,10 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="netplay_players_name">Name</string>
<string name="netplay_players_ping">Ping</string>
<string name="netplay_players_mapping">Mapping</string>
<string name="netplay_network_mode_label">Input mode</string>
<string name="netplay_network_mode_fair_input_delay">Fair Input Delay</string>
<string name="netplay_network_mode_host_input_authority">Host Input Authority</string>
<string name="netplay_network_mode_golf">Golf Mode</string>
<string name="netplay_buffer">Buffer</string>
<string name="netplay_client_buffer">Max buffer</string>
<string name="netplay_connection_lost">Netplay connection lost</string>

View File

@@ -49,6 +49,14 @@ Java_org_dolphinemu_dolphinemu_features_netplay_NetplaySession_nativeSendMessage
client->SendChatMessage(GetJString(env, jmessage));
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_netplay_NetplaySession_nativeSetHostInputAuthority(
JNIEnv* env, jobject obj, jboolean enable)
{
if (auto* server = GetServerPointer(env, obj))
server->SetHostInputAuthority(static_cast<bool>(enable));
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_netplay_NetplaySession_nativeAdjustClientPadBufferSize(JNIEnv* env,
jobject obj,