summaryrefslogtreecommitdiff
path: root/Source/Android
diff options
context:
space:
mode:
authorTom Pratt <tom.pratt@outlook.com>2026-04-08 15:20:19 +0100
committerTom Pratt <tompratt@squareup.com>2026-05-19 12:02:54 +0200
commit3ed91742080194630bac072f4dbc9bdbffc94049 (patch)
treead5c7301c2f283fd1f49de1efa5008b36f0b2a95 /Source/Android
parent766374434c56bb8d77a5676870a6779b2ee1799f (diff)
Store netplay BootSessionData and use it to run the netplay game
Diffstat (limited to 'Source/Android')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.kt6
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/Netplay.kt17
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/netplay/ui/NetplaySetupActivity.kt9
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.kt7
-rw-r--r--Source/Android/jni/AndroidCommon/IDCache.cpp15
-rw-r--r--Source/Android/jni/AndroidCommon/IDCache.h2
-rw-r--r--Source/Android/jni/MainAndroid.cpp17
-rw-r--r--Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp13
8 files changed, 82 insertions, 4 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 2ead4b534b..2e2c295c7d 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
@@ -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)
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/Netplay.kt
index aaaca2b4fb..6f8532a15c 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/Netplay.kt
@@ -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)
+ }
}
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 71d83ddf81..ca1a892901 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
@@ -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 {
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 e4698ba43c..4ee4aee8aa 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
@@ -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) {
diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp
index 1cd6e257e6..9c41869f5b 100644
--- a/Source/Android/jni/AndroidCommon/IDCache.cpp
+++ b/Source/Android/jni/AndroidCommon/IDCache.cpp
@@ -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");
diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h
index e13646eb5a..1879ff2afb 100644
--- a/Source/Android/jni/AndroidCommon/IDCache.h
+++ b/Source/Android/jni/AndroidCommon/IDCache.h
@@ -33,6 +33,8 @@ jfieldID GetGameFileCacheManagerInstance();
jclass GetNetplayClass();
jfieldID GetNetPlayClientPointer();
+jfieldID GetNetplayBootSessionDataPointer();
+jmethodID GetNetplayOnBootGame();
jclass GetPairClass();
jmethodID GetPairConstructor();
diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp
index a25d4a1d4a..13471a6cd0 100644
--- a/Source/Android/jni/MainAndroid.cpp
+++ b/Source/Android/jni/MainAndroid.cpp
@@ -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"
@@ -616,6 +617,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)
{
diff --git a/Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp b/Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp
index 31bc977e2a..27e7d36ee6 100644
--- a/Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp
+++ b/Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp
@@ -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() {}