NetPlay: Rework pad mapping

Pad mapping and the UI behind it is significantly confusing,
and has been confusing users since NetPlay was introduced.

As part of a large code cleanup to help NetPlay more stable,
introduce a better pad mapping system where local pad mapping
is handled by the client. Now, all the server and other
clients know is which player have which controllers, and the
clients only know this so they can show the pad mapping in the
UI; they don't do anything else with it.

A future cleanup will use this pad mapping data to completely
ignore the pads configured in the settings pane on the host.
This commit is contained in:
Jasper St. Pierre 2013-08-18 09:43:01 -04:00
parent 7a5374258e
commit 23ff31451f
7 changed files with 163 additions and 313 deletions

View File

@ -23,22 +23,6 @@ NetSettings g_NetPlaySettings;
#define RPT_SIZE_HACK (1 << 16) #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() NetPad::NetPad()
{ {
nHi = 0x00808080; nHi = 0x00808080;
@ -200,16 +184,8 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
case NP_MSG_PAD_MAPPING : case NP_MSG_PAD_MAPPING :
{ {
PlayerId pid; for (PadMapping i = 0; i < 4; i++)
packet >> pid; packet >> m_pad_map[i];
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
Player& player = m_players[pid];
for (unsigned int i=0; i<4; ++i)
packet >> player.pad_map[i];
}
m_dialog->Update(); m_dialog->Update();
} }
@ -223,7 +199,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
// trusting server for good map value (>=0 && <4) // trusting server for good map value (>=0 && <4)
// add to pad buffer // add to pad buffer
m_pad_buffer[(unsigned)map].Push(np); m_pad_buffer[map].Push(np);
} }
break; break;
@ -361,13 +337,36 @@ void NetPlayClient::GetPlayerList(std::string& list, std::vector<int>& pid_list)
e = m_players.end(); e = m_players.end();
for ( ; i!=e; ++i) for ( ; i!=e; ++i)
{ {
ss << i->second.ToString() << '\n'; const Player *player = &(i->second);
pid_list.push_back(i->second.pid); ss << player->name << "[" << (int)player->pid << "] : " << player->revision << " | ";
for (unsigned int j = 0; j < 4; j++)
{
if (m_pad_map[j] == player->pid)
ss << j + 1;
else
ss << '-';
}
ss << " | " << player->ping << "ms\n";
pid_list.push_back(player->pid);
} }
list = ss.str(); list = ss.str();
} }
// called from ---GUI--- thread
void NetPlayClient::GetPlayers(std::vector<const Player *> &player_list)
{
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
std::map<PlayerId, Player>::const_iterator
i = m_players.begin(),
e = m_players.end();
for ( ; i!=e; ++i)
{
const Player *player = &(i->second);
player_list.push_back(player);
}
}
// called from ---GUI--- thread // called from ---GUI--- thread
void NetPlayClient::SendChatMessage(const std::string& msg) void NetPlayClient::SendChatMessage(const std::string& msg)
@ -381,12 +380,12 @@ void NetPlayClient::SendChatMessage(const std::string& msg)
} }
// called from ---CPU--- thread // called from ---CPU--- thread
void NetPlayClient::SendPadState(const PadMapping local_nb, const NetPad& np) void NetPlayClient::SendPadState(const PadMapping in_game_pad, const NetPad& np)
{ {
// send to server // send to server
sf::Packet spac; sf::Packet spac;
spac << (MessageId)NP_MSG_PAD_DATA; spac << (MessageId)NP_MSG_PAD_DATA;
spac << local_nb; // local pad num spac << in_game_pad;
spac << np.nHi << np.nLo; spac << np.nHi << np.nLo;
std::lock_guard<std::recursive_mutex> lks(m_crit.send); std::lock_guard<std::recursive_mutex> lks(m_crit.send);
@ -457,13 +456,35 @@ void NetPlayClient::ClearBuffers()
// called from ---CPU--- thread // called from ---CPU--- thread
bool NetPlayClient::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues) bool NetPlayClient::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
{ {
{ // The interface for this is extremely silly.
std::lock_guard<std::recursive_mutex> lkp(m_crit.players); //
// 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.
//
// The interface here gives us the status of local pads, and
// expects to get back "in-game" pad numbers back in response.
// e.g. it asks "here's the input that slot 1 has, and by the
// way, what's the state of P1?"
//
// We should add this split between "in-game" pads and "local"
// pads higher up.
// in game mapping for this local pad int in_game_num = GetPadNum(pad_nb);
unsigned int in_game_num = m_local_player->pad_map[pad_nb];
// does this local pad map in game? // If this in-game pad is one of ours, then update from the
// information given.
if (in_game_num < 4) if (in_game_num < 4)
{ {
NetPad np(pad_status); NetPad np(pad_status);
@ -476,89 +497,36 @@ bool NetPlayClient::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_stat
m_pad_buffer[in_game_num].Push(np); m_pad_buffer[in_game_num].Push(np);
// send // send
SendPadState(pad_nb, np); SendPadState(in_game_num, np);
} }
} }
} // unlock players // Now, we need to swap out the local value with the values
// retrieved from NetPlay. This could be the value we pushed
//Common::Timer bufftimer; // above if we're configured as P1 and the code is trying
//bufftimer.Start(); // to retrieve data for slot 1.
// get padstate from buffer and send to game
while (!m_pad_buffer[pad_nb].Pop(*netvalues)) while (!m_pad_buffer[pad_nb].Pop(*netvalues))
{ {
// wait for receiving thread to push some data if (!m_is_running)
Common::SleepCurrentThread(1);
if (false == m_is_running)
return false; return false;
// TODO: check the time of bufftimer here, // TODO: use a condition instead of sleeping
// if it gets pretty high, ask the user if they want to disconnect Common::SleepCurrentThread(1);
} }
//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; return true;
} }
// called from ---CPU--- thread // called from ---CPU--- thread
void NetPlayClient::WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size) void NetPlayClient::WiimoteInput(int _number, u16 _channelID, const void* _pData, u32 _Size)
{ {
//// in game mapping for this local wiimote // XXX
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 // called from ---CPU--- thread
void NetPlayClient::WiimoteUpdate(int _number) void NetPlayClient::WiimoteUpdate(int _number)
{ {
{ // XXX
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) // called from ---GUI--- thread and ---NETPLAY--- thread (client side)
@ -586,13 +554,21 @@ bool NetPlayClient::StopGame()
// called from ---CPU--- thread // called from ---CPU--- thread
u8 NetPlayClient::GetPadNum(u8 numPAD) u8 NetPlayClient::GetPadNum(u8 numPAD)
{ {
// TODO: i don't like that this loop is running everytime there is rumble // Figure out which in-game pad maps to which local pad.
unsigned int i = 0; // The logic we have here is that the local slots always
for (; i<4; ++i) // go in order.
if (numPAD == m_local_player->pad_map[i]) int local_pad_count = -1;
break; int ingame_pad = 0;
for (; ingame_pad < 4; ingame_pad++)
{
if (m_pad_map[ingame_pad] == m_local_player->pid)
local_pad_count++;
return i; if (local_pad_count == numPAD)
break;
}
return ingame_pad;
} }
// stuff hacked into dolphin // stuff hacked into dolphin
@ -658,21 +634,12 @@ u8 CSIDevice_DanceMat::NetPlay_GetPadNum(u8 numPAD)
//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 _number)
void CWII_IPC_HLE_Device_usb_oh1_57e_305::NetPlay_WiimoteUpdate(int) 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 // called from ---CPU--- thread
// //
int CWII_IPC_HLE_WiiMote::NetPlay_GetWiimoteNum(int _number) 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; return _number;
} }
@ -684,19 +651,7 @@ bool CWII_IPC_HLE_WiiMote::NetPlay_WiimoteInput(int, u16, const void*, u32&)
std::lock_guard<std::mutex> lk(crit_netplay_client); std::lock_guard<std::mutex> lk(crit_netplay_client);
if (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; return true;
// }
//}
else else
return false; return false;
} }

View File

@ -50,6 +50,15 @@ public:
extern NetSettings g_NetPlaySettings; extern NetSettings g_NetPlaySettings;
class Player
{
public:
PlayerId pid;
std::string name;
std::string revision;
u32 ping;
};
class NetPlayClient class NetPlayClient
{ {
public: public:
@ -59,6 +68,7 @@ public:
~NetPlayClient(); ~NetPlayClient();
void GetPlayerList(std::string& list, std::vector<int>& pid_list); void GetPlayerList(std::string& list, std::vector<int>& pid_list);
void GetPlayers(std::vector<const Player *>& player_list);
bool is_connected; bool is_connected;
@ -84,19 +94,6 @@ protected:
std::recursive_mutex players, send; std::recursive_mutex players, send;
} m_crit; } 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<NetPad> m_pad_buffer[4];
Common::FifoQueue<NetWiimote> m_wiimote_buffer[4]; Common::FifoQueue<NetWiimote> m_wiimote_buffer[4];
@ -117,8 +114,10 @@ protected:
u32 m_current_game; u32 m_current_game;
PadMapping m_pad_map[4];
private: private:
void SendPadState(const PadMapping local_nb, const NetPad& np); void SendPadState(const PadMapping in_game_pad, const NetPad& np);
unsigned int OnData(sf::Packet& packet); unsigned int OnData(sf::Packet& packet);
PlayerId m_pid; PlayerId m_pid;

View File

@ -24,7 +24,7 @@ struct Rpt : public std::vector<u8>
typedef std::vector<Rpt> NetWiimote; typedef std::vector<Rpt> NetWiimote;
#define NETPLAY_VERSION "Dolphin NetPlay 2013-08-18" #define NETPLAY_VERSION "Dolphin NetPlay 2013-08-23"
// messages // messages
enum enum

View File

@ -4,11 +4,6 @@
#include "NetPlayServer.h" #include "NetPlayServer.h"
NetPlayServer::Client::Client()
{
memset(pad_map, -1, sizeof(pad_map));
}
NetPlayServer::~NetPlayServer() NetPlayServer::~NetPlayServer()
{ {
if (is_connected) if (is_connected)
@ -29,6 +24,7 @@ NetPlayServer::~NetPlayServer()
// called from ---GUI--- thread // called from ---GUI--- thread
NetPlayServer::NetPlayServer(const u16 port) : is_connected(false), m_is_running(false) NetPlayServer::NetPlayServer(const u16 port) : is_connected(false), m_is_running(false)
{ {
memset(m_pad_map, -1, sizeof(m_pad_map));
if (m_socket.Listen(port)) if (m_socket.Listen(port))
{ {
is_connected = true; is_connected = true;
@ -155,45 +151,16 @@ unsigned int NetPlayServer::OnConnect(sf::SocketTCP& socket)
rpac >> player.name; rpac >> player.name;
// give new client first available id // give new client first available id
player.pid = 0; player.pid = m_players.size() + 1;
std::map<sf::SocketTCP, Client>::const_iterator
i,
e = m_players.end();
for (PlayerId p = 1; 0 == player.pid; ++p)
{
for (i = m_players.begin(); ; ++i)
{
if (e == i)
{
player.pid = p;
break;
}
if (p == i->second.pid)
break;
}
}
// TODO: this is crappy
// try to automatically assign new user a pad // try to automatically assign new user a pad
{
bool is_mapped[4] = {false,false,false,false};
for (unsigned int m = 0; m < 4; ++m) for (unsigned int m = 0; m < 4; ++m)
{ {
for (i = m_players.begin(); i!=e; ++i) if (m_pad_map[m] == -1)
{ {
if (i->second.pad_map[m] >= 0) m_pad_map[m] = player.pid;
is_mapped[(unsigned)i->second.pad_map[m]] = true;
}
}
for ( unsigned int m = 0; m<4; ++m)
if (false == is_mapped[m])
{
player.pad_map[0] = m;
break; break;
} }
} }
{ {
@ -221,6 +188,9 @@ unsigned int NetPlayServer::OnConnect(sf::SocketTCP& socket)
} }
// sync values with new client // sync values with new client
std::map<sf::SocketTCP, Client>::const_iterator
i,
e = m_players.end();
for (i = m_players.begin(); i!=e; ++i) for (i = m_players.begin(); i!=e; ++i)
{ {
spac.Clear(); spac.Clear();
@ -279,85 +249,30 @@ unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP& socket)
} }
// called from ---GUI--- thread // called from ---GUI--- thread
bool NetPlayServer::GetPadMapping(const int pid, int map[]) void NetPlayServer::GetPadMapping(PadMapping map[4])
{ {
std::lock_guard<std::recursive_mutex> lkp(m_crit.players); for (int i = 0; i < 4; i++)
std::map<sf::SocketTCP, Client>::const_iterator map[i] = m_pad_map[i];
i = m_players.begin(),
e = m_players.end();
for (; i!=e; ++i)
if (pid == i->second.pid)
break;
// player not found
if (i == e)
return false;
// get pad mapping
for (unsigned int m = 0; m<4; ++m)
map[m] = i->second.pad_map[m];
return true;
} }
// called from ---GUI--- thread // called from ---GUI--- thread
bool NetPlayServer::SetPadMapping(const int pid, const int map[]) void NetPlayServer::SetPadMapping(const PadMapping map[4])
{ {
std::lock_guard<std::recursive_mutex> lkg(m_crit.game); for (int i = 0; i < 4; i++)
if (m_is_running) m_pad_map[i] = map[i];
return false; UpdatePadMapping();
std::lock_guard<std::recursive_mutex> lkp(m_crit.players);
std::map<sf::SocketTCP, Client>::iterator
i = m_players.begin(),
e = m_players.end();
for (; i!=e; ++i)
if (pid == i->second.pid)
break;
// player not found
if (i == e)
return false;
Client& player = i->second;
// set pad mapping
for (unsigned int m = 0; m<4; ++m)
{
player.pad_map[m] = (PadMapping)map[m];
// remove duplicate mappings
for (i = m_players.begin(); i!=e; ++i)
for (unsigned int p = 0; p<4; ++p)
if (p != m || i->second.pid != pid)
if (player.pad_map[m] == i->second.pad_map[p])
i->second.pad_map[p] = -1;
} }
std::lock_guard<std::recursive_mutex> lks(m_crit.send); // called from ---GUI--- thread and ---NETPLAY--- thread
UpdatePadMapping(); // sync pad mappings with everyone
return true;
}
// called from ---NETPLAY--- thread
void NetPlayServer::UpdatePadMapping() void NetPlayServer::UpdatePadMapping()
{
std::map<sf::SocketTCP, Client>::const_iterator
i = m_players.begin(),
e = m_players.end();
for (; i!=e; ++i)
{ {
sf::Packet spac; sf::Packet spac;
spac << (MessageId)NP_MSG_PAD_MAPPING; spac << (MessageId)NP_MSG_PAD_MAPPING;
spac << i->second.pid; for (int i = 0; i < 4; i++)
for (unsigned int pm = 0; pm<4; ++pm) spac << m_pad_map[i];
spac << i->second.pad_map[pm];
SendToClients(spac); SendToClients(spac);
} }
}
// 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)
{ {
@ -415,23 +330,15 @@ unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
int hi, lo; int hi, lo;
packet >> map >> hi >> lo; packet >> map >> hi >> lo;
// check if client's pad indeed maps in game // If the data is not from the correct player,
if (map >= 0 && map < 4) // then disconnect them.
map = player.pad_map[(unsigned)map]; if (m_pad_map[map] != player.pid)
else
map = -1;
// if not, they are hacking, so disconnect them
// this could happen right after a pad map change, but that isn't implemented yet
if (map < 0)
return 1; return 1;
// 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 << hi << lo;
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);

View File

@ -35,8 +35,8 @@ public:
bool StartGame(const std::string &path); bool StartGame(const std::string &path);
bool StopGame(); bool StopGame();
bool GetPadMapping(const int pid, int map[]); void GetPadMapping(PadMapping map[]);
bool SetPadMapping(const int pid, const int map[]); void SetPadMapping(const PadMapping map[]);
void AdjustPadBufferSize(unsigned int size); void AdjustPadBufferSize(unsigned int size);
@ -50,11 +50,8 @@ private:
class Client class Client
{ {
public: public:
Client();
PlayerId pid; PlayerId pid;
std::string name; std::string name;
PadMapping pad_map[4];
std::string revision; std::string revision;
sf::SocketTCP socket; sf::SocketTCP socket;
@ -77,6 +74,7 @@ private:
bool m_update_pings; bool m_update_pings;
u32 m_current_game; u32 m_current_game;
unsigned int m_target_buffer_size; unsigned int m_target_buffer_size;
PadMapping m_pad_map[4];
std::map<sf::SocketTCP, Client> m_players; std::map<sf::SocketTCP, Client> m_players;

View File

@ -559,23 +559,13 @@ void NetPlayDiag::OnChangeGame(wxCommandEvent&)
void NetPlayDiag::OnConfigPads(wxCommandEvent&) void NetPlayDiag::OnConfigPads(wxCommandEvent&)
{ {
int mapping[4]; PadMapping mapping[4];
std::vector<const Player *> player_list;
// get selected player id netplay_server->GetPadMapping(mapping);
int pid = m_player_lbox->GetSelection(); netplay_client->GetPlayers(player_list);
if (pid < 0) PadMapDiag pmd(this, mapping, player_list);
return;
pid = m_playerids.at(pid);
if (false == netplay_server->GetPadMapping(pid, mapping))
return;
PadMapDiag pmd(this, mapping);
pmd.ShowModal(); pmd.ShowModal();
netplay_server->SetPadMapping(mapping);
if (false == netplay_server->SetPadMapping(pid, mapping))
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)");
} }
ChangeGameDiag::ChangeGameDiag(wxWindow* const parent, const CGameListCtrl* const game_list, wxString& game_name) ChangeGameDiag::ChangeGameDiag(wxWindow* const parent, const CGameListCtrl* const game_list, wxString& game_name)
@ -605,45 +595,39 @@ void ChangeGameDiag::OnPick(wxCommandEvent& event)
EndModal(wxID_OK); EndModal(wxID_OK);
} }
PadMapDiag::PadMapDiag(wxWindow* const parent, int map[]) PadMapDiag::PadMapDiag(wxWindow* const parent, PadMapping map[], std::vector<const Player *>& player_list)
: wxDialog(parent, wxID_ANY, _("Configure Pads"), wxDefaultPosition, wxDefaultSize) : wxDialog(parent, wxID_ANY, _("Configure Pads"), wxDefaultPosition, wxDefaultSize)
, m_mapping(map) , m_mapping(map)
, m_player_list(player_list)
{ {
wxBoxSizer* const h_szr = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* const h_szr = new wxBoxSizer(wxHORIZONTAL);
h_szr->AddSpacer(20); wxArrayString player_names;
player_names.Add(_("None"));
// labels for (unsigned int i = 0; i < m_player_list.size(); i++)
wxBoxSizer* const label_szr = new wxBoxSizer(wxVERTICAL); player_names.Add(m_player_list[i]->name);
label_szr->Add(new wxStaticText(this, wxID_ANY, _("Local")), 0, wxALIGN_TOP);
label_szr->AddStretchSpacer(1);
label_szr->Add(new wxStaticText(this, wxID_ANY, _("In-Game")), 0, wxALIGN_BOTTOM);
h_szr->Add(label_szr, 1, wxTOP | wxEXPAND, 20);
// set up choices
wxString pad_names[5];
pad_names[0] = _("None");
for (unsigned int i=1; i<5; ++i)
pad_names[i] = wxString(_("Pad ")) + (wxChar)(wxT('0')+i);
for (unsigned int i=0; i<4; ++i) for (unsigned int i=0; i<4; ++i)
{ {
wxChoice* const pad_cbox = m_map_cbox[i]
= new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 5, pad_names);
pad_cbox->Select(m_mapping[i] + 1);
pad_cbox->Bind(wxEVT_COMMAND_CHOICE_SELECTED, &PadMapDiag::OnAdjust, this);
wxBoxSizer* const v_szr = new wxBoxSizer(wxVERTICAL); wxBoxSizer* const v_szr = new wxBoxSizer(wxVERTICAL);
v_szr->Add(new wxStaticText(this,wxID_ANY, pad_names[i + 1]), 1, wxALIGN_CENTER_HORIZONTAL); v_szr->Add(new wxStaticText(this, wxID_ANY, (wxString(_("Pad ")) + (wxChar)(wxT('0')+i))),
v_szr->Add(pad_cbox, 1); 1, wxALIGN_CENTER_HORIZONTAL);
m_map_cbox[i] = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, player_names);
m_map_cbox[i]->Bind(wxEVT_COMMAND_CHOICE_SELECTED, &PadMapDiag::OnAdjust, this);
if (m_mapping[i] == -1)
m_map_cbox[i]->Select(0);
else
for (unsigned int j = 0; j < m_player_list.size(); j++)
if (m_mapping[i] == m_player_list[j]->pid)
m_map_cbox[i]->Select(j + 1);
v_szr->Add(m_map_cbox[i], 1);
h_szr->Add(v_szr, 1, wxTOP | wxEXPAND, 20); h_szr->Add(v_szr, 1, wxTOP | wxEXPAND, 20);
h_szr->AddSpacer(10);
} }
h_szr->AddSpacer(20);
wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL); wxBoxSizer* const main_szr = new wxBoxSizer(wxVERTICAL);
main_szr->Add(h_szr); main_szr->Add(h_szr);
main_szr->AddSpacer(5); main_szr->AddSpacer(5);
@ -656,8 +640,14 @@ PadMapDiag::PadMapDiag(wxWindow* const parent, int map[])
void PadMapDiag::OnAdjust(wxCommandEvent& event) void PadMapDiag::OnAdjust(wxCommandEvent& event)
{ {
(void)event; (void)event;
for (unsigned int i=0; i<4; ++i) for (unsigned int i = 0; i < 4; i++)
m_mapping[i] = m_map_cbox[i]->GetSelection() - 1; {
int player_idx = m_map_cbox[i]->GetSelection();
if (player_idx > 0)
m_mapping[i] = m_player_list[player_idx - 1]->pid;
else
m_mapping[i] = -1;
}
} }
void NetPlay::StopGame() void NetPlay::StopGame()

View File

@ -124,13 +124,14 @@ private:
class PadMapDiag : public wxDialog class PadMapDiag : public wxDialog
{ {
public: public:
PadMapDiag(wxWindow* const parent, int map[]); PadMapDiag(wxWindow* const parent, PadMapping map[], std::vector<const Player *>& player_list);
private: private:
void OnAdjust(wxCommandEvent& event); void OnAdjust(wxCommandEvent& event);
wxChoice* m_map_cbox[4]; wxChoice* m_map_cbox[4];
int* const m_mapping; PadMapping* const m_mapping;
std::vector<const Player *>& m_player_list;
}; };
namespace NetPlay namespace NetPlay