mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-08-20 01:15:08 -05:00
Max buffer
This commit is contained in:
@@ -148,6 +148,9 @@ object Netplay {
|
||||
@JvmStatic
|
||||
external fun sendMessage(message: String)
|
||||
|
||||
@JvmStatic
|
||||
external fun adjustPadBufferSize(buffer: Int)
|
||||
|
||||
@JvmStatic
|
||||
private external fun ReleaseNetplayClient()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1007,4 +1007,5 @@ 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_max_buffer">Max buffer</string>
|
||||
</resources>
|
||||
|
||||
@@ -99,7 +99,6 @@ void NetPlayUICallbacks::OnPlayerDisconnect(const std::string&) {}
|
||||
|
||||
void NetPlayUICallbacks::OnPadBufferChanged(u32 buffer)
|
||||
{
|
||||
//TODO handle host input authority = true
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(IDCache::GetNetplayClass(), IDCache::GetNetplayOnPadBufferChanged(),
|
||||
static_cast<jint>(buffer));
|
||||
|
||||
@@ -103,6 +103,19 @@ Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_getIndexPassword(JNIEnv*
|
||||
return ToJString(env, Config::Get(Config::NETPLAY_INDEX_PASSWORD));
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_getClientBufferSize(JNIEnv*, jclass)
|
||||
{
|
||||
return static_cast<jint>(Config::Get(Config::NETPLAY_CLIENT_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_setClientBufferSize(JNIEnv*, jclass,
|
||||
jint buffer)
|
||||
{
|
||||
Config::SetBase(Config::NETPLAY_CLIENT_BUFFER_SIZE, static_cast<u32>(buffer));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_SaveSetup(
|
||||
JNIEnv* env, jclass, jstring jnickname, jstring traversalChoice, jstring jaddress,
|
||||
@@ -144,6 +157,14 @@ Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_sendMessage(JNIEnv* env,
|
||||
client->SendChatMessage(GetJString(env, jmessage));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_adjustPadBufferSize(JNIEnv* env, jclass,
|
||||
jint buffer)
|
||||
{
|
||||
if (auto* client = GetPointer(env))
|
||||
client->AdjustPadBufferSize(static_cast<u32>(buffer));
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_Join(JNIEnv* env, jclass)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user