dolphin/Source/Android/jni/NetPlay/NetPlayUICallbacks.cpp
Tom Pratt 2c82e5188e Game digest progress dialog
We just about get away with using a StateFlow in NetplaySession since the host sends AbortGameDigest when closing their own dialog. Without that it would be harder for the UI to distinguish between subsequent dialogs. If that wasn't the case then NetplaySession might need to expose the individual progress and result updates and have the view model assemble it into the overall GameDigestProgress.
2026-05-07 11:22:24 +02:00

359 lines
12 KiB
C++

// Copyright 2003 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "UICommon/GameFile.h"
#include "NetPlayUICallbacks.h"
#include "Core/Boot/Boot.h"
#include "Core/Core.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
namespace NetPlay {
NetPlayUICallbacks::NetPlayUICallbacks(jobject netplay_session,
std::vector<std::shared_ptr<const UICommon::GameFile>> games)
: m_netplay_session(IDCache::GetEnvForThread()->NewWeakGlobalRef(netplay_session)),
m_games(std::move(games))
{
m_state_changed_hook = Core::AddOnStateChangedCallback([this](Core::State state) {
if ((state == Core::State::Uninitialized || state == Core::State::Stopping) &&
!m_got_stop_request)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
auto* client = reinterpret_cast<NetPlay::NetPlayClient*>(
env->GetLongField(netplay_session, IDCache::GetNetPlayClientPointer()));
if (client)
client->RequestStopGame();
env->DeleteLocalRef(netplay_session);
}
});
}
NetPlayUICallbacks::~NetPlayUICallbacks()
{
JNIEnv* env = IDCache::GetEnvForThread();
env->DeleteWeakGlobalRef(m_netplay_session);
}
jobject NetPlayUICallbacks::GetNetplaySessionLocalRef(JNIEnv* env) const
{
return env->NewLocalRef(m_netplay_session);
}
void NetPlayUICallbacks::BootGame(const std::string& filename,
std::unique_ptr<BootSessionData> boot_session_data)
{
m_got_stop_request = false;
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnBootGame(), ToJString(env, filename),
reinterpret_cast<jlong>(boot_session_data.release()));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::StopGame()
{
if (m_got_stop_request)
return;
m_got_stop_request = true;
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnStopGame());
env->DeleteLocalRef(netplay_session);
}
bool NetPlayUICallbacks::IsHosting() const { return false; }
void NetPlayUICallbacks::Update()
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
auto* client = reinterpret_cast<NetPlay::NetPlayClient*>(
env->GetLongField(netplay_session, IDCache::GetNetPlayClientPointer()));
if (!client)
{
env->DeleteLocalRef(netplay_session);
return;
}
const std::vector<const NetPlay::Player*> players = client->GetPlayers();
jobjectArray player_array =
env->NewObjectArray(static_cast<jsize>(players.size()), IDCache::GetNetplayPlayerClass(), nullptr);
for (jsize i = 0; i < static_cast<jsize>(players.size()); i++)
{
const NetPlay::Player* player = players[i];
const std::string mapping = NetPlay::GetPlayerMappingString(
player->pid, client->GetPadMapping(), client->GetGBAConfig(), client->GetWiimoteMapping());
jobject player_obj = env->NewObject(
IDCache::GetNetplayPlayerClass(), IDCache::GetNetplayPlayerConstructor(),
static_cast<jint>(player->pid),
ToJString(env, player->name),
ToJString(env, player->revision),
static_cast<jint>(player->ping),
static_cast<jboolean>(player->IsHost()),
ToJString(env, mapping));
env->SetObjectArrayElement(player_array, i, player_obj);
env->DeleteLocalRef(player_obj);
}
env->CallVoidMethod(netplay_session, IDCache::GetNetplayUpdate(), player_array);
env->DeleteLocalRef(player_array);
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::AppendChat(const std::string& message)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnChatMessageReceived(),
ToJString(env, message));
env->DeleteLocalRef(netplay_session);
}
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();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnGameChanged(),
ToJString(env, netplay_name));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::OnMsgChangeGBARom(int, const NetPlay::GBAConfig&) {}
void NetPlayUICallbacks::OnMsgStartGame()
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
auto* client = reinterpret_cast<NetPlay::NetPlayClient*>(
env->GetLongField(netplay_session, IDCache::GetNetPlayClientPointer()));
if (client)
{
if (const auto game = FindGameFile(m_current_game_identifier))
client->StartGame(game->GetFilePath());
}
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::OnMsgStopGame() {}
void NetPlayUICallbacks::OnMsgPowerButton() {}
void NetPlayUICallbacks::OnPlayerConnect(const std::string&) {}
void NetPlayUICallbacks::OnPlayerDisconnect(const std::string&) {}
void NetPlayUICallbacks::OnPadBufferChanged(u32 buffer)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnPadBufferChanged(),
static_cast<jint>(buffer));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::OnHostInputAuthorityChanged(bool enabled)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnHostInputAuthorityChanged(),
static_cast<jboolean>(enabled));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::OnDesync(u32 frame, const std::string& player)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnDesync(),
static_cast<jint>(frame), ToJString(env, player));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::OnConnectionLost()
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnConnectionLost());
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::OnConnectionError(const std::string& message)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnConnectionError(),
ToJString(env, message));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::OnTraversalError(Common::TraversalClient::FailureReason) {}
void NetPlayUICallbacks::OnTraversalStateChanged(Common::TraversalClient::State) {}
void NetPlayUICallbacks::OnGameStartAborted() {}
void NetPlayUICallbacks::OnGolferChanged(bool, const std::string&) {}
void NetPlayUICallbacks::OnTtlDetermined(u8) {}
void NetPlayUICallbacks::OnIndexAdded(bool, std::string) {}
void NetPlayUICallbacks::OnIndexRefreshFailed(std::string) {}
bool NetPlayUICallbacks::IsRecording() { return false; }
std::shared_ptr<const UICommon::GameFile>
NetPlayUICallbacks::FindGameFile(const NetPlay::SyncIdentifier& sync_identifier,
NetPlay::SyncIdentifierComparison* found)
{
NetPlay::SyncIdentifierComparison temp;
if (!found)
found = &temp;
*found = NetPlay::SyncIdentifierComparison::DifferentGame;
std::shared_ptr<const UICommon::GameFile> 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<u8, 20>&, std::string_view, int) { return {}; }
void NetPlayUICallbacks::ShowGameDigestDialog(const std::string& title)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnShowGameDigestDialog(),
ToJString(env, title));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::SetGameDigestProgress(int pid, int progress)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnSetGameDigestProgress(),
static_cast<jint>(pid), static_cast<jint>(progress));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::SetGameDigestResult(int pid, const std::string& result)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnSetGameDigestResult(),
static_cast<jint>(pid), ToJString(env, result));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::AbortGameDigest()
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnAbortGameDigest());
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::ShowChunkedProgressDialog(const std::string& title, u64 data_size,
std::span<const int> players)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
jintArray j_players = env->NewIntArray(static_cast<jsize>(players.size()));
env->SetIntArrayRegion(j_players, 0, static_cast<jsize>(players.size()), players.data());
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnShowChunkedProgressDialog(),
ToJString(env, title), static_cast<jlong>(data_size), j_players);
env->DeleteLocalRef(j_players);
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::HideChunkedProgressDialog()
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnHideChunkedProgressDialog());
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::SetChunkedProgress(int pid, u64 progress)
{
JNIEnv* env = IDCache::GetEnvForThread();
jobject netplay_session = GetNetplaySessionLocalRef(env);
if (!netplay_session)
return;
env->CallVoidMethod(netplay_session, IDCache::GetNetplayOnSetChunkedProgress(),
static_cast<jint>(pid), static_cast<jlong>(progress));
env->DeleteLocalRef(netplay_session);
}
void NetPlayUICallbacks::SetHostWiiSyncData(std::vector<u64>, std::string) {}
} // namespace NetPlay