2015-05-24 06:55:12 +02:00
|
|
|
// Copyright 2010 Dolphin Emulator Project
|
2021-07-05 03:22:19 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2013-04-17 22:43:11 -04:00
|
|
|
|
2015-02-02 02:08:58 -08:00
|
|
|
#include "Core/NetPlayClient.h"
|
2017-06-06 23:36:16 -04:00
|
|
|
|
2016-01-24 22:46:37 -05:00
|
|
|
#include <algorithm>
|
2017-09-09 15:52:35 -04:00
|
|
|
#include <cstddef>
|
|
|
|
#include <cstring>
|
2016-07-14 00:45:38 +02:00
|
|
|
#include <fstream>
|
2021-12-10 15:21:46 -08:00
|
|
|
#include <functional>
|
2016-01-24 22:10:38 -05:00
|
|
|
#include <memory>
|
2017-06-06 23:36:16 -04:00
|
|
|
#include <mutex>
|
2022-09-23 03:21:52 +02:00
|
|
|
#include <span>
|
2017-06-06 23:36:16 -04:00
|
|
|
#include <sstream>
|
2016-07-14 00:45:38 +02:00
|
|
|
#include <thread>
|
2022-09-23 03:21:52 +02:00
|
|
|
#include <tuple>
|
2018-06-15 08:11:18 -04:00
|
|
|
#include <type_traits>
|
2018-07-04 17:01:50 -04:00
|
|
|
#include <vector>
|
2017-06-06 23:36:16 -04:00
|
|
|
|
2019-10-20 07:35:11 -04:00
|
|
|
#include <fmt/format.h>
|
2017-06-06 23:36:16 -04:00
|
|
|
|
2018-07-09 15:52:31 -04:00
|
|
|
#include "Common/Assert.h"
|
2016-07-16 21:40:19 +02:00
|
|
|
#include "Common/CommonPaths.h"
|
2016-01-24 21:46:46 -05:00
|
|
|
#include "Common/CommonTypes.h"
|
2022-07-27 18:47:52 -07:00
|
|
|
#include "Common/Crypto/SHA1.h"
|
2023-04-11 11:48:46 -04:00
|
|
|
#include "Common/ENet.h"
|
2018-06-09 16:15:14 -04:00
|
|
|
#include "Common/FileUtil.h"
|
2018-07-10 17:46:13 +02:00
|
|
|
#include "Common/Logging/Log.h"
|
2016-01-24 21:46:46 -05:00
|
|
|
#include "Common/MsgHandler.h"
|
2018-07-04 17:01:50 -04:00
|
|
|
#include "Common/NandPaths.h"
|
2017-10-28 01:42:25 +02:00
|
|
|
#include "Common/QoSSession.h"
|
2018-07-04 17:01:50 -04:00
|
|
|
#include "Common/SFMLHelper.h"
|
2017-02-24 22:56:33 -05:00
|
|
|
#include "Common/StringUtil.h"
|
2015-05-08 17:28:03 -04:00
|
|
|
#include "Common/Timer.h"
|
2017-09-09 15:52:35 -04:00
|
|
|
#include "Common/Version.h"
|
NetPlay/Jit64: Avoid using software FMA
When I added the software FMA path in 2c38d64 and made us use
it when determinism is enabled, I was assuming that either the
performance impact of software FMA wouldn't be too large or CPUs
that were too old to have FMA instructions were too slow to run
Dolphin well anyway. This was wrong. To give an example, the
netplay performance went from 60 FPS to 30 FPS in one case.
This change makes netplay clients negotiate whether FMA should
be used. If all clients use an x64 CPU that supports FMA, or
AArch64, then FMA is enabled, and otherwise FMA is disabled.
In other words, we sacrifice accuracy if needed to avoid massive
slowdown, but not otherwise. When not using netplay, whether to
enable FMA is simply based on whether the host CPU supports it.
The only remaining case where the software FMA path gets used
under normal circumstances is when an input recording is created
on a CPU with FMA support and then played back on a CPU without.
This is not an especially common scenario (though it can happen),
and TASers are generally less picky about performance and more
picky about accuracy than other users anyway.
With this change, FMA desyncs are avoided between AArch64 and
modern x64 CPUs (unlike before 2c38d64), but we do get FMA
desyncs between AArch64 and old x64 CPUs (like before 2c38d64).
This desync can be avoided by adding a non-FMA path to JitArm64 as
an option, which I will wait with for another pull request so that
we can get the performance regression fixed as quickly as possible.
https://bugs.dolphin-emu.org/issues/12542
2021-06-09 20:16:41 +02:00
|
|
|
|
2018-10-04 18:49:41 -04:00
|
|
|
#include "Core/ActionReplay.h"
|
2021-11-20 19:59:14 +01:00
|
|
|
#include "Core/Boot/Boot.h"
|
2021-07-04 13:33:58 +02:00
|
|
|
#include "Core/Config/MainSettings.h"
|
2017-10-28 01:42:25 +02:00
|
|
|
#include "Core/Config/NetplaySettings.h"
|
NetPlay/Jit64: Avoid using software FMA
When I added the software FMA path in 2c38d64 and made us use
it when determinism is enabled, I was assuming that either the
performance impact of software FMA wouldn't be too large or CPUs
that were too old to have FMA instructions were too slow to run
Dolphin well anyway. This was wrong. To give an example, the
netplay performance went from 60 FPS to 30 FPS in one case.
This change makes netplay clients negotiate whether FMA should
be used. If all clients use an x64 CPU that supports FMA, or
AArch64, then FMA is enabled, and otherwise FMA is disabled.
In other words, we sacrifice accuracy if needed to avoid massive
slowdown, but not otherwise. When not using netplay, whether to
enable FMA is simply based on whether the host CPU supports it.
The only remaining case where the software FMA path gets used
under normal circumstances is when an input recording is created
on a CPU with FMA support and then played back on a CPU without.
This is not an especially common scenario (though it can happen),
and TASers are generally less picky about performance and more
picky about accuracy than other users anyway.
With this change, FMA desyncs are avoided between AArch64 and
modern x64 CPUs (unlike before 2c38d64), but we do get FMA
desyncs between AArch64 and old x64 CPUs (like before 2c38d64).
This desync can be avoided by adding a non-FMA path to JitArm64 as
an option, which I will wait with for another pull request so that
we can get the performance regression fixed as quickly as possible.
https://bugs.dolphin-emu.org/issues/12542
2021-06-09 20:16:41 +02:00
|
|
|
#include "Core/Config/SessionSettings.h"
|
2022-01-25 20:34:03 +01:00
|
|
|
#include "Core/Config/WiimoteSettings.h"
|
2014-02-19 13:09:14 -05:00
|
|
|
#include "Core/ConfigManager.h"
|
2018-10-04 18:49:41 -04:00
|
|
|
#include "Core/GeckoCode.h"
|
2020-08-30 13:43:45 -07:00
|
|
|
#include "Core/HW/EXI/EXI.h"
|
2017-01-20 15:33:43 -05:00
|
|
|
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
2021-07-04 13:33:58 +02:00
|
|
|
#ifdef HAS_LIBMGBA
|
|
|
|
#include "Core/HW/GBACore.h"
|
|
|
|
#endif
|
|
|
|
#include "Core/HW/GBAPad.h"
|
2021-05-27 01:59:12 -04:00
|
|
|
#include "Core/HW/GCMemcard/GCMemcard.h"
|
2021-07-04 13:33:58 +02:00
|
|
|
#include "Core/HW/GCPad.h"
|
2017-01-20 18:45:11 -05:00
|
|
|
#include "Core/HW/SI/SI.h"
|
2019-06-07 18:25:32 -04:00
|
|
|
#include "Core/HW/SI/SI_Device.h"
|
2017-01-20 18:45:11 -05:00
|
|
|
#include "Core/HW/SI/SI_DeviceGCController.h"
|
2015-06-14 13:59:41 +02:00
|
|
|
#include "Core/HW/Sram.h"
|
2018-07-04 17:01:50 -04:00
|
|
|
#include "Core/HW/WiiSave.h"
|
|
|
|
#include "Core/HW/WiiSaveStructs.h"
|
2022-09-23 03:21:52 +02:00
|
|
|
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
|
2014-02-17 05:18:15 -05:00
|
|
|
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
2016-06-26 10:44:15 +02:00
|
|
|
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
2018-07-04 17:01:50 -04:00
|
|
|
#include "Core/IOS/FS/FileSystem.h"
|
|
|
|
#include "Core/IOS/FS/HostBackend/FS.h"
|
2017-01-18 13:50:28 +01:00
|
|
|
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
2018-07-04 17:01:50 -04:00
|
|
|
#include "Core/IOS/Uids.h"
|
2015-02-02 02:08:58 -08:00
|
|
|
#include "Core/Movie.h"
|
2021-05-28 20:53:02 -04:00
|
|
|
#include "Core/NetPlayCommon.h"
|
2018-06-15 08:11:18 -04:00
|
|
|
#include "Core/PowerPC/PowerPC.h"
|
2020-06-07 22:58:03 +02:00
|
|
|
#include "Core/SyncIdentifier.h"
|
2023-03-12 14:20:32 +01:00
|
|
|
#include "Core/System.h"
|
2021-12-10 15:21:46 -08:00
|
|
|
#include "DiscIO/Blob.h"
|
NetPlay/Jit64: Avoid using software FMA
When I added the software FMA path in 2c38d64 and made us use
it when determinism is enabled, I was assuming that either the
performance impact of software FMA wouldn't be too large or CPUs
that were too old to have FMA instructions were too slow to run
Dolphin well anyway. This was wrong. To give an example, the
netplay performance went from 60 FPS to 30 FPS in one case.
This change makes netplay clients negotiate whether FMA should
be used. If all clients use an x64 CPU that supports FMA, or
AArch64, then FMA is enabled, and otherwise FMA is disabled.
In other words, we sacrifice accuracy if needed to avoid massive
slowdown, but not otherwise. When not using netplay, whether to
enable FMA is simply based on whether the host CPU supports it.
The only remaining case where the software FMA path gets used
under normal circumstances is when an input recording is created
on a CPU with FMA support and then played back on a CPU without.
This is not an especially common scenario (though it can happen),
and TASers are generally less picky about performance and more
picky about accuracy than other users anyway.
With this change, FMA desyncs are avoided between AArch64 and
modern x64 CPUs (unlike before 2c38d64), but we do get FMA
desyncs between AArch64 and old x64 CPUs (like before 2c38d64).
This desync can be avoided by adding a non-FMA path to JitArm64 as
an option, which I will wait with for another pull request so that
we can get the performance regression fixed as quickly as possible.
https://bugs.dolphin-emu.org/issues/12542
2021-06-09 20:16:41 +02:00
|
|
|
|
2019-01-05 07:09:11 -06:00
|
|
|
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
|
2016-05-08 15:29:01 +02:00
|
|
|
#include "InputCommon/GCAdapter.h"
|
2018-11-22 06:52:48 -05:00
|
|
|
#include "InputCommon/InputConfig.h"
|
2020-06-07 22:58:03 +02:00
|
|
|
#include "UICommon/GameFile.h"
|
2016-02-02 16:35:27 +01:00
|
|
|
#include "VideoCommon/OnScreenDisplay.h"
|
|
|
|
#include "VideoCommon/VideoConfig.h"
|
2013-08-05 05:05:06 -04:00
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
namespace NetPlay
|
|
|
|
{
|
2019-01-01 08:32:39 -06:00
|
|
|
using namespace WiimoteCommon;
|
|
|
|
|
2014-07-08 15:58:25 +02:00
|
|
|
static std::mutex crit_netplay_client;
|
2014-03-09 21:14:26 +01:00
|
|
|
static NetPlayClient* netplay_client = nullptr;
|
2021-07-04 13:33:58 +02:00
|
|
|
static bool s_si_poll_batching = false;
|
2013-08-05 05:05:06 -04:00
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---GUI--- thread
|
|
|
|
NetPlayClient::~NetPlayClient()
|
|
|
|
{
|
2013-08-05 04:56:30 -04:00
|
|
|
// not perfect
|
2016-08-05 16:04:39 +02:00
|
|
|
if (m_is_running.IsSet())
|
2013-08-05 04:56:30 -04:00
|
|
|
StopGame();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-01-24 21:52:02 -05:00
|
|
|
if (m_is_connected)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2022-07-27 18:43:16 -07:00
|
|
|
m_should_compute_game_digest = false;
|
|
|
|
m_dialog->AbortGameDigest();
|
|
|
|
if (m_game_digest_thread.joinable())
|
|
|
|
m_game_digest_thread.join();
|
2016-08-05 16:04:39 +02:00
|
|
|
m_do_loop.Clear();
|
2011-01-27 20:47:58 +00:00
|
|
|
m_thread.join();
|
2019-03-28 02:32:06 -04:00
|
|
|
|
|
|
|
m_chunked_data_receive_queue.clear();
|
|
|
|
m_dialog->HideChunkedProgressDialog();
|
2013-08-05 04:56:30 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
if (m_server)
|
|
|
|
{
|
|
|
|
Disconnect();
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
if (g_MainNetHost.get() == m_client)
|
|
|
|
{
|
|
|
|
g_MainNetHost.release();
|
|
|
|
}
|
|
|
|
if (m_client)
|
|
|
|
{
|
|
|
|
enet_host_destroy(m_client);
|
|
|
|
m_client = nullptr;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
if (m_traversal_client)
|
|
|
|
{
|
|
|
|
ReleaseTraversalClient();
|
|
|
|
}
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---GUI--- thread
|
2015-05-28 20:28:48 -04:00
|
|
|
NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog,
|
2017-08-07 00:22:33 -07:00
|
|
|
const std::string& name, const NetTraversalConfig& traversal_config)
|
2016-01-24 22:06:57 -05:00
|
|
|
: m_dialog(dialog), m_player_name(name)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2013-08-05 04:56:30 -04:00
|
|
|
ClearBuffers();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-08-07 00:22:33 -07:00
|
|
|
if (!traversal_config.use_traversal)
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2015-02-02 01:56:53 -08:00
|
|
|
// Direct Connection
|
2018-10-18 04:33:05 -04:00
|
|
|
m_client = enet_host_create(nullptr, 1, CHANNEL_COUNT, 0, 0);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
if (m_client == nullptr)
|
|
|
|
{
|
2018-07-01 22:52:43 -04:00
|
|
|
m_dialog->OnConnectionError(_trans("Could not create client."));
|
|
|
|
return;
|
2015-02-02 01:56:53 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-04-02 17:21:26 +02:00
|
|
|
m_client->mtu = std::min(m_client->mtu, NetPlay::MAX_ENET_MTU);
|
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
ENetAddress addr;
|
|
|
|
enet_address_set_host(&addr, address.c_str());
|
|
|
|
addr.port = port;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-10-18 04:33:05 -04:00
|
|
|
m_server = enet_host_connect(m_client, &addr, CHANNEL_COUNT, 0);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
if (m_server == nullptr)
|
|
|
|
{
|
2018-07-01 22:52:43 -04:00
|
|
|
m_dialog->OnConnectionError(_trans("Could not create peer."));
|
|
|
|
return;
|
2015-02-02 01:56:53 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-08-07 09:37:28 -06:00
|
|
|
// Update time in milliseconds of no acknoledgment of
|
|
|
|
// sent packets before a connection is deemed disconnected
|
|
|
|
enet_peer_timeout(m_server, 0, PEER_TIMEOUT.count(), PEER_TIMEOUT.count());
|
2022-01-06 02:12:54 -05:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
ENetEvent netEvent;
|
|
|
|
int net = enet_host_service(m_client, &netEvent, 5000);
|
|
|
|
if (net > 0 && netEvent.type == ENET_EVENT_TYPE_CONNECT)
|
|
|
|
{
|
|
|
|
if (Connect())
|
2015-03-14 15:19:18 +01:00
|
|
|
{
|
2023-04-11 09:12:01 -04:00
|
|
|
m_client->intercept = Common::ENet::InterceptCallback;
|
2015-02-02 01:56:53 -08:00
|
|
|
m_thread = std::thread(&NetPlayClient::ThreadFunc, this);
|
2015-03-14 15:19:18 +01:00
|
|
|
}
|
2015-02-02 01:56:53 -08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-07-01 22:52:43 -04:00
|
|
|
m_dialog->OnConnectionError(_trans("Could not communicate with host."));
|
2015-02-02 01:56:53 -08:00
|
|
|
}
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
else
|
2014-02-10 09:26:29 -05:00
|
|
|
{
|
2015-02-14 19:51:08 -08:00
|
|
|
if (address.size() > NETPLAY_CODE_SIZE)
|
|
|
|
{
|
2018-07-01 22:52:43 -04:00
|
|
|
m_dialog->OnConnectionError(
|
2018-09-08 15:19:01 +02:00
|
|
|
_trans("The host code is too long.\nPlease recheck that you have the correct code."));
|
2015-02-14 19:51:08 -08:00
|
|
|
return;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-08-07 00:22:33 -07:00
|
|
|
if (!EnsureTraversalClient(traversal_config.traversal_host, traversal_config.traversal_port))
|
2015-02-02 01:56:53 -08:00
|
|
|
return;
|
|
|
|
m_client = g_MainNetHost.get();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
m_traversal_client = g_TraversalClient.get();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
// If we were disconnected in the background, reconnect.
|
2021-01-19 14:00:01 -05:00
|
|
|
if (m_traversal_client->HasFailed())
|
2015-02-02 01:56:53 -08:00
|
|
|
m_traversal_client->ReconnectToServer();
|
|
|
|
m_traversal_client->m_Client = this;
|
|
|
|
m_host_spec = address;
|
2016-01-24 21:58:56 -05:00
|
|
|
m_connection_state = ConnectionState::WaitingForTraversalClientConnection;
|
2015-02-02 01:56:53 -08:00
|
|
|
OnTraversalStateChanged();
|
|
|
|
m_connecting = true;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-03-28 20:15:34 +01:00
|
|
|
Common::Timer connect_timer;
|
|
|
|
connect_timer.Start();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
while (m_connecting)
|
|
|
|
{
|
|
|
|
ENetEvent netEvent;
|
|
|
|
if (m_traversal_client)
|
|
|
|
m_traversal_client->HandleResends();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
while (enet_host_service(m_client, &netEvent, 4) > 0)
|
|
|
|
{
|
|
|
|
sf::Packet rpac;
|
|
|
|
switch (netEvent.type)
|
|
|
|
{
|
|
|
|
case ENET_EVENT_TYPE_CONNECT:
|
|
|
|
m_server = netEvent.peer;
|
2022-03-14 00:19:54 -04:00
|
|
|
|
2022-08-07 09:37:28 -06:00
|
|
|
// Update time in milliseconds of no acknoledgment of
|
|
|
|
// sent packets before a connection is deemed disconnected
|
|
|
|
enet_peer_timeout(m_server, 0, PEER_TIMEOUT.count(), PEER_TIMEOUT.count());
|
2022-03-14 00:19:54 -04:00
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
if (Connect())
|
|
|
|
{
|
2016-01-24 21:58:56 -05:00
|
|
|
m_connection_state = ConnectionState::Connected;
|
2015-02-02 01:56:53 -08:00
|
|
|
m_thread = std::thread(&NetPlayClient::ThreadFunc, this);
|
|
|
|
}
|
|
|
|
return;
|
2015-02-02 02:08:58 -08:00
|
|
|
default:
|
|
|
|
break;
|
2015-02-02 01:56:53 -08:00
|
|
|
}
|
|
|
|
}
|
2022-07-17 20:43:47 -07:00
|
|
|
if (connect_timer.ElapsedMs() > 5000)
|
2015-03-28 20:15:34 +01:00
|
|
|
break;
|
2015-02-02 01:56:53 -08:00
|
|
|
}
|
2018-07-01 22:52:43 -04:00
|
|
|
m_dialog->OnConnectionError(_trans("Could not communicate with host."));
|
2014-02-10 09:26:29 -05:00
|
|
|
}
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
bool NetPlayClient::Connect()
|
|
|
|
{
|
2023-03-30 00:02:25 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Connecting to server.");
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
// send connect message
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
2022-01-14 00:04:22 +01:00
|
|
|
packet << Common::GetScmRevGitStr();
|
|
|
|
packet << Common::GetNetplayDolphinVer();
|
2017-02-28 14:33:54 -08:00
|
|
|
packet << m_player_name;
|
|
|
|
Send(packet);
|
2015-02-02 01:27:06 -08:00
|
|
|
enet_host_flush(m_client);
|
|
|
|
sf::Packet rpac;
|
|
|
|
// TODO: make this not hang
|
|
|
|
ENetEvent netEvent;
|
2022-12-26 23:35:42 +01:00
|
|
|
int net;
|
|
|
|
while ((net = enet_host_service(m_client, &netEvent, 5000)) > 0 && netEvent.type == 42)
|
|
|
|
{
|
|
|
|
// ignore packets from traversal server
|
|
|
|
}
|
|
|
|
if (net > 0 && netEvent.type == ENET_EVENT_TYPE_RECEIVE)
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
|
|
|
rpac.append(netEvent.packet->data, netEvent.packet->dataLength);
|
2015-08-18 08:09:17 -04:00
|
|
|
enet_packet_destroy(netEvent.packet);
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 15:06:46 -04:00
|
|
|
ConnectionError error;
|
2015-02-02 01:27:06 -08:00
|
|
|
rpac >> error;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
// got error message
|
2021-09-22 15:06:46 -04:00
|
|
|
if (error != ConnectionError::NoError)
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
|
|
|
switch (error)
|
|
|
|
{
|
2021-09-22 15:06:46 -04:00
|
|
|
case ConnectionError::ServerFull:
|
2018-07-01 22:52:43 -04:00
|
|
|
m_dialog->OnConnectionError(_trans("The server is full."));
|
2015-02-02 01:27:06 -08:00
|
|
|
break;
|
2021-09-22 15:06:46 -04:00
|
|
|
case ConnectionError::VersionMismatch:
|
2018-07-01 22:52:43 -04:00
|
|
|
m_dialog->OnConnectionError(
|
|
|
|
_trans("The server and client's NetPlay versions are incompatible."));
|
2015-02-02 01:27:06 -08:00
|
|
|
break;
|
2021-09-22 15:06:46 -04:00
|
|
|
case ConnectionError::GameRunning:
|
2018-07-01 22:52:43 -04:00
|
|
|
m_dialog->OnConnectionError(_trans("The game is currently running."));
|
2015-02-02 01:27:06 -08:00
|
|
|
break;
|
2021-09-22 15:06:46 -04:00
|
|
|
case ConnectionError::NameTooLong:
|
2018-11-21 00:15:44 -05:00
|
|
|
m_dialog->OnConnectionError(_trans("Nickname is too long."));
|
|
|
|
break;
|
2015-02-02 01:27:06 -08:00
|
|
|
default:
|
2018-07-01 22:52:43 -04:00
|
|
|
m_dialog->OnConnectionError(_trans("The server sent an unknown error message."));
|
2015-02-02 01:27:06 -08:00
|
|
|
break;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
Disconnect();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rpac >> m_pid;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
Player player;
|
|
|
|
player.name = m_player_name;
|
|
|
|
player.pid = m_pid;
|
2022-01-14 00:04:22 +01:00
|
|
|
player.revision = Common::GetNetplayDolphinVer();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
// add self to player list
|
|
|
|
m_players[m_pid] = player;
|
|
|
|
m_local_player = &m_players[m_pid];
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
m_dialog->Update();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-01-24 21:52:02 -05:00
|
|
|
m_is_connected = true;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
static void ReceiveSyncIdentifier(sf::Packet& spac, SyncIdentifier& sync_identifier)
|
|
|
|
{
|
|
|
|
// We use a temporary variable here due to a potential long vs long long mismatch
|
|
|
|
sf::Uint64 dol_elf_size;
|
|
|
|
spac >> dol_elf_size;
|
|
|
|
sync_identifier.dol_elf_size = dol_elf_size;
|
|
|
|
|
|
|
|
spac >> sync_identifier.game_id;
|
|
|
|
spac >> sync_identifier.revision;
|
|
|
|
spac >> sync_identifier.disc_number;
|
|
|
|
spac >> sync_identifier.is_datel;
|
|
|
|
|
|
|
|
for (u8& x : sync_identifier.sync_hash)
|
|
|
|
spac >> x;
|
|
|
|
}
|
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---NETPLAY--- thread
|
2021-09-15 11:28:04 -04:00
|
|
|
void NetPlayClient::OnData(sf::Packet& packet)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2021-09-22 14:17:42 -04:00
|
|
|
MessageID mid;
|
2010-05-05 04:44:19 +00:00
|
|
|
packet >> mid;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-01-12 16:47:26 -08:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Got server message: {:x}", static_cast<u8>(mid));
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
switch (mid)
|
|
|
|
{
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::PlayerJoin:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnPlayerJoin(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::PlayerLeave:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnPlayerLeave(packet);
|
|
|
|
break;
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::ChatMessage:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnChatMessage(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::ChunkedDataStart:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnChunkedDataStart(packet);
|
|
|
|
break;
|
2019-07-30 20:14:51 -07:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::ChunkedDataEnd:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnChunkedDataEnd(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::ChunkedDataPayload:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnChunkedDataPayload(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::ChunkedDataAbort:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnChunkedDataAbort(packet);
|
|
|
|
break;
|
2019-08-13 18:00:12 -07:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::PadMapping:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnPadMapping(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::GBAConfig:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnGBAConfig(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::WiimoteMapping:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnWiimoteMapping(packet);
|
|
|
|
break;
|
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::PadData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnPadData(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::PadHostData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnPadHostData(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::WiimoteData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnWiimoteData(packet);
|
|
|
|
break;
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::PadBuffer:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnPadBuffer(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::HostInputAuthority:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnHostInputAuthority(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::GolfSwitch:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnGolfSwitch(packet);
|
|
|
|
break;
|
2018-10-18 04:33:05 -04:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::GolfPrepare:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnGolfPrepare(packet);
|
|
|
|
break;
|
2018-10-18 04:33:05 -04:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::ChangeGame:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnChangeGame(packet);
|
|
|
|
break;
|
2018-10-18 04:33:05 -04:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::GameStatus:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnGameStatus(packet);
|
|
|
|
break;
|
2018-10-18 04:33:05 -04:00
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::StartGame:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnStartGame(packet);
|
|
|
|
break;
|
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::StopGame:
|
|
|
|
case MessageID::DisableGame:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnStopGame(packet);
|
|
|
|
break;
|
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::PowerButton:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnPowerButton();
|
|
|
|
break;
|
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::Ping:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnPing(packet);
|
|
|
|
break;
|
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::PlayerPingData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnPlayerPingData(packet);
|
|
|
|
break;
|
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::DesyncDetected:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnDesyncDetected(packet);
|
|
|
|
break;
|
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::SyncSaveData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncSaveData(packet);
|
|
|
|
break;
|
|
|
|
|
2021-09-22 14:17:42 -04:00
|
|
|
case MessageID::SyncCodes:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncCodes(packet);
|
|
|
|
break;
|
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
case MessageID::ComputeGameDigest:
|
|
|
|
OnComputeGameDigest(packet);
|
2021-09-15 10:26:01 -04:00
|
|
|
break;
|
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
case MessageID::GameDigestProgress:
|
|
|
|
OnGameDigestProgress(packet);
|
2021-09-15 10:26:01 -04:00
|
|
|
break;
|
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
case MessageID::GameDigestResult:
|
|
|
|
OnGameDigestResult(packet);
|
2021-09-15 10:26:01 -04:00
|
|
|
break;
|
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
case MessageID::GameDigestError:
|
|
|
|
OnGameDigestError(packet);
|
2021-09-15 10:26:01 -04:00
|
|
|
break;
|
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
case MessageID::GameDigestAbort:
|
|
|
|
OnGameDigestAbort();
|
2021-09-15 10:26:01 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2022-01-12 16:47:26 -08:00
|
|
|
PanicAlertFmtT("Unknown message received with id : {0}", static_cast<u8>(mid));
|
2021-09-15 10:26:01 -04:00
|
|
|
break;
|
2018-10-18 04:33:05 -04:00
|
|
|
}
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2018-10-18 04:33:05 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnPlayerJoin(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
Player player{};
|
|
|
|
packet >> player.pid;
|
|
|
|
packet >> player.name;
|
|
|
|
packet >> player.revision;
|
2019-03-28 02:32:06 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Player {} ({}) using {} joined", player.name, player.pid, player.revision);
|
2019-03-28 02:32:06 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
{
|
|
|
|
std::lock_guard lkp(m_crit.players);
|
|
|
|
m_players[player.pid] = player;
|
2019-03-28 02:32:06 -04:00
|
|
|
}
|
2018-10-18 04:33:05 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
m_dialog->OnPlayerConnect(player.name);
|
|
|
|
|
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnPlayerLeave(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
|
2019-03-28 02:32:06 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
std::lock_guard lkp(m_crit.players);
|
|
|
|
const auto it = m_players.find(pid);
|
|
|
|
if (it == m_players.end())
|
|
|
|
return;
|
2018-10-18 04:33:05 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
const auto& player = it->second;
|
|
|
|
INFO_LOG_FMT(NETPLAY, "Player {} ({}) left", player.name, pid);
|
|
|
|
m_dialog->OnPlayerDisconnect(player.name);
|
|
|
|
m_players.erase(m_players.find(pid));
|
2018-10-18 04:33:05 -04:00
|
|
|
}
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnChatMessage(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
std::string msg;
|
|
|
|
packet >> msg;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// don't need lock to read in this thread
|
|
|
|
const Player& player = m_players[pid];
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Player {} ({}) wrote: {}", player.name, player.pid, msg);
|
|
|
|
|
|
|
|
// add to gui
|
|
|
|
std::ostringstream ss;
|
|
|
|
ss << player.name << '[' << char(pid + '0') << "]: " << msg;
|
|
|
|
|
|
|
|
m_dialog->AppendChat(ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnChunkedDataStart(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
u32 cid;
|
|
|
|
packet >> cid;
|
|
|
|
std::string title;
|
|
|
|
packet >> title;
|
|
|
|
const u64 data_size = Common::PacketReadU64(packet);
|
|
|
|
|
2023-03-30 00:02:25 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Starting data chunk {}.", cid);
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
m_chunked_data_receive_queue.emplace(cid, sf::Packet{});
|
|
|
|
|
|
|
|
std::vector<int> players;
|
|
|
|
players.push_back(m_local_player->pid);
|
|
|
|
m_dialog->ShowChunkedProgressDialog(title, data_size, players);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnChunkedDataEnd(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
u32 cid;
|
|
|
|
packet >> cid;
|
|
|
|
|
|
|
|
const auto data_packet_iter = m_chunked_data_receive_queue.find(cid);
|
|
|
|
if (data_packet_iter == m_chunked_data_receive_queue.end())
|
2023-03-30 00:02:25 +02:00
|
|
|
{
|
|
|
|
INFO_LOG_FMT(NETPLAY, "Invalid data chunk ID {}.", cid);
|
2021-09-15 10:26:01 -04:00
|
|
|
return;
|
2023-03-30 00:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
INFO_LOG_FMT(NETPLAY, "Ending data chunk {}.", cid);
|
2021-09-15 10:26:01 -04:00
|
|
|
|
|
|
|
auto& data_packet = data_packet_iter->second;
|
|
|
|
OnData(data_packet);
|
|
|
|
m_chunked_data_receive_queue.erase(data_packet_iter);
|
|
|
|
m_dialog->HideChunkedProgressDialog();
|
|
|
|
|
|
|
|
sf::Packet complete_packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
complete_packet << MessageID::ChunkedDataComplete;
|
2021-09-15 10:26:01 -04:00
|
|
|
complete_packet << cid;
|
|
|
|
Send(complete_packet, CHUNKED_DATA_CHANNEL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnChunkedDataPayload(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
u32 cid;
|
|
|
|
packet >> cid;
|
|
|
|
|
|
|
|
const auto data_packet_iter = m_chunked_data_receive_queue.find(cid);
|
|
|
|
if (data_packet_iter == m_chunked_data_receive_queue.end())
|
2023-03-30 00:02:25 +02:00
|
|
|
{
|
|
|
|
INFO_LOG_FMT(NETPLAY, "Invalid data chunk ID {}.", cid);
|
2021-09-15 10:26:01 -04:00
|
|
|
return;
|
2023-03-30 00:02:25 +02:00
|
|
|
}
|
2021-09-15 10:26:01 -04:00
|
|
|
|
|
|
|
auto& data_packet = data_packet_iter->second;
|
|
|
|
while (!packet.endOfPacket())
|
|
|
|
{
|
|
|
|
u8 byte;
|
|
|
|
packet >> byte;
|
|
|
|
data_packet << byte;
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-30 00:02:25 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received {} bytes of data chunk {}.", data_packet.getDataSize(), cid);
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
m_dialog->SetChunkedProgress(m_local_player->pid, data_packet.getDataSize());
|
|
|
|
|
|
|
|
sf::Packet progress_packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
progress_packet << MessageID::ChunkedDataProgress;
|
2021-09-15 10:26:01 -04:00
|
|
|
progress_packet << cid;
|
|
|
|
progress_packet << sf::Uint64{data_packet.getDataSize()};
|
|
|
|
Send(progress_packet, CHUNKED_DATA_CHANNEL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnChunkedDataAbort(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
u32 cid;
|
|
|
|
packet >> cid;
|
|
|
|
|
|
|
|
const auto iter = m_chunked_data_receive_queue.find(cid);
|
|
|
|
if (iter == m_chunked_data_receive_queue.end())
|
2023-03-30 00:02:25 +02:00
|
|
|
{
|
|
|
|
INFO_LOG_FMT(NETPLAY, "Invalid data chunk ID {}.", cid);
|
2021-09-15 10:26:01 -04:00
|
|
|
return;
|
2023-03-30 00:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
INFO_LOG_FMT(NETPLAY, "Aborting data chunk {}.", cid);
|
2021-09-15 10:26:01 -04:00
|
|
|
|
|
|
|
m_chunked_data_receive_queue.erase(iter);
|
|
|
|
m_dialog->HideChunkedProgressDialog();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnPadMapping(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
for (PlayerId& mapping : m_pad_map)
|
|
|
|
packet >> mapping;
|
|
|
|
|
|
|
|
UpdateDevices();
|
|
|
|
|
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnWiimoteMapping(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
for (PlayerId& mapping : m_wiimote_map)
|
|
|
|
packet >> mapping;
|
|
|
|
|
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnGBAConfig(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < m_gba_config.size(); ++i)
|
2021-07-04 13:33:58 +02:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
auto& config = m_gba_config[i];
|
|
|
|
const auto old_config = config;
|
2021-07-04 13:33:58 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
packet >> config.enabled >> config.has_rom >> config.title;
|
|
|
|
for (auto& data : config.hash)
|
|
|
|
packet >> data;
|
2021-07-04 13:33:58 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
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);
|
2022-09-19 01:18:26 +02:00
|
|
|
m_net_settings.gba_rom_paths[i] =
|
2021-09-15 10:26:01 -04:00
|
|
|
config.has_rom ?
|
|
|
|
m_dialog->FindGBARomPath(config.hash, config.title, static_cast<int>(i)) :
|
|
|
|
"";
|
2021-07-04 13:33:58 +02:00
|
|
|
}
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2021-07-04 13:33:58 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
SendGameStatus();
|
|
|
|
UpdateDevices();
|
2021-07-04 13:33:58 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
m_dialog->Update();
|
|
|
|
}
|
2021-07-04 13:33:58 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnPadData(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
while (!packet.endOfPacket())
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
PadIndex map;
|
|
|
|
packet >> map;
|
|
|
|
|
|
|
|
GCPadStatus pad;
|
|
|
|
packet >> pad.button;
|
|
|
|
if (!m_gba_config.at(map).enabled)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >>
|
|
|
|
pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
|
2013-08-06 23:48:52 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Trusting server for good map value (>=0 && <4)
|
|
|
|
// add to pad buffer
|
|
|
|
m_pad_buffer.at(map).Push(pad);
|
|
|
|
m_gc_pad_event.Set();
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnPadHostData(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
while (!packet.endOfPacket())
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
PadIndex map;
|
|
|
|
packet >> map;
|
|
|
|
|
|
|
|
GCPadStatus pad;
|
|
|
|
packet >> pad.button;
|
|
|
|
if (!m_gba_config.at(map).enabled)
|
2018-07-09 16:45:52 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
packet >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >> pad.substickX >>
|
|
|
|
pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Trusting server for good map value (>=0 && <4)
|
|
|
|
// write to last status
|
|
|
|
m_last_pad_status[map] = pad;
|
2018-07-09 16:45:52 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (!m_first_pad_status_received[map])
|
|
|
|
{
|
|
|
|
m_first_pad_status_received[map] = true;
|
|
|
|
m_first_pad_status_received_event.Set();
|
2018-07-09 16:45:52 -04:00
|
|
|
}
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnWiimoteData(sf::Packet& packet)
|
|
|
|
{
|
2022-09-23 03:21:52 +02:00
|
|
|
while (!packet.endOfPacket())
|
|
|
|
{
|
|
|
|
PadIndex map;
|
|
|
|
packet >> map;
|
2019-04-01 22:36:48 -04:00
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
WiimoteEmu::SerializedWiimoteState pad;
|
|
|
|
packet >> pad.length;
|
|
|
|
ASSERT(pad.length <= pad.data.size());
|
|
|
|
if (pad.length <= pad.data.size())
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < pad.length; ++i)
|
|
|
|
packet >> pad.data[i];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pad.length = 0;
|
|
|
|
}
|
2019-04-01 22:36:48 -04:00
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
// Trusting server for good map value (>=0 && <4)
|
|
|
|
// add to pad buffer
|
|
|
|
m_wiimote_buffer.at(map).Push(pad);
|
|
|
|
m_wii_pad_event.Set();
|
|
|
|
}
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2019-04-01 22:36:48 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnPadBuffer(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
u32 size = 0;
|
|
|
|
packet >> size;
|
|
|
|
|
|
|
|
m_target_buffer_size = size;
|
|
|
|
m_dialog->OnPadBufferChanged(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnHostInputAuthority(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
packet >> m_host_input_authority;
|
|
|
|
m_dialog->OnHostInputAuthorityChanged(m_host_input_authority);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnGolfSwitch(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
|
|
|
|
const PlayerId previous_golfer = m_current_golfer;
|
|
|
|
m_current_golfer = pid;
|
|
|
|
m_dialog->OnGolferChanged(m_local_player->pid == pid, pid != 0 ? m_players[pid].name : "");
|
|
|
|
|
|
|
|
if (m_local_player->pid == previous_golfer)
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
sf::Packet spac;
|
2021-09-22 14:17:42 -04:00
|
|
|
spac << MessageID::GolfRelease;
|
2021-09-15 10:26:01 -04:00
|
|
|
Send(spac);
|
|
|
|
}
|
|
|
|
else if (m_local_player->pid == pid)
|
|
|
|
{
|
|
|
|
sf::Packet spac;
|
2021-09-22 14:17:42 -04:00
|
|
|
spac << MessageID::GolfAcquire;
|
2021-09-15 10:26:01 -04:00
|
|
|
Send(spac);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Pads are already calibrated so we can just ignore this
|
|
|
|
m_first_pad_status_received.fill(true);
|
|
|
|
|
|
|
|
m_wait_on_input = false;
|
|
|
|
m_wait_on_input_event.Set();
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnGolfPrepare(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
m_wait_on_input_received = true;
|
|
|
|
m_wait_on_input = true;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnChangeGame(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
std::string netplay_name;
|
|
|
|
{
|
|
|
|
std::lock_guard lkg(m_crit.game);
|
|
|
|
ReceiveSyncIdentifier(packet, m_selected_game);
|
|
|
|
packet >> netplay_name;
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Game changed to {}", netplay_name);
|
|
|
|
|
|
|
|
// update gui
|
|
|
|
m_dialog->OnMsgChangeGame(m_selected_game, netplay_name);
|
|
|
|
|
|
|
|
SendGameStatus();
|
|
|
|
|
|
|
|
sf::Packet client_capabilities_packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
client_capabilities_packet << MessageID::ClientCapabilities;
|
2021-09-15 10:26:01 -04:00
|
|
|
client_capabilities_packet << ExpansionInterface::CEXIIPL::HasIPLDump();
|
|
|
|
client_capabilities_packet << Config::Get(Config::SESSION_USE_FMA);
|
|
|
|
Send(client_capabilities_packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnGameStatus(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
std::lock_guard lkp(m_crit.players);
|
2021-09-22 15:29:04 -04:00
|
|
|
packet >> m_players[pid].game_status;
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
}
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnStartGame(sf::Packet& packet)
|
|
|
|
{
|
2019-04-02 08:08:27 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
std::lock_guard lkg(m_crit.game);
|
2019-04-02 08:08:27 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Start of game {}", m_selected_game.game_id);
|
2019-04-02 08:08:27 -04:00
|
|
|
|
2021-09-22 15:29:04 -04:00
|
|
|
packet >> m_current_game;
|
2022-09-19 01:18:26 +02:00
|
|
|
packet >> m_net_settings.cpu_thread;
|
|
|
|
packet >> m_net_settings.cpu_core;
|
|
|
|
packet >> m_net_settings.enable_cheats;
|
|
|
|
packet >> m_net_settings.selected_language;
|
|
|
|
packet >> m_net_settings.override_region_settings;
|
|
|
|
packet >> m_net_settings.dsp_enable_jit;
|
|
|
|
packet >> m_net_settings.dsp_hle;
|
|
|
|
packet >> m_net_settings.ram_override_enable;
|
|
|
|
packet >> m_net_settings.mem1_size;
|
|
|
|
packet >> m_net_settings.mem2_size;
|
|
|
|
packet >> m_net_settings.fallback_region;
|
|
|
|
packet >> m_net_settings.allow_sd_writes;
|
|
|
|
packet >> m_net_settings.oc_enable;
|
|
|
|
packet >> m_net_settings.oc_factor;
|
2019-04-02 08:08:27 -04:00
|
|
|
|
2020-08-30 13:43:45 -07:00
|
|
|
for (auto slot : ExpansionInterface::SLOTS)
|
2022-09-19 01:18:26 +02:00
|
|
|
packet >> m_net_settings.exi_device[slot];
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-09-19 01:18:26 +02:00
|
|
|
packet >> m_net_settings.memcard_size_override;
|
2022-01-26 00:57:44 +01:00
|
|
|
|
2022-09-19 01:18:26 +02:00
|
|
|
for (u32& value : m_net_settings.sysconf_settings)
|
2021-09-15 10:26:01 -04:00
|
|
|
packet >> value;
|
|
|
|
|
2022-09-19 01:18:26 +02:00
|
|
|
packet >> m_net_settings.efb_access_enable;
|
|
|
|
packet >> m_net_settings.bbox_enable;
|
|
|
|
packet >> m_net_settings.force_progressive;
|
|
|
|
packet >> m_net_settings.efb_to_texture_enable;
|
|
|
|
packet >> m_net_settings.xfb_to_texture_enable;
|
|
|
|
packet >> m_net_settings.disable_copy_to_vram;
|
|
|
|
packet >> m_net_settings.immediate_xfb_enable;
|
|
|
|
packet >> m_net_settings.efb_emulate_format_changes;
|
|
|
|
packet >> m_net_settings.safe_texture_cache_color_samples;
|
|
|
|
packet >> m_net_settings.perf_queries_enable;
|
|
|
|
packet >> m_net_settings.float_exceptions;
|
|
|
|
packet >> m_net_settings.divide_by_zero_exceptions;
|
|
|
|
packet >> m_net_settings.fprf;
|
|
|
|
packet >> m_net_settings.accurate_nans;
|
|
|
|
packet >> m_net_settings.disable_icache;
|
|
|
|
packet >> m_net_settings.sync_on_skip_idle;
|
|
|
|
packet >> m_net_settings.sync_gpu;
|
|
|
|
packet >> m_net_settings.sync_gpu_max_distance;
|
|
|
|
packet >> m_net_settings.sync_gpu_min_distance;
|
|
|
|
packet >> m_net_settings.sync_gpu_overclock;
|
|
|
|
packet >> m_net_settings.jit_follow_branch;
|
|
|
|
packet >> m_net_settings.fast_disc_speed;
|
|
|
|
packet >> m_net_settings.mmu;
|
|
|
|
packet >> m_net_settings.fastmem;
|
|
|
|
packet >> m_net_settings.skip_ipl;
|
|
|
|
packet >> m_net_settings.load_ipl_dump;
|
|
|
|
packet >> m_net_settings.vertex_rounding;
|
|
|
|
packet >> m_net_settings.internal_resolution;
|
|
|
|
packet >> m_net_settings.efb_scaled_copy;
|
|
|
|
packet >> m_net_settings.fast_depth_calc;
|
|
|
|
packet >> m_net_settings.enable_pixel_lighting;
|
|
|
|
packet >> m_net_settings.widescreen_hack;
|
2022-11-14 06:01:19 +01:00
|
|
|
packet >> m_net_settings.force_texture_filtering;
|
2022-09-19 01:18:26 +02:00
|
|
|
packet >> m_net_settings.max_anisotropy;
|
|
|
|
packet >> m_net_settings.force_true_color;
|
|
|
|
packet >> m_net_settings.disable_copy_filter;
|
|
|
|
packet >> m_net_settings.disable_fog;
|
|
|
|
packet >> m_net_settings.arbitrary_mipmap_detection;
|
|
|
|
packet >> m_net_settings.arbitrary_mipmap_detection_threshold;
|
|
|
|
packet >> m_net_settings.enable_gpu_texture_decoding;
|
|
|
|
packet >> m_net_settings.defer_efb_copies;
|
|
|
|
packet >> m_net_settings.efb_access_tile_size;
|
|
|
|
packet >> m_net_settings.efb_access_defer_invalidation;
|
2022-09-11 02:35:20 +02:00
|
|
|
packet >> m_net_settings.savedata_load;
|
|
|
|
packet >> m_net_settings.savedata_write;
|
|
|
|
packet >> m_net_settings.savedata_sync_all_wii;
|
|
|
|
if (!m_net_settings.savedata_load)
|
|
|
|
{
|
|
|
|
m_net_settings.savedata_write = false;
|
|
|
|
m_net_settings.savedata_sync_all_wii = false;
|
|
|
|
}
|
2022-09-19 01:18:26 +02:00
|
|
|
packet >> m_net_settings.strict_settings_sync;
|
2021-09-15 10:26:01 -04:00
|
|
|
|
|
|
|
m_initial_rtc = Common::PacketReadU64(packet);
|
|
|
|
|
2022-09-19 01:18:26 +02:00
|
|
|
packet >> m_net_settings.save_data_region;
|
|
|
|
packet >> m_net_settings.sync_codes;
|
2021-09-15 10:26:01 -04:00
|
|
|
|
2022-09-19 01:18:26 +02:00
|
|
|
packet >> m_net_settings.golf_mode;
|
|
|
|
packet >> m_net_settings.use_fma;
|
|
|
|
packet >> m_net_settings.hide_remote_gbas;
|
2021-09-15 10:26:01 -04:00
|
|
|
|
2022-09-13 04:44:59 +02:00
|
|
|
for (size_t i = 0; i < sizeof(m_net_settings.sram); ++i)
|
|
|
|
packet >> m_net_settings.sram[i];
|
|
|
|
|
2022-09-19 01:18:26 +02:00
|
|
|
m_net_settings.is_hosting = m_local_player->IsHost();
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
m_dialog->OnMsgStartGame();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnStopGame(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
INFO_LOG_FMT(NETPLAY, "Game stopped");
|
|
|
|
|
|
|
|
StopGame();
|
|
|
|
m_dialog->OnMsgStopGame();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnPowerButton()
|
|
|
|
{
|
2022-09-13 01:23:17 +02:00
|
|
|
InvokeStop();
|
2021-09-15 10:26:01 -04:00
|
|
|
m_dialog->OnMsgPowerButton();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnPing(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
u32 ping_key = 0;
|
|
|
|
packet >> ping_key;
|
|
|
|
|
|
|
|
sf::Packet response_packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
response_packet << MessageID::Pong;
|
2021-09-15 10:26:01 -04:00
|
|
|
response_packet << ping_key;
|
|
|
|
|
|
|
|
Send(response_packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::OnPlayerPingData(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard lkp(m_crit.players);
|
|
|
|
Player& player = m_players[pid];
|
|
|
|
packet >> player.ping;
|
|
|
|
}
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
DisplayPlayersPing();
|
|
|
|
m_dialog->Update();
|
|
|
|
}
|
2016-07-10 10:13:34 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnDesyncDetected(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
int pid_to_blame;
|
|
|
|
u32 frame;
|
|
|
|
packet >> pid_to_blame;
|
|
|
|
packet >> frame;
|
2018-07-19 18:10:37 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
std::string player = "??";
|
|
|
|
std::lock_guard lkp(m_crit.players);
|
|
|
|
{
|
|
|
|
const auto it = m_players.find(pid_to_blame);
|
|
|
|
if (it != m_players.end())
|
|
|
|
player = it->second.name;
|
2016-07-10 10:13:34 +02:00
|
|
|
}
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Player {} ({}) desynced!", player, pid_to_blame);
|
2016-07-10 10:13:34 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
m_dialog->OnDesync(frame, player);
|
|
|
|
}
|
2016-07-10 10:13:34 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncSaveData(sf::Packet& packet)
|
|
|
|
{
|
2021-09-22 14:49:41 -04:00
|
|
|
SyncSaveDataID sub_id;
|
2021-09-15 10:26:01 -04:00
|
|
|
packet >> sub_id;
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return;
|
2018-06-15 08:11:18 -04:00
|
|
|
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Processing OnSyncSaveData sub id: {}", static_cast<u8>(sub_id));
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
switch (sub_id)
|
|
|
|
{
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncSaveDataID::Notify:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncSaveDataNotify(packet);
|
|
|
|
break;
|
2021-03-06 18:58:19 +01:00
|
|
|
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncSaveDataID::RawData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncSaveDataRaw(packet);
|
|
|
|
break;
|
2021-03-06 18:58:19 +01:00
|
|
|
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncSaveDataID::GCIData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncSaveDataGCI(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncSaveDataID::WiiData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncSaveDataWii(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncSaveDataID::GBAData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncSaveDataGBA(packet);
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
default:
|
2022-01-12 16:47:26 -08:00
|
|
|
PanicAlertFmtT("Unknown SYNC_SAVE_DATA message received with id: {0}", static_cast<u8>(sub_id));
|
2021-09-15 10:26:01 -04:00
|
|
|
break;
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncSaveDataNotify(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
packet >> m_sync_save_data_count;
|
|
|
|
m_sync_save_data_success_count = 0;
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Initializing wait for {} savegame chunks.", m_sync_save_data_count);
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (m_sync_save_data_count == 0)
|
|
|
|
SyncSaveDataResponse(true);
|
|
|
|
else
|
|
|
|
m_dialog->AppendChat(Common::GetStringT("Synchronizing save data..."));
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncSaveDataRaw(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
bool is_slot_a;
|
|
|
|
std::string region;
|
|
|
|
int size_override;
|
|
|
|
packet >> is_slot_a >> region >> size_override;
|
|
|
|
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received raw memcard data for slot {}: region {}, size override {}.",
|
|
|
|
is_slot_a ? 'A' : 'B', region, size_override);
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// This check is mainly intended to filter out characters which have special meanings in paths
|
|
|
|
if (region != JAP_DIR && region != USA_DIR && region != EUR_DIR)
|
2018-11-10 22:37:49 -05:00
|
|
|
{
|
2022-11-16 04:00:43 +01:00
|
|
|
WARN_LOG_FMT(NETPLAY, "Received invalid raw memory card region.");
|
2021-09-15 10:26:01 -04:00
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return;
|
2018-11-10 22:37:49 -05:00
|
|
|
}
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
std::string size_suffix;
|
|
|
|
if (size_override >= 0 && size_override <= 4)
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
size_suffix = fmt::format(
|
|
|
|
".{}", Memcard::MbitToFreeBlocks(Memcard::MBIT_SIZE_MEMORY_CARD_59 << size_override));
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY +
|
|
|
|
(is_slot_a ? "A." : "B.") + region + size_suffix + ".raw";
|
|
|
|
if (File::Exists(path) && !File::Delete(path))
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
PanicAlertFmtT("Failed to delete NetPlay memory card. Verify your write permissions.");
|
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return;
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
const bool success = DecompressPacketIntoFile(packet, path);
|
|
|
|
SyncSaveDataResponse(success);
|
|
|
|
}
|
2016-02-02 16:35:27 +01:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncSaveDataGCI(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
bool is_slot_a;
|
|
|
|
u8 file_count;
|
|
|
|
packet >> is_slot_a >> file_count;
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY DIR_SEP +
|
|
|
|
fmt::format("Card {}", is_slot_a ? 'A' : 'B');
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received GCI memcard data for slot {}: {}, {} files.",
|
|
|
|
is_slot_a ? 'A' : 'B', path, file_count);
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if ((File::Exists(path) && !File::DeleteDirRecursively(path + DIR_SEP)) ||
|
|
|
|
!File::CreateFullPath(path + DIR_SEP))
|
|
|
|
{
|
|
|
|
PanicAlertFmtT("Failed to reset NetPlay GCI folder. Verify your write permissions.");
|
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return;
|
2015-03-08 06:50:47 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
for (u8 i = 0; i < file_count; i++)
|
2015-06-14 13:59:41 +02:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
std::string file_name;
|
|
|
|
packet >> file_name;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received GCI: {}", file_name);
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (!Common::IsFileNameSafe(file_name) ||
|
|
|
|
!DecompressPacketIntoFile(packet, path + DIR_SEP + file_name))
|
2015-06-14 13:59:41 +02:00
|
|
|
{
|
2022-11-16 04:00:43 +01:00
|
|
|
WARN_LOG_FMT(NETPLAY, "Received invalid GCI.");
|
2021-09-15 10:26:01 -04:00
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return;
|
2015-06-14 13:59:41 +02:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
SyncSaveDataResponse(true);
|
|
|
|
}
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncSaveDataWii(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP;
|
2021-11-14 02:22:59 +01:00
|
|
|
std::string redirect_path = File::GetUserPath(D_USER_IDX) + "Redirect" GC_MEMCARD_NETPLAY DIR_SEP;
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (File::Exists(path) && !File::DeleteDirRecursively(path))
|
|
|
|
{
|
|
|
|
PanicAlertFmtT("Failed to reset NetPlay NAND folder. Verify your write permissions.");
|
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return;
|
|
|
|
}
|
2021-11-14 02:22:59 +01:00
|
|
|
if (File::Exists(redirect_path) && !File::DeleteDirRecursively(redirect_path))
|
|
|
|
{
|
|
|
|
PanicAlertFmtT("Failed to reset NetPlay redirect folder. Verify your write permissions.");
|
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return;
|
|
|
|
}
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
auto temp_fs = std::make_unique<IOS::HLE::FS::HostFileSystem>(path);
|
|
|
|
std::vector<u64> titles;
|
2021-05-07 18:19:42 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
constexpr IOS::HLE::FS::Modes fs_modes{
|
|
|
|
IOS::HLE::FS::Mode::ReadWrite,
|
|
|
|
IOS::HLE::FS::Mode::ReadWrite,
|
|
|
|
IOS::HLE::FS::Mode::ReadWrite,
|
|
|
|
};
|
2021-05-27 01:59:12 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Read the Mii data
|
|
|
|
bool mii_data;
|
|
|
|
packet >> mii_data;
|
|
|
|
if (mii_data)
|
|
|
|
{
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received Mii data.");
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
auto buffer = DecompressPacketIntoBuffer(packet);
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
temp_fs->CreateFullPath(IOS::PID_KERNEL, IOS::PID_KERNEL, "/shared2/menu/FaceLib/", 0,
|
|
|
|
fs_modes);
|
|
|
|
auto file = temp_fs->CreateAndOpenFile(IOS::PID_KERNEL, IOS::PID_KERNEL,
|
|
|
|
Common::GetMiiDatabasePath(), fs_modes);
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (!buffer || !file || !file->Write(buffer->data(), buffer->size()))
|
2018-07-04 17:01:50 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
PanicAlertFmtT("Failed to write Mii data.");
|
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Read the saves
|
|
|
|
u32 save_count;
|
|
|
|
packet >> save_count;
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received data for {} Wii saves.", save_count);
|
2021-09-15 10:26:01 -04:00
|
|
|
for (u32 n = 0; n < save_count; n++)
|
|
|
|
{
|
|
|
|
u64 title_id = Common::PacketReadU64(packet);
|
|
|
|
titles.push_back(title_id);
|
|
|
|
temp_fs->CreateFullPath(IOS::PID_KERNEL, IOS::PID_KERNEL,
|
|
|
|
Common::GetTitleDataPath(title_id) + '/', 0, fs_modes);
|
|
|
|
auto save = WiiSave::MakeNandStorage(temp_fs.get(), title_id);
|
|
|
|
|
|
|
|
bool exists;
|
|
|
|
packet >> exists;
|
|
|
|
if (!exists)
|
2022-11-16 04:00:43 +01:00
|
|
|
{
|
|
|
|
INFO_LOG_FMT(NETPLAY, "No data for Wii save of title {:016x}.", title_id);
|
2021-09-15 10:26:01 -04:00
|
|
|
continue;
|
2022-11-16 04:00:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
INFO_LOG_FMT(NETPLAY, "Received Wii save of title {:016x}.", title_id);
|
2021-09-15 10:26:01 -04:00
|
|
|
|
|
|
|
// Header
|
|
|
|
WiiSave::Header header;
|
|
|
|
packet >> header.tid;
|
|
|
|
packet >> header.banner_size;
|
|
|
|
packet >> header.permissions;
|
|
|
|
packet >> header.unk1;
|
|
|
|
for (u8& byte : header.md5)
|
|
|
|
packet >> byte;
|
|
|
|
packet >> header.unk2;
|
|
|
|
for (size_t i = 0; i < header.banner_size; i++)
|
|
|
|
packet >> header.banner[i];
|
|
|
|
|
|
|
|
// BkHeader
|
|
|
|
WiiSave::BkHeader bk_header;
|
|
|
|
packet >> bk_header.size;
|
|
|
|
packet >> bk_header.magic;
|
|
|
|
packet >> bk_header.ngid;
|
|
|
|
packet >> bk_header.number_of_files;
|
|
|
|
packet >> bk_header.size_of_files;
|
|
|
|
packet >> bk_header.unk1;
|
|
|
|
packet >> bk_header.unk2;
|
|
|
|
packet >> bk_header.total_size;
|
|
|
|
for (u8& byte : bk_header.unk3)
|
|
|
|
packet >> byte;
|
|
|
|
packet >> bk_header.tid;
|
|
|
|
for (u8& byte : bk_header.mac_address)
|
|
|
|
packet >> byte;
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Files
|
|
|
|
std::vector<WiiSave::Storage::SaveFile> files;
|
|
|
|
for (u32 i = 0; i < bk_header.number_of_files; i++)
|
|
|
|
{
|
|
|
|
WiiSave::Storage::SaveFile file;
|
|
|
|
packet >> file.mode >> file.attributes;
|
2021-09-22 15:29:04 -04:00
|
|
|
packet >> file.type;
|
2021-09-15 10:26:01 -04:00
|
|
|
packet >> file.path;
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received Wii save data of type {} at {}", static_cast<u8>(file.type),
|
|
|
|
file.path);
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (file.type == WiiSave::Storage::SaveFile::Type::File)
|
2018-07-04 17:01:50 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
auto buffer = DecompressPacketIntoBuffer(packet);
|
|
|
|
if (!buffer)
|
2018-07-04 17:01:50 -04:00
|
|
|
{
|
|
|
|
SyncSaveDataResponse(false);
|
2021-09-15 10:26:01 -04:00
|
|
|
return;
|
2018-07-04 17:01:50 -04:00
|
|
|
}
|
2021-09-15 10:26:01 -04:00
|
|
|
|
|
|
|
file.data = std::move(*buffer);
|
2018-07-04 17:01:50 -04:00
|
|
|
}
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
files.push_back(std::move(file));
|
2018-07-04 17:01:50 -04:00
|
|
|
}
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (!save->WriteHeader(header) || !save->WriteBkHeader(bk_header) || !save->WriteFiles(files))
|
2018-07-04 17:01:50 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
PanicAlertFmtT("Failed to write Wii save.");
|
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-11-14 02:22:59 +01:00
|
|
|
bool has_redirected_save;
|
|
|
|
packet >> has_redirected_save;
|
|
|
|
if (has_redirected_save)
|
|
|
|
{
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received redirected save.");
|
2021-11-14 02:22:59 +01:00
|
|
|
if (!DecompressPacketIntoFolder(packet, redirect_path))
|
|
|
|
{
|
|
|
|
PanicAlertFmtT("Failed to write redirected save.");
|
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SetWiiSyncData(std::move(temp_fs), std::move(titles), std::move(redirect_path));
|
2021-09-15 10:26:01 -04:00
|
|
|
SyncSaveDataResponse(true);
|
|
|
|
}
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncSaveDataGBA(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
u8 slot;
|
|
|
|
packet >> slot;
|
2018-10-02 09:16:12 -04:00
|
|
|
|
2022-11-16 04:00:43 +01:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received GBA save for slot {}.", slot);
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
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;
|
|
|
|
}
|
2019-04-08 07:06:21 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
const bool success = DecompressPacketIntoFile(packet, path);
|
|
|
|
SyncSaveDataResponse(success);
|
|
|
|
}
|
2019-04-08 07:06:21 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncCodes(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
// Recieve Data Packet
|
2021-09-22 14:49:41 -04:00
|
|
|
SyncCodeID sub_id;
|
2021-09-15 10:26:01 -04:00
|
|
|
packet >> sub_id;
|
2019-04-08 07:06:21 -04:00
|
|
|
|
2023-03-30 00:02:25 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Processing OnSyncCodes sub id: {}", static_cast<u8>(sub_id));
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Check Which Operation to Perform with This Packet
|
|
|
|
switch (sub_id)
|
|
|
|
{
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncCodeID::Notify:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncCodesNotify();
|
|
|
|
break;
|
2019-04-08 07:06:21 -04:00
|
|
|
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncCodeID::NotifyGecko:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncCodesNotifyGecko(packet);
|
|
|
|
break;
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncCodeID::GeckoData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncCodesDataGecko(packet);
|
|
|
|
break;
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncCodeID::NotifyAR:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncCodesNotifyAR(packet);
|
|
|
|
break;
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-22 14:49:41 -04:00
|
|
|
case SyncCodeID::ARData:
|
2021-09-15 10:26:01 -04:00
|
|
|
OnSyncCodesDataAR(packet);
|
|
|
|
break;
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
default:
|
2022-01-12 16:47:26 -08:00
|
|
|
PanicAlertFmtT("Unknown SYNC_CODES message received with id: {0}", static_cast<u8>(sub_id));
|
2018-07-04 17:01:50 -04:00
|
|
|
break;
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
|
|
|
}
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncCodesNotify()
|
|
|
|
{
|
|
|
|
// Set both codes as unsynced
|
|
|
|
m_sync_gecko_codes_complete = false;
|
|
|
|
m_sync_ar_codes_complete = false;
|
|
|
|
}
|
2021-07-04 13:33:58 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncCodesNotifyGecko(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
// Return if this is the host
|
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return;
|
2021-07-04 13:33:58 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Receive Number of Codelines to Receive
|
|
|
|
packet >> m_sync_gecko_codes_count;
|
2021-07-04 13:33:58 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
m_sync_gecko_codes_success_count = 0;
|
2021-07-04 13:33:58 +02:00
|
|
|
|
2023-03-30 00:02:25 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Receiving {} Gecko codelines", m_sync_gecko_codes_count);
|
2018-07-04 17:01:50 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Check if no codes to sync, if so return as finished
|
|
|
|
if (m_sync_gecko_codes_count == 0)
|
2018-10-04 18:49:41 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
m_sync_gecko_codes_complete = true;
|
|
|
|
SyncCodeResponse(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_dialog->AppendChat(Common::GetStringT("Synchronizing Gecko codes..."));
|
|
|
|
}
|
|
|
|
}
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncCodesDataGecko(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
// Return if this is the host
|
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
std::vector<Gecko::GeckoCode> synced_codes;
|
|
|
|
synced_codes.reserve(m_sync_gecko_codes_count);
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
Gecko::GeckoCode gcode{};
|
|
|
|
gcode.name = "Synced Codes";
|
|
|
|
gcode.enabled = true;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Receive code contents from packet
|
|
|
|
for (u32 i = 0; i < m_sync_gecko_codes_count; i++)
|
|
|
|
{
|
|
|
|
Gecko::GeckoCode::Code new_code;
|
|
|
|
packet >> new_code.address;
|
|
|
|
packet >> new_code.data;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2023-03-30 00:02:25 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received {:08x} {:08x}", new_code.address, new_code.data);
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
gcode.codes.push_back(std::move(new_code));
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (++m_sync_gecko_codes_success_count >= m_sync_gecko_codes_count)
|
2018-10-04 18:49:41 -04:00
|
|
|
{
|
2021-09-15 10:26:01 -04:00
|
|
|
m_sync_gecko_codes_complete = true;
|
|
|
|
SyncCodeResponse(true);
|
|
|
|
}
|
|
|
|
}
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Add gcode containing all codes to Gecko Code vector
|
|
|
|
synced_codes.push_back(std::move(gcode));
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Clear Vector if received 0 codes (match host's end when using no codes)
|
|
|
|
if (m_sync_gecko_codes_count == 0)
|
|
|
|
synced_codes.clear();
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Copy this to the vector located in GeckoCode.cpp
|
|
|
|
Gecko::UpdateSyncedCodes(synced_codes);
|
|
|
|
}
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncCodesNotifyAR(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
// Return if this is the host
|
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Receive Number of Codelines to Receive
|
|
|
|
packet >> m_sync_ar_codes_count;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
m_sync_ar_codes_success_count = 0;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2023-03-30 00:02:25 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Receiving {} AR codelines", m_sync_ar_codes_count);
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// 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(Common::GetStringT("Synchronizing AR codes..."));
|
|
|
|
}
|
|
|
|
}
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
void NetPlayClient::OnSyncCodesDataAR(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
// Return if this is the host
|
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
std::vector<ActionReplay::ARCode> synced_codes;
|
|
|
|
synced_codes.reserve(m_sync_ar_codes_count);
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
ActionReplay::ARCode arcode{};
|
|
|
|
arcode.name = "Synced Codes";
|
|
|
|
arcode.enabled = true;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Receive code contents from packet
|
|
|
|
for (u32 i = 0; i < m_sync_ar_codes_count; i++)
|
|
|
|
{
|
|
|
|
ActionReplay::AREntry new_code;
|
|
|
|
packet >> new_code.cmd_addr;
|
|
|
|
packet >> new_code.value;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2023-03-30 00:02:25 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Received {:08x} {:08x}", new_code.cmd_addr, new_code.value);
|
2021-09-15 10:26:01 -04:00
|
|
|
arcode.ops.push_back(new_code);
|
2018-10-04 18:49:41 -04:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
if (++m_sync_ar_codes_success_count >= m_sync_ar_codes_count)
|
|
|
|
{
|
|
|
|
m_sync_ar_codes_complete = true;
|
|
|
|
SyncCodeResponse(true);
|
2018-10-04 18:49:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Add arcode containing all codes to AR Code vector
|
|
|
|
synced_codes.push_back(std::move(arcode));
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Clear Vector if received 0 codes (match host's end when using no codes)
|
|
|
|
if (m_sync_ar_codes_count == 0)
|
|
|
|
synced_codes.clear();
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2021-09-15 10:26:01 -04:00
|
|
|
// Copy this to the vector located in ActionReplay.cpp
|
|
|
|
ActionReplay::UpdateSyncedCodes(synced_codes);
|
|
|
|
}
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
void NetPlayClient::OnComputeGameDigest(sf::Packet& packet)
|
2021-09-15 10:26:01 -04:00
|
|
|
{
|
|
|
|
SyncIdentifier sync_identifier;
|
|
|
|
ReceiveSyncIdentifier(packet, sync_identifier);
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
ComputeGameDigest(sync_identifier);
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
void NetPlayClient::OnGameDigestProgress(sf::Packet& packet)
|
2021-09-15 10:26:01 -04:00
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
int progress;
|
|
|
|
packet >> pid;
|
|
|
|
packet >> progress;
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
m_dialog->SetGameDigestProgress(pid, progress);
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
void NetPlayClient::OnGameDigestResult(sf::Packet& packet)
|
2021-09-15 10:26:01 -04:00
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
std::string result;
|
|
|
|
packet >> pid;
|
|
|
|
packet >> result;
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
m_dialog->SetGameDigestResult(pid, result);
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
void NetPlayClient::OnGameDigestError(sf::Packet& packet)
|
2021-09-15 10:26:01 -04:00
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
std::string error;
|
|
|
|
packet >> pid;
|
|
|
|
packet >> error;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
m_dialog->SetGameDigestResult(pid, error);
|
2021-09-15 10:26:01 -04:00
|
|
|
}
|
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
void NetPlayClient::OnGameDigestAbort()
|
2021-09-15 10:26:01 -04:00
|
|
|
{
|
2022-07-27 18:43:16 -07:00
|
|
|
m_should_compute_game_digest = false;
|
|
|
|
m_dialog->AbortGameDigest();
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
2018-10-18 04:33:05 -04:00
|
|
|
void NetPlayClient::Send(const sf::Packet& packet, const u8 channel_id)
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2023-04-11 09:12:01 -04:00
|
|
|
Common::ENet::SendPacket(m_server, packet, channel_id);
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
|
|
|
|
2016-02-02 16:35:27 +01:00
|
|
|
void NetPlayClient::DisplayPlayersPing()
|
|
|
|
{
|
|
|
|
if (!g_ActiveConfig.bShowNetPlayPing)
|
|
|
|
return;
|
|
|
|
|
2019-10-20 07:35:11 -04:00
|
|
|
OSD::AddTypedMessage(OSD::MessageType::NetPlayPing, fmt::format("Ping: {}", GetPlayersMaxPing()),
|
|
|
|
OSD::Duration::SHORT, OSD::Color::CYAN);
|
2016-02-02 16:35:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
u32 NetPlayClient::GetPlayersMaxPing() const
|
|
|
|
{
|
|
|
|
return std::max_element(
|
|
|
|
m_players.begin(), m_players.end(),
|
|
|
|
[](const auto& a, const auto& b) { return a.second.ping < b.second.ping; })
|
|
|
|
->second.ping;
|
|
|
|
}
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
void NetPlayClient::Disconnect()
|
|
|
|
{
|
|
|
|
ENetEvent netEvent;
|
2015-02-14 19:51:08 -08:00
|
|
|
m_connecting = false;
|
2016-01-24 21:58:56 -05:00
|
|
|
m_connection_state = ConnectionState::Failure;
|
2015-02-14 19:51:08 -08:00
|
|
|
if (m_server)
|
|
|
|
enet_peer_disconnect(m_server, 0);
|
|
|
|
else
|
|
|
|
return;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
while (enet_host_service(m_client, &netEvent, 3000) > 0)
|
|
|
|
{
|
|
|
|
switch (netEvent.type)
|
|
|
|
{
|
|
|
|
case ENET_EVENT_TYPE_RECEIVE:
|
|
|
|
enet_packet_destroy(netEvent.packet);
|
|
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_DISCONNECT:
|
|
|
|
m_server = nullptr;
|
|
|
|
return;
|
2015-02-02 02:08:58 -08:00
|
|
|
default:
|
|
|
|
break;
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// didn't disconnect gracefully force disconnect
|
|
|
|
enet_peer_reset(m_server);
|
|
|
|
m_server = nullptr;
|
|
|
|
}
|
|
|
|
|
2018-10-18 04:33:05 -04:00
|
|
|
void NetPlayClient::SendAsync(sf::Packet&& packet, const u8 channel_id)
|
2015-03-09 17:31:13 +01:00
|
|
|
{
|
|
|
|
{
|
2020-12-29 18:31:29 -05:00
|
|
|
std::lock_guard lkq(m_crit.async_queue_write);
|
2018-10-18 04:33:05 -04:00
|
|
|
m_async_queue.Push(AsyncQueueEntry{std::move(packet), channel_id});
|
2015-03-09 17:31:13 +01:00
|
|
|
}
|
2023-04-11 09:12:01 -04:00
|
|
|
Common::ENet::WakeupThread(m_client);
|
2015-03-09 17:31:13 +01:00
|
|
|
}
|
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---NETPLAY--- thread
|
2011-01-31 08:19:27 +00:00
|
|
|
void NetPlayClient::ThreadFunc()
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2023-03-30 16:56:04 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "NetPlayClient starting.");
|
|
|
|
|
2017-10-28 01:42:25 +02:00
|
|
|
Common::QoSSession qos_session;
|
|
|
|
if (Config::Get(Config::NETPLAY_ENABLE_QOS))
|
|
|
|
{
|
|
|
|
qos_session = Common::QoSSession(m_server);
|
|
|
|
|
|
|
|
if (qos_session.Successful())
|
2019-06-16 23:45:37 -04:00
|
|
|
{
|
|
|
|
m_dialog->AppendChat(
|
|
|
|
Common::GetStringT("Quality of Service (QoS) was successfully enabled."));
|
|
|
|
}
|
2017-10-28 01:42:25 +02:00
|
|
|
else
|
2019-06-16 23:45:37 -04:00
|
|
|
{
|
|
|
|
m_dialog->AppendChat(Common::GetStringT("Quality of Service (QoS) couldn't be enabled."));
|
|
|
|
}
|
2017-10-28 01:42:25 +02:00
|
|
|
}
|
|
|
|
|
2016-08-05 16:04:39 +02:00
|
|
|
while (m_do_loop.IsSet())
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2015-02-02 01:27:06 -08:00
|
|
|
ENetEvent netEvent;
|
|
|
|
int net;
|
2015-03-09 17:31:13 +01:00
|
|
|
if (m_traversal_client)
|
|
|
|
m_traversal_client->HandleResends();
|
2015-03-14 15:19:18 +01:00
|
|
|
net = enet_host_service(m_client, &netEvent, 250);
|
2015-03-13 02:03:09 +01:00
|
|
|
while (!m_async_queue.Empty())
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2023-03-30 16:56:04 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Processing async queue event.");
|
2018-10-18 04:33:05 -04:00
|
|
|
{
|
|
|
|
auto& e = m_async_queue.Front();
|
|
|
|
Send(e.packet, e.channel_id);
|
|
|
|
}
|
2023-03-30 16:56:04 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Processing async queue event done.");
|
2015-03-13 02:03:09 +01:00
|
|
|
m_async_queue.Pop();
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
|
|
|
if (net > 0)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
|
|
|
sf::Packet rpac;
|
2015-02-02 01:27:06 -08:00
|
|
|
switch (netEvent.type)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2023-03-30 16:56:04 +02:00
|
|
|
case ENET_EVENT_TYPE_CONNECT:
|
|
|
|
INFO_LOG_FMT(NETPLAY, "enet_host_service: connect event");
|
|
|
|
break;
|
2015-02-02 01:27:06 -08:00
|
|
|
case ENET_EVENT_TYPE_RECEIVE:
|
2023-03-30 16:56:04 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "enet_host_service: receive event");
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
rpac.append(netEvent.packet->data, netEvent.packet->dataLength);
|
2010-05-05 04:44:19 +00:00
|
|
|
OnData(rpac);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
enet_packet_destroy(netEvent.packet);
|
|
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_DISCONNECT:
|
2023-03-30 16:56:04 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "enet_host_service: disconnect event");
|
|
|
|
|
2016-02-02 16:35:27 +01:00
|
|
|
m_dialog->OnConnectionLost();
|
|
|
|
|
2016-08-05 16:04:39 +02:00
|
|
|
if (m_is_running.IsSet())
|
2016-02-02 16:35:27 +01:00
|
|
|
StopGame();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
break;
|
2015-02-02 02:08:58 -08:00
|
|
|
default:
|
2023-03-30 16:56:04 +02:00
|
|
|
ERROR_LOG_FMT(NETPLAY, "enet_host_service: unknown event type: {}", int(netEvent.type));
|
2015-02-02 02:08:58 -08:00
|
|
|
break;
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
}
|
2023-03-30 16:56:04 +02:00
|
|
|
else if (net == 0)
|
|
|
|
{
|
|
|
|
INFO_LOG_FMT(NETPLAY, "enet_host_service: no event occurred");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ERROR_LOG_FMT(NETPLAY, "enet_host_service error: {}", net);
|
|
|
|
}
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-30 16:56:04 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "NetPlayClient shutting down.");
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
Disconnect();
|
2010-05-05 04:44:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-18 09:43:01 -04:00
|
|
|
// called from ---GUI--- thread
|
2015-08-16 00:58:15 -04:00
|
|
|
std::vector<const Player*> NetPlayClient::GetPlayers()
|
2013-08-18 09:43:01 -04:00
|
|
|
{
|
2020-12-29 18:31:29 -05:00
|
|
|
std::lock_guard lkp(m_crit.players);
|
2015-08-16 00:58:15 -04:00
|
|
|
std::vector<const Player*> players;
|
|
|
|
|
|
|
|
for (const auto& pair : m_players)
|
|
|
|
players.push_back(&pair.second);
|
|
|
|
|
|
|
|
return players;
|
2013-08-18 09:43:01 -04:00
|
|
|
}
|
|
|
|
|
2018-07-09 15:52:31 -04:00
|
|
|
const NetSettings& NetPlayClient::GetNetSettings() const
|
|
|
|
{
|
|
|
|
return m_net_settings;
|
|
|
|
}
|
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---GUI--- thread
|
|
|
|
void NetPlayClient::SendChatMessage(const std::string& msg)
|
|
|
|
{
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::ChatMessage;
|
2017-02-28 14:33:54 -08:00
|
|
|
packet << msg;
|
2016-01-24 22:10:38 -05:00
|
|
|
|
2017-02-28 14:33:54 -08:00
|
|
|
SendAsync(std::move(packet));
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---CPU--- thread
|
2018-07-09 16:45:52 -04:00
|
|
|
void NetPlayClient::AddPadStateToPacket(const int in_game_pad, const GCPadStatus& pad,
|
|
|
|
sf::Packet& packet)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2018-11-19 05:30:39 -05:00
|
|
|
packet << static_cast<PadIndex>(in_game_pad);
|
2021-07-04 13:33:58 +02:00
|
|
|
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;
|
|
|
|
}
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
2013-08-06 23:48:52 -04:00
|
|
|
// called from ---CPU--- thread
|
2022-09-23 03:21:52 +02:00
|
|
|
void NetPlayClient::AddWiimoteStateToPacket(int in_game_pad,
|
|
|
|
const WiimoteEmu::SerializedWiimoteState& state,
|
|
|
|
sf::Packet& packet)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2018-11-19 05:30:39 -05:00
|
|
|
packet << static_cast<PadIndex>(in_game_pad);
|
2022-09-23 03:21:52 +02:00
|
|
|
packet << state.length;
|
|
|
|
for (size_t i = 0; i < state.length; ++i)
|
|
|
|
packet << state.data[i];
|
2016-01-24 22:10:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---GUI--- thread
|
|
|
|
void NetPlayClient::SendStartGamePacket()
|
|
|
|
{
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::StartGame;
|
2017-02-28 14:33:54 -08:00
|
|
|
packet << m_current_game;
|
2016-01-24 22:10:38 -05:00
|
|
|
|
2017-02-28 14:33:54 -08:00
|
|
|
SendAsync(std::move(packet));
|
2016-01-24 22:10:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---GUI--- thread
|
|
|
|
void NetPlayClient::SendStopGamePacket()
|
|
|
|
{
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::StopGame;
|
2016-01-24 22:10:38 -05:00
|
|
|
|
2017-02-28 14:33:54 -08:00
|
|
|
SendAsync(std::move(packet));
|
2013-08-06 23:48:52 -04:00
|
|
|
}
|
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---GUI--- thread
|
|
|
|
bool NetPlayClient::StartGame(const std::string& path)
|
|
|
|
{
|
2020-12-29 18:31:29 -05:00
|
|
|
std::lock_guard lkg(m_crit.game);
|
2016-01-24 22:10:38 -05:00
|
|
|
SendStartGamePacket();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-08-05 16:04:39 +02:00
|
|
|
if (m_is_running.IsSet())
|
2013-08-05 04:56:30 -04:00
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Game is already running!");
|
2013-08-05 04:56:30 -04:00
|
|
|
return false;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-06-06 01:20:51 -04:00
|
|
|
m_timebase_frame = 0;
|
2019-04-02 08:08:27 -04:00
|
|
|
m_current_golfer = 1;
|
|
|
|
m_wait_on_input = false;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-08-05 16:04:39 +02:00
|
|
|
m_is_running.Set();
|
2013-08-05 04:56:30 -04:00
|
|
|
NetPlay_Enable(this);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-08-05 04:56:30 -04:00
|
|
|
ClearBuffers();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
m_first_pad_status_received.fill(false);
|
|
|
|
|
2013-09-03 15:50:41 -04:00
|
|
|
if (m_dialog->IsRecording())
|
|
|
|
{
|
|
|
|
if (Movie::IsReadOnly())
|
|
|
|
Movie::SetReadOnly(false);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2021-07-04 13:38:30 +02:00
|
|
|
Movie::ControllerTypeArray controllers{};
|
|
|
|
Movie::WiimoteEnabledArray wiimotes{};
|
2013-09-03 15:50:41 -04:00
|
|
|
for (unsigned int i = 0; i < 4; ++i)
|
|
|
|
{
|
2021-07-04 13:38:30 +02:00
|
|
|
if (m_pad_map[i] > 0 && m_gba_config[i].enabled)
|
|
|
|
controllers[i] = Movie::ControllerType::GBA;
|
|
|
|
else if (m_pad_map[i] > 0)
|
|
|
|
controllers[i] = Movie::ControllerType::GC;
|
|
|
|
else
|
|
|
|
controllers[i] = Movie::ControllerType::None;
|
|
|
|
wiimotes[i] = m_wiimote_map[i] > 0;
|
2013-09-03 15:50:41 -04:00
|
|
|
}
|
2021-07-04 13:38:30 +02:00
|
|
|
Movie::BeginRecordingInput(controllers, wiimotes);
|
2013-09-03 15:50:41 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-02-22 22:50:05 +01:00
|
|
|
for (unsigned int i = 0; i < 4; ++i)
|
2019-11-12 18:50:16 -06:00
|
|
|
{
|
2022-01-25 20:34:03 +01:00
|
|
|
Config::SetCurrent(Config::GetInfoForWiimoteSource(i),
|
|
|
|
m_wiimote_map[i] > 0 ? WiimoteSource::Emulated : WiimoteSource::None);
|
2019-11-12 18:50:16 -06:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-02-22 22:50:05 +01:00
|
|
|
// boot game
|
2021-11-20 21:03:34 +01:00
|
|
|
auto boot_session_data = std::make_unique<BootSessionData>();
|
2022-09-10 04:24:57 +02:00
|
|
|
|
|
|
|
INFO_LOG_FMT(NETPLAY,
|
|
|
|
"Setting Wii sync data: has FS {}, sync_titles = {:016x}, redirect folder = {}",
|
|
|
|
!!m_wii_sync_fs, fmt::join(m_wii_sync_titles, ", "), m_wii_sync_redirect_folder);
|
|
|
|
|
2022-01-16 21:30:53 -08:00
|
|
|
boot_session_data->SetWiiSyncData(std::move(m_wii_sync_fs), std::move(m_wii_sync_titles),
|
|
|
|
std::move(m_wii_sync_redirect_folder), [] {
|
|
|
|
// on emulation end clean up the Wii save sync directory --
|
|
|
|
// see OnSyncSaveDataWii()
|
|
|
|
const std::string wii_path = File::GetUserPath(D_USER_IDX) +
|
|
|
|
"Wii" GC_MEMCARD_NETPLAY DIR_SEP;
|
|
|
|
if (File::Exists(wii_path))
|
|
|
|
File::DeleteDirRecursively(wii_path);
|
|
|
|
const std::string redirect_path =
|
|
|
|
File::GetUserPath(D_USER_IDX) +
|
|
|
|
"Redirect" GC_MEMCARD_NETPLAY DIR_SEP;
|
|
|
|
if (File::Exists(redirect_path))
|
|
|
|
File::DeleteDirRecursively(redirect_path);
|
|
|
|
});
|
2022-09-11 03:55:30 +02:00
|
|
|
boot_session_data->SetNetplaySettings(std::make_unique<NetPlay::NetSettings>(m_net_settings));
|
|
|
|
|
2021-11-20 21:03:34 +01:00
|
|
|
m_dialog->BootGame(path, std::move(boot_session_data));
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-08-23 20:24:45 -04:00
|
|
|
UpdateDevices();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-04 17:01:50 -04:00
|
|
|
void NetPlayClient::SyncSaveDataResponse(const bool success)
|
|
|
|
{
|
2019-06-16 23:45:37 -04:00
|
|
|
m_dialog->AppendChat(success ? Common::GetStringT("Data received!") :
|
|
|
|
Common::GetStringT("Error processing data."));
|
2018-07-04 17:01:50 -04:00
|
|
|
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
if (++m_sync_save_data_success_count >= m_sync_save_data_count)
|
|
|
|
{
|
|
|
|
sf::Packet response_packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
response_packet << MessageID::SyncSaveData;
|
2021-09-22 14:49:41 -04:00
|
|
|
response_packet << SyncSaveDataID::Success;
|
2018-07-04 17:01:50 -04:00
|
|
|
|
|
|
|
Send(response_packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sf::Packet response_packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
response_packet << MessageID::SyncSaveData;
|
2021-09-22 14:49:41 -04:00
|
|
|
response_packet << SyncSaveDataID::Failure;
|
2018-07-04 17:01:50 -04:00
|
|
|
|
|
|
|
Send(response_packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-04 18:49:41 -04:00
|
|
|
void NetPlayClient::SyncCodeResponse(const bool success)
|
|
|
|
{
|
|
|
|
// If something failed, immediately report back that code sync failed
|
|
|
|
if (!success)
|
|
|
|
{
|
2019-06-16 23:45:37 -04:00
|
|
|
m_dialog->AppendChat(Common::GetStringT("Error processing codes."));
|
2018-10-04 18:49:41 -04:00
|
|
|
|
|
|
|
sf::Packet response_packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
response_packet << MessageID::SyncCodes;
|
2021-09-22 14:49:41 -04:00
|
|
|
response_packet << SyncCodeID::Failure;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2019-06-16 23:45:37 -04:00
|
|
|
m_dialog->AppendChat(Common::GetStringT("Codes received!"));
|
2018-10-04 18:49:41 -04:00
|
|
|
|
|
|
|
sf::Packet response_packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
response_packet << MessageID::SyncCodes;
|
2021-09-22 14:49:41 -04:00
|
|
|
response_packet << SyncCodeID::Success;
|
2018-10-04 18:49:41 -04:00
|
|
|
|
|
|
|
Send(response_packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---GUI--- thread
|
2010-11-16 01:55:29 +00:00
|
|
|
bool NetPlayClient::ChangeGame(const std::string&)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2013-08-05 05:05:06 -04:00
|
|
|
|
2013-08-23 20:24:45 -04:00
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
void NetPlayClient::UpdateDevices()
|
|
|
|
{
|
2016-01-26 00:21:13 +01:00
|
|
|
u8 local_pad = 0;
|
2016-05-05 15:32:54 +02:00
|
|
|
u8 pad = 0;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2023-03-12 14:20:32 +01:00
|
|
|
auto& si = Core::System::GetInstance().GetSerialInterface();
|
2016-01-26 00:21:13 +01:00
|
|
|
for (auto player_id : m_pad_map)
|
2013-08-23 20:24:45 -04:00
|
|
|
{
|
2021-07-04 13:33:58 +02:00
|
|
|
if (m_gba_config[pad].enabled && player_id > 0)
|
|
|
|
{
|
2023-03-12 14:20:32 +01:00
|
|
|
si.ChangeDevice(SerialInterface::SIDEVICE_GC_GBA_EMULATED, pad);
|
2021-07-04 13:33:58 +02:00
|
|
|
}
|
|
|
|
else if (player_id == m_local_player->pid)
|
2016-01-26 00:21:13 +01:00
|
|
|
{
|
2021-07-04 13:33:58 +02:00
|
|
|
// Use local controller types for local controllers if they are compatible
|
2022-01-06 08:08:02 +01:00
|
|
|
const SerialInterface::SIDevices si_device =
|
|
|
|
Config::Get(Config::GetInfoForSIDevice(local_pad));
|
|
|
|
if (SerialInterface::SIDevice_IsGCController(si_device))
|
2016-01-28 20:24:18 +01:00
|
|
|
{
|
2023-03-12 14:20:32 +01:00
|
|
|
si.ChangeDevice(si_device, pad);
|
2019-04-13 06:31:10 -04:00
|
|
|
|
2022-01-06 08:08:02 +01:00
|
|
|
if (si_device == SerialInterface::SIDEVICE_WIIU_ADAPTER)
|
2019-04-13 06:31:10 -04:00
|
|
|
{
|
|
|
|
GCAdapter::ResetDeviceType(local_pad);
|
|
|
|
}
|
2016-01-28 20:24:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-03-12 14:20:32 +01:00
|
|
|
si.ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad);
|
2016-01-28 20:24:18 +01:00
|
|
|
}
|
2016-01-26 00:21:13 +01:00
|
|
|
local_pad++;
|
|
|
|
}
|
2016-05-05 15:32:54 +02:00
|
|
|
else if (player_id > 0)
|
2016-01-26 00:21:13 +01:00
|
|
|
{
|
2023-03-12 14:20:32 +01:00
|
|
|
si.ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad);
|
2016-01-26 00:21:13 +01:00
|
|
|
}
|
2016-05-05 15:32:54 +02:00
|
|
|
else
|
|
|
|
{
|
2023-03-12 14:20:32 +01:00
|
|
|
si.ChangeDevice(SerialInterface::SIDEVICE_NONE, pad);
|
2016-05-05 15:32:54 +02:00
|
|
|
}
|
|
|
|
pad++;
|
2013-08-23 20:24:45 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
void NetPlayClient::ClearBuffers()
|
|
|
|
{
|
|
|
|
// clear pad buffers, Clear method isn't thread safe
|
2015-02-02 01:27:06 -08:00
|
|
|
for (unsigned int i = 0; i < 4; ++i)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
|
|
|
while (m_pad_buffer[i].Size())
|
|
|
|
m_pad_buffer[i].Pop();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
while (m_wiimote_buffer[i].Size())
|
|
|
|
m_wiimote_buffer[i].Pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-02 01:56:53 -08:00
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
void NetPlayClient::OnTraversalStateChanged()
|
|
|
|
{
|
2018-04-16 16:02:21 -04:00
|
|
|
const TraversalClient::State state = m_traversal_client->GetState();
|
|
|
|
|
2016-01-24 21:58:56 -05:00
|
|
|
if (m_connection_state == ConnectionState::WaitingForTraversalClientConnection &&
|
2021-01-19 14:00:01 -05:00
|
|
|
state == TraversalClient::State::Connected)
|
2015-02-02 01:56:53 -08:00
|
|
|
{
|
2016-01-24 21:58:56 -05:00
|
|
|
m_connection_state = ConnectionState::WaitingForTraversalClientConnectReady;
|
2015-02-02 01:56:53 -08:00
|
|
|
m_traversal_client->ConnectToClient(m_host_spec);
|
|
|
|
}
|
2021-01-19 14:00:01 -05:00
|
|
|
else if (m_connection_state != ConnectionState::Failure &&
|
|
|
|
state == TraversalClient::State::Failure)
|
2015-02-02 01:56:53 -08:00
|
|
|
{
|
|
|
|
Disconnect();
|
2018-04-16 16:02:21 -04:00
|
|
|
m_dialog->OnTraversalError(m_traversal_client->GetFailureReason());
|
2015-02-02 01:56:53 -08:00
|
|
|
}
|
2018-07-20 18:27:43 -04:00
|
|
|
m_dialog->OnTraversalStateChanged(state);
|
2015-02-02 01:56:53 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
void NetPlayClient::OnConnectReady(ENetAddress addr)
|
|
|
|
{
|
2016-01-24 21:58:56 -05:00
|
|
|
if (m_connection_state == ConnectionState::WaitingForTraversalClientConnectReady)
|
2015-02-02 01:56:53 -08:00
|
|
|
{
|
2016-01-24 21:58:56 -05:00
|
|
|
m_connection_state = ConnectionState::Connecting;
|
2018-10-18 04:33:05 -04:00
|
|
|
enet_host_connect(m_client, &addr, CHANNEL_COUNT, 0);
|
2015-02-02 01:56:53 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---NETPLAY--- thread
|
2021-01-19 14:05:16 -05:00
|
|
|
void NetPlayClient::OnConnectFailed(TraversalConnectFailedReason reason)
|
2015-02-02 01:56:53 -08:00
|
|
|
{
|
|
|
|
m_connecting = false;
|
2016-01-24 21:58:56 -05:00
|
|
|
m_connection_state = ConnectionState::Failure;
|
2015-03-01 16:17:32 +01:00
|
|
|
switch (reason)
|
2015-02-02 01:56:53 -08:00
|
|
|
{
|
2021-01-19 14:05:16 -05:00
|
|
|
case TraversalConnectFailedReason::ClientDidntRespond:
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Traversal server timed out connecting to the host");
|
2015-02-02 01:56:53 -08:00
|
|
|
break;
|
2021-01-19 14:05:16 -05:00
|
|
|
case TraversalConnectFailedReason::ClientFailure:
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Server rejected traversal attempt");
|
2015-02-02 01:56:53 -08:00
|
|
|
break;
|
2021-01-19 14:05:16 -05:00
|
|
|
case TraversalConnectFailedReason::NoSuchClient:
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Invalid host");
|
2015-02-02 01:56:53 -08:00
|
|
|
break;
|
|
|
|
default:
|
2022-01-12 16:47:26 -08:00
|
|
|
PanicAlertFmtT("Unknown error {0:x}", static_cast<int>(reason));
|
2015-02-02 01:56:53 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
// called from ---CPU--- thread
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
bool NetPlayClient::GetNetPads(const int pad_nb, const bool batching, GCPadStatus* pad_status)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2013-08-18 09:43:01 -04:00
|
|
|
// The interface for this is extremely silly.
|
|
|
|
//
|
2014-11-13 21:28:27 -05:00
|
|
|
// Imagine a physical device that links three GameCubes together
|
2014-06-07 11:30:39 +09:00
|
|
|
// and emulates NetPlay that way. Which GameCube controls which
|
2013-08-18 09:43:01 -04:00
|
|
|
// in-game controllers can be configured on the device (m_pad_map)
|
2014-06-07 11:30:39 +09:00
|
|
|
// but which sockets on each individual GameCube should be used
|
2013-08-18 09:43:01 -04:00
|
|
|
// to control which players? The solution that Dolphin uses is
|
|
|
|
// that we hardcode the knowledge that they go in order, so if
|
2014-11-13 21:28:27 -05:00
|
|
|
// you have a 3P game with three GameCubes, then every single
|
2013-08-18 09:43:01 -04:00
|
|
|
// controller should be plugged into slot 1.
|
|
|
|
//
|
2014-11-13 21:28:27 -05:00
|
|
|
// If you have a 4P game, then one of the GameCubes will have
|
2013-08-18 09:43:01 -04:00
|
|
|
// a controller plugged into slot 1, and another in slot 2.
|
|
|
|
//
|
2016-05-05 15:32:54 +02:00
|
|
|
// The slot number is the "local" pad number, and what player
|
2013-08-18 09:43:01 -04:00
|
|
|
// it actually means is the "in-game" pad number.
|
2016-06-24 10:43:46 +02:00
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
// When the 1st in-game pad is polled and batching is set, the
|
|
|
|
// others will be polled as well. To reduce latency, we poll all
|
|
|
|
// local controllers at once and then send the status to the other
|
2016-05-08 15:29:01 +02:00
|
|
|
// clients.
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
//
|
|
|
|
// Batching is enabled when polled from VI. If batching is not
|
|
|
|
// enabled, the poll is probably from MMIO, which can poll any
|
|
|
|
// specific pad arbitrarily. In this case, we poll just that pad
|
|
|
|
// and send it.
|
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
// When here when told to so we don't deadlock in certain situations
|
|
|
|
while (m_wait_on_input)
|
|
|
|
{
|
|
|
|
if (!m_is_running.IsSet())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_wait_on_input_received)
|
|
|
|
{
|
|
|
|
// Tell the server we've acknowledged the message
|
|
|
|
sf::Packet spac;
|
2021-09-22 14:17:42 -04:00
|
|
|
spac << MessageID::GolfPrepare;
|
2019-04-02 08:08:27 -04:00
|
|
|
Send(spac);
|
|
|
|
|
|
|
|
m_wait_on_input_received = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_wait_on_input_event.Wait();
|
|
|
|
}
|
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
if (IsFirstInGamePad(pad_nb) && batching)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2018-07-09 16:45:52 -04:00
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::PadData;
|
2018-07-09 16:45:52 -04:00
|
|
|
|
|
|
|
bool send_packet = false;
|
2016-10-07 07:49:32 -04:00
|
|
|
const int num_local_pads = NumLocalPads();
|
|
|
|
for (int local_pad = 0; local_pad < num_local_pads; local_pad++)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
send_packet = PollLocalPad(local_pad, packet) || send_packet;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
if (send_packet)
|
|
|
|
SendAsync(std::move(packet));
|
2016-06-24 10:43:46 +02:00
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
if (m_host_input_authority)
|
|
|
|
SendPadHostPoll(-1);
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
if (!batching)
|
|
|
|
{
|
2021-09-22 14:17:42 -04:00
|
|
|
const int local_pad = InGamePadToLocalPad(pad_nb);
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
if (local_pad < 4)
|
|
|
|
{
|
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::PadData;
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
if (PollLocalPad(local_pad, packet))
|
|
|
|
SendAsync(std::move(packet));
|
2013-08-05 05:05:06 -04:00
|
|
|
}
|
2018-07-09 16:45:52 -04:00
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
if (m_host_input_authority)
|
|
|
|
SendPadHostPoll(pad_nb);
|
|
|
|
}
|
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
if (m_host_input_authority)
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
{
|
2019-04-02 08:08:27 -04:00
|
|
|
if (m_local_player->pid != m_current_golfer)
|
|
|
|
{
|
|
|
|
// CoreTiming acts funny and causes what looks like frame skip if
|
|
|
|
// we toggle the emulation speed too quickly, so to prevent this
|
|
|
|
// we wait until the buffer has been over for at least 1 second.
|
2018-08-26 22:28:23 -04:00
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
const bool buffer_over_target = m_pad_buffer[pad_nb].Size() > m_target_buffer_size + 1;
|
|
|
|
if (!buffer_over_target)
|
|
|
|
m_buffer_under_target_last = std::chrono::steady_clock::now();
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
std::chrono::duration<double> time_diff =
|
|
|
|
std::chrono::steady_clock::now() - m_buffer_under_target_last;
|
|
|
|
if (time_diff.count() >= 1.0 || !buffer_over_target)
|
|
|
|
{
|
|
|
|
// run fast if the buffer is overfilled, otherwise run normal speed
|
2022-01-05 23:22:07 +01:00
|
|
|
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, buffer_over_target ? 0.0f : 1.0f);
|
2019-04-02 08:08:27 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
{
|
2019-04-02 08:08:27 -04:00
|
|
|
// Set normal speed when we're the host, otherwise it can get stuck at unlimited
|
2022-01-05 23:22:07 +01:00
|
|
|
Config::SetCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
}
|
2013-08-05 05:05:06 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-05-08 15:29:01 +02:00
|
|
|
// Now, we either use the data pushed earlier, or wait for the
|
2016-05-05 15:32:54 +02:00
|
|
|
// other clients to send it to us
|
2016-07-25 00:40:15 +02:00
|
|
|
while (m_pad_buffer[pad_nb].Size() == 0)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2016-08-05 16:04:39 +02:00
|
|
|
if (!m_is_running.IsSet())
|
2016-07-25 00:40:15 +02:00
|
|
|
{
|
2013-08-05 05:05:06 -04:00
|
|
|
return false;
|
2016-07-25 00:40:15 +02:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-07-25 00:40:15 +02:00
|
|
|
m_gc_pad_event.Wait();
|
2013-08-05 05:05:06 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-07-25 00:40:15 +02:00
|
|
|
m_pad_buffer[pad_nb].Pop(*pad_status);
|
|
|
|
|
2013-09-05 16:09:56 -04:00
|
|
|
if (Movie::IsRecordingInput())
|
|
|
|
{
|
2014-10-16 02:36:39 -04:00
|
|
|
Movie::RecordInput(pad_status, pad_nb);
|
2013-09-05 16:09:56 -04:00
|
|
|
Movie::InputUpdate();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-10-16 02:36:39 -04:00
|
|
|
Movie::CheckPadStatus(pad_status, pad_nb);
|
2013-09-05 16:09:56 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-07-14 18:55:17 -04:00
|
|
|
u64 NetPlayClient::GetInitialRTCValue() const
|
|
|
|
{
|
|
|
|
return m_initial_rtc;
|
|
|
|
}
|
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
// called from ---CPU--- thread
|
2022-09-25 04:02:53 +02:00
|
|
|
bool NetPlayClient::WiimoteUpdate(const std::span<WiimoteDataBatchEntry>& entries)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2022-09-25 04:02:53 +02:00
|
|
|
for (const WiimoteDataBatchEntry& entry : entries)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2022-09-25 04:02:53 +02:00
|
|
|
const int local_wiimote = InGameWiimoteToLocalWiimote(entry.wiimote);
|
|
|
|
DEBUG_LOG_FMT(NETPLAY,
|
|
|
|
"Entering WiimoteUpdate() with wiimote {}, local_wiimote {}, state [{:02x}]",
|
|
|
|
entry.wiimote, local_wiimote,
|
|
|
|
fmt::join(std::span(entry.state->data.data(), entry.state->length), ", "));
|
2022-09-23 03:21:52 +02:00
|
|
|
if (local_wiimote < 4)
|
2013-08-22 11:37:38 -04:00
|
|
|
{
|
2022-09-23 03:21:52 +02:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << MessageID::WiimoteData;
|
2022-09-25 04:02:53 +02:00
|
|
|
if (AddLocalWiimoteToBuffer(local_wiimote, *entry.state, packet))
|
2022-09-23 03:21:52 +02:00
|
|
|
SendAsync(std::move(packet));
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
2022-09-25 04:02:53 +02:00
|
|
|
// Now, we either use the data pushed earlier, or wait for the
|
|
|
|
// other clients to send it to us
|
|
|
|
while (m_wiimote_buffer[entry.wiimote].Size() == 0)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2022-09-25 04:02:53 +02:00
|
|
|
if (!m_is_running.IsSet())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_wii_pad_event.Wait();
|
2013-08-06 23:48:52 -04:00
|
|
|
}
|
2022-09-23 03:21:52 +02:00
|
|
|
|
2022-09-25 04:02:53 +02:00
|
|
|
m_wiimote_buffer[entry.wiimote].Pop(*entry.state);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-09-25 04:02:53 +02:00
|
|
|
DEBUG_LOG_FMT(NETPLAY, "Exiting WiimoteUpdate() with wiimote {}, state [{:02x}]", entry.wiimote,
|
|
|
|
fmt::join(std::span(entry.state->data.data(), entry.state->length), ", "));
|
|
|
|
}
|
2022-09-23 03:21:52 +02:00
|
|
|
|
2013-08-06 23:48:52 -04:00
|
|
|
return true;
|
2013-08-05 05:05:06 -04:00
|
|
|
}
|
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
bool NetPlayClient::PollLocalPad(const int local_pad, sf::Packet& packet)
|
|
|
|
{
|
2021-07-04 13:33:58 +02:00
|
|
|
const int ingame_pad = LocalPadToInGamePad(local_pad);
|
|
|
|
bool data_added = false;
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
GCPadStatus pad_status;
|
|
|
|
|
2021-07-04 13:33:58 +02:00
|
|
|
if (m_gba_config[ingame_pad].enabled)
|
|
|
|
{
|
|
|
|
pad_status = Pad::GetGBAStatus(local_pad);
|
|
|
|
}
|
2022-01-06 08:08:02 +01:00
|
|
|
else if (Config::Get(Config::GetInfoForSIDevice(local_pad)) ==
|
|
|
|
SerialInterface::SIDEVICE_WIIU_ADAPTER)
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
{
|
|
|
|
pad_status = GCAdapter::Input(local_pad);
|
2021-07-04 13:33:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
pad_status = Pad::GetStatus(local_pad);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_host_input_authority)
|
|
|
|
{
|
2019-04-02 08:08:27 -04:00
|
|
|
if (m_local_player->pid != m_current_golfer)
|
2019-04-01 22:36:48 -04:00
|
|
|
{
|
|
|
|
// add to packet
|
|
|
|
AddPadStateToPacket(ingame_pad, pad_status, packet);
|
|
|
|
data_added = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// set locally
|
|
|
|
m_last_pad_status[ingame_pad] = pad_status;
|
|
|
|
m_first_pad_status_received[ingame_pad] = true;
|
|
|
|
}
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// adjust the buffer either up or down
|
|
|
|
// inserting multiple padstates or dropping states
|
|
|
|
while (m_pad_buffer[ingame_pad].Size() <= m_target_buffer_size)
|
|
|
|
{
|
|
|
|
// add to buffer
|
|
|
|
m_pad_buffer[ingame_pad].Push(pad_status);
|
|
|
|
|
|
|
|
// add to packet
|
|
|
|
AddPadStateToPacket(ingame_pad, pad_status, packet);
|
|
|
|
data_added = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return data_added;
|
|
|
|
}
|
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
bool NetPlayClient::AddLocalWiimoteToBuffer(const int local_wiimote,
|
|
|
|
const WiimoteEmu::SerializedWiimoteState& state,
|
|
|
|
sf::Packet& packet)
|
|
|
|
{
|
|
|
|
const int ingame_pad = LocalWiimoteToInGameWiimote(local_wiimote);
|
|
|
|
bool data_added = false;
|
|
|
|
|
|
|
|
// adjust the buffer either up or down
|
|
|
|
// inserting multiple padstates or dropping states
|
|
|
|
while (m_wiimote_buffer[ingame_pad].Size() <= m_target_buffer_size)
|
|
|
|
{
|
|
|
|
// add to buffer
|
|
|
|
m_wiimote_buffer[ingame_pad].Push(state);
|
|
|
|
|
|
|
|
// add to packet
|
|
|
|
AddWiimoteStateToPacket(ingame_pad, state, packet);
|
|
|
|
data_added = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data_added;
|
|
|
|
}
|
|
|
|
|
2018-11-19 05:30:39 -05:00
|
|
|
void NetPlayClient::SendPadHostPoll(const PadIndex pad_num)
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
{
|
2020-02-06 06:39:17 -05:00
|
|
|
// Here we handle polling for the Host Input Authority and Golf modes. Pad data is "polled" from
|
|
|
|
// the most recent data received for the given pad. Passing pad_num < 0 will poll all assigned
|
|
|
|
// pads (used for batched polls), while 0..3 will poll the respective pad (used for MMIO polls).
|
|
|
|
// See GetNetPads for more details.
|
|
|
|
//
|
|
|
|
// If the local buffer is non-empty, we skip actually buffering and sending new pad data, this way
|
|
|
|
// don't end up with permanent local latency. It does create a period of time where no inputs are
|
|
|
|
// accepted, but under typical circumstances this is not noticeable.
|
|
|
|
//
|
|
|
|
// Additionally, we wait until some actual pad data has been received before buffering and sending
|
|
|
|
// it, otherwise controllers get calibrated wrongly with the default values of GCPadStatus.
|
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
if (m_local_player->pid != m_current_golfer)
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
return;
|
|
|
|
|
2019-04-01 22:36:48 -04:00
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::PadHostData;
|
2019-04-01 22:36:48 -04:00
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
if (pad_num < 0)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < m_pad_map.size(); i++)
|
|
|
|
{
|
|
|
|
if (m_pad_map[i] <= 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
while (!m_first_pad_status_received[i])
|
|
|
|
{
|
|
|
|
if (!m_is_running.IsSet())
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_first_pad_status_received_event.Wait();
|
|
|
|
}
|
|
|
|
}
|
2019-04-01 22:36:48 -04:00
|
|
|
|
|
|
|
for (size_t i = 0; i < m_pad_map.size(); i++)
|
|
|
|
{
|
2019-04-02 08:08:27 -04:00
|
|
|
if (m_pad_map[i] == 0 || m_pad_buffer[i].Size() > 0)
|
2019-04-01 22:36:48 -04:00
|
|
|
continue;
|
|
|
|
|
|
|
|
const GCPadStatus& pad_status = m_last_pad_status[i];
|
|
|
|
m_pad_buffer[i].Push(pad_status);
|
|
|
|
AddPadStateToPacket(static_cast<int>(i), pad_status, packet);
|
|
|
|
}
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
}
|
2018-11-19 05:30:39 -05:00
|
|
|
else if (m_pad_map[pad_num] != 0)
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
{
|
|
|
|
while (!m_first_pad_status_received[pad_num])
|
|
|
|
{
|
|
|
|
if (!m_is_running.IsSet())
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_first_pad_status_received_event.Wait();
|
|
|
|
}
|
2019-04-01 22:36:48 -04:00
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
if (m_pad_buffer[pad_num].Size() == 0)
|
|
|
|
{
|
|
|
|
const GCPadStatus& pad_status = m_last_pad_status[pad_num];
|
|
|
|
m_pad_buffer[pad_num].Push(pad_status);
|
|
|
|
AddPadStateToPacket(pad_num, pad_status, packet);
|
|
|
|
}
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
SendAsync(std::move(packet));
|
|
|
|
}
|
|
|
|
|
2022-09-13 01:22:51 +02:00
|
|
|
void NetPlayClient::InvokeStop()
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2016-08-05 16:04:39 +02:00
|
|
|
m_is_running.Clear();
|
2016-07-25 00:40:15 +02:00
|
|
|
|
|
|
|
// stop waiting for input
|
|
|
|
m_gc_pad_event.Set();
|
|
|
|
m_wii_pad_event.Set();
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
m_first_pad_status_received_event.Set();
|
2019-04-02 08:08:27 -04:00
|
|
|
m_wait_on_input_event.Set();
|
2022-09-13 01:22:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---GUI--- thread and ---NETPLAY--- thread (client side)
|
|
|
|
bool NetPlayClient::StopGame()
|
|
|
|
{
|
|
|
|
InvokeStop();
|
2016-07-25 00:40:15 +02:00
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
NetPlay_Disable();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
// stop game
|
|
|
|
m_dialog->StopGame();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-09 17:31:13 +01:00
|
|
|
// called from ---GUI--- thread
|
2013-09-02 21:54:28 -04:00
|
|
|
void NetPlayClient::Stop()
|
|
|
|
{
|
2016-08-05 16:04:39 +02:00
|
|
|
if (!m_is_running.IsSet())
|
2013-09-02 23:33:53 -04:00
|
|
|
return;
|
2015-05-21 19:52:26 -04:00
|
|
|
|
2022-09-13 01:22:51 +02:00
|
|
|
InvokeStop();
|
2016-07-25 00:40:15 +02:00
|
|
|
|
2018-07-04 01:02:13 +02:00
|
|
|
// Tell the server to stop if we have a pad mapped in game.
|
|
|
|
if (LocalPlayerHasControllerMapped())
|
|
|
|
SendStopGamePacket();
|
|
|
|
else
|
|
|
|
StopGame();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::RequestStopGame()
|
|
|
|
{
|
2016-01-24 22:10:38 -05:00
|
|
|
// Tell the server to stop if we have a pad mapped in game.
|
2016-01-24 22:46:37 -05:00
|
|
|
if (LocalPlayerHasControllerMapped())
|
2016-01-24 22:10:38 -05:00
|
|
|
SendStopGamePacket();
|
2013-09-02 21:54:28 -04:00
|
|
|
}
|
|
|
|
|
2018-11-10 22:37:49 -05:00
|
|
|
void NetPlayClient::SendPowerButtonEvent()
|
|
|
|
{
|
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::PowerButton;
|
2018-11-10 22:37:49 -05:00
|
|
|
SendAsync(std::move(packet));
|
|
|
|
}
|
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
void NetPlayClient::RequestGolfControl(const PlayerId pid)
|
|
|
|
{
|
2022-09-19 01:18:26 +02:00
|
|
|
if (!m_host_input_authority || !m_net_settings.golf_mode)
|
2019-04-02 08:08:27 -04:00
|
|
|
return;
|
|
|
|
|
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::GolfRequest;
|
2019-04-02 08:08:27 -04:00
|
|
|
packet << pid;
|
|
|
|
SendAsync(std::move(packet));
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlayClient::RequestGolfControl()
|
|
|
|
{
|
|
|
|
RequestGolfControl(m_local_player->pid);
|
|
|
|
}
|
|
|
|
|
2016-01-24 22:46:37 -05:00
|
|
|
// called from ---GUI--- thread
|
2019-04-02 17:13:42 -04:00
|
|
|
std::string NetPlayClient::GetCurrentGolfer()
|
2016-01-24 22:46:37 -05:00
|
|
|
{
|
2020-12-29 18:31:29 -05:00
|
|
|
std::lock_guard lkp(m_crit.players);
|
2019-04-02 17:13:42 -04:00
|
|
|
if (m_players.count(m_current_golfer))
|
|
|
|
return m_players[m_current_golfer].name;
|
|
|
|
return "";
|
|
|
|
}
|
2016-01-24 22:46:37 -05:00
|
|
|
|
2019-04-02 17:13:42 -04:00
|
|
|
// called from ---GUI--- thread
|
|
|
|
bool NetPlayClient::LocalPlayerHasControllerMapped() const
|
|
|
|
{
|
|
|
|
return PlayerHasControllerMapped(m_local_player->pid);
|
2016-01-24 22:46:37 -05:00
|
|
|
}
|
|
|
|
|
2016-10-07 07:49:32 -04:00
|
|
|
bool NetPlayClient::IsFirstInGamePad(int ingame_pad) const
|
2016-05-08 15:29:01 +02:00
|
|
|
{
|
|
|
|
return std::none_of(m_pad_map.begin(), m_pad_map.begin() + ingame_pad,
|
|
|
|
[](auto mapping) { return mapping > 0; });
|
|
|
|
}
|
|
|
|
|
2022-09-25 04:22:30 +02:00
|
|
|
static int CountLocalPads(const PadMappingArray& pad_map, const PlayerId& local_player_pid)
|
|
|
|
{
|
|
|
|
return static_cast<int>(
|
|
|
|
std::count_if(pad_map.begin(), pad_map.end(), [&local_player_pid](const auto& mapping) {
|
|
|
|
return mapping == local_player_pid;
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2016-10-07 07:49:32 -04:00
|
|
|
int NetPlayClient::NumLocalPads() const
|
2016-05-08 15:29:01 +02:00
|
|
|
{
|
2022-09-25 04:22:30 +02:00
|
|
|
return CountLocalPads(m_pad_map, m_local_player->pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
int NetPlayClient::NumLocalWiimotes() const
|
|
|
|
{
|
|
|
|
return CountLocalPads(m_wiimote_map, m_local_player->pid);
|
2016-05-08 15:29:01 +02:00
|
|
|
}
|
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
static int InGameToLocal(int ingame_pad, const PadMappingArray& pad_map, PlayerId local_player_pid)
|
2013-09-09 03:09:45 -04:00
|
|
|
{
|
|
|
|
// not our pad
|
2022-09-23 03:21:52 +02:00
|
|
|
if (pad_map[ingame_pad] != local_player_pid)
|
2013-09-09 03:09:45 -04:00
|
|
|
return 4;
|
|
|
|
|
|
|
|
int local_pad = 0;
|
|
|
|
int pad = 0;
|
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
for (; pad < ingame_pad; ++pad)
|
2013-09-09 03:09:45 -04:00
|
|
|
{
|
2022-09-23 03:21:52 +02:00
|
|
|
if (pad_map[pad] == local_player_pid)
|
2013-09-09 03:09:45 -04:00
|
|
|
local_pad++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return local_pad;
|
|
|
|
}
|
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
static int LocalToInGame(int local_pad, const PadMappingArray& pad_map, PlayerId local_player_pid)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2013-08-18 09:43:01 -04:00
|
|
|
// Figure out which in-game pad maps to which local pad.
|
|
|
|
// The logic we have here is that the local slots always
|
|
|
|
// go in order.
|
|
|
|
int local_pad_count = -1;
|
|
|
|
int ingame_pad = 0;
|
|
|
|
for (; ingame_pad < 4; ingame_pad++)
|
|
|
|
{
|
2022-09-23 03:21:52 +02:00
|
|
|
if (pad_map[ingame_pad] == local_player_pid)
|
2013-08-18 09:43:01 -04:00
|
|
|
local_pad_count++;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-09-09 03:09:45 -04:00
|
|
|
if (local_pad_count == local_pad)
|
2013-08-05 05:05:06 -04:00
|
|
|
break;
|
2013-08-18 09:43:01 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-08-18 09:43:01 -04:00
|
|
|
return ingame_pad;
|
2013-08-05 05:05:06 -04:00
|
|
|
}
|
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
int NetPlayClient::InGamePadToLocalPad(int ingame_pad) const
|
|
|
|
{
|
|
|
|
return InGameToLocal(ingame_pad, m_pad_map, m_local_player->pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
int NetPlayClient::LocalPadToInGamePad(int local_pad) const
|
|
|
|
{
|
|
|
|
return LocalToInGame(local_pad, m_pad_map, m_local_player->pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
int NetPlayClient::InGameWiimoteToLocalWiimote(int ingame_wiimote) const
|
|
|
|
{
|
|
|
|
return InGameToLocal(ingame_wiimote, m_wiimote_map, m_local_player->pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
int NetPlayClient::LocalWiimoteToInGameWiimote(int local_wiimote) const
|
|
|
|
{
|
|
|
|
return LocalToInGame(local_wiimote, m_wiimote_map, m_local_player->pid);
|
|
|
|
}
|
|
|
|
|
2019-04-02 17:13:42 -04:00
|
|
|
bool NetPlayClient::PlayerHasControllerMapped(const PlayerId pid) const
|
|
|
|
{
|
|
|
|
const auto mapping_matches_player_id = [pid](const PlayerId& mapping) { return mapping == pid; };
|
|
|
|
|
|
|
|
return std::any_of(m_pad_map.begin(), m_pad_map.end(), mapping_matches_player_id) ||
|
|
|
|
std::any_of(m_wiimote_map.begin(), m_wiimote_map.end(), mapping_matches_player_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool NetPlayClient::IsLocalPlayer(const PlayerId pid) const
|
|
|
|
{
|
|
|
|
return pid == m_local_player->pid;
|
|
|
|
}
|
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
const PlayerId& NetPlayClient::GetLocalPlayerId() const
|
|
|
|
{
|
|
|
|
return m_local_player->pid;
|
|
|
|
}
|
|
|
|
|
2021-07-04 13:33:58 +02:00
|
|
|
void NetPlayClient::SendGameStatus()
|
|
|
|
{
|
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::GameStatus;
|
2021-07-04 13:33:58 +02:00
|
|
|
|
|
|
|
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 &&
|
2022-09-19 01:18:26 +02:00
|
|
|
m_net_settings.gba_rom_paths[i].empty())
|
2021-07-04 13:33:58 +02:00
|
|
|
{
|
|
|
|
result = SyncIdentifierComparison::DifferentGame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
packet << static_cast<u32>(result);
|
|
|
|
Send(packet);
|
|
|
|
}
|
|
|
|
|
2015-03-08 06:50:47 -04:00
|
|
|
void NetPlayClient::SendTimeBase()
|
|
|
|
{
|
2020-12-29 16:01:12 -05:00
|
|
|
std::lock_guard lk(crit_netplay_client);
|
2015-03-08 06:50:47 -04:00
|
|
|
|
2018-07-08 01:51:30 -04:00
|
|
|
if (netplay_client->m_timebase_frame % 60 == 0)
|
|
|
|
{
|
2018-08-27 17:08:41 -04:00
|
|
|
const sf::Uint64 timebase = SystemTimers::GetFakeTimeBase();
|
2015-03-08 06:50:47 -04:00
|
|
|
|
2018-07-08 01:51:30 -04:00
|
|
|
sf::Packet packet;
|
2021-09-22 14:17:42 -04:00
|
|
|
packet << MessageID::TimeBase;
|
2018-08-27 17:08:41 -04:00
|
|
|
packet << timebase;
|
2018-07-08 01:51:30 -04:00
|
|
|
packet << netplay_client->m_timebase_frame;
|
|
|
|
|
|
|
|
netplay_client->SendAsync(std::move(packet));
|
|
|
|
}
|
2016-01-24 22:10:38 -05:00
|
|
|
|
2018-07-08 01:51:30 -04:00
|
|
|
netplay_client->m_timebase_frame++;
|
2015-03-08 06:50:47 -04:00
|
|
|
}
|
|
|
|
|
2016-07-10 10:13:34 +02:00
|
|
|
bool NetPlayClient::DoAllPlayersHaveGame()
|
|
|
|
{
|
2020-12-29 18:31:29 -05:00
|
|
|
std::lock_guard lkp(m_crit.players);
|
2016-07-10 10:13:34 +02:00
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
return std::all_of(std::begin(m_players), std::end(m_players), [](auto entry) {
|
|
|
|
return entry.second.game_status == SyncIdentifierComparison::SameGame;
|
|
|
|
});
|
2016-07-10 10:13:34 +02:00
|
|
|
}
|
|
|
|
|
2022-07-27 18:47:52 -07:00
|
|
|
static std::string SHA1Sum(const std::string& file_path, std::function<bool(int)> report_progress)
|
2021-12-10 15:21:46 -08:00
|
|
|
{
|
|
|
|
std::vector<u8> data(8 * 1024 * 1024);
|
|
|
|
u64 read_offset = 0;
|
|
|
|
|
|
|
|
std::unique_ptr<DiscIO::BlobReader> file(DiscIO::CreateBlobReader(file_path));
|
|
|
|
u64 game_size = file->GetDataSize();
|
|
|
|
|
2022-07-27 18:47:52 -07:00
|
|
|
auto ctx = Common::SHA1::CreateContext();
|
2021-12-10 15:21:46 -08:00
|
|
|
|
|
|
|
while (read_offset < game_size)
|
|
|
|
{
|
|
|
|
size_t read_size = std::min(static_cast<u64>(data.size()), game_size - read_offset);
|
|
|
|
if (!file->Read(read_offset, read_size, data.data()))
|
2021-12-18 12:46:47 -08:00
|
|
|
return "";
|
2021-12-10 15:21:46 -08:00
|
|
|
|
2022-07-27 18:47:52 -07:00
|
|
|
ctx->Update(data.data(), read_size);
|
2021-12-10 15:21:46 -08:00
|
|
|
read_offset += read_size;
|
|
|
|
|
|
|
|
int progress =
|
|
|
|
static_cast<int>(static_cast<float>(read_offset) / static_cast<float>(game_size) * 100);
|
|
|
|
if (!report_progress(progress))
|
2021-12-18 12:46:47 -08:00
|
|
|
return "";
|
2021-12-10 15:21:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Convert to hex
|
2022-07-27 18:47:52 -07:00
|
|
|
return fmt::format("{:02x}", fmt::join(ctx->Finish(), ""));
|
2021-12-10 15:21:46 -08:00
|
|
|
}
|
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
void NetPlayClient::ComputeGameDigest(const SyncIdentifier& sync_identifier)
|
2016-07-14 00:45:38 +02:00
|
|
|
{
|
2022-07-27 18:43:16 -07:00
|
|
|
if (m_should_compute_game_digest)
|
2016-07-14 00:45:38 +02:00
|
|
|
return;
|
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
m_dialog->ShowGameDigestDialog(sync_identifier.game_id);
|
|
|
|
m_should_compute_game_digest = true;
|
2016-07-14 00:45:38 +02:00
|
|
|
|
|
|
|
std::string file;
|
2020-06-07 22:58:03 +02:00
|
|
|
if (sync_identifier == GetSDCardIdentifier())
|
2022-04-17 03:28:34 +02:00
|
|
|
file = File::GetUserPath(F_WIISDCARDIMAGE_IDX);
|
2020-06-07 22:58:03 +02:00
|
|
|
else if (auto game = m_dialog->FindGameFile(sync_identifier))
|
|
|
|
file = game->GetFilePath();
|
2016-07-14 00:45:38 +02:00
|
|
|
|
|
|
|
if (file.empty() || !File::Exists(file))
|
|
|
|
{
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
2022-07-27 18:43:16 -07:00
|
|
|
packet << MessageID::GameDigestError;
|
2017-02-28 14:33:54 -08:00
|
|
|
packet << "file not found";
|
|
|
|
Send(packet);
|
2016-07-14 00:45:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
if (m_game_digest_thread.joinable())
|
|
|
|
m_game_digest_thread.join();
|
|
|
|
m_game_digest_thread = std::thread([this, file]() {
|
2022-07-27 18:47:52 -07:00
|
|
|
std::string sum = SHA1Sum(file, [&](int progress) {
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
2022-07-27 18:43:16 -07:00
|
|
|
packet << MessageID::GameDigestProgress;
|
2017-02-28 14:33:54 -08:00
|
|
|
packet << progress;
|
2018-07-12 20:37:12 -04:00
|
|
|
SendAsync(std::move(packet));
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2022-07-27 18:43:16 -07:00
|
|
|
return m_should_compute_game_digest;
|
2016-07-14 00:45:38 +02:00
|
|
|
});
|
|
|
|
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
2022-07-27 18:43:16 -07:00
|
|
|
packet << MessageID::GameDigestResult;
|
2017-02-28 14:33:54 -08:00
|
|
|
packet << sum;
|
2018-07-12 20:37:12 -04:00
|
|
|
SendAsync(std::move(packet));
|
2016-07-14 00:45:38 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-05-12 15:13:30 +02:00
|
|
|
const PadMappingArray& NetPlayClient::GetPadMapping() const
|
|
|
|
{
|
|
|
|
return m_pad_map;
|
|
|
|
}
|
|
|
|
|
2021-07-04 13:33:58 +02:00
|
|
|
const GBAConfigArray& NetPlayClient::GetGBAConfig() const
|
|
|
|
{
|
|
|
|
return m_gba_config;
|
|
|
|
}
|
|
|
|
|
2018-05-12 15:13:30 +02:00
|
|
|
const PadMappingArray& NetPlayClient::GetWiimoteMapping() const
|
|
|
|
{
|
|
|
|
return m_wiimote_map;
|
|
|
|
}
|
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
void NetPlayClient::AdjustPadBufferSize(const unsigned int size)
|
|
|
|
{
|
|
|
|
m_target_buffer_size = size;
|
|
|
|
m_dialog->OnPadBufferChanged(size);
|
|
|
|
}
|
|
|
|
|
2021-11-20 21:03:34 +01:00
|
|
|
void NetPlayClient::SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs,
|
2021-11-14 02:22:59 +01:00
|
|
|
std::vector<u64> titles, std::string redirect_folder)
|
2021-11-20 21:03:34 +01:00
|
|
|
{
|
|
|
|
m_wii_sync_fs = std::move(fs);
|
|
|
|
m_wii_sync_titles = std::move(titles);
|
2021-11-14 02:22:59 +01:00
|
|
|
m_wii_sync_redirect_folder = std::move(redirect_folder);
|
2021-11-20 21:03:34 +01:00
|
|
|
}
|
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
SyncIdentifier NetPlayClient::GetSDCardIdentifier()
|
|
|
|
{
|
|
|
|
return SyncIdentifier{{}, "sd", {}, {}, {}, {}};
|
|
|
|
}
|
|
|
|
|
2021-07-04 13:33:58 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
bool IsNetPlayRunning()
|
|
|
|
{
|
|
|
|
return netplay_client != nullptr;
|
|
|
|
}
|
|
|
|
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
void SetSIPollBatching(bool state)
|
|
|
|
{
|
|
|
|
s_si_poll_batching = state;
|
|
|
|
}
|
|
|
|
|
2018-11-10 22:37:49 -05:00
|
|
|
void SendPowerButtonEvent()
|
|
|
|
{
|
|
|
|
ASSERT(IsNetPlayRunning());
|
|
|
|
netplay_client->SendPowerButtonEvent();
|
|
|
|
}
|
|
|
|
|
2021-07-04 13:33:58 +02:00
|
|
|
std::string GetGBASavePath(int pad_num)
|
|
|
|
{
|
|
|
|
std::lock_guard lk(crit_netplay_client);
|
|
|
|
|
2022-09-19 01:18:26 +02:00
|
|
|
if (!netplay_client || netplay_client->GetNetSettings().is_hosting)
|
2021-07-04 13:33:58 +02:00
|
|
|
{
|
|
|
|
#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
|
|
|
|
}
|
|
|
|
|
2022-09-11 02:35:20 +02:00
|
|
|
if (!netplay_client->GetNetSettings().savedata_load)
|
2021-07-04 13:33:58 +02:00
|
|
|
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);
|
|
|
|
|
2021-07-21 09:36:52 +02:00
|
|
|
PadDetails res{};
|
|
|
|
res.local_pad = 4;
|
2021-07-04 13:33:58 +02:00
|
|
|
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;
|
2022-09-19 01:18:26 +02:00
|
|
|
res.hide_gba = !res.is_local && netplay_client->GetNetSettings().hide_remote_gbas &&
|
2021-07-04 13:33:58 +02:00
|
|
|
netplay_client->LocalPlayerHasControllerMapped();
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-09-25 04:22:30 +02:00
|
|
|
int NumLocalWiimotes()
|
|
|
|
{
|
|
|
|
std::lock_guard lk(crit_netplay_client);
|
|
|
|
if (netplay_client)
|
|
|
|
return netplay_client->NumLocalWiimotes();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
void NetPlay_Enable(NetPlayClient* const np)
|
|
|
|
{
|
2020-12-29 16:01:12 -05:00
|
|
|
std::lock_guard lk(crit_netplay_client);
|
2018-07-06 19:39:42 -04:00
|
|
|
netplay_client = np;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlay_Disable()
|
|
|
|
{
|
2020-12-29 16:01:12 -05:00
|
|
|
std::lock_guard lk(crit_netplay_client);
|
2018-07-06 19:39:42 -04:00
|
|
|
netplay_client = nullptr;
|
|
|
|
}
|
|
|
|
} // namespace NetPlay
|
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
// stuff hacked into dolphin
|
|
|
|
|
|
|
|
// called from ---CPU--- thread
|
|
|
|
// Actual Core function which is called on every frame
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
bool SerialInterface::CSIDevice_GCController::NetPlay_GetInput(int pad_num, GCPadStatus* status)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2020-12-29 16:01:12 -05:00
|
|
|
std::lock_guard lk(NetPlay::crit_netplay_client);
|
2013-08-05 05:05:06 -04:00
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
if (NetPlay::netplay_client)
|
NetPlay host input authority mode
Currently, each player buffers their own inputs and sends them to the
host. The host then relays those inputs to everyone else. Every player
waits on inputs from all players to be buffered before continuing. What
this means is all clients run in lockstep, and the total latency of
inputs cannot be lower than the sum of the 2 highest client ping times
in the game (in 3+ player sessions with people across the world, the
latency can be very high).
Host input authority mode changes it so players no longer buffer their
own inputs, and only send them to the host. The host stores only the
most recent input received from a player. The host then sends inputs
for all pads at the SI poll interval, similar to the existing code. If
a player sends inputs to slowly, their last received input is simply
sent again. If they send too quickly, inputs are dropped. This means
that the host has full control over what inputs are actually read by
the game, hence the name of the mode. Also, because the rate at which
inputs are received by SI is decoupled from the rate at which players
are sending inputs, clients are no longer dependent on each other. They
only care what the host is doing. This means that they can set their
buffer individually based on their latency to the host, rather than the
highest latency between any 2 players, allowing someone with lower ping
to the host to have less latency than someone else.
This is a catch to this: as a necessity of how the host's input sending
works, the host has 0 latency. There isn't a good way to fix this, as
input delay is now solely dependent on the real latency to the host's
server. Having differing latency between players would be considered
unfair for competitive play, but for casual play we don't really care.
For this reason though, combined with the potential for a few inputs to
be dropped on a bad connection, the old mode will remain and this new
mode is entirely optional.
2018-08-24 04:17:18 -04:00
|
|
|
return NetPlay::netplay_client->GetNetPads(pad_num, NetPlay::s_si_poll_batching, status);
|
2018-07-06 19:39:42 -04:00
|
|
|
|
|
|
|
return false;
|
2013-08-05 05:05:06 -04:00
|
|
|
}
|
|
|
|
|
2022-09-25 04:02:53 +02:00
|
|
|
bool NetPlay::NetPlay_GetWiimoteData(const std::span<NetPlayClient::WiimoteDataBatchEntry>& entries)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2022-09-23 03:21:52 +02:00
|
|
|
std::lock_guard lk(crit_netplay_client);
|
2013-08-06 23:48:52 -04:00
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
if (netplay_client)
|
2022-09-25 04:02:53 +02:00
|
|
|
return netplay_client->WiimoteUpdate(entries);
|
2018-07-06 19:39:42 -04:00
|
|
|
|
|
|
|
return false;
|
2013-08-06 23:48:52 -04:00
|
|
|
}
|
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
unsigned int NetPlay::NetPlay_GetLocalWiimoteForSlot(unsigned int slot)
|
2017-07-15 00:33:23 +02:00
|
|
|
{
|
2022-09-23 03:21:52 +02:00
|
|
|
if (slot >= std::tuple_size_v<PadMappingArray>)
|
|
|
|
return slot;
|
2017-07-15 00:33:23 +02:00
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
std::lock_guard lk(crit_netplay_client);
|
2017-07-15 00:33:23 +02:00
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
if (!netplay_client)
|
|
|
|
return slot;
|
|
|
|
|
|
|
|
const auto& mapping = netplay_client->GetWiimoteMapping();
|
|
|
|
const auto& local_player_id = netplay_client->GetLocalPlayerId();
|
|
|
|
|
|
|
|
std::array<unsigned int, std::tuple_size_v<std::decay_t<decltype(mapping)>>> slot_map;
|
|
|
|
size_t player_count = 0;
|
|
|
|
for (size_t i = 0; i < mapping.size(); ++i)
|
2017-07-15 00:33:23 +02:00
|
|
|
{
|
2022-09-23 03:21:52 +02:00
|
|
|
if (mapping[i] == local_player_id)
|
2017-07-15 00:33:23 +02:00
|
|
|
{
|
2022-09-23 03:21:52 +02:00
|
|
|
slot_map[i] = static_cast<unsigned int>(player_count);
|
|
|
|
++player_count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < mapping.size(); ++i)
|
|
|
|
{
|
|
|
|
if (mapping[i] != local_player_id)
|
|
|
|
{
|
|
|
|
slot_map[i] = static_cast<unsigned int>(player_count);
|
|
|
|
++player_count;
|
2017-07-15 00:33:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-23 03:21:52 +02:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Wiimote slot map: [{}]", fmt::join(slot_map, ", "));
|
|
|
|
|
|
|
|
return slot_map[slot];
|
2017-07-15 00:33:23 +02:00
|
|
|
}
|
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
// called from ---CPU--- thread
|
|
|
|
// so all players' games get the same time
|
2016-07-11 16:49:58 +02:00
|
|
|
//
|
|
|
|
// also called from ---GUI--- thread when starting input recording
|
2017-03-18 17:46:05 -04:00
|
|
|
u64 ExpansionInterface::CEXIIPL::NetPlay_GetEmulatedTime()
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2020-12-29 16:01:12 -05:00
|
|
|
std::lock_guard lk(NetPlay::crit_netplay_client);
|
2013-08-05 05:05:06 -04:00
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
if (NetPlay::netplay_client)
|
2018-07-14 18:55:17 -04:00
|
|
|
return NetPlay::netplay_client->GetInitialRTCValue();
|
2018-07-06 19:39:42 -04:00
|
|
|
|
|
|
|
return 0;
|
2013-08-05 05:05:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---CPU--- thread
|
|
|
|
// return the local pad num that should rumble given a ingame pad num
|
2017-03-16 04:41:36 -04:00
|
|
|
int SerialInterface::CSIDevice_GCController::NetPlay_InGamePadToLocalPad(int numPAD)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2020-12-29 16:01:12 -05:00
|
|
|
std::lock_guard lk(NetPlay::crit_netplay_client);
|
2013-08-05 05:05:06 -04:00
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
if (NetPlay::netplay_client)
|
|
|
|
return NetPlay::netplay_client->InGamePadToLocalPad(numPAD);
|
2013-08-05 05:05:06 -04:00
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
return numPAD;
|
2013-08-05 05:05:06 -04:00
|
|
|
}
|