Netplay: Sync codes
Adds a tickbox to the server's window to syncronize codes. Codes are temporarily sent to each client and are used for the duration of the session. Saves the "sync codes" tickbox as per PR Netplay: Properly save hosting settings #7483
This commit is contained in:
parent
0140009114
commit
469f29350f
|
@ -83,6 +83,7 @@ enum
|
|||
// General lock. Protects codes list and internal log.
|
||||
static std::mutex s_lock;
|
||||
static std::vector<ARCode> s_active_codes;
|
||||
static std::vector<ARCode> s_synced_codes;
|
||||
static std::vector<std::string> s_internal_log;
|
||||
static std::atomic<bool> s_use_internal_log{false};
|
||||
// pointer to the code currently being run, (used by log messages that include the code name)
|
||||
|
@ -123,6 +124,37 @@ void ApplyCodes(const std::vector<ARCode>& codes)
|
|||
s_active_codes.shrink_to_fit();
|
||||
}
|
||||
|
||||
void SetSyncedCodesAsActive()
|
||||
{
|
||||
s_active_codes.clear();
|
||||
s_active_codes.reserve(s_synced_codes.size());
|
||||
s_active_codes = s_synced_codes;
|
||||
}
|
||||
|
||||
void UpdateSyncedCodes(const std::vector<ARCode>& codes)
|
||||
{
|
||||
s_synced_codes.clear();
|
||||
s_synced_codes.reserve(codes.size());
|
||||
std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_synced_codes),
|
||||
[](const ARCode& code) { return code.active; });
|
||||
s_synced_codes.shrink_to_fit();
|
||||
}
|
||||
|
||||
std::vector<ARCode> ApplyAndReturnCodes(const std::vector<ARCode>& codes)
|
||||
{
|
||||
if (SConfig::GetInstance().bEnableCheats)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(s_lock);
|
||||
s_disable_logging = false;
|
||||
s_active_codes.clear();
|
||||
std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_active_codes),
|
||||
[](const ARCode& code) { return code.active; });
|
||||
}
|
||||
s_active_codes.shrink_to_fit();
|
||||
|
||||
return s_active_codes;
|
||||
}
|
||||
|
||||
void AddCode(ARCode code)
|
||||
{
|
||||
if (!SConfig::GetInstance().bEnableCheats)
|
||||
|
|
|
@ -35,6 +35,9 @@ struct ARCode
|
|||
void RunAllActive();
|
||||
|
||||
void ApplyCodes(const std::vector<ARCode>& codes);
|
||||
void SetSyncedCodesAsActive();
|
||||
void UpdateSyncedCodes(const std::vector<ARCode>& codes);
|
||||
std::vector<ARCode> ApplyAndReturnCodes(const std::vector<ARCode>& codes);
|
||||
void AddCode(ARCode new_code);
|
||||
void LoadAndApplyCodes(const IniFile& global_ini, const IniFile& local_ini);
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ const ConfigInfo<std::string> MAIN_GCI_FOLDER_B_PATH_OVERRIDE{
|
|||
{System::Main, "Core", "GCIFolderBPathOverride"}, ""};
|
||||
const ConfigInfo<bool> MAIN_GCI_FOLDER_CURRENT_GAME_ONLY{
|
||||
{System::Main, "Core", "GCIFolderCurrentGameOnly"}, false};
|
||||
const ConfigInfo<bool> MAIN_CODE_SYNC_OVERRIDE{{System::Main, "Core", "CheatSyncOverride"}, false};
|
||||
const ConfigInfo<int> MAIN_SLOT_A{{System::Main, "Core", "SlotA"},
|
||||
ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER};
|
||||
const ConfigInfo<int> MAIN_SLOT_B{{System::Main, "Core", "SlotB"},
|
||||
|
|
|
@ -41,6 +41,7 @@ extern const ConfigInfo<std::string> MAIN_AGP_CART_A_PATH;
|
|||
extern const ConfigInfo<std::string> MAIN_AGP_CART_B_PATH;
|
||||
extern const ConfigInfo<std::string> MAIN_GCI_FOLDER_A_PATH_OVERRIDE;
|
||||
extern const ConfigInfo<std::string> MAIN_GCI_FOLDER_B_PATH_OVERRIDE;
|
||||
extern const ConfigInfo<bool> MAIN_CODE_SYNC_OVERRIDE;
|
||||
extern const ConfigInfo<bool> MAIN_GCI_FOLDER_CURRENT_GAME_ONLY;
|
||||
extern const ConfigInfo<int> MAIN_SLOT_A;
|
||||
extern const ConfigInfo<int> MAIN_SLOT_B;
|
||||
|
|
|
@ -42,6 +42,7 @@ const ConfigInfo<bool> NETPLAY_WRITE_SAVE_SDCARD_DATA{
|
|||
{System::Main, "NetPlay", "WriteSaveSDCardData"}, false};
|
||||
const ConfigInfo<bool> NETPLAY_LOAD_WII_SAVE{{System::Main, "NetPlay", "LoadWiiSave"}, false};
|
||||
const ConfigInfo<bool> NETPLAY_SYNC_SAVES{{System::Main, "NetPlay", "SyncSaves"}, true};
|
||||
const ConfigInfo<bool> NETPLAY_SYNC_CODES{{System::Main, "NetPlay", "SyncCodes"}, true};
|
||||
const ConfigInfo<bool> NETPLAY_RECORD_INPUTS{{System::Main, "NetPlay", "RecordInputs"}, false};
|
||||
const ConfigInfo<bool> NETPLAY_REDUCE_POLLING_RATE{{System::Main, "NetPlay", "ReducePollingRate"},
|
||||
false};
|
||||
|
|
|
@ -37,6 +37,7 @@ extern const ConfigInfo<u32> NETPLAY_CLIENT_BUFFER_SIZE;
|
|||
extern const ConfigInfo<bool> NETPLAY_WRITE_SAVE_SDCARD_DATA;
|
||||
extern const ConfigInfo<bool> NETPLAY_LOAD_WII_SAVE;
|
||||
extern const ConfigInfo<bool> NETPLAY_SYNC_SAVES;
|
||||
extern const ConfigInfo<bool> NETPLAY_SYNC_CODES;
|
||||
extern const ConfigInfo<bool> NETPLAY_RECORD_INPUTS;
|
||||
extern const ConfigInfo<bool> NETPLAY_REDUCE_POLLING_RATE;
|
||||
extern const ConfigInfo<bool> NETPLAY_STRICT_SETTINGS_SYNC;
|
||||
|
|
|
@ -105,6 +105,13 @@ public:
|
|||
|
||||
layer->Set(Config::MAIN_GCI_FOLDER_CURRENT_GAME_ONLY, true);
|
||||
}
|
||||
|
||||
// Check To Override Client's Cheat Codes
|
||||
if (m_settings.m_SyncCodes && !m_settings.m_IsHosting)
|
||||
{
|
||||
// Raise flag to use host's codes
|
||||
layer->Set(Config::MAIN_CODE_SYNC_OVERRIDE, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Save(Config::Layer* layer) override
|
||||
|
|
|
@ -61,6 +61,7 @@ enum class Installation
|
|||
static Installation s_code_handler_installed = Installation::Uninstalled;
|
||||
// the currently active codes
|
||||
static std::vector<GeckoCode> s_active_codes;
|
||||
static std::vector<GeckoCode> s_synced_codes;
|
||||
static std::mutex s_active_codes_lock;
|
||||
|
||||
void SetActiveCodes(const std::vector<GeckoCode>& gcodes)
|
||||
|
@ -79,6 +80,40 @@ void SetActiveCodes(const std::vector<GeckoCode>& gcodes)
|
|||
s_code_handler_installed = Installation::Uninstalled;
|
||||
}
|
||||
|
||||
void SetSyncedCodesAsActive()
|
||||
{
|
||||
s_active_codes.clear();
|
||||
s_active_codes.reserve(s_synced_codes.size());
|
||||
s_active_codes = s_synced_codes;
|
||||
}
|
||||
|
||||
void UpdateSyncedCodes(const std::vector<GeckoCode>& gcodes)
|
||||
{
|
||||
s_synced_codes.clear();
|
||||
s_synced_codes.reserve(gcodes.size());
|
||||
std::copy_if(gcodes.begin(), gcodes.end(), std::back_inserter(s_synced_codes),
|
||||
[](const GeckoCode& code) { return code.enabled; });
|
||||
s_synced_codes.shrink_to_fit();
|
||||
}
|
||||
|
||||
std::vector<GeckoCode> SetAndReturnActiveCodes(const std::vector<GeckoCode>& gcodes)
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(s_active_codes_lock);
|
||||
|
||||
s_active_codes.clear();
|
||||
if (SConfig::GetInstance().bEnableCheats)
|
||||
{
|
||||
s_active_codes.reserve(gcodes.size());
|
||||
std::copy_if(gcodes.begin(), gcodes.end(), std::back_inserter(s_active_codes),
|
||||
[](const GeckoCode& code) { return code.enabled; });
|
||||
}
|
||||
s_active_codes.shrink_to_fit();
|
||||
|
||||
s_code_handler_installed = Installation::Uninstalled;
|
||||
|
||||
return s_active_codes;
|
||||
}
|
||||
|
||||
// Requires s_active_codes_lock
|
||||
// NOTE: Refer to "codehandleronly.s" from Gecko OS.
|
||||
static Installation InstallCodeHandlerLocked()
|
||||
|
|
|
@ -59,6 +59,9 @@ constexpr u32 HLE_TRAMPOLINE_ADDRESS = INSTALLER_END_ADDRESS - 4;
|
|||
constexpr u32 MAGIC_GAMEID = 0xD01F1BAD;
|
||||
|
||||
void SetActiveCodes(const std::vector<GeckoCode>& gcodes);
|
||||
void SetSyncedCodesAsActive();
|
||||
void UpdateSyncedCodes(const std::vector<GeckoCode>& gcodes);
|
||||
std::vector<GeckoCode> SetAndReturnActiveCodes(const std::vector<GeckoCode>& gcodes);
|
||||
void RunCodeHandler();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap&);
|
||||
|
|
|
@ -33,8 +33,10 @@
|
|||
#include "Common/StringUtil.h"
|
||||
#include "Common/Timer.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/Config/NetplaySettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/GeckoCode.h"
|
||||
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
||||
#include "Core/HW/SI/SI.h"
|
||||
#include "Core/HW/SI/SI_DeviceGCController.h"
|
||||
|
@ -549,6 +551,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
|
|||
|
||||
packet >> m_net_settings.m_SyncSaveData;
|
||||
packet >> m_net_settings.m_SaveDataRegion;
|
||||
packet >> m_net_settings.m_SyncCodes;
|
||||
|
||||
m_net_settings.m_IsHosting = m_local_player->IsHost();
|
||||
m_net_settings.m_HostInputAuthority = m_host_input_authority;
|
||||
|
@ -644,6 +647,9 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
|
|||
{
|
||||
case SYNC_SAVE_DATA_NOTIFY:
|
||||
{
|
||||
if (m_local_player->IsHost())
|
||||
return 0;
|
||||
|
||||
packet >> m_sync_save_data_count;
|
||||
m_sync_save_data_success_count = 0;
|
||||
|
||||
|
@ -823,6 +829,163 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
|
|||
}
|
||||
break;
|
||||
|
||||
case NP_MSG_SYNC_CODES:
|
||||
{
|
||||
// Recieve Data Packet
|
||||
MessageId sub_id;
|
||||
packet >> sub_id;
|
||||
|
||||
// Check Which Operation to Perform with This Packet
|
||||
switch (sub_id)
|
||||
{
|
||||
case SYNC_CODES_NOTIFY:
|
||||
{
|
||||
// Set both codes as unsynced
|
||||
m_sync_gecko_codes_complete = false;
|
||||
m_sync_ar_codes_complete = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case SYNC_CODES_NOTIFY_GECKO:
|
||||
{
|
||||
// Return if this is the host
|
||||
if (m_local_player->IsHost())
|
||||
return 0;
|
||||
|
||||
// Receive Number of Codelines to Receive
|
||||
packet >> m_sync_gecko_codes_count;
|
||||
|
||||
m_sync_gecko_codes_success_count = 0;
|
||||
|
||||
NOTICE_LOG(ACTIONREPLAY, "Receiving %d Gecko codelines", m_sync_gecko_codes_count);
|
||||
|
||||
// Check if no codes to sync, if so return as finished
|
||||
if (m_sync_gecko_codes_count == 0)
|
||||
{
|
||||
m_sync_gecko_codes_complete = true;
|
||||
SyncCodeResponse(true);
|
||||
}
|
||||
else
|
||||
m_dialog->AppendChat(GetStringT("Synchronizing gecko codes..."));
|
||||
}
|
||||
break;
|
||||
|
||||
case SYNC_CODES_DATA_GECKO:
|
||||
{
|
||||
// Return if this is the host
|
||||
if (m_local_player->IsHost())
|
||||
return 0;
|
||||
|
||||
// Create a synced code vector
|
||||
std::vector<Gecko::GeckoCode> synced_codes;
|
||||
// Create a GeckoCode
|
||||
Gecko::GeckoCode gcode;
|
||||
gcode = Gecko::GeckoCode();
|
||||
// Initialize gcode
|
||||
gcode.name = "Synced Codes";
|
||||
gcode.enabled = true;
|
||||
|
||||
// Receive code contents from packet
|
||||
for (int i = 0; i < m_sync_gecko_codes_count; i++)
|
||||
{
|
||||
Gecko::GeckoCode::Code new_code;
|
||||
packet >> new_code.address;
|
||||
packet >> new_code.data;
|
||||
|
||||
NOTICE_LOG(ACTIONREPLAY, "Received %08x %08x", new_code.address, new_code.data);
|
||||
|
||||
gcode.codes.push_back(std::move(new_code));
|
||||
|
||||
if (++m_sync_gecko_codes_success_count >= m_sync_gecko_codes_count)
|
||||
{
|
||||
m_sync_gecko_codes_complete = true;
|
||||
SyncCodeResponse(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Add gcode containing all codes to Gecko Code vector
|
||||
synced_codes.push_back(std::move(gcode));
|
||||
|
||||
// Clear Vector if received 0 codes (match host's end when using no codes)
|
||||
if (m_sync_gecko_codes_count == 0)
|
||||
synced_codes.clear();
|
||||
|
||||
// Copy this to the vector located in GeckoCode.cpp
|
||||
Gecko::UpdateSyncedCodes(synced_codes);
|
||||
}
|
||||
break;
|
||||
|
||||
case SYNC_CODES_NOTIFY_AR:
|
||||
{
|
||||
// Return if this is the host
|
||||
if (m_local_player->IsHost())
|
||||
return 0;
|
||||
|
||||
// Receive Number of Codelines to Receive
|
||||
packet >> m_sync_ar_codes_count;
|
||||
|
||||
m_sync_ar_codes_success_count = 0;
|
||||
|
||||
NOTICE_LOG(ACTIONREPLAY, "Receiving %d AR codelines", m_sync_ar_codes_count);
|
||||
|
||||
// Check if no codes to sync, if so return as finished
|
||||
if (m_sync_ar_codes_count == 0)
|
||||
{
|
||||
m_sync_ar_codes_complete = true;
|
||||
SyncCodeResponse(true);
|
||||
}
|
||||
else
|
||||
m_dialog->AppendChat(GetStringT("Synchronizing AR codes..."));
|
||||
}
|
||||
break;
|
||||
|
||||
case SYNC_CODES_DATA_AR:
|
||||
{
|
||||
// Return if this is the host
|
||||
if (m_local_player->IsHost())
|
||||
return 0;
|
||||
|
||||
// Create a synced code vector
|
||||
std::vector<ActionReplay::ARCode> synced_codes;
|
||||
// Create an ARCode
|
||||
ActionReplay::ARCode arcode;
|
||||
arcode = ActionReplay::ARCode();
|
||||
// Initialize arcode
|
||||
arcode.name = "Synced Codes";
|
||||
arcode.active = true;
|
||||
|
||||
// Receive code contents from packet
|
||||
for (int i = 0; i < m_sync_ar_codes_count; i++)
|
||||
{
|
||||
ActionReplay::AREntry new_code;
|
||||
packet >> new_code.cmd_addr;
|
||||
packet >> new_code.value;
|
||||
|
||||
NOTICE_LOG(ACTIONREPLAY, "Received %08x %08x", new_code.cmd_addr, new_code.value);
|
||||
arcode.ops.push_back(new_code);
|
||||
|
||||
if (++m_sync_ar_codes_success_count >= m_sync_ar_codes_count)
|
||||
{
|
||||
m_sync_ar_codes_complete = true;
|
||||
SyncCodeResponse(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Add arcode containing all codes to AR Code vector
|
||||
synced_codes.push_back(std::move(arcode));
|
||||
|
||||
// Clear Vector if received 0 codes (match host's end when using no codes)
|
||||
if (m_sync_ar_codes_count == 0)
|
||||
synced_codes.clear();
|
||||
|
||||
// Copy this to the vector located in ActionReplay.cpp
|
||||
ActionReplay::UpdateSyncedCodes(synced_codes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NP_MSG_COMPUTE_MD5:
|
||||
{
|
||||
std::string file_identifier;
|
||||
|
@ -1193,6 +1356,34 @@ void NetPlayClient::SyncSaveDataResponse(const bool success)
|
|||
}
|
||||
}
|
||||
|
||||
void NetPlayClient::SyncCodeResponse(const bool success)
|
||||
{
|
||||
// If something failed, immediately report back that code sync failed
|
||||
if (!success)
|
||||
{
|
||||
m_dialog->AppendChat(GetStringT("Error processing Codes."));
|
||||
|
||||
sf::Packet response_packet;
|
||||
response_packet << static_cast<MessageId>(NP_MSG_SYNC_CODES);
|
||||
response_packet << static_cast<MessageId>(SYNC_CODES_FAILURE);
|
||||
|
||||
Send(response_packet);
|
||||
return;
|
||||
}
|
||||
|
||||
// If both gecko and AR codes have completely finished transferring, report back as successful
|
||||
if (m_sync_gecko_codes_complete && m_sync_ar_codes_complete)
|
||||
{
|
||||
m_dialog->AppendChat(GetStringT("Codes received!"));
|
||||
|
||||
sf::Packet response_packet;
|
||||
response_packet << static_cast<MessageId>(NP_MSG_SYNC_CODES);
|
||||
response_packet << static_cast<MessageId>(SYNC_CODES_SUCCESS);
|
||||
|
||||
Send(response_packet);
|
||||
}
|
||||
}
|
||||
|
||||
bool NetPlayClient::DecompressPacketIntoFile(sf::Packet& packet, const std::string& file_path)
|
||||
{
|
||||
u64 file_size = Common::PacketReadU64(packet);
|
||||
|
|
|
@ -190,6 +190,7 @@ private:
|
|||
void SendStopGamePacket();
|
||||
|
||||
void SyncSaveDataResponse(bool success);
|
||||
void SyncCodeResponse(bool success);
|
||||
bool DecompressPacketIntoFile(sf::Packet& packet, const std::string& file_path);
|
||||
std::optional<std::vector<u8>> DecompressPacketIntoBuffer(sf::Packet& packet);
|
||||
|
||||
|
@ -224,6 +225,12 @@ private:
|
|||
Common::Event m_first_pad_status_received_event;
|
||||
u8 m_sync_save_data_count = 0;
|
||||
u8 m_sync_save_data_success_count = 0;
|
||||
u16 m_sync_gecko_codes_count = 0;
|
||||
u16 m_sync_gecko_codes_success_count = 0;
|
||||
bool m_sync_gecko_codes_complete = false;
|
||||
u16 m_sync_ar_codes_count = 0;
|
||||
u16 m_sync_ar_codes_success_count = 0;
|
||||
bool m_sync_ar_codes_complete = false;
|
||||
|
||||
u64 m_initial_rtc = 0;
|
||||
u32 m_timebase_frame = 0;
|
||||
|
|
|
@ -76,6 +76,7 @@ struct NetSettings
|
|||
bool m_EnableGPUTextureDecoding;
|
||||
bool m_StrictSettingsSync;
|
||||
bool m_SyncSaveData;
|
||||
bool m_SyncCodes;
|
||||
std::string m_SaveDataRegion;
|
||||
bool m_IsHosting;
|
||||
bool m_HostInputAuthority;
|
||||
|
@ -143,6 +144,7 @@ enum
|
|||
|
||||
NP_MSG_SYNC_GC_SRAM = 0xF0,
|
||||
NP_MSG_SYNC_SAVE_DATA = 0xF1,
|
||||
NP_MSG_SYNC_CODES = 0xF2,
|
||||
};
|
||||
|
||||
enum
|
||||
|
@ -162,6 +164,17 @@ enum
|
|||
SYNC_SAVE_DATA_WII = 5
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SYNC_CODES_NOTIFY = 0,
|
||||
SYNC_CODES_NOTIFY_GECKO = 1,
|
||||
SYNC_CODES_NOTIFY_AR = 2,
|
||||
SYNC_CODES_DATA_GECKO = 3,
|
||||
SYNC_CODES_DATA_AR = 4,
|
||||
SYNC_CODES_SUCCESS = 5,
|
||||
SYNC_CODES_FAILURE = 6,
|
||||
};
|
||||
|
||||
constexpr u32 NETPLAY_LZO_IN_LEN = 1024 * 64;
|
||||
constexpr u32 NETPLAY_LZO_OUT_LEN = NETPLAY_LZO_IN_LEN + (NETPLAY_LZO_IN_LEN / 16) + 64 + 3;
|
||||
|
||||
|
|
|
@ -29,9 +29,13 @@
|
|||
#include "Common/StringUtil.h"
|
||||
#include "Common/UPnP.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/NetplaySettings.h"
|
||||
#include "Core/ConfigLoaders/GameConfigLoader.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/GeckoCode.h"
|
||||
#include "Core/GeckoCodeConfig.h"
|
||||
#include "Core/HW/GCMemcard/GCMemcardDirectory.h"
|
||||
#include "Core/HW/GCMemcard/GCMemcardRaw.h"
|
||||
#include "Core/HW/Sram.h"
|
||||
|
@ -844,8 +848,11 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
|
|||
m_save_data_synced_players++;
|
||||
if (m_save_data_synced_players >= m_players.size() - 1)
|
||||
{
|
||||
m_dialog->AppendChat(GetStringT("All players synchronized."));
|
||||
StartGame();
|
||||
m_dialog->AppendChat(GetStringT("All players saves synchronized."));
|
||||
|
||||
// Saves are synced, check if codes are as well and attempt to start the game
|
||||
m_saves_synced = true;
|
||||
CheckSyncAndStartGame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -869,6 +876,48 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, Client& player)
|
|||
}
|
||||
break;
|
||||
|
||||
case NP_MSG_SYNC_CODES:
|
||||
{
|
||||
// Receive Status of Code Sync
|
||||
MessageId sub_id;
|
||||
packet >> sub_id;
|
||||
|
||||
// Check If Code Sync was successful or not
|
||||
switch (sub_id)
|
||||
{
|
||||
case SYNC_CODES_SUCCESS:
|
||||
{
|
||||
if (m_start_pending)
|
||||
{
|
||||
if (++m_codes_synced_players >= m_players.size() - 1)
|
||||
{
|
||||
m_dialog->AppendChat(GetStringT("All players' codes synchronized."));
|
||||
|
||||
// Codes are synced, check if saves are as well and attempt to start the game
|
||||
m_codes_synced = true;
|
||||
CheckSyncAndStartGame();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SYNC_CODES_FAILURE:
|
||||
{
|
||||
m_dialog->AppendChat(StringFromFormat(GetStringT("%s failed to synchronize codes.").c_str(),
|
||||
player.name.c_str()));
|
||||
m_start_pending = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlertT(
|
||||
"Unknown SYNC_GECKO_CODES message with id:%d received from player:%d Kicking player!",
|
||||
sub_id, player.pid);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
PanicAlertT("Unknown message with id:%d received from player:%d Kicking player!", mid,
|
||||
player.pid);
|
||||
|
@ -958,17 +1007,34 @@ bool NetPlayServer::DoAllPlayersHaveIPLDump() const
|
|||
// called from ---GUI--- thread
|
||||
bool NetPlayServer::RequestStartGame()
|
||||
{
|
||||
bool start_now = true;
|
||||
|
||||
if (m_settings.m_SyncSaveData && m_players.size() > 1)
|
||||
{
|
||||
start_now = false;
|
||||
m_start_pending = true;
|
||||
if (!SyncSaveData())
|
||||
{
|
||||
PanicAlertT("Error synchronizing save data!");
|
||||
m_start_pending = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_start_pending = true;
|
||||
}
|
||||
else
|
||||
|
||||
// Check To Send Codes to Clients
|
||||
if (m_settings.m_SyncCodes && m_players.size() > 1)
|
||||
{
|
||||
start_now = false;
|
||||
m_start_pending = true;
|
||||
if (!SyncCodes())
|
||||
{
|
||||
PanicAlertT("Error synchronizing save gecko codes!");
|
||||
m_start_pending = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (start_now)
|
||||
{
|
||||
return StartGame();
|
||||
}
|
||||
|
@ -1056,6 +1122,7 @@ bool NetPlayServer::StartGame()
|
|||
spac << initial_rtc;
|
||||
spac << m_settings.m_SyncSaveData;
|
||||
spac << region;
|
||||
spac << m_settings.m_SyncCodes;
|
||||
|
||||
SendAsyncToClients(std::move(spac));
|
||||
|
||||
|
@ -1068,6 +1135,9 @@ bool NetPlayServer::StartGame()
|
|||
// called from ---GUI--- thread
|
||||
bool NetPlayServer::SyncSaveData()
|
||||
{
|
||||
// We're about to sync saves, so set m_saves_synced to false (waits to start game)
|
||||
m_saves_synced = false;
|
||||
|
||||
m_save_data_synced_players = 0;
|
||||
|
||||
u8 save_count = 0;
|
||||
|
@ -1244,6 +1314,150 @@ bool NetPlayServer::SyncSaveData()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool NetPlayServer::SyncCodes()
|
||||
{
|
||||
// Sync Codes is ticked, so set m_codes_synced to false
|
||||
m_codes_synced = false;
|
||||
|
||||
// Get Game Path
|
||||
const auto game = m_dialog->FindGameFile(m_selected_game);
|
||||
if (game == nullptr)
|
||||
{
|
||||
PanicAlertT("Selected game doesn't exist in game list!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find all INI files
|
||||
const auto game_id = game->GetGameID();
|
||||
const auto revision = game->GetRevision();
|
||||
IniFile globalIni;
|
||||
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(game_id, revision))
|
||||
globalIni.Load(File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP + filename, true);
|
||||
IniFile localIni;
|
||||
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(game_id, revision))
|
||||
localIni.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + filename, true);
|
||||
|
||||
// Initialize Number of Synced Players
|
||||
m_codes_synced_players = 0;
|
||||
|
||||
// Notify Clients of Incoming Code Sync
|
||||
{
|
||||
sf::Packet pac;
|
||||
pac << static_cast<MessageId>(NP_MSG_SYNC_CODES);
|
||||
pac << static_cast<MessageId>(SYNC_CODES_NOTIFY);
|
||||
SendAsyncToClients(std::move(pac));
|
||||
}
|
||||
// Sync Gecko Codes
|
||||
{
|
||||
// Create a Gecko Code Vector with just the active codes
|
||||
std::vector<Gecko::GeckoCode> s_active_codes =
|
||||
Gecko::SetAndReturnActiveCodes(Gecko::LoadCodes(globalIni, localIni));
|
||||
|
||||
// Determine Codelist Size
|
||||
u16 codelines = 0;
|
||||
for (const Gecko::GeckoCode& active_code : s_active_codes)
|
||||
{
|
||||
NOTICE_LOG(ACTIONREPLAY, "Indexing %s", active_code.name.c_str());
|
||||
for (const Gecko::GeckoCode::Code& code : active_code.codes)
|
||||
{
|
||||
NOTICE_LOG(ACTIONREPLAY, "%08x %08x", code.address, code.data);
|
||||
codelines++;
|
||||
}
|
||||
}
|
||||
|
||||
// Output codelines to send
|
||||
NOTICE_LOG(ACTIONREPLAY, "Sending %d Gecko codelines", codelines);
|
||||
|
||||
// Send initial packet. Notify of the sync operation and total number of lines being sent.
|
||||
{
|
||||
sf::Packet pac;
|
||||
pac << static_cast<MessageId>(NP_MSG_SYNC_CODES);
|
||||
pac << static_cast<MessageId>(SYNC_CODES_NOTIFY_GECKO);
|
||||
pac << codelines;
|
||||
SendAsyncToClients(std::move(pac));
|
||||
}
|
||||
|
||||
// Send entire codeset in the second packet
|
||||
{
|
||||
sf::Packet pac;
|
||||
pac << static_cast<MessageId>(NP_MSG_SYNC_CODES);
|
||||
pac << static_cast<MessageId>(SYNC_CODES_DATA_GECKO);
|
||||
// Iterate through the active code vector and send each codeline
|
||||
for (const Gecko::GeckoCode& active_code : s_active_codes)
|
||||
{
|
||||
NOTICE_LOG(ACTIONREPLAY, "Sending %s", active_code.name.c_str());
|
||||
for (const Gecko::GeckoCode::Code& code : active_code.codes)
|
||||
{
|
||||
NOTICE_LOG(ACTIONREPLAY, "%08x %08x", code.address, code.data);
|
||||
pac << code.address;
|
||||
pac << code.data;
|
||||
}
|
||||
}
|
||||
SendAsyncToClients(std::move(pac));
|
||||
}
|
||||
}
|
||||
|
||||
// Sync AR Codes
|
||||
{
|
||||
// Create an AR Code Vector with just the active codes
|
||||
std::vector<ActionReplay::ARCode> s_active_codes =
|
||||
ActionReplay::ApplyAndReturnCodes(ActionReplay::LoadCodes(globalIni, localIni));
|
||||
|
||||
// Determine Codelist Size
|
||||
u16 codelines = 0;
|
||||
for (const ActionReplay::ARCode& active_code : s_active_codes)
|
||||
{
|
||||
NOTICE_LOG(ACTIONREPLAY, "Indexing %s", active_code.name.c_str());
|
||||
for (const ActionReplay::AREntry& op : active_code.ops)
|
||||
{
|
||||
NOTICE_LOG(ACTIONREPLAY, "%08x %08x", op.cmd_addr, op.value);
|
||||
codelines++;
|
||||
}
|
||||
}
|
||||
|
||||
// Output codelines to send
|
||||
NOTICE_LOG(ACTIONREPLAY, "Sending %d AR codelines", codelines);
|
||||
|
||||
// Send initial packet. Notify of the sync operation and total number of lines being sent.
|
||||
{
|
||||
sf::Packet pac;
|
||||
pac << static_cast<MessageId>(NP_MSG_SYNC_CODES);
|
||||
pac << static_cast<MessageId>(SYNC_CODES_NOTIFY_AR);
|
||||
pac << codelines;
|
||||
SendAsyncToClients(std::move(pac));
|
||||
}
|
||||
|
||||
// Send entire codeset in the second packet
|
||||
{
|
||||
sf::Packet pac;
|
||||
pac << static_cast<MessageId>(NP_MSG_SYNC_CODES);
|
||||
pac << static_cast<MessageId>(SYNC_CODES_DATA_AR);
|
||||
// Iterate through the active code vector and send each codeline
|
||||
for (const ActionReplay::ARCode& active_code : s_active_codes)
|
||||
{
|
||||
NOTICE_LOG(ACTIONREPLAY, "Sending %s", active_code.name.c_str());
|
||||
for (const ActionReplay::AREntry& op : active_code.ops)
|
||||
{
|
||||
NOTICE_LOG(ACTIONREPLAY, "%08x %08x", op.cmd_addr, op.value);
|
||||
pac << op.cmd_addr;
|
||||
pac << op.value;
|
||||
}
|
||||
}
|
||||
SendAsyncToClients(std::move(pac));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetPlayServer::CheckSyncAndStartGame()
|
||||
{
|
||||
if (m_saves_synced && m_codes_synced)
|
||||
{
|
||||
StartGame();
|
||||
}
|
||||
}
|
||||
|
||||
bool NetPlayServer::CompressFileIntoPacket(const std::string& file_path, sf::Packet& packet)
|
||||
{
|
||||
File::IOFile file(file_path, "rb");
|
||||
|
|
|
@ -85,6 +85,8 @@ private:
|
|||
};
|
||||
|
||||
bool SyncSaveData();
|
||||
bool SyncCodes();
|
||||
void CheckSyncAndStartGame();
|
||||
bool CompressFileIntoPacket(const std::string& file_path, sf::Packet& packet);
|
||||
bool CompressBufferIntoPacket(const std::vector<u8>& in_buffer, sf::Packet& packet);
|
||||
void SendFirstReceivedToHost(PadMapping map, bool state);
|
||||
|
@ -116,6 +118,9 @@ private:
|
|||
PadMappingArray m_pad_map;
|
||||
PadMappingArray m_wiimote_map;
|
||||
unsigned int m_save_data_synced_players = 0;
|
||||
unsigned int m_codes_synced_players = 0;
|
||||
bool m_saves_synced = true;
|
||||
bool m_codes_synced = true;
|
||||
bool m_start_pending = false;
|
||||
bool m_host_input_authority = false;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/GeckoCode.h"
|
||||
#include "Core/GeckoCodeConfig.h"
|
||||
|
@ -164,9 +165,18 @@ void LoadPatches()
|
|||
IniFile localIni = SConfig::GetInstance().LoadLocalGameIni();
|
||||
|
||||
LoadPatchSection("OnFrame", s_on_frame, globalIni, localIni);
|
||||
ActionReplay::LoadAndApplyCodes(globalIni, localIni);
|
||||
|
||||
Gecko::SetActiveCodes(Gecko::LoadCodes(globalIni, localIni));
|
||||
// Check if I'm syncing Codes
|
||||
if (Config::Get(Config::MAIN_CODE_SYNC_OVERRIDE))
|
||||
{
|
||||
Gecko::SetSyncedCodesAsActive();
|
||||
ActionReplay::SetSyncedCodesAsActive();
|
||||
}
|
||||
else
|
||||
{
|
||||
Gecko::SetActiveCodes(Gecko::LoadCodes(globalIni, localIni));
|
||||
ActionReplay::LoadAndApplyCodes(globalIni, localIni);
|
||||
}
|
||||
|
||||
LoadSpeedhacks("Speedhacks", merged);
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ NetPlayDialog::NetPlayDialog(QWidget* parent)
|
|||
const bool write_save_sdcard_data = Config::Get(Config::NETPLAY_WRITE_SAVE_SDCARD_DATA);
|
||||
const bool load_wii_save = Config::Get(Config::NETPLAY_LOAD_WII_SAVE);
|
||||
const bool sync_saves = Config::Get(Config::NETPLAY_SYNC_SAVES);
|
||||
const bool sync_codes = Config::Get(Config::NETPLAY_SYNC_CODES);
|
||||
const bool record_inputs = Config::Get(Config::NETPLAY_RECORD_INPUTS);
|
||||
const bool reduce_polling_rate = Config::Get(Config::NETPLAY_REDUCE_POLLING_RATE);
|
||||
const bool strict_settings_sync = Config::Get(Config::NETPLAY_STRICT_SETTINGS_SYNC);
|
||||
|
@ -85,6 +86,7 @@ NetPlayDialog::NetPlayDialog(QWidget* parent)
|
|||
m_save_sd_box->setChecked(write_save_sdcard_data);
|
||||
m_load_wii_box->setChecked(load_wii_save);
|
||||
m_sync_save_data_box->setChecked(sync_saves);
|
||||
m_sync_codes_box->setChecked(sync_codes);
|
||||
m_record_input_box->setChecked(record_inputs);
|
||||
m_reduce_polling_rate_box->setChecked(reduce_polling_rate);
|
||||
m_strict_settings_sync_box->setChecked(strict_settings_sync);
|
||||
|
@ -120,6 +122,7 @@ void NetPlayDialog::CreateMainLayout()
|
|||
m_reduce_polling_rate_box = new QCheckBox(tr("Reduce Polling Rate"));
|
||||
m_strict_settings_sync_box = new QCheckBox(tr("Strict Settings Sync"));
|
||||
m_host_input_authority_box = new QCheckBox(tr("Host Input Authority"));
|
||||
m_sync_codes_box = new QCheckBox(tr("Sync Codes"));
|
||||
m_buffer_label = new QLabel(tr("Buffer:"));
|
||||
m_quit_button = new QPushButton(tr("Quit"));
|
||||
m_splitter = new QSplitter(Qt::Horizontal);
|
||||
|
@ -128,6 +131,7 @@ void NetPlayDialog::CreateMainLayout()
|
|||
m_game_button->setAutoDefault(false);
|
||||
|
||||
m_sync_save_data_box->setChecked(true);
|
||||
m_sync_codes_box->setChecked(true);
|
||||
|
||||
auto* default_button = new QAction(tr("Calculate MD5 hash"), m_md5_button);
|
||||
|
||||
|
@ -163,6 +167,9 @@ void NetPlayDialog::CreateMainLayout()
|
|||
tr("This will sync additional graphics settings, and force everyone to the same internal "
|
||||
"resolution.\nMay prevent desync in some games that use EFB reads. Please ensure everyone "
|
||||
"uses the same video backend."));
|
||||
m_sync_codes_box->setToolTip(tr("This will sync the client's AR and Gecko Codes with the host's. "
|
||||
"The client will be sent the codes regardless "
|
||||
"\nof whether or not the client has them."));
|
||||
m_host_input_authority_box->setToolTip(
|
||||
tr("This gives the host control over when inputs are sent to the game, effectively "
|
||||
"decoupling players from each other in terms of buffering.\nThis allows players to have "
|
||||
|
@ -189,6 +196,7 @@ void NetPlayDialog::CreateMainLayout()
|
|||
options_boxes->addWidget(m_save_sd_box);
|
||||
options_boxes->addWidget(m_load_wii_box);
|
||||
options_boxes->addWidget(m_sync_save_data_box);
|
||||
options_boxes->addWidget(m_sync_codes_box);
|
||||
options_boxes->addWidget(m_record_input_box);
|
||||
options_boxes->addWidget(m_reduce_polling_rate_box);
|
||||
options_boxes->addWidget(m_strict_settings_sync_box);
|
||||
|
@ -333,6 +341,7 @@ void NetPlayDialog::ConnectWidgets()
|
|||
connect(m_save_sd_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings);
|
||||
connect(m_load_wii_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings);
|
||||
connect(m_sync_save_data_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings);
|
||||
connect(m_sync_codes_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings);
|
||||
connect(m_record_input_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings);
|
||||
connect(m_reduce_polling_rate_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings);
|
||||
connect(m_strict_settings_sync_box, &QCheckBox::stateChanged, this, &NetPlayDialog::SaveSettings);
|
||||
|
@ -448,6 +457,7 @@ void NetPlayDialog::OnStart()
|
|||
settings.m_EnableGPUTextureDecoding = Config::Get(Config::GFX_ENABLE_GPU_TEXTURE_DECODING);
|
||||
settings.m_StrictSettingsSync = m_strict_settings_sync_box->isChecked();
|
||||
settings.m_SyncSaveData = m_sync_save_data_box->isChecked();
|
||||
settings.m_SyncCodes = m_sync_codes_box->isChecked();
|
||||
|
||||
// Unload GameINI to restore things to normal
|
||||
Config::RemoveLayer(Config::LayerType::GlobalGame);
|
||||
|
@ -496,6 +506,7 @@ void NetPlayDialog::show(std::string nickname, bool use_traversal)
|
|||
m_save_sd_box->setHidden(!is_hosting);
|
||||
m_load_wii_box->setHidden(!is_hosting);
|
||||
m_sync_save_data_box->setHidden(!is_hosting);
|
||||
m_sync_codes_box->setHidden(!is_hosting);
|
||||
m_reduce_polling_rate_box->setHidden(!is_hosting);
|
||||
m_strict_settings_sync_box->setHidden(!is_hosting);
|
||||
m_host_input_authority_box->setHidden(!is_hosting);
|
||||
|
@ -769,6 +780,7 @@ void NetPlayDialog::SetOptionsEnabled(bool enabled)
|
|||
m_load_wii_box->setEnabled(enabled);
|
||||
m_save_sd_box->setEnabled(enabled);
|
||||
m_sync_save_data_box->setEnabled(enabled);
|
||||
m_sync_codes_box->setEnabled(enabled);
|
||||
m_assign_ports_button->setEnabled(enabled);
|
||||
m_reduce_polling_rate_box->setEnabled(enabled);
|
||||
m_strict_settings_sync_box->setEnabled(enabled);
|
||||
|
@ -955,6 +967,7 @@ void NetPlayDialog::SaveSettings()
|
|||
Config::SetBase(Config::NETPLAY_WRITE_SAVE_SDCARD_DATA, m_save_sd_box->isChecked());
|
||||
Config::SetBase(Config::NETPLAY_LOAD_WII_SAVE, m_load_wii_box->isChecked());
|
||||
Config::SetBase(Config::NETPLAY_SYNC_SAVES, m_sync_save_data_box->isChecked());
|
||||
Config::SetBase(Config::NETPLAY_SYNC_CODES, m_sync_codes_box->isChecked());
|
||||
Config::SetBase(Config::NETPLAY_RECORD_INPUTS, m_record_input_box->isChecked());
|
||||
Config::SetBase(Config::NETPLAY_REDUCE_POLLING_RATE, m_reduce_polling_rate_box->isChecked());
|
||||
Config::SetBase(Config::NETPLAY_STRICT_SETTINGS_SYNC, m_strict_settings_sync_box->isChecked());
|
||||
|
|
|
@ -109,6 +109,7 @@ private:
|
|||
QCheckBox* m_save_sd_box;
|
||||
QCheckBox* m_load_wii_box;
|
||||
QCheckBox* m_sync_save_data_box;
|
||||
QCheckBox* m_sync_codes_box;
|
||||
QCheckBox* m_record_input_box;
|
||||
QCheckBox* m_reduce_polling_rate_box;
|
||||
QCheckBox* m_strict_settings_sync_box;
|
||||
|
|
Loading…
Reference in New Issue