diff options
Diffstat (limited to 'Source/Android/app/src/main/java')
| -rw-r--r-- | Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt | 2 | ||||
| -rw-r--r-- | Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplayManager.kt | 38 | ||||
| -rw-r--r-- | Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplaySession.kt (renamed from Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/Netplay.kt) | 140 | ||||
| -rw-r--r-- | Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplaySetupViewModel.kt | 52 | ||||
| -rw-r--r-- | Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplayViewModel.kt | 47 | ||||
| -rw-r--r-- | Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplayActivity.kt | 11 | ||||
| -rw-r--r-- | Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt | 6 | ||||
| -rw-r--r-- | Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt | 15 |
8 files changed, 195 insertions, 116 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt index 2e2c295c7d..b52b7b4fa4 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt @@ -346,7 +346,7 @@ object NativeLibrary { * Begins emulation for a netplay session, using the BootSessionData provided by the host. */ @JvmStatic - external fun RunNetPlay(paths: Array<String>, riivolution: Boolean) + external fun RunNetPlay(paths: Array<String>, riivolution: Boolean, bootSessionDataPointer: Long) @JvmStatic external fun ChangeDisc(path: String) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplayManager.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplayManager.kt new file mode 100644 index 0000000000..eae44e65be --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/NetplayManager.kt @@ -0,0 +1,38 @@ +// Copyright 2003 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.netplay + +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock + +object NetplayManager { + + private val mutex = Mutex() + + @Volatile + private var closeComplete: CompletableDeferred<Unit>? = null + + @Volatile + var activeSession: NetplaySession? = null + private set + + suspend fun createSession(): NetplaySession = mutex.withLock { + closeComplete?.await() + + // Sessions should be closed by UI navigation, but just in case. + activeSession?.closeBlocking() + + closeComplete = CompletableDeferred() + + NetplaySession( + onClosed = { + activeSession = null + closeComplete?.complete(Unit) + } + ).also { + activeSession = it + } + } +} 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/NetplaySession.kt index 1cec94f19d..f47d9ab198 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/NetplaySession.kt @@ -6,7 +6,6 @@ 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 @@ -29,14 +28,21 @@ import org.dolphinemu.dolphinemu.features.netplay.model.NetplayMessage import org.dolphinemu.dolphinemu.features.netplay.model.Player import org.dolphinemu.dolphinemu.features.netplay.model.SaveTransferProgress -object Netplay { - @Keep +class NetplaySession( + private val onClosed: (NetplaySession) -> Unit, +) { + + private var netPlayUICallbacksPointer: Long = nativeCreateUICallbacks() + private var netPlayClientPointer: Long = 0 - @Keep private var bootSessionDataPointer: Long = 0 - private var sessionScope: CoroutineScope? = null + private val sessionScope = CoroutineScope(SupervisorJob()) + + @Volatile + var isClosed = false + private set val isLaunching: Boolean get() = bootSessionDataPointer != 0L @@ -93,97 +99,96 @@ object Netplay { val saveTransferProgress = _saveTransferProgress.asStateFlow() suspend fun join(): Boolean = withContext(Dispatchers.IO) { - val scope = createSessionScope() + if (isClosed) throw IllegalStateException("Cannot join a closed session") - // 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) + .launchIn(sessionScope) - netPlayClientPointer = Join() - val isConnected = netPlayClientPointer != 0L && isClientConnected() + netPlayClientPointer = nativeJoin() - if (!isActive) { - releaseNetplayClient() + if (netPlayClientPointer == 0L || !isActive) { + closeBlocking() return@withContext false } - if (isConnected) { - return@withContext true + true + } + + fun sendMessage(message: String) = nativeSendMessage(message) + + fun adjustPadBufferSize(buffer: Int) = nativeAdjustPadBufferSize(buffer) + + fun consumeBootSessionData(): Long { + return bootSessionDataPointer.also { + bootSessionDataPointer = 0 } + } - releaseNetplayClient() - false + suspend fun close() = withContext(Dispatchers.IO) { + closeBlocking() } - suspend fun quit() = withContext(Dispatchers.IO) { - releaseNetplayClient() + @Synchronized + fun closeBlocking() { + if (isClosed) return + isClosed = true + sessionScope.cancel() + releaseNativeResources() + onClosed(this) } - @OptIn(ExperimentalCoroutinesApi::class) - private fun releaseNetplayClient() { - sessionScope?.cancel() - sessionScope = null + protected fun finalize() { + releaseNativeResources() + } - if (bootSessionDataPointer != 0L) { - ReleaseBootSessionData() + 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) }, + ) + + private fun releaseNativeResources() { + val currentBootSessionDataPointer = bootSessionDataPointer + if (currentBootSessionDataPointer != 0L) { bootSessionDataPointer = 0 + nativeReleaseBootSessionData(currentBootSessionDataPointer) } - if (netPlayClientPointer != 0L) { - ReleaseNetplayClient() + val currentNetPlayClientPointer = netPlayClientPointer + if (currentNetPlayClientPointer != 0L) { netPlayClientPointer = 0 + nativeReleaseClient(currentNetPlayClientPointer) } - _launchGame.flush() - _stopGame.flush() - _connectionErrors.flush() - _players.resetReplayCache() - _messages.resetReplayCache() - _chatMessages.resetReplayCache() - _game.resetReplayCache() - _hostInputAuthorityEnabled.resetReplayCache() - _padBuffer.resetReplayCache() - _saveTransferProgress.value = null - } - - private fun createSessionScope(): CoroutineScope { - sessionScope?.cancel() - return CoroutineScope(SupervisorJob() + Dispatchers.IO).also { - sessionScope = it + val currentNetPlayUICallbacksPointer = netPlayUICallbacksPointer + if (currentNetPlayUICallbacksPointer != 0L) { + netPlayUICallbacksPointer = 0 + nativeReleaseUICallbacks(currentNetPlayUICallbacksPointer) } } - @JvmStatic - private external fun Join(): Long + // JNI methods - @JvmStatic - external fun isClientConnected(): Boolean + private external fun nativeCreateUICallbacks(): Long - @JvmStatic - external fun sendMessage(message: String) + private external fun nativeJoin(): Long - @JvmStatic - external fun adjustPadBufferSize(buffer: Int) + private external fun nativeSendMessage(message: String) - @JvmStatic - private external fun ReleaseBootSessionData() + private external fun nativeAdjustPadBufferSize(buffer: Int) - @JvmStatic - private external fun ReleaseNetplayClient() + private external fun nativeReleaseUICallbacks(pointer: Long) - 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) }, - ) + private external fun nativeReleaseClient(pointer: Long) + + private external fun nativeReleaseBootSessionData(pointer: Long) // NetPlayUI callbacks @Keep - @JvmStatic fun onBootGame(gameFilePath: String, bootSessionDataPointer: Long) { this.bootSessionDataPointer = bootSessionDataPointer _stopGame.flush() @@ -191,57 +196,47 @@ object Netplay { } @Keep - @JvmStatic fun onStopGame() { _stopGame.trySend(Unit) } @Keep - @JvmStatic fun onConnectionLost() { _connectionLost.trySend(Unit) } @Keep - @JvmStatic fun onConnectionError(message: String) { _connectionErrors.trySend(message) } @Keep - @JvmStatic fun onUpdate(players: Array<Player>) { _players.tryEmit(players.toList()) } @Keep - @JvmStatic fun onChatMessageReceived(message: String) { _chatMessages.tryEmit(message) } @Keep - @JvmStatic fun onHostInputAuthorityChanged(enabled: Boolean) { _hostInputAuthorityEnabled.tryEmit(enabled) } @Keep - @JvmStatic fun onGameChanged(game: String) { _game.tryEmit(game) } @Keep - @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) } @Keep - @JvmStatic fun onShowChunkedProgressDialog(title: String, dataSize: Long, playerIds: IntArray) { val players = _players.replayCache.firstOrNull() _saveTransferProgress.value = SaveTransferProgress( @@ -258,7 +253,6 @@ object Netplay { } @Keep - @JvmStatic fun onSetChunkedProgress(playerId: Int, progress: Long) { val current = _saveTransferProgress.value _saveTransferProgress.value = current?.copy( @@ -273,11 +267,9 @@ object Netplay { } @Keep - @JvmStatic fun onHideChunkedProgressDialog() { _saveTransferProgress.value = null } - } private fun <T> Channel<T>.flush() { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplaySetupViewModel.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplaySetupViewModel.kt index 2a04e41c35..a9b34d3147 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplaySetupViewModel.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/model/NetplaySetupViewModel.kt @@ -3,22 +3,31 @@ package org.dolphinemu.dolphinemu.features.netplay.model import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.asFlow import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel.Factory.CONFLATED +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch -import org.dolphinemu.dolphinemu.features.netplay.Netplay +import org.dolphinemu.dolphinemu.features.netplay.NetplayManager import org.dolphinemu.dolphinemu.features.settings.model.IntSetting import org.dolphinemu.dolphinemu.features.settings.model.NativeConfig import org.dolphinemu.dolphinemu.features.settings.model.StringSetting import org.dolphinemu.dolphinemu.services.GameFileCacheManager -class NetplaySetupViewModel : ViewModel() { +class NetplaySetupViewModel( + private val netplayManager: NetplayManager, +) : ViewModel() { + private val _connectionRole = MutableStateFlow<ConnectionRole>(ConnectionRole.Connect) val connectionRole = _connectionRole.asStateFlow() @@ -45,7 +54,8 @@ class NetplaySetupViewModel : ViewModel() { private val _connecting = MutableStateFlow(false) val connecting = _connecting.asStateFlow() - val errors = Netplay.connectionErrors + private val _errors = MutableSharedFlow<String>(extraBufferCapacity = 8) + val errors = _errors.asSharedFlow() init { GameFileCacheManager.startLoad() @@ -89,16 +99,42 @@ class NetplaySetupViewModel : ViewModel() { } fun connect() { + if (_connecting.value) return + _connecting.value = true viewModelScope.launch { - GameFileCacheManager.isLoading().asFlow().first { it == false } - - if (Netplay.join()) { - _showNetplayScreen.trySend(Unit) + var errorForwarding: Job? = null + + try { + GameFileCacheManager.isLoading().asFlow().first { it == false } + + val session = netplayManager.createSession() + errorForwarding = session.connectionErrors + .onEach { _errors.emit(it) } + .launchIn(this) + + if (session.join()) { + _showNetplayScreen.trySend(Unit) + } + } finally { + errorForwarding?.cancel() + _connecting.value = false } + } + } + + override fun onCleared() { + super.onCleared() + // There should not be an active session at this point but in case one was created + // but launching the Netplay screen failed, close it. + netplayManager.activeSession?.closeBlocking() + } - _connecting.value = false + class Factory(private val netplayManager: NetplayManager) : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun <T : ViewModel> create(modelClass: Class<T>): T { + return NetplaySetupViewModel(netplayManager) as T } } } 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 66ad26aaec..96f9d7328b 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 @@ -3,51 +3,43 @@ package org.dolphinemu.dolphinemu.features.netplay.model import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope 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 -import org.dolphinemu.dolphinemu.features.netplay.Netplay +import org.dolphinemu.dolphinemu.features.netplay.NetplaySession import org.dolphinemu.dolphinemu.features.settings.model.IntSetting import org.dolphinemu.dolphinemu.features.settings.model.NativeConfig -class NetplayViewModel : ViewModel() { - val launchGame = Netplay.launchGame +class NetplayViewModel( + private val netplaySession: NetplaySession, +) : ViewModel() { - private val _goBack = Channel<Unit>(CONFLATED) - val goBack = _goBack.receiveAsFlow() + val launchGame = netplaySession.launchGame - val connectionLost = Netplay.connectionLost + val connectionLost = netplaySession.connectionLost - val players = Netplay.players + val players = netplaySession.players .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList()) - val messages = Netplay.messages + val messages = netplaySession.messages .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList()) - val game = Netplay.game + val game = netplaySession.game .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "") - val hostInputAuthority = Netplay.hostInputAuthorityEnabled + val hostInputAuthority = netplaySession.hostInputAuthorityEnabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) private val _maxBuffer = MutableStateFlow(IntSetting.NETPLAY_CLIENT_BUFFER_SIZE.int) val maxBuffer = _maxBuffer.asStateFlow() - val saveTransferProgress = Netplay.saveTransferProgress - - init { - if (!Netplay.isClientConnected()) { - _goBack.trySend(Unit) - } - } + val saveTransferProgress = netplaySession.saveTransferProgress fun sendMessage(message: String) { val trimmedMessage = message.trim() @@ -55,20 +47,29 @@ class NetplayViewModel : ViewModel() { return } - Netplay.sendMessage(trimmedMessage) + netplaySession.sendMessage(trimmedMessage) } fun setMaxBuffer(buffer: Int) { _maxBuffer.value = buffer IntSetting.NETPLAY_CLIENT_BUFFER_SIZE.setInt(NativeConfig.LAYER_BASE, buffer) - Netplay.adjustPadBufferSize(buffer) + netplaySession.adjustPadBufferSize(buffer) } @OptIn(DelicateCoroutinesApi::class) override fun onCleared() { super.onCleared() + // Closing the netplay session is a bit slow for the main thread so launch in + // GlobalScope and allow the activity and view model to finish immediately. GlobalScope.launch { - Netplay.quit() + netplaySession.close() + } + } + + class Factory(private val session: NetplaySession) : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun <T : ViewModel> create(modelClass: Class<T>): T { + return NetplayViewModel(session) as T } } } 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 8a8f860825..f2acf50107 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 @@ -16,6 +16,7 @@ import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.dolphinemu.dolphinemu.activities.EmulationActivity +import org.dolphinemu.dolphinemu.features.netplay.NetplayManager import org.dolphinemu.dolphinemu.features.netplay.model.NetplayViewModel import org.dolphinemu.dolphinemu.ui.main.ThemeProvider import org.dolphinemu.dolphinemu.ui.theme.DolphinTheme @@ -29,11 +30,13 @@ class NetplayActivity : AppCompatActivity(), ThemeProvider { enableEdgeToEdge() super.onCreate(savedInstanceState) - val viewModel = ViewModelProvider(this)[NetplayViewModel::class.java] + val session = NetplayManager.activeSession + if (session == null) { + finish() + return + } - viewModel.goBack - .onEach { finish() } - .launchIn(lifecycleScope) + val viewModel = ViewModelProvider(this, NetplayViewModel.Factory(session))[NetplayViewModel::class.java] viewModel.launchGame .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt index 8058b3ad96..145cf7d635 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt @@ -15,6 +15,7 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import org.dolphinemu.dolphinemu.features.netplay.NetplayManager import org.dolphinemu.dolphinemu.features.netplay.model.NetplaySetupViewModel import org.dolphinemu.dolphinemu.ui.main.ThemeProvider import org.dolphinemu.dolphinemu.ui.theme.DolphinTheme @@ -28,7 +29,10 @@ class NetplaySetupActivity : AppCompatActivity(), ThemeProvider { enableEdgeToEdge() super.onCreate(savedInstanceState) - val viewModel = ViewModelProvider(this)[NetplaySetupViewModel::class.java] + val viewModel = ViewModelProvider( + this, + NetplaySetupViewModel.Factory(NetplayManager) + )[NetplaySetupViewModel::class.java] viewModel.showNetplayScreen .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt index 49331d8a4c..c989cdc780 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.launch import org.dolphinemu.dolphinemu.NativeLibrary import org.dolphinemu.dolphinemu.activities.EmulationActivity import org.dolphinemu.dolphinemu.databinding.FragmentEmulationBinding -import org.dolphinemu.dolphinemu.features.netplay.Netplay +import org.dolphinemu.dolphinemu.features.netplay.NetplayManager import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting import org.dolphinemu.dolphinemu.features.settings.model.Settings import org.dolphinemu.dolphinemu.overlay.InputOverlay @@ -211,7 +211,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { // Don't load temporary saves when launching Netplay, this path can trigger // when a game starts due to orientation changes caused by a mismatch in menu // vs emulation activity orientations. - if (loadPreviousTemporaryState && !Netplay.isLaunching) { + val netplaySession = NetplayManager.activeSession + if (loadPreviousTemporaryState && netplaySession?.isLaunching != true) { Log.debug("[EmulationFragment] Starting emulation thread from previous state.") val paths = requireNotNull(gamePaths) { "Cannot start emulation without any game paths" @@ -221,16 +222,20 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { if (launchSystemMenu) { Log.debug("[EmulationFragment] Starting emulation thread for the Wii Menu.") NativeLibrary.RunSystemMenu() - } else if (Netplay.isLaunching) { + } else if (netplaySession?.isLaunching == true) { Log.debug("[EmulationFragment] Starting emulation thread for Netplay.") val paths = requireNotNull(gamePaths) { "Cannot start emulation without any game paths" } lifecycleScope.launch { - Netplay.stopGame.first() + netplaySession.stopGame.first() stopEmulation() } - NativeLibrary.RunNetPlay(paths, riivolution) + NativeLibrary.RunNetPlay( + paths, + riivolution, + netplaySession.consumeBootSessionData() + ) } else { Log.debug("[EmulationFragment] Starting emulation thread.") val paths = requireNotNull(gamePaths) { |
