Merge branch 'netplay'

This commit is contained in:
Jasper St. Pierre 2013-08-05 06:44:17 -04:00
commit 0d2083c670
14 changed files with 827 additions and 853 deletions

View file

@ -10,7 +10,6 @@ set(SRCS Src/ActionReplay.cpp
Src/GeckoCodeConfig.cpp Src/GeckoCodeConfig.cpp
Src/GeckoCode.cpp Src/GeckoCode.cpp
Src/Movie.cpp Src/Movie.cpp
Src/NetPlay.cpp
Src/NetPlayClient.cpp Src/NetPlayClient.cpp
Src/NetPlayServer.cpp Src/NetPlayServer.cpp
Src/PatchEngine.cpp Src/PatchEngine.cpp

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations"> <ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugFast|Win32"> <ProjectConfiguration Include="DebugFast|Win32">
@ -334,7 +334,6 @@
<ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_WiiMote.cpp" /> <ClCompile Include="Src\IPC_HLE\WII_IPC_HLE_WiiMote.cpp" />
<ClCompile Include="Src\x64MemTools.cpp" /> <ClCompile Include="Src\x64MemTools.cpp" />
<ClCompile Include="Src\Movie.cpp" /> <ClCompile Include="Src\Movie.cpp" />
<ClCompile Include="Src\NetPlay.cpp" />
<ClCompile Include="Src\NetPlayClient.cpp" /> <ClCompile Include="Src\NetPlayClient.cpp" />
<ClCompile Include="Src\NetPlayServer.cpp" /> <ClCompile Include="Src\NetPlayServer.cpp" />
<ClCompile Include="Src\PatchEngine.cpp" /> <ClCompile Include="Src\PatchEngine.cpp" />
@ -540,7 +539,6 @@
<ClInclude Include="Src\IPC_HLE\WII_IPC_HLE_WiiMote.h" /> <ClInclude Include="Src\IPC_HLE\WII_IPC_HLE_WiiMote.h" />
<ClInclude Include="Src\MemTools.h" /> <ClInclude Include="Src\MemTools.h" />
<ClInclude Include="Src\Movie.h" /> <ClInclude Include="Src\Movie.h" />
<ClInclude Include="Src\NetPlay.h" />
<ClInclude Include="Src\PatchEngine.h" /> <ClInclude Include="Src\PatchEngine.h" />
<ClInclude Include="Src\DSPEmulator.h" /> <ClInclude Include="Src\DSPEmulator.h" />
<ClInclude Include="Src\PowerPC\CPUCoreBase.h" /> <ClInclude Include="Src\PowerPC\CPUCoreBase.h" />
@ -599,4 +597,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>
</Project> </Project>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup> <ItemGroup>
<ClCompile Include="Src\ConfigManager.cpp" /> <ClCompile Include="Src\ConfigManager.cpp" />
@ -529,9 +529,6 @@
<ClCompile Include="Src\HW\Wiimote.cpp"> <ClCompile Include="Src\HW\Wiimote.cpp">
<Filter>HW %28Flipper/Hollywood%29\Wiimote</Filter> <Filter>HW %28Flipper/Hollywood%29\Wiimote</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Src\NetPlay.cpp">
<Filter>NetPlay</Filter>
</ClCompile>
<ClCompile Include="Src\NetPlayServer.cpp"> <ClCompile Include="Src\NetPlayServer.cpp">
<Filter>NetPlay</Filter> <Filter>NetPlay</Filter>
</ClCompile> </ClCompile>
@ -1014,9 +1011,6 @@
<ClInclude Include="Src\PowerPC\CPUCoreBase.h"> <ClInclude Include="Src\PowerPC\CPUCoreBase.h">
<Filter>PowerPC</Filter> <Filter>PowerPC</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Src\NetPlay.h">
<Filter>NetPlay</Filter>
</ClInclude>
<ClInclude Include="Src\BootManager.h" /> <ClInclude Include="Src\BootManager.h" />
<ClInclude Include="Src\FifoPlayer\FifoDataFile.h"> <ClInclude Include="Src\FifoPlayer\FifoDataFile.h">
<Filter>FifoPlayer</Filter> <Filter>FifoPlayer</Filter>
@ -1177,9 +1171,6 @@
<Filter Include="HW %28Flipper/Hollywood%29\Wiimote\Real"> <Filter Include="HW %28Flipper/Hollywood%29\Wiimote\Real">
<UniqueIdentifier>{1c21a3e1-b791-4a23-b0d5-ed2b2c34007f}</UniqueIdentifier> <UniqueIdentifier>{1c21a3e1-b791-4a23-b0d5-ed2b2c34007f}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="NetPlay">
<UniqueIdentifier>{231ceb02-1122-402a-87a8-094a9ed768c2}</UniqueIdentifier>
</Filter>
<Filter Include="FifoPlayer"> <Filter Include="FifoPlayer">
<UniqueIdentifier>{ca7d56f7-4e84-4d15-9aea-7ae6fa7d6586}</UniqueIdentifier> <UniqueIdentifier>{ca7d56f7-4e84-4d15-9aea-7ae6fa7d6586}</UniqueIdentifier>
</Filter> </Filter>
@ -1187,4 +1178,4 @@
<UniqueIdentifier>{3e9e6e83-c1bf-45f9-aeff-231f98f60d29}</UniqueIdentifier> <UniqueIdentifier>{3e9e6e83-c1bf-45f9-aeff-231f98f60d29}</UniqueIdentifier>
</Filter> </Filter>
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -34,7 +34,7 @@
#include "Host.h" #include "Host.h"
#include "VideoBackendBase.h" #include "VideoBackendBase.h"
#include "Movie.h" #include "Movie.h"
#include "NetPlay.h" #include "NetPlayClient.h"
namespace BootManager namespace BootManager
{ {
@ -138,7 +138,7 @@ bool BootCore(const std::string& _rFilename)
} }
} }
if (NetPlay::GetNetPlayPtr()) if (NetPlay::IsNetPlayRunning())
{ {
StartUp.bDSPHLE = g_NetPlaySettings.m_DSPHLE; StartUp.bDSPHLE = g_NetPlaySettings.m_DSPHLE;
StartUp.bEnableMemcardSaving = g_NetPlaySettings.m_WriteToMemcard; StartUp.bEnableMemcardSaving = g_NetPlaySettings.m_WriteToMemcard;

View file

@ -7,7 +7,7 @@
#include "../ConfigManager.h" #include "../ConfigManager.h"
#include "../CoreTiming.h" #include "../CoreTiming.h"
#include "../Movie.h" #include "../Movie.h"
#include "../NetPlay.h" #include "../NetPlayClient.h"
#include "SystemTimers.h" #include "SystemTimers.h"
#include "ProcessorInterface.h" #include "ProcessorInterface.h"
@ -262,7 +262,7 @@ void Init()
if (Movie::IsRecordingInput() || Movie::IsPlayingInput()) if (Movie::IsRecordingInput() || Movie::IsPlayingInput())
AddDevice(Movie::IsUsingPad(i) ? (Movie::IsUsingBongo(i) ? SIDEVICE_GC_TARUKONGA : SIDEVICE_GC_CONTROLLER) : SIDEVICE_NONE, i); AddDevice(Movie::IsUsingPad(i) ? (Movie::IsUsingBongo(i) ? SIDEVICE_GC_TARUKONGA : SIDEVICE_GC_CONTROLLER) : SIDEVICE_NONE, i);
else if (NetPlay::GetNetPlayPtr()) else if (NetPlay::IsNetPlayRunning())
AddDevice((SIDevices) g_NetPlaySettings.m_Controllers[i], i); AddDevice((SIDevices) g_NetPlaySettings.m_Controllers[i], i);
else else
AddDevice(SConfig::GetInstance().m_SIDevice[i], i); AddDevice(SConfig::GetInstance().m_SIDevice[i], i);
@ -644,7 +644,7 @@ void RunSIBuffer()
int GetTicksToNextSIPoll() int GetTicksToNextSIPoll()
{ {
// Poll for input at regular intervals (once per frame) when playing or recording a movie // Poll for input at regular intervals (once per frame) when playing or recording a movie
if (Movie::IsPlayingInput() || Movie::IsRecordingInput() || NetPlay::GetNetPlayPtr()) if (Movie::IsPlayingInput() || Movie::IsRecordingInput() || NetPlay::IsNetPlayRunning())
{ {
return SystemTimers::GetTicksPerSecond() / VideoInterface::TargetRefreshRate; return SystemTimers::GetTicksPerSecond() / VideoInterface::TargetRefreshRate;
} }

View file

@ -1,401 +0,0 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include "NetPlay.h"
// for wiimote
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
#include "IPC_HLE/WII_IPC_HLE_WiiMote.h"
// for gcpad
#include "HW/SI_DeviceGCController.h"
#include "HW/SI_DeviceGCSteeringWheel.h"
#include "HW/SI_DeviceDanceMat.h"
// for gctime
#include "HW/EXI_DeviceIPL.h"
// for wiimote/ OSD messages
#include "Core.h"
#include "ConfigManager.h"
std::mutex crit_netplay_ptr;
static NetPlay* netplay_ptr = NULL;
NetSettings g_NetPlaySettings;
#define RPT_SIZE_HACK (1 << 16)
// called from ---GUI--- thread
NetPlay::NetPlay(NetPlayUI* dialog)
: m_dialog(dialog), m_is_running(false), m_do_loop(true)
{
m_target_buffer_size = 20;
ClearBuffers();
}
void NetPlay_Enable(NetPlay* const np)
{
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
netplay_ptr = np;
}
void NetPlay_Disable()
{
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
netplay_ptr = NULL;
}
// called from ---GUI--- thread
NetPlay::~NetPlay()
{
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
netplay_ptr = NULL;
// not perfect
if (m_is_running)
StopGame();
}
NetPlay::Player::Player()
{
memset(pad_map, -1, sizeof(pad_map));
}
// called from ---GUI--- thread
std::string NetPlay::Player::ToString() const
{
std::ostringstream ss;
ss << name << '[' << (char)(pid+'0') << "] : " << revision << " |";
for (unsigned int i=0; i<4; ++i)
ss << (pad_map[i]>=0 ? (char)(pad_map[i]+'1') : '-');
ss << '|';
return ss.str();
}
NetPad::NetPad()
{
nHi = 0x00808080;
nLo = 0x80800000;
}
NetPad::NetPad(const SPADStatus* const pad_status)
{
nHi = (u32)((u8)pad_status->stickY);
nHi |= (u32)((u8)pad_status->stickX << 8);
nHi |= (u32)((u16)pad_status->button << 16);
nHi |= 0x00800000;
nLo = (u8)pad_status->triggerRight;
nLo |= (u32)((u8)pad_status->triggerLeft << 8);
nLo |= (u32)((u8)pad_status->substickY << 16);
nLo |= (u32)((u8)pad_status->substickX << 24);
}
// called from ---NETPLAY--- thread
void NetPlay::ClearBuffers()
{
// 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();
m_wiimote_input[i].clear();
}
}
// called from ---CPU--- thread
bool NetPlay::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
{
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
// in game mapping for this local pad
unsigned int in_game_num = m_local_player->pad_map[pad_nb];
// does this local pad map in game?
if (in_game_num < 4)
{
NetPad np(pad_status);
// adjust the buffer either up or down
// inserting multiple padstates or dropping states
while (m_pad_buffer[in_game_num].Size() <= m_target_buffer_size)
{
// add to buffer
m_pad_buffer[in_game_num].Push(np);
// send
SendPadState(pad_nb, np);
}
}
} // unlock players
//Common::Timer bufftimer;
//bufftimer.Start();
// get padstate from buffer and send to game
while (!m_pad_buffer[pad_nb].Pop(*netvalues))
{
// wait for receiving thread to push some data
Common::SleepCurrentThread(1);
if (false == m_is_running)
return false;
// TODO: check the time of bufftimer here,
// if it gets pretty high, ask the user if they want to disconnect
}
//u64 hangtime = bufftimer.GetTimeElapsed();
//if (hangtime > 10)
//{
// std::ostringstream ss;
// ss << "Pad " << (int)pad_nb << ": Had to wait " << hangtime << "ms for pad data. (increase pad Buffer maybe)";
// Core::DisplayMessage(ss.str(), 1000);
//}
return true;
}
// called from ---CPU--- thread
void NetPlay::WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size)
{
//// in game mapping for this local wiimote
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
// does this local pad map in game?
if (in_game_num < 4)
{
m_wiimote_input[_number].resize(m_wiimote_input[_number].size() + 1);
m_wiimote_input[_number].back().assign((char*)_pData, (char*)_pData + _Size);
m_wiimote_input[_number].back().channel = _channelID;
}
}
// called from ---CPU--- thread
void NetPlay::WiimoteUpdate(int _number)
{
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
// in game mapping for this local wiimote
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
// does this local pad map in game?
if (in_game_num < 4)
{
m_wiimote_buffer[in_game_num].Push(m_wiimote_input[_number]);
// TODO: send it
m_wiimote_input[_number].clear();
}
} // unlock players
if (0 == m_wiimote_buffer[_number].Size())
{
//PanicAlert("PANIC");
return;
}
NetWiimote nw;
m_wiimote_buffer[_number].Pop(nw);
NetWiimote::const_iterator
i = nw.begin(), e = nw.end();
for ( ; i!=e; ++i)
Core::Callback_WiimoteInterruptChannel(_number, i->channel, &(*i)[0], (u32)i->size() + RPT_SIZE_HACK);
}
// called from ---GUI--- thread
bool NetPlay::StartGame(const std::string &path)
{
if (m_is_running)
{
PanicAlertT("Game is already running!");
return false;
}
m_dialog->AppendChat(" -- STARTING GAME -- ");
m_is_running = true;
NetPlay_Enable(this);
ClearBuffers();
// boot game
m_dialog->BootGame(path);
// temporary
NetWiimote nw;
for (unsigned int i = 0; i<4; ++i)
for (unsigned int f = 0; f<2; ++f)
m_wiimote_buffer[i].Push(nw);
return true;
}
// called from ---GUI--- thread and ---NETPLAY--- thread (client side)
bool NetPlay::StopGame()
{
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
if (false == m_is_running)
{
PanicAlertT("Game isn't running!");
return false;
}
m_dialog->AppendChat(" -- STOPPING GAME -- ");
m_is_running = false;
NetPlay_Disable();
// stop game
m_dialog->StopGame();
return true;
}
void NetPlay::SetMemcardWriteEnabled(bool enabled)
{
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
g_NetPlaySettings.m_WriteToMemcard = enabled;
}
// called from ---CPU--- thread
u8 NetPlay::GetPadNum(u8 numPAD)
{
// TODO: i don't like that this loop is running everytime there is rumble
unsigned int i = 0;
for (; i<4; ++i)
if (numPAD == m_local_player->pad_map[i])
break;
return i;
}
void NetPlay::GetNetSettings()
{
SConfig &instance = SConfig::GetInstance();
g_NetPlaySettings.m_DSPHLE = instance.m_LocalCoreStartupParameter.bDSPHLE;
g_NetPlaySettings.m_DSPEnableJIT = instance.m_EnableJIT;
for (unsigned int i = 0; i < 4; ++i)
g_NetPlaySettings.m_Controllers[i] = SConfig::GetInstance().m_SIDevice[i];
}
// stuff hacked into dolphin
// called from ---CPU--- thread
// Actual Core function which is called on every frame
bool CSIDevice_GCController::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
{
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
if (netplay_ptr)
return netplay_ptr->GetNetPads(numPAD, &PadStatus, (NetPad*)PADStatus);
else
return false;
}
bool CSIDevice_GCSteeringWheel::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
{
return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus);
}
bool CSIDevice_DanceMat::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
{
return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus);
}
// called from ---CPU--- thread
// so all players' games get the same time
u32 CEXIIPL::NetPlay_GetGCTime()
{
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
if (netplay_ptr)
return 1272737767; // watev
else
return 0;
}
// called from ---CPU--- thread
// return the local pad num that should rumble given a ingame pad num
u8 CSIDevice_GCController::NetPlay_GetPadNum(u8 numPAD)
{
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
if (netplay_ptr)
return netplay_ptr->GetPadNum(numPAD);
else
return numPAD;
}
u8 CSIDevice_GCSteeringWheel::NetPlay_GetPadNum(u8 numPAD)
{
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
}
u8 CSIDevice_DanceMat::NetPlay_GetPadNum(u8 numPAD)
{
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
}
// called from ---CPU--- thread
// wiimote update / used for frame counting
//void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int _number)
void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int)
{
//CritLocker crit(crit_netplay_ptr);
//if (netplay_ptr)
// netplay_ptr->WiimoteUpdate(_number);
}
// called from ---CPU--- thread
//
int CWII_IPC_HLE_WiiMote::NetPlay_GetWiimoteNum(int _number)
{
//CritLocker crit(crit_netplay_ptr);
//if (netplay_ptr)
// return netplay_ptr->GetPadNum(_number); // just using gcpad mapping for now
//else
return _number;
}
// called from ---CPU--- thread
// intercept wiimote input callback
//bool CWII_IPC_HLE_WiiMote::NetPlay_WiimoteInput(int _number, u16 _channelID, const void* _pData, u32& _Size)
bool CWII_IPC_HLE_WiiMote::NetPlay_WiimoteInput(int, u16, const void*, u32&)
{
std::lock_guard<std::mutex> lk(crit_netplay_ptr);
if (netplay_ptr)
//{
// if (_Size >= RPT_SIZE_HACK)
// {
// _Size -= RPT_SIZE_HACK;
// return false;
// }
// else
// {
// netplay_ptr->WiimoteInput(_number, _channelID, _pData, _Size);
// // don't use this packet
return true;
// }
//}
else
return false;
}
NetPlay* NetPlay::GetNetPlayPtr()
{
return netplay_ptr;
}

