4 // called from ---GUI--- thread
5 NetPlayServer::~NetPlayServer()
10 m_thread
->WaitForDeath();
15 // called from ---GUI--- thread
16 NetPlayServer::NetPlayServer(const u16 port
, const std::string
& name
, NetPlayDiag
* const npd
, const std::string
& game
)
19 m_selected_game
= game
;
21 m_update_pings
= true;
23 if (m_socket
.Listen(port
))
27 player
.revision
= NETPLAY_DOLPHIN_VER
;
28 player
.socket
= m_socket
;
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");
43 m_selector
.Add(m_socket
);
44 m_thread
= new Common::Thread(NetPlayThreadFunc
, this);
50 // called from ---NETPLAY--- thread
51 void NetPlayServer::Entry()
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();
63 spac
<< (MessageId
)NP_MSG_PING
;
66 CritLocker
player_lock(m_crit
.players
);
67 CritLocker
send_lock(m_crit
.send
);
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
);
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
);
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();
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
))
112 //case sf::Socket::Disconnected :
114 m_crit
.game
.Enter(); // lock game state
115 OnDisconnect(ready_socket
);
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();
129 i
->second
.socket
.Close();
135 // called from ---NETPLAY--- thread
136 unsigned int NetPlayServer::OnConnect(sf::SocketTCP
& socket
)
139 // TODO: make this not hang / check if good packet
140 socket
.Receive(rpac
);
144 // dolphin netplay version
145 if (npver
!= NETPLAY_VERSION
)
146 return CON_ERR_VERSION_MISMATCH
;
148 // game is currently running
150 return CON_ERR_GAME_RUNNING
;
153 if (m_players
.size() >= 255)
154 return CON_ERR_SERVER_FULL
;
156 // cause pings to be updated
157 m_update_pings
= true;
160 player
.socket
= socket
;
161 rpac
>> player
.revision
;
164 // give new client first available id
166 std::map
<sf::SocketTCP
, Client
>::const_iterator
169 for (PlayerId p
= 1; 0 == player
.pid
; ++p
)
171 for (i
= m_players
.begin(); ; ++i
)
178 if (p
== i
->second
.pid
)
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
;
209 // send join message to already connected clients
211 spac
<< (MessageId
)NP_MSG_PLAYER_JOIN
;
212 spac
<< player
.pid
<< player
.name
<< player
.revision
;
215 // send new client success message with their id
217 spac
<< (MessageId
)0;
221 // send user their pad mapping
223 spac
<< (MessageId
)NP_MSG_PAD_MAPPING
;
225 for (unsigned int pm
= 0; pm
<4; ++pm
)
226 spac
<< player
.pad_map
[pm
];
229 // send new client the selected game
231 spac
<< (MessageId
)NP_MSG_CHANGE_GAME
;
232 spac
<< m_selected_game
;
235 // sync values with new client
236 for (i
= m_players
.begin(); i
!=e
; ++i
)
239 spac
<< (MessageId
)NP_MSG_PLAYER_JOIN
;
240 spac
<< i
->second
.pid
<< i
->second
.name
<< i
->second
.revision
;
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
];
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
);
267 // called from ---NETPLAY--- thread
268 unsigned int NetPlayServer::OnDisconnect(sf::SocketTCP
& socket
)
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;
278 spac
<< (MessageId
)NP_MSG_DISABLE_GAME
;
279 // this thread doesnt need players lock
280 m_crit
.send
.Enter(); // lock send
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
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(),
313 std::priority_queue
<unsigned int> pings
;
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)
322 required_ms
+= pings
.top();
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
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
346 m_crit
.players
.Leave();
349 // called from ---NETPLAY--- thread
350 unsigned int NetPlayServer::OnData(sf::Packet
& packet
, sf::SocketTCP
& socket
)
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
];
361 case NP_MSG_CHAT_MESSAGE
:
366 // send msg to other clients
368 spac
<< (MessageId
)NP_MSG_CHAT_MESSAGE
;
372 m_crit
.send
.Enter(); // lock send
373 SendToClients(spac
, player
.pid
);
377 std::ostringstream ss
;
378 ss
<< player
.name
<< '[' << (char)(player
.pid
+'0') << "]: " << msg
;
380 AppendChatGUI(ss
.str());
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
)
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
];
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
408 m_pad_buffer
[(unsigned)map
].push(np
);
409 m_crit
.buffer
.Leave();
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
);
425 const u32 ping
= m_ping_timer
.GetTimeElapsed();
429 if (m_ping_key
== ping_key
)
431 //PanicAlert("good pong");
438 case NP_MSG_START_GAME
:
440 packet
>> player
.on_game
;
445 PanicAlert("Unknown message with id:%d received from player:%d Kicking player!", mid
, player
.pid
);
446 // unknown message, kick the client
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(),
465 ss
<< i
->second
.ToString() << " " << i
->second
.ping
<< "ms\n";
470 // called from ---GUI--- thread / and ---NETPLAY--- thread
471 void NetPlayServer::SendChatMessage(const std::string
& msg
)
474 spac
<< (MessageId
)NP_MSG_CHAT_MESSAGE
;
475 spac
<< (PlayerId
)0; // server id always 0
478 m_crit
.send
.Enter(); // lock send
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
492 spac
<< (MessageId
)NP_MSG_CHANGE_GAME
;
495 m_crit
.players
.Enter(); // lock players
496 m_crit
.send
.Enter(); // lock send
499 m_crit
.players
.Leave();
504 // called from ---CPU--- thread
505 void NetPlayServer::SendPadState(const PadMapping local_nb
, const NetPad
& np
)
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
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
))
526 // TODO: i dont like this here
527 m_on_game
= Common::Timer::GetTimeMs();
529 m_crit
.buffer
.Leave();
531 // no change, just update with clients
532 AdjustPadBufferSize(m_target_buffer_size
);
534 // tell clients to start game
536 spac
<< (MessageId
)NP_MSG_START_GAME
;
539 m_crit
.players
.Enter(); // lock players
540 m_crit
.send
.Enter(); // lock send
543 m_crit
.players
.Leave();
549 // called from ---GUI--- thread
550 bool NetPlayServer::StopGame()
552 if (false == NetPlay::StopGame())
555 // tell clients to stop game
557 spac
<< (MessageId
)NP_MSG_STOP_GAME
;
559 m_crit
.players
.Enter(); // lock players
560 m_crit
.send
.Enter(); // lock send
562 m_crit
.players
.Leave();
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(),
575 if (i
->second
.pid
&& (i
->second
.pid
!= skip_pid
))
576 i
->second
.socket
.Send(packet
);