NetPlay: GBA Support

This commit is contained in:
Bonta 2021-07-04 13:33:58 +02:00
parent b73d16a71a
commit 45f2461a53
16 changed files with 491 additions and 85 deletions

View File

@ -122,6 +122,7 @@
#define GC_MEMCARD_NETPLAY "NetPlayTemp"
#define GBA_BIOS "gba_bios.bin"
#define GBA_SAVE_NETPLAY "NetPlayTemp"
#define WII_STATE "state.dat"

View File

@ -57,5 +57,6 @@ const Info<std::string> NETPLAY_NETWORK_MODE{{System::Main, "NetPlay", "NetworkM
"fixeddelay"};
const Info<bool> NETPLAY_SYNC_ALL_WII_SAVES{{System::Main, "NetPlay", "SyncAllWiiSaves"}, false};
const Info<bool> NETPLAY_GOLF_MODE_OVERLAY{{System::Main, "NetPlay", "GolfModeOverlay"}, true};
const Info<bool> NETPLAY_HIDE_REMOTE_GBAS{{System::Main, "NetPlay", "HideRemoteGBAs"}, false};
} // namespace Config

View File

@ -50,5 +50,6 @@ extern const Info<bool> NETPLAY_STRICT_SETTINGS_SYNC;
extern const Info<std::string> NETPLAY_NETWORK_MODE;
extern const Info<bool> NETPLAY_SYNC_ALL_WII_SAVES;
extern const Info<bool> NETPLAY_GOLF_MODE_OVERLAY;
extern const Info<bool> NETPLAY_HIDE_REMOTE_GBAS;
} // namespace Config

View File

@ -133,6 +133,11 @@ public:
layer->Set(Config::SESSION_GCI_FOLDER_CURRENT_GAME_ONLY, true);
}
for (size_t i = 0; i < m_settings.m_GBARomPaths.size(); ++i)
{
layer->Set(Config::MAIN_GBA_ROM_PATHS[i], m_settings.m_GBARomPaths[i]);
}
// Check To Override Client's Cheat Codes
if (m_settings.m_SyncCodes && !m_settings.m_IsHosting)
{

View File

@ -219,7 +219,8 @@ bool Core::Start(u64 gc_ticks)
m_core->getGameTitle(m_core, game_title.data());
m_game_title = game_title.data();
m_save_path = GetSavePath(m_rom_path, m_device_number);
m_save_path = NetPlay::IsNetPlayRunning() ? NetPlay::GetGBASavePath(m_device_number) :
GetSavePath(m_rom_path, m_device_number);
if (!m_save_path.empty() && !LoadSave(m_save_path.c_str()))
return false;
}

View File

