Merge pull request #7535 from UnclePunch/synccodes

Netplay: Sync AR and gecko codes with clients
This commit is contained in:
Pierre Bourdon 2018-11-13 22:39:25 +01:00 committed by GitHub
commit 6b7a1ca6d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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

@ -40,6 +40,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

@ -36,6 +36,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

@ -106,6 +106,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"
@ -550,6 +552,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;
@ -651,6 +654,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;
@ -830,6 +836,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;
@ -1200,6 +1363,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

@ -192,6 +192,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);
@ -226,6 +227,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

@ -77,6 +77,7 @@ struct NetSettings
bool m_DeferEFBCopies;
bool m_StrictSettingsSync;
bool m_SyncSaveData;
bool m_SyncCodes;
std::string m_SaveDataRegion;
bool m_IsHosting;
bool m_HostInputAuthority;
@ -145,6 +146,7 @@ enum
NP_MSG_SYNC_GC_SRAM = 0xF0,
NP_MSG_SYNC_SAVE_DATA = 0xF1,
NP_MSG_SYNC_CODES = 0xF2,
};
enum
@ -164,6 +166,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"
@ -852,8 +856,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();
}
}
}
@ -877,6 +884,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);
@ -966,17 +1015,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();
}
@ -1065,6 +1131,7 @@ bool NetPlayServer::StartGame()
spac << initial_rtc;
spac << m_settings.m_SyncSaveData;
spac << region;
spac << m_settings.m_SyncCodes;
SendAsyncToClients(std::move(spac));
@ -1077,6 +1144,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;
@ -1253,6 +1323,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

@ -77,6 +77,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);
@ -86,6 +87,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);
@ -121,6 +123,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);
@ -129,6 +132,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);
@ -164,6 +168,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 "
@ -190,6 +197,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);
@ -335,6 +343,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);
@ -451,6 +460,7 @@ void NetPlayDialog::OnStart()
settings.m_DeferEFBCopies = Config::Get(Config::GFX_HACK_DEFER_EFB_COPIES);
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);
@ -499,6 +509,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);
@ -772,6 +783,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);
@ -965,6 +977,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

@ -110,6 +110,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;