2015-05-24 06:55:12 +02:00
|
|
|
// Copyright 2010 Dolphin Emulator Project
|
2015-05-18 01:08:10 +02:00
|
|
|
// Licensed under GPLv2+
|
2013-04-17 22:43:11 -04:00
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
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>
|
2020-09-27 12:36:19 -05:00
|
|
|
#include <cassert>
|
2017-09-09 15:52:35 -04:00
|
|
|
#include <cstddef>
|
|
|
|
#include <cstring>
|
2016-07-14 00:45:38 +02:00
|
|
|
#include <fstream>
|
2016-01-24 22:10:38 -05:00
|
|
|
#include <memory>
|
2017-06-06 23:36:16 -04:00
|
|
|
#include <mutex>
|
|
|
|
#include <sstream>
|
2016-07-14 00:45:38 +02:00
|
|
|
#include <thread>
|
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>
|
2018-07-04 17:01:50 -04:00
|
|
|
#include <lzo/lzo1x.h>
|
2017-06-06 23:36:16 -04:00
|
|
|
#include <mbedtls/md5.h>
|
|
|
|
|
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"
|
2015-05-08 17:28:03 -04:00
|
|
|
#include "Common/ENetUtil.h"
|
2018-07-04 17:01:50 -04:00
|
|
|
#include "Common/File.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-07-14 00:45:38 +02:00
|
|
|
#include "Common/MD5.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"
|
2018-10-04 18:49:41 -04:00
|
|
|
#include "Core/ActionReplay.h"
|
2017-10-28 01:42:25 +02:00
|
|
|
#include "Core/Config/NetplaySettings.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"
|
2017-01-20 15:33:43 -05:00
|
|
|
#include "Core/HW/EXI/EXI_DeviceIPL.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"
|
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"
|
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"
|
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;
|
2018-07-04 17:01:50 -04:00
|
|
|
static std::unique_ptr<IOS::HLE::FS::FileSystem> s_wii_sync_fs;
|
2018-10-02 09:16:12 -04:00
|
|
|
static std::vector<u64> s_wii_sync_titles;
|
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
|
|
|
static bool s_si_poll_batching;
|
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
|
|
|
{
|
2018-07-12 20:37:12 -04:00
|
|
|
m_should_compute_MD5 = false;
|
|
|
|
m_dialog->AbortMD5();
|
|
|
|
if (m_MD5_thread.joinable())
|
|
|
|
m_MD5_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
|
|
|
|
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
|
|
|
|
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
|
|
|
{
|
|
|
|
m_client->intercept = ENetUtil::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.
|
2018-04-16 16:02:21 -04:00
|
|
|
if (m_traversal_client->GetState() == TraversalClient::Failure)
|
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;
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2015-03-28 20:15:34 +01:00
|
|
|
if (connect_timer.GetTimeElapsed() > 5000)
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
// send connect message
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
2017-09-09 15:52:35 -04:00
|
|
|
packet << Common::scm_rev_git_str;
|
|
|
|
packet << Common::netplay_dolphin_ver;
|
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;
|
|
|
|
if (enet_host_service(m_client, &netEvent, 5000) > 0 && netEvent.type == ENET_EVENT_TYPE_RECEIVE)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
MessageId error;
|
|
|
|
rpac >> error;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
// got error message
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
switch (error)
|
|
|
|
{
|
|
|
|
case CON_ERR_SERVER_FULL:
|
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;
|
|
|
|
case CON_ERR_VERSION_MISMATCH:
|
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;
|
|
|
|
case CON_ERR_GAME_RUNNING:
|
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;
|
2018-11-21 00:15:44 -05:00
|
|
|
case CON_ERR_NAME_TOO_LONG:
|
|
|
|
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;
|
2017-09-09 15:52:35 -04:00
|
|
|
player.revision = Common::netplay_dolphin_ver;
|
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
|
|
|
|
unsigned int NetPlayClient::OnData(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
MessageId mid;
|
|
|
|
packet >> mid;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Got server message: {:x}", mid);
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
switch (mid)
|
|
|
|
{
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_PLAYER_JOIN:
|
|
|
|
{
|
|
|
|
Player player;
|
|
|
|
packet >> player.pid;
|
|
|
|
packet >> player.name;
|
|
|
|
packet >> player.revision;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Player {} ({}) using {} joined", player.name, player.pid,
|
|
|
|
player.revision);
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2011-03-05 06:11:26 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
2010-05-05 04:44:19 +00:00
|
|
|
m_players[player.pid] = player;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2019-07-30 20:14:51 -07:00
|
|
|
m_dialog->OnPlayerConnect(player.name);
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_PLAYER_LEAVE:
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2011-03-05 06:11:26 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
2019-08-13 18:00:12 -07:00
|
|
|
const auto it = m_players.find(pid);
|
|
|
|
if (it == m_players.end())
|
|
|
|
break;
|
|
|
|
|
|
|
|
const auto& player = it->second;
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Player {} ({}) left", player.name, pid);
|
2019-08-13 18:00:12 -07:00
|
|
|
m_dialog->OnPlayerDisconnect(player.name);
|
2010-05-05 04:44:19 +00:00
|
|
|
m_players.erase(m_players.find(pid));
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_CHAT_MESSAGE:
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
std::string msg;
|
|
|
|
packet >> msg;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
// don't need lock to read in this thread
|
|
|
|
const Player& player = m_players[pid];
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Player {} ({}) wrote: {}", player.name, player.pid, msg);
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
// add to gui
|
|
|
|
std::ostringstream ss;
|
|
|
|
ss << player.name << '[' << (char)(pid + '0') << "]: " << msg;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
m_dialog->AppendChat(ss.str());
|
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-10-18 04:33:05 -04:00
|
|
|
case NP_MSG_CHUNKED_DATA_START:
|
|
|
|
{
|
|
|
|
u32 cid;
|
|
|
|
packet >> cid;
|
|
|
|
std::string title;
|
|
|
|
packet >> title;
|
|
|
|
u64 data_size = Common::PacketReadU64(packet);
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_CHUNKED_DATA_END:
|
|
|
|
{
|
|
|
|
u32 cid;
|
|
|
|
packet >> cid;
|
|
|
|
|
2019-03-28 02:32:06 -04:00
|
|
|
if (m_chunked_data_receive_queue.count(cid))
|
|
|
|
{
|
|
|
|
OnData(m_chunked_data_receive_queue[cid]);
|
|
|
|
m_chunked_data_receive_queue.erase(cid);
|
|
|
|
m_dialog->HideChunkedProgressDialog();
|
|
|
|
|
|
|
|
sf::Packet complete_packet;
|
|
|
|
complete_packet << static_cast<MessageId>(NP_MSG_CHUNKED_DATA_COMPLETE);
|
|
|
|
complete_packet << cid;
|
|
|
|
Send(complete_packet, CHUNKED_DATA_CHANNEL);
|
|
|
|
}
|
2018-10-18 04:33:05 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_CHUNKED_DATA_PAYLOAD:
|
|
|
|
{
|
|
|
|
u32 cid;
|
|
|
|
packet >> cid;
|
|
|
|
|
2019-03-28 02:32:06 -04:00
|
|
|
if (m_chunked_data_receive_queue.count(cid))
|
2018-10-18 04:33:05 -04:00
|
|
|
{
|
2019-03-28 02:32:06 -04:00
|
|
|
while (!packet.endOfPacket())
|
|
|
|
{
|
|
|
|
u8 byte;
|
|
|
|
packet >> byte;
|
|
|
|
m_chunked_data_receive_queue[cid] << byte;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_dialog->SetChunkedProgress(m_local_player->pid,
|
|
|
|
m_chunked_data_receive_queue[cid].getDataSize());
|
|
|
|
|
|
|
|
sf::Packet progress_packet;
|
|
|
|
progress_packet << static_cast<MessageId>(NP_MSG_CHUNKED_DATA_PROGRESS);
|
|
|
|
progress_packet << cid;
|
|
|
|
progress_packet << sf::Uint64{m_chunked_data_receive_queue[cid].getDataSize()};
|
|
|
|
Send(progress_packet, CHUNKED_DATA_CHANNEL);
|
2018-10-18 04:33:05 -04:00
|
|
|
}
|
2019-03-28 02:32:06 -04:00
|
|
|
}
|
|
|
|
break;
|
2018-10-18 04:33:05 -04:00
|
|
|
|
2019-03-28 02:32:06 -04:00
|
|
|
case NP_MSG_CHUNKED_DATA_ABORT:
|
|
|
|
{
|
|
|
|
u32 cid;
|
|
|
|
packet >> cid;
|
2018-10-18 04:33:05 -04:00
|
|
|
|
2019-03-28 02:32:06 -04:00
|
|
|
if (m_chunked_data_receive_queue.count(cid))
|
|
|
|
{
|
|
|
|
m_chunked_data_receive_queue.erase(cid);
|
|
|
|
m_dialog->HideChunkedProgressDialog();
|
|
|
|
}
|
2018-10-18 04:33:05 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_PAD_MAPPING:
|
|
|
|
{
|
2018-11-19 05:30:39 -05:00
|
|
|
for (PlayerId& mapping : m_pad_map)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2015-02-02 01:27:06 -08:00
|
|
|
packet >> mapping;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
UpdateDevices();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_WIIMOTE_MAPPING:
|
|
|
|
{
|
2018-11-19 05:30:39 -05:00
|
|
|
for (PlayerId& mapping : m_wiimote_map)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2015-02-02 01:27:06 -08:00
|
|
|
packet >> mapping;
|
2013-08-06 23:48:52 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_PAD_DATA:
|
|
|
|
{
|
2018-07-09 16:45:52 -04:00
|
|
|
while (!packet.endOfPacket())
|
|
|
|
{
|
2018-11-19 05:30:39 -05:00
|
|
|
PadIndex map;
|
2018-07-09 16:45:52 -04:00
|
|
|
packet >> map;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-07-09 16:45:52 -04:00
|
|
|
GCPadStatus pad;
|
|
|
|
packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >>
|
|
|
|
pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
|
|
|
|
|
|
|
|
// Trusting server for good map value (>=0 && <4)
|
|
|
|
// add to pad buffer
|
|
|
|
m_pad_buffer.at(map).Push(pad);
|
|
|
|
m_gc_pad_event.Set();
|
|
|
|
}
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2019-04-01 22:36:48 -04:00
|
|
|
case NP_MSG_PAD_HOST_DATA:
|
|
|
|
{
|
|
|
|
while (!packet.endOfPacket())
|
|
|
|
{
|
|
|
|
PadIndex map;
|
|
|
|
packet >> map;
|
|
|
|
|
|
|
|
GCPadStatus pad;
|
|
|
|
packet >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >>
|
|
|
|
pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight >> pad.isConnected;
|
|
|
|
|
|
|
|
// Trusting server for good map value (>=0 && <4)
|
|
|
|
// write to last status
|
|
|
|
m_last_pad_status[map] = pad;
|
|
|
|
|
|
|
|
if (!m_first_pad_status_received[map])
|
|
|
|
{
|
|
|
|
m_first_pad_status_received[map] = true;
|
|
|
|
m_first_pad_status_received_event.Set();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_WIIMOTE_DATA:
|
|
|
|
{
|
2018-11-19 05:30:39 -05:00
|
|
|
PadIndex map;
|
2020-09-27 12:36:19 -05:00
|
|
|
WiimoteInput nw;
|
2015-02-02 01:27:06 -08:00
|
|
|
u8 size;
|
2020-09-27 12:36:19 -05:00
|
|
|
packet >> map >> nw.report_id >> size;
|
|
|
|
nw.data.resize(size);
|
|
|
|
for (auto& byte : nw.data)
|
|
|
|
packet >> byte;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-01-24 23:26:38 -05:00
|
|
|
// Trusting server for good map value (>=0 && <4)
|
2015-02-02 01:27:06 -08:00
|
|
|
// add to Wiimote buffer
|
2016-01-24 23:26:38 -05:00
|
|
|
m_wiimote_buffer.at(map).Push(nw);
|
2016-07-25 00:40:15 +02:00
|
|
|
m_wii_pad_event.Set();
|
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
|
|
|
case NP_MSG_PAD_BUFFER:
|
|
|
|
{
|
|
|
|
u32 size = 0;
|
|
|
|
packet >> size;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
m_target_buffer_size = size;
|
2016-02-02 16:35:27 +01:00
|
|
|
m_dialog->OnPadBufferChanged(size);
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
|
|
|
break;
|
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
|
|
|
case NP_MSG_HOST_INPUT_AUTHORITY:
|
|
|
|
{
|
|
|
|
packet >> m_host_input_authority;
|
|
|
|
m_dialog->OnHostInputAuthorityChanged(m_host_input_authority);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
case NP_MSG_GOLF_SWITCH:
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
sf::Packet spac;
|
|
|
|
spac << static_cast<MessageId>(NP_MSG_GOLF_RELEASE);
|
|
|
|
Send(spac);
|
|
|
|
}
|
|
|
|
else if (m_local_player->pid == pid)
|
|
|
|
{
|
|
|
|
sf::Packet spac;
|
|
|
|
spac << static_cast<MessageId>(NP_MSG_GOLF_ACQUIRE);
|
|
|
|
Send(spac);
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_GOLF_PREPARE:
|
|
|
|
{
|
|
|
|
m_wait_on_input_received = true;
|
|
|
|
m_wait_on_input = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_CHANGE_GAME:
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2020-06-07 22:58:03 +02:00
|
|
|
std::string netplay_name;
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2011-03-05 06:11:26 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
2020-06-07 22:58:03 +02:00
|
|
|
ReceiveSyncIdentifier(packet, m_selected_game);
|
|
|
|
packet >> netplay_name;
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Game changed to {}", netplay_name);
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
// update gui
|
2020-06-07 22:58:03 +02:00
|
|
|
m_dialog->OnMsgChangeGame(m_selected_game, netplay_name);
|
2016-07-10 10:13:34 +02:00
|
|
|
|
2017-03-18 22:55:04 -04:00
|
|
|
sf::Packet game_status_packet;
|
|
|
|
game_status_packet << static_cast<MessageId>(NP_MSG_GAME_STATUS);
|
2016-07-10 10:13:34 +02:00
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
SyncIdentifierComparison result;
|
|
|
|
m_dialog->FindGameFile(m_selected_game, &result);
|
2016-07-10 10:13:34 +02:00
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
game_status_packet << static_cast<u32>(result);
|
2017-03-18 22:55:04 -04:00
|
|
|
Send(game_status_packet);
|
2018-07-19 18:10:37 -04:00
|
|
|
|
|
|
|
sf::Packet ipl_status_packet;
|
|
|
|
ipl_status_packet << static_cast<MessageId>(NP_MSG_IPL_STATUS);
|
|
|
|
ipl_status_packet << ExpansionInterface::CEXIIPL::HasIPLDump();
|
|
|
|
Send(ipl_status_packet);
|
2016-07-10 10:13:34 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_GAME_STATUS:
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
Player& player = m_players[pid];
|
|
|
|
u32 status;
|
|
|
|
packet >> status;
|
2020-06-07 22:58:03 +02:00
|
|
|
player.game_status = static_cast<SyncIdentifierComparison>(status);
|
2016-07-10 10:13:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m_dialog->Update();
|
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
|
|
|
case NP_MSG_START_GAME:
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2011-03-05 06:11:26 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
2010-11-16 01:55:29 +00:00
|
|
|
packet >> m_current_game;
|
2018-07-09 15:52:31 -04:00
|
|
|
packet >> m_net_settings.m_CPUthread;
|
2018-06-15 08:11:18 -04:00
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Start of game {}", m_selected_game.game_id);
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2018-06-15 08:11:18 -04:00
|
|
|
{
|
|
|
|
std::underlying_type_t<PowerPC::CPUCore> core;
|
|
|
|
if (packet >> core)
|
2018-07-09 15:52:31 -04:00
|
|
|
m_net_settings.m_CPUcore = static_cast<PowerPC::CPUCore>(core);
|
2018-06-15 08:11:18 -04:00
|
|
|
else
|
2018-07-09 15:52:31 -04:00
|
|
|
m_net_settings.m_CPUcore = PowerPC::CPUCore::CachedInterpreter;
|
2018-06-15 08:11:18 -04:00
|
|
|
}
|
|
|
|
|
2018-07-09 15:52:31 -04:00
|
|
|
packet >> m_net_settings.m_EnableCheats;
|
|
|
|
packet >> m_net_settings.m_SelectedLanguage;
|
2019-06-30 11:48:49 +02:00
|
|
|
packet >> m_net_settings.m_OverrideRegionSettings;
|
2018-07-09 15:52:31 -04:00
|
|
|
packet >> m_net_settings.m_ProgressiveScan;
|
|
|
|
packet >> m_net_settings.m_PAL60;
|
|
|
|
packet >> m_net_settings.m_DSPEnableJIT;
|
|
|
|
packet >> m_net_settings.m_DSPHLE;
|
|
|
|
packet >> m_net_settings.m_WriteToMemcard;
|
|
|
|
packet >> m_net_settings.m_CopyWiiSave;
|
|
|
|
packet >> m_net_settings.m_OCEnable;
|
|
|
|
packet >> m_net_settings.m_OCFactor;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-11-22 06:52:48 -05:00
|
|
|
for (auto& device : m_net_settings.m_EXIDevice)
|
|
|
|
{
|
|
|
|
int tmp;
|
|
|
|
packet >> tmp;
|
|
|
|
device = static_cast<ExpansionInterface::TEXIDevices>(tmp);
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-07-19 18:10:37 -04:00
|
|
|
packet >> m_net_settings.m_EFBAccessEnable;
|
|
|
|
packet >> m_net_settings.m_BBoxEnable;
|
|
|
|
packet >> m_net_settings.m_ForceProgressive;
|
|
|
|
packet >> m_net_settings.m_EFBToTextureEnable;
|
|
|
|
packet >> m_net_settings.m_XFBToTextureEnable;
|
|
|
|
packet >> m_net_settings.m_DisableCopyToVRAM;
|
|
|
|
packet >> m_net_settings.m_ImmediateXFBEnable;
|
|
|
|
packet >> m_net_settings.m_EFBEmulateFormatChanges;
|
|
|
|
packet >> m_net_settings.m_SafeTextureCacheColorSamples;
|
|
|
|
packet >> m_net_settings.m_PerfQueriesEnable;
|
|
|
|
packet >> m_net_settings.m_FPRF;
|
|
|
|
packet >> m_net_settings.m_AccurateNaNs;
|
|
|
|
packet >> m_net_settings.m_SyncOnSkipIdle;
|
|
|
|
packet >> m_net_settings.m_SyncGPU;
|
|
|
|
packet >> m_net_settings.m_SyncGpuMaxDistance;
|
|
|
|
packet >> m_net_settings.m_SyncGpuMinDistance;
|
|
|
|
packet >> m_net_settings.m_SyncGpuOverclock;
|
|
|
|
packet >> m_net_settings.m_JITFollowBranch;
|
|
|
|
packet >> m_net_settings.m_FastDiscSpeed;
|
|
|
|
packet >> m_net_settings.m_MMU;
|
|
|
|
packet >> m_net_settings.m_Fastmem;
|
|
|
|
packet >> m_net_settings.m_SkipIPL;
|
|
|
|
packet >> m_net_settings.m_LoadIPLDump;
|
|
|
|
packet >> m_net_settings.m_VertexRounding;
|
|
|
|
packet >> m_net_settings.m_InternalResolution;
|
|
|
|
packet >> m_net_settings.m_EFBScaledCopy;
|
|
|
|
packet >> m_net_settings.m_FastDepthCalc;
|
|
|
|
packet >> m_net_settings.m_EnablePixelLighting;
|
|
|
|
packet >> m_net_settings.m_WidescreenHack;
|
|
|
|
packet >> m_net_settings.m_ForceFiltering;
|
|
|
|
packet >> m_net_settings.m_MaxAnisotropy;
|
|
|
|
packet >> m_net_settings.m_ForceTrueColor;
|
|
|
|
packet >> m_net_settings.m_DisableCopyFilter;
|
|
|
|
packet >> m_net_settings.m_DisableFog;
|
|
|
|
packet >> m_net_settings.m_ArbitraryMipmapDetection;
|
|
|
|
packet >> m_net_settings.m_ArbitraryMipmapDetectionThreshold;
|
|
|
|
packet >> m_net_settings.m_EnableGPUTextureDecoding;
|
2018-11-12 20:45:12 -05:00
|
|
|
packet >> m_net_settings.m_DeferEFBCopies;
|
2019-03-28 00:38:52 -04:00
|
|
|
packet >> m_net_settings.m_EFBAccessTileSize;
|
|
|
|
packet >> m_net_settings.m_EFBAccessDeferInvalidation;
|
2018-07-19 18:10:37 -04:00
|
|
|
packet >> m_net_settings.m_StrictSettingsSync;
|
|
|
|
|
2018-07-14 18:55:17 -04:00
|
|
|
m_initial_rtc = Common::PacketReadU64(packet);
|
2018-07-04 17:01:50 -04:00
|
|
|
|
|
|
|
packet >> m_net_settings.m_SyncSaveData;
|
|
|
|
packet >> m_net_settings.m_SaveDataRegion;
|
2018-10-04 18:49:41 -04:00
|
|
|
packet >> m_net_settings.m_SyncCodes;
|
2018-10-02 09:16:12 -04:00
|
|
|
packet >> m_net_settings.m_SyncAllWiiSaves;
|
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-22 06:52:48 -05:00
|
|
|
for (int& extension : m_net_settings.m_WiimoteExtension)
|
|
|
|
packet >> extension;
|
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
packet >> m_net_settings.m_GolfMode;
|
|
|
|
|
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_net_settings.m_IsHosting = m_local_player->IsHost();
|
|
|
|
m_net_settings.m_HostInputAuthority = m_host_input_authority;
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
m_dialog->OnMsgStartGame();
|
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_STOP_GAME:
|
|
|
|
case NP_MSG_DISABLE_GAME:
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Game stopped");
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2016-02-02 16:35:27 +01:00
|
|
|
StopGame();
|
|
|
|
m_dialog->OnMsgStopGame();
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-11-10 22:37:49 -05:00
|
|
|
case NP_MSG_POWER_BUTTON:
|
|
|
|
{
|
|
|
|
m_dialog->OnMsgPowerButton();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
case NP_MSG_PING:
|
|
|
|
{
|
|
|
|
u32 ping_key = 0;
|
|
|
|
packet >> ping_key;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-03-18 22:55:04 -04:00
|
|
|
sf::Packet response_packet;
|
|
|
|
response_packet << static_cast<MessageId>(NP_MSG_PONG);
|
|
|
|
response_packet << ping_key;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-03-18 22:55:04 -04:00
|
|
|
Send(response_packet);
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-08-05 05:50:26 -04:00
|
|
|
case NP_MSG_PLAYER_PING_DATA:
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
{
|
2013-08-05 05:50:26 -04:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
Player& player = m_players[pid];
|
|
|
|
packet >> player.ping;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-02-02 16:35:27 +01:00
|
|
|
DisplayPlayersPing();
|
2015-02-02 01:27:06 -08:00
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-03-08 06:50:47 -04:00
|
|
|
case NP_MSG_DESYNC_DETECTED:
|
|
|
|
{
|
2015-06-05 19:00:26 -04:00
|
|
|
int pid_to_blame;
|
2015-03-08 06:50:47 -04:00
|
|
|
u32 frame;
|
2015-06-05 19:00:26 -04:00
|
|
|
packet >> pid_to_blame;
|
2015-03-08 06:50:47 -04:00
|
|
|
packet >> frame;
|
2016-02-02 16:35:27 +01:00
|
|
|
|
|
|
|
std::string player = "??";
|
2015-06-05 19:00:26 -04:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
{
|
|
|
|
auto it = m_players.find(pid_to_blame);
|
2016-02-02 16:35:27 +01:00
|
|
|
if (it != m_players.end())
|
|
|
|
player = it->second.name;
|
2015-06-05 19:00:26 -04:00
|
|
|
}
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
INFO_LOG_FMT(NETPLAY, "Player {} ({}) desynced!", player, pid_to_blame);
|
2018-07-10 17:46:13 +02:00
|
|
|
|
2016-02-02 16:35:27 +01:00
|
|
|
m_dialog->OnDesync(frame, player);
|
2015-03-08 06:50:47 -04:00
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-06-14 13:59:41 +02:00
|
|
|
case NP_MSG_SYNC_GC_SRAM:
|
|
|
|
{
|
2017-09-04 03:35:21 -07:00
|
|
|
const size_t sram_settings_len = sizeof(g_SRAM) - offsetof(Sram, settings);
|
|
|
|
u8 sram[sram_settings_len];
|
2019-02-01 19:47:07 +01:00
|
|
|
for (u8& cell : sram)
|
2015-06-14 13:59:41 +02:00
|
|
|
{
|
2019-02-01 19:47:07 +01:00
|
|
|
packet >> cell;
|
2015-06-14 13:59:41 +02:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-06-14 13:59:41 +02:00
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
2017-09-04 03:35:21 -07:00
|
|
|
memcpy(&g_SRAM.settings, sram, sram_settings_len);
|
2015-06-14 13:59:41 +02:00
|
|
|
g_SRAM_netplay_initialized = true;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
2015-06-14 13:59:41 +02:00
|
|
|
break;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-07-04 17:01:50 -04:00
|
|
|
case NP_MSG_SYNC_SAVE_DATA:
|
|
|
|
{
|
|
|
|
MessageId sub_id;
|
|
|
|
packet >> sub_id;
|
|
|
|
|
|
|
|
switch (sub_id)
|
|
|
|
{
|
|
|
|
case SYNC_SAVE_DATA_NOTIFY:
|
|
|
|
{
|
2018-10-04 18:49:41 -04:00
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return 0;
|
|
|
|
|
2018-07-04 17:01:50 -04:00
|
|
|
packet >> m_sync_save_data_count;
|
|
|
|
m_sync_save_data_success_count = 0;
|
|
|
|
|
|
|
|
if (m_sync_save_data_count == 0)
|
|
|
|
SyncSaveDataResponse(true);
|
|
|
|
else
|
2019-06-16 23:45:37 -04:00
|
|
|
m_dialog->AppendChat(Common::GetStringT("Synchronizing save data..."));
|
2018-07-04 17:01:50 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYNC_SAVE_DATA_RAW:
|
|
|
|
{
|
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_local_player->IsHost())
|
2018-07-04 17:01:50 -04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
bool is_slot_a;
|
|
|
|
std::string region;
|
|
|
|
bool mc251;
|
|
|
|
packet >> is_slot_a >> region >> mc251;
|
|
|
|
|
|
|
|
const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY +
|
|
|
|
(is_slot_a ? "A." : "B.") + region + (mc251 ? ".251" : "") + ".raw";
|
|
|
|
if (File::Exists(path) && !File::Delete(path))
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Failed to delete NetPlay memory card. Verify your write permissions.");
|
2018-07-04 17:01:50 -04:00
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool success = DecompressPacketIntoFile(packet, path);
|
|
|
|
SyncSaveDataResponse(success);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYNC_SAVE_DATA_GCI:
|
|
|
|
{
|
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_local_player->IsHost())
|
2018-07-04 17:01:50 -04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
bool is_slot_a;
|
|
|
|
u8 file_count;
|
|
|
|
packet >> is_slot_a >> file_count;
|
|
|
|
|
|
|
|
const std::string path = File::GetUserPath(D_GCUSER_IDX) + GC_MEMCARD_NETPLAY DIR_SEP +
|
2019-10-20 07:35:11 -04:00
|
|
|
fmt::format("Card {}", is_slot_a ? 'A' : 'B');
|
2018-07-04 17:01:50 -04:00
|
|
|
|
|
|
|
if ((File::Exists(path) && !File::DeleteDirRecursively(path + DIR_SEP)) ||
|
|
|
|
!File::CreateFullPath(path + DIR_SEP))
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Failed to reset NetPlay GCI folder. Verify your write permissions.");
|
2018-07-04 17:01:50 -04:00
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (u8 i = 0; i < file_count; i++)
|
|
|
|
{
|
|
|
|
std::string file_name;
|
|
|
|
packet >> file_name;
|
|
|
|
|
|
|
|
if (!DecompressPacketIntoFile(packet, path + DIR_SEP + file_name))
|
|
|
|
{
|
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SyncSaveDataResponse(true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYNC_SAVE_DATA_WII:
|
|
|
|
{
|
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_local_player->IsHost())
|
2018-07-04 17:01:50 -04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP;
|
|
|
|
|
|
|
|
if (File::Exists(path) && !File::DeleteDirRecursively(path))
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Failed to reset NetPlay NAND folder. Verify your write permissions.");
|
2018-07-04 17:01:50 -04:00
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto temp_fs = std::make_unique<IOS::HLE::FS::HostFileSystem>(path);
|
2018-10-02 09:16:12 -04:00
|
|
|
std::vector<u64> titles;
|
|
|
|
|
2019-04-08 07:06:21 -04:00
|
|
|
const IOS::HLE::FS::Modes fs_modes = {IOS::HLE::FS::Mode::ReadWrite,
|
|
|
|
IOS::HLE::FS::Mode::ReadWrite,
|
|
|
|
IOS::HLE::FS::Mode::ReadWrite};
|
|
|
|
|
|
|
|
// Read the Mii data
|
|
|
|
bool mii_data;
|
|
|
|
packet >> mii_data;
|
|
|
|
if (mii_data)
|
|
|
|
{
|
|
|
|
auto buffer = DecompressPacketIntoBuffer(packet);
|
|
|
|
|
2020-01-04 01:55:40 +01:00
|
|
|
temp_fs->CreateFullPath(IOS::PID_KERNEL, IOS::PID_KERNEL, "/shared2/menu/FaceLib/", 0,
|
|
|
|
fs_modes);
|
2019-04-08 07:06:21 -04:00
|
|
|
auto file = temp_fs->CreateAndOpenFile(IOS::PID_KERNEL, IOS::PID_KERNEL,
|
|
|
|
Common::GetMiiDatabasePath(), fs_modes);
|
|
|
|
|
|
|
|
if (!buffer || !file || !file->Write(buffer->data(), buffer->size()))
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Failed to write Mii data.");
|
2019-04-08 07:06:21 -04:00
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the saves
|
2018-10-02 09:16:12 -04:00
|
|
|
u32 save_count;
|
|
|
|
packet >> save_count;
|
|
|
|
for (u32 n = 0; n < save_count; n++)
|
2018-07-04 17:01:50 -04:00
|
|
|
{
|
2018-10-02 09:16:12 -04:00
|
|
|
u64 title_id = Common::PacketReadU64(packet);
|
|
|
|
titles.push_back(title_id);
|
2020-01-04 01:55:40 +01:00
|
|
|
temp_fs->CreateFullPath(IOS::PID_KERNEL, IOS::PID_KERNEL,
|
|
|
|
Common::GetTitleDataPath(title_id) + '/', 0, fs_modes);
|
2018-10-02 09:16:12 -04:00
|
|
|
auto save = WiiSave::MakeNandStorage(temp_fs.get(), title_id);
|
|
|
|
|
|
|
|
bool exists;
|
|
|
|
packet >> exists;
|
|
|
|
if (!exists)
|
|
|
|
continue;
|
|
|
|
|
2018-07-04 17:01:50 -04:00
|
|
|
// Header
|
|
|
|
WiiSave::Header header;
|
2018-10-02 04:45:36 -04:00
|
|
|
packet >> header.tid;
|
|
|
|
packet >> header.banner_size;
|
2018-07-04 17:01:50 -04:00
|
|
|
packet >> header.permissions;
|
|
|
|
packet >> header.unk1;
|
2019-02-01 19:47:07 +01:00
|
|
|
for (u8& byte : header.md5)
|
|
|
|
packet >> byte;
|
2018-10-02 04:45:36 -04:00
|
|
|
packet >> header.unk2;
|
2018-07-04 17:01:50 -04:00
|
|
|
for (size_t i = 0; i < header.banner_size; i++)
|
|
|
|
packet >> header.banner[i];
|
|
|
|
|
|
|
|
// BkHeader
|
|
|
|
WiiSave::BkHeader bk_header;
|
2018-10-02 04:45:36 -04:00
|
|
|
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;
|
2019-02-01 19:47:07 +01:00
|
|
|
for (u8& byte : bk_header.unk3)
|
|
|
|
packet >> byte;
|
2018-10-02 04:45:36 -04:00
|
|
|
packet >> bk_header.tid;
|
2019-02-01 19:47:07 +01:00
|
|
|
for (u8& byte : bk_header.mac_address)
|
|
|
|
packet >> byte;
|
2018-07-04 17:01:50 -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;
|
|
|
|
{
|
|
|
|
u8 tmp;
|
|
|
|
packet >> tmp;
|
|
|
|
file.type = static_cast<WiiSave::Storage::SaveFile::Type>(tmp);
|
|
|
|
}
|
|
|
|
packet >> file.path;
|
|
|
|
|
|
|
|
if (file.type == WiiSave::Storage::SaveFile::Type::File)
|
|
|
|
{
|
|
|
|
auto buffer = DecompressPacketIntoBuffer(packet);
|
|
|
|
if (!buffer)
|
|
|
|
{
|
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
file.data = std::move(*buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
files.push_back(std::move(file));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!save->WriteHeader(header) || !save->WriteBkHeader(bk_header) ||
|
|
|
|
!save->WriteFiles(files))
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Failed to write Wii save.");
|
2018-07-04 17:01:50 -04:00
|
|
|
SyncSaveDataResponse(false);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-02 09:16:12 -04:00
|
|
|
SetWiiSyncData(std::move(temp_fs), titles);
|
2018-07-04 17:01:50 -04:00
|
|
|
SyncSaveDataResponse(true);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Unknown SYNC_SAVE_DATA message received with id: {0}", sub_id);
|
2018-07-04 17:01:50 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-10-04 18:49:41 -04:00
|
|
|
case NP_MSG_SYNC_CODES:
|
|
|
|
{
|
|
|
|
// Recieve Data Packet
|
|
|
|
MessageId sub_id;
|
|
|
|
packet >> sub_id;
|
|
|
|
|
|
|
|
// Check Which Operation to Perform with This Packet
|
|
|
|
switch (sub_id)
|
|
|
|
{
|
|
|
|
case SYNC_CODES_NOTIFY:
|
|
|
|
{
|
|
|
|
// Set both codes as unsynced
|
|
|
|
m_sync_gecko_codes_complete = false;
|
|
|
|
m_sync_ar_codes_complete = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYNC_CODES_NOTIFY_GECKO:
|
|
|
|
{
|
|
|
|
// Return if this is the host
|
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Receive Number of Codelines to Receive
|
|
|
|
packet >> m_sync_gecko_codes_count;
|
|
|
|
|
|
|
|
m_sync_gecko_codes_success_count = 0;
|
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
NOTICE_LOG_FMT(ACTIONREPLAY, "Receiving {} Gecko codelines", m_sync_gecko_codes_count);
|
2018-10-04 18:49:41 -04:00
|
|
|
|
|
|
|
// Check if no codes to sync, if so return as finished
|
|
|
|
if (m_sync_gecko_codes_count == 0)
|
|
|
|
{
|
|
|
|
m_sync_gecko_codes_complete = true;
|
|
|
|
SyncCodeResponse(true);
|
|
|
|
}
|
|
|
|
else
|
2020-11-18 06:01:15 -05:00
|
|
|
{
|
2019-06-16 23:45:37 -04:00
|
|
|
m_dialog->AppendChat(Common::GetStringT("Synchronizing Gecko codes..."));
|
2020-11-18 06:01:15 -05:00
|
|
|
}
|
2018-10-04 18:49:41 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYNC_CODES_DATA_GECKO:
|
|
|
|
{
|
|
|
|
// Return if this is the host
|
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Create a synced code vector
|
|
|
|
std::vector<Gecko::GeckoCode> synced_codes;
|
|
|
|
// Create a GeckoCode
|
|
|
|
Gecko::GeckoCode gcode;
|
|
|
|
gcode = Gecko::GeckoCode();
|
|
|
|
// Initialize gcode
|
|
|
|
gcode.name = "Synced Codes";
|
|
|
|
gcode.enabled = true;
|
|
|
|
|
|
|
|
// Receive code contents from packet
|
|
|
|
for (int i = 0; i < m_sync_gecko_codes_count; i++)
|
|
|
|
{
|
|
|
|
Gecko::GeckoCode::Code new_code;
|
|
|
|
packet >> new_code.address;
|
|
|
|
packet >> new_code.data;
|
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
NOTICE_LOG_FMT(ACTIONREPLAY, "Received {:08x} {:08x}", new_code.address, new_code.data);
|
2018-10-04 18:49:41 -04:00
|
|
|
|
|
|
|
gcode.codes.push_back(std::move(new_code));
|
|
|
|
|
|
|
|
if (++m_sync_gecko_codes_success_count >= m_sync_gecko_codes_count)
|
|
|
|
{
|
|
|
|
m_sync_gecko_codes_complete = true;
|
|
|
|
SyncCodeResponse(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add gcode containing all codes to Gecko Code vector
|
|
|
|
synced_codes.push_back(std::move(gcode));
|
|
|
|
|
|
|
|
// Clear Vector if received 0 codes (match host's end when using no codes)
|
|
|
|
if (m_sync_gecko_codes_count == 0)
|
|
|
|
synced_codes.clear();
|
|
|
|
|
|
|
|
// Copy this to the vector located in GeckoCode.cpp
|
|
|
|
Gecko::UpdateSyncedCodes(synced_codes);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYNC_CODES_NOTIFY_AR:
|
|
|
|
{
|
|
|
|
// Return if this is the host
|
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Receive Number of Codelines to Receive
|
|
|
|
packet >> m_sync_ar_codes_count;
|
|
|
|
|
|
|
|
m_sync_ar_codes_success_count = 0;
|
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
NOTICE_LOG_FMT(ACTIONREPLAY, "Receiving {} AR codelines", m_sync_ar_codes_count);
|
2018-10-04 18:49:41 -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
|
2020-11-18 06:01:15 -05:00
|
|
|
{
|
2019-06-16 23:45:37 -04:00
|
|
|
m_dialog->AppendChat(Common::GetStringT("Synchronizing AR codes..."));
|
2020-11-18 06:01:15 -05:00
|
|
|
}
|
2018-10-04 18:49:41 -04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SYNC_CODES_DATA_AR:
|
|
|
|
{
|
|
|
|
// Return if this is the host
|
|
|
|
if (m_local_player->IsHost())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// Create a synced code vector
|
|
|
|
std::vector<ActionReplay::ARCode> synced_codes;
|
|
|
|
// Create an ARCode
|
|
|
|
ActionReplay::ARCode arcode;
|
|
|
|
arcode = ActionReplay::ARCode();
|
|
|
|
// Initialize arcode
|
|
|
|
arcode.name = "Synced Codes";
|
|
|
|
arcode.active = true;
|
|
|
|
|
|
|
|
// Receive code contents from packet
|
|
|
|
for (int i = 0; i < m_sync_ar_codes_count; i++)
|
|
|
|
{
|
|
|
|
ActionReplay::AREntry new_code;
|
|
|
|
packet >> new_code.cmd_addr;
|
|
|
|
packet >> new_code.value;
|
|
|
|
|
2020-11-18 06:01:15 -05:00
|
|
|
NOTICE_LOG_FMT(ACTIONREPLAY, "Received {:08x} {:08x}", new_code.cmd_addr, new_code.value);
|
2018-10-04 18:49:41 -04:00
|
|
|
arcode.ops.push_back(new_code);
|
|
|
|
|
|
|
|
if (++m_sync_ar_codes_success_count >= m_sync_ar_codes_count)
|
|
|
|
{
|
|
|
|
m_sync_ar_codes_complete = true;
|
|
|
|
SyncCodeResponse(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add arcode containing all codes to AR Code vector
|
|
|
|
synced_codes.push_back(std::move(arcode));
|
|
|
|
|
|
|
|
// Clear Vector if received 0 codes (match host's end when using no codes)
|
|
|
|
if (m_sync_ar_codes_count == 0)
|
|
|
|
synced_codes.clear();
|
|
|
|
|
|
|
|
// Copy this to the vector located in ActionReplay.cpp
|
|
|
|
ActionReplay::UpdateSyncedCodes(synced_codes);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2016-07-14 00:45:38 +02:00
|
|
|
case NP_MSG_COMPUTE_MD5:
|
|
|
|
{
|
2020-06-07 22:58:03 +02:00
|
|
|
SyncIdentifier sync_identifier;
|
|
|
|
ReceiveSyncIdentifier(packet, sync_identifier);
|
2016-07-14 00:45:38 +02:00
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
ComputeMD5(sync_identifier);
|
2016-07-14 00:45:38 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_MD5_PROGRESS:
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
int progress;
|
|
|
|
packet >> pid;
|
|
|
|
packet >> progress;
|
|
|
|
|
|
|
|
m_dialog->SetMD5Progress(pid, progress);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_MD5_RESULT:
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
std::string result;
|
|
|
|
packet >> pid;
|
|
|
|
packet >> result;
|
|
|
|
|
|
|
|
m_dialog->SetMD5Result(pid, result);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_MD5_ERROR:
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
std::string error;
|
|
|
|
packet >> pid;
|
|
|
|
packet >> error;
|
|
|
|
|
|
|
|
m_dialog->SetMD5Result(pid, error);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_MD5_ABORT:
|
|
|
|
{
|
|
|
|
m_should_compute_MD5 = false;
|
|
|
|
m_dialog->AbortMD5();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
default:
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Unknown message received with id : {0}", mid);
|
2010-05-05 04:44:19 +00:00
|
|
|
break;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
ENetPacket* epac =
|
|
|
|
enet_packet_create(packet.getData(), packet.getDataSize(), ENET_PACKET_FLAG_RELIABLE);
|
2018-10-18 04:33:05 -04:00
|
|
|
enet_peer_send(m_server, channel_id, epac);
|
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
|
|
|
{
|
|
|
|
{
|
2015-03-13 02:03:09 +01:00
|
|
|
std::lock_guard<std::recursive_mutex> 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
|
|
|
}
|
2015-03-09 18:37:02 +01:00
|
|
|
ENetUtil::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
|
|
|
{
|
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
|
|
|
{
|
2018-10-18 04:33:05 -04:00
|
|
|
{
|
|
|
|
auto& e = m_async_queue.Front();
|
|
|
|
Send(e.packet, e.channel_id);
|
|
|
|
}
|
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
|
|
|
{
|
2015-02-02 01:27:06 -08:00
|
|
|
case ENET_EVENT_TYPE_RECEIVE:
|
|
|
|
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:
|
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:
|
|
|
|
break;
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-02 01:27:06 -08:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2015-02-02 01:27:06 -08:00
|
|
|
Disconnect();
|
2010-05-05 04:44:19 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---GUI--- thread
|
2010-06-06 03:52:11 +00:00
|
|
|
void NetPlayClient::GetPlayerList(std::string& list, std::vector<int>& pid_list)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2011-03-05 06:11:26 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
std::ostringstream ss;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-01-24 23:06:04 -05:00
|
|
|
const auto enumerate_player_controller_mappings = [&ss](const PadMappingArray& mappings,
|
|
|
|
const Player& player) {
|
|
|
|
for (size_t i = 0; i < mappings.size(); i++)
|
2013-08-18 09:43:01 -04:00
|
|
|
{
|
2016-01-24 23:06:04 -05:00
|
|
|
if (mappings[i] == player.pid)
|
|
|
|
ss << i + 1;
|
2013-08-18 09:43:01 -04:00
|
|
|
else
|
|
|
|
ss << '-';
|
|
|
|
}
|
2016-01-24 23:06:04 -05:00
|
|
|
};
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-01-24 23:06:04 -05:00
|
|
|
for (const auto& entry : m_players)
|
|
|
|
{
|
|
|
|
const Player& player = entry.second;
|
|
|
|
ss << player.name << "[" << static_cast<int>(player.pid) << "] : " << player.revision << " | ";
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-01-24 23:06:04 -05:00
|
|
|
enumerate_player_controller_mappings(m_pad_map, player);
|
|
|
|
enumerate_player_controller_mappings(m_wiimote_map, player);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-07-10 10:13:34 +02:00
|
|
|
ss << " |\nPing: " << player.ping << "ms\n";
|
|
|
|
ss << "Status: ";
|
|
|
|
|
|
|
|
switch (player.game_status)
|
|
|
|
{
|
2020-06-07 22:58:03 +02:00
|
|
|
case SyncIdentifierComparison::SameGame:
|
2016-07-10 10:13:34 +02:00
|
|
|
ss << "ready";
|
|
|
|
break;
|
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
case SyncIdentifierComparison::DifferentVersion:
|
|
|
|
ss << "wrong game version";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SyncIdentifierComparison::DifferentGame:
|
2016-07-10 10:13:34 +02:00
|
|
|
ss << "game missing";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ss << "unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ss << "\n\n";
|
|
|
|
|
2016-01-24 23:06:04 -05:00
|
|
|
pid_list.push_back(player.pid);
|
2010-06-06 03:52:11 +00:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
list = ss.str();
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> 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;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_CHAT_MESSAGE);
|
|
|
|
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);
|
2017-02-28 14:33:54 -08:00
|
|
|
packet << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX
|
2017-02-09 15:03:43 -08:00
|
|
|
<< 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
|
2020-09-27 12:36:19 -05:00
|
|
|
void NetPlayClient::SendWiimoteState(const int in_game_pad, const WiimoteInput& nw)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_WIIMOTE_DATA);
|
2018-11-19 05:30:39 -05:00
|
|
|
packet << static_cast<PadIndex>(in_game_pad);
|
2020-09-27 12:36:19 -05:00
|
|
|
packet << static_cast<u8>(nw.report_id);
|
|
|
|
packet << static_cast<u8>(nw.data.size());
|
|
|
|
packet.append(nw.data.data(), nw.data.size());
|
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::SendStartGamePacket()
|
|
|
|
{
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_START_GAME);
|
|
|
|
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;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_STOP_GAME);
|
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)
|
|
|
|
{
|
2015-03-13 02:03:09 +01:00
|
|
|
std::lock_guard<std::recursive_mutex> 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
|
|
|
|
2013-09-03 15:50:41 -04:00
|
|
|
u8 controllers_mask = 0;
|
|
|
|
for (unsigned int i = 0; i < 4; ++i)
|
|
|
|
{
|
|
|
|
if (m_pad_map[i] > 0)
|
|
|
|
controllers_mask |= (1 << i);
|
2013-09-22 17:32:11 -04:00
|
|
|
if (m_wiimote_map[i] > 0)
|
2013-09-22 22:01:25 -04:00
|
|
|
controllers_mask |= (1 << (i + 4));
|
2013-09-03 15:50:41 -04:00
|
|
|
}
|
|
|
|
Movie::BeginRecordingInput(controllers_mask);
|
|
|
|
}
|
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
|
|
|
{
|
|
|
|
WiimoteCommon::SetSource(i,
|
|
|
|
m_wiimote_map[i] > 0 ? WiimoteSource::Emulated : WiimoteSource::None);
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-02-22 22:50:05 +01:00
|
|
|
// boot game
|
2013-08-05 04:56:30 -04:00
|
|
|
m_dialog->BootGame(path);
|
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;
|
|
|
|
response_packet << static_cast<MessageId>(NP_MSG_SYNC_SAVE_DATA);
|
|
|
|
response_packet << static_cast<MessageId>(SYNC_SAVE_DATA_SUCCESS);
|
|
|
|
|
|
|
|
Send(response_packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sf::Packet response_packet;
|
|
|
|
response_packet << static_cast<MessageId>(NP_MSG_SYNC_SAVE_DATA);
|
|
|
|
response_packet << static_cast<MessageId>(SYNC_SAVE_DATA_FAILURE);
|
|
|
|
|
|
|
|
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;
|
|
|
|
response_packet << static_cast<MessageId>(NP_MSG_SYNC_CODES);
|
|
|
|
response_packet << static_cast<MessageId>(SYNC_CODES_FAILURE);
|
|
|
|
|
|
|
|
Send(response_packet);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If both gecko and AR codes have completely finished transferring, report back as successful
|
|
|
|
if (m_sync_gecko_codes_complete && m_sync_ar_codes_complete)
|
|
|
|
{
|
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;
|
|
|
|
response_packet << static_cast<MessageId>(NP_MSG_SYNC_CODES);
|
|
|
|
response_packet << static_cast<MessageId>(SYNC_CODES_SUCCESS);
|
|
|
|
|
|
|
|
Send(response_packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-04 17:01:50 -04:00
|
|
|
bool NetPlayClient::DecompressPacketIntoFile(sf::Packet& packet, const std::string& file_path)
|
|
|
|
{
|
|
|
|
u64 file_size = Common::PacketReadU64(packet);
|
|
|
|
|
|
|
|
if (file_size == 0)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
File::IOFile file(file_path, "wb");
|
|
|
|
if (!file)
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Failed to open file \"{0}\". Verify your write permissions.", file_path);
|
2018-07-04 17:01:50 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<u8> in_buffer(NETPLAY_LZO_OUT_LEN);
|
|
|
|
std::vector<u8> out_buffer(NETPLAY_LZO_IN_LEN);
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
2019-12-18 19:11:43 -08:00
|
|
|
u32 cur_len = 0; // number of bytes to read
|
|
|
|
lzo_uint new_len = 0; // number of bytes to write
|
2018-07-04 17:01:50 -04:00
|
|
|
|
|
|
|
packet >> cur_len;
|
|
|
|
if (!cur_len)
|
|
|
|
break; // We reached the end of the data stream
|
|
|
|
|
|
|
|
for (size_t j = 0; j < cur_len; j++)
|
|
|
|
{
|
|
|
|
packet >> in_buffer[j];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lzo1x_decompress(in_buffer.data(), cur_len, out_buffer.data(), &new_len, nullptr) !=
|
|
|
|
LZO_E_OK)
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Internal LZO Error - decompression failed");
|
2018-07-04 17:01:50 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!file.WriteBytes(out_buffer.data(), new_len))
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Error writing file: {0}", file_path);
|
2018-07-04 17:01:50 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::vector<u8>> NetPlayClient::DecompressPacketIntoBuffer(sf::Packet& packet)
|
|
|
|
{
|
|
|
|
u64 size = Common::PacketReadU64(packet);
|
|
|
|
|
|
|
|
std::vector<u8> out_buffer(size);
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
return out_buffer;
|
|
|
|
|
|
|
|
std::vector<u8> in_buffer(NETPLAY_LZO_OUT_LEN);
|
|
|
|
|
|
|
|
lzo_uint i = 0;
|
|
|
|
while (true)
|
|
|
|
{
|
2019-12-18 19:11:43 -08:00
|
|
|
u32 cur_len = 0; // number of bytes to read
|
|
|
|
lzo_uint new_len = 0; // number of bytes to write
|
2018-07-04 17:01:50 -04:00
|
|
|
|
|
|
|
packet >> cur_len;
|
|
|
|
if (!cur_len)
|
|
|
|
break; // We reached the end of the data stream
|
|
|
|
|
|
|
|
for (size_t j = 0; j < cur_len; j++)
|
|
|
|
{
|
|
|
|
packet >> in_buffer[j];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lzo1x_decompress(in_buffer.data(), cur_len, &out_buffer[i], &new_len, nullptr) != LZO_E_OK)
|
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Internal LZO Error - decompression failed");
|
2018-07-04 17:01:50 -04:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
i += new_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return out_buffer;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2016-01-26 00:21:13 +01:00
|
|
|
for (auto player_id : m_pad_map)
|
2013-08-23 20:24:45 -04:00
|
|
|
{
|
2016-02-02 22:08:38 +01:00
|
|
|
// Use local controller types for local controllers if they are compatible
|
2016-05-05 15:32:54 +02:00
|
|
|
// Only GCController-like controllers are supported, GBA and similar
|
|
|
|
// exotic devices are not supported on netplay.
|
2016-01-26 00:21:13 +01:00
|
|
|
if (player_id == m_local_player->pid)
|
|
|
|
{
|
2017-03-16 04:41:36 -04:00
|
|
|
if (SerialInterface::SIDevice_IsGCController(SConfig::GetInstance().m_SIDevice[local_pad]))
|
2016-01-28 20:24:18 +01:00
|
|
|
{
|
2019-01-26 10:29:06 -06:00
|
|
|
SerialInterface::ChangeDevice(SConfig::GetInstance().m_SIDevice[local_pad], pad);
|
2019-04-13 06:31:10 -04:00
|
|
|
|
|
|
|
if (SConfig::GetInstance().m_SIDevice[local_pad] == SerialInterface::SIDEVICE_WIIU_ADAPTER)
|
|
|
|
{
|
|
|
|
GCAdapter::ResetDeviceType(local_pad);
|
|
|
|
}
|
2016-01-28 20:24:18 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-01-26 10:29:06 -06:00
|
|
|
SerialInterface::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
|
|
|
{
|
2019-01-26 10:29:06 -06:00
|
|
|
SerialInterface::ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad);
|
2016-01-26 00:21:13 +01:00
|
|
|
}
|
2016-05-05 15:32:54 +02:00
|
|
|
else
|
|
|
|
{
|
2019-01-26 10:29:06 -06:00
|
|
|
SerialInterface::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 &&
|
2018-04-16 16:02:21 -04:00
|
|
|
state == TraversalClient::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);
|
|
|
|
}
|
2018-04-16 16:02:21 -04:00
|
|
|
else if (m_connection_state != ConnectionState::Failure && state == TraversalClient::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
|
|
|
|
void NetPlayClient::OnConnectFailed(u8 reason)
|
|
|
|
{
|
|
|
|
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
|
|
|
{
|
2015-03-01 16:17:32 +01:00
|
|
|
case TraversalConnectFailedClientDidntRespond:
|
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;
|
2015-03-01 16:17:32 +01:00
|
|
|
case TraversalConnectFailedClientFailure:
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Server rejected traversal attempt");
|
2015-02-02 01:56:53 -08:00
|
|
|
break;
|
2015-03-01 16:17:32 +01:00
|
|
|
case TraversalConnectFailedNoSuchClient:
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Invalid host");
|
2015-02-02 01:56:53 -08:00
|
|
|
break;
|
|
|
|
default:
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Unknown error {0:x}", 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;
|
|
|
|
spac << static_cast<MessageId>(NP_MSG_GOLF_PREPARE);
|
|
|
|
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;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_PAD_DATA);
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
int local_pad = InGamePadToLocalPad(pad_nb);
|
|
|
|
if (local_pad < 4)
|
|
|
|
{
|
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_PAD_DATA);
|
|
|
|
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
|
|
|
|
SConfig::GetInstance().m_EmulationSpeed = buffer_over_target ? 0.0f : 1.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
SConfig::GetInstance().m_EmulationSpeed = 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
|
2020-09-27 12:36:19 -05:00
|
|
|
bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const std::size_t size, u8 reporting_mode)
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2020-09-27 12:36:19 -05:00
|
|
|
WiimoteInput nw;
|
|
|
|
nw.report_id = reporting_mode;
|
2013-08-05 05:05:06 -04:00
|
|
|
{
|
2015-02-02 01:27:06 -08:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
Netplay: Fix synchronization for the Wiinote netplay
The old implementation always polled the local 1st Wiimote and used that as input for the Wiimote that is mapped to the player. But the reporting mode for Wiimotes can be different, even when using the same extensions. So an input for Wiimote 1 with a data size 4 could be used for Wiimote 2, which actually requires data size 7 at that time for example.
The 2nd problem was that the code added a dummy input into the buffer, when the reporting mode changed. But when the data from the other player hasn't arrived yet, the data in the buffer is out of order. Well, i think this is the problem, i'm not 100% sure, because i don't fully understand how the buffer works. But on the other hand, i'm pretty sure this will just force sync the players on reporting mode changes, instead of allowing them to be apart.
Pros:
- No more desyncs caused by big bugs in the code.
- Can use different extensions for different players.
Cons:
- Higher latency, because instead of polling 1 controller per player at once, all controllers are polled in order, send to the other players, before the next is processed.
- Have to setup the Wiimote, which the player is going to use, instead of the 1st one.
Now, if the controller config could temporarily be overridden with the one from another slot, the 2nd problem could be fixed. But at the same time, we would lose the ability to use different extensions. (unless we hack around it somehow, or properly send the used extension to the other players)
2016-07-16 12:46:23 +02:00
|
|
|
// Only send data, if this Wiimote is mapped to this player
|
|
|
|
if (m_wiimote_map[_number] == m_local_player->pid)
|
2013-08-22 11:37:38 -04:00
|
|
|
{
|
2020-09-27 12:36:19 -05:00
|
|
|
nw.data.assign(data, data + size);
|
2019-10-13 13:03:09 -05:00
|
|
|
|
|
|
|
// TODO: add a seperate setting for wiimote buffer?
|
|
|
|
while (m_wiimote_buffer[_number].Size() <= m_target_buffer_size * 200 / 120)
|
2013-08-19 13:45:02 -04:00
|
|
|
{
|
Netplay: Fix synchronization for the Wiinote netplay
The old implementation always polled the local 1st Wiimote and used that as input for the Wiimote that is mapped to the player. But the reporting mode for Wiimotes can be different, even when using the same extensions. So an input for Wiimote 1 with a data size 4 could be used for Wiimote 2, which actually requires data size 7 at that time for example.
The 2nd problem was that the code added a dummy input into the buffer, when the reporting mode changed. But when the data from the other player hasn't arrived yet, the data in the buffer is out of order. Well, i think this is the problem, i'm not 100% sure, because i don't fully understand how the buffer works. But on the other hand, i'm pretty sure this will just force sync the players on reporting mode changes, instead of allowing them to be apart.
Pros:
- No more desyncs caused by big bugs in the code.
- Can use different extensions for different players.
Cons:
- Higher latency, because instead of polling 1 controller per player at once, all controllers are polled in order, send to the other players, before the next is processed.
- Have to setup the Wiimote, which the player is going to use, instead of the 1st one.
Now, if the controller config could temporarily be overridden with the one from another slot, the 2nd problem could be fixed. But at the same time, we would lose the ability to use different extensions. (unless we hack around it somehow, or properly send the used extension to the other players)
2016-07-16 12:46:23 +02:00
|
|
|
// add to buffer
|
|
|
|
m_wiimote_buffer[_number].Push(nw);
|
2016-06-24 10:43:46 +02:00
|
|
|
|
Netplay: Fix synchronization for the Wiinote netplay
The old implementation always polled the local 1st Wiimote and used that as input for the Wiimote that is mapped to the player. But the reporting mode for Wiimotes can be different, even when using the same extensions. So an input for Wiimote 1 with a data size 4 could be used for Wiimote 2, which actually requires data size 7 at that time for example.
The 2nd problem was that the code added a dummy input into the buffer, when the reporting mode changed. But when the data from the other player hasn't arrived yet, the data in the buffer is out of order. Well, i think this is the problem, i'm not 100% sure, because i don't fully understand how the buffer works. But on the other hand, i'm pretty sure this will just force sync the players on reporting mode changes, instead of allowing them to be apart.
Pros:
- No more desyncs caused by big bugs in the code.
- Can use different extensions for different players.
Cons:
- Higher latency, because instead of polling 1 controller per player at once, all controllers are polled in order, send to the other players, before the next is processed.
- Have to setup the Wiimote, which the player is going to use, instead of the 1st one.
Now, if the controller config could temporarily be overridden with the one from another slot, the 2nd problem could be fixed. But at the same time, we would lose the ability to use different extensions. (unless we hack around it somehow, or properly send the used extension to the other players)
2016-07-16 12:46:23 +02:00
|
|
|
SendWiimoteState(_number, nw);
|
2019-10-13 13:03:09 -05:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
} // unlock players
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2016-07-25 00:40:15 +02:00
|
|
|
while (m_wiimote_buffer[_number].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-06 23:48:52 -04:00
|
|
|
return false;
|
2016-07-25 00:40:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// wait for receiving thread to push some data
|
|
|
|
m_wii_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_wiimote_buffer[_number].Pop(nw);
|
|
|
|
|
Netplay: Fix synchronization for the Wiinote netplay
The old implementation always polled the local 1st Wiimote and used that as input for the Wiimote that is mapped to the player. But the reporting mode for Wiimotes can be different, even when using the same extensions. So an input for Wiimote 1 with a data size 4 could be used for Wiimote 2, which actually requires data size 7 at that time for example.
The 2nd problem was that the code added a dummy input into the buffer, when the reporting mode changed. But when the data from the other player hasn't arrived yet, the data in the buffer is out of order. Well, i think this is the problem, i'm not 100% sure, because i don't fully understand how the buffer works. But on the other hand, i'm pretty sure this will just force sync the players on reporting mode changes, instead of allowing them to be apart.
Pros:
- No more desyncs caused by big bugs in the code.
- Can use different extensions for different players.
Cons:
- Higher latency, because instead of polling 1 controller per player at once, all controllers are polled in order, send to the other players, before the next is processed.
- Have to setup the Wiimote, which the player is going to use, instead of the 1st one.
Now, if the controller config could temporarily be overridden with the one from another slot, the 2nd problem could be fixed. But at the same time, we would lose the ability to use different extensions. (unless we hack around it somehow, or properly send the used extension to the other players)
2016-07-16 12:46:23 +02:00
|
|
|
// If the reporting mode has changed, we just need to pop through the buffer,
|
2013-08-19 13:45:02 -04:00
|
|
|
// until we reach a good input
|
2020-09-27 12:36:19 -05:00
|
|
|
if (nw.report_id != reporting_mode)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2016-07-08 23:15:38 +02:00
|
|
|
u32 tries = 0;
|
2020-09-27 12:36:19 -05:00
|
|
|
while (nw.report_id != reporting_mode)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2016-07-25 00:40:15 +02:00
|
|
|
while (m_wiimote_buffer[_number].Size() == 0)
|
2013-08-07 10:10:40 -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-07 10:10:40 -04:00
|
|
|
return false;
|
2016-07-25 00:40:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// wait for receiving thread to push some data
|
|
|
|
m_wii_pad_event.Wait();
|
2013-08-07 10:10:40 -04:00
|
|
|
}
|
2016-07-25 00:40:15 +02:00
|
|
|
|
|
|
|
m_wiimote_buffer[_number].Pop(nw);
|
|
|
|
|
2013-08-07 10:10:40 -04:00
|
|
|
++tries;
|
2013-09-22 14:27:52 -04:00
|
|
|
if (tries > m_target_buffer_size * 200 / 120)
|
2013-08-22 11:37:38 -04:00
|
|
|
break;
|
2013-08-19 13:45:02 -04:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2013-08-06 23:48:52 -04:00
|
|
|
// If it still mismatches, it surely desynced
|
2020-09-27 12:36:19 -05:00
|
|
|
if (nw.report_id != reporting_mode)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Netplay has desynced. There is no way to recover from this.");
|
2013-08-22 11:37:38 -04:00
|
|
|
return false;
|
2013-08-06 23:48:52 -04:00
|
|
|
}
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-09-27 12:36:19 -05:00
|
|
|
assert(nw.data.size() == size);
|
|
|
|
std::copy(nw.data.begin(), nw.data.end(), data);
|
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)
|
|
|
|
{
|
|
|
|
GCPadStatus pad_status;
|
|
|
|
|
|
|
|
switch (SConfig::GetInstance().m_SIDevice[local_pad])
|
|
|
|
{
|
|
|
|
case SerialInterface::SIDEVICE_WIIU_ADAPTER:
|
|
|
|
pad_status = GCAdapter::Input(local_pad);
|
|
|
|
break;
|
|
|
|
case SerialInterface::SIDEVICE_GC_CONTROLLER:
|
|
|
|
default:
|
|
|
|
pad_status = Pad::GetStatus(local_pad);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int ingame_pad = LocalPadToInGamePad(local_pad);
|
|
|
|
bool data_added = false;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_PAD_HOST_DATA);
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2013-08-05 05:05:06 -04:00
|
|
|
// called from ---GUI--- thread and ---NETPLAY--- thread (client side)
|
|
|
|
bool NetPlayClient::StopGame()
|
|
|
|
{
|
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();
|
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
|
|
|
|
2018-10-02 09:16:12 -04:00
|
|
|
ClearWiiSyncData();
|
2018-07-04 17:01:50 -04: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
|
|
|
|
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();
|
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;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_POWER_BUTTON);
|
|
|
|
SendAsync(std::move(packet));
|
|
|
|
}
|
|
|
|
|
2019-04-02 08:08:27 -04:00
|
|
|
void NetPlayClient::RequestGolfControl(const PlayerId pid)
|
|
|
|
{
|
|
|
|
if (!m_host_input_authority || !m_net_settings.m_GolfMode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_GOLF_REQUEST);
|
|
|
|
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
|
|
|
{
|
2019-04-02 17:13:42 -04:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
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; });
|
|
|
|
}
|
|
|
|
|
2016-10-07 07:49:32 -04:00
|
|
|
int NetPlayClient::NumLocalPads() const
|
2016-05-08 15:29:01 +02:00
|
|
|
{
|
2016-10-07 07:49:32 -04:00
|
|
|
return static_cast<int>(std::count_if(m_pad_map.begin(), m_pad_map.end(), [this](auto mapping) {
|
2016-05-08 15:29:01 +02:00
|
|
|
return mapping == m_local_player->pid;
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2017-03-19 09:07:33 -04:00
|
|
|
int NetPlayClient::InGamePadToLocalPad(int ingame_pad) const
|
2013-09-09 03:09:45 -04:00
|
|
|
{
|
|
|
|
// not our pad
|
|
|
|
if (m_pad_map[ingame_pad] != m_local_player->pid)
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
int local_pad = 0;
|
|
|
|
int pad = 0;
|
|
|
|
|
|
|
|
for (; pad < ingame_pad; pad++)
|
|
|
|
{
|
|
|
|
if (m_pad_map[pad] == m_local_player->pid)
|
|
|
|
local_pad++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return local_pad;
|
|
|
|
}
|
|
|
|
|
2017-03-19 09:07:33 -04:00
|
|
|
int NetPlayClient::LocalPadToInGamePad(int local_pad) const
|
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++)
|
|
|
|
{
|
|
|
|
if (m_pad_map[ingame_pad] == m_local_player->pid)
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-03-08 06:50:47 -04:00
|
|
|
void NetPlayClient::SendTimeBase()
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
|
|
|
|
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;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_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()
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
void NetPlayClient::ComputeMD5(const SyncIdentifier& sync_identifier)
|
2016-07-14 00:45:38 +02:00
|
|
|
{
|
|
|
|
if (m_should_compute_MD5)
|
|
|
|
return;
|
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
m_dialog->ShowMD5Dialog(sync_identifier.game_id);
|
2016-07-14 00:45:38 +02:00
|
|
|
m_should_compute_MD5 = true;
|
|
|
|
|
|
|
|
std::string file;
|
2020-06-07 22:58:03 +02:00
|
|
|
if (sync_identifier == GetSDCardIdentifier())
|
2016-07-16 21:40:19 +02:00
|
|
|
file = File::GetUserPath(F_WIISDCARD_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;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_MD5_ERROR);
|
|
|
|
packet << "file not found";
|
|
|
|
Send(packet);
|
2016-07-14 00:45:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-12 20:37:12 -04:00
|
|
|
if (m_MD5_thread.joinable())
|
|
|
|
m_MD5_thread.join();
|
2016-07-14 00:45:38 +02:00
|
|
|
m_MD5_thread = std::thread([this, file]() {
|
|
|
|
std::string sum = MD5::MD5Sum(file, [&](int progress) {
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_MD5_PROGRESS);
|
|
|
|
packet << progress;
|
2018-07-12 20:37:12 -04:00
|
|
|
SendAsync(std::move(packet));
|
2016-07-14 00:45:38 +02:00
|
|
|
|
|
|
|
return m_should_compute_MD5;
|
|
|
|
});
|
|
|
|
|
2017-02-28 14:33:54 -08:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_MD5_RESULT);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-06-07 22:58:03 +02:00
|
|
|
SyncIdentifier NetPlayClient::GetSDCardIdentifier()
|
|
|
|
{
|
|
|
|
return SyncIdentifier{{}, "sd", {}, {}, {}, {}};
|
|
|
|
}
|
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
bool IsNetPlayRunning()
|
|
|
|
{
|
|
|
|
return netplay_client != nullptr;
|
|
|
|
}
|
|
|
|
|
2018-07-09 15:52:31 -04:00
|
|
|
const NetSettings& GetNetSettings()
|
|
|
|
{
|
|
|
|
ASSERT(IsNetPlayRunning());
|
|
|
|
return netplay_client->GetNetSettings();
|
|
|
|
}
|
|
|
|
|
2018-07-04 17:01:50 -04:00
|
|
|
IOS::HLE::FS::FileSystem* GetWiiSyncFS()
|
|
|
|
{
|
|
|
|
return s_wii_sync_fs.get();
|
|
|
|
}
|
|
|
|
|
2018-10-02 09:16:12 -04:00
|
|
|
const std::vector<u64>& GetWiiSyncTitles()
|
|
|
|
{
|
|
|
|
return s_wii_sync_titles;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs, const std::vector<u64>& titles)
|
2018-07-04 17:01:50 -04:00
|
|
|
{
|
|
|
|
s_wii_sync_fs = std::move(fs);
|
2018-10-02 09:16:12 -04:00
|
|
|
s_wii_sync_titles.insert(s_wii_sync_titles.end(), titles.begin(), titles.end());
|
2018-07-04 17:01:50 -04:00
|
|
|
}
|
|
|
|
|
2018-10-02 09:16:12 -04:00
|
|
|
void ClearWiiSyncData()
|
2018-07-04 17:01:50 -04:00
|
|
|
{
|
|
|
|
// We're just assuming it will always be here because it is
|
|
|
|
const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP;
|
|
|
|
if (File::Exists(path))
|
|
|
|
File::DeleteDirRecursively(path);
|
|
|
|
|
|
|
|
s_wii_sync_fs.reset();
|
2018-10-02 09:16:12 -04:00
|
|
|
s_wii_sync_titles.clear();
|
2018-07-04 17:01:50 -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
|
|
|
void SetSIPollBatching(bool state)
|
|
|
|
{
|
|
|
|
s_si_poll_batching = state;
|
|
|
|
}
|
|
|
|
|
2018-11-10 22:37:49 -05:00
|
|
|
void SendPowerButtonEvent()
|
|
|
|
{
|
|
|
|
ASSERT(IsNetPlayRunning());
|
|
|
|
netplay_client->SendPowerButtonEvent();
|
|
|
|
}
|
|
|
|
|
2018-10-02 09:16:12 -04:00
|
|
|
bool IsSyncingAllWiiSaves()
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
|
|
|
|
|
|
|
if (netplay_client)
|
|
|
|
return netplay_client->GetNetSettings().m_SyncAllWiiSaves;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-11-22 06:52:48 -05:00
|
|
|
void SetupWiimotes()
|
|
|
|
{
|
|
|
|
ASSERT(IsNetPlayRunning());
|
|
|
|
const NetSettings& netplay_settings = netplay_client->GetNetSettings();
|
|
|
|
const PadMappingArray& wiimote_map = netplay_client->GetWiimoteMapping();
|
2019-01-25 17:33:17 +01:00
|
|
|
for (size_t i = 0; i < netplay_settings.m_WiimoteExtension.size(); i++)
|
2018-11-22 06:52:48 -05:00
|
|
|
{
|
|
|
|
if (wiimote_map[i] > 0)
|
|
|
|
{
|
2019-01-05 07:09:11 -06:00
|
|
|
static_cast<ControllerEmu::Attachments*>(
|
|
|
|
static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(int(i)))
|
|
|
|
->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments))
|
|
|
|
->SetSelectedAttachment(netplay_settings.m_WiimoteExtension[i]);
|
2018-11-22 06:52:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
void NetPlay_Enable(NetPlayClient* const np)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
|
|
|
netplay_client = np;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlay_Disable()
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
|
|
|
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
|
|
|
{
|
2018-07-06 19:39:42 -04:00
|
|
|
std::lock_guard<std::mutex> 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
|
|
|
}
|
|
|
|
|
2016-08-14 15:51:29 +02:00
|
|
|
bool WiimoteEmu::Wiimote::NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode)
|
2013-08-06 23:48:52 -04:00
|
|
|
{
|
2018-07-06 19:39:42 -04:00
|
|
|
std::lock_guard<std::mutex> lk(NetPlay::crit_netplay_client);
|
2013-08-06 23:48:52 -04:00
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
if (NetPlay::netplay_client)
|
|
|
|
return NetPlay::netplay_client->WiimoteUpdate(wiimote, data, size, reporting_mode);
|
|
|
|
|
|
|
|
return false;
|
2013-08-06 23:48:52 -04:00
|
|
|
}
|
|
|
|
|
2017-07-15 00:33:23 +02:00
|
|
|
// Sync the info whether a button was pressed or not. Used for the reconnect on button press feature
|
|
|
|
bool Wiimote::NetPlay_GetButtonPress(int wiimote, bool pressed)
|
|
|
|
{
|
2018-07-06 19:39:42 -04:00
|
|
|
std::lock_guard<std::mutex> lk(NetPlay::crit_netplay_client);
|
2017-07-15 00:33:23 +02:00
|
|
|
|
|
|
|
// Use the reporting mode 0 for the button pressed event, the real ones start at RT_REPORT_CORE
|
2020-09-27 12:36:19 -05:00
|
|
|
static const u8 BUTTON_PRESS_REPORTING_MODE = 0;
|
2017-07-15 00:33:23 +02:00
|
|
|
|
2018-07-06 19:39:42 -04:00
|
|
|
if (NetPlay::netplay_client)
|
2017-07-15 00:33:23 +02:00
|
|
|
{
|
2020-09-27 12:36:19 -05:00
|
|
|
std::array<u8, 1> data = {u8(pressed)};
|
|
|
|
if (NetPlay::netplay_client->WiimoteUpdate(wiimote, data.data(), data.size(),
|
|
|
|
BUTTON_PRESS_REPORTING_MODE))
|
2017-07-15 00:33:23 +02:00
|
|
|
{
|
|
|
|
return data[0];
|
|
|
|
}
|
2020-11-18 06:01:15 -05:00
|
|
|
PanicAlertFmtT("Netplay has desynced in NetPlay_GetButtonPress()");
|
2017-07-15 00:33:23 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pressed;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2018-07-06 19:39:42 -04:00
|
|
|
std::lock_guard<std::mutex> 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
|
|
|
{
|
2018-07-06 19:39:42 -04:00
|
|
|
std::lock_guard<std::mutex> 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
|
|
|
}
|