View file

@ -1,275 +0,0 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _NETPLAY_H
#define _NETPLAY_H
#include "Common.h"
#include "CommonTypes.h"
#include "Thread.h"
#include "Timer.h"
#include <SFML/Network.hpp>
#include "GCPadStatus.h"
#include <functional>
#include <map>
#include <queue>
#include <sstream>
#include "FifoQueue.h"
class NetPad
{
public:
NetPad();
NetPad(const SPADStatus* const);
u32 nHi;
u32 nLo;
};
struct NetSettings
{
bool m_DSPHLE;
bool m_DSPEnableJIT;
bool m_WriteToMemcard;
u8 m_Controllers[4];
};
extern NetSettings g_NetPlaySettings;
struct Rpt : public std::vector<u8>
{
u16 channel;
};
typedef std::vector<Rpt> NetWiimote;
#define NETPLAY_VERSION "Dolphin NetPlay 2013-07-22"
// messages
enum
{
NP_MSG_PLAYER_JOIN = 0x10,
NP_MSG_PLAYER_LEAVE = 0x11,
NP_MSG_CHAT_MESSAGE = 0x30,
NP_MSG_PAD_DATA = 0x60,
NP_MSG_PAD_MAPPING = 0x61,
NP_MSG_PAD_BUFFER = 0x62,
NP_MSG_WIIMOTE_DATA = 0x70,
NP_MSG_WIIMOTE_MAPPING = 0x71, // just using pad mapping for now
NP_MSG_START_GAME = 0xA0,
NP_MSG_CHANGE_GAME = 0xA1,
NP_MSG_STOP_GAME = 0xA2,
NP_MSG_DISABLE_GAME = 0xA3,
NP_MSG_READY = 0xD0,
NP_MSG_NOT_READY = 0xD1,
NP_MSG_PING = 0xE0,
NP_MSG_PONG = 0xE1,
};
typedef u8 MessageId;
typedef u8 PlayerId;
typedef s8 PadMapping;
typedef u32 FrameNum;
enum
{
CON_ERR_SERVER_FULL = 1,
CON_ERR_GAME_RUNNING,
CON_ERR_VERSION_MISMATCH
};
class NetPlayUI
{
public:
virtual ~NetPlayUI() {};
virtual void BootGame(const std::string& filename) = 0;
virtual void StopGame() = 0;
virtual void Update() = 0;
virtual void AppendChat(const std::string& msg) = 0;
virtual void OnMsgChangeGame(const std::string& filename) = 0;
virtual void OnMsgStartGame() = 0;
virtual void OnMsgStopGame() = 0;
};
class NetPlay
{
public:
NetPlay(NetPlayUI* _dialog);
virtual ~NetPlay();
//virtual void ThreadFunc() = 0;
bool is_connected;
// Send and receive pads values
void WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size);
void WiimoteUpdate(int _number);
bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
virtual bool ChangeGame(const std::string& game) = 0;
virtual void GetPlayerList(std::string& list, std::vector<int>& pid_list) = 0;
virtual void SendChatMessage(const std::string& msg) = 0;
virtual bool StartGame(const std::string &path);
virtual bool StopGame();
virtual void SetMemcardWriteEnabled(bool enabled);
//void PushPadStates(unsigned int count);
u8 GetPadNum(u8 numPAD);
static NetPlay* GetNetPlayPtr();
protected:
//void GetBufferedPad(const u8 pad_nb, NetPad* const netvalues);
void ClearBuffers();
void GetNetSettings();
virtual void SendPadState(const PadMapping local_nb, const NetPad& np) = 0;
struct
{
std::recursive_mutex game;
// lock order
std::recursive_mutex players, send;
} m_crit;
class Player
{
public:
Player();
std::string ToString() const;
PlayerId pid;
std::string name;
PadMapping pad_map[4];
std::string revision;
};
Common::FifoQueue<NetPad> m_pad_buffer[4];
Common::FifoQueue<NetWiimote> m_wiimote_buffer[4];
NetWiimote m_wiimote_input[4];
NetPlayUI* m_dialog;
sf::SocketTCP m_socket;
std::thread m_thread;
sf::Selector<sf::SocketTCP> m_selector;
std::string m_selected_game;
volatile bool m_is_running;
volatile bool m_do_loop;
unsigned int m_target_buffer_size;
Player* m_local_player;
u32 m_current_game;
};
void NetPlay_Enable(NetPlay* const np);
void NetPlay_Disable();
class NetPlayServer : public NetPlay
{
public:
void ThreadFunc();
NetPlayServer(const u16 port, const std::string& name, NetPlayUI* dialog);
~NetPlayServer();
void GetPlayerList(std::string& list, std::vector<int>& pid_list);
// Send and receive pads values
//bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
bool ChangeGame(const std::string& game);
void SendChatMessage(const std::string& msg);
bool StartGame(const std::string &path);
bool StopGame();
bool GetPadMapping(const int pid, int map[]);
bool SetPadMapping(const int pid, const int map[]);
u64 CalculateMinimumBufferTime();
void AdjustPadBufferSize(unsigned int size);
#ifdef USE_UPNP
void TryPortmapping(u16 port);
#endif
private:
class Client : public Player
{
public:
Client() : ping(0), current_game(0) {}
sf::SocketTCP socket;
u64 ping;
u32 current_game;
};
void SendPadState(const PadMapping local_nb, const NetPad& np);
void SendToClients(sf::Packet& packet, const PlayerId skip_pid = 0);
unsigned int OnConnect(sf::SocketTCP& socket);
unsigned int OnDisconnect(sf::SocketTCP& socket);
unsigned int OnData(sf::Packet& packet, sf::SocketTCP& socket);
void UpdatePadMapping();
std::map<sf::SocketTCP, Client> m_players;
Common::Timer m_ping_timer;
u32 m_ping_key;
bool m_update_pings;
#ifdef USE_UPNP
static void mapPortThread(const u16 port);
static void unmapPortThread();
static bool initUPnP();
static bool UPnPMapPort(const std::string& addr, const u16 port);
static bool UPnPUnmapPort(const u16 port);
static struct UPNPUrls m_upnp_urls;
static struct IGDdatas m_upnp_data;
static u16 m_upnp_mapped;
static bool m_upnp_inited;
static bool m_upnp_error;
static std::thread m_upnp_thread;
#endif
};
class NetPlayClient : public NetPlay
{
public:
void ThreadFunc();
NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name);
~NetPlayClient();
void GetPlayerList(std::string& list, std::vector<int>& pid_list);
// Send and receive pads values
//bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
bool StartGame(const std::string &path);
bool ChangeGame(const std::string& game);
void SendChatMessage(const std::string& msg);
private:
void SendPadState(const PadMapping local_nb, const NetPad& np);
unsigned int OnData(sf::Packet& packet);
PlayerId m_pid;
std::map<PlayerId, Player> m_players;
};
#endif

