Forgot some files. Also... Hacks.
[dolphin.git] / Source / Core / DolphinWX / Src / NetPlayServer.cpp
blobc827c06e7c92d5fa51e48c19abf3fac9ced9e7f1
1 #include "NetPlay.h"
2 #include "NetWindow.h"
4 // called from ---GUI--- thread
5 NetPlayServer::~NetPlayServer()
7 if (is_connected)
9 m_do_loop = false;
10 m_thread->WaitForDeath();
11 delete m_thread;
15 // called from ---GUI--- thread
16 NetPlayServer::NetPlayServer(const u16 port, const std::string& name, NetPlayDiag* const npd, const std::string& game)
18 m_dialog = npd;
19 m_selected_game = game;
21 m_update_pings = true;
23 if (m_socket.Listen(port))
25 Client player;
26 player.pid = 0;
27 player.revision = NETPLAY_DOLPHIN_VER;
28 player.socket = m_socket;
29 player.name = name;
31 // map local pad 1 to game pad 1
32 player.pad_map[0] = 0;
34 // add self to player list
35 m_players[m_socket] = player;
36 m_local_player = &m_players[m_socket];
37 //PanicAlert("Listening");
39 UpdateGUI();
41 is_connected = true;
43 m_selector.Add(m_socket);
44 m_thread = new Common::Thread(NetPlayThreadFunc, this);
46 else
47 is_connected = false;
50 // called from ---NETPLAY--- thread
51 void NetPlayServer::Entry()
53 while (m_do_loop)
55 // update pings every so many seconds
56 if ((m_ping_timer.GetTimeElapsed() > (10 * 1000)) || m_update_pings)
58 //PanicAlert("sending pings");
60 m_ping_key = Common::Timer::GetTimeMs();
62 sf::Packet spac;
63 spac << (MessageId)NP_MSG_PING;
64 spac << m_ping_key;
66 CritLocker player_lock(m_crit.players);
67 CritLocker send_lock(m_crit.send);
68 m_ping_timer.Start();
69 SendToClients(spac);
71 m_update_pings = false;
74 // check which sockets need attention
75 const unsigned int num = m_selector.Wait(0.01f);
76 for (unsigned int i=0; i<num; ++i)
78 sf::SocketTCP ready_socket = m_selector.GetSocketReady(i);
80 // listening socket
81 if (ready_socket == m_socket)
83 sf::SocketTCP accept_socket;
84 m_socket.Accept(accept_socket);
86 m_crit.game.Enter(); // lock game state
87 const unsigned int error = OnConnect(accept_socket);
88 m_crit.game.Leave();
90 if (error)
92 sf::Packet spac;
93 spac << (MessageId)error;
94 // don't need to lock, this client isn't in the client map
95 accept_socket.Send(spac);
97 // TODO: not sure if client gets the message if i close right away
98 accept_socket.Close();
101 // client socket
102 else
104 sf::Packet rpac;
105 switch (ready_socket.Receive(rpac))
107 case sf::Socket::Done :
108 // if a bad packet is recieved, disconnect the client
109 if (0 == OnData(rpac, ready_socket))
110 break;
112 //case sf::Socket::Disconnected :
113 default :
114 m_crit.game.Enter(); // lock game state
115 OnDisconnect(ready_socket);
116 m_crit.game.Leave();
117 break;
123 // close listening socket and client sockets
125 std::map<sf::SocketTCP, Client>::reverse_iterator
126 i = m_players.rbegin(),
127 e = m_players.rend();
128 for ( ; i!=e; ++i)
129 i->second.socket.Close();
132 return;
135 // called from ---NETPLAY--- thread
136 unsigned int NetPlayServer::OnConnect(sf::SocketTCP& socket)
138 sf::Packet rpac;
139 // TODO: make this not hang / check if good packet
140 socket.Receive(rpac);
142 std::string npver;
143 rpac >> npver;
144 // dolphin netplay version
145 if (npver != NETPLAY_VERSION)
146 return CON_ERR_VERSION_MISMATCH;
148 // game is currently running
149 if (m_is_running)
150 return CON_ERR_GAME_RUNNING;
152 // too many players
153 if (m_players.size() >= 255)
154 return CON_ERR_SERVER_FULL;
156 // cause pings to be updated
157 m_update_pings = true;
159 Client player;
160 player.socket = socket;
161 rpac >> player.revision;
162 rpac >> player.name;
164 // give new client first available id
165 player.pid = 0;
166 std::map<sf::SocketTCP, Client>::const_iterator
168 e = m_players.end();
169 for (PlayerId p = 1; 0 == player.pid; ++p)
171 for (i = m_players.begin(); ; ++i)
173 if (e == i)
175 player.pid = p;
176 break;
178 if (p == i->second.pid)
179 break;
183 // TODO: this is crappy
184 // try to automatically assign new user a pad
186 bool is_mapped[4] = {false,false,false,false};
188 for ( unsigned int m = 0; m<4; ++m)
190 for (i = m_players.begin(); i!=e; ++i)
192 if (i->second.pad_map[m] >= 0)
193 is_mapped[(unsigned)i->second.pad_map[m]] = true;
197 for ( unsigned int m = 0; m<4; ++m)
198 if (false == is_mapped[m])
200 player.pad_map[0] = m;
201 break;
206 // ENTER
207 m_crit.send.Enter();
209 // send join message to already connected clients
210 sf::Packet spac;
211 spac << (MessageId)NP_MSG_PLAYER_JOIN;
212 spac << player.pid << player.name << player.revision;
213 SendToClients(spac);
215 // send new client success message with their id
216 spac.Clear();
217 spac << (MessageId)0;
218 spac << player.pid;
219 socket.Send(spac);
221 // send user their pad mapping
222 spac.Clear();
223 spac << (MessageId)NP_MSG_PAD_MAPPING;
224 spac << player.pid;
225 for (unsigned int pm = 0; pm<4; ++pm)
226 spac << player.pad_map[pm];
227 socket.Send(spac);
229 // send new client the selected game
230 spac.Clear();
231 spac << (MessageId)NP_MSG_CHANGE_GAME;
232 spac << m_selected_game;
233 socket.Send(spac);
235 // sync values with new client
236 for (i = m_players.begin(); i!=e; ++i)
238 spac.Clear();
239 spac << (MessageId)NP_MSG_PLAYER_JOIN;
240 spac << i->second.pid << i->second.name << i->second.revision;
241 socket.Send(spac);
243 spac.Clear();
244 spac << (MessageId)NP_MSG_PAD_MAPPING;
245 spac << i->second.pid;
246 for (unsigned int pm = 0; pm<4; ++pm)
247 spac << i->second.pad_map[pm];
248 socket.Send(spac);
251 // LEAVE
252 m_crit.send.Leave();
254 // add client to the player list
255 m_crit.players.Enter(); // lock players
256 m_players[socket] = player;
257 m_crit.players.Leave();
259 // add client to selector/ used for receiving
260 m_selector.Add(socket);
262 UpdateGUI();
264 return 0;
267 // called from ---NETPLAY--- thread
268 unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP& socket)
270 if (m_is_running)
272 PanicAlert("Client disconnect while game is running!! NetPlay is disabled. You manually stop the game.");
273 CritLocker game_lock(m_crit.game); // lock game state
274 m_is_running = false;
275 NetPlay_Disable();
277 sf::Packet spac;
278 spac << (MessageId)NP_MSG_DISABLE_GAME;
279 // this thread doesnt need players lock
280 m_crit.send.Enter(); // lock send
281 SendToClients(spac);
282 m_crit.send.Leave();
285 sf::Packet spac;
286 spac << (MessageId)NP_MSG_PLAYER_LEAVE;
287 spac << m_players[socket].pid;
289 m_selector.Remove(socket);
291 m_crit.players.Enter(); // lock players
292 m_players.erase(m_players.find(socket));
293 m_crit.players.Leave();
295 // alert other players of disconnect
296 m_crit.send.Enter(); // lock send
297 SendToClients(spac);
298 m_crit.send.Leave();
300 UpdateGUI();
302 return 0;
305 // called from ---GUI--- thread and ---NETPLAY--- thread
306 u64 NetPlayServer::CalculateMinimumBufferTime()
308 CritLocker player_lock(m_crit.players);
310 std::map<sf::SocketTCP, Client>::const_iterator
311 i = m_players.begin(),
312 e = m_players.end();
313 std::priority_queue<unsigned int> pings;
314 for ( ;i!=e; ++i)
315 pings.push(i->second.ping);
317 unsigned int required_ms = pings.top();
318 // if there is more than 1 client, buffersize must be >= (2 highest ping times combined)
319 if (pings.size() > 1)
321 pings.pop();
322 required_ms += pings.top();
325 return required_ms;
328 // called from ---GUI--- thread and ---NETPLAY--- thread
329 void NetPlayServer::AdjustPadBufferSize(unsigned int size)
331 CritLocker game_lock(m_crit.game); // lock game state
333 m_crit.buffer.Enter(); // lock buffer
334 m_target_buffer_size = size;
335 m_crit.buffer.Leave();
337 // tell clients to change buffer size
338 sf::Packet spac;
339 spac << (MessageId)NP_MSG_PAD_BUFFER;
340 spac << (u32)m_target_buffer_size;
342 m_crit.players.Enter(); // lock players
343 m_crit.send.Enter(); // lock send
344 SendToClients(spac);
345 m_crit.send.Leave();
346 m_crit.players.Leave();
349 // called from ---NETPLAY--- thread
350 unsigned int NetPlayServer::OnData(sf::Packet& packet, sf::SocketTCP& socket)
352 MessageId mid;
353 packet >> mid;
355 // don't need lock because this is the only thread that modifies the players
356 // only need locks for writes to m_players in this thread
357 Client& player = m_players[socket];
359 switch (mid)
361 case NP_MSG_CHAT_MESSAGE :
363 std::string msg;
364 packet >> msg;
366 // send msg to other clients
367 sf::Packet spac;
368 spac << (MessageId)NP_MSG_CHAT_MESSAGE;
369 spac << player.pid;
370 spac << msg;
372 m_crit.send.Enter(); // lock send
373 SendToClients(spac, player.pid);
374 m_crit.send.Leave();
376 // add to gui
377 std::ostringstream ss;
378 ss << player.name << '[' << (char)(player.pid+'0') << "]: " << msg;
380 AppendChatGUI(ss.str());
382 break;
384 case NP_MSG_PAD_DATA :
386 m_crit.buffer.Enter(); // lock buffer
388 // if this is pad data from the last game still being received, ignore it
389 if (player.on_game != m_on_game)
390 break;
392 PadMapping map = 0;
393 NetPad np;
394 packet >> map >> np.nHi >> np.nLo;
396 // check if client's pad indeed maps in game
397 if (map >= 0 && map < 4)
398 map = player.pad_map[(unsigned)map];
399 else
400 map = -1;
402 // if not, they are hacking, so disconnect them
403 // this could happen right after a pad map change, but that isn't implimented yet
404 if (map < 0)
405 return 1;
407 // add to pad buffer
408 m_pad_buffer[(unsigned)map].push(np);
409 m_crit.buffer.Leave();
411 // relay to clients
412 sf::Packet spac;
413 spac << (MessageId)NP_MSG_PAD_DATA;
414 spac << map; // in game mapping
415 spac << np.nHi << np.nLo;
417 m_crit.send.Enter(); // lock send
418 SendToClients(spac, player.pid);
419 m_crit.send.Leave();
421 break;
423 case NP_MSG_PONG :
425 const u32 ping = m_ping_timer.GetTimeElapsed();
426 u32 ping_key = 0;
427 packet >> ping_key;
429 if (m_ping_key == ping_key)
431 //PanicAlert("good pong");
432 player.ping = ping;
434 UpdateGUI();
436 break;
438 case NP_MSG_START_GAME :
440 packet >> player.on_game;
442 break;
444 default :
445 PanicAlert("Unknown message with id:%d received from player:%d Kicking player!", mid, player.pid);
446 // unknown message, kick the client
447 return 1;
448 break;
451 return 0;
454 // called from ---GUI--- thread
455 void NetPlayServer::GetPlayerList(std::string &list)
457 CritLocker player_lock(m_crit.players);
459 std::ostringstream ss;
461 std::map<sf::SocketTCP, Client>::const_iterator
462 i = m_players.begin(),
463 e = m_players.end();
464 for ( ; i!=e; ++i)
465 ss << i->second.ToString() << " " << i->second.ping << "ms\n";
467 list = ss.str();
470 // called from ---GUI--- thread / and ---NETPLAY--- thread
471 void NetPlayServer::SendChatMessage(const std::string& msg)
473 sf::Packet spac;
474 spac << (MessageId)NP_MSG_CHAT_MESSAGE;
475 spac << (PlayerId)0; // server id always 0
476 spac << msg;
478 m_crit.send.Enter(); // lock send
479 SendToClients(spac);
480 m_crit.send.Leave();
483 // called from ---GUI--- thread
484 bool NetPlayServer::ChangeGame(const std::string &game)
486 CritLocker game_lock(m_crit.game); // lock game state
488 m_selected_game = game;
490 // send changed game to clients
491 sf::Packet spac;
492 spac << (MessageId)NP_MSG_CHANGE_GAME;
493 spac << game;
495 m_crit.players.Enter(); // lock players
496 m_crit.send.Enter(); // lock send
497 SendToClients(spac);
498 m_crit.send.Leave();
499 m_crit.players.Leave();
501 return true;
504 // called from ---CPU--- thread
505 void NetPlayServer::SendPadState(const PadMapping local_nb, const NetPad& np)
507 // send to server
508 sf::Packet spac;
509 spac << (MessageId)NP_MSG_PAD_DATA;
510 spac << m_local_player->pad_map[local_nb]; // in-game pad num
511 spac << np.nHi << np.nLo;
513 m_crit.send.Enter(); // lock send
514 SendToClients(spac);
515 m_crit.send.Leave();
518 // called from ---GUI--- thread
519 bool NetPlayServer::StartGame(const std::string &path)
521 m_crit.buffer.Enter(); // lock buffer
523 if (false == NetPlay::StartGame(path))
524 return false;
526 // TODO: i dont like this here
527 m_on_game = Common::Timer::GetTimeMs();
528 ClearBuffers();
529 m_crit.buffer.Leave();
531 // no change, just update with clients
532 AdjustPadBufferSize(m_target_buffer_size);
534 // tell clients to start game
535 sf::Packet spac;
536 spac << (MessageId)NP_MSG_START_GAME;
537 spac << m_on_game;
539 m_crit.players.Enter(); // lock players
540 m_crit.send.Enter(); // lock send
541 SendToClients(spac);
542 m_crit.send.Leave();
543 m_crit.players.Leave();
545 return true;
549 // called from ---GUI--- thread
550 bool NetPlayServer::StopGame()
552 if (false == NetPlay::StopGame())
553 return false;
555 // tell clients to stop game
556 sf::Packet spac;
557 spac << (MessageId)NP_MSG_STOP_GAME;
559 m_crit.players.Enter(); // lock players
560 m_crit.send.Enter(); // lock send
561 SendToClients(spac);
562 m_crit.players.Leave();
563 m_crit.send.Leave();
565 return true;
568 // called from multiple threads
569 void NetPlayServer::SendToClients(sf::Packet& packet, const PlayerId skip_pid)
571 std::map<sf::SocketTCP, Client>::iterator
572 i = m_players.begin(),
573 e = m_players.end();
574 for ( ; i!=e; ++i)
575 if (i->second.pid && (i->second.pid != skip_pid))
576 i->second.socket.Send(packet);