NetPlay: completely redone - should be somewhat usable when using Single Core and...
[dolphin.git] / Source / Core / DolphinWX / Src / NetPlay.cpp
blobaecf0e0a8d1cdb7097bebb0f6201837ca6a6d56f
1 // Copyright (C) 2003 Dolphin Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
18 #include "HW/SI_DeviceGCController.h"
19 #include "HW/EXI_DeviceIPL.h"
21 #include "Frame.h"
23 #include "NetPlay.h"
24 #include "NetWindow.h"
26 #include <sstream>
28 // this will be removed soon
29 #define TEMP_FIXED_BUFFER 20
31 enum
33 CON_ERR_SERVER_FULL = 1,
34 CON_ERR_GAME_RUNNING,
35 CON_ERR_VERSION_MISMATCH
38 NetPlay* NetClass_ptr = NULL;
39 extern CFrame* main_frame;
41 DEFINE_EVENT_TYPE(wxEVT_THREAD)
43 THREAD_RETURN NetPlayThreadFunc(void* arg)
45 ((NetPlay*)arg)->Entry();
46 return 0;
49 NetPlay::~NetPlay()
51 ::NetClass_ptr = NULL;
54 NetPlayServer::~NetPlayServer()
56 if (is_connected)
58 m_do_loop = false;
59 m_thread->WaitForDeath();
60 delete m_thread;
64 NetPlayClient::~NetPlayClient()
66 if (is_connected)
68 m_do_loop = false;
69 m_thread->WaitForDeath();
70 delete m_thread;
74 NetPlayServer::Player::Player()
76 memset(pad_map, -1, sizeof(pad_map));
79 NetPlayClient::Player::Player()
81 memset(pad_map, -1, sizeof(pad_map));
84 NetPlayServer::NetPlayServer(const u16 port, const std::string& name, NetPlayDiag* const npd, const std::string& game)
86 m_dialog = npd;
87 m_selected_game = game;
89 if (m_socket.Listen(port))
91 Player player;
92 player.pid = 0;
93 player.revision = NETPLAY_DOLPHIN_VER;
94 player.socket = m_socket;
95 player.name = name;
97 // map local pad 1 to game pad 1
98 player.pad_map[0] = 0;
100 // add self to player list
101 m_players[m_socket] = player;
102 //PanicAlert("Listening");
104 UpdateGUI();
106 is_connected = true;
108 m_selector.Add(m_socket);
109 m_thread = new Common::Thread(NetPlayThreadFunc, this);
111 else
112 is_connected = false;
115 NetPlayClient::NetPlayClient(const std::string& address, const u16 port, const std::string& name, NetPlayDiag* const npd)
117 m_dialog = npd;
118 is_connected = false;
120 // why is false successful? documentation says true is
121 if (0 == m_socket.Connect(port, address))
123 // send connect message
124 sf::Packet spac;
125 spac << NETPLAY_VERSION;
126 spac << NETPLAY_DOLPHIN_VER;
127 spac << name;
128 m_socket.Send(spac);
130 sf::Packet rpac;
131 // TODO: make this not hang
132 m_socket.Receive(rpac);
133 u8 error;
134 rpac >> error;
136 // got error message
137 if (error)
139 switch (error)
141 case CON_ERR_SERVER_FULL :
142 PanicAlert("The server is full!");
143 break;
144 case CON_ERR_VERSION_MISMATCH :
145 PanicAlert("The NetPlay versions are incompatible!");
146 break;
147 case CON_ERR_GAME_RUNNING :
148 PanicAlert("The game is currently running!");
149 break;
151 m_socket.Close();
153 else
155 rpac >> m_pid;
157 Player player;
158 player.name = "Player";
159 player.pid = m_pid;
160 player.revision = NETPLAY_DOLPHIN_VER;
162 // add self to player list
163 m_players[m_pid] = player;
165 UpdateGUI();
167 //PanicAlert("Connection successful: assigned player id: %d", m_pid);
168 is_connected = true;
170 m_selector.Add(m_socket);
171 m_thread = new Common::Thread(NetPlayThreadFunc, this);
174 else
175 PanicAlert("Failed to Connect!");
179 void NetPlayServer::Entry()
181 while (m_do_loop)
183 const unsigned int num = m_selector.Wait(0.01f);
185 for (unsigned int i=0; i<num; ++i)
187 sf::SocketTCP ready_socket = m_selector.GetSocketReady(i);
189 // listening socket
190 if (ready_socket == m_socket)
192 sf::SocketTCP accept_socket;
193 m_socket.Accept(accept_socket);
195 m_crit.other.Enter();
196 unsigned int error = OnConnect(accept_socket);
197 m_crit.other.Leave();
199 if (error)
201 sf::Packet spac;
202 spac << (MessageId)error;
203 // don't need to lock, this client isn't in the client map
204 accept_socket.Send(spac);
206 // TODO: not sure if client gets the message if i close right away
207 accept_socket.Close();
210 // client socket
211 else
213 sf::Packet rpac;
214 switch (ready_socket.Receive(rpac))
216 case sf::Socket::Done :
217 // if a bad packet is recieved, disconnect the client
218 if (0 == OnData(rpac, ready_socket))
219 break;
221 case sf::Socket::Disconnected :
222 m_crit.other.Enter();
223 OnDisconnect(ready_socket);
224 m_crit.other.Leave();
225 break;
231 // close listening socket and client sockets
233 std::map<sf::SocketTCP, Player>::reverse_iterator
234 i = m_players.rbegin(),
235 e = m_players.rend();
236 for ( ; i!=e; ++i)
237 i->second.socket.Close();
240 return;
243 unsigned int NetPlayServer::OnConnect(sf::SocketTCP& socket)
245 sf::Packet rpac;
246 // TODO: make this not hang / check if good packet
247 socket.Receive(rpac);
249 std::string npver;
250 rpac >> npver;
251 // dolphin netplay version
252 if (npver != NETPLAY_VERSION)
253 return CON_ERR_VERSION_MISMATCH;
255 // game is currently running
256 if (m_is_running)
257 return CON_ERR_GAME_RUNNING;
259 // too many players
260 if (m_players.size() >= 255)
261 return CON_ERR_SERVER_FULL;
263 Player player;
264 player.socket = socket;
265 rpac >> player.revision;
266 rpac >> player.name;
268 // give new client first available id
269 player.pid = 0;
270 std::map<sf::SocketTCP, Player>::const_iterator
272 e = m_players.end();
273 for (u8 p = 1; 0 == player.pid; ++p)
275 for (i = m_players.begin(); ; ++i)
277 if (e == i)
279 player.pid = p;
280 break;
282 if (p == i->second.pid)
283 break;
287 // TODO: this is crappy
288 // try to automatically assign new user a pad
290 bool is_mapped[4] = {false,false,false,false};
292 for ( unsigned int m = 0; m<4; ++m)
294 for (i = m_players.begin(); i!=e; ++i)
296 if (i->second.pad_map[m] >= 0)
297 is_mapped[i->second.pad_map[m]] = true;
301 for ( unsigned int m = 0; m<4; ++m)
302 if (false == is_mapped[m])
304 player.pad_map[0] = m;
305 break;
310 // ENTER
311 m_crit.send.Enter();
313 // send join message to already connected clients
314 sf::Packet spac;
315 spac << (MessageId)NP_MSG_PLAYER_JOIN;
316 spac << player.pid << player.name << player.revision;
317 SendToClients(spac);
319 // send new client success message with their id
320 spac.Clear();
321 spac << (MessageId)0;
322 spac << player.pid;
323 socket.Send(spac);
325 // send user their pad mapping
326 spac.Clear();
327 spac << (MessageId)NP_MSG_PAD_MAPPING;
328 spac << player.pid;
329 for (unsigned int pm = 0; pm<4; ++pm)
330 spac << player.pad_map[pm];
331 socket.Send(spac);
333 // send new client the selected game
334 spac.Clear();
335 spac << (MessageId)NP_MSG_CHANGE_GAME;
336 spac << m_selected_game;
337 socket.Send(spac);
339 // sync values with new client
340 for (i = m_players.begin(); i!=e; ++i)
342 spac.Clear();
343 spac << (MessageId)NP_MSG_PLAYER_JOIN;
344 spac << i->second.pid << i->second.name << i->second.revision;
345 socket.Send(spac);
347 spac.Clear();
348 spac << (MessageId)NP_MSG_PAD_MAPPING;
349 spac << i->second.pid;
350 for (unsigned int pm = 0; pm<4; ++pm)
351 spac << i->second.pad_map[pm];
352 socket.Send(spac);
355 // LEAVE
356 m_crit.send.Leave();
358 // add client to the player list
359 m_crit.players.Enter();
360 m_players[socket] = player;
361 m_crit.players.Leave();
363 // add client to selector/ used for receiving
364 m_selector.Add(socket);
366 UpdateGUI();
368 return 0;
371 unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP& socket)
373 if (m_is_running)
374 PanicAlert("Disconnect while game is running. Game will likely hang!");
376 sf::Packet spac;
377 spac << (MessageId)NP_MSG_PLAYER_LEAVE;
378 spac << m_players[socket].pid;
380 m_selector.Remove(socket);
382 m_crit.players.Enter();
383 m_players.erase(m_players.find(socket));
384 m_crit.players.Leave();
386 UpdateGUI();
388 m_crit.send.Enter();
389 SendToClients(spac);
390 m_crit.send.Leave();
392 return 0;
395 void NetPlay::UpdateGUI()
397 if (m_dialog)
399 wxCommandEvent evt(wxEVT_THREAD, 1);
400 m_dialog->AddPendingEvent(evt);
404 void NetPlayServer::SendToClients(sf::Packet& packet, const u8 skip_pid)
406 std::map<sf::SocketTCP, Player>::iterator
407 i = m_players.begin(),
408 e = m_players.end();
409 for ( ; i!=e; ++i)
410 if (i->second.pid && (i->second.pid != skip_pid))
411 i->second.socket.Send(packet);
414 unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
416 MessageId mid;
417 packet >> mid;
419 // don't need lock because this is the only thread that modifies the players
420 // only need locks for writes to m_players in this thread
421 Player& player = m_players[socket];
423 switch (mid)
425 case NP_MSG_CHAT_MESSAGE :
427 std::string msg;
428 packet >> msg;
430 // send msg to other clients
431 sf::Packet spac;
432 spac << (MessageId)NP_MSG_CHAT_MESSAGE;
433 spac << player.pid;
434 spac << msg;
436 m_crit.send.Enter();
437 SendToClients(spac, player.pid);
438 m_crit.send.Leave();
440 // add to gui
441 std::ostringstream ss;
442 ss << player.name << '[' << (char)(player.pid+'0') << "]: " << msg;
444 m_dialog->chat_msgs.Push(ss.str());
446 UpdateGUI();
448 break;
450 case NP_MSG_PAD_DATA :
452 PadMapping map;
453 NetPad np;
454 packet >> map >> np.nHi >> np.nLo;
456 // check if client's pad indeed maps in game
457 if (map >= 0 && map < 4)
458 map = player.pad_map[map];
459 else
460 map = -1;
462 // if not, they are hacking, so disconnect them
463 // this could happen right after a pad map change, but that isn't implimented yet
464 if (map < 0)
465 return 1;
467 // add to pad buffer
468 m_crit.buffer.Enter();
469 m_pad_buffer[map].push(np);
470 m_crit.buffer.Leave();
472 // relay to clients
473 sf::Packet spac;
474 spac << (MessageId)NP_MSG_PAD_DATA;
475 spac << map; // in game mapping
476 spac << np.nHi << np.nLo;
478 m_crit.send.Enter();
479 SendToClients(spac, player.pid);
480 m_crit.send.Leave();
482 break;
484 default :
485 //PanicAlert("Unknown message received with id : %d", mid);
486 // unknown message, kick the client
487 return 1;
488 break;
491 return 0;
494 unsigned int NetPlayClient::OnData(sf::Packet& packet)
496 MessageId mid;
497 packet >> mid;
499 switch (mid)
501 case NP_MSG_PLAYER_JOIN :
503 Player player;
504 packet >> player.pid;
505 packet >> player.name;
506 packet >> player.revision;
508 m_crit.players.Enter();
509 m_players[player.pid] = player;
510 m_crit.players.Leave();
512 UpdateGUI();
514 break;
516 case NP_MSG_PLAYER_LEAVE :
518 PlayerId pid;
519 packet >> pid;
521 m_crit.players.Enter();
522 m_players.erase(m_players.find(pid));
523 m_crit.players.Leave();
525 UpdateGUI();
527 break;
529 case NP_MSG_CHAT_MESSAGE :
531 PlayerId pid;
532 packet >> pid;
533 std::string msg;
534 packet >> msg;
536 // don't need lock to read in this thread
537 const Player& player = m_players[pid];
539 // add to gui
540 std::ostringstream ss;
541 ss << player.name << '[' << (char)(pid+'0') << "]: " << msg;
543 m_dialog->chat_msgs.Push(ss.str());
544 UpdateGUI();
546 break;
548 case NP_MSG_PAD_MAPPING :
550 PlayerId pid;
551 packet >> pid;
553 m_crit.players.Enter();
554 Player& player = m_players[pid];
556 for (unsigned int i=0; i<4; ++i)
557 packet >> player.pad_map[i];
558 m_crit.players.Leave();
560 UpdateGUI();
562 break;
564 case NP_MSG_PAD_DATA :
566 PadMapping map;
567 NetPad np;
568 packet >> map >> np.nHi >> np.nLo;
570 // add to pad buffer
571 m_crit.buffer.Enter();
572 m_pad_buffer[map].push(np);
573 m_crit.buffer.Leave();
575 break;
577 case NP_MSG_CHANGE_GAME :
579 m_crit.other.Enter();
580 packet >> m_selected_game;
581 m_crit.other.Leave();
583 // update gui
584 wxCommandEvent evt(wxEVT_THREAD, 45);
585 evt.SetString(wxString(m_selected_game.c_str(), *wxConvCurrent));
586 m_dialog->AddPendingEvent(evt);
588 break;
590 case NP_MSG_START_GAME :
592 // kinda silly
593 wxCommandEvent evt;
594 m_dialog->OnStart(evt);
596 break;
598 default :
599 PanicAlert("Unknown message received with id : %d", mid);
600 break;
604 return 0;
607 void NetPlayClient::Entry()
609 while (m_do_loop)
611 if (m_selector.Wait(0.01f))
613 sf::Packet rpac;
614 switch (m_socket.Receive(rpac))
616 case sf::Socket::Done :
617 OnData(rpac);
618 break;
620 case sf::Socket::Disconnected :
621 PanicAlert("Lost connection to server! If the game is running, it will likely hang!");
622 m_do_loop = false;
623 break;
628 m_socket.Close();
630 return;
633 template <typename P>
634 static inline std::string PlayerToString(const P& p)
636 std::ostringstream ss;
637 ss << p.name << '[' << (char)(p.pid+'0') << "] : " << p.revision << " |";
638 for (unsigned int i=0; i<4; ++i)
639 ss << (p.pad_map[i]>=0 ? (char)(p.pad_map[i]+'1') : '-');
640 ss << '|';
641 return ss.str();
644 void NetPlayClient::GetPlayerList(std::string &list)
646 m_crit.players.Enter();
648 std::ostringstream ss;
650 std::map<u8, Player>::const_iterator
651 i = m_players.begin(),
652 e = m_players.end();
653 for ( ; i!=e; ++i)
654 ss << PlayerToString(i->second) << '\n';
656 list = ss.str();
658 m_crit.players.Leave();
661 void NetPlayServer::GetPlayerList(std::string &list)
663 m_crit.players.Enter();
665 std::ostringstream ss;
667 std::map<sf::SocketTCP, Player>::const_iterator
668 i = m_players.begin(),
669 e = m_players.end();
670 for ( ; i!=e; ++i)
671 ss << PlayerToString(i->second) << '\n';
673 list = ss.str();
675 m_crit.players.Leave();
678 void NetPlayServer::SendChatMessage(const std::string& msg)
680 sf::Packet spac;
681 spac << (MessageId)NP_MSG_CHAT_MESSAGE;
682 spac << (PlayerId)0; // server id always 0
683 spac << msg;
685 m_crit.send.Enter();
686 SendToClients(spac);
687 m_crit.send.Leave();
690 void NetPlayClient::SendChatMessage(const std::string& msg)
692 sf::Packet spac;
693 spac << (MessageId)NP_MSG_CHAT_MESSAGE;
694 spac << msg;
696 m_crit.send.Enter();
697 m_socket.Send(spac);
698 m_crit.send.Leave();
701 NetPad::NetPad()
703 //SPADStatus sp;
704 //memset(&sp, 0, sizeof(sp));
705 //memset(&sp.stickX, 0x80, 4);
706 //*this = NetPad(&sp);
708 // i'd rather do something like this
709 nHi = 0x00808080;
710 nLo = 0x80800000;
713 NetPad::NetPad(const SPADStatus* const pad_status)
715 nHi = (u32)((u8)pad_status->stickY);
716 nHi |= (u32)((u8)pad_status->stickX << 8);
717 nHi |= (u32)((u16)pad_status->button << 16);
718 nHi |= 0x00800000;
719 nLo = (u8)pad_status->triggerRight;
720 nLo |= (u32)((u8)pad_status->triggerLeft << 8);
721 nLo |= (u32)((u8)pad_status->substickY << 16);
722 nLo |= (u32)((u8)pad_status->substickX << 24);
725 bool NetPlayServer::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
727 m_crit.players.Enter();
729 // in game mapping for this local pad, kinda silly method
730 PadMapping in_game_num = m_players[m_socket].pad_map[pad_nb];
732 // check if this local pad maps in game
733 if (in_game_num >= 0)
735 NetPad np(pad_status);
737 // add to buffer
738 m_crit.buffer.Enter();
739 m_pad_buffer[in_game_num].push(np);
740 m_crit.buffer.Leave();
742 // send to clients
743 sf::Packet spac;
744 spac << (MessageId)NP_MSG_PAD_DATA;
745 spac << in_game_num; // in game pad num
746 spac << np.nHi << np.nLo;
748 m_crit.send.Enter();
749 SendToClients(spac);
750 m_crit.send.Leave();
753 // get padstate from buffer and send to game
754 m_crit.buffer.Enter();
755 while (0 == m_pad_buffer[pad_nb].size())
757 m_crit.buffer.Leave();
758 // wait for receiving thread to push some data
759 Common::SleepCurrentThread(10);
760 m_crit.buffer.Enter();
762 *netvalues = m_pad_buffer[pad_nb].front();
763 m_pad_buffer[pad_nb].pop();
764 m_crit.buffer.Leave();
766 m_crit.players.Leave();
768 return true;
771 bool NetPlayServer::SetSelectedGame(const std::string &game)
773 // warning removal
774 game.size();
776 return true;
779 bool NetPlayClient::GetNetPads(const u8 pad_nb, const SPADStatus* const pad_status, NetPad* const netvalues)
781 m_crit.players.Enter();
783 // in game mapping for this local pad
784 PadMapping in_game_num = m_players[m_pid].pad_map[pad_nb];
786 // does this local pad map in game?
787 if (in_game_num >= 0)
789 NetPad np(pad_status);
791 // add to buffer
792 m_crit.buffer.Enter();
793 m_pad_buffer[in_game_num].push(np);
794 m_crit.buffer.Leave();
796 // send to server
797 sf::Packet spac;
798 spac << (MessageId)NP_MSG_PAD_DATA;
799 spac << (PadMapping)pad_nb; // local pad num
800 spac << np.nHi << np.nLo;
802 m_crit.send.Enter();
803 m_socket.Send(spac);
804 m_crit.send.Leave();
807 // get padstate from buffer and send to game
808 m_crit.buffer.Enter();
809 while (0 == m_pad_buffer[pad_nb].size())
811 m_crit.buffer.Leave();
812 // wait for receiving thread to push some data
813 Common::SleepCurrentThread(10);
814 m_crit.buffer.Enter();
816 *netvalues = m_pad_buffer[pad_nb].front();
817 m_pad_buffer[pad_nb].pop();
818 m_crit.buffer.Leave();
820 m_crit.players.Leave();
821 return true;
824 bool NetPlayClient::SetSelectedGame(const std::string &game)
826 // warning removal
827 game.size();
829 return true;
832 bool NetPlayServer::StartGame(const std::string &path)
834 m_crit.other.Enter();
836 if (m_is_running)
838 PanicAlert("Game is already running!");
839 return false;
842 m_is_running = true;
843 ::NetClass_ptr = this;
845 m_crit.buffer.Enter();
846 // testing
847 NetPad np;
848 for (unsigned int i=0; i<TEMP_FIXED_BUFFER; ++i)
849 for (unsigned int p=0; p<4; ++p)
850 m_pad_buffer[p].push(np);
851 m_crit.buffer.Leave();
853 // tell clients to start game
854 sf::Packet spac;
855 spac << (MessageId)NP_MSG_START_GAME;
857 m_crit.send.Enter();
858 SendToClients(spac);
859 m_crit.send.Leave();
861 // boot game
862 ::main_frame->BootGame(path);
863 //BootManager::BootCore(path);
865 m_crit.other.Leave();
867 return true;
870 bool NetPlayClient::StartGame(const std::string &path)
872 m_crit.other.Enter();
874 m_is_running = true;
875 ::NetClass_ptr = this;
877 m_crit.buffer.Enter();
878 // testing
879 NetPad np;
880 for (unsigned int i=0; i<TEMP_FIXED_BUFFER; ++i)
881 for (unsigned int p=0; p<4; ++p)
882 m_pad_buffer[p].push(np);
883 m_crit.buffer.Leave();
885 // boot game
886 ::main_frame->BootGame(path);
887 //BootManager::BootCore(path);
889 m_crit.other.Leave();
891 return true;
894 // Actual Core function which is called on every frame
895 int CSIDevice_GCController::GetNetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
897 if (NetClass_ptr)
898 // TODO: NetClass_ptr could go null here, need a lock
899 return NetClass_ptr->GetNetPads(numPAD, &PadStatus, (NetPad*)PADStatus) ? 1 : 0;
900 else
901 return 2;
904 // so all players' games get the same time
905 u32 CEXIIPL::GetNetGCTime()
907 if (NetClass_ptr)
908 // TODO: NetClass_ptr could go null here, need a lock
909 return 1272737767; // watev
910 else
911 return 0;