@ -33,12 +33,18 @@
#include "Common/Version.h"
#include "Core/ActionReplay.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/NetplaySettings.h"
#include "Core/Config/SessionSettings.h"
#include "Core/ConfigManager.h"
#include "Core/GeckoCode.h"
#include "Core/HW/EXI/EXI_DeviceIPL.h"
#ifdef HAS_LIBMGBA
#include "Core/HW/GBACore.h"
#endif
#include "Core/HW/GBAPad.h"
#include "Core/HW/GCMemcard/GCMemcard.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/SI/SI_Device.h"
#include "Core/HW/SI/SI_DeviceGCController.h"
@ -71,7 +77,7 @@ static std::mutex crit_netplay_client;
static NetPlayClient* netplay_client = nullptr;
static std::unique_ptr<IOS::HLE::FS::FileSystem> s_wii_sync_fs;
static std::vector<u64> s_wii_sync_titles;
static bool s_si_poll_batching;
static bool s_si_poll_batching = false;
// called from ---GUI--- thread
NetPlayClient::~NetPlayClient()
@ -463,6 +469,35 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
}
break;
case NP_MSG_GBA_CONFIG:
{
for (size_t i = 0; i < m_gba_config.size(); ++i)
{
auto& config = m_gba_config[i];
const auto old_config = config;
packet >> config.enabled >> config.has_rom >> config.title;
for (auto& data : config.hash)
packet >> data;
if (std::tie(config.has_rom, config.title, config.hash) !=
std::tie(old_config.has_rom, old_config.title, old_config.hash))
{
m_dialog->OnMsgChangeGBARom(static_cast<int>(i), config);
m_net_settings.m_GBARomPaths[i] =
config.has_rom ?
m_dialog->FindGBARomPath(config.hash, config.title, static_cast<int>(i)) :
"";
}
}
SendGameStatus();
UpdateDevices();
m_dialog->Update();
}
break;
case NP_MSG_WIIMOTE_MAPPING:
{
for (PlayerId& mapping : m_wiimote_map)
@ -482,8 +517,12 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
packet >> map;
GCPadStatus pad;
packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >>
pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
packet >> pad.button;
if (!m_gba_config.at(map).enabled)
{
packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >>
pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
}
// Trusting server for good map value (>=0 && <4)
// add to pad buffer
@ -501,8 +540,12 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
packet >> map;
GCPadStatus pad;
packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >>
pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
packet >> pad.button;
if (!m_gba_config.at(map).enabled)
{
packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >>
pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
}
// Trusting server for good map value (>=0 && <4)
// write to last status
@ -602,14 +645,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
// update gui
m_dialog->OnMsgChangeGame(m_selected_game, netplay_name);
sf::Packet game_status_packet;
game_status_packet << static_cast<MessageId>(NP_MSG_GAME_STATUS);
SyncIdentifierComparison result;
m_dialog->FindGameFile(m_selected_game, &result);
game_status_packet << static_cast<u32>(result);
Send(game_status_packet);
SendGameStatus();
sf::Packet client_capabilities_packet;
client_capabilities_packet << static_cast<MessageId>(NP_MSG_CLIENT_CAPABILITIES);
@ -739,6 +775,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
packet >> m_net_settings.m_GolfMode;
packet >> m_net_settings.m_UseFMA;
packet >> m_net_settings.m_HideRemoteGBAs;
m_net_settings.m_IsHosting = m_local_player->IsHost();
m_net_settings.m_HostInputAuthority = m_host_input_authority;
@ -1055,6 +1092,29 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
}
break;
case SYNC_SAVE_DATA_GBA:
{
if (m_local_player->IsHost())
return 0;
u8 slot;
packet >> slot;
const std::string path =
fmt::format("{}{}{}.sav", File::GetUserPath(D_GBAUSER_IDX), GBA_SAVE_NETPLAY, slot + 1);
if (File::Exists(path) && !File::Delete(path))
{
PanicAlertFmtT("Failed to delete NetPlay GBA{0} save file. Verify your write permissions.",
slot + 1);
SyncSaveDataResponse(false);
return 0;
}
const bool success = DecompressPacketIntoFile(packet, path);
SyncSaveDataResponse(success);
}
break;
default:
PanicAlertFmtT("Unknown SYNC_SAVE_DATA message received with id: {0}", sub_id);
break;
@ -1411,26 +1471,13 @@ void NetPlayClient::GetPlayerList(std::string& list, std::vector<int>& pid_list)
std::ostringstream ss;
const auto enumerate_player_controller_mappings = [&ss](const PadMappingArray& mappings,
const Player& player) {
for (size_t i = 0; i < mappings.size(); i++)
{
if (mappings[i] == player.pid)
ss << i + 1;
else
ss << '-';
}
};
for (const auto& entry : m_players)
{
const Player& player = entry.second;
ss << player.name << "[" << static_cast<int>(player.pid) << "] : " << player.revision << " | ";
ss << player.name << "[" << static_cast<int>(player.pid) << "] : " << player.revision << " | "
<< GetPlayerMappingString(player.pid, m_pad_map, m_gba_config, m_wiimote_map) << " |\n";
enumerate_player_controller_mappings(m_pad_map, player);
enumerate_player_controller_mappings(m_wiimote_map, player);
ss << " |\nPing: " << player.ping << "ms\n";
ss << "Ping: " << player.ping << "ms\n";
ss << "Status: ";
switch (player.game_status)
@ -1492,8 +1539,12 @@ void NetPlayClient::AddPadStateToPacket(const int in_game_pad, const GCPadStatus
sf::Packet& packet)
{
packet << static_cast<PadIndex>(in_game_pad);
packet << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX
<< pad.substickY << pad.triggerLeft << pad.triggerRight << pad.isConnected;
packet << pad.button;
if (!m_gba_config[in_game_pad].enabled)
{
packet << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX
<< pad.substickY << pad.triggerLeft << pad.triggerRight << pad.isConnected;
}
}
// called from ---CPU--- thread
@ -1648,11 +1699,13 @@ void NetPlayClient::UpdateDevices()
for (auto player_id : m_pad_map)
{
// Use local controller types for local controllers if they are compatible
// Only GCController-like controllers are supported, GBA and similar
// exotic devices are not supported on netplay.
if (player_id == m_local_player->pid)
if (m_gba_config[pad].enabled && player_id > 0)
{
SerialInterface::ChangeDevice(SerialInterface::SIDEVICE_GC_GBA_EMULATED, pad);
}
else if (player_id == m_local_player->pid)
{
// Use local controller types for local controllers if they are compatible
if (SerialInterface::SIDevice_IsGCController(SConfig::GetInstance().m_SIDevice[local_pad]))
{
SerialInterface::ChangeDevice(SConfig::GetInstance().m_SIDevice[local_pad], pad);
@ -1968,21 +2021,22 @@ bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const std::size_t size,
bool NetPlayClient::PollLocalPad(const int local_pad, sf::Packet& packet)
{
GCPadStatus pad_status;
switch (SConfig::GetInstance().m_SIDevice[local_pad])
{
case SerialInterface::SIDEVICE_WIIU_ADAPTER:
pad_status = GCAdapter::Input(local_pad);
break;
case SerialInterface::SIDEVICE_GC_CONTROLLER:
default:
pad_status = Pad::GetStatus(local_pad);
break;
}
const int ingame_pad = LocalPadToInGamePad(local_pad);
bool data_added = false;
GCPadStatus pad_status;
if (m_gba_config[ingame_pad].enabled)
{
pad_status = Pad::GetGBAStatus(local_pad);
}
else if (SConfig::GetInstance().m_SIDevice[local_pad] == SerialInterface::SIDEVICE_WIIU_ADAPTER)
{
pad_status = GCAdapter::Input(local_pad);
}
else
{
pad_status = Pad::GetStatus(local_pad);
}
if (m_host_input_authority)
{
@ -2234,6 +2288,26 @@ bool NetPlayClient::IsLocalPlayer(const PlayerId pid) const
return pid == m_local_player->pid;
}
void NetPlayClient::SendGameStatus()
{
sf::Packet packet;
packet << static_cast<MessageId>(NP_MSG_GAME_STATUS);
SyncIdentifierComparison result;
m_dialog->FindGameFile(m_selected_game, &result);
for (size_t i = 0; i < 4; ++i)
{
if (m_gba_config[i].enabled && m_gba_config[i].has_rom &&
m_net_settings.m_GBARomPaths[i].empty())
{
result = SyncIdentifierComparison::DifferentGame;
}
}
packet << static_cast<u32>(result);
Send(packet);
}
void NetPlayClient::SendTimeBase()
{
std::lock_guard lk(crit_netplay_client);
@ -2309,6 +2383,11 @@ const PadMappingArray& NetPlayClient::GetPadMapping() const
return m_pad_map;
}
const GBAConfigArray& NetPlayClient::GetGBAConfig() const
{
return m_gba_config;
}
const PadMappingArray& NetPlayClient::GetWiimoteMapping() const
{
return m_wiimote_map;
@ -2325,6 +2404,32 @@ SyncIdentifier NetPlayClient::GetSDCardIdentifier()
return SyncIdentifier{{}, "sd", {}, {}, {}, {}};
}
std::string GetPlayerMappingString(PlayerId pid, const PadMappingArray& pad_map,
const GBAConfigArray& gba_config,
const PadMappingArray& wiimote_map)
{
std::vector<size_t> gc_slots, gba_slots, wiimote_slots;
for (size_t i = 0; i < pad_map.size(); ++i)
{
if (pad_map[i] == pid && !gba_config[i].enabled)
gc_slots.push_back(i + 1);
if (pad_map[i] == pid && gba_config[i].enabled)
gba_slots.push_back(i + 1);
if (wiimote_map[i] == pid)
wiimote_slots.push_back(i + 1);
}
std::vector<std::string> groups;
for (const auto& [group_name, slots] :
{std::make_pair("GC", &gc_slots), std::make_pair("GBA", &gba_slots),
std::make_pair("Wii", &wiimote_slots)})
{
if (!slots->empty())
groups.emplace_back(fmt::format("{}{}", group_name, fmt::join(*slots, ",")));
}
std::string res = fmt::format("{}", fmt::join(groups, "|"));
return res.empty() ? "None" : res;
}
bool IsNetPlayRunning()
{
return netplay_client != nullptr;
@ -2401,6 +2506,60 @@ void SetupWiimotes()
}
}
std::string GetGBASavePath(int pad_num)
{
std::lock_guard lk(crit_netplay_client);
if (!netplay_client || NetPlay::GetNetSettings().m_IsHosting)
{
#ifdef HAS_LIBMGBA
std::string rom_path = Config::Get(Config::MAIN_GBA_ROM_PATHS[pad_num]);
return HW::GBA::Core::GetSavePath(rom_path, pad_num);
#else
return {};
#endif
}
if (!NetPlay::GetNetSettings().m_SyncSaveData)
return {};
return fmt::format("{}{}{}.sav", File::GetUserPath(D_GBAUSER_IDX), GBA_SAVE_NETPLAY, pad_num + 1);
}
PadDetails GetPadDetails(int pad_num)
{
std::lock_guard lk(crit_netplay_client);
PadDetails res{.local_pad = 4};
if (!netplay_client)
return res;
auto pad_map = netplay_client->GetPadMapping();
if (pad_map[pad_num] <= 0)
return res;
for (auto player : netplay_client->GetPlayers())
{
if (player->pid == pad_map[pad_num])
res.player_name = player->name;
}
int local_pad = 0;
int non_local_pad = 0;
for (int i = 0; i < pad_num; ++i)
{
if (netplay_client->IsLocalPlayer(pad_map[i]))
++local_pad;
else
++non_local_pad;
}
res.is_local = netplay_client->IsLocalPlayer(pad_map[pad_num]);
res.local_pad = res.is_local ? local_pad : netplay_client->NumLocalPads() + non_local_pad;
res.hide_gba = !res.is_local && netplay_client->GetNetSettings().m_HideRemoteGBAs &&
netplay_client->LocalPlayerHasControllerMapped();
return res;
}
void NetPlay_Enable(NetPlayClient* const np)
{
std::lock_guard lk(crit_netplay_client);

View File

@ -43,6 +43,7 @@ public:
virtual void OnMsgChangeGame(const SyncIdentifier& sync_identifier,
const std::string& netplay_name) = 0;
virtual void OnMsgChangeGBARom(int pad, const NetPlay::GBAConfig& config) = 0;
virtual void OnMsgStartGame() = 0;
virtual void OnMsgStopGame() = 0;
virtual void OnMsgPowerButton() = 0;
@ -62,6 +63,8 @@ public:
virtual std::shared_ptr<const UICommon::GameFile>
FindGameFile(const SyncIdentifier& sync_identifier,
SyncIdentifierComparison* found = nullptr) = 0;
virtual std::string FindGBARomPath(const std::array<u8, 20>& hash, std::string_view title,
int device_number) = 0;
virtual void ShowMD5Dialog(const std::string& title) = 0;
virtual void SetMD5Progress(int pid, int progress) = 0;
virtual void SetMD5Result(int pid, const std::string& result) = 0;
@ -139,6 +142,7 @@ public:
bool DoAllPlayersHaveGame();
const PadMappingArray& GetPadMapping() const;
const GBAConfigArray& GetGBAConfig() const;
const PadMappingArray& GetWiimoteMapping() const;
void AdjustPadBufferSize(unsigned int size);
@ -199,8 +203,9 @@ protected:
u32 m_current_game = 0;
PadMappingArray m_pad_map;
PadMappingArray m_wiimote_map;
PadMappingArray m_pad_map{};
GBAConfigArray m_gba_config{};
PadMappingArray m_wiimote_map{};
bool m_is_recording = false;
@ -231,6 +236,7 @@ private:
void Send(const sf::Packet& packet, u8 channel_id = DEFAULT_CHANNEL);
void Disconnect();
bool Connect();
void SendGameStatus();
void ComputeMD5(const SyncIdentifier& sync_identifier);
void DisplayPlayersPing();
u32 GetPlayersMaxPing() const;

View File

@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
@ -97,10 +98,12 @@ struct NetSettings
std::array<int, 4> m_WiimoteExtension;
bool m_GolfMode;
bool m_UseFMA;
bool m_HideRemoteGBAs;
// These aren't sent over the network directly
bool m_IsHosting;
bool m_HostInputAuthority;
std::array<std::string, 4> m_GBARomPaths;
};
struct NetTraversalConfig
@ -136,6 +139,7 @@ enum
NP_MSG_PAD_MAPPING = 0x61,
NP_MSG_PAD_BUFFER = 0x62,
NP_MSG_PAD_HOST_DATA = 0x63,
NP_MSG_GBA_CONFIG = 0x64,
NP_MSG_WIIMOTE_DATA = 0x70,
NP_MSG_WIIMOTE_MAPPING = 0x71,
@ -191,7 +195,8 @@ enum
SYNC_SAVE_DATA_FAILURE = 2,
SYNC_SAVE_DATA_RAW = 3,
SYNC_SAVE_DATA_GCI = 4,
SYNC_SAVE_DATA_WII = 5
SYNC_SAVE_DATA_WII = 5,
SYNC_SAVE_DATA_GBA = 6
};
enum
@ -225,7 +230,26 @@ using PlayerId = u8;
using FrameNum = u32;
using PadIndex = s8;
using PadMappingArray = std::array<PlayerId, 4>;
struct GBAConfig
{
bool enabled;
bool has_rom;
std::string title;
std::array<u8, 20> hash;
};
using GBAConfigArray = std::array<GBAConfig, 4>;
struct PadDetails
{
std::string player_name;
bool is_local;
int local_pad;
bool hide_gba;
};
std::string GetPlayerMappingString(PlayerId pid, const PadMappingArray& pad_map,
const GBAConfigArray& gba_config,
const PadMappingArray& wiimote_map);
bool IsNetPlayRunning();
// Precondition: A netplay client instance must be present. In other words,
// IsNetPlayRunning() must be true before calling this.
@ -238,4 +262,6 @@ void SetSIPollBatching(bool state);
void SendPowerButtonEvent();
bool IsSyncingAllWiiSaves();
void SetupWiimotes();
std::string GetGBASavePath(int pad_num);
PadDetails GetPadDetails(int pad_num);
} // namespace NetPlay

View File

@ -40,6 +40,9 @@
#include "Core/ConfigManager.h"
#include "Core/GeckoCode.h"
#include "Core/GeckoCodeConfig.h"
#ifdef HAS_LIBMGBA
#include "Core/HW/GBACore.h"
#endif
#include "Core/HW/GCMemcard/GCMemcard.h"
#include "Core/HW/GCMemcard/GCMemcardDirectory.h"
#include "Core/HW/GCMemcard/GCMemcardRaw.h"
@ -119,6 +122,7 @@ NetPlayServer::NetPlayServer(const u16 port, const bool forward_port, NetPlayUI*
}
m_pad_map.fill(0);
m_gba_config.fill({});
m_wiimote_map.fill(0);
if (traversal_config.use_traversal)
@ -480,6 +484,7 @@ unsigned int NetPlayServer::OnConnect(ENetPeer* socket, sf::Packet& rpac)
std::lock_guard lkp(m_crit.players);
m_players.emplace(*PeerPlayerId(player.socket), std::move(player));
UpdatePadMapping(); // sync pad mappings with everyone
UpdateGBAConfig();
UpdateWiimoteMapping();
}
@ -530,12 +535,14 @@ unsigned int NetPlayServer::OnDisconnect(const Client& player)
// alert other players of disconnect
SendToClients(spac);
for (PlayerId& mapping : m_pad_map)
for (size_t i = 0; i < m_pad_map.size(); ++i)
{
if (mapping == pid)
if (m_pad_map[i] == pid)
{
mapping = 0;
m_pad_map[i] = 0;
m_gba_config[i].enabled = false;
UpdatePadMapping();
UpdateGBAConfig();
}
}
@ -557,6 +564,11 @@ PadMappingArray NetPlayServer::GetPadMapping() const
return m_pad_map;
}
GBAConfigArray NetPlayServer::GetGBAConfig() const
{
return m_gba_config;
}
PadMappingArray NetPlayServer::GetWiimoteMapping() const
{
return m_wiimote_map;
@ -569,6 +581,26 @@ void NetPlayServer::SetPadMapping(const PadMappingArray& mappings)
UpdatePadMapping();
}
// called from ---GUI--- thread
void NetPlayServer::SetGBAConfig(const GBAConfigArray& mappings, bool update_rom)
{
#ifdef HAS_LIBMGBA
m_gba_config = mappings;
if (update_rom)
{
for (size_t i = 0; i < m_gba_config.size(); ++i)
{
auto& config = m_gba_config[i];
if (!config.enabled)
continue;
std::string rom_path = Config::Get(Config::MAIN_GBA_ROM_PATHS[i]);
config.has_rom = HW::GBA::Core::GetRomInfo(rom_path.c_str(), config.hash, config.title);
}
}
#endif
UpdateGBAConfig();
}
// called from ---GUI--- thread
void NetPlayServer::SetWiimoteMapping(const PadMappingArray& mappings)
{
@ -588,6 +620,20 @@ void NetPlayServer::UpdatePadMapping()
SendToClients(spac);
}
// called from ---GUI--- thread and ---NETPLAY--- thread
void NetPlayServer::UpdateGBAConfig()
{
sf::Packet spac;
spac << static_cast<MessageId>(NP_MSG_GBA_CONFIG);
for (const auto& config : m_gba_config)
{
spac << config.enabled << config.has_rom << config.title;
for (auto& data : config.hash)
spac << data;
}
SendToClients(spac);
}
// called from ---NETPLAY--- thread
void NetPlayServer::UpdateWiimoteMapping()
{
@ -751,12 +797,16 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
}
GCPadStatus pad;
packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >>
pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
packet >> pad.button;
spac << map << pad.button;
if (!m_gba_config.at(map).enabled)
{
packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >>
pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
spac << map << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY
<< pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight
<< pad.isConnected;
spac << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX
<< pad.substickY << pad.triggerLeft << pad.triggerRight << pad.isConnected;
}
}
if (m_host_input_authority)
@ -787,12 +837,16 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
packet >> map;
GCPadStatus pad;
packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >>
pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
packet >> pad.button;
spac << map << pad.button;
if (!m_gba_config.at(map).enabled)
{
packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >>
pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
spac << map << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY
<< pad.substickX << pad.substickY << pad.triggerLeft << pad.triggerRight
<< pad.isConnected;
spac << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX
<< pad.substickY << pad.triggerLeft << pad.triggerRight << pad.isConnected;
}
}
SendToClients(spac, player.pid);
@ -1316,6 +1370,7 @@ bool NetPlayServer::SetupNetSettings()
Config::Get(Config::NETPLAY_SYNC_ALL_WII_SAVES) && Config::Get(Config::NETPLAY_SYNC_SAVES);
settings.m_GolfMode = Config::Get(Config::NETPLAY_NETWORK_MODE) == "golf";
settings.m_UseFMA = DoAllPlayersHaveHardwareFMA();
settings.m_HideRemoteGBAs = Config::Get(Config::NETPLAY_HIDE_REMOTE_GBAS);
// Unload GameINI to restore things to normal
Config::RemoveLayer(Config::LayerType::GlobalGame);
@ -1501,6 +1556,7 @@ bool NetPlayServer::StartGame()
spac << m_settings.m_GolfMode;
spac << m_settings.m_UseFMA;
spac << m_settings.m_HideRemoteGBAs;
SendAsyncToClients(std::move(spac));
@ -1556,6 +1612,12 @@ bool NetPlayServer::SyncSaveData()
save_count++;
}
for (const auto& config : m_gba_config)
{
if (config.enabled && config.has_rom)
save_count++;
}
{
sf::Packet pac;
pac << static_cast<MessageId>(NP_MSG_SYNC_SAVE_DATA);
@ -1758,6 +1820,36 @@ bool NetPlayServer::SyncSaveData()
SendChunkedToClients(std::move(pac), 1, "Wii Save Synchronization");
}
for (size_t i = 0; i < m_gba_config.size(); ++i)
{
if (m_gba_config[i].enabled && m_gba_config[i].has_rom)
{
sf::Packet pac;
pac << static_cast<MessageId>(NP_MSG_SYNC_SAVE_DATA);
pac << static_cast<MessageId>(SYNC_SAVE_DATA_GBA);
pac << static_cast<u8>(i);
std::string path;
#ifdef HAS_LIBMGBA
path = HW::GBA::Core::GetSavePath(Config::Get(Config::MAIN_GBA_ROM_PATHS[i]),
static_cast<int>(i));
#endif
if (File::Exists(path))
{
if (!CompressFileIntoPacket(path, pac))
return false;
}
else
{
// No file, so we'll say the size is 0
pac << sf::Uint64{0};
}
SendChunkedToClients(std::move(pac), 1,
fmt::format("GBA{} Save File Synchronization", i + 1));
}
}
return true;
}

