1 #include "pch.h"
2 #include "gameclient.hpp"
3 #include "xtime.hpp"
4 #include "../ogre/common/Def_Str.h"
5
6 #include <MyGUI_LanguageManager.h>
7 #include <ctime>
8
9
P2PGameClient(GameClientCallback * callback,int port)10 P2PGameClient::P2PGameClient(GameClientCallback* callback, int port)
11 : m_callback(callback), m_client(*this, port), m_state(DISCONNECTED),
12 m_mutex(), m_cond(), m_playerInfo(), m_game(), m_carState()
13 {
14 std::srand(time(0)); // randomize, based on current time
15 m_playerInfo.random_id = std::rand(); // Client id is based on this
16 m_game.packet_type = -1; // Invalidate until set
17 m_carState.packet_type = -1; // Invalidate until set
18 }
19
~P2PGameClient()20 P2PGameClient::~P2PGameClient()
21 {
22 // Shuts down possibly running threads
23 m_state = DISCONNECTED;
24 m_cond.notify_all();
25 m_senderThread.join();
26 }
27
connect(const std::string & address,int port,std::string password)28 void P2PGameClient::connect(const std::string& address, int port, std::string password)
29 {
30 setPassword(password);
31 startLobby();
32 m_client.connect(address, port, NULL);
33 }
34
updatePlayerInfo(const std::string & name,const std::string & car)35 void P2PGameClient::updatePlayerInfo(const std::string& name, const std::string& car)
36 {
37 boost::mutex::scoped_lock lock(m_mutex);
38 m_playerInfo.name = name;
39 m_playerInfo.car = car;
40 m_playerInfo.address = m_client.getAddress();
41 }
42
setPassword(const std::string & password)43 void P2PGameClient::setPassword(const std::string& password)
44 {
45 boost::mutex::scoped_lock lock(m_mutex);
46 m_playerInfo.password = password;
47 }
48
toggleReady()49 void P2PGameClient::toggleReady()
50 {
51 boost::mutex::scoped_lock lock(m_mutex);
52 m_playerInfo.ready = !m_playerInfo.ready;
53 }
54
isReady() const55 bool P2PGameClient::isReady() const
56 {
57 return m_playerInfo.ready;
58 }
59
sendMessage(const std::string & msg)60 void P2PGameClient::sendMessage(const std::string& msg)
61 {
62 m_client.broadcast(char(protocol::TEXT_MESSAGE) + msg, net::PACKET_RELIABLE);
63 if (m_callback)
64 {
65 PeerInfo pi;
66 pi.name = m_playerInfo.name;
67 m_callback->peerMessage(pi, msg);
68 }
69 }
70
startLobby()71 void P2PGameClient::startLobby()
72 {
73 if (m_state == LOBBY)
74 {
75 LogO("== Netw startLobby WRONG already");
76 return;
77 } else
78 m_state = LOBBY;
79
80 if (!m_senderThread.joinable())
81 m_senderThread = boost::thread(boost::bind(&P2PGameClient::senderThread, boost::ref(*this)));
82 else
83 LogO("== Netw startLobby WRONG? !m_senderThread.joinable()");
84 }
85
startGame(bool broadcast)86 void P2PGameClient::startGame(bool broadcast)
87 {
88 if (m_state == GAME)
89 {
90 LogO("== Netw startGame WRONG already");
91 return;
92 }
93 LogO("== Netw startGame");
94 m_state = GAME;
95 {
96 // Clean up all zombie peers
97 boost::mutex::scoped_lock lock(m_mutex);
98
99 PeerMap::iterator it = m_peers.begin();
100 while (it != m_peers.end())
101 {
102 PeerInfo& pi = it->second;
103 pi.loaded = false; // Reset loading status to be sure
104
105 // Check condition
106 if (pi.connection != PeerInfo::CONNECTED || pi.name.empty() || !pi.authenticated)
107 {
108 if (pi.peer_id != 0)
109 m_client.disconnect(pi.peer_id, true);
110 m_peers.erase(it++);
111 } else
112 ++it;
113 }
114 m_playerInfo.loaded = false;
115
116 // Assign id numbers
117 recountPeersAndAssignIds(true);
118 }
119 // Send notification
120 if (broadcast)
121 {
122 LogO("== Netw startGame broadcast");
123 m_client.broadcast(char(protocol::START_GAME) + std::string(" "), net::PACKET_RELIABLE);
124 } else
125 LogO("== Netw startGame !broadcast");
126 }
127
loadingFinished()128 void P2PGameClient::loadingFinished()
129 {
130 LogO("== Netw loadingFinished");
131 m_client.broadcast(char(protocol::START_COUNTDOWN) + std::string(" "), net::PACKET_RELIABLE);
132 boost::mutex::scoped_lock lock(m_mutex);
133 m_playerInfo.loaded = true;
134
135 // Check for callback, because we might be the last one to finish loading
136 if (!m_callback)
137 {
138 LogO("== Netw loadingFinished WRONG !m_callback");
139 return;
140 }
141 for (PeerMap::const_iterator it = m_peers.begin(); it != m_peers.end(); ++it)
142 if (!it->second.loaded)
143 {
144 LogO("== Netw loadingFinished (but others not ready yet)");
145 return;
146 }
147 lock.unlock(); // Mutex unlocked in callback to avoid dead-locks
148
149 // Wait a tiny bit to give the network some time
150 boost::this_thread::sleep(boost::posix_time::milliseconds(50));
151 m_callback->startRace();
152 }
153
lap(uint8_t num,double time)154 void P2PGameClient::lap(uint8_t num, double time)
155 {
156 protocol::TimeInfoPackage tip(num, time);
157 m_client.broadcast(net::convert(tip), net::PACKET_RELIABLE);
158 LogO("== Netw Lap " +toStr(num) +" finished by U time:"+ toStr(float(time)));
159 }
160
returnToLobby(bool broadcast)161 void P2PGameClient::returnToLobby(bool broadcast)
162 {
163 if (m_state != GAME)
164 {
165 LogO("== Netw returnToLobby WRONG we should be in-game");
166 return;
167 }
168 LogO("== Netw returnToLobby");
169 boost::mutex::scoped_lock lock(m_mutex);
170 m_state = LOBBY;
171 m_playerInfo.loaded = false;
172 m_playerInfo.ready = false;
173 for (PeerMap::iterator it = m_peers.begin(); it != m_peers.end(); ++it)
174 it->second.loaded = false;
175 if (broadcast)
176 m_client.broadcast(char(protocol::RETURN_LOBBY) + std::string(" "), net::PACKET_RELIABLE);
177 }
178
senderThread()179 void P2PGameClient::senderThread()
180 {
181 do {
182 boost::mutex::scoped_lock lock(m_mutex);
183 if (m_state == LOBBY)
184 {
185 // Broadcast local player's meta info
186 protocol::PlayerInfoPacket pip = (protocol::PlayerInfoPacket)m_playerInfo;
187 broadcastToAuthed(net::convert(pip));
188
189 // If game info is set, broadcast it
190 if (m_game.packet_type == protocol::GAME_STATUS)
191 broadcastToAuthed(net::convert(m_game));
192
193 // Loop all peers
194 for (PeerMap::iterator it = m_peers.begin(); it != m_peers.end(); ++it)
195 { PeerInfo& pi = it->second;
196
197 // Check if we should try connecting to the peer
198 // TODO: Should we also do this during GAME phase ?..
199 if (pi.connection == PeerInfo::DISCONNECTED)
200 {
201 //LogO("== Netw Connecting to "+pi.address);
202 pi.connection = PeerInfo::CONNECTING;
203 m_client.connect(pi.address, NULL);
204 }
205 // Broadcast peer's info
206 protocol::PeerAddressPacket pap(it->second.address);
207 broadcastToAuthed(net::convert(pap));
208 }
209
210 // Wait some
211 m_cond.timed_wait(lock, now() + 2.0);
212 }
213 else if (m_state == GAME)
214 {
215 // Broadcast car state
216 if ((bool)m_carState)
217 m_client.broadcast(net::convert(m_carState));
218
219 // Wait some
220 m_cond.timed_wait(lock, now() + 0.050); // 20 FPS
221 }
222 if (m_state == DISCONNECTED)
223 { //LogO("== Netw DISCONNECTED");
224 break;
225 }
226 } while (true);
227 }
228
setLocalCarState(protocol::CarStatePackage const & cs)229 void P2PGameClient::setLocalCarState(protocol::CarStatePackage const& cs)
230 {
231 boost::mutex::scoped_lock lock(m_mutex);
232 m_carState = cs;
233 }
234
getReceivedCarStates()235 protocol::CarStates P2PGameClient::getReceivedCarStates()
236 {
237 boost::mutex::scoped_lock lock(m_mutex);
238 protocol::CarStates states = m_receivedCarStates;
239 m_receivedCarStates.clear();
240 return states;
241 }
242
getPeerCount() const243 size_t P2PGameClient::getPeerCount() const
244 {
245 boost::mutex::scoped_lock lock(m_mutex);
246 return m_playerInfo.peers;
247 }
248
getPeer(ClientID id) const249 PeerInfo P2PGameClient::getPeer(ClientID id) const
250 {
251 boost::mutex::scoped_lock lock(m_mutex);
252 for (PeerMap::const_iterator it = m_peers.begin(); it != m_peers.end(); ++it)
253 if (it->second.id == id)
254 return it->second;
255 return PeerInfo();
256 }
257
broadcastGameInfo(const protocol::GameInfo & game)258 void P2PGameClient::broadcastGameInfo(const protocol::GameInfo &game)
259 {
260 boost::mutex::scoped_lock lock(m_mutex);
261 m_game = game;
262 m_game.packet_type = protocol::GAME_STATUS; // Just to make sure
263 }
264
265 // Mutex should be already locked when this is called
recountPeersAndAssignIds(bool validate)266 void P2PGameClient::recountPeersAndAssignIds(bool validate)
267 {
268 LogO("== Netw recountPeersAndAssignIds");
269 m_playerInfo.address = m_client.getAddress();
270 m_playerInfo.peers = 0;
271 m_playerInfo.id = -1;
272 typedef std::map<int32_t, PeerInfo*> IDSorter;
273 IDSorter idsorter;
274 idsorter[m_playerInfo.random_id] = &m_playerInfo;
275 for (PeerMap::iterator it = m_peers.begin(); it != m_peers.end(); ++it) {
276 if (it->second.connection == PeerInfo::CONNECTED
277 && !it->second.name.empty()
278 && it->second.authenticated)
279 {
280 idsorter[it->second.random_id] = &it->second;
281 ++m_playerInfo.peers;
282 }
283 it->second.id = -1;
284 }
285 // Assign IDs
286 ClientID id = 0;
287 for (IDSorter::iterator it = idsorter.begin(); it != idsorter.end(); ++it, ++id)
288 it->second->id = id;
289
290 if (!validate)
291 { LogO("== Netw recountPeersAndAssignIds !validate");
292 return;
293 }
294 // Validate unique IDs
295 for (PeerMap::const_iterator it = m_peers.begin(); it != m_peers.end(); ++it)
296 if (it->second.id == -1)
297 { LogO("== Netw recountPeersAndAssignIds Unassigned or duplicate client ID!");
298 throw std::runtime_error("Unassigned or duplicate client ID!");
299 }
300 }
301
302 // Mutex should be already locked when this is called
broadcastToAuthed(net::NetworkTraffic const & msg,int flags)303 void P2PGameClient::broadcastToAuthed(net::NetworkTraffic const& msg, int flags)
304 {
305 for (PeerMap::const_iterator it = m_peers.begin(); it != m_peers.end(); ++it)
306 if (it->second.authenticated)
307 m_client.send(it->second.peer_id, msg, flags);
308 }
309
connectionEvent(net::NetworkTraffic const & e)310 void P2PGameClient::connectionEvent(net::NetworkTraffic const& e)
311 {
312 LogO("== Netw Connected "+e.peer_address.str());
313 if (m_state == LOBBY)
314 {
315 if (m_playerInfo.address == e.peer_address)
316 {
317 LogO("== Netw CConnected reject, same !!");
318 return;
319 }
320 boost::mutex::scoped_lock lock(m_mutex);
321 PeerInfo& pi = m_peers[e.peer_address.str()];
322 pi.address = e.peer_address;
323 pi.peer_id = e.peer_id;
324 pi.connection = PeerInfo::CONNECTED;
325 pi.ping = e.ping;
326 protocol::HandshakePackage hp(m_playerInfo.password);
327 m_client.send(e.peer_id, net::convert(hp), net::PACKET_RELIABLE);
328 // No connection callback here, because player info is not yet received
329 }
330 }
331
disconnectEvent(net::NetworkTraffic const & e)332 void P2PGameClient::disconnectEvent(net::NetworkTraffic const& e)
333 {
334 LogO("== Netw Disconnected "+e.peer_address.str());
335 if (m_callback && e.event_data == protocol::INCOMPATIBLE_GAME_PROTOCOL)
336 m_callback->error(TR("#{NetGameProtocolError}"));
337 if (m_callback && e.event_data == protocol::WRONG_PASSWORD)
338 m_callback->error(TR("#{NetWrongPassword}"));
339 PeerInfo picopy;
340 {
341 boost::mutex::scoped_lock lock(m_mutex);
342 picopy = m_peers[e.peer_address.str()];
343 picopy.connection = PeerInfo::DISCONNECTED;
344 m_peers.erase(e.peer_address.str());
345 recountPeersAndAssignIds();
346 }
347 // Callback (mutex unlocked to avoid dead-locks)
348 if (m_callback)
349 m_callback->peerDisconnected(picopy);
350 }
351
receiveEvent(net::NetworkTraffic const & e)352 void P2PGameClient::receiveEvent(net::NetworkTraffic const& e)
353 {
354 if (e.packet_length <= 0 || !e.packet_data)
355 {
356 LogO("== Netw receive event BAD");
357 return;
358 }
359 switch (e.packet_data[0])
360 {
361 case protocol::HANDSHAKE:
362 {
363 if (m_state != LOBBY)
364 { LogO("== Netw HANDSHAKE WRONG !lobby");
365 break;
366 }
367 protocol::HandshakePackage hp = *reinterpret_cast<protocol::HandshakePackage const*>(e.packet_data);
368 if (hp.game_protocol_version != protocol::GAME_PROTOCOL_VERSION)
369 {
370 m_client.disconnect(e.peer_id, false, protocol::INCOMPATIBLE_GAME_PROTOCOL);
371 if (m_callback)
372 m_callback->error(TR("#{NetGameProtocolError}"));
373 LogO("== Netw HANDSHAKE WRONG protocol ver");
374 break;
375 }
376 boost::mutex::scoped_lock lock(m_mutex);
377 if (std::string(m_playerInfo.password) != std::string(hp.password))
378 {
379 m_client.disconnect(e.peer_id, false, protocol::WRONG_PASSWORD);
380 LogO("== Netw HANDSHAKE WRONG passwod");
381 break;
382 }
383 PeerInfo& pi = m_peers[e.peer_address.str()];
384 pi.authenticated = true;
385 pi.ping = e.ping;
386 LogO("== Netw HANDSHAKE ok");
387 break;
388 }
389 case protocol::PEER_ADDRESS:
390 {
391 if (m_state != LOBBY)
392 { LogO("== Netw PEER_ADDRESS WRONG !lobby");
393 break;
394 }
395 protocol::PeerAddressPacket pap = *reinterpret_cast<protocol::PeerAddressPacket const*>(e.packet_data);
396 boost::mutex::scoped_lock lock(m_mutex);
397 m_peers[pap.address.str()].address = pap.address;
398 m_peers[e.peer_address.str()].ping = e.ping;
399 break;
400 }
401 case protocol::TEXT_MESSAGE:
402 {
403 std::string msg((const char*)e.packet_data, e.packet_length);
404 msg = msg.substr(1); // Remove the packet_type char
405 LogO("== Netw TEXT_MESSAGE received: "+msg);
406 if (m_callback)
407 {
408 boost::mutex::scoped_lock lock(m_mutex);
409 PeerInfo& pi = m_peers[e.peer_address.str()];
410 pi.ping = e.ping;
411
412 PeerInfo picopy = pi;
413 lock.unlock(); // Mutex unlocked in callback to avoid dead-locks
414 m_callback->peerMessage(picopy, msg);
415 } else
416 LogO("== Netw TEXT_MESSAGE !callback");
417 break;
418 }
419 case protocol::PLAYER_INFO:
420 {
421 LogO("== Netw PLAYER_INFO start");
422 protocol::PlayerInfoPacket pip = *reinterpret_cast<protocol::PlayerInfoPacket const*>(e.packet_data);
423
424 if (std::string(pip.name) == m_playerInfo.name)
425 {
426 LogO("== Netw PLAYER_INFO reject, same !!");
427 break;
428 }
429
430 boost::mutex::scoped_lock lock(m_mutex);
431
432 PeerInfo& pi = m_peers[e.peer_address.str()];
433 bool isNew = pi.name.empty();
434
435 //LogO("== Netw PI0 adr: "+pi.address.str()+" name: "+pi.name+" id: "+toStr(pi.id)+" rid: "+toStr(pi.random_id)+" peers: "+toStr(pi.peers)+" rdy: "+toStr(pi.ready?1:0)+" ld: "+toStr(pi.loaded?1:0)+" ping: "+toStr(pi.ping)+" aut: "+toStr(pi.authenticated));
436 pi = pip;
437 pi.ping = e.ping;
438 //LogO("== Netw PI1 adr: "+pi.address.str()+" name: "+pi.name+" id: "+toStr(pi.id)+" rid: "+toStr(pi.random_id)+" peers: "+toStr(pi.peers)+" rdy: "+toStr(pi.ready?1:0)+" ld: "+toStr(pi.loaded?1:0)+" ping: "+toStr(pi.ping)+" aut: "+toStr(pi.authenticated));
439
440 if (m_callback) // Callback
441 {
442 LogO(std::string("== Netw PI callb ")+(isNew?"new":""));
443 PeerInfo picopy = pi;
444 if (isNew)
445 recountPeersAndAssignIds();
446
447 lock.unlock(); // Mutex unlocked in callback to avoid dead-locks
448
449 if (isNew) // First info means completed connection
450 m_callback->peerConnected(picopy);
451 else // Callback regardless if the info changed in order to give ping updates
452 m_callback->peerInfo(picopy);
453 } else
454 LogO("== Netw START_GAME !callback");
455 break;
456 }
457 case protocol::START_GAME:
458 {
459 LogO("== Netw START_GAME start");
460 startGame(false);
461 if (m_callback)
462 {
463 boost::mutex::scoped_lock lock(m_mutex);
464 PeerInfo& pi = m_peers[e.peer_address.str()];
465 pi.ping = e.ping;
466
467 PeerInfo picopy = pi;
468 lock.unlock(); // Mutex unlocked in callback to avoid dead-locks
469 m_callback->peerState(picopy, e.packet_data[0]);
470 LogO("== Netw START_GAME end");
471 } else
472 LogO("== Netw START_GAME WRONG !callback");
473 break;
474 }
475 case protocol::START_COUNTDOWN:
476 {
477 LogO("== Netw START_COUNTDOWN start");
478 if (m_callback)
479 {
480 boost::mutex::scoped_lock lock(m_mutex);
481 PeerInfo& pi = m_peers[e.peer_address.str()];
482 pi.ping = e.ping;
483 pi.loaded = true;
484 if (!m_playerInfo.loaded)
485 {
486 LogO("== Netw START_COUNTDOWN WRONG !m_playerInfo.loaded");
487 break;
488 }
489
490 bool start = true;
491 for (PeerMap::const_iterator it = m_peers.begin(); it != m_peers.end(); ++it)
492 if (!it->second.loaded)
493 { start = false; break; }
494
495 lock.unlock(); // Mutex unlocked in callback to avoid dead-locks
496 if (start)
497 {
498 LogO("== Netw START_COUNTDOWN startRace");
499 m_callback->startRace();
500 }
501 LogO("== Netw START_COUNTDOWN end");
502 } else
503 LogO("== Netw START_COUNTDOWN WRONG !callback");
504 break;
505 }
506 case protocol::CAR_UPDATE:
507 {
508 if (m_state != GAME)
509 { LogO("== Netw CAR_UPDATE WRONG !game");
510 break;
511 }
512 protocol::CarStatePackage csp = *reinterpret_cast<protocol::CarStatePackage const*>(e.packet_data);
513 boost::mutex::scoped_lock lock(m_mutex);
514
515 PeerInfo& pi = m_peers[e.peer_address.str()];
516 pi.ping = e.ping;
517 if (pi.id < 0)
518 { LogO("== Netw CAR_UPDATE WRONG pi.id < 0");
519 break;
520 }
521 m_receivedCarStates[pi.id] = csp;
522 break;
523 }
524 case protocol::GAME_STATUS:
525 {
526 if (m_state != LOBBY || !m_callback)
527 { LogO("== Netw GAME_STATUS WRONG !lobby or !callback");
528 break;
529 }
530 protocol::GameInfo game = *reinterpret_cast<protocol::GameInfo const*>(e.packet_data);
531 m_callback->gameInfo(game);
532 break;
533 }
534 case protocol::TIME_INFO:
535 {
536 if (m_state != GAME || !m_callback)
537 { LogO("== Netw TIME_INFO WRONG !game or !callback");
538 break;
539 }
540 protocol::TimeInfoPackage time = *reinterpret_cast<protocol::TimeInfoPackage const*>(e.packet_data);
541 boost::mutex::scoped_lock lock(m_mutex);
542
543 ClientID id = m_peers[e.peer_address.str()].id;
544 lock.unlock(); // Mutex unlocked in callback to avoid dead-locks
545 m_callback->timeInfo(id, time.lap, time.time);
546 break;
547 }
548 case protocol::RETURN_LOBBY:
549 {
550 returnToLobby(false);
551 m_callback->returnToLobby();
552 break;
553 }
554 default:
555 {
556 LogO("== Netw Received unknown packet type: "+toStr((int)e.packet_data[0]));
557 break;
558 }
559 }
560 }
561