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:
Vin Bertinelli 2018-10-04 18:49:41 -04:00 committed by Pierre Bourdon
parent 0140009114
commit 469f29350f
17 changed files with 545 additions and 7 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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"},

View File

@ -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;

View File

@ -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};

View File

@ -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;

View File

@ -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

View File

@ -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()

View File

@ -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&);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -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);
}

View File

@ -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());

View File

@ -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;