View file

@ -2,21 +2,81 @@
// Licensed under GPLv2 // Licensed under GPLv2
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "NetPlay.h" #include "NetPlayClient.h"
// for wiimote
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
#include "IPC_HLE/WII_IPC_HLE_WiiMote.h"
// for gcpad
#include "HW/SI_DeviceGCController.h"
#include "HW/SI_DeviceGCSteeringWheel.h"
#include "HW/SI_DeviceDanceMat.h"
// for gctime
#include "HW/EXI_DeviceIPL.h"
// for wiimote/ OSD messages
#include "Core.h"
#include "ConfigManager.h"
std::mutex crit_netplay_client;
static NetPlayClient * netplay_client = NULL;
NetSettings g_NetPlaySettings;
#define RPT_SIZE_HACK (1 << 16)
NetPlayClient::Player::Player()
{
memset(pad_map, -1, sizeof(pad_map));
}
// called from ---GUI--- thread
std::string NetPlayClient::Player::ToString() const
{
std::ostringstream ss;
ss << name << '[' << (char)(pid+'0') << "] : " << revision << " |";
for (unsigned int i=0; i<4; ++i)
ss << (pad_map[i]>=0 ? (char)(pad_map[i]+'1') : '-');
ss << " | " << ping << "ms";
return ss.str();
}
NetPad::NetPad()
{
nHi = 0x00808080;
nLo = 0x80800000;
}
NetPad::NetPad(const SPADStatus* const pad_status)
{
nHi = (u32)((u8)pad_status->stickY);
nHi |= (u32)((u8)pad_status->stickX << 8);
nHi |= (u32)((u16)pad_status->button << 16);
nHi |= 0x00800000;
nLo = (u8)pad_status->triggerRight;
nLo |= (u32)((u8)pad_status->triggerLeft << 8);
nLo |= (u32)((u8)pad_status->substickY << 16);
nLo |= (u32)((u8)pad_status->substickX << 24);
}
// called from ---GUI--- thread // called from ---GUI--- thread
NetPlayClient::~NetPlayClient() NetPlayClient::~NetPlayClient()
{ {
// not perfect
if (m_is_running)
StopGame();
if (is_connected) if (is_connected)
{ {
m_do_loop = false; m_do_loop = false;
m_thread.join(); m_thread.join();
} }
} }
// called from ---GUI--- thread // called from ---GUI--- thread
NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name) : NetPlay(dialog) NetPlayClient::NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name) : m_dialog(dialog), m_is_running(false), m_do_loop(true)
{ {
m_target_buffer_size = 20;
ClearBuffers();
is_connected = false; is_connected = false;
// why is false successful? documentation says true is // why is false successful? documentation says true is
@ -233,6 +293,21 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
} }
break; 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;
}
m_dialog->Update();
}
break;
default : default :
PanicAlertT("Unknown message received with id : %d", mid); PanicAlertT("Unknown message received with id : %d", mid);
break; break;
@ -328,12 +403,31 @@ bool NetPlayClient::StartGame(const std::string &path)
spac << m_current_game; spac << m_current_game;
spac << (char *)&g_NetPlaySettings; spac << (char *)&g_NetPlaySettings;
if (false == NetPlay::StartGame(path))
return false;
std::lock_guard<std::recursive_mutex> lks(m_crit.send); std::lock_guard<std::recursive_mutex> lks(m_crit.send);
m_socket.Send(spac); m_socket.Send(spac);
if (m_is_running)
{
PanicAlertT("Game is already running!");
return false;
}
m_dialog->AppendChat(" -- STARTING GAME -- ");
m_is_running = true;
NetPlay_Enable(this);
ClearBuffers();
// boot game
m_dialog->BootGame(path);
// temporary
NetWiimote nw;
for (unsigned int i = 0; i<4; ++i)
for (unsigned int f = 0; f<2; ++f)
m_wiimote_buffer[i].Push(nw);
return true; return true;
} }
@ -342,3 +436,283 @@ bool NetPlayClient::ChangeGame(const std::string&)
{ {
return true; return true;
} }
// called from ---NETPLAY--- thread
void NetPlayClient::ClearBuffers()
{
// 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();
m_wiimote_input[i].clear();
}
}
// called from ---CPU--- thread
bool NetPlayClient::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
{
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
// in game mapping for this local pad
unsigned int in_game_num = m_local_player->pad_map[pad_nb];
// does this local pad map in game?
if (in_game_num < 4)
{
NetPad np(pad_status);
// adjust the buffer either up or down
// inserting multiple padstates or dropping states
while (m_pad_buffer[in_game_num].Size() <= m_target_buffer_size)
{
// add to buffer
m_pad_buffer[in_game_num].Push(np);
// send
SendPadState(pad_nb, np);
}
}
} // unlock players
//Common::Timer bufftimer;
//bufftimer.Start();
// get padstate from buffer and send to game
while (!m_pad_buffer[pad_nb].Pop(*netvalues))
{
// wait for receiving thread to push some data
Common::SleepCurrentThread(1);
if (false == m_is_running)
return false;
// TODO: check the time of bufftimer here,
// if it gets pretty high, ask the user if they want to disconnect
}
//u64 hangtime = bufftimer.GetTimeElapsed();
//if (hangtime > 10)
//{
// std::ostringstream ss;
// ss << "Pad " << (int)pad_nb << ": Had to wait " << hangtime << "ms for pad data. (increase pad Buffer maybe)";
// Core::DisplayMessage(ss.str(), 1000);
//}
return true;
}
// called from ---CPU--- thread
void NetPlayClient::WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size)
{
//// in game mapping for this local wiimote
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
// does this local pad map in game?
if (in_game_num < 4)
{
m_wiimote_input[_number].resize(m_wiimote_input[_number].size() + 1);
m_wiimote_input[_number].back().assign((char*)_pData, (char*)_pData + _Size);
m_wiimote_input[_number].back().channel = _channelID;
}
}
// called from ---CPU--- thread
void NetPlayClient::WiimoteUpdate(int _number)
{
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
// in game mapping for this local wiimote
unsigned int in_game_num = m_local_player->pad_map[_number]; // just using gc pad_map for now
// does this local pad map in game?
if (in_game_num < 4)
{
m_wiimote_buffer[in_game_num].Push(m_wiimote_input[_number]);
// TODO: send it
m_wiimote_input[_number].clear();
}
} // unlock players
if (0 == m_wiimote_buffer[_number].Size())
{
//PanicAlert("PANIC");
return;
}
NetWiimote nw;
m_wiimote_buffer[_number].Pop(nw);
NetWiimote::const_iterator
i = nw.begin(), e = nw.end();
for ( ; i!=e; ++i)
Core::Callback_WiimoteInterruptChannel(_number, i->channel, &(*i)[0], (u32)i->size() + RPT_SIZE_HACK);
}
// called from ---GUI--- thread and ---NETPLAY--- thread (client side)
bool NetPlayClient::StopGame()
{
std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
if (false == m_is_running)
{
PanicAlertT("Game isn't running!");
return false;
}
m_dialog->AppendChat(" -- STOPPING GAME -- ");
m_is_running = false;
NetPlay_Disable();
// stop game
m_dialog->StopGame();
return true;
}
// called from ---CPU--- thread
u8 NetPlayClient::GetPadNum(u8 numPAD)
{
// TODO: i don't like that this loop is running everytime there is rumble
unsigned int i = 0;
for (; i<4; ++i)
if (numPAD == m_local_player->pad_map[i])
break;
return i;
}
// stuff hacked into dolphin
// called from ---CPU--- thread
// Actual Core function which is called on every frame
bool CSIDevice_GCController::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
{
std::lock_guard<std::mutex> lk(crit_netplay_client);
if (netplay_client)
return netplay_client->GetNetPads(numPAD, &PadStatus, (NetPad*)PADStatus);
else
return false;
}
bool CSIDevice_GCSteeringWheel::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
{
return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus);
}
bool CSIDevice_DanceMat::NetPlay_GetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
{
return CSIDevice_GCController::NetPlay_GetInput(numPAD, PadStatus, PADStatus);
}
// called from ---CPU--- thread
// so all players' games get the same time
u32 CEXIIPL::NetPlay_GetGCTime()
{
std::lock_guard<std::mutex> lk(crit_netplay_client);
if (netplay_client)
return 1272737767; // watev
else
return 0;
}
// called from ---CPU--- thread
// return the local pad num that should rumble given a ingame pad num
u8 CSIDevice_GCController::NetPlay_GetPadNum(u8 numPAD)
{
std::lock_guard<std::mutex> lk(crit_netplay_client);
if (netplay_client)
return netplay_client->GetPadNum(numPAD);
else
return numPAD;
}
u8 CSIDevice_GCSteeringWheel::NetPlay_GetPadNum(u8 numPAD)
{
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
}
u8 CSIDevice_DanceMat::NetPlay_GetPadNum(u8 numPAD)
{
return CSIDevice_GCController::NetPlay_GetPadNum(numPAD);
}
// called from ---CPU--- thread
// wiimote update / used for frame counting
//void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int _number)
void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int)
{
//CritLocker crit(crit_netplay_client);
//if (netplay_client)
// netplay_client->WiimoteUpdate(_number);
}
// called from ---CPU--- thread
//
int CWII_IPC_HLE_WiiMote::NetPlay_GetWiimoteNum(int _number)
{
//CritLocker crit(crit_netplay_client);
//if (netplay_client)
// return netplay_client->GetPadNum(_number); // just using gcpad mapping for now
//else
return _number;
}
// called from ---CPU--- thread
// intercept wiimote input callback
//bool CWII_IPC_HLE_WiiMote::NetPlay_WiimoteInput(int _number, u16 _channelID, const void* _pData, u32& _Size)
bool CWII_IPC_HLE_WiiMote::NetPlay_WiimoteInput(int, u16, const void*, u32&)
{
std::lock_guard<std::mutex> lk(crit_netplay_client);
if (netplay_client)
//{
// if (_Size >= RPT_SIZE_HACK)
// {
// _Size -= RPT_SIZE_HACK;
// return false;
// }
// else
// {
// netplay_client->WiimoteInput(_number, _channelID, _pData, _Size);
// // don't use this packet
return true;
// }
//}
else
return false;
}
bool NetPlay::IsNetPlayRunning()
{
return netplay_client != NULL;
}
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 = NULL;
}

