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 34cee1e5bf..5ede907e85 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 @@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import org.dolphinemu.dolphinemu.features.netplay.Netplay +import org.dolphinemu.dolphinemu.services.GameFileCacheManager class NetplaySetupViewModel : ViewModel() { private val _connectionRole = MutableStateFlow(ConnectionRole.Connect) @@ -26,6 +27,10 @@ class NetplaySetupViewModel : ViewModel() { private val _connectPort = MutableStateFlow(Netplay.getConnectPort().toString()) val connectPort = _connectPort.asStateFlow() + init { + GameFileCacheManager.startLoad() + } + fun setConnectionRole(connectionRole: ConnectionRole) { _connectionRole.value = connectionRole } @@ -55,6 +60,10 @@ class NetplaySetupViewModel : ViewModel() { } fun connect() { + if (GameFileCacheManager.isLoading().value == true) { + return + } + Netplay.saveSetup( nickname = nickname.value, connectionType = connectionType.value, diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 18b486023a..1cd6e257e6 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -25,6 +25,13 @@ static jmethodID s_game_file_constructor; static jclass s_game_file_cache_class; static jfieldID s_game_file_cache_pointer; +static jclass s_game_file_cache_manager_class; +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 jclass s_analytics_class; static jmethodID s_get_analytics_value; @@ -220,6 +227,26 @@ jfieldID GetGameFileCachePointer() return s_game_file_cache_pointer; } +jclass GetGameFileCacheManagerClass() +{ + return s_game_file_cache_manager_class; +} + +jfieldID GetGameFileCacheManagerInstance() +{ + return s_game_file_cache_manager_instance; +} + +jclass GetNetplayClass() +{ + return s_netplay_class; +} + +jfieldID GetNetPlayClientPointer() +{ + return s_net_play_client_pointer; +} + jclass GetPairClass() { return s_pair_class; @@ -615,6 +642,21 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) s_game_file_cache_pointer = env->GetFieldID(game_file_cache_class, "pointer", "J"); env->DeleteLocalRef(game_file_cache_class); + const jclass game_file_cache_manager_class = + env->FindClass("org/dolphinemu/dolphinemu/services/GameFileCacheManager"); + s_game_file_cache_manager_class = + reinterpret_cast(env->NewGlobalRef(game_file_cache_manager_class)); + s_game_file_cache_manager_instance = env->GetStaticFieldID( + game_file_cache_manager_class, "gameFileCache", + "Lorg/dolphinemu/dolphinemu/model/GameFileCache;"); + env->DeleteLocalRef(game_file_cache_manager_class); + + const jclass netplay_class = + env->FindClass("org/dolphinemu/dolphinemu/features/netplay/Netplay"); + s_netplay_class = reinterpret_cast(env->NewGlobalRef(netplay_class)); + s_net_play_client_pointer = env->GetStaticFieldID(netplay_class, "netPlayClientPointer", "J"); + env->DeleteLocalRef(netplay_class); + const jclass analytics_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Analytics"); s_analytics_class = reinterpret_cast(env->NewGlobalRef(analytics_class)); s_get_analytics_value = env->GetStaticMethodID(s_analytics_class, "getValue", @@ -828,6 +870,8 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) env->DeleteGlobalRef(s_native_library_class); env->DeleteGlobalRef(s_game_file_class); env->DeleteGlobalRef(s_game_file_cache_class); + env->DeleteGlobalRef(s_game_file_cache_manager_class); + env->DeleteGlobalRef(s_netplay_class); env->DeleteGlobalRef(s_analytics_class); env->DeleteGlobalRef(s_pair_class); env->DeleteGlobalRef(s_hash_map_class); diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index 0aaa9feec3..e13646eb5a 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -28,6 +28,12 @@ jmethodID GetGameFileConstructor(); jclass GetGameFileCacheClass(); jfieldID GetGameFileCachePointer(); +jclass GetGameFileCacheManagerClass(); +jfieldID GetGameFileCacheManagerInstance(); + +jclass GetNetplayClass(); +jfieldID GetNetPlayClientPointer(); + jclass GetPairClass(); jmethodID GetPairConstructor(); diff --git a/Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp b/Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp index ab56458eff..31bc977e2a 100644 --- a/Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp +++ b/Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp @@ -3,9 +3,15 @@ #include "jni/NetPlay/NetPlayUICallbacks.h" +#include "jni/AndroidCommon/IDCache.h" + namespace NetPlay { -NetPlayUICallbacks::NetPlayUICallbacks() = default; +NetPlayUICallbacks::NetPlayUICallbacks(std::vector> games) + : m_games(std::move(games)) +{ +} + NetPlayUICallbacks::~NetPlayUICallbacks() = default; void NetPlayUICallbacks::BootGame(const std::string&, std::unique_ptr) {} @@ -13,9 +19,28 @@ void NetPlayUICallbacks::StopGame() {} bool NetPlayUICallbacks::IsHosting() const { return false; } void NetPlayUICallbacks::Update() {} void NetPlayUICallbacks::AppendChat(const std::string&) {} -void NetPlayUICallbacks::OnMsgChangeGame(const NetPlay::SyncIdentifier&, const std::string&) {} + +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; +} + void NetPlayUICallbacks::OnMsgChangeGBARom(int, const NetPlay::GBAConfig&) {} -void NetPlayUICallbacks::OnMsgStartGame() {} + +void NetPlayUICallbacks::OnMsgStartGame() +{ + JNIEnv* env = IDCache::GetEnvForThread(); + auto* client = reinterpret_cast( + env->GetStaticLongField(IDCache::GetNetplayClass(), IDCache::GetNetPlayClientPointer())); + if (client) + { + if (const auto game = FindGameFile(m_current_game_identifier)) + client->StartGame(game->GetFilePath()); + } +} + void NetPlayUICallbacks::OnMsgStopGame() {} void NetPlayUICallbacks::OnMsgPowerButton() {} void NetPlayUICallbacks::OnPlayerConnect(const std::string&) {} @@ -35,9 +60,26 @@ void NetPlayUICallbacks::OnIndexRefreshFailed(std::string) {} bool NetPlayUICallbacks::IsRecording() { return false; } std::shared_ptr -NetPlayUICallbacks::FindGameFile(const NetPlay::SyncIdentifier&, NetPlay::SyncIdentifierComparison*) +NetPlayUICallbacks::FindGameFile(const NetPlay::SyncIdentifier& sync_identifier, + NetPlay::SyncIdentifierComparison* found) { - return nullptr; + NetPlay::SyncIdentifierComparison temp; + if (!found) + found = &temp; + + *found = NetPlay::SyncIdentifierComparison::DifferentGame; + + std::shared_ptr result; + for (const auto& game : m_games) + { + const auto cmp = game->CompareSyncIdentifier(sync_identifier); + if (cmp < *found) + { + *found = cmp; + result = game; + } + } + return result; } std::string NetPlayUICallbacks::FindGBARomPath(const std::array&, std::string_view, int) { return {}; } diff --git a/Source/Android/jni/NetPlay/NetPlayUICallbacks.h b/Source/Android/jni/NetPlay/NetPlayUICallbacks.h index 18fdf1df93..acccfccabf 100644 --- a/Source/Android/jni/NetPlay/NetPlayUICallbacks.h +++ b/Source/Android/jni/NetPlay/NetPlayUICallbacks.h @@ -1,14 +1,18 @@ #pragma once +#include #include +#include +#include #include "Core/NetPlayClient.h" +#include "UICommon/GameFile.h" namespace NetPlay { class NetPlayUICallbacks : public NetPlay::NetPlayUI { public: - NetPlayUICallbacks(); + NetPlayUICallbacks(std::vector> games); ~NetPlayUICallbacks() override; void BootGame(const std::string& filename, @@ -52,6 +56,11 @@ public: void HideChunkedProgressDialog() override; void SetChunkedProgress(int pid, u64 progress) override; void SetHostWiiSyncData(std::vector titles, std::string redirect_folder) override; + +private: + std::vector> m_games; + NetPlay::SyncIdentifier m_current_game_identifier; + std::string m_current_game_name; }; } // namespace NetPlay diff --git a/Source/Android/jni/NetPlay/Netplay.cpp b/Source/Android/jni/NetPlay/Netplay.cpp index 42ed6483ce..2346b685e0 100644 --- a/Source/Android/jni/NetPlay/Netplay.cpp +++ b/Source/Android/jni/NetPlay/Netplay.cpp @@ -3,14 +3,18 @@ #include #include +#include #include #include "Common/CommonTypes.h" #include "Core/Config/NetplaySettings.h" #include "Core/NetPlayClient.h" +#include "UICommon/GameFile.h" +#include "UICommon/GameFileCache.h" #include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" #include "jni/NetPlay/NetPlayUICallbacks.h" extern "C" { @@ -121,7 +125,7 @@ Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_SaveSetup( } JNIEXPORT jlong JNICALL -Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_Join(JNIEnv*, jclass) +Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_Join(JNIEnv* env, jclass) { const std::string traversal_choice = Config::Get(Config::NETPLAY_TRAVERSAL_CHOICE); const bool is_traversal = traversal_choice == "traversal"; @@ -135,8 +139,17 @@ Java_org_dolphinemu_dolphinemu_features_netplay_Netplay_Join(JNIEnv*, jclass) const u16 traversal_port = Config::Get(Config::NETPLAY_TRAVERSAL_PORT); const std::string nickname = Config::Get(Config::NETPLAY_NICKNAME); + jobject jgame_file_cache = env->GetStaticObjectField( + IDCache::GetGameFileCacheManagerClass(), IDCache::GetGameFileCacheManagerInstance()); + auto* game_file_cache = reinterpret_cast( + env->GetLongField(jgame_file_cache, IDCache::GetGameFileCachePointer())); + + std::vector> games; + game_file_cache->ForEach( + [&games](const std::shared_ptr& game) { games.push_back(game); }); + auto* client = new NetPlay::NetPlayClient( - host_ip, host_port, new NetPlay::NetPlayUICallbacks(), nickname, + host_ip, host_port, new NetPlay::NetPlayUICallbacks(std::move(games)), nickname, NetPlay::NetTraversalConfig{is_traversal, traversal_host, traversal_port}); return reinterpret_cast(client);