mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-05-09 04:13:28 -05:00
Implement more NetPlayUICallbacks
Includes chat, game changes, pad buffer changes and host input authority. Merges them all into the chat window.
This commit is contained in:
parent
d82a9242a1
commit
a5bd27d731
|
|
@ -4,19 +4,31 @@
|
|||
package org.dolphinemu.dolphinemu.features.netplay
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
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.ConnectionType
|
||||
import org.dolphinemu.dolphinemu.features.netplay.model.NetplayMessage
|
||||
import org.dolphinemu.dolphinemu.features.netplay.model.Player
|
||||
|
||||
//TODO add other necessary @Keep annotations
|
||||
//TODO clear boot session data at appropriate time
|
||||
object Netplay {
|
||||
@Keep
|
||||
private var netPlayClientPointer: Long = 0
|
||||
|
|
@ -24,6 +36,8 @@ object Netplay {
|
|||
@Keep
|
||||
private var bootSessionDataPointer: Long = 0
|
||||
|
||||
private var sessionScope: CoroutineScope? = null
|
||||
|
||||
val isLaunching: Boolean
|
||||
get() = bootSessionDataPointer != 0L
|
||||
|
||||
|
|
@ -33,13 +47,51 @@ object Netplay {
|
|||
private val _connectionErrors = Channel<String>(Channel.BUFFERED)
|
||||
val connectionErrors = _connectionErrors.receiveAsFlow()
|
||||
|
||||
private val _messages = MutableSharedFlow<List<NetplayMessage>>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
)
|
||||
val messages = _messages.asSharedFlow()
|
||||
|
||||
private val _players = MutableSharedFlow<List<Player>>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
)
|
||||
val players = _players.asSharedFlow().distinctUntilChanged()
|
||||
|
||||
private val _chatMessages = MutableSharedFlow<String>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
)
|
||||
val chatMessages = _chatMessages.asSharedFlow()
|
||||
|
||||
private val _game = MutableSharedFlow<String>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
)
|
||||
val game = _game.asSharedFlow()
|
||||
|
||||
private val _hostInputAuthorityEnabled = MutableSharedFlow<Boolean>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
)
|
||||
val hostInputAuthorityEnabled = _hostInputAuthorityEnabled.asSharedFlow()
|
||||
|
||||
private val _padBuffer = MutableSharedFlow<Int>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
)
|
||||
val padBuffer = _padBuffer.asSharedFlow()
|
||||
|
||||
suspend fun join(): Boolean = withContext(Dispatchers.IO) {
|
||||
val scope = createSessionScope()
|
||||
|
||||
// Gather all messages that should appear in the chat window.
|
||||
mergeMessages()
|
||||
.runningFold(emptyList<NetplayMessage>()) { acc, msg -> listOf(msg) + acc }
|
||||
.onEach { _messages.tryEmit(it) }
|
||||
.launchIn(scope)
|
||||
|
||||
netPlayClientPointer = Join()
|
||||
val isConnected = netPlayClientPointer != 0L && isClientConnected()
|
||||
|
||||
|
|
@ -62,13 +114,29 @@ object Netplay {
|
|||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private fun releaseNetplayClient() {
|
||||
sessionScope?.cancel()
|
||||
sessionScope = null
|
||||
|
||||
if (netPlayClientPointer != 0L) {
|
||||
ReleaseNetplayClient()
|
||||
netPlayClientPointer = 0
|
||||
}
|
||||
|
||||
_launchGame.flush()
|
||||
_connectionErrors.flush()
|
||||
_players.resetReplayCache()
|
||||
_messages.resetReplayCache()
|
||||
_chatMessages.resetReplayCache()
|
||||
_game.resetReplayCache()
|
||||
_hostInputAuthorityEnabled.resetReplayCache()
|
||||
_padBuffer.resetReplayCache()
|
||||
}
|
||||
|
||||
private fun createSessionScope(): CoroutineScope {
|
||||
sessionScope?.cancel()
|
||||
return CoroutineScope(SupervisorJob() + Dispatchers.IO).also {
|
||||
sessionScope = it
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
|
|
@ -77,9 +145,19 @@ object Netplay {
|
|||
@JvmStatic
|
||||
external fun isClientConnected(): Boolean
|
||||
|
||||
@JvmStatic
|
||||
external fun sendMessage(message: String)
|
||||
|
||||
@JvmStatic
|
||||
private external fun ReleaseNetplayClient()
|
||||
|
||||
private fun mergeMessages(): Flow<NetplayMessage> = merge(
|
||||
chatMessages.map { NetplayMessage.Chat(it) },
|
||||
game.map { NetplayMessage.GameChanged(it) },
|
||||
hostInputAuthorityEnabled.map { NetplayMessage.HostInputAuthorityChanged(it) },
|
||||
padBuffer.map { NetplayMessage.BufferChanged(it) },
|
||||
)
|
||||
|
||||
// NetPlayUI callbacks
|
||||
|
||||
@JvmStatic
|
||||
|
|
@ -98,6 +176,26 @@ object Netplay {
|
|||
_players.tryEmit(players.toList())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onChatMessageReceived(message: String) {
|
||||
_chatMessages.tryEmit(message)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onHostInputAuthorityChanged(enabled: Boolean) {
|
||||
_hostInputAuthorityEnabled.tryEmit(enabled)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onGameChanged(game: String) {
|
||||
_game.tryEmit(game)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onPadBufferChanged(buffer: Int) {
|
||||
_padBuffer.tryEmit(buffer)
|
||||
}
|
||||
|
||||
// Settings
|
||||
|
||||
@JvmStatic
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package org.dolphinemu.dolphinemu.features.netplay.model
|
||||
|
||||
import android.content.Context
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
|
||||
sealed class NetplayMessage {
|
||||
abstract fun message(context: Context): String
|
||||
|
||||
class Chat(private val chatMessage: String) : NetplayMessage() {
|
||||
override fun message(context: Context) = chatMessage
|
||||
}
|
||||
|
||||
class GameChanged(private val game: String) : NetplayMessage() {
|
||||
override fun message(context: Context) =
|
||||
context.getString(R.string.netplay_message_game_changed, game)
|
||||
}
|
||||
|
||||
class HostInputAuthorityChanged(private val hostInputAuthorityEnabled: Boolean) : NetplayMessage() {
|
||||
override fun message(context: Context) = context.getString(
|
||||
R.string.netplay_message_host_input_authority_changed,
|
||||
if (hostInputAuthorityEnabled) "enabled" else "disabled"
|
||||
)
|
||||
}
|
||||
|
||||
class BufferChanged(private val buffer: Int) : NetplayMessage() {
|
||||
override fun message(context: Context) =
|
||||
context.getString(R.string.netplay_message_buffer_changed, buffer)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
package org.dolphinemu.dolphinemu.features.netplay.model
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
|
@ -14,6 +16,7 @@ import kotlinx.coroutines.flow.stateIn
|
|||
import kotlinx.coroutines.launch
|
||||
import org.dolphinemu.dolphinemu.features.netplay.Netplay
|
||||
|
||||
//TODO save settings
|
||||
class NetplayViewModel : ViewModel() {
|
||||
val launchGame = Netplay.launchGame
|
||||
|
||||
|
|
@ -23,12 +26,27 @@ class NetplayViewModel : ViewModel() {
|
|||
val players = Netplay.players
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
|
||||
|
||||
val messages = Netplay.messages
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
|
||||
|
||||
val game = Netplay.game
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "")
|
||||
|
||||
init {
|
||||
if (!Netplay.isClientConnected()) {
|
||||
_goBack.trySend(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendMessage(message: String) {
|
||||
val trimmedMessage = message.trim()
|
||||
if (trimmedMessage.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
Netplay.sendMessage(trimmedMessage)
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
|
|
|||
|
|
@ -44,6 +44,9 @@ class NetplayActivity : AppCompatActivity(), ThemeProvider {
|
|||
DolphinTheme {
|
||||
NetplayScreen(
|
||||
onBackClicked = { finish() },
|
||||
messages = viewModel.messages.collectAsState().value,
|
||||
onSendMessage = viewModel::sendMessage,
|
||||
game = viewModel.game.collectAsState().value,
|
||||
players = viewModel.players.collectAsState().value,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ 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.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
|
||||
|
|
@ -42,12 +45,15 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
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.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.features.netplay.model.NetplayMessage
|
||||
import org.dolphinemu.dolphinemu.features.netplay.model.Player
|
||||
import org.dolphinemu.dolphinemu.ui.theme.DolphinTheme
|
||||
import org.dolphinemu.dolphinemu.ui.theme.MenuSpacer
|
||||
|
|
@ -58,6 +64,9 @@ import org.dolphinemu.dolphinemu.ui.theme.PreviewTheme
|
|||
@Composable
|
||||
fun NetplayScreen(
|
||||
onBackClicked: () -> Unit,
|
||||
messages: List<NetplayMessage>,
|
||||
onSendMessage: (String) -> Unit,
|
||||
game: String,
|
||||
players: List<Player>,
|
||||
) {
|
||||
Scaffold(
|
||||
|
|
@ -82,11 +91,17 @@ fun NetplayScreen(
|
|||
|
||||
if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
LandscapeContent(
|
||||
messages = messages,
|
||||
onSendMessage = onSendMessage,
|
||||
game = game,
|
||||
players = players,
|
||||
modifier = modifier
|
||||
)
|
||||
} else {
|
||||
PortraitContent(
|
||||
messages = messages,
|
||||
onSendMessage = onSendMessage,
|
||||
game = game,
|
||||
players = players,
|
||||
modifier = modifier
|
||||
)
|
||||
|
|
@ -96,6 +111,9 @@ fun NetplayScreen(
|
|||
|
||||
@Composable
|
||||
private fun PortraitContent(
|
||||
messages: List<NetplayMessage>,
|
||||
onSendMessage: (String) -> Unit,
|
||||
game: String,
|
||||
players: List<Player>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -103,6 +121,8 @@ private fun PortraitContent(
|
|||
modifier = modifier
|
||||
) {
|
||||
Chat(
|
||||
messages = messages,
|
||||
onSendMessage = onSendMessage,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(0.3f)
|
||||
|
|
@ -112,6 +132,7 @@ private fun PortraitContent(
|
|||
MenuSpacer()
|
||||
|
||||
PLayersAndSettings(
|
||||
game = game,
|
||||
players = players,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
|
|
@ -122,6 +143,9 @@ private fun PortraitContent(
|
|||
|
||||
@Composable
|
||||
private fun LandscapeContent(
|
||||
messages: List<NetplayMessage>,
|
||||
onSendMessage: (String) -> Unit,
|
||||
game: String,
|
||||
players: List<Player>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -129,6 +153,8 @@ private fun LandscapeContent(
|
|||
modifier = modifier
|
||||
) {
|
||||
Chat(
|
||||
messages = messages,
|
||||
onSendMessage = onSendMessage,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxHeight()
|
||||
|
|
@ -136,6 +162,7 @@ private fun LandscapeContent(
|
|||
)
|
||||
|
||||
PLayersAndSettings(
|
||||
game = game,
|
||||
players = players,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
|
|
@ -146,6 +173,7 @@ private fun LandscapeContent(
|
|||
|
||||
@Composable
|
||||
private fun PLayersAndSettings(
|
||||
game: String,
|
||||
players: List<Player>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -153,6 +181,17 @@ private fun PLayersAndSettings(
|
|||
modifier = modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = game,
|
||||
onValueChange = {},
|
||||
label = { Text(stringResource(R.string.netplay_game_label)) },
|
||||
readOnly = true,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
)
|
||||
|
||||
MenuSpacer()
|
||||
|
||||
PlayersTable(
|
||||
rows = buildList {
|
||||
add(listOf("Player", "Ping", "Mapping"))
|
||||
|
|
@ -168,8 +207,24 @@ private fun PLayersAndSettings(
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun Chat(
|
||||
messages: List<NetplayMessage>,
|
||||
onSendMessage: (String) -> Unit,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
fun LazyListScope.messages() {
|
||||
items(messages.size) { index ->
|
||||
Text(text = messages[index].message(context))
|
||||
}
|
||||
}
|
||||
|
||||
var draftMessage by remember { mutableStateOf("") }
|
||||
val submitMessage = {
|
||||
onSendMessage(draftMessage)
|
||||
draftMessage = ""
|
||||
}
|
||||
|
||||
var showBottomSheet by remember { mutableStateOf(false) }
|
||||
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
|
||||
|
|
@ -187,6 +242,7 @@ private fun Chat(
|
|||
.weight(1f)
|
||||
.padding(horizontal = DolphinTheme.scaffoldPadding)
|
||||
) {
|
||||
messages()
|
||||
}
|
||||
|
||||
Row(
|
||||
|
|
@ -197,19 +253,23 @@ private fun Chat(
|
|||
.padding(horizontal = 8.dp)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = "",
|
||||
onValueChange = {},
|
||||
value = draftMessage,
|
||||
onValueChange = { draftMessage = it },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send),
|
||||
keyboardActions = KeyboardActions(onSend = { submitMessage() }),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
)
|
||||
TextButton(
|
||||
onClick = {},
|
||||
onClick = submitMessage,
|
||||
enabled = draftMessage.isNotBlank(),
|
||||
) {
|
||||
Text(stringResource(R.string.netplay_chat_send))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutlinedBox(
|
||||
onClick = { showBottomSheet = true },
|
||||
label = { Text(stringResource(R.string.netplay_chat_label)) },
|
||||
|
|
@ -221,6 +281,7 @@ private fun Chat(
|
|||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
messages()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -286,10 +347,7 @@ private fun PlayersTable(
|
|||
@Composable
|
||||
private fun NetplayScreenPreview() {
|
||||
PreviewTheme(darkTheme = false) {
|
||||
NetplayScreen(
|
||||
onBackClicked = {},
|
||||
players = previewPlayers,
|
||||
)
|
||||
PreviewNetplayScreen()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -297,10 +355,7 @@ private fun NetplayScreenPreview() {
|
|||
@Composable
|
||||
private fun NetplayScreenDarkPreview() {
|
||||
PreviewTheme(darkTheme = true) {
|
||||
NetplayScreen(
|
||||
onBackClicked = {},
|
||||
players = previewPlayers,
|
||||
)
|
||||
PreviewNetplayScreen()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,10 +363,7 @@ private fun NetplayScreenDarkPreview() {
|
|||
@Composable
|
||||
private fun LandscapeNetplayScreenPreview() {
|
||||
PreviewTheme(darkTheme = false) {
|
||||
NetplayScreen(
|
||||
onBackClicked = {},
|
||||
players = previewPlayers,
|
||||
)
|
||||
PreviewNetplayScreen()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -320,31 +372,42 @@ private fun LandscapeNetplayScreenPreview() {
|
|||
heightDp = 411,
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun LandscapeNetplayScreenDarkPreview() {
|
||||
PreviewTheme(darkTheme = true) {
|
||||
NetplayScreen(
|
||||
onBackClicked = {},
|
||||
players = previewPlayers,
|
||||
)
|
||||
PreviewNetplayScreen()
|
||||
}
|
||||
}
|
||||
|
||||
private val previewPlayers = listOf(
|
||||
Player(
|
||||
pid = 1,
|
||||
name = "Player 1",
|
||||
revision = "123",
|
||||
ping = 2,
|
||||
isHost = true,
|
||||
mapping = "m1"
|
||||
),
|
||||
Player(
|
||||
pid = 2,
|
||||
name = "Player 2",
|
||||
revision = "123",
|
||||
ping = 23,
|
||||
isHost = false,
|
||||
mapping = "m2"
|
||||
),
|
||||
)
|
||||
@Composable
|
||||
private fun PreviewNetplayScreen() {
|
||||
NetplayScreen(
|
||||
onBackClicked = {},
|
||||
players = listOf(
|
||||
Player(
|
||||
pid = 1,
|
||||
name = "Player 1",
|
||||
revision = "123",
|
||||
ping = 2,
|
||||
isHost = true,
|
||||
mapping = "m1"
|
||||
),
|
||||
Player(
|
||||
pid = 2,
|
||||
name = "Player 2",
|
||||
revision = "123",
|
||||
ping = 23,
|
||||
isHost = false,
|
||||
mapping = "m2"
|
||||
),
|
||||
),
|
||||
messages = buildList {
|
||||
repeat(5) {
|
||||
add(NetplayMessage.Chat("Hello"))
|
||||
}
|
||||
},
|
||||
onSendMessage = {},
|
||||
game = "Game name",
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -999,4 +999,8 @@ It can efficiently compress both junk data and encrypted Wii data.
|
|||
<string name="netplay_start">Start</string>
|
||||
<string name="netplay_chat_label">Chat</string>
|
||||
<string name="netplay_chat_send">Send</string>
|
||||
<string name="netplay_message_game_changed">Game changed to %1$s</string>
|
||||
<string name="netplay_message_buffer_changed">Buffer size changed to %1$d</string>
|
||||
<string name="netplay_message_host_input_authority_changed">"Host input authority %1$s"</string>
|
||||
<string name="netplay_game_label">Game</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ 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_connection_error;
|
||||
static jmethodID s_netplay_on_game_changed;
|
||||
static jmethodID s_netplay_on_host_input_authority_changed;
|
||||
static jmethodID s_netplay_on_pad_buffer_changed;
|
||||
static jmethodID s_netplay_on_chat_message_received;
|
||||
static jmethodID s_netplay_update;
|
||||
|
||||
static jclass s_netplay_player_class;
|
||||
|
|
@ -268,6 +272,26 @@ jmethodID GetNetplayOnConnectionError()
|
|||
return s_netplay_on_connection_error;
|
||||
}
|
||||
|
||||
jmethodID GetNetplayOnGameChanged()
|
||||
{
|
||||
return s_netplay_on_game_changed;
|
||||
}
|
||||
|
||||
jmethodID GetNetplayOnHostInputAuthorityChanged()
|
||||
{
|
||||
return s_netplay_on_host_input_authority_changed;
|
||||
}
|
||||
|
||||
jmethodID GetNetplayOnPadBufferChanged()
|
||||
{
|
||||
return s_netplay_on_pad_buffer_changed;
|
||||
}
|
||||
|
||||
jmethodID GetNetplayOnChatMessageReceived()
|
||||
{
|
||||
return s_netplay_on_chat_message_received;
|
||||
}
|
||||
|
||||
jmethodID GetNetplayUpdate()
|
||||
{
|
||||
return s_netplay_update;
|
||||
|
|
@ -694,6 +718,14 @@ 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_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");
|
||||
s_netplay_on_host_input_authority_changed =
|
||||
env->GetStaticMethodID(netplay_class, "onHostInputAuthorityChanged", "(Z)V");
|
||||
s_netplay_on_pad_buffer_changed =
|
||||
env->GetStaticMethodID(netplay_class, "onPadBufferChanged", "(I)V");
|
||||
s_netplay_on_chat_message_received =
|
||||
env->GetStaticMethodID(netplay_class, "onChatMessageReceived", "(Ljava/lang/String;)V");
|
||||
s_netplay_update = env->GetStaticMethodID(netplay_class, "onUpdate", "([Lorg/dolphinemu/dolphinemu/features/netplay/model/Player;)V");
|
||||
env->DeleteLocalRef(netplay_class);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ jfieldID GetNetPlayClientPointer();
|
|||
jfieldID GetNetplayBootSessionDataPointer();
|
||||
jmethodID GetNetplayOnBootGame();
|
||||
jmethodID GetNetplayOnConnectionError();
|
||||
jmethodID GetNetplayOnGameChanged();
|
||||
jmethodID GetNetplayOnHostInputAuthorityChanged();
|
||||
jmethodID GetNetplayOnPadBufferChanged();
|
||||
jmethodID GetNetplayOnChatMessageReceived();
|
||||
jmethodID GetNetplayUpdate();
|
||||
|
||||
jclass GetNetplayPlayerClass();
|
||||
|
|
|
|||
|
|
@ -59,13 +59,23 @@ void NetPlayUICallbacks::Update()
|
|||
env->DeleteLocalRef(player_array);
|
||||
}
|
||||
|
||||
void NetPlayUICallbacks::AppendChat(const std::string&) {}
|
||||
void NetPlayUICallbacks::AppendChat(const std::string& message)
|
||||
{
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(IDCache::GetNetplayClass(),
|
||||
IDCache::GetNetplayOnChatMessageReceived(),
|
||||
ToJString(env, message));
|
||||
}
|
||||
|
||||
void NetPlayUICallbacks::OnMsgChangeGame(const NetPlay::SyncIdentifier& sync_identifier,
|
||||
const std::string& netplay_name)
|
||||
{
|
||||
m_current_game_identifier = sync_identifier;
|
||||
m_current_game_name = netplay_name;
|
||||
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(IDCache::GetNetplayClass(), IDCache::GetNetplayOnGameChanged(),
|
||||
ToJString(env, netplay_name));
|
||||
}
|
||||
|
||||
void NetPlayUICallbacks::OnMsgChangeGBARom(int, const NetPlay::GBAConfig&) {}
|
||||
|
|
@ -86,8 +96,23 @@ void NetPlayUICallbacks::OnMsgStopGame() {}
|
|||
void NetPlayUICallbacks::OnMsgPowerButton() {}
|
||||
void NetPlayUICallbacks::OnPlayerConnect(const std::string&) {}
|
||||
void NetPlayUICallbacks::OnPlayerDisconnect(const std::string&) {}
|
||||
void NetPlayUICallbacks::OnPadBufferChanged(u32) {}
|
||||
void NetPlayUICallbacks::OnHostInputAuthorityChanged(bool) {}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void NetPlayUICallbacks::OnHostInputAuthorityChanged(bool enabled)
|
||||
{
|
||||
JNIEnv* env = IDCache::GetEnvForThread();
|
||||
env->CallStaticVoidMethod(IDCache::GetNetplayClass(),
|
||||
IDCache::GetNetplayOnHostInputAuthorityChanged(),
|
||||
static_cast<jboolean>(enabled));
|
||||
}
|
||||
|
||||
void NetPlayUICallbacks::OnDesync(u32, const std::string&) {}
|
||||
void NetPlayUICallbacks::OnConnectionLost() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,14 @@ Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_isClientConnected(JNIEnv
|
|||
return static_cast<jboolean>(GetPointer(env)->IsConnected());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_sendMessage(JNIEnv* env, jclass,
|
||||
jstring jmessage)
|
||||
{
|
||||
if (auto* client = GetPointer(env))
|
||||
client->SendChatMessage(GetJString(env, jmessage));
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_Join(JNIEnv* env, jclass)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user