View file

@ -0,0 +1,135 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _NETPLAY_H
#define _NETPLAY_H
#include "Common.h"
#include "CommonTypes.h"
#include "Thread.h"
#include "Timer.h"
#include <SFML/Network.hpp>
#include "NetPlayProto.h"
#include "GCPadStatus.h"
#include <functional>
#include <map>
#include <queue>
#include <sstream>
#include "FifoQueue.h"
class NetPad
{
public:
NetPad();
NetPad(const SPADStatus* const);
u32 nHi;
u32 nLo;
};
class NetPlayUI
{
public:
virtual ~NetPlayUI() {};
virtual void BootGame(const std::string& filename) = 0;
virtual void StopGame() = 0;
virtual void Update() = 0;
virtual void AppendChat(const std::string& msg) = 0;
virtual void OnMsgChangeGame(const std::string& filename) = 0;
virtual void OnMsgStartGame() = 0;
virtual void OnMsgStopGame() = 0;
};
extern NetSettings g_NetPlaySettings;
class NetPlayClient
{
public:
void ThreadFunc();
NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, const std::string& name);
~NetPlayClient();
void GetPlayerList(std::string& list, std::vector<int>& pid_list);
bool is_connected;
bool StartGame(const std::string &path);
bool StopGame();
bool ChangeGame(const std::string& game);
void SendChatMessage(const std::string& msg);
// Send and receive pads values
void WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size);
void WiimoteUpdate(int _number);
bool GetNetPads(const u8 pad_nb, const SPADStatus* const, NetPad* const netvalues);
u8 GetPadNum(u8 numPAD);
protected:
void ClearBuffers();
struct
{
std::recursive_mutex game;
// lock order
std::recursive_mutex players, send;
} m_crit;
class Player
{
public:
Player();
std::string ToString() const;
PlayerId pid;
std::string name;
PadMapping pad_map[4];
std::string revision;
u32 ping;
};
Common::FifoQueue<NetPad> m_pad_buffer[4];
Common::FifoQueue<NetWiimote> m_wiimote_buffer[4];
NetWiimote m_wiimote_input[4];
NetPlayUI* m_dialog;
sf::SocketTCP m_socket;
std::thread m_thread;
sf::Selector<sf::SocketTCP> m_selector;
std::string m_selected_game;
volatile bool m_is_running;
volatile bool m_do_loop;
unsigned int m_target_buffer_size;
Player* m_local_player;
u32 m_current_game;
private:
void SendPadState(const PadMapping local_nb, const NetPad& np);
unsigned int OnData(sf::Packet& packet);
PlayerId m_pid;
std::map<PlayerId, Player> m_players;
};
namespace NetPlay {
bool IsNetPlayRunning();
};
void NetPlay_Enable(NetPlayClient* const np);
void NetPlay_Disable();
#endif

