Store netplay BootSessionData and use it to run the netplay game

This commit is contained in:
Tom Pratt 2026-04-08 15:20:19 +01:00
parent 01c8c4aee2
commit 12343ebf86
8 changed files with 82 additions and 4 deletions

View File

@ -342,6 +342,12 @@ object NativeLibrary {
@JvmStatic
external fun RunSystemMenu()
/**
* Begins emulation for a netplay session, using the BootSessionData provided by the host.
*/
@JvmStatic
external fun RunNetPlay(paths: Array<String>, riivolution: Boolean)
@JvmStatic
external fun ChangeDisc(path: String)

View File

@ -4,12 +4,23 @@
package org.dolphinemu.dolphinemu.features.netplay
import androidx.annotation.Keep
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
import org.dolphinemu.dolphinemu.features.netplay.model.ConnectionType
object Netplay {
@Keep
private var netPlayClientPointer: Long = 0
@Keep
private var bootSessionDataPointer: Long = 0
val isLaunching: Boolean
get() = bootSessionDataPointer != 0L
private val _launchGame = Channel<String>(Channel.CONFLATED)
val launchGame = _launchGame.receiveAsFlow()
@JvmStatic
external fun getNickname(): String
@ -103,4 +114,10 @@ object Netplay {
@JvmStatic
private external fun Join(): Long
@JvmStatic
fun onBootGame(gameFilePath: String, bootSessionDataPointer: Long) {
this.bootSessionDataPointer = bootSessionDataPointer
_launchGame.trySend(gameFilePath)
}
}

View File

@ -46,7 +46,12 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.activities.EmulationActivity
import org.dolphinemu.dolphinemu.features.netplay.Netplay
import org.dolphinemu.dolphinemu.features.netplay.model.ConnectionRole
import org.dolphinemu.dolphinemu.features.netplay.model.ConnectionType
import org.dolphinemu.dolphinemu.features.netplay.model.NetplaySetupViewModel
@ -64,6 +69,10 @@ class NetplaySetupActivity : AppCompatActivity(), ThemeProvider {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
Netplay.launchGame
.onEach { EmulationActivity.launch(this, it, false) }
.launchIn(lifecycleScope)
viewModel = ViewModelProvider(this)[NetplaySetupViewModel::class.java]
setContent {

View File

@ -13,6 +13,7 @@ import androidx.fragment.app.Fragment
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.settings.model.BooleanSetting
import org.dolphinemu.dolphinemu.features.settings.model.Settings
import org.dolphinemu.dolphinemu.overlay.InputOverlay
@ -214,6 +215,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (launchSystemMenu) {
Log.debug("[EmulationFragment] Starting emulation thread for the Wii Menu.")
NativeLibrary.RunSystemMenu()
} else if (Netplay.isLaunching) {
Log.debug("[EmulationFragment] Starting emulation thread for Netplay.")
val paths = requireNotNull(gamePaths) {
"Cannot start emulation without any game paths"
}
NativeLibrary.RunNetPlay(paths, riivolution)
} else {
Log.debug("[EmulationFragment] Starting emulation thread.")
val paths = requireNotNull(gamePaths) {

View File

@ -30,7 +30,8 @@ static jfieldID s_game_file_cache_manager_instance;
static jclass s_netplay_class;
static jfieldID s_net_play_client_pointer;
static jmethodID s_netplay_on_msg_start_game;
static jfieldID s_netplay_boot_session_data_pointer;
static jmethodID s_netplay_on_boot_game;
static jclass s_analytics_class;
static jmethodID s_get_analytics_value;
@ -247,6 +248,16 @@ jfieldID GetNetPlayClientPointer()
return s_net_play_client_pointer;
}
jfieldID GetNetplayBootSessionDataPointer()
{
return s_netplay_boot_session_data_pointer;
}
jmethodID GetNetplayOnBootGame()
{
return s_netplay_on_boot_game;
}
jclass GetPairClass()
{
return s_pair_class;
@ -655,6 +666,8 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
env->FindClass("org/dolphinemu/dolphinemu/features/netplay/Netplay");
s_netplay_class = reinterpret_cast<jclass>(env->NewGlobalRef(netplay_class));
s_net_play_client_pointer = env->GetStaticFieldID(netplay_class, "netPlayClientPointer", "J");
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");
env->DeleteLocalRef(netplay_class);
const jclass analytics_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Analytics");

View File

@ -33,6 +33,8 @@ jfieldID GetGameFileCacheManagerInstance();
jclass GetNetplayClass();
jfieldID GetNetPlayClientPointer();
jfieldID GetNetplayBootSessionDataPointer();
jmethodID GetNetplayOnBootGame();
jclass GetPairClass();
jmethodID GetPairConstructor();

View File

@ -35,6 +35,7 @@
#include "Core/AchievementManager.h"
#include "Core/Boot/Boot.h"
#include "jni/NetPlay/NetPlayUICallbacks.h"
#include "Core/BootManager.h"
#include "Core/CommonTitles.h"
#include "Core/ConfigLoaders/GameConfigLoader.h"
@ -614,6 +615,22 @@ Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2ZLjava_la
BootSessionData(GetJString(env, jSavestate), delete_state));
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RunNetPlay(
JNIEnv* env, jclass, jobjectArray jPaths, jboolean jRiivolution)
{
auto boot_session_data = std::unique_ptr<BootSessionData>(reinterpret_cast<BootSessionData*>(
env->GetStaticLongField(IDCache::GetNetplayClass(), IDCache::GetNetplayBootSessionDataPointer())));
if (!boot_session_data)
{
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetDisplayToastMsg(),
ToJString(env, "Netplay: no boot session data"), JNI_TRUE);
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetFinishEmulationActivity());
return;
}
env->SetStaticLongField(IDCache::GetNetplayClass(), IDCache::GetNetplayBootSessionDataPointer(), 0);
Run(env, JStringArrayToVector(env, jPaths), jRiivolution, std::move(*boot_session_data));
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RunSystemMenu(JNIEnv* env,
jclass)
{

View File

@ -1,8 +1,10 @@
// Copyright 2003 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/NetPlay/NetPlayUICallbacks.h"
#include "UICommon/GameFile.h"
#include "NetPlayUICallbacks.h"
#include "Core/Boot/Boot.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
namespace NetPlay {
@ -14,7 +16,12 @@ NetPlayUICallbacks::NetPlayUICallbacks(std::vector<std::shared_ptr<const UICommo
NetPlayUICallbacks::~NetPlayUICallbacks() = default;
void NetPlayUICallbacks::BootGame(const std::string&, std::unique_ptr<BootSessionData>) {}
void NetPlayUICallbacks::BootGame(const std::string& filename, std::unique_ptr<BootSessionData> boot_session_data) {
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNetplayClass(), IDCache::GetNetplayOnBootGame(),
ToJString(env, filename), reinterpret_cast<jlong>(boot_session_data.release()));
}
void NetPlayUICallbacks::StopGame() {}
bool NetPlayUICallbacks::IsHosting() const { return false; }
void NetPlayUICallbacks::Update() {}