BF2MC-Matchmaker
qr/client.cpp
1 #include <map>
2 #include <vector>
3 
4 #include <logger.h>
5 #include <util.h>
6 #include <settings.h>
7 #include <globals.h>
8 #include <database.h>
9 #include <qr/constants.h>
10 #include <battlefield/gameserver.h>
11 
12 #include <qr/client.h>
13 
14 typedef void (QR::Client::*RequestActionFunc)(const std::vector<unsigned char>&) const;
15 
16 static std::map<int, RequestActionFunc> mRequestActions =
17 {
18  { REQUEST_CHALLENGE, &QR::Client::requestChallenge }, // Done
19  { REQUEST_AVAILABLE, &QR::Client::requestAvailable }, // Done
20  { REQUEST_HEARTBEAT, &QR::Client::requestHeartbeat }, // Done
21  { REQUEST_KEEPALIVE, &QR::Client::requestKeepAlive }, // Done
22 };
23 
24 QR::Client::Client(int socket, struct sockaddr_in address)
25 {
26  this->_socket = socket;
27  this->_address = address;
28 }
29 
31 {
32 
33 }
34 
35 // Events
36 
37 void QR::Client::onRequest(const std::vector<unsigned char>& request)
38 {
39  this->_LogTransaction("-->", Util::Buffer::ToString(request));
40 
41  int action = request[0];
42 
43  // Find function name
44  auto it = mRequestActions.find(action);
45  if (it != mRequestActions.end())
46  {
47  // Get Function address
48  RequestActionFunc func = it->second;
49 
50  // Execute action function with class object.
51  (this->*(func))(request);
52  }
53  else
54  {
55  Logger::warning("action \"" + std::to_string(action) + "\" not implemented!", Server::Type::QR);
56  }
57 }
58 
59 void QR::Client::requestChallenge(const std::vector<unsigned char>& request) const
60 {
61  std::vector<unsigned char> response = {
62  RESPONSE_MAGIC_1, RESPONSE_MAGIC_2, // Magic
63  RESPONSE_CHALLENGE, // Challenge response action
64  request[1], request[2], request[3], request[4], // Instance key
65  };
66 
67  this->UDPSend(response);
68 
69  this->_LogTransaction("<--", Util::Buffer::ToString(response));
70 }
71 
72 void QR::Client::requestAvailable(const std::vector<unsigned char>& request) const
73 {
74  std::vector<unsigned char> response = {
75  RESPONSE_MAGIC_1, RESPONSE_MAGIC_2,
76  RESPONSE_AVAILABLE,
77  0x0, 0x0, 0x0, 0x0
78  };
79 
80  this->UDPSend(response);
81 
82  this->_LogTransaction("<--", Util::Buffer::ToString(response));
83 }
84 
85 /*
86  Request:
87  0030 03 e0 dd 81 84 6c 6f 63 61 6c 69 70 30 00 31 30 .....localip0.10
88  0040 2e 31 30 2e 31 30 2e 31 31 30 00 6c 6f 63 61 6c .10.10.110.local
89  0050 70 6f 72 74 00 33 36 35 38 00 6e 61 74 6e 65 67 port.3658.natneg
90  0060 00 31 00 67 61 6d 65 6e 61 6d 65 00 62 66 69 65 .1.gamename.bfie
91  0070 6c 64 31 39 34 32 70 73 32 00 68 6f 73 74 6e 61 ld1942ps2.hostna
92  0080 6d 65 00 5b 43 51 5d 42 46 32 4d 43 2d 49 61 6d me.[CQ]BF2MC-Iam
93  0090 4c 75 70 6f 00 68 6f 73 74 70 6f 72 74 00 33 36 Lupo.hostport.36
94  00a0 35 38 00 67 61 6d 65 76 65 72 00 56 31 2e 33 31 58.gamever.V1.31
95  00b0 61 00 6d 61 70 6e 61 6d 65 00 32 00 67 61 6d 65 a.mapname.2.game
96  00c0 74 79 70 65 00 63 6f 6e 71 75 65 73 74 00 67 61 type.conquest.ga
97  00d0 6d 65 76 61 72 69 61 6e 74 00 42 6f 72 64 65 72 mevariant.Border
98  00e0 00 6e 75 6d 70 6c 61 79 65 72 73 00 31 00 6e 75 .numplayers.1.nu
99  00f0 6d 74 65 61 6d 73 00 32 00 6d 61 78 70 6c 61 79 mteams.2.maxplay
100  0100 65 72 73 00 32 34 00 67 61 6d 65 6d 6f 64 65 00 ers.24.gamemode.
101  0110 6f 70 65 6e 70 6c 61 79 69 6e 67 00 74 65 61 6d openplaying.team
102  0120 70 6c 61 79 00 30 00 66 72 61 67 6c 69 6d 69 74 play.0.fraglimit
103  0130 00 30 00 74 65 61 6d 66 72 61 67 6c 69 6d 69 74 .0.teamfraglimit
104  0140 00 30 00 74 69 6d 65 6c 69 6d 69 74 00 31 32 30 .0.timelimit.120
105  0150 30 00 74 69 6d 65 65 6c 61 70 73 65 64 00 39 30 0.timeelapsed.90
106  0160 30 00 70 61 73 73 77 6f 72 64 00 30 00 6e 72 00 0.password.0.nr.
107  0170 31 00 78 72 00 32 30 00 66 66 00 31 00 6d 63 00 1.xr.20.ff.1.mc.
108  0180 31 00 72 76 00 72 65 74 61 69 6c 00 67 63 00 30 1.rv.retail.gc.0
109  0190 00 72 63 00 31 00 73 72 00 31 00 6e 69 00 2d 32 .rc.1.sr.1.ni.-2
110  01a0 31 34 37 34 38 33 36 34 38 00 78 69 00 32 31 34 147483648.xi.214
111  01b0 37 34 38 33 36 34 37 00 71 6d 00 30 00 63 6c 00 7483647.qm.0.cl.
112  01c0 31 30 30 2e 32 31 36 33 33 35 2e 61 6c 70 68 61 100.216335.alpha
113  01d0 00 72 65 67 69 6f 6e 00 36 35 35 33 36 00 6d 61 .region.65536.ma
114  01e0 70 00 62 72 69 64 67 65 74 6f 6f 66 61 72 00 00 p.bridgetoofar..
115  01f0 00 01 70 6c 61 79 65 72 5f 00 73 63 6f 72 65 5f ..player_.score_
116  0200 00 73 6b 69 6c 6c 5f 00 70 69 6e 67 5f 00 74 65 .skill_.ping_.te
117  0210 61 6d 5f 00 64 65 61 74 68 73 5f 00 70 69 64 5f am_.deaths_.pid_
118  0220 00 00 49 61 6d 4c 75 70 6f 00 30 00 72 6f 6f 6b ..IamLupo.0.rook
119  0230 69 65 00 30 00 30 00 30 00 31 30 30 33 36 38 31 ie.0.0.0.1003681
120  0240 39 00 00 02 74 65 61 6d 5f 74 00 73 63 6f 72 65 9...team_t.score
121  0250 5f 74 00 00 43 48 00 34 35 30 00 55 53 00 34 35 _t..CH.450.US.45
122  0260 30 00 0.
123 
124  First bytes is the action code.
125  next 4 bytes are the session code.
126  The rest are response data to the action. In this the status from the server.
127 
128  Response:
129  0030 fe fd 01 e0 dd 81 84 30 30 30 30 30 30 30 30 30 .......000000000
130  0040 30 30 30 30 30 30 30 30 30 30 30 00 00000000000.
131 
132  First 2 bytes are the magic QR code (0xfefd)
133  Next byte is the action request to the server. In this case (0x01) is requesting a challenge.
134  All the bytes after are the input for the challenge, this can be random data
135 */
136 
137 void QR::Client::requestHeartbeat(const std::vector<unsigned char>& request) const
138 {
139  size_t offset = 5;
140  std::string key, value, player, score, skill, ping, team, deaths, pid;
141  Battlefield::GameServer game_server;
142 
143  // Set game server ip
144  game_server.SetIp(this->GetIP());
145  game_server.SetPort(this->GetPort());
146 
147  // Check game server information in database
148  g_database->queryGameServerByIpAndPort(game_server);
149 
150  // Make some records empty to avoid old values
151  game_server.SetClan1Id(-1);
152  game_server.SetClan1Name("");
153  game_server.SetClan1Claimed(0);
154  game_server.SetClan2Id(-1);
155  game_server.SetClan2Name("");
156  game_server.SetClan2Claimed(0);
157 
158  // Read game server information
159  while(Util::Buffer::ReadString(request, offset, key) && key.size() > 0)
160  {
161  // Read value
162  Util::Buffer::ReadString(request, offset, value);
163 
164  auto it = Battlefield::GameServer::SetterMap.find(key);
165  if(it != Battlefield::GameServer::SetterMap.end())
166  {
167  (game_server.*(it->second))(value);
168  }
169 
170  // Debug
171  //Logger::debug(key + " = " + value);
172  }
173 
174  // Read players information
175  offset += 2;
176  if(Util::Buffer::ReadString(request, offset, player) && player.size() > 0)
177  {
178  Util::Buffer::ReadString(request, offset, score);
179  Util::Buffer::ReadString(request, offset, skill);
180  Util::Buffer::ReadString(request, offset, ping);
181  Util::Buffer::ReadString(request, offset, team);
182  Util::Buffer::ReadString(request, offset, deaths);
183  Util::Buffer::ReadString(request, offset, pid);
184  offset += 1;
185 
186  while(Util::Buffer::ReadString(request, offset, player) && player.size() > 0)
187  {
188  Util::Buffer::ReadString(request, offset, score);
189  Util::Buffer::ReadString(request, offset, skill);
190  Util::Buffer::ReadString(request, offset, ping);
191  Util::Buffer::ReadString(request, offset, team);
192  Util::Buffer::ReadString(request, offset, deaths);
193  Util::Buffer::ReadString(request, offset, pid);
194 
196 
197  gsplayer.SetName(player);
198  gsplayer.SetScore(score);
199  gsplayer.SetSkill(skill);
200  gsplayer.SetPing(ping);
201  gsplayer.SetTeam(team);
202  gsplayer.SetDeaths(deaths);
203  gsplayer.SetProfileId(pid);
204 
205  game_server.AddPlayer(gsplayer);
206 
207  // Debug
208  //Logger::debug("player = " + gsplayer.GetName());
209  //Logger::debug("score = " + std::to_string(gsplayer.GetScore()));
210  //Logger::debug("skill = " + gsplayer.GetSkill());
211  //Logger::debug("ping = " + std::to_string(gsplayer.GetPing()));
212  //Logger::debug("team = " + std::to_string(gsplayer.GetTeam()));
213  //Logger::debug("deaths = " + std::to_string(gsplayer.GetDeaths()));
214  //Logger::debug("pid = " + std::to_string(gsplayer.GetProfileId()));
215  }
216  }
217 
218  // Read teams information
219  offset += 1;
220  if(Util::Buffer::ReadString(request, offset, key) && key.size() > 0)
221  {
222  Util::Buffer::ReadString(request, offset, key);
223  offset += 1;
224 
225  // Read team 1
226  Util::Buffer::ReadString(request, offset, team);
227  Util::Buffer::ReadString(request, offset, score);
228 
229  // Set Team name and score
230  game_server.SetTeam1Name(team);
231  game_server.SetTeam1Score(score);
232 
233  // Debug
234  //Logger::debug("team0 = " + team);
235  //Logger::debug("score0 = " + score);
236 
237  // Read team 2
238  Util::Buffer::ReadString(request, offset, team);
239  Util::Buffer::ReadString(request, offset, score);
240 
241  // Set Team name and score
242  game_server.SetTeam2Name(team);
243  game_server.SetTeam2Score(score);
244 
245  // Debug
246  //Logger::debug("team1 = " + team);
247  //Logger::debug("score1 = " + score);
248  }
249 
250  // Debug
251  //game_server.Debug();
252 
253  if(game_server.GetId() == -1)
254  {
255  g_database->insertGameServer(game_server);
256  }
257  else
258  {
259  g_database->updateGameServer(game_server);
260  }
261 
262  // Send response
263  std::vector<unsigned char> response = {
264  RESPONSE_MAGIC_1, RESPONSE_MAGIC_2, // Magic
265  RESPONSE_HEARTBEAT, // Challenge response action
266  request[1], request[2], request[3], request[4], // Instance key
267  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // - 8 random characters
268  0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // - client ip in hex
269  0x30, 0x30, 0x30, 0x30, // - client port in hex
270  0x00
271  };
272 
273  this->UDPSend(response);
274 
275  this->_LogTransaction("<--", Util::Buffer::ToString(response));
276 }
277 
278 void QR::Client::requestKeepAlive(const std::vector<unsigned char>& request) const
279 {
280  std::vector<unsigned char> response = {
281  RESPONSE_MAGIC_1, RESPONSE_MAGIC_2, // Magic
282  RESPONSE_KEEPALIVE, // Challenge response action
283  request[1], request[2], request[3], request[4], // Instance key
284  0x00, 0x00, 0x00, 0x00, // time
285  };
286 
287  this->UDPSend(response);
288 
289  this->_LogTransaction("<--", Util::Buffer::ToString(response));
290 }
291 
292 // Private functions
293 
294 void QR::Client::_LogTransaction(const std::string& direction, const std::string& response) const
295 {
296  std::shared_lock<std::shared_mutex> guard(g_settings_mutex); // settings lock (read)
297 
298  if ((g_logger_mode & Logger::Mode::Development) == 0)
299  {
300  return;
301  }
302 
303  bool show_console = (g_settings["qr"]["show_requests"].asBool() && direction == "-->") ||
304  (g_settings["qr"]["show_responses"].asBool() && direction == "<--");
305 
306  Logger::info(this->GetAddress() + " " + direction + " " + response,
307  Server::Type::QR, show_console);
308 }
309 
Represents a player in a game server.
Definition: gameserver.h:385
Class representing game server information.
Definition: gameserver.h:63
void AddPlayer(const GameServerPlayer &gsplayer)
Adds a player to the game server.
Definition: gameserver.cpp:763
bool queryGameServerByIpAndPort(Battlefield::GameServer &game_server)
Queries a game server by its IP address and port.
bool insertGameServer(Battlefield::GameServer &game_server)
Inserts a game server into the database.
bool updateGameServer(const Battlefield::GameServer &game_server)
Updates a game server in the database.
struct sockaddr_in _address
Definition: socket.h:18
int _socket
Definition: socket.h:17
Represents a client for QR protocol.
Definition: qr/client.h:13
void requestAvailable(const std::vector< unsigned char > &request) const
Sends a request for available servers to the client.
Definition: qr/client.cpp:72
~Client()
Destructor for Client.
Definition: qr/client.cpp:30
void _LogTransaction(const std::string &direction, const std::string &response) const
Logs a transaction with direction and response.
Definition: qr/client.cpp:294
void requestChallenge(const std::vector< unsigned char > &request) const
Sends a request for challenge to the client.
Definition: qr/client.cpp:59
void requestKeepAlive(const std::vector< unsigned char > &request) const
Sends a keep-alive request to the client.
Definition: qr/client.cpp:278
Client(int socket, struct sockaddr_in address)
Constructor for Client.
Definition: qr/client.cpp:24
void requestHeartbeat(const std::vector< unsigned char > &request) const
Sends a heartbeat request to the client.
Definition: qr/client.cpp:137
void onRequest(const std::vector< unsigned char > &request)
Event handler for incoming requests.
Definition: qr/client.cpp:37