View file

@ -0,0 +1,68 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _NETPLAY_PROTO_H
#define _NETPLAY_PROTO_H
#include "Common.h"
#include "CommonTypes.h"
struct NetSettings
{
bool m_DSPHLE;
bool m_DSPEnableJIT;
bool m_WriteToMemcard;
u8 m_Controllers[4];
};
struct Rpt : public std::vector<u8>
{
u16 channel;
};
typedef std::vector<Rpt> NetWiimote;
#define NETPLAY_VERSION "Dolphin NetPlay 2013-08-05"
// messages
enum
{
NP_MSG_PLAYER_JOIN = 0x10,
NP_MSG_PLAYER_LEAVE = 0x11,
NP_MSG_CHAT_MESSAGE = 0x30,
NP_MSG_PAD_DATA = 0x60,
NP_MSG_PAD_MAPPING = 0x61,
NP_MSG_PAD_BUFFER = 0x62,
NP_MSG_WIIMOTE_DATA = 0x70,
NP_MSG_WIIMOTE_MAPPING = 0x71, // just using pad mapping for now
NP_MSG_START_GAME = 0xA0,
NP_MSG_CHANGE_GAME = 0xA1,
NP_MSG_STOP_GAME = 0xA2,
NP_MSG_DISABLE_GAME = 0xA3,
NP_MSG_READY = 0xD0,
NP_MSG_NOT_READY = 0xD1,
NP_MSG_PING = 0xE0,
NP_MSG_PONG = 0xE1,
NP_MSG_PLAYER_PING_DATA = 0xE2,
};
typedef u8 MessageId;
typedef u8 PlayerId;
typedef s8 PadMapping;
typedef u32 FrameNum;
enum
{
CON_ERR_SERVER_FULL = 1,
CON_ERR_GAME_RUNNING,
CON_ERR_VERSION_MISMATCH
};
#endif

View file

