BF2MC-Matchmaker
gpcm/client.cpp
1 #include <unistd.h>
2 #include <iostream>
3 #include <iomanip>
4 #include <algorithm>
5 #include <thread>
6 
7 #include <settings.h>
8 #include <logger.h>
9 #include <server.h>
10 #include <globals.h>
11 #include <database.h>
12 #include <util.h>
13 
14 #include <gpcm/client.h>
15 
16 typedef void (GPCM::Client::*RequestActionFunc)(const GameSpy::Parameter&);
17 
18 static std::map<std::string, RequestActionFunc> mRequestActions =
19 {
20  { "login", &GPCM::Client::requestLogin }, // Done
21  { "inviteto", &GPCM::Client::requestInviteTo }, // Done
22  { "getprofile", &GPCM::Client::requestGetProfile }, // Done
23  { "status", &GPCM::Client::requestStatus }, // Done
24  { "bm", &GPCM::Client::requestBm }, // Done
25  { "addbuddy", &GPCM::Client::requestAddBuddy }, // Done
26  { "authadd", &GPCM::Client::requestAuthAdd }, // Done
27  { "revoke", &GPCM::Client::requestRevoke }, // Done
28  { "delbuddy", &GPCM::Client::requestDeleteBuddy }, // Done
29  { "pinvite", &GPCM::Client::requestPlayerInvite }, // Done
30  { "logout", &GPCM::Client::requestLogout }, // Done
31 };
32 
33 GPCM::Client::Client(int socket, struct sockaddr_in address)
34 {
35  this->_socket = socket;
36  this->_address = address;
37  this->UpdateLastRecievedTime();
38 }
39 
41 {
42  this->Disconnect();
43 }
44 
46 {
47  bool isDisconnected = false;
48 
49  // Initialize connection send challenge
50  this->requestChallenge();
51 
52  while(!isDisconnected)
53  {
54  std::vector<unsigned char> combined_buffer;
55  std::string last_seven_chars = "";
56 
57  do
58  {
59  std::vector<unsigned char> buffer(16384, 0);
60 
61  int v = read(this->_socket, &(buffer[0]), 16384);
62 
63  // If error or no data is recieved we end the connection
64  if(v <= 0)
65  {
66  isDisconnected = true;
67  break;
68  }
69 
70  // Resize buffer
71  buffer.resize(v);
72 
73  this->UpdateLastRecievedTime();
74 
75  // Debug
76  //std::stringstream ss;
77  //for(int i = 0; i < buffer.size(); i++)
78  //{
79  // ss << std::hex << std::setfill('0') << std::setw(2) << (int)(buffer[i]);
80  //}
81  //Logger::info("buffer = " + ss.str());
82 
83  combined_buffer.insert(combined_buffer.end(), buffer.begin(), buffer.end());
84 
85  if(combined_buffer.size() > 7)
86  {
87  last_seven_chars.assign(combined_buffer.end() - 7, combined_buffer.end());
88  }
89  } while (last_seven_chars != "\\final\\" && combined_buffer.size() < 32768);
90 
91  std::vector<std::string> requests = GameSpy::RequestToRequests(Util::Buffer::ToString(combined_buffer));
92 
93  for(const std::string& request : requests)
94  {
95  this->_LogTransaction("-->", request);
96 
97  this->onRequest(request);
98  }
99  }
100 
101  this->Disconnect();
102 }
103 
105 {
106  this->_session.status = "|s|0|ss|Offline";
107 
108  this->_SendNewStatus();
109 
110  this->Close();
111  g_gpcm_server->onClientDisconnect(*this);
112 }
113 
115 {
116  GPCM::Session copy_session = this->_session;
117 
118  return copy_session;
119 }
120 
121 // Events
122 
123 void GPCM::Client::onRequest(const std::string& request)
124 {
125  GameSpy::Parameter parameter = GameSpy::Request2Parameter(request);
126 
127  // Find function name
128  std::string action = parameter[0];
129 
130  auto it = mRequestActions.find(action);
131  if (it != mRequestActions.end())
132  {
133  // Get Function address
134  RequestActionFunc func = it->second;
135 
136  // Execute action function with class object.
137  (this->*(func))(parameter);
138  }
139  else
140  {
141  Logger::warning("action \"" + action + "\" not implemented!", Server::Type::GPCM);
142 
143  this->Disconnect();
144  }
145 }
146 
148 {
149  this->_session.challenge = Util::generateRandomChallenge();
150 
151  Logger::info(this->GetAddress() + " --> Challenge", Server::Type::GPCM);
152 
153  std::string response = GameSpy::Parameter2Response({
154  "lc", "1",
155  "challenge", this->_session.challenge,
156  "id", "1",
157  "final"
158  });
159 
160  this->Send(response);
161 
162  this->_LogTransaction("<--", response);
163 }
164 
165 /*
166  Response:
167  \lc\1\challenge\Thc3BZFhfv\id\1\final\
168  Request:
169  \login\\challenge\otMLwtUZyLCV85ERGzg5mVER8nknWX0B\uniquenick\IamLupo\response\b836c6a19b240bb9fb2a31179b28062b\firewall\1\port\0\productid\10307\gamename\bfield1942ps2\namespaceid\13\id\1\final\
170  Response:
171  \lc\2\sesskey\1\userid\64679\profileid\10036819\uniquenick\IamLupo\lt\ee5540bbd764b321378ccedd\proof\70eabd6f5b004fc0b42056ac3cef5c7b\id\1\final\
172 
173  To do:
174  - The client sends in the login request parameter "response". We have to figure out how this response is been generated.
175 */
176 
177 void GPCM::Client::requestLogin(const GameSpy::Parameter& parameter)
178 {
179  if(parameter.size() != 21 ||
180  !Util::isAscii(parameter[3]) ||
181  !Util::isAscii(parameter[5]) ||
182  !Util::isAscii(parameter[7]) ||
183  !Util::isAscii(parameter[19]))
184  {
185  return;
186  }
187 
188  std::string client_challenge = parameter[3];
189  std::string uniquenick = parameter[5];
190  std::string client_response = parameter[7];
191  std::string id = parameter[19];
192  std::string server_challenge = this->_session.challenge;
193 
194  Logger::info(this->GetAddress() + " --> Login", Server::Type::GPCM);
195 
196  Battlefield::Player player;
197  player.SetUniquenickWithoutClanTag(uniquenick);
198 
199  // Get player information
200  g_database->queryPlayerByUniquenick(player);
201 
202  player.SetUniquenick(uniquenick);
203 
204  if(
205  !player.isVerified() ||
206  client_response != GameSpy::LoginResponse(player.GetPassword(), player.GetUniquenick(), client_challenge, server_challenge)
207  )
208  {
209  std::string response = GameSpy::Parameter2Response({
210  "error", "",
211  "err", "260",
212  "fatal", "",
213  "errmsg", "The password provided was incorrect.",
214  "id", "1",
215  "final"
216  });
217 
218  this->Send(response);
219 
220  this->_LogTransaction("<--", response);
221 
222  return;
223  }
224 
225  // Generate proof
226  std::string proof = GameSpy::LoginProof(player.GetPassword(), player.GetUniquenick(), client_challenge, server_challenge);
227 
228  // Disconnect old session
229  this->Disconnect(player.GetProfileId());
230 
231  // Save session profileid
232  this->_session.profileid = player.GetProfileId();
233  this->_session.authtoken = Util::generateRandomAuthtoken();
234  this->_session.status = "|s|1|ss|Online";
235 
236  std::string response = GameSpy::Parameter2Response({
237  "lc", "2",
238  "sesskey", "1",
239  "userid", std::to_string(player.GetUserId()),
240  "profileid", std::to_string(player.GetProfileId()),
241  "uniquenick", player.GetUniquenick(),
242  "lt", this->_session.authtoken,
243  "proof", proof,
244  "id", id,
245  "final"
246  });
247  this->Send(response);
248  this->_LogTransaction("<--", response);
249 
250  g_database->updatePlayerLastLogin(player, this->GetIP());
251  Logger::info(this->GetAddress() + " <-- User \"" + player.GetUniquenick() + "\" logged in.", Server::Type::GPCM);
252 
253  // Get player friends
254  g_database->queryPlayerFriendsByProfileId(player);
255  std::vector<int> player_friends = player.GetFriends();
256 
257  response = GameSpy::Parameter2Response({
258  "bdy", std::to_string(player_friends.size()),
259  "list", Util::ToString(player_friends),
260  "final"
261  });
262  this->Send(response);
263  this->_LogTransaction("<--", response);
264 
265  // Sync friends status
266  this->_sync_friends = player_friends;
267  this->_SyncFriends();
268 }
269 
270 /*
271  Request:
272  \inviteto\\sesskey\1\products\10307\final\
273 */
274 
275 void GPCM::Client::requestInviteTo(const GameSpy::Parameter& parameter)
276 {
277  if(parameter.size() != 7)
278  {
279  return;
280  }
281 }
282 
283 /*
284  Request:
285  \getprofile\\sesskey\1\profileid\10036819\id\3\final\
286  \getprofile\\sesskey\1\profileid\10036029\id\4\final\
287  \getprofile\\sesskey\1\profileid\10036113\id\5\final\
288  \getprofile\\sesskey\1\profileid\10036271\id\6\final\
289  \getprofile\\sesskey\1\profileid\10036444\id\7\final\
290  \getprofile\\sesskey\1\profileid\10036492\id\8\final\
291  \getprofile\\sesskey\1\profileid\10036585\id\9\final\
292  \getprofile\\sesskey\1\profileid\10037053\id\10\final\
293  Response:
294  \pi\\profileid\10036819\userid\64679\nick\IamLupo@6507BAD7\uniquenick\IamLupo\email\help0001@gmail.com\sex\0\birthday\0\countrycode\\aim\\videocard1string\\videocard2string\\osstring\\id\3\sig\d41d8cd98f00b204e9800998ecf8427e\final\
295  \pi\\profileid\10036492\userid\64397\nick\Tha_j0k3r@B9708875\uniquenick\Tha_j0k3r\aim\\videocard1string\\videocard2string\\osstring\\id\8\sig\d41d8cd98f00b204e9800998ecf8427e\final\
296  \pi\\profileid\10036271\userid\64222\nick\BostonBrew@7F291349\uniquenick\BostonBrew\aim\\videocard1string\\videocard2string\\osstring\\id\6\sig\d41d8cd98f00b204e9800998ecf8427e\final\
297  \pi\\profileid\10036029\userid\64015\nick\Shikamaru@93753F85\uniquenick\Shikamaru\aim\\videocard1string\\videocard2string\\osstring\\id\4\sig\d41d8cd98f00b204e9800998ecf8427e\final\
298  \pi\\profileid\10036444\userid\64362\nick\CHURRU-GP@BDFB5921\uniquenick\CHURRU-GP\aim\\videocard1string\\videocard2string\\osstring\\id\7\sig\d41d8cd98f00b204e9800998ecf8427e\final\
299  \pi\\profileid\10036113\userid\64089\nick\Scram@E3883FAF\uniquenick\Scram\aim\\videocard1string\\videocard2string\\osstring\\id\5\sig\d41d8cd98f00b204e9800998ecf8427e\final\
300  \pi\\profileid\10036585\userid\64473\nick\NPC@A15FEDF3\uniquenick\NPC\aim\\videocard1string\\videocard2string\\osstring\\id\9\sig\d41d8cd98f00b204e9800998ecf8427e\final\
301  \pi\\profileid\10037053\userid\64872\nick\-Gh0sTeD-@7DE1959D\uniquenick\-Gh0sTeD-\aim\\videocard1string\\videocard2string\\osstring\\id\10\sig\d41d8cd98f00b204e9800998ecf8427e\final\
302 */
303 
304 void GPCM::Client::requestGetProfile(const GameSpy::Parameter& parameter)
305 {
306  if(parameter.size() != 9 ||
307  !Util::isAscii(parameter[5]) ||
308  !Util::isAscii(parameter[7]))
309  {
310  return;
311  }
312 
313  std::string profileid = parameter[5];
314  std::string id = parameter[7];
315 
316  Logger::info(this->GetAddress() + " --> GetProfile: " + profileid, Server::Type::GPCM);
317 
318  Battlefield::Player player;
319  player.SetProfileId(profileid);
320 
321  // Get player information
322  g_database->queryPlayerByProfileId(player);
323 
324  std::string response;
325 
326  if(player.GetUserId() != -1)
327  {
328  std::string uniquenick = player.GetUniquenick();
329 
330  // Update unique nick if is in clan
331  Battlefield::Clan clan;
332  g_database->queryClanByProfileId(clan, player);
333  if(clan.GetClanId() != -1)
334  {
335  uniquenick = clan.GetTag() + " " + uniquenick;
336  }
337 
338  response = GameSpy::Parameter2Response({
339  "pi", "",
340  "profileid", std::to_string(player.GetProfileId()),
341  "userid", std::to_string(player.GetUserId()),
342  "nick", player.GetNick(),
343  "uniquenick", uniquenick,
344  "email", "<private>",
345  "sex", "0",
346  "birthday", "0",
347  "countrycode", "",
348  "aim", "",
349  "videocard1string", "",
350  "videocard2string", "",
351  "osstring", "",
352  "id", id,
353  "sig", "d41d8cd98f00b204e9800998ecf8427e",
354  "final"
355  });
356  }
357  else
358  {
359  response = GameSpy::Parameter2Response({
360  "pi", "",
361  "profileid", profileid,
362  "userid", std::to_string(-1),
363  "nick", "<Unknown>",
364  "uniquenick", "<Unknown>",
365  "email", "<Unknown>",
366  "sex", "0",
367  "birthday", "0",
368  "countrycode", "",
369  "aim", "",
370  "videocard1string", "",
371  "videocard2string", "",
372  "osstring", "",
373  "id", id,
374  "sig", "d41d8cd98f00b204e9800998ecf8427e",
375  "final"
376  });
377  }
378 
379  this->Send(response);
380 
381  this->_LogTransaction("<--", response);
382 
383  this->_SyncFriends();
384 }
385 
386 /*
387  Request:
388  \status\1\sesskey\1\statstring\Resting\locstring\bfield1942ps2:/\final\
389  \status\2\sesskey\1\statstring\Playing\locstring\bfield1942ps2:/[EU]CTF-SERVER1@78.47.184.23:3659\final\
390  \status\1\sesskey\1\statstring\Online\locstring\bfield1942ps2:/\final\
391 
392  Response:
393  \bm\100\f\10036271\msg\|s|0|ss|Offline\final\
394  \bm\100\f\10036029\msg\|s|0|ss|Offline\final\
395  \bm\100\f\10037810\msg\|s|2|ss|Playing|ls|bfield1942ps2:/[EU]CTF-SERVER1@78.47.184.23:3659|ip|2970240317|p|44223\final\
396 */
397 
398 void GPCM::Client::requestStatus(const GameSpy::Parameter& parameter)
399 {
400  if(parameter.size() != 9 ||
401  !Util::isAscii(parameter[5]) ||
402  !Util::isAscii(parameter[7]))
403  {
404  return;
405  }
406 
407  std::string statstring = parameter[5];
408  std::string locstring = parameter[7];
409 
410  // Update status
411  if(statstring == "Resting")
412  {
413  this->_session.status = "|s|1|ss|Resting";
414  }
415  else if(statstring == "Online")
416  {
417  this->_session.status = "|s|1|ss|Online";
418  }
419  else if(statstring == "Playing")
420  {
421  this->_session.status = "|s|2|ss|Playing";
422  }
423 
424  if(locstring != "")
425  {
426  this->_session.status += "|ls|" + locstring;
427  }
428 
429  this->_SendNewStatus();
430 }
431 
432 /*
433  Request:
434  \bm\1\sesskey\1\t\10036271\msg\aaa\final\
435 
436  Response to target:
437  \bm\1\f\10037318\msg\awesome\final\
438 */
439 
440 void GPCM::Client::requestBm(const GameSpy::Parameter& parameter)
441 {
442  if(parameter.size() != 9 ||
443  !Util::isAscii(parameter[5]) ||
444  !Util::UTF8::isValid(parameter[7]))
445  {
446  return;
447  }
448 
449  std::string profileid = parameter[5];
450  std::string msg = parameter[7];
451 
452  if(msg.find("BFMCC-GAMEVALIDREQ ") != std::string::npos || msg.find("BFMCC-GAMEVALIDRESP ") != std::string::npos)
453  {
454  return;
455  }
456 
457  Battlefield::Player target_player;
458  target_player.SetProfileId(profileid);
459 
460  // To-do: Needs extra check that accually friends with the target
462  this->_session.profileid,
463  target_player.GetProfileId(),
464  "1",
465  msg
466  );
467 }
468 
469 /*
470  Request:
471  \addbuddy\\sesskey\1\newprofileid\10037827\reason\\final\
472 
473  Response:
474  \bm\2\f\10037049\msg\|signed|d41d8cd98f00b204e9800998ecf8427e\final\
475 
476  Extra:
477  \error\\err\1539\errmsg\The profile requested is already a buddy.\final\
478 */
479 
480 void GPCM::Client::requestAddBuddy(const GameSpy::Parameter& parameter)
481 {
482  if(parameter.size() != 9 ||
483  !Util::isAscii(parameter[5]) ||
484  !Util::isAscii(parameter[7]))
485  {
486  return;
487  }
488 
489  std::string target_profileid = parameter[5];
490  std::string reason = parameter[7];
491 
492  Battlefield::Player target_player;
493  target_player.SetProfileId(target_profileid);
494 
496  this->_session.profileid,
497  target_player.GetProfileId(),
498  "2",
499  reason + "|signed|d41d8cd98f00b204e9800998ecf8427e"
500  );
501 }
502 
503 /*
504  Request:
505  \authadd\\sesskey\1\fromprofileid\10036819\sig\d41d8cd98f00b204e9800998ecf8427e\final\
506  \authadd\\sesskey\1\fromprofileid\10037049\sig\d41d8cd98f00b204e9800998ecf8427e\final\
507 
508  Response:
509  \bm\4\f\10037049\msg\I have authorized your request to add me to your list|signed|d41d8cd98f00b204e9800998ecf8427e\final\
510  \error\\err\1539\errmsg\The profile requested is already a buddy.\final\
511 */
512 
513 void GPCM::Client::requestAuthAdd(const GameSpy::Parameter& parameter)
514 {
515  if(parameter.size() != 9)
516  {
517  return;
518  }
519 
520  std::string target_profileid = parameter[5];
521  std::string sig = parameter[7];
522 
523  Battlefield::Player player;
524  Battlefield::Player target_player;
525 
526  // Set profileid
527  player.SetProfileId(this->_session.profileid);
528  target_player.SetProfileId(target_profileid);
529 
530  // Send authorization to target player
532  this->_session.profileid,
533  target_player.GetProfileId(),
534  "4",
535  "I have authorized your request to add me to your list|signed|d41d8cd98f00b204e9800998ecf8427e"
536  );
537 
538  // Send player status to target player
540  this->_session.profileid,
541  target_player.GetProfileId(),
542  "100",
543  this->_session.status
544  );
545 
546  // Get friends
547  g_database->queryPlayerFriendsByProfileId(player);
548  std::vector<int> player_friends = player.GetFriends();
549 
550  auto it = std::find(player_friends.begin(), player_friends.end(), target_player.GetProfileId());
551 
552  if(it == player_friends.end())
553  {
554  // Save friendship
555  g_database->insertPlayerFriend(player, target_player);
556  }
557  else
558  {
559  std::string response = GameSpy::Parameter2Response({
560  "error", "",
561  "err", "1539",
562  "errmsg", "The profile requested is already a buddy.",
563  "final"
564  });
565 
566  this->Send(response);
567 
568  this->_LogTransaction("<--", response);
569  }
570 }
571 
572 /*
573  Request:
574  \revoke\\sesskey\1\profileid\10036819\final\
575  \revoke\\sesskey\1\profileid\10037049\final\
576 
577  Response:
578  \bm\6\f\10036819\msg\I have revoked you from my list.|signed|d41d8cd98f00b204e9800998ecf8427e\final\
579  \bm\6\f\10037049\msg\I have revoked you from my list.|signed|d41d8cd98f00b204e9800998ecf8427e\final\
580 */
581 
582 void GPCM::Client::requestRevoke(const GameSpy::Parameter& parameter)
583 {
584  if(parameter.size() != 7 ||
585  !Util::isAscii(parameter[5]))
586  {
587  return;
588  }
589 
590  std::string target_profileid = parameter[5];
591 
592  Battlefield::Player player;
593  Battlefield::Player target_player;
594 
595  // Set profileid
596  player.SetProfileId(this->_session.profileid);
597  target_player.SetProfileId(target_profileid);
598 
600  target_player.GetProfileId(),
601  player.GetProfileId(),
602  "6",
603  "I have revoked you from my list.|signed|d41d8cd98f00b204e9800998ecf8427e"
604  );
605 }
606 
607 /*
608  Request:
609  \delbuddy\\sesskey\1\delprofileid\10036819\final\
610 
611  Response:
612  \error\\err\2817\errmsg\The buddy to be deleted is not a buddy. \final\
613 */
614 
615 void GPCM::Client::requestDeleteBuddy(const GameSpy::Parameter& parameter)
616 {
617  if(parameter.size() != 7 ||
618  !Util::isAscii(parameter[5]))
619  {
620  return;
621  }
622 
623  std::string target_profileid = parameter[5];
624 
625  Battlefield::Player player;
626  Battlefield::Player target_player;
627 
628  // Set profileid
629  player.SetProfileId(this->_session.profileid);
630  target_player.SetProfileId(target_profileid);
631 
632  // Get friends
633  g_database->queryPlayerFriendsByProfileId(player);
634  std::vector<int> player_friends = player.GetFriends();
635 
636  auto it = std::find(player_friends.begin(), player_friends.end(), target_player.GetProfileId());
637  if(it != player_friends.end())
638  {
639  g_database->removePlayerFriend(player, target_player);
640  }
641  else
642  {
643  std::string response = GameSpy::Parameter2Response({
644  "error", "",
645  "err", "2817",
646  "errmsg", "The buddy to be deleted is not a buddy. ",
647  "final"
648  });
649 
650  this->Send(response);
651 
652  this->_LogTransaction("<--", response);
653  }
654 }
655 
656 /*
657  Request:
658  \pinvite\\sesskey\1\profileid\10037095\productid\10307\final\
659 */
660 
661 void GPCM::Client::requestPlayerInvite(const GameSpy::Parameter& parameter)
662 {
663  if(parameter.size() != 9 ||
664  !Util::isAscii(parameter[5]))
665  {
666  return;
667  }
668 
669  std::string target_profileid = parameter[5];
670 
671  Battlefield::Player target_player;
672 
673  // Set profileid
674  target_player.SetProfileId(target_profileid);
675 
676  // Send player status to target player
678  this->_session.profileid,
679  target_player.GetProfileId(),
680  "100",
681  this->_session.status
682  );
683 
684  // Send game invite
686  this->_session.profileid,
687  target_player.GetProfileId(),
688  "101",
689  "|p|1337|l|bfield1942ps2:/YOLO@1.1.1.1:1337"
690  );
691 }
692 
693 /*
694  Request:
695  \logout\\sesskey\1\final\
696 */
697 
698 void GPCM::Client::requestLogout(const GameSpy::Parameter& parameter)
699 {
700  //if(parameter.size() != 5)
701  //{
702  // return;
703  //}
704 
705  Battlefield::Player player;
706 
707  player.SetProfileId(this->_session.profileid);
708 
709  g_database->queryPlayerByProfileId(player);
710 
711  Logger::info("User \"" + player.GetUniquenick() + "\" logged out", Server::Type::GPCM);
712 }
713 
714 // Private functions
715 
716 void GPCM::Client::_LogTransaction(const std::string& direction, const std::string& response) const
717 {
718  std::shared_lock<std::shared_mutex> guard2(g_settings_mutex); // settings lock (read)
719 
720  if ((g_logger_mode & Logger::Mode::Development) == 0)
721  {
722  return;
723  }
724 
725  bool show_console = (g_settings["gpcm"]["show_requests"].asBool() && direction == "-->") ||
726  (g_settings["gpcm"]["show_responses"].asBool() && direction == "<--");
727 
728  Logger::info(this->GetAddress() + " " + direction + " " + response,
729  Server::Type::GPCM, show_console);
730 }
731 
733 {
734  // Get friends
735  Battlefield::Player player;
736  player.SetProfileId(this->_session.profileid);
737 
738  g_database->queryPlayerFriendsByProfileId(player);
739 
740  // Send update to online players
741  for(int friend_profileid : player.GetFriends())
742  {
743  // Send player status to friend
745  this->_session.profileid,
746  friend_profileid,
747  "100",
748  this->_session.status
749  );
750  }
751 }
752 
754 {
755  if(this->_sync_friends.size() >= 1)
756  {
757  int id = this->_sync_friends.size() - 1;
758  int friend_profileid = this->_sync_friends[id];
759 
760  GPCM::Session session = GPCM::Client::findSessionByProfileId(friend_profileid);
761  std::string response;
762 
763  if(session.profileid != -1)
764  {
765  response = GameSpy::Parameter2Response({
766  "bm", "100",
767  "f", std::to_string(friend_profileid),
768  "msg", session.status,
769  "final"
770  });
771  }
772  else
773  {
774  response = GameSpy::Parameter2Response({
775  "bm", "100",
776  "f", std::to_string(friend_profileid),
777  "msg", "|s|0|ss|Offline",
778  "final"
779  });
780  }
781 
782  this->Send(response);
783 
784  this->_LogTransaction("<--", response);
785 
786  // Erase last profileid from list
787  this->_sync_friends.pop_back();
788  }
789 }
790 
791 // Static functions
792 
794 {
795  GPCM::Session session;
796 
797  for(std::shared_ptr<Net::Socket> client : g_gpcm_server->GetClients())
798  {
799  std::shared_ptr<GPCM::Client> gpcm_client = std::dynamic_pointer_cast<GPCM::Client>(client);
800 
801  // Find session
802  session = gpcm_client->GetSession();
803 
804  if(session.profileid == profileid)
805  {
806  session.client = gpcm_client;
807  return session;
808  }
809  }
810 
811  return {};
812 }
813 
815 {
816  GPCM::Session session;
817 
818  for(std::shared_ptr<Net::Socket> client : g_gpcm_server->GetClients())
819  {
820  std::shared_ptr<GPCM::Client> gpcm_client = std::dynamic_pointer_cast<GPCM::Client>(client);
821 
822  // Find session
823  session = gpcm_client->GetSession();
824 
825  if(session.authtoken == authtoken)
826  {
827  session.client = gpcm_client;
828  return session;
829  }
830  }
831 
832  return {};
833 }
834 
835 void GPCM::Client::SendBuddyMessage(int profileid, int target_profileid, const std::string& bm, const std::string& message)
836 {
837  GPCM::Session target_session = findSessionByProfileId(target_profileid);
838 
839  if(target_session.profileid != -1) // Check target is online
840  {
841  // Save messages in database
842  if(bm == "1")
843  {
844  // Get ip of sender
845  std::string ip = "";
846  GPCM::Session session = findSessionByProfileId(profileid);
847 
848  if(session.profileid != -1) // Check sender is online
849  {
850  ip = session.client->GetIP();
851  }
852 
853  g_database->insertChat(profileid, ip, target_profileid, message);
854  }
855 
856  std::string response = GameSpy::Parameter2Response({
857  "bm", bm,
858  "f", std::to_string(profileid),
859  "msg", message,
860  "final"
861  });
862 
863  target_session.client->Send(response);
864 
865  target_session.client->_LogTransaction("<--", response);
866  }
867 }
868 
869 void GPCM::Client::Disconnect(int profileid)
870 {
871  GPCM::Session session;
872 
873  for(std::shared_ptr<Net::Socket> client : g_gpcm_server->GetClients())
874  {
875  std::shared_ptr<GPCM::Client> gpcm_client = std::dynamic_pointer_cast<GPCM::Client>(client);
876 
877  // Find session
878  session = gpcm_client->GetSession();
879 
880  if(session.profileid == profileid)
881  {
882  gpcm_client.get()->Disconnect();
883  }
884  }
885 }
886 
888 {
889  Logger::info("Heartbeat started", Server::GPCM);
890 
891  while(true)
892  {
893  std::this_thread::sleep_for (std::chrono::seconds(60));
894 
895  for(std::shared_ptr<Net::Socket> client : g_gpcm_server->GetClients())
896  {
897  std::shared_ptr<GPCM::Client> gpcm_client = std::dynamic_pointer_cast<GPCM::Client>(client);
898 
899  std::string response = GameSpy::Parameter2Response({
900  "ka", "",
901  "final"
902  });
903 
904  gpcm_client.get()->Send(response);
905 
906  gpcm_client.get()->_LogTransaction("<--", response);
907  }
908  }
909 }
910 
Represents a clan in the Battlefield game.
Definition: clan.h:54
Represents a player with extended statistics.
Definition: player.h:38
bool queryPlayerByProfileId(Battlefield::Player &player)
Queries a player by their profile ID.
bool updatePlayerLastLogin(Battlefield::Player &player, const std::string &ip)
Updates the last login IP address and date for a player.
bool insertPlayerFriend(const Battlefield::Player &player, const Battlefield::Player &target_player)
Inserts a friendship relation between two players into the database.
bool queryPlayerFriendsByProfileId(Battlefield::Player &player)
Queries a player's friends by their profile ID.
bool queryPlayerByUniquenick(Battlefield::Player &player)
Queries a player by their unique nickname.
bool removePlayerFriend(const Battlefield::Player &player, const Battlefield::Player &target_player)
Removes a friendship relation between two players from the database.
bool queryClanByProfileId(Battlefield::Clan &clan, const Battlefield::Player &player)
Queries a clan by a player's profile ID.
bool insertChat(int profileid, const std::string &ip, int target_profileid, const std::string &msg)
Inserts a chat message into the database.
Definition: chat.cpp:10
Represents a GPCM client.
Definition: gpcm/client.h:34
void requestChallenge()
Sends a challenge to the client.
void requestInviteTo(const GameSpy::Parameter &parameter)
Process an invite request from the client.
void requestGetProfile(const GameSpy::Parameter &parameter)
Process a get-profile request from the client.
void requestAuthAdd(const GameSpy::Parameter &parameter)
Process an auth-add request from the client.
void Disconnect()
Disconnects the client.
void Listen()
Listens for incoming messages from the GPCM client.
Definition: gpcm/client.cpp:45
void _LogTransaction(const std::string &direction, const std::string &response) const
Log a transaction with direction and response.
~Client()
Destroys the GPCM client.
Definition: gpcm/client.cpp:40
void requestRevoke(const GameSpy::Parameter &parameter)
Process a revoke request from the client.
void requestPlayerInvite(const GameSpy::Parameter &parameter)
Process a player-invite request from the client.
void _SyncFriends()
Synchronizes friends with the client.
static GPCM::Session findSessionByProfileId(int profileid)
Finds a session by profile ID.
static void SendBuddyMessage(int profileid, int target_profileid, const std::string &bm, const std::string &message)
Sends a buddy message from one profile ID to another.
Client(int socket, struct sockaddr_in address)
Constructs a new GPCM client.
Definition: gpcm/client.cpp:33
static void Heartbeat()
Heartbeat function to manage client connections.
void requestStatus(const GameSpy::Parameter &parameter)
Process a status request from the client.
void requestBm(const GameSpy::Parameter &parameter)
Process a BM request from the client.
void requestDeleteBuddy(const GameSpy::Parameter &parameter)
Process a delete-buddy request from the client.
void requestLogout(const GameSpy::Parameter &parameter)
Process a logout request from the client.
void onRequest(const std::string &msg)
Event handler for incoming messages from the client.
static GPCM::Session findSessionByAuthtoken(const std::string &authtoken)
Finds a session by authentication token.
void requestAddBuddy(const GameSpy::Parameter &parameter)
Process an add-buddy request from the client.
void requestLogin(const GameSpy::Parameter &parameter)
Process a login request from the client.
void _SendNewStatus() const
Sends a new status to the client.
GPCM::Session GetSession() const
Get the session information of the client.
void UpdateLastRecievedTime()
Updates the last received time to the current system time.
Definition: socket.cpp:112
struct sockaddr_in _address
Definition: socket.h:18
int _socket
Definition: socket.h:17
std::vector< std::shared_ptr< Net::Socket > > GetClients()
Get the vector of client sockets connected to this server.
Definition: server.cpp:86
@ GPCM
Definition: server.h:19
void onClientDisconnect(const Net::Socket &client)
Called when a client disconnects from the server.
Definition: server.cpp:336
Represents a session with a GPCM client.
Definition: gpcm/client.h:19