View File

@ -58,6 +58,9 @@ public:
PadMappingArray GetPadMapping() const;
void SetPadMapping(const PadMappingArray& mappings);
GBAConfigArray GetGBAConfig() const;
void SetGBAConfig(const GBAConfigArray& configs, bool update_rom);
PadMappingArray GetWiimoteMapping() const;
void SetWiimoteMapping(const PadMappingArray& mappings);
@ -134,6 +137,7 @@ private:
void OnConnectReady(ENetAddress) override {}
void OnConnectFailed(TraversalConnectFailedReason) override {}
void UpdatePadMapping();
void UpdateGBAConfig();
void UpdateWiimoteMapping();
std::vector<std::pair<std::string, std::string>> GetInterfaceListInternal() const;
void ChunkedDataThreadFunc();
@ -153,6 +157,7 @@ private:
u32 m_current_game = 0;
unsigned int m_target_buffer_size = 0;
PadMappingArray m_pad_map;
GBAConfigArray m_gba_config;
PadMappingArray m_wiimote_map;
unsigned int m_save_data_synced_players = 0;
unsigned int m_codes_synced_players = 0;

View File

@ -58,6 +58,17 @@ GBAWidget::GBAWidget(std::weak_ptr<HW::GBA::Core> core, int device_number,
m_is_local_pad(true), m_volume(0), m_muted(false), m_force_disconnect(false)
{
bool visible = true;
if (NetPlay::IsNetPlayRunning())
{
NetPlay::PadDetails details = NetPlay::GetPadDetails(m_device_number);
if (details.local_pad < 4)
{
m_netplayer_name = details.player_name;
m_is_local_pad = details.is_local;
m_local_pad = details.local_pad;
visible = !details.hide_gba;
}
}
setWindowIcon(Resources::GetAppIcon());
setAcceptDrops(true);

View File

@ -8,6 +8,7 @@
#include <QApplication>
#include <QClipboard>
#include <QComboBox>
#include <QFileDialog>
#include <QGridLayout>
#include <QGroupBox>
#include <QHeaderView>
@ -35,6 +36,9 @@
#include "Core/Config/NetplaySettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#ifdef HAS_LIBMGBA
#include "Core/HW/GBACore.h"
#endif
#include "Core/NetPlayServer.h"
#include "Core/SyncIdentifier.h"
@ -47,6 +51,7 @@
#include "DolphinQt/QtUtils/RunOnObject.h"
#include "DolphinQt/Resources.h"
#include "DolphinQt/Settings.h"
#include "DolphinQt/Settings/GameCubePane.h"
#include "UICommon/DiscordPresence.h"
#include "UICommon/GameFile.h"
@ -171,6 +176,8 @@ void NetPlayDialog::CreateMainLayout()
m_record_input_action->setCheckable(true);
m_golf_mode_overlay_action = m_other_menu->addAction(tr("Show Golf Mode Overlay"));
m_golf_mode_overlay_action->setCheckable(true);
m_hide_remote_gbas_action = m_other_menu->addAction(tr("Hide Remote GBAs"));
m_hide_remote_gbas_action->setCheckable(true);
m_game_button->setDefault(false);
m_game_button->setAutoDefault(false);
@ -279,6 +286,7 @@ void NetPlayDialog::ConnectWidgets()
m_pad_mapping->exec();
Settings::Instance().GetNetPlayServer()->SetPadMapping(m_pad_mapping->GetGCPadArray());
Settings::Instance().GetNetPlayServer()->SetGBAConfig(m_pad_mapping->GetGBAArray(), true);
Settings::Instance().GetNetPlayServer()->SetWiimoteMapping(m_pad_mapping->GetWiimoteArray());
});
@ -365,6 +373,7 @@ void NetPlayDialog::ConnectWidgets()
connect(m_golf_mode_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings);
connect(m_golf_mode_overlay_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings);
connect(m_fixed_delay_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings);
connect(m_hide_remote_gbas_action, &QAction::toggled, this, &NetPlayDialog::SaveSettings);
}
void NetPlayDialog::SendMessage(const std::string& msg)
@ -471,6 +480,11 @@ void NetPlayDialog::show(std::string nickname, bool use_traversal)
m_data_menu->menuAction()->setVisible(is_hosting);
m_network_menu->menuAction()->setVisible(is_hosting);
m_md5_menu->menuAction()->setVisible(is_hosting);
#ifdef HAS_LIBMGBA
m_hide_remote_gbas_action->setVisible(is_hosting);
#else
m_hide_remote_gbas_action->setVisible(false);
#endif
m_start_button->setHidden(!is_hosting);
m_kick_button->setHidden(!is_hosting);
m_assign_ports_button->setHidden(!is_hosting);
@ -570,20 +584,6 @@ void NetPlayDialog::UpdateGUI()
{tr("Player"), tr("Game Status"), tr("Ping"), tr("Mapping"), tr("Revision")});
m_players_list->setRowCount(m_player_count);
const auto get_mapping_string = [](const NetPlay::Player* player,
const NetPlay::PadMappingArray& array) {
std::string str;
for (size_t i = 0; i < array.size(); i++)
{
if (player->pid == array[i])
str += std::to_string(i + 1);
else
str += '-';
}
return '|' + str + '|';
};
static const std::map<NetPlay::SyncIdentifierComparison, QString> player_status{
{NetPlay::SyncIdentifierComparison::SameGame, tr("OK")},
{NetPlay::SyncIdentifierComparison::DifferentVersion, tr("Wrong Version")},
@ -599,9 +599,9 @@ void NetPlayDialog::UpdateGUI()
player_status.at(p->game_status) :
QStringLiteral("?"));
auto* ping_item = new QTableWidgetItem(QStringLiteral("%1 ms").arg(p->ping));
auto* mapping_item = new QTableWidgetItem(
QString::fromStdString(get_mapping_string(p, client->GetPadMapping()) +
get_mapping_string(p, client->GetWiimoteMapping())));
auto* mapping_item =
new QTableWidgetItem(QString::fromStdString(NetPlay::GetPlayerMappingString(
p->pid, client->GetPadMapping(), client->GetGBAConfig(), client->GetWiimoteMapping())));
auto* revision_item = new QTableWidgetItem(QString::fromStdString(p->revision));
for (auto* item : {name_item, status_item, ping_item, mapping_item, revision_item})
@ -743,6 +743,20 @@ void NetPlayDialog::OnMsgChangeGame(const NetPlay::SyncIdentifier& sync_identifi
DisplayMessage(tr("Game changed to \"%1\"").arg(qname), "magenta");
}
void NetPlayDialog::OnMsgChangeGBARom(int pad, const NetPlay::GBAConfig& config)
{
if (config.has_rom)
{
DisplayMessage(
tr("GBA%1 ROM changed to \"%2\"").arg(pad + 1).arg(QString::fromStdString(config.title)),
"magenta");
}
else
{
DisplayMessage(tr("GBA%1 ROM disabled").arg(pad + 1), "magenta");
}
}
void NetPlayDialog::GameStatusChanged(bool running)
{
QueueOnObject(this, [this, running] { SetOptionsEnabled(!running); });
@ -976,6 +990,51 @@ NetPlayDialog::FindGameFile(const NetPlay::SyncIdentifier& sync_identifier,
return nullptr;
}
std::string NetPlayDialog::FindGBARomPath(const std::array<u8, 20>& hash, std::string_view title,
int device_number)
{
#ifdef HAS_LIBMGBA
auto result = RunOnObject(this, [&, this] {
std::string rom_path;
std::array<u8, 20> rom_hash;
std::string rom_title;
for (size_t i = device_number; i < static_cast<size_t>(device_number) + 4; ++i)
{
rom_path = Config::Get(Config::MAIN_GBA_ROM_PATHS[i % 4]);
if (!rom_path.empty() && HW::GBA::Core::GetRomInfo(rom_path.c_str(), rom_hash, rom_title) &&
rom_hash == hash && rom_title == title)
{
return rom_path;
}
}
while (!(rom_path = GameCubePane::GetOpenGBARom(title)).empty())
{
if (HW::GBA::Core::GetRomInfo(rom_path.c_str(), rom_hash, rom_title))
{
if (rom_hash == hash && rom_title == title)
return rom_path;
ModalMessageBox::critical(
this, tr("Error"),
QString::fromStdString(Common::FmtFormatT(
"Mismatched ROMs\n"
"Selected: {0}\n- Title: {1}\n- Hash: {2:02X}\n"
"Expected:\n- Title: {3}\n- Hash: {4:02X}",
rom_path, rom_title, fmt::join(rom_hash, ""), title, fmt::join(hash, ""))));
}
else
{
ModalMessageBox::critical(
this, tr("Error"), tr("%1 is not a valid ROM").arg(QString::fromStdString(rom_path)));
}
}
return std::string();
});
if (result)
return *result;
#endif
return {};
}
void NetPlayDialog::LoadSettings()
{
const int buffer_size = Config::Get(Config::NETPLAY_BUFFER_SIZE);
@ -987,6 +1046,7 @@ void NetPlayDialog::LoadSettings()
const bool strict_settings_sync = Config::Get(Config::NETPLAY_STRICT_SETTINGS_SYNC);
const bool sync_all_wii_saves = Config::Get(Config::NETPLAY_SYNC_ALL_WII_SAVES);
const bool golf_mode_overlay = Config::Get(Config::NETPLAY_GOLF_MODE_OVERLAY);
const bool hide_remote_gbas = Config::Get(Config::NETPLAY_HIDE_REMOTE_GBAS);
m_buffer_size_box->setValue(buffer_size);
m_save_sd_action->setChecked(write_save_sdcard_data);
@ -997,6 +1057,7 @@ void NetPlayDialog::LoadSettings()
m_strict_settings_sync_action->setChecked(strict_settings_sync);
m_sync_all_wii_saves_action->setChecked(sync_all_wii_saves);
m_golf_mode_overlay_action->setChecked(golf_mode_overlay);
m_hide_remote_gbas_action->setChecked(hide_remote_gbas);
const std::string network_mode = Config::Get(Config::NETPLAY_NETWORK_MODE);
@ -1036,6 +1097,7 @@ void NetPlayDialog::SaveSettings()
Config::SetBase(Config::NETPLAY_STRICT_SETTINGS_SYNC, m_strict_settings_sync_action->isChecked());
Config::SetBase(Config::NETPLAY_SYNC_ALL_WII_SAVES, m_sync_all_wii_saves_action->isChecked());
Config::SetBase(Config::NETPLAY_GOLF_MODE_OVERLAY, m_golf_mode_overlay_action->isChecked());
Config::SetBase(Config::NETPLAY_HIDE_REMOTE_GBAS, m_hide_remote_gbas_action->isChecked());
std::string network_mode;
if (m_fixed_delay_action->isChecked())

View File

@ -46,6 +46,7 @@ public:
void OnMsgChangeGame(const NetPlay::SyncIdentifier& sync_identifier,
const std::string& netplay_name) override;
void OnMsgChangeGBARom(int pad, const NetPlay::GBAConfig& config) override;
void OnMsgStartGame() override;
void OnMsgStopGame() override;
void OnMsgPowerButton() override;
@ -68,6 +69,8 @@ public:
std::shared_ptr<const UICommon::GameFile>
FindGameFile(const NetPlay::SyncIdentifier& sync_identifier,
NetPlay::SyncIdentifierComparison* found = nullptr) override;
std::string FindGBARomPath(const std::array<u8, 20>& hash, std::string_view title,
int device_number) override;
void LoadSettings();
void SaveSettings();
@ -138,6 +141,7 @@ private:
QAction* m_golf_mode_action;
QAction* m_golf_mode_overlay_action;
QAction* m_fixed_delay_action;
QAction* m_hide_remote_gbas_action;
QPushButton* m_quit_button;
QSplitter* m_splitter;
QActionGroup* m_network_mode_group;

View File

@ -3,6 +3,7 @@
#include "DolphinQt/NetPlay/PadMappingDialog.h"
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QGridLayout>
@ -31,15 +32,19 @@ void PadMappingDialog::CreateWidgets()
for (unsigned int i = 0; i < m_wii_boxes.size(); i++)
{
m_gc_boxes[i] = new QComboBox;
m_gba_boxes[i] = new QCheckBox(tr("GBA Port %1").arg(i + 1));
m_wii_boxes[i] = new QComboBox;
m_main_layout->addWidget(new QLabel(tr("GC Port %1").arg(i + 1)), 0, i);
m_main_layout->addWidget(m_gc_boxes[i], 1, i);
m_main_layout->addWidget(new QLabel(tr("Wii Remote %1").arg(i + 1)), 2, i);
m_main_layout->addWidget(m_wii_boxes[i], 3, i);
#ifdef HAS_LIBMGBA
m_main_layout->addWidget(m_gba_boxes[i], 2, i);
#endif
m_main_layout->addWidget(new QLabel(tr("Wii Remote %1").arg(i + 1)), 3, i);
m_main_layout->addWidget(m_wii_boxes[i], 4, i);
}
m_main_layout->addWidget(m_button_box, 4, 0, 1, -1);
m_main_layout->addWidget(m_button_box, 5, 0, 1, -1);
setLayout(m_main_layout);
}
@ -55,6 +60,11 @@ void PadMappingDialog::ConnectWidgets()
&PadMappingDialog::OnMappingChanged);
}
}
for (const auto& checkbox : m_gba_boxes)
{
connect(checkbox, qOverload<int>(&QCheckBox::stateChanged), this,
&PadMappingDialog::OnMappingChanged);
}
}
int PadMappingDialog::exec()
@ -64,6 +74,7 @@ int PadMappingDialog::exec()
// Load Settings
m_players = client->GetPlayers();
m_pad_mapping = server->GetPadMapping();
m_gba_config = server->GetGBAConfig();
m_wii_mapping = server->GetWiimoteMapping();
QStringList players;
@ -93,6 +104,13 @@ int PadMappingDialog::exec()
}
}
for (size_t i = 0; i < m_gba_boxes.size(); i++)
{
const QSignalBlocker blocker(m_gba_boxes[i]);
m_gba_boxes[i]->setChecked(m_gba_config[i].enabled);
}
return QDialog::exec();
}
@ -101,6 +119,11 @@ NetPlay::PadMappingArray PadMappingDialog::GetGCPadArray()
return m_pad_mapping;
}
NetPlay::GBAConfigArray PadMappingDialog::GetGBAArray()
{
return m_gba_config;
}
NetPlay::PadMappingArray PadMappingDialog::GetWiimoteArray()
{
return m_wii_mapping;
@ -114,6 +137,7 @@ void PadMappingDialog::OnMappingChanged()
int wii_id = m_wii_boxes[i]->currentIndex();
m_pad_mapping[i] = gc_id > 0 ? m_players[gc_id - 1]->pid : 0;
m_gba_config[i].enabled = m_gba_boxes[i]->isChecked();
m_wii_mapping[i] = wii_id > 0 ? m_players[wii_id - 1]->pid : 0;
}
}

