2015-05-24 04:55:12 +00:00
|
|
|
// Copyright 2010 Dolphin Emulator Project
|
2015-05-17 23:08:10 +00:00
|
|
|
// Licensed under GPLv2+
|
2013-04-18 02:43:11 +00:00
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
#include "Core/NetPlayClient.h"
|
2016-01-25 03:46:37 +00:00
|
|
|
#include <algorithm>
|
2016-07-13 22:45:38 +00:00
|
|
|
#include <fstream>
|
|
|
|
#include <mbedtls/md5.h>
|
2016-01-25 03:10:38 +00:00
|
|
|
#include <memory>
|
2016-07-13 22:45:38 +00:00
|
|
|
#include <thread>
|
2016-01-06 22:46:50 +00:00
|
|
|
#include "Common/Common.h"
|
2016-07-16 19:40:19 +00:00
|
|
|
#include "Common/CommonPaths.h"
|
2016-01-25 02:46:46 +00:00
|
|
|
#include "Common/CommonTypes.h"
|
2015-05-08 21:28:03 +00:00
|
|
|
#include "Common/ENetUtil.h"
|
2016-07-13 22:45:38 +00:00
|
|
|
#include "Common/MD5.h"
|
2016-01-25 02:46:46 +00:00
|
|
|
#include "Common/MsgHandler.h"
|
2017-02-25 03:56:33 +00:00
|
|
|
#include "Common/StringUtil.h"
|
2015-05-08 21:28:03 +00:00
|
|
|
#include "Common/Timer.h"
|
2014-02-19 18:09:14 +00:00
|
|
|
#include "Core/ConfigManager.h"
|
2017-01-20 20:33:43 +00:00
|
|
|
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
2017-01-20 23:45:11 +00:00
|
|
|
#include "Core/HW/SI/SI.h"
|
|
|
|
#include "Core/HW/SI/SI_DeviceGCController.h"
|
2015-06-14 11:59:41 +00:00
|
|
|
#include "Core/HW/Sram.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
2016-06-26 08:44:15 +00:00
|
|
|
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
2017-01-18 12:50:28 +00:00
|
|
|
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
2016-06-24 08:43:46 +00:00
|
|
|
#include "Core/Movie.h"
|
2016-05-08 13:29:01 +00:00
|
|
|
#include "InputCommon/GCAdapter.h"
|
2016-02-02 15:35:27 +00:00
|
|
|
#include "VideoCommon/OnScreenDisplay.h"
|
|
|
|
#include "VideoCommon/VideoConfig.h"
|
2013-08-05 09:05:06 +00:00
|
|
|
|
2014-07-08 13:58:25 +00:00
|
|
|
static std::mutex crit_netplay_client;
|
2016-06-24 08:43:46 +00:00
|
|
|
static NetPlayClient* netplay_client = nullptr;
|
2013-08-05 09:05:06 +00:00
|
|
|
NetSettings g_NetPlaySettings;
|
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---GUI--- thread
|
|
|
|
NetPlayClient::~NetPlayClient()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
// not perfect
|
2016-08-05 14:04:39 +00:00
|
|
|
if (m_is_running.IsSet())
|
2016-06-24 08:43:46 +00:00
|
|
|
StopGame();
|
|
|
|
|
|
|
|
if (m_is_connected)
|
|
|
|
{
|
2016-08-05 14:04:39 +00:00
|
|
|
m_do_loop.Clear();
|
2016-06-24 08:43:46 +00:00
|
|
|
m_thread.join();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_server)
|
|
|
|
{
|
|
|
|
Disconnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_MainNetHost.get() == m_client)
|
|
|
|
{
|
|
|
|
g_MainNetHost.release();
|
|
|
|
}
|
|
|
|
if (m_client)
|
|
|
|
{
|
|
|
|
enet_host_destroy(m_client);
|
|
|
|
m_client = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_traversal_client)
|
|
|
|
{
|
|
|
|
ReleaseTraversalClient();
|
|
|
|
}
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---GUI--- thread
|
2016-06-24 08:43:46 +00:00
|
|
|
NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog,
|
|
|
|
const std::string& name, bool traversal,
|
|
|
|
const std::string& centralServer, u16 centralPort)
|
|
|
|
: m_dialog(dialog), m_player_name(name)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
ClearBuffers();
|
|
|
|
|
|
|
|
if (!traversal)
|
|
|
|
{
|
|
|
|
// Direct Connection
|
|
|
|
m_client = enet_host_create(nullptr, 1, 3, 0, 0);
|
|
|
|
|
|
|
|
if (m_client == nullptr)
|
|
|
|
{
|
|
|
|
PanicAlertT("Couldn't Create Client");
|
|
|
|
}
|
|
|
|
|
|
|
|
ENetAddress addr;
|
|
|
|
enet_address_set_host(&addr, address.c_str());
|
|
|
|
addr.port = port;
|
|
|
|
|
|
|
|
m_server = enet_host_connect(m_client, &addr, 3, 0);
|
|
|
|
|
|
|
|
if (m_server == nullptr)
|
|
|
|
{
|
|
|
|
PanicAlertT("Couldn't create peer.");
|
|
|
|
}
|
|
|
|
|
|
|
|
ENetEvent netEvent;
|
|
|
|
int net = enet_host_service(m_client, &netEvent, 5000);
|
|
|
|
if (net > 0 && netEvent.type == ENET_EVENT_TYPE_CONNECT)
|
|
|
|
{
|
|
|
|
if (Connect())
|
|
|
|
{
|
|
|
|
m_client->intercept = ENetUtil::InterceptCallback;
|
|
|
|
m_thread = std::thread(&NetPlayClient::ThreadFunc, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PanicAlertT("Failed to Connect!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (address.size() > NETPLAY_CODE_SIZE)
|
|
|
|
{
|
|
|
|
PanicAlertT("Host code size is to large.\nPlease recheck that you have the correct code");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!EnsureTraversalClient(centralServer, centralPort))
|
|
|
|
return;
|
|
|
|
m_client = g_MainNetHost.get();
|
|
|
|
|
|
|
|
m_traversal_client = g_TraversalClient.get();
|
|
|
|
|
|
|
|
// If we were disconnected in the background, reconnect.
|
|
|
|
if (m_traversal_client->m_State == TraversalClient::Failure)
|
|
|
|
m_traversal_client->ReconnectToServer();
|
|
|
|
m_traversal_client->m_Client = this;
|
|
|
|
m_host_spec = address;
|
|
|
|
m_connection_state = ConnectionState::WaitingForTraversalClientConnection;
|
|
|
|
OnTraversalStateChanged();
|
|
|
|
m_connecting = true;
|
|
|
|
|
|
|
|
Common::Timer connect_timer;
|
|
|
|
connect_timer.Start();
|
|
|
|
|
|
|
|
while (m_connecting)
|
|
|
|
{
|
|
|
|
ENetEvent netEvent;
|
|
|
|
if (m_traversal_client)
|
|
|
|
m_traversal_client->HandleResends();
|
|
|
|
|
|
|
|
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())
|
|
|
|
{
|
|
|
|
m_connection_state = ConnectionState::Connected;
|
|
|
|
m_thread = std::thread(&NetPlayClient::ThreadFunc, this);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (connect_timer.GetTimeElapsed() > 5000)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
PanicAlertT("Failed To Connect!");
|
|
|
|
}
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
2015-02-02 09:27:06 +00:00
|
|
|
bool NetPlayClient::Connect()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
// send connect message
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << scm_rev_git_str;
|
|
|
|
packet << netplay_dolphin_ver;
|
|
|
|
packet << m_player_name;
|
|
|
|
Send(packet);
|
2016-06-24 08:43:46 +00: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);
|
|
|
|
enet_packet_destroy(netEvent.packet);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageId error;
|
|
|
|
rpac >> error;
|
|
|
|
|
|
|
|
// got error message
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
switch (error)
|
|
|
|
{
|
|
|
|
case CON_ERR_SERVER_FULL:
|
|
|
|
PanicAlertT("The server is full!");
|
|
|
|
break;
|
|
|
|
case CON_ERR_VERSION_MISMATCH:
|
|
|
|
PanicAlertT("The server and client's NetPlay versions are incompatible!");
|
|
|
|
break;
|
|
|
|
case CON_ERR_GAME_RUNNING:
|
|
|
|
PanicAlertT("The server responded: the game is currently running!");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PanicAlertT("The server sent an unknown error message!");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Disconnect();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rpac >> m_pid;
|
|
|
|
|
|
|
|
Player player;
|
|
|
|
player.name = m_player_name;
|
|
|
|
player.pid = m_pid;
|
|
|
|
player.revision = netplay_dolphin_ver;
|
|
|
|
|
|
|
|
// add self to player list
|
|
|
|
m_players[m_pid] = player;
|
|
|
|
m_local_player = &m_players[m_pid];
|
|
|
|
|
|
|
|
m_dialog->Update();
|
|
|
|
|
|
|
|
m_is_connected = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2015-02-02 09:27:06 +00:00
|
|
|
}
|
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
unsigned int NetPlayClient::OnData(sf::Packet& packet)
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
MessageId mid;
|
|
|
|
packet >> mid;
|
|
|
|
|
|
|
|
switch (mid)
|
|
|
|
{
|
|
|
|
case NP_MSG_PLAYER_JOIN:
|
|
|
|
{
|
|
|
|
Player player;
|
|
|
|
packet >> player.pid;
|
|
|
|
packet >> player.name;
|
|
|
|
packet >> player.revision;
|
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
m_players[player.pid] = player;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_PLAYER_LEAVE:
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
m_players.erase(m_players.find(pid));
|
|
|
|
}
|
|
|
|
|
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_CHAT_MESSAGE:
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
std::string msg;
|
|
|
|
packet >> msg;
|
|
|
|
|
|
|
|
// don't need lock to read in this thread
|
|
|
|
const Player& player = m_players[pid];
|
|
|
|
|
|
|
|
// add to gui
|
|
|
|
std::ostringstream ss;
|
|
|
|
ss << player.name << '[' << (char)(pid + '0') << "]: " << msg;
|
|
|
|
|
|
|
|
m_dialog->AppendChat(ss.str());
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_PAD_MAPPING:
|
|
|
|
{
|
|
|
|
for (PadMapping& mapping : m_pad_map)
|
|
|
|
{
|
|
|
|
packet >> mapping;
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateDevices();
|
|
|
|
|
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_WIIMOTE_MAPPING:
|
|
|
|
{
|
|
|
|
for (PadMapping& mapping : m_wiimote_map)
|
|
|
|
{
|
|
|
|
packet >> mapping;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_PAD_DATA:
|
|
|
|
{
|
|
|
|
PadMapping map = 0;
|
|
|
|
GCPadStatus pad;
|
|
|
|
packet >> map >> pad.button >> pad.analogA >> pad.analogB >> pad.stickX >> pad.stickY >>
|
|
|
|
pad.substickX >> pad.substickY >> pad.triggerLeft >> pad.triggerRight;
|
|
|
|
|
|
|
|
// Trusting server for good map value (>=0 && <4)
|
|
|
|
// add to pad buffer
|
|
|
|
m_pad_buffer.at(map).Push(pad);
|
2016-07-24 22:40:15 +00:00
|
|
|
m_gc_pad_event.Set();
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_WIIMOTE_DATA:
|
|
|
|
{
|
|
|
|
PadMapping map = 0;
|
|
|
|
NetWiimote nw;
|
|
|
|
u8 size;
|
|
|
|
packet >> map >> size;
|
|
|
|
|
|
|
|
nw.resize(size);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < size; ++i)
|
|
|
|
packet >> nw[i];
|
|
|
|
|
|
|
|
// Trusting server for good map value (>=0 && <4)
|
|
|
|
// add to Wiimote buffer
|
|
|
|
m_wiimote_buffer.at(map).Push(nw);
|
2016-07-24 22:40:15 +00:00
|
|
|
m_wii_pad_event.Set();
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_PAD_BUFFER:
|
|
|
|
{
|
|
|
|
u32 size = 0;
|
|
|
|
packet >> size;
|
|
|
|
|
|
|
|
m_target_buffer_size = size;
|
2016-02-02 15:35:27 +00:00
|
|
|
m_dialog->OnPadBufferChanged(size);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_CHANGE_GAME:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
|
|
|
packet >> m_selected_game;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update gui
|
|
|
|
m_dialog->OnMsgChangeGame(m_selected_game);
|
2016-07-10 08:13:34 +00:00
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_GAME_STATUS);
|
2016-07-10 08:13:34 +00:00
|
|
|
|
|
|
|
PlayerGameStatus status = m_dialog->FindGame(m_selected_game).empty() ?
|
|
|
|
PlayerGameStatus::NotFound :
|
|
|
|
PlayerGameStatus::Ok;
|
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
packet << static_cast<u32>(status);
|
|
|
|
Send(packet);
|
2016-07-10 08:13:34 +00: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;
|
|
|
|
player.game_status = static_cast<PlayerGameStatus>(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_dialog->Update();
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_START_GAME:
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
|
|
|
packet >> m_current_game;
|
|
|
|
packet >> g_NetPlaySettings.m_CPUthread;
|
|
|
|
packet >> g_NetPlaySettings.m_CPUcore;
|
2016-07-04 23:48:04 +00:00
|
|
|
packet >> g_NetPlaySettings.m_EnableCheats;
|
2016-06-24 08:43:46 +00:00
|
|
|
packet >> g_NetPlaySettings.m_SelectedLanguage;
|
|
|
|
packet >> g_NetPlaySettings.m_OverrideGCLanguage;
|
|
|
|
packet >> g_NetPlaySettings.m_ProgressiveScan;
|
|
|
|
packet >> g_NetPlaySettings.m_PAL60;
|
|
|
|
packet >> g_NetPlaySettings.m_DSPEnableJIT;
|
|
|
|
packet >> g_NetPlaySettings.m_DSPHLE;
|
|
|
|
packet >> g_NetPlaySettings.m_WriteToMemcard;
|
2016-12-24 01:37:23 +00:00
|
|
|
packet >> g_NetPlaySettings.m_CopyWiiSave;
|
2016-06-24 08:43:46 +00:00
|
|
|
packet >> g_NetPlaySettings.m_OCEnable;
|
|
|
|
packet >> g_NetPlaySettings.m_OCFactor;
|
|
|
|
|
|
|
|
int tmp;
|
|
|
|
packet >> tmp;
|
|
|
|
g_NetPlaySettings.m_EXIDevice[0] = (TEXIDevices)tmp;
|
|
|
|
packet >> tmp;
|
|
|
|
g_NetPlaySettings.m_EXIDevice[1] = (TEXIDevices)tmp;
|
|
|
|
|
|
|
|
u32 time_low, time_high;
|
|
|
|
packet >> time_low;
|
|
|
|
packet >> time_high;
|
2016-10-29 12:01:00 +00:00
|
|
|
g_netplay_initial_rtc = time_low | ((u64)time_high << 32);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_dialog->OnMsgStartGame();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_STOP_GAME:
|
|
|
|
case NP_MSG_DISABLE_GAME:
|
|
|
|
{
|
2016-02-02 15:35:27 +00:00
|
|
|
StopGame();
|
|
|
|
m_dialog->OnMsgStopGame();
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_PING:
|
|
|
|
{
|
|
|
|
u32 ping_key = 0;
|
|
|
|
packet >> ping_key;
|
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << (MessageId)NP_MSG_PONG;
|
|
|
|
packet << ping_key;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
Send(packet);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_PLAYER_PING_DATA:
|
|
|
|
{
|
|
|
|
PlayerId pid;
|
|
|
|
packet >> pid;
|
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
Player& player = m_players[pid];
|
|
|
|
packet >> player.ping;
|
|
|
|
}
|
|
|
|
|
2016-02-02 15:35:27 +00:00
|
|
|
DisplayPlayersPing();
|
2016-06-24 08:43:46 +00:00
|
|
|
m_dialog->Update();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_DESYNC_DETECTED:
|
|
|
|
{
|
|
|
|
int pid_to_blame;
|
|
|
|
u32 frame;
|
|
|
|
packet >> pid_to_blame;
|
|
|
|
packet >> frame;
|
2016-02-02 15:35:27 +00:00
|
|
|
|
|
|
|
std::string player = "??";
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
{
|
|
|
|
auto it = m_players.find(pid_to_blame);
|
2016-02-02 15:35:27 +00:00
|
|
|
if (it != m_players.end())
|
|
|
|
player = it->second.name;
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
2016-02-02 15:35:27 +00:00
|
|
|
m_dialog->OnDesync(frame, player);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NP_MSG_SYNC_GC_SRAM:
|
|
|
|
{
|
|
|
|
u8 sram[sizeof(g_SRAM.p_SRAM)];
|
|
|
|
for (size_t i = 0; i < sizeof(g_SRAM.p_SRAM); ++i)
|
|
|
|
{
|
|
|
|
packet >> sram[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
|
|
|
memcpy(g_SRAM.p_SRAM, sram, sizeof(g_SRAM.p_SRAM));
|
|
|
|
g_SRAM_netplay_initialized = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2016-07-13 22:45:38 +00:00
|
|
|
case NP_MSG_COMPUTE_MD5:
|
|
|
|
{
|
|
|
|
std::string file_identifier;
|
|
|
|
packet >> file_identifier;
|
|
|
|
|
|
|
|
ComputeMD5(file_identifier);
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
default:
|
|
|
|
PanicAlertT("Unknown message received with id : %d", mid);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
2015-02-02 09:27:06 +00:00
|
|
|
void NetPlayClient::Send(sf::Packet& packet)
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
ENetPacket* epac =
|
|
|
|
enet_packet_create(packet.getData(), packet.getDataSize(), ENET_PACKET_FLAG_RELIABLE);
|
|
|
|
enet_peer_send(m_server, 0, epac);
|
2015-02-02 09:27:06 +00:00
|
|
|
}
|
|
|
|
|
2016-02-02 15:35:27 +00:00
|
|
|
void NetPlayClient::DisplayPlayersPing()
|
|
|
|
{
|
|
|
|
if (!g_ActiveConfig.bShowNetPlayPing)
|
|
|
|
return;
|
|
|
|
|
|
|
|
OSD::AddTypedMessage(OSD::MessageType::NetPlayPing,
|
|
|
|
StringFromFormat("Ping: %u", GetPlayersMaxPing()), OSD::Duration::SHORT,
|
|
|
|
OSD::Color::CYAN);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 09:27:06 +00:00
|
|
|
void NetPlayClient::Disconnect()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
ENetEvent netEvent;
|
|
|
|
m_connecting = false;
|
|
|
|
m_connection_state = ConnectionState::Failure;
|
|
|
|
if (m_server)
|
|
|
|
enet_peer_disconnect(m_server, 0);
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
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;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// didn't disconnect gracefully force disconnect
|
|
|
|
enet_peer_reset(m_server);
|
|
|
|
m_server = nullptr;
|
2015-02-02 09:27:06 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 00:58:27 +00:00
|
|
|
void NetPlayClient::SendAsync(sf::Packet&& packet)
|
2015-03-09 16:31:13 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkq(m_crit.async_queue_write);
|
|
|
|
m_async_queue.Push(std::move(packet));
|
|
|
|
}
|
|
|
|
ENetUtil::WakeupThread(m_client);
|
2015-03-09 16:31:13 +00: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
|
|
|
{
|
2016-08-05 14:04:39 +00:00
|
|
|
while (m_do_loop.IsSet())
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
|
|
|
ENetEvent netEvent;
|
|
|
|
int net;
|
|
|
|
if (m_traversal_client)
|
|
|
|
m_traversal_client->HandleResends();
|
|
|
|
net = enet_host_service(m_client, &netEvent, 250);
|
|
|
|
while (!m_async_queue.Empty())
|
|
|
|
{
|
2017-02-10 00:58:27 +00:00
|
|
|
Send(m_async_queue.Front());
|
2016-06-24 08:43:46 +00:00
|
|
|
m_async_queue.Pop();
|
|
|
|
}
|
|
|
|
if (net > 0)
|
|
|
|
{
|
|
|
|
sf::Packet rpac;
|
|
|
|
switch (netEvent.type)
|
|
|
|
{
|
|
|
|
case ENET_EVENT_TYPE_RECEIVE:
|
|
|
|
rpac.append(netEvent.packet->data, netEvent.packet->dataLength);
|
|
|
|
OnData(rpac);
|
|
|
|
|
|
|
|
enet_packet_destroy(netEvent.packet);
|
|
|
|
break;
|
|
|
|
case ENET_EVENT_TYPE_DISCONNECT:
|
2016-02-02 15:35:27 +00:00
|
|
|
m_dialog->OnConnectionLost();
|
|
|
|
|
2016-08-05 14:04:39 +00:00
|
|
|
if (m_is_running.IsSet())
|
2016-02-02 15:35:27 +00:00
|
|
|
StopGame();
|
2016-06-24 08:43:46 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Disconnect();
|
|
|
|
return;
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
|
|
|
|
std::ostringstream ss;
|
|
|
|
|
|
|
|
const auto enumerate_player_controller_mappings = [&ss](const PadMappingArray& mappings,
|
|
|
|
const Player& player) {
|
|
|
|
for (size_t i = 0; i < mappings.size(); i++)
|
|
|
|
{
|
|
|
|
if (mappings[i] == player.pid)
|
|
|
|
ss << i + 1;
|
|
|
|
else
|
|
|
|
ss << '-';
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const auto& entry : m_players)
|
|
|
|
{
|
|
|
|
const Player& player = entry.second;
|
|
|
|
ss << player.name << "[" << static_cast<int>(player.pid) << "] : " << player.revision << " | ";
|
|
|
|
|
|
|
|
enumerate_player_controller_mappings(m_pad_map, player);
|
|
|
|
enumerate_player_controller_mappings(m_wiimote_map, player);
|
|
|
|
|
2016-07-10 08:13:34 +00:00
|
|
|
ss << " |\nPing: " << player.ping << "ms\n";
|
|
|
|
ss << "Status: ";
|
|
|
|
|
|
|
|
switch (player.game_status)
|
|
|
|
{
|
|
|
|
case PlayerGameStatus::Ok:
|
|
|
|
ss << "ready";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PlayerGameStatus::NotFound:
|
|
|
|
ss << "game missing";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ss << "unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ss << "\n\n";
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
pid_list.push_back(player.pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
list = ss.str();
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
2013-08-18 13:43:01 +00:00
|
|
|
// called from ---GUI--- thread
|
2015-08-16 04:58:15 +00:00
|
|
|
std::vector<const Player*> NetPlayClient::GetPlayers()
|
2013-08-18 13:43:01 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
std::vector<const Player*> players;
|
2015-08-16 04:58:15 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
for (const auto& pair : m_players)
|
|
|
|
players.push_back(&pair.second);
|
2015-08-16 04:58:15 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
return players;
|
2013-08-18 13:43:01 +00:00
|
|
|
}
|
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---GUI--- thread
|
|
|
|
void NetPlayClient::SendChatMessage(const std::string& msg)
|
|
|
|
{
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_CHAT_MESSAGE);
|
|
|
|
packet << msg;
|
2016-01-25 03:10:38 +00:00
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
SendAsync(std::move(packet));
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---CPU--- thread
|
2016-10-07 11:49:32 +00:00
|
|
|
void NetPlayClient::SendPadState(const int in_game_pad, const GCPadStatus& pad)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_PAD_DATA);
|
|
|
|
packet << static_cast<PadMapping>(in_game_pad);
|
|
|
|
packet << pad.button << pad.analogA << pad.analogB << pad.stickX << pad.stickY << pad.substickX
|
|
|
|
<< pad.substickY << pad.triggerLeft << pad.triggerRight;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
SendAsync(std::move(packet));
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
|
|
|
|
2013-08-07 03:48:52 +00:00
|
|
|
// called from ---CPU--- thread
|
2016-10-07 11:49:32 +00:00
|
|
|
void NetPlayClient::SendWiimoteState(const int in_game_pad, const NetWiimote& nw)
|
2013-08-07 03:48:52 +00:00
|
|
|
{
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_WIIMOTE_DATA);
|
|
|
|
packet << static_cast<PadMapping>(in_game_pad);
|
|
|
|
packet << static_cast<u8>(nw.size());
|
2016-06-24 08:43:46 +00:00
|
|
|
for (auto it : nw)
|
|
|
|
{
|
2017-02-28 22:33:54 +00:00
|
|
|
packet << it;
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
SendAsync(std::move(packet));
|
2016-01-25 03:10:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---GUI--- thread
|
|
|
|
void NetPlayClient::SendStartGamePacket()
|
|
|
|
{
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_START_GAME);
|
|
|
|
packet << m_current_game;
|
2016-01-25 03:10:38 +00:00
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
SendAsync(std::move(packet));
|
2016-01-25 03:10:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---GUI--- thread
|
|
|
|
void NetPlayClient::SendStopGamePacket()
|
|
|
|
{
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_STOP_GAME);
|
2016-01-25 03:10:38 +00:00
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
SendAsync(std::move(packet));
|
2013-08-07 03:48:52 +00:00
|
|
|
}
|
|
|
|
|
2010-05-05 04:44:19 +00:00
|
|
|
// called from ---GUI--- thread
|
2016-06-24 08:43:46 +00:00
|
|
|
bool NetPlayClient::StartGame(const std::string& path)
|
2010-05-05 04:44:19 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
|
|
|
|
SendStartGamePacket();
|
|
|
|
|
2016-08-05 14:04:39 +00:00
|
|
|
if (m_is_running.IsSet())
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
|
|
|
PanicAlertT("Game is already running!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_timebase_frame = 0;
|
|
|
|
|
2016-08-05 14:04:39 +00:00
|
|
|
m_is_running.Set();
|
2016-06-24 08:43:46 +00:00
|
|
|
NetPlay_Enable(this);
|
|
|
|
|
|
|
|
ClearBuffers();
|
|
|
|
|
|
|
|
if (m_dialog->IsRecording())
|
|
|
|
{
|
|
|
|
if (Movie::IsReadOnly())
|
|
|
|
Movie::SetReadOnly(false);
|
|
|
|
|
|
|
|
u8 controllers_mask = 0;
|
|
|
|
for (unsigned int i = 0; i < 4; ++i)
|
|
|
|
{
|
|
|
|
if (m_pad_map[i] > 0)
|
|
|
|
controllers_mask |= (1 << i);
|
|
|
|
if (m_wiimote_map[i] > 0)
|
|
|
|
controllers_mask |= (1 << (i + 4));
|
|
|
|
}
|
|
|
|
Movie::BeginRecordingInput(controllers_mask);
|
|
|
|
}
|
|
|
|
|
2017-02-22 21:50:05 +00:00
|
|
|
for (unsigned int i = 0; i < 4; ++i)
|
|
|
|
WiimoteReal::ChangeWiimoteSource(i, m_wiimote_map[i] > 0 ? WIIMOTE_SRC_EMU : WIIMOTE_SRC_NONE);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2017-02-22 21:50:05 +00:00
|
|
|
// boot game
|
2016-06-24 08:43:46 +00:00
|
|
|
m_dialog->BootGame(path);
|
|
|
|
|
|
|
|
UpdateDevices();
|
|
|
|
|
|
|
|
return true;
|
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
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
return true;
|
2010-05-05 04:44:19 +00:00
|
|
|
}
|
2013-08-05 09:05:06 +00:00
|
|
|
|
2013-08-24 00:24:45 +00:00
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
void NetPlayClient::UpdateDevices()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
u8 local_pad = 0;
|
|
|
|
u8 pad = 0;
|
|
|
|
|
|
|
|
for (auto player_id : m_pad_map)
|
|
|
|
{
|
|
|
|
// Use local controller types for local controllers if they are compatible
|
|
|
|
// Only GCController-like controllers are supported, GBA and similar
|
|
|
|
// exotic devices are not supported on netplay.
|
|
|
|
if (player_id == m_local_player->pid)
|
|
|
|
{
|
|
|
|
if (SIDevice_IsGCController(SConfig::GetInstance().m_SIDevice[local_pad]))
|
|
|
|
{
|
|
|
|
SerialInterface::AddDevice(SConfig::GetInstance().m_SIDevice[local_pad], pad);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SerialInterface::AddDevice(SIDEVICE_GC_CONTROLLER, pad);
|
|
|
|
}
|
|
|
|
local_pad++;
|
|
|
|
}
|
|
|
|
else if (player_id > 0)
|
|
|
|
{
|
|
|
|
SerialInterface::AddDevice(SIDEVICE_GC_CONTROLLER, pad);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SerialInterface::AddDevice(SIDEVICE_NONE, pad);
|
|
|
|
}
|
|
|
|
pad++;
|
|
|
|
}
|
2013-08-24 00:24:45 +00:00
|
|
|
}
|
|
|
|
|
2013-08-05 09:05:06 +00:00
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
void NetPlayClient::ClearBuffers()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
// clear pad buffers, Clear method isn't thread safe
|
|
|
|
for (unsigned int i = 0; i < 4; ++i)
|
|
|
|
{
|
|
|
|
while (m_pad_buffer[i].Size())
|
|
|
|
m_pad_buffer[i].Pop();
|
|
|
|
|
|
|
|
while (m_wiimote_buffer[i].Size())
|
|
|
|
m_wiimote_buffer[i].Pop();
|
|
|
|
}
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
2015-02-02 09:56:53 +00:00
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
void NetPlayClient::OnTraversalStateChanged()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
if (m_connection_state == ConnectionState::WaitingForTraversalClientConnection &&
|
|
|
|
m_traversal_client->m_State == TraversalClient::Connected)
|
|
|
|
{
|
|
|
|
m_connection_state = ConnectionState::WaitingForTraversalClientConnectReady;
|
|
|
|
m_traversal_client->ConnectToClient(m_host_spec);
|
|
|
|
}
|
|
|
|
else if (m_connection_state != ConnectionState::Failure &&
|
|
|
|
m_traversal_client->m_State == TraversalClient::Failure)
|
|
|
|
{
|
|
|
|
Disconnect();
|
2016-02-02 15:35:27 +00:00
|
|
|
m_dialog->OnTraversalError(m_traversal_client->m_FailureReason);
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
2015-02-02 09:56:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
void NetPlayClient::OnConnectReady(ENetAddress addr)
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
if (m_connection_state == ConnectionState::WaitingForTraversalClientConnectReady)
|
|
|
|
{
|
|
|
|
m_connection_state = ConnectionState::Connecting;
|
|
|
|
enet_host_connect(m_client, &addr, 0, 0);
|
|
|
|
}
|
2015-02-02 09:56:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---NETPLAY--- thread
|
|
|
|
void NetPlayClient::OnConnectFailed(u8 reason)
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
m_connecting = false;
|
|
|
|
m_connection_state = ConnectionState::Failure;
|
|
|
|
switch (reason)
|
|
|
|
{
|
|
|
|
case TraversalConnectFailedClientDidntRespond:
|
|
|
|
PanicAlertT("Traversal server timed out connecting to the host");
|
|
|
|
break;
|
|
|
|
case TraversalConnectFailedClientFailure:
|
|
|
|
PanicAlertT("Server rejected traversal attempt");
|
|
|
|
break;
|
|
|
|
case TraversalConnectFailedNoSuchClient:
|
|
|
|
PanicAlertT("Invalid host");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PanicAlertT("Unknown error %x", reason);
|
|
|
|
break;
|
|
|
|
}
|
2015-02-02 09:56:53 +00:00
|
|
|
}
|
|
|
|
|
2013-08-05 09:05:06 +00:00
|
|
|
// called from ---CPU--- thread
|
2016-10-07 11:49:32 +00:00
|
|
|
bool NetPlayClient::GetNetPads(const int pad_nb, GCPadStatus* pad_status)
|
2013-08-05 09:05:06 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
// The interface for this is extremely silly.
|
|
|
|
//
|
|
|
|
// Imagine a physical device that links three GameCubes together
|
|
|
|
// and emulates NetPlay that way. Which GameCube controls which
|
|
|
|
// in-game controllers can be configured on the device (m_pad_map)
|
|
|
|
// but which sockets on each individual GameCube should be used
|
|
|
|
// to control which players? The solution that Dolphin uses is
|
|
|
|
// that we hardcode the knowledge that they go in order, so if
|
|
|
|
// you have a 3P game with three GameCubes, then every single
|
|
|
|
// controller should be plugged into slot 1.
|
|
|
|
//
|
|
|
|
// If you have a 4P game, then one of the GameCubes will have
|
|
|
|
// a controller plugged into slot 1, and another in slot 2.
|
|
|
|
//
|
|
|
|
// The slot number is the "local" pad number, and what player
|
|
|
|
// it actually means is the "in-game" pad number.
|
|
|
|
|
|
|
|
// When the 1st in-game pad is polled, we assume the others will
|
|
|
|
// will be polled as well. To reduce latency, we poll all local
|
|
|
|
// controllers at once and then send the status to the other
|
|
|
|
// clients.
|
|
|
|
if (IsFirstInGamePad(pad_nb))
|
|
|
|
{
|
2016-10-07 11:49:32 +00:00
|
|
|
const int num_local_pads = NumLocalPads();
|
|
|
|
for (int local_pad = 0; local_pad < num_local_pads; local_pad++)
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
|
|
|
switch (SConfig::GetInstance().m_SIDevice[local_pad])
|
|
|
|
{
|
|
|
|
case SIDEVICE_WIIU_ADAPTER:
|
2016-08-02 01:16:00 +00:00
|
|
|
*pad_status = GCAdapter::Input(local_pad);
|
2016-06-24 08:43:46 +00:00
|
|
|
break;
|
|
|
|
case SIDEVICE_GC_CONTROLLER:
|
|
|
|
default:
|
2016-08-02 00:30:03 +00:00
|
|
|
*pad_status = Pad::GetStatus(local_pad);
|
2016-06-24 08:43:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-10-07 11:49:32 +00:00
|
|
|
int ingame_pad = LocalPadToInGamePad(local_pad);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
// send
|
|
|
|
SendPadState(ingame_pad, *pad_status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now, we either use the data pushed earlier, or wait for the
|
|
|
|
// other clients to send it to us
|
2016-07-24 22:40:15 +00:00
|
|
|
while (m_pad_buffer[pad_nb].Size() == 0)
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
2016-08-05 14:04:39 +00:00
|
|
|
if (!m_is_running.IsSet())
|
2016-07-24 22:40:15 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
return false;
|
2016-07-24 22:40:15 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2016-07-24 22:40:15 +00:00
|
|
|
m_gc_pad_event.Wait();
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
2016-07-24 22:40:15 +00:00
|
|
|
m_pad_buffer[pad_nb].Pop(*pad_status);
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
if (Movie::IsRecordingInput())
|
|
|
|
{
|
|
|
|
Movie::RecordInput(pad_status, pad_nb);
|
|
|
|
Movie::InputUpdate();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Movie::CheckPadStatus(pad_status, pad_nb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---CPU--- thread
|
2016-08-14 13:51:29 +00:00
|
|
|
bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size, u8 reporting_mode)
|
2013-08-05 09:05:06 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
NetWiimote nw;
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
|
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 10:46:23 +00:00
|
|
|
// Only send data, if this Wiimote is mapped to this player
|
|
|
|
if (m_wiimote_map[_number] == m_local_player->pid)
|
2016-06-24 08:43:46 +00: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 10:46:23 +00:00
|
|
|
nw.assign(data, data + size);
|
|
|
|
do
|
2016-06-24 08:43:46 +00: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 10:46:23 +00:00
|
|
|
// add to buffer
|
|
|
|
m_wiimote_buffer[_number].Push(nw);
|
2016-06-24 08:43:46 +00: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 10:46:23 +00:00
|
|
|
SendWiimoteState(_number, nw);
|
|
|
|
} while (m_wiimote_buffer[_number].Size() <=
|
|
|
|
m_target_buffer_size * 200 /
|
|
|
|
120); // TODO: add a seperate setting for wiimote buffer?
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // unlock players
|
|
|
|
|
2016-07-24 22:40:15 +00:00
|
|
|
while (m_wiimote_buffer[_number].Size() == 0)
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
2016-08-05 14:04:39 +00:00
|
|
|
if (!m_is_running.IsSet())
|
2016-07-24 22:40:15 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
return false;
|
2016-07-24 22:40:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// wait for receiving thread to push some data
|
|
|
|
m_wii_pad_event.Wait();
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
|
|
|
|
2016-07-24 22:40:15 +00: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 10:46:23 +00:00
|
|
|
// If the reporting mode has changed, we just need to pop through the buffer,
|
2016-06-24 08:43:46 +00:00
|
|
|
// until we reach a good input
|
2016-08-14 13:51:29 +00:00
|
|
|
if (nw[1] != reporting_mode)
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
2016-07-08 21:15:38 +00:00
|
|
|
u32 tries = 0;
|
2016-08-14 13:51:29 +00:00
|
|
|
while (nw[1] != reporting_mode)
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
2016-07-24 22:40:15 +00:00
|
|
|
while (m_wiimote_buffer[_number].Size() == 0)
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
2016-08-05 14:04:39 +00:00
|
|
|
if (!m_is_running.IsSet())
|
2016-07-24 22:40:15 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
return false;
|
2016-07-24 22:40:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// wait for receiving thread to push some data
|
|
|
|
m_wii_pad_event.Wait();
|
2016-06-24 08:43:46 +00:00
|
|
|
}
|
2016-07-24 22:40:15 +00:00
|
|
|
|
|
|
|
m_wiimote_buffer[_number].Pop(nw);
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
++tries;
|
|
|
|
if (tries > m_target_buffer_size * 200 / 120)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it still mismatches, it surely desynced
|
2016-08-14 13:51:29 +00:00
|
|
|
if (nw[1] != reporting_mode)
|
2016-06-24 08:43:46 +00:00
|
|
|
{
|
|
|
|
PanicAlertT("Netplay has desynced. There is no way to recover from this.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data, nw.data(), size);
|
|
|
|
return true;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---GUI--- thread and ---NETPLAY--- thread (client side)
|
|
|
|
bool NetPlayClient::StopGame()
|
|
|
|
{
|
2016-08-05 14:04:39 +00:00
|
|
|
m_is_running.Clear();
|
2016-07-24 22:40:15 +00:00
|
|
|
|
|
|
|
// stop waiting for input
|
|
|
|
m_gc_pad_event.Set();
|
|
|
|
m_wii_pad_event.Set();
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
NetPlay_Disable();
|
|
|
|
|
|
|
|
// stop game
|
|
|
|
m_dialog->StopGame();
|
|
|
|
|
|
|
|
return true;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
2015-03-09 16:31:13 +00:00
|
|
|
// called from ---GUI--- thread
|
2013-09-03 01:54:28 +00:00
|
|
|
void NetPlayClient::Stop()
|
|
|
|
{
|
2016-08-05 14:04:39 +00:00
|
|
|
if (!m_is_running.IsSet())
|
2016-06-24 08:43:46 +00:00
|
|
|
return;
|
2015-05-21 23:52:26 +00:00
|
|
|
|
2016-08-05 14:04:39 +00:00
|
|
|
m_is_running.Clear();
|
2016-07-24 22:40:15 +00:00
|
|
|
|
|
|
|
// stop waiting for input
|
|
|
|
m_gc_pad_event.Set();
|
|
|
|
m_wii_pad_event.Set();
|
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
// Tell the server to stop if we have a pad mapped in game.
|
|
|
|
if (LocalPlayerHasControllerMapped())
|
|
|
|
SendStopGamePacket();
|
2013-09-03 01:54:28 +00:00
|
|
|
}
|
|
|
|
|
2016-01-25 03:46:37 +00:00
|
|
|
// called from ---GUI--- thread
|
|
|
|
bool NetPlayClient::LocalPlayerHasControllerMapped() const
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
const auto mapping_matches_player_id = [this](const PadMapping& mapping) {
|
|
|
|
return mapping == m_local_player->pid;
|
|
|
|
};
|
2016-01-25 03:46:37 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
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);
|
2016-01-25 03:46:37 +00:00
|
|
|
}
|
|
|
|
|
2016-10-07 11:49:32 +00:00
|
|
|
bool NetPlayClient::IsFirstInGamePad(int ingame_pad) const
|
2016-05-08 13:29:01 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
return std::none_of(m_pad_map.begin(), m_pad_map.begin() + ingame_pad,
|
|
|
|
[](auto mapping) { return mapping > 0; });
|
2016-05-08 13:29:01 +00:00
|
|
|
}
|
|
|
|
|
2016-10-07 11:49:32 +00:00
|
|
|
int NetPlayClient::NumLocalPads() const
|
2016-05-08 13:29:01 +00:00
|
|
|
{
|
2016-10-07 11:49:32 +00:00
|
|
|
return static_cast<int>(std::count_if(m_pad_map.begin(), m_pad_map.end(), [this](auto mapping) {
|
2016-06-24 08:43:46 +00:00
|
|
|
return mapping == m_local_player->pid;
|
|
|
|
}));
|
2016-05-08 13:29:01 +00:00
|
|
|
}
|
|
|
|
|
2016-10-07 11:49:32 +00:00
|
|
|
int NetPlayClient::InGamePadToLocalPad(int ingame_pad)
|
2013-09-09 07:09:45 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
// not our pad
|
|
|
|
if (m_pad_map[ingame_pad] != m_local_player->pid)
|
|
|
|
return 4;
|
2013-09-09 07:09:45 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
int local_pad = 0;
|
|
|
|
int pad = 0;
|
2013-09-09 07:09:45 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
for (; pad < ingame_pad; pad++)
|
|
|
|
{
|
|
|
|
if (m_pad_map[pad] == m_local_player->pid)
|
|
|
|
local_pad++;
|
|
|
|
}
|
2013-09-09 07:09:45 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
return local_pad;
|
2013-09-09 07:09:45 +00:00
|
|
|
}
|
|
|
|
|
2016-10-07 11:49:32 +00:00
|
|
|
int NetPlayClient::LocalPadToInGamePad(int local_pad)
|
2013-08-05 09:05:06 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00: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++;
|
|
|
|
|
|
|
|
if (local_pad_count == local_pad)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ingame_pad;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
2015-03-08 10:50:47 +00:00
|
|
|
void NetPlayClient::SendTimeBase()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
2015-03-08 10:50:47 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
u64 timebase = SystemTimers::GetFakeTimeBase();
|
2015-03-08 10:50:47 +00:00
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_TIMEBASE);
|
|
|
|
packet << static_cast<u32>(timebase);
|
|
|
|
packet << static_cast<u32>(timebase << 32);
|
|
|
|
packet << netplay_client->m_timebase_frame++;
|
2016-01-25 03:10:38 +00:00
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
netplay_client->SendAsync(std::move(packet));
|
2015-03-08 10:50:47 +00:00
|
|
|
}
|
|
|
|
|
2016-07-10 08:13:34 +00:00
|
|
|
bool NetPlayClient::DoAllPlayersHaveGame()
|
|
|
|
{
|
|
|
|
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
|
|
|
|
|
|
|
|
return std::all_of(std::begin(m_players), std::end(m_players),
|
|
|
|
[](auto entry) { return entry.second.game_status == PlayerGameStatus::Ok; });
|
|
|
|
}
|
|
|
|
|
2016-07-13 22:45:38 +00:00
|
|
|
void NetPlayClient::ComputeMD5(const std::string& file_identifier)
|
|
|
|
{
|
|
|
|
if (m_should_compute_MD5)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_dialog->ShowMD5Dialog(file_identifier);
|
|
|
|
m_should_compute_MD5 = true;
|
|
|
|
|
|
|
|
std::string file;
|
2016-07-16 19:40:19 +00:00
|
|
|
if (file_identifier == WII_SDCARD)
|
|
|
|
file = File::GetUserPath(F_WIISDCARD_IDX);
|
2016-07-13 22:45:38 +00:00
|
|
|
else
|
|
|
|
file = m_dialog->FindGame(file_identifier);
|
|
|
|
|
|
|
|
if (file.empty() || !File::Exists(file))
|
|
|
|
{
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_MD5_ERROR);
|
|
|
|
packet << "file not found";
|
|
|
|
Send(packet);
|
2016-07-13 22:45:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_MD5_thread = std::thread([this, file]() {
|
|
|
|
std::string sum = MD5::MD5Sum(file, [&](int progress) {
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_MD5_PROGRESS);
|
|
|
|
packet << progress;
|
|
|
|
Send(packet);
|
2016-07-13 22:45:38 +00:00
|
|
|
|
|
|
|
return m_should_compute_MD5;
|
|
|
|
});
|
|
|
|
|
2017-02-28 22:33:54 +00:00
|
|
|
sf::Packet packet;
|
|
|
|
packet << static_cast<MessageId>(NP_MSG_MD5_RESULT);
|
|
|
|
packet << sum;
|
|
|
|
Send(packet);
|
2016-07-13 22:45:38 +00:00
|
|
|
});
|
|
|
|
m_MD5_thread.detach();
|
|
|
|
}
|
|
|
|
|
2013-08-05 09:05:06 +00:00
|
|
|
// stuff hacked into dolphin
|
|
|
|
|
|
|
|
// called from ---CPU--- thread
|
|
|
|
// Actual Core function which is called on every frame
|
2016-10-07 11:49:32 +00:00
|
|
|
bool CSIDevice_GCController::NetPlay_GetInput(int numPAD, GCPadStatus* PadStatus)
|
2013-08-05 09:05:06 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
2013-08-05 09:05:06 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
if (netplay_client)
|
|
|
|
return netplay_client->GetNetPads(numPAD, PadStatus);
|
|
|
|
else
|
|
|
|
return false;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
2016-08-14 13:51:29 +00:00
|
|
|
bool WiimoteEmu::Wiimote::NetPlay_GetWiimoteData(int wiimote, u8* data, u8 size, u8 reporting_mode)
|
2013-08-07 03:48:52 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
2013-08-07 03:48:52 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
if (netplay_client)
|
2016-08-14 13:51:29 +00:00
|
|
|
return netplay_client->WiimoteUpdate(wiimote, data, size, reporting_mode);
|
2016-06-24 08:43:46 +00:00
|
|
|
else
|
|
|
|
return false;
|
2013-08-07 03:48:52 +00:00
|
|
|
}
|
|
|
|
|
2013-08-05 09:05:06 +00:00
|
|
|
// called from ---CPU--- thread
|
|
|
|
// so all players' games get the same time
|
2016-07-11 14:49:58 +00:00
|
|
|
//
|
|
|
|
// also called from ---GUI--- thread when starting input recording
|
2016-10-29 12:01:00 +00:00
|
|
|
u64 CEXIIPL::NetPlay_GetEmulatedTime()
|
2013-08-05 09:05:06 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
2013-08-05 09:05:06 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
if (netplay_client)
|
2016-10-29 12:01:00 +00:00
|
|
|
return g_netplay_initial_rtc;
|
2016-06-24 08:43:46 +00:00
|
|
|
else
|
|
|
|
return 0;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called from ---CPU--- thread
|
|
|
|
// return the local pad num that should rumble given a ingame pad num
|
2016-10-07 11:49:32 +00:00
|
|
|
int CSIDevice_GCController::NetPlay_InGamePadToLocalPad(int numPAD)
|
2013-08-05 09:05:06 +00:00
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
2013-08-05 09:05:06 +00:00
|
|
|
|
2016-06-24 08:43:46 +00:00
|
|
|
if (netplay_client)
|
|
|
|
return netplay_client->InGamePadToLocalPad(numPAD);
|
|
|
|
else
|
|
|
|
return numPAD;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool NetPlay::IsNetPlayRunning()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
return netplay_client != nullptr;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlay_Enable(NetPlayClient* const np)
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
|
|
|
netplay_client = np;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NetPlay_Disable()
|
|
|
|
{
|
2016-06-24 08:43:46 +00:00
|
|
|
std::lock_guard<std::mutex> lk(crit_netplay_client);
|
|
|
|
netplay_client = nullptr;
|
2013-08-05 09:05:06 +00:00
|
|
|
}
|