@ -2,15 +2,31 @@
// Licensed under GPLv2 // Licensed under GPLv2
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "NetPlay.h" #include "NetPlayServer.h"
NetPlayServer::Client::Client()
{
memset(pad_map, -1, sizeof(pad_map));
}
// called from ---GUI--- thread // called from ---GUI--- thread
std::string NetPlayServer::Client::ToString() const
{
std::ostringstream ss;
ss << name << '[' << (char)(pid+'0') << "] : " << revision << " |";
for (unsigned int i=0; i<4; ++i)
ss << (pad_map[i]>=0 ? (char)(pad_map[i]+'1') : '-');
ss << '|';
return ss.str();
}
NetPlayServer::~NetPlayServer() NetPlayServer::~NetPlayServer()
{ {
if (is_connected) if (is_connected)
{ {
m_do_loop = false; m_do_loop = false;
m_thread.join(); m_thread.join();
m_socket.Close();
} }
#ifdef USE_UPNP #ifdef USE_UPNP
@ -22,35 +38,15 @@ NetPlayServer::~NetPlayServer()
} }
// called from ---GUI--- thread // called from ---GUI--- thread
NetPlayServer::NetPlayServer(const u16 port, const std::string& name, NetPlayUI* dialog) : NetPlay(dialog) NetPlayServer::NetPlayServer(const u16 port) : is_connected(false), m_is_running(false)
{ {
m_update_pings = true;
if (m_socket.Listen(port)) if (m_socket.Listen(port))
{ {
Client player;
player.pid = 0;
player.revision = netplay_dolphin_ver;
player.socket = m_socket;
player.name = name;
// map local pad 1 to game pad 1
player.pad_map[0] = 0;
// add self to player list
m_players[m_socket] = player;
m_local_player = &m_players[m_socket];
//PanicAlertT("Listening");
m_dialog->Update();
is_connected = true; is_connected = true;
m_do_loop = true;
m_selector.Add(m_socket); m_selector.Add(m_socket);
m_thread = std::thread(std::mem_fun(&NetPlayServer::ThreadFunc), this); m_thread = std::thread(std::mem_fun(&NetPlayServer::ThreadFunc), this);
} }
else
is_connected = false;
} }
// called from ---NETPLAY--- thread // called from ---NETPLAY--- thread
@ -258,8 +254,6 @@ unsigned int NetPlayServer::OnConnect(sf::SocketTCP& socket)
// add client to selector/ used for receiving // add client to selector/ used for receiving
m_selector.Add(socket); m_selector.Add(socket);
m_dialog->Update();
return 0; return 0;
} }
@ -271,7 +265,6 @@ unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP& socket)
PanicAlertT("Client disconnect while game is running!! NetPlay is disabled. You must manually stop the game."); PanicAlertT("Client disconnect while game is running!! NetPlay is disabled. You must manually stop the game.");
std::lock_guard<std::recursive_mutex> lkg(m_crit.game); std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
m_is_running = false; m_is_running = false;
NetPlay_Disable();
sf::Packet spac; sf::Packet spac;
spac << (MessageId)NP_MSG_DISABLE_GAME; spac << (MessageId)NP_MSG_DISABLE_GAME;
@ -293,8 +286,6 @@ unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP& socket)
std::lock_guard<std::recursive_mutex> lks(m_crit.send); std::lock_guard<std::recursive_mutex> lks(m_crit.send);
SendToClients(spac); SendToClients(spac);
m_dialog->Update();
return 0; return 0;
} }
@ -357,8 +348,6 @@ bool NetPlayServer::SetPadMapping(const int pid, const int map[])
std::lock_guard<std::recursive_mutex> lks(m_crit.send); std::lock_guard<std::recursive_mutex> lks(m_crit.send);
UpdatePadMapping(); // sync pad mappings with everyone UpdatePadMapping(); // sync pad mappings with everyone
m_dialog->Update();
return true; return true;
} }
@ -380,29 +369,6 @@ void NetPlayServer::UpdatePadMapping()
} }
// called from ---GUI--- thread and ---NETPLAY--- thread
u64 NetPlayServer::CalculateMinimumBufferTime()
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
std::map<sf::SocketTCP, Client>::const_iterator
i = m_players.begin(),
e = m_players.end();
std::priority_queue<unsigned int> pings;
for ( ;i!=e; ++i)
pings.push(i->second.ping/2);
unsigned int required_ms = pings.top();
// if there is more than 1 client, buffersize must be >= (2 highest ping times combined)
if (pings.size() > 1)
{
pings.pop();
required_ms += pings.top();
}
return required_ms;
}
// called from ---GUI--- thread and ---NETPLAY--- thread // called from ---GUI--- thread and ---NETPLAY--- thread
void NetPlayServer::AdjustPadBufferSize(unsigned int size) void NetPlayServer::AdjustPadBufferSize(unsigned int size)
{ {
@ -447,12 +413,6 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
std::lock_guard<std::recursive_mutex> lks(m_crit.send); std::lock_guard<std::recursive_mutex> lks(m_crit.send);
SendToClients(spac, player.pid); SendToClients(spac, player.pid);
} }
// add to gui
std::ostringstream ss;
ss << player.name << '[' << (char)(player.pid+'0') << "]: " << msg;
m_dialog->AppendChat(ss.str());
} }
break; break;
@ -463,8 +423,8 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
break; break;
PadMapping map = 0; PadMapping map = 0;
NetPad np; int hi, lo;
packet >> map >> np.nHi >> np.nLo; packet >> map >> hi >> lo;
// check if client's pad indeed maps in game // check if client's pad indeed maps in game
if (map >= 0 && map < 4) if (map >= 0 && map < 4)
@ -477,14 +437,12 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
if (map < 0) if (map < 0)
return 1; return 1;
// add to pad buffer
m_pad_buffer[(unsigned)map].Push(np);
// relay to clients // relay to clients
sf::Packet spac; sf::Packet spac;
spac << (MessageId)NP_MSG_PAD_DATA; spac << (MessageId)NP_MSG_PAD_DATA;
spac << map; // in game mapping spac << map; // in game mapping
spac << np.nHi << np.nLo; spac << hi << lo;
std::lock_guard<std::recursive_mutex> lks(m_crit.send); std::lock_guard<std::recursive_mutex> lks(m_crit.send);
SendToClients(spac, player.pid); SendToClients(spac, player.pid);
@ -498,11 +456,15 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
packet >> ping_key; packet >> ping_key;
if (m_ping_key == ping_key) if (m_ping_key == ping_key)
{
//PanicAlertT("Good pong");
player.ping = ping; player.ping = ping;
}
m_dialog->Update(); sf::Packet spac;
spac << (MessageId)NP_MSG_PLAYER_PING_DATA;
spac << player.pid;
spac << player.ping;
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
SendToClients(spac);
} }
break; break;
@ -573,29 +535,16 @@ bool NetPlayServer::ChangeGame(const std::string &game)
return true; return true;
} }
// called from ---CPU--- thread // called from ---GUI--- thread
void NetPlayServer::SendPadState(const PadMapping local_nb, const NetPad& np) void NetPlayServer::SetNetSettings(const NetSettings &settings)
{ {
// send to server m_settings = settings;
sf::Packet spac;
spac << (MessageId)NP_MSG_PAD_DATA;
spac << m_local_player->pad_map[local_nb]; // in-game pad num
spac << np.nHi << np.nLo;
std::lock_guard<std::recursive_mutex> lks(m_crit.send);
SendToClients(spac);
} }
// called from ---GUI--- thread // called from ---GUI--- thread
bool NetPlayServer::StartGame(const std::string &path) bool NetPlayServer::StartGame(const std::string &path)
{ {
std::lock_guard<std::recursive_mutex> lkg(m_crit.game); std::lock_guard<std::recursive_mutex> lkg(m_crit.game);
GetNetSettings();
if (false == NetPlay::StartGame(path))
return false;
// TODO: i dont like this here
m_current_game = Common::Timer::GetTimeMs(); m_current_game = Common::Timer::GetTimeMs();
// no change, just update with clients // no change, just update with clients
@ -604,17 +553,19 @@ bool NetPlayServer::StartGame(const std::string &path)
// tell clients to start game // tell clients to start game
sf::Packet spac; sf::Packet spac;
spac << (MessageId)NP_MSG_START_GAME; spac << (MessageId)NP_MSG_START_GAME;
spac << NetPlay::m_current_game; spac << m_current_game;
spac << g_NetPlaySettings.m_DSPEnableJIT; spac << m_settings.m_DSPEnableJIT;
spac << g_NetPlaySettings.m_DSPHLE; spac << m_settings.m_DSPHLE;
spac << g_NetPlaySettings.m_WriteToMemcard; spac << m_settings.m_WriteToMemcard;
for (unsigned int i = 0; i < 4; ++i) for (unsigned int i = 0; i < 4; ++i)
spac << g_NetPlaySettings.m_Controllers[i]; spac << m_settings.m_Controllers[i];
std::lock_guard<std::recursive_mutex> lkp(m_crit.players); std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
std::lock_guard<std::recursive_mutex> lks(m_crit.send); std::lock_guard<std::recursive_mutex> lks(m_crit.send);
SendToClients(spac); SendToClients(spac);
m_is_running = true;
return true; return true;
} }
@ -622,9 +573,6 @@ bool NetPlayServer::StartGame(const std::string &path)
// called from ---GUI--- thread // called from ---GUI--- thread
bool NetPlayServer::StopGame() bool NetPlayServer::StopGame()
{ {
if (false == NetPlay::StopGame())
return false;
// tell clients to stop game // tell clients to stop game
sf::Packet spac; sf::Packet spac;
spac << (MessageId)NP_MSG_STOP_GAME; spac << (MessageId)NP_MSG_STOP_GAME;

View file

@ -0,0 +1,116 @@
// Copyright 2013 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#ifndef _NETPLAY_SERVER_H
#define _NETPLAY_SERVER_H
#include "Common.h"
#include "CommonTypes.h"
#include "Thread.h"
#include "Timer.h"
#include <SFML/Network.hpp>
#include "NetPlayProto.h"
#include <functional>
#include <map>
#include <queue>
#include <sstream>
class NetPlayServer
{
public:
void ThreadFunc();
NetPlayServer(const u16 port);
~NetPlayServer();
void GetPlayerList(std::string& list, std::vector<int>& pid_list);
bool ChangeGame(const std::string& game);
void SendChatMessage(const std::string& msg);
void SetNetSettings(const NetSettings &settings);
bool StartGame(const std::string &path);
bool StopGame();
bool GetPadMapping(const int pid, int map[]);
bool SetPadMapping(const int pid, const int map[]);
void AdjustPadBufferSize(unsigned int size);
bool is_connected;
#ifdef USE_UPNP
void TryPortmapping(u16 port);
#endif
private:
class Client
{
public:
Client();
std::string ToString() const;
PlayerId pid;
std::string name;
PadMapping pad_map[4];
std::string revision;
sf::SocketTCP socket;
u32 ping;
u32 current_game;
};
void SendToClients(sf::Packet& packet, const PlayerId skip_pid = 0);
unsigned int OnConnect(sf::SocketTCP& socket);
unsigned int OnDisconnect(sf::SocketTCP& socket);
unsigned int OnData(sf::Packet& packet, sf::SocketTCP& socket);
void UpdatePadMapping();
NetSettings m_settings;
bool m_is_running;
bool m_do_loop;
Common::Timer m_ping_timer;
u32 m_ping_key;
bool m_update_pings;
u32 m_current_game;
unsigned int m_target_buffer_size;
std::map<sf::SocketTCP, Client> m_players;
struct
{
std::recursive_mutex game;
// lock order
std::recursive_mutex players, send;
} m_crit;
std::string m_selected_game;
sf::SocketTCP m_socket;
std::thread m_thread;
sf::Selector<sf::SocketTCP> m_selector;
#ifdef USE_UPNP
static void mapPortThread(const u16 port);
static void unmapPortThread();
static bool initUPnP();
static bool UPnPMapPort(const std::string& addr, const u16 port);
static bool UPnPUnmapPort(const u16 port);
static struct UPNPUrls m_upnp_urls;
static struct IGDdatas m_upnp_data;
static u16 m_upnp_mapped;
static bool m_upnp_inited;
static bool m_upnp_error;
static std::thread m_upnp_thread;
#endif
};
#endif

View file

@ -6,10 +6,12 @@
#include <IniFile.h> #include <IniFile.h>
#include "WxUtils.h" #include "WxUtils.h"
#include "NetPlay.h" #include "NetPlayClient.h"
#include "NetPlayServer.h"
#include "NetWindow.h" #include "NetWindow.h"
#include "Frame.h" #include "Frame.h"
#include "Core.h" #include "Core.h"
#include "ConfigManager.h"
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -20,7 +22,8 @@ BEGIN_EVENT_TABLE(NetPlayDiag, wxFrame)
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, NetPlayDiag::OnThread) EVT_COMMAND(wxID_ANY, wxEVT_THREAD, NetPlayDiag::OnThread)
END_EVENT_TABLE() END_EVENT_TABLE()
static NetPlay* netplay_ptr = NULL; static NetPlayServer* netplay_server = NULL;
static NetPlayClient* netplay_client = NULL;
extern CFrame* main_frame; extern CFrame* main_frame;
NetPlayDiag *NetPlayDiag::npd = NULL; NetPlayDiag *NetPlayDiag::npd = NULL;
@ -200,6 +203,28 @@ NetPlaySetupDiag::~NetPlaySetupDiag()
main_frame->g_NetPlaySetupDiag = NULL; main_frame->g_NetPlaySetupDiag = NULL;
} }
void NetPlaySetupDiag::MakeNetPlayDiag(int port, const std::string &game, bool is_hosting)
{
NetPlayDiag *&npd = NetPlayDiag::GetInstance();
std::string ip;
npd = new NetPlayDiag(m_parent, m_game_list, game, is_hosting);
if (is_hosting)
ip = "127.0.0.1";
else
ip = WxStrToStr(m_connect_ip_text->GetValue());
netplay_client = new NetPlayClient(ip, (u16)port, npd, WxStrToStr(m_nickname_text->GetValue()));
if (netplay_client->is_connected)
{
npd->Show();
Destroy();
}
else
{
npd->Destroy();
}
}
void NetPlaySetupDiag::OnHost(wxCommandEvent&) void NetPlaySetupDiag::OnHost(wxCommandEvent&)
{ {
NetPlayDiag *&npd = NetPlayDiag::GetInstance(); NetPlayDiag *&npd = NetPlayDiag::GetInstance();
@ -217,25 +242,19 @@ void NetPlaySetupDiag::OnHost(wxCommandEvent&)
std::string game(WxStrToStr(m_game_lbox->GetStringSelection())); std::string game(WxStrToStr(m_game_lbox->GetStringSelection()));
npd = new NetPlayDiag(m_parent, m_game_list, game, true);
unsigned long port = 0; unsigned long port = 0;
m_host_port_text->GetValue().ToULong(&port); m_host_port_text->GetValue().ToULong(&port);
netplay_ptr = new NetPlayServer(u16(port), WxStrToStr(m_nickname_text->GetValue()), npd); netplay_server = new NetPlayServer(u16(port));
netplay_ptr->ChangeGame(game); netplay_server->ChangeGame(game);
if (netplay_ptr->is_connected) if (netplay_server->is_connected)
{ {
#ifdef USE_UPNP #ifdef USE_UPNP
if(m_upnp_chk->GetValue()) if(m_upnp_chk->GetValue())
((NetPlayServer*)netplay_ptr)->TryPortmapping(port); netplay_server->TryPortmapping(port);
#endif #endif
npd->Show();
Destroy();
}
else
{
PanicAlertT("Failed to Listen!!");
npd->Destroy();
} }
MakeNetPlayDiag(port, game, true);
} }
void NetPlaySetupDiag::OnJoin(wxCommandEvent&) void NetPlaySetupDiag::OnJoin(wxCommandEvent&)
@ -247,20 +266,9 @@ void NetPlaySetupDiag::OnJoin(wxCommandEvent&)
return; return;
} }
npd = new NetPlayDiag(m_parent, m_game_list, "");
unsigned long port = 0; unsigned long port = 0;
m_connect_port_text->GetValue().ToULong(&port); m_connect_port_text->GetValue().ToULong(&port);
netplay_ptr = new NetPlayClient(WxStrToStr(m_connect_ip_text->GetValue()) MakeNetPlayDiag(port, "", false);
, (u16)port, npd, WxStrToStr(m_nickname_text->GetValue()));
if (netplay_ptr->is_connected)
{
npd->Show();
Destroy();
}
else
{
npd->Destroy();
}
} }
void NetPlaySetupDiag::OnQuit(wxCommandEvent&) void NetPlaySetupDiag::OnQuit(wxCommandEvent&)
@ -344,7 +352,6 @@ NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game
} }
m_memcard_write = new wxCheckBox(panel, wxID_ANY, _("Write memcards (GC)")); m_memcard_write = new wxCheckBox(panel, wxID_ANY, _("Write memcards (GC)"));
m_memcard_write->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &NetPlayDiag::OnMemcardWriteCheck, this);
bottom_szr->Add(m_memcard_write, 0, wxCENTER); bottom_szr->Add(m_memcard_write, 0, wxCENTER);
bottom_szr->AddStretchSpacer(1); bottom_szr->AddStretchSpacer(1);
@ -366,10 +373,15 @@ NetPlayDiag::NetPlayDiag(wxWindow* const parent, const CGameListCtrl* const game
NetPlayDiag::~NetPlayDiag() NetPlayDiag::~NetPlayDiag()
{ {
if (netplay_ptr) if (netplay_client)
{ {
delete netplay_ptr; delete netplay_client;
netplay_ptr = NULL; netplay_client = NULL;
}
if (netplay_server)
{
delete netplay_server;
netplay_server = NULL;
} }
npd = NULL; npd = NULL;
} }
@ -380,37 +392,50 @@ void NetPlayDiag::OnChat(wxCommandEvent&)
if (s.Length()) if (s.Length())
{ {
netplay_ptr->SendChatMessage(WxStrToStr(s)); netplay_client->SendChatMessage(WxStrToStr(s));
m_chat_text->AppendText(s.Prepend(wxT(" >> ")).Append(wxT('\n'))); m_chat_text->AppendText(s.Prepend(wxT(" >> ")).Append(wxT('\n')));
m_chat_msg_text->Clear(); m_chat_msg_text->Clear();
} }
} }
void NetPlayDiag::OnStart(wxCommandEvent&) void NetPlayDiag::GetNetSettings(NetSettings &settings)
{
SConfig &instance = SConfig::GetInstance();
settings.m_DSPHLE = instance.m_LocalCoreStartupParameter.bDSPHLE;
settings.m_DSPEnableJIT = instance.m_EnableJIT;
settings.m_WriteToMemcard = m_memcard_write->GetValue();
for (unsigned int i = 0; i < 4; ++i)
settings.m_Controllers[i] = SConfig::GetInstance().m_SIDevice[i];
}
const std::string& NetPlayDiag::FindGame()
{ {
// find path for selected game, sloppy.. // find path for selected game, sloppy..
for (u32 i = 0 ; auto game = m_game_list->GetISO(i); ++i) for (u32 i = 0 ; auto game = m_game_list->GetISO(i); ++i)
{
if (m_selected_game == BuildGameName(*game)) if (m_selected_game == BuildGameName(*game))
{ return game->GetFileName();
netplay_ptr->StartGame(game->GetFileName());
return;
}
}
PanicAlertT("Game not found!"); PanicAlertT("Game not found!");
return "";
}
void NetPlayDiag::OnStart(wxCommandEvent&)
{
NetSettings settings;
GetNetSettings(settings);
netplay_server->SetNetSettings(settings);
netplay_server->StartGame(FindGame());
} }
void NetPlayDiag::OnStop(wxCommandEvent&) void NetPlayDiag::OnStop(wxCommandEvent&)
{ {
netplay_ptr->StopGame(); netplay_server->StopGame();
} }
void NetPlayDiag::BootGame(const std::string& filename) void NetPlayDiag::BootGame(const std::string& filename)
{ {
main_frame->BootGame(filename); main_frame->BootGame(filename);
Core::g_CoreStartupParameter.bEnableMemcardSaving = m_memcard_write->GetValue();
} }
void NetPlayDiag::StopGame() void NetPlayDiag::StopGame()
@ -452,19 +477,14 @@ void NetPlayDiag::OnMsgStopGame()
GetEventHandler()->AddPendingEvent(evt); GetEventHandler()->AddPendingEvent(evt);
} }
void NetPlayDiag::OnMemcardWriteCheck(wxCommandEvent &event)
{
netplay_ptr->SetMemcardWriteEnabled(m_memcard_write->GetValue());
}
void NetPlayDiag::OnAdjustBuffer(wxCommandEvent& event) void NetPlayDiag::OnAdjustBuffer(wxCommandEvent& event)
{ {
const int val = ((wxSpinCtrl*)event.GetEventObject())->GetValue(); const int val = ((wxSpinCtrl*)event.GetEventObject())->GetValue();
((NetPlayServer*)netplay_ptr)->AdjustPadBufferSize(val); netplay_server->AdjustPadBufferSize(val);
std::ostringstream ss; std::ostringstream ss;
ss << "< Pad Buffer: " << val << " >"; ss << "< Pad Buffer: " << val << " >";
netplay_ptr->SendChatMessage(ss.str()); netplay_client->SendChatMessage(ss.str());
m_chat_text->AppendText(StrToWxStr(ss.str()).Append(wxT('\n'))); m_chat_text->AppendText(StrToWxStr(ss.str()).Append(wxT('\n')));
} }
@ -479,7 +499,7 @@ void NetPlayDiag::OnThread(wxCommandEvent& event)
// player list // player list
m_playerids.clear(); m_playerids.clear();
std::string tmps; std::string tmps;
netplay_ptr->GetPlayerList(tmps, m_playerids); netplay_client->GetPlayerList(tmps, m_playerids);
const int selection = m_player_lbox->GetSelection(); const int selection = m_player_lbox->GetSelection();
@ -502,15 +522,13 @@ void NetPlayDiag::OnThread(wxCommandEvent& event)
case NP_GUI_EVT_START_GAME : case NP_GUI_EVT_START_GAME :
// client start game :/ // client start game :/
{ {
wxCommandEvent evt; netplay_client->StartGame(FindGame());
OnStart(evt);
} }
break; break;
case NP_GUI_EVT_STOP_GAME : case NP_GUI_EVT_STOP_GAME :
// client stop game // client stop game
{ {
wxCommandEvent evt; netplay_client->StopGame();
OnStop(evt);
} }
break; break;
} }
@ -534,7 +552,7 @@ void NetPlayDiag::OnChangeGame(wxCommandEvent&)
if (game_name.length()) if (game_name.length())
{ {
m_selected_game = WxStrToStr(game_name); m_selected_game = WxStrToStr(game_name);
netplay_ptr->ChangeGame(m_selected_game); netplay_server->ChangeGame(m_selected_game);
m_game_btn->SetLabel(game_name.Prepend(_(" Game : "))); m_game_btn->SetLabel(game_name.Prepend(_(" Game : ")));
} }
} }
@ -549,13 +567,13 @@ void NetPlayDiag::OnConfigPads(wxCommandEvent&)
return; return;
pid = m_playerids.at(pid); pid = m_playerids.at(pid);
if (false == ((NetPlayServer*)netplay_ptr)->GetPadMapping(pid, mapping)) if (false == netplay_server->GetPadMapping(pid, mapping))
return; return;
PadMapDiag pmd(this, mapping); PadMapDiag pmd(this, mapping);
pmd.ShowModal(); pmd.ShowModal();
if (false == ((NetPlayServer*)netplay_ptr)->SetPadMapping(pid, mapping)) if (false == netplay_server->SetPadMapping(pid, mapping))
PanicAlertT("Could not set pads. The player left or the game is currently running!\n" PanicAlertT("Could not set pads. The player left or the game is currently running!\n"
"(setting pads while the game is running is not yet supported)"); "(setting pads while the game is running is not yet supported)");
} }