View File

@ -7,6 +7,7 @@
#include "Core/NetPlayProto.h"
class QCheckBox;
class QGridLayout;
class QComboBox;
class QDialogButtonBox;
@ -25,6 +26,7 @@ public:
int exec() override;
NetPlay::PadMappingArray GetGCPadArray();
NetPlay::GBAConfigArray GetGBAArray();
NetPlay::PadMappingArray GetWiimoteArray();
private:
@ -34,10 +36,12 @@ private:
void OnMappingChanged();
NetPlay::PadMappingArray m_pad_mapping;
NetPlay::GBAConfigArray m_gba_config;
NetPlay::PadMappingArray m_wii_mapping;
QGridLayout* m_main_layout;
std::array<QComboBox*, 4> m_gc_boxes;
std::array<QCheckBox*, 4> m_gba_boxes;
std::array<QComboBox*, 4> m_wii_boxes;
std::vector<const NetPlay::Player*> m_players;
QDialogButtonBox* m_button_box;

View File

@ -542,6 +542,10 @@ void GameCubePane::SaveSettings()
Config::SetBaseOrCurrent(Config::MAIN_GBA_ROM_PATHS[i],
m_gba_rom_edits[i]->text().toStdString());
}
auto server = Settings::Instance().GetNetPlayServer();
if (server)
server->SetGBAConfig(server->GetGBAConfig(), true);
}
#endif