View file

@ -23,7 +23,7 @@
#include "FifoQueue.h" #include "FifoQueue.h"
#include "NetPlay.h" #include "NetPlayClient.h"
enum enum
{ {
@ -42,6 +42,8 @@ private:
void OnHost(wxCommandEvent& event); void OnHost(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event); void OnQuit(wxCommandEvent& event);
void MakeNetPlayDiag(int port, const std::string &game, bool is_hosting);
wxTextCtrl *m_nickname_text, wxTextCtrl *m_nickname_text,
*m_host_port_text, *m_host_port_text,
*m_connect_port_text, *m_connect_port_text,
@ -85,11 +87,12 @@ private:
void OnChat(wxCommandEvent& event); void OnChat(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event); void OnQuit(wxCommandEvent& event);
void OnMemcardWriteCheck(wxCommandEvent& event);
void OnThread(wxCommandEvent& event); void OnThread(wxCommandEvent& event);
void OnChangeGame(wxCommandEvent& event); void OnChangeGame(wxCommandEvent& event);
void OnAdjustBuffer(wxCommandEvent& event); void OnAdjustBuffer(wxCommandEvent& event);
void OnConfigPads(wxCommandEvent& event); void OnConfigPads(wxCommandEvent& event);
void GetNetSettings(NetSettings &settings);
const std::string& FindGame();
wxListBox* m_player_lbox; wxListBox* m_player_lbox;
wxTextCtrl* m_chat_text; wxTextCtrl* m_chat_text;