BF2MC-Matchmaker
game_stat.cpp
1 #include <iostream>
2 #include <mysql/mysql.h>
3 
4 #include <settings.h>
5 #include <logger.h>
6 #include <util.h>
7 
8 #include <database.h>
9 
10 bool Database::queryGameStatsByDate(Battlefield::GameStats& game_stats, const std::string& date)
11 {
12  std::lock_guard<std::mutex> guard(this->_mutex); // database lock
13 
14  std::string query = "";
15  query += "SELECT ";
16  query += " `id`, `gametype`, `gamver`, `hostname`, `mapid`, `numplayers`, `pplayers`, `tplayed`, ";
17  query += " `clanid_t0`, `clanid_t1`, `country_t0`, `country_t1`, `victory_t0`, `victory_t1`, ";
18  query += " `score0`, `score1`, `created_at` ";
19  query += "FROM ";
20  query += " `GameStats` ";
21  query += "WHERE ";
22  query += " DATE(`created_at`) = ?";
23 
24  int output_id;
25  uint8_t output_gametype;
26  char output_gamever[VARCHAR_LEN(20)];
27  char output_hostname[VARCHAR_LEN(45)];
28  uint8_t output_mapid;
29  uint8_t output_numplayers;
30  uint8_t output_pplayers;
31  uint16_t output_tplayed;
32  int output_clanid_t0;
33  int output_clanid_t1;
34  uint8_t output_country_t0;
35  uint8_t output_country_t1;
36  uint8_t output_victory_t0;
37  uint8_t output_victory_t1;
38  int16_t output_score0;
39  int16_t output_score1;
40  MYSQL_TIME output_created_at;
41 
42  // Allocate input binds
43  MYSQL_BIND* input_bind = (MYSQL_BIND *)calloc(1, sizeof(MYSQL_BIND));
44  input_bind[0].buffer_type = MYSQL_TYPE_STRING;
45  input_bind[0].buffer = const_cast<char*>(&(date[0]));
46  input_bind[0].buffer_length = date.size();
47 
48  // Allocate output binds
49  MYSQL_BIND* output_bind = (MYSQL_BIND *)calloc(17, sizeof(MYSQL_BIND));
50  output_bind[0].buffer_type = MYSQL_TYPE_LONG;
51  output_bind[0].buffer = &output_id;
52  output_bind[0].is_unsigned = false;
53  output_bind[1].buffer_type = MYSQL_TYPE_TINY;
54  output_bind[1].buffer = &output_gametype;
55  output_bind[1].is_unsigned = true;
56  output_bind[2].buffer_type = MYSQL_TYPE_VAR_STRING;
57  output_bind[2].buffer = output_gamever;
58  output_bind[2].buffer_length = VARCHAR_LEN(20);
59  output_bind[3].buffer_type = MYSQL_TYPE_VAR_STRING;
60  output_bind[3].buffer = output_hostname;
61  output_bind[3].buffer_length = VARCHAR_LEN(45);
62  output_bind[4].buffer_type = MYSQL_TYPE_TINY;
63  output_bind[4].buffer = &output_mapid;
64  output_bind[4].is_unsigned = true;
65  output_bind[5].buffer_type = MYSQL_TYPE_TINY;
66  output_bind[5].buffer = &output_numplayers;
67  output_bind[5].is_unsigned = true;
68  output_bind[6].buffer_type = MYSQL_TYPE_TINY;
69  output_bind[6].buffer = &output_pplayers;
70  output_bind[6].is_unsigned = true;
71  output_bind[7].buffer_type = MYSQL_TYPE_SHORT;
72  output_bind[7].buffer = &output_tplayed;
73  output_bind[7].is_unsigned = true;
74  output_bind[8].buffer_type = MYSQL_TYPE_LONG;
75  output_bind[8].buffer = &output_clanid_t0;
76  output_bind[8].is_unsigned = true;
77  output_bind[9].buffer_type = MYSQL_TYPE_LONG;
78  output_bind[9].buffer = &output_clanid_t1;
79  output_bind[9].is_unsigned = true;
80  output_bind[10].buffer_type = MYSQL_TYPE_TINY;
81  output_bind[10].buffer = &output_country_t0;
82  output_bind[10].is_unsigned = true;
83  output_bind[11].buffer_type = MYSQL_TYPE_TINY;
84  output_bind[11].buffer = &output_country_t1;
85  output_bind[11].is_unsigned = true;
86  output_bind[12].buffer_type = MYSQL_TYPE_TINY;
87  output_bind[12].buffer = &output_victory_t0;
88  output_bind[12].is_unsigned = true;
89  output_bind[13].buffer_type = MYSQL_TYPE_TINY;
90  output_bind[13].buffer = &output_victory_t1;
91  output_bind[13].is_unsigned = true;
92  output_bind[14].buffer_type = MYSQL_TYPE_SHORT;
93  output_bind[14].buffer = &output_score0;
94  output_bind[14].is_unsigned = false;
95  output_bind[15].buffer_type = MYSQL_TYPE_SHORT;
96  output_bind[15].buffer = &output_score1;
97  output_bind[15].is_unsigned = false;
98  output_bind[16].buffer_type = MYSQL_TYPE_DATETIME;
99  output_bind[16].buffer = &output_created_at;
100 
101  // Prepare and execute with binds
102  MYSQL_STMT* statement;
103 
104  if(
105  !this->_init(&statement) ||
106  !this->_prepare(statement, query, input_bind) ||
107  !this->_execute(statement, output_bind)
108  )
109  {
110  // Cleanup
111  free(input_bind);
112  free(output_bind);
113 
114  return false;
115  }
116 
117  while(true)
118  {
119  int status = mysql_stmt_fetch(statement);
120 
121  if (status == 1 || status == MYSQL_NO_DATA)
122  {
123  break;
124  }
125 
126  Battlefield::GameStat game_stat;
127 
128  game_stat.SetId(output_id);
129  game_stat.SetGameType(output_gametype);
130  game_stat.SetGameVersion(output_gamever);
131  game_stat.SetHostName(output_hostname);
132  game_stat.SetMapId(output_mapid);
133  game_stat.SetNumPlayers(output_numplayers);
134  game_stat.SetPPlayers(output_pplayers);
135  game_stat.SetTimePlayed(output_tplayed);
136  game_stat.SetTeam1ClanId(output_clanid_t0);
137  game_stat.SetTeam2ClanId(output_clanid_t1);
138  game_stat.SetTeam1Country(output_country_t0);
139  game_stat.SetTeam2Country(output_country_t1);
140  game_stat.SetTeam1Victory(output_victory_t0);
141  game_stat.SetTeam2Victory(output_victory_t1);
142  game_stat.SetTeam1Score(output_score0);
143  game_stat.SetTeam2Score(output_score1);
144  game_stat.SetCreatedAt(output_created_at);
145 
146  game_stats.push_back(game_stat);
147  }
148 
149  // Cleanup
150  mysql_stmt_free_result(statement);
151  mysql_stmt_close(statement);
152  free(input_bind);
153  free(output_bind);
154 
155  return true;
156 }
157 
159 {
160  std::lock_guard<std::mutex> guard(this->_mutex); // database lock
161 
162  std::string query = "";
163  query += "SELECT ";
164  query += " `gametype`, `gamver`, `hostname`, `mapid`, `numplayers`, `pplayers`, `tplayed`, ";
165  query += " `clanid_t0`, `clanid_t1`, `country_t0`, `country_t1`, `victory_t0`, `victory_t1`, ";
166  query += " `score0`, `score1`, `created_at` ";
167  query += "FROM ";
168  query += " `GameStats` ";
169  query += "WHERE ";
170  query += " `id` = ?";
171 
172  int input_id = game_stat.GetId();
173 
174  uint8_t output_gametype;
175  char output_gamever[VARCHAR_LEN(20)];
176  char output_hostname[VARCHAR_LEN(45)];
177  uint8_t output_mapid;
178  uint8_t output_numplayers;
179  uint8_t output_pplayers;
180  uint16_t output_tplayed;
181  int output_clanid_t0;
182  int output_clanid_t1;
183  uint8_t output_country_t0;
184  uint8_t output_country_t1;
185  uint8_t output_victory_t0;
186  uint8_t output_victory_t1;
187  int16_t output_score0;
188  int16_t output_score1;
189  MYSQL_TIME output_created_at;
190 
191  // Allocate input binds
192  MYSQL_BIND* input_bind = (MYSQL_BIND *)calloc(1, sizeof(MYSQL_BIND));
193  input_bind[0].buffer_type = MYSQL_TYPE_LONG;
194  input_bind[0].buffer = &input_id;
195  input_bind[0].is_unsigned = false;
196 
197  // Allocate output binds
198  MYSQL_BIND* output_bind = (MYSQL_BIND *)calloc(16, sizeof(MYSQL_BIND));
199  output_bind[0].buffer_type = MYSQL_TYPE_TINY;
200  output_bind[0].buffer = &output_gametype;
201  output_bind[0].is_unsigned = true;
202  output_bind[1].buffer_type = MYSQL_TYPE_VAR_STRING;
203  output_bind[1].buffer = output_gamever;
204  output_bind[1].buffer_length = VARCHAR_LEN(20);
205  output_bind[2].buffer_type = MYSQL_TYPE_VAR_STRING;
206  output_bind[2].buffer = output_hostname;
207  output_bind[2].buffer_length = VARCHAR_LEN(45);
208  output_bind[3].buffer_type = MYSQL_TYPE_TINY;
209  output_bind[3].buffer = &output_mapid;
210  output_bind[3].is_unsigned = true;
211  output_bind[4].buffer_type = MYSQL_TYPE_TINY;
212  output_bind[4].buffer = &output_numplayers;
213  output_bind[4].is_unsigned = true;
214  output_bind[5].buffer_type = MYSQL_TYPE_TINY;
215  output_bind[5].buffer = &output_pplayers;
216  output_bind[5].is_unsigned = true;
217  output_bind[6].buffer_type = MYSQL_TYPE_SHORT;
218  output_bind[6].buffer = &output_tplayed;
219  output_bind[6].is_unsigned = true;
220  output_bind[7].buffer_type = MYSQL_TYPE_LONG;
221  output_bind[7].buffer = &output_clanid_t0;
222  output_bind[7].is_unsigned = true;
223  output_bind[8].buffer_type = MYSQL_TYPE_LONG;
224  output_bind[8].buffer = &output_clanid_t1;
225  output_bind[8].is_unsigned = true;
226  output_bind[9].buffer_type = MYSQL_TYPE_TINY;
227  output_bind[9].buffer = &output_country_t0;
228  output_bind[9].is_unsigned = true;
229  output_bind[10].buffer_type = MYSQL_TYPE_TINY;
230  output_bind[10].buffer = &output_country_t1;
231  output_bind[10].is_unsigned = true;
232  output_bind[11].buffer_type = MYSQL_TYPE_TINY;
233  output_bind[11].buffer = &output_victory_t0;
234  output_bind[11].is_unsigned = true;
235  output_bind[12].buffer_type = MYSQL_TYPE_TINY;
236  output_bind[12].buffer = &output_victory_t1;
237  output_bind[12].is_unsigned = true;
238  output_bind[13].buffer_type = MYSQL_TYPE_SHORT;
239  output_bind[13].buffer = &output_score0;
240  output_bind[13].is_unsigned = false;
241  output_bind[14].buffer_type = MYSQL_TYPE_SHORT;
242  output_bind[14].buffer = &output_score1;
243  output_bind[14].is_unsigned = false;
244  output_bind[15].buffer_type = MYSQL_TYPE_DATETIME;
245  output_bind[15].buffer = &output_created_at;
246 
247  // Prepare and execute with binds
248  MYSQL_STMT* statement;
249 
250  if(
251  !this->_init(&statement) ||
252  !this->_prepare(statement, query, input_bind) ||
253  !this->_execute(statement, output_bind)
254  )
255  {
256  // Cleanup
257  free(input_bind);
258  free(output_bind);
259 
260  return false;
261  }
262 
263  while(true)
264  {
265  int status = mysql_stmt_fetch(statement);
266 
267  if (status == 1 || status == MYSQL_NO_DATA)
268  {
269  break;
270  }
271 
272  game_stat.SetGameType(output_gametype);
273  game_stat.SetGameVersion(output_gamever);
274  game_stat.SetHostName(output_hostname);
275  game_stat.SetMapId(output_mapid);
276  game_stat.SetNumPlayers(output_numplayers);
277  game_stat.SetPPlayers(output_pplayers);
278  game_stat.SetTimePlayed(output_tplayed);
279  game_stat.SetTeam1ClanId(output_clanid_t0);
280  game_stat.SetTeam2ClanId(output_clanid_t1);
281  game_stat.SetTeam1Country(output_country_t0);
282  game_stat.SetTeam2Country(output_country_t1);
283  game_stat.SetTeam1Victory(output_victory_t0);
284  game_stat.SetTeam2Victory(output_victory_t1);
285  game_stat.SetTeam1Score(output_score0);
286  game_stat.SetTeam2Score(output_score1);
287  game_stat.SetCreatedAt(output_created_at);
288  }
289 
290  // Cleanup
291  mysql_stmt_free_result(statement);
292  mysql_stmt_close(statement);
293  free(input_bind);
294  free(output_bind);
295 
296  return true;
297 }
298 
300 {
301  std::lock_guard<std::mutex> guard(this->_mutex); // database lock
302 
303  std::string query = "";
304  query += "INSERT INTO `GameStats` ";
305  query += " (`gametype`, `gamver`, `hostname`, `mapid`, `numplayers`, `pplayers`, `tplayed`, ";
306  query += " `clanid_t0`, `clanid_t1`, `country_t0`, `country_t1`, `victory_t0`, `victory_t1`, ";
307  query += " `score0`, `score1`) ";
308  query += "VALUES ";
309  query += " (?, ?, ?, ?, ?, ?, ?, ";
310  query += " ?, ?, ?, ?, ?, ?, ";
311  query += " ?, ?)";
312 
313  uint8_t input_gametype = game_stat.GetGameType();
314  std::string input_gamver = game_stat.GetGameVersion();
315  std::string input_hostname = game_stat.GetHostName();
316  uint8_t input_mapid = game_stat.GetMapId();
317  uint8_t input_numplayers = game_stat.GetNumPlayers();
318  uint8_t input_pplayers = game_stat.GetPPlayers();
319  uint16_t input_tplayed = game_stat.GetTimePlayed();
320  int input_clanid_t0 = game_stat.GetTeam1ClanId();
321  int input_clanid_t1 = game_stat.GetTeam2ClanId();
322  uint8_t input_country_t0 = game_stat.GetTeam1Country();
323  uint8_t input_country_t1 = game_stat.GetTeam2Country();
324  uint8_t input_victory_t0 = game_stat.GetTeam1Victory();
325  uint8_t input_victory_t1 = game_stat.GetTeam2Victory();
326  int16_t input_score0 = game_stat.GetTeam1Score();
327  int16_t input_score1 = game_stat.GetTeam2Score();
328 
329  // Allocate input binds
330  MYSQL_BIND* input_bind = (MYSQL_BIND *)calloc(15, sizeof(MYSQL_BIND));
331  input_bind[0].buffer_type = MYSQL_TYPE_TINY;
332  input_bind[0].buffer = &input_gametype;
333  input_bind[0].is_unsigned = true;
334  input_bind[1].buffer_type = MYSQL_TYPE_STRING;
335  input_bind[1].buffer = const_cast<char*>(&(input_gamver[0]));
336  input_bind[1].buffer_length = input_gamver.size();
337  input_bind[2].buffer_type = MYSQL_TYPE_STRING;
338  input_bind[2].buffer = const_cast<char*>(&(input_hostname[0]));
339  input_bind[2].buffer_length = input_hostname.size();
340  input_bind[3].buffer_type = MYSQL_TYPE_TINY;
341  input_bind[3].buffer = &input_mapid;
342  input_bind[3].is_unsigned = true;
343  input_bind[4].buffer_type = MYSQL_TYPE_TINY;
344  input_bind[4].buffer = &input_numplayers;
345  input_bind[4].is_unsigned = true;
346  input_bind[5].buffer_type = MYSQL_TYPE_TINY;
347  input_bind[5].buffer = &input_pplayers;
348  input_bind[5].is_unsigned = true;
349  input_bind[6].buffer_type = MYSQL_TYPE_SHORT;
350  input_bind[6].buffer = &input_tplayed;
351  input_bind[6].is_unsigned = true;
352  input_bind[7].buffer_type = MYSQL_TYPE_LONG;
353  input_bind[7].buffer = &input_clanid_t0;
354  input_bind[7].is_unsigned = true;
355  input_bind[8].buffer_type = MYSQL_TYPE_LONG;
356  input_bind[8].buffer = &input_clanid_t1;
357  input_bind[8].is_unsigned = true;
358  input_bind[9].buffer_type = MYSQL_TYPE_TINY;
359  input_bind[9].buffer = &input_country_t0;
360  input_bind[9].is_unsigned = true;
361  input_bind[10].buffer_type = MYSQL_TYPE_TINY;
362  input_bind[10].buffer = &input_country_t1;
363  input_bind[10].is_unsigned = true;
364  input_bind[11].buffer_type = MYSQL_TYPE_TINY;
365  input_bind[11].buffer = &input_victory_t0;
366  input_bind[11].is_unsigned = true;
367  input_bind[12].buffer_type = MYSQL_TYPE_TINY;
368  input_bind[12].buffer = &input_victory_t1;
369  input_bind[12].is_unsigned = true;
370  input_bind[13].buffer_type = MYSQL_TYPE_SHORT;
371  input_bind[13].buffer = &input_score0;
372  input_bind[13].is_unsigned = false;
373  input_bind[14].buffer_type = MYSQL_TYPE_SHORT;
374  input_bind[14].buffer = &input_score1;
375  input_bind[14].is_unsigned = false;
376 
377  // Prepare and execute with binds
378  MYSQL_STMT* statement;
379 
380  if(
381  !this->_init(&statement) ||
382  !this->_prepare(statement, query, input_bind) ||
383  !this->_execute(statement)
384  )
385  {
386  // Cleanup
387  free(input_bind);
388 
389  return false;
390  }
391 
392  // Update GameStat id
393  int id = mysql_stmt_insert_id(statement);
394  game_stat.SetId(id);
395 
396  for(Battlefield::GameStatPlayer gsplayer : game_stat.GetPlayers())
397  {
398  this->_insertGameStatPlayer(game_stat, gsplayer);
399  }
400 
401  // Cleanup
402  mysql_stmt_free_result(statement);
403  mysql_stmt_close(statement);
404  free(input_bind);
405 
406  return true;
407 }
Represents a player's statistics in a game.
Definition: gamestat.h:146
Represents game statistics.
Definition: gamestat.h:37
bool queryGameStatById(Battlefield::GameStat &game_stat)
Queries a game statistic by its ID.
Definition: game_stat.cpp:158
bool _insertGameStatPlayer(const Battlefield::GameStat &game_stat, Battlefield::GameStatPlayer &gsplayer)
Inserts a game stat player record into the database.
bool _prepare(MYSQL_STMT *statement, const std::string &query)
Prepares a MySQL statement with a query.
Definition: database.cpp:59
bool queryGameStatsByDate(Battlefield::GameStats &game_stats, const std::string &date)
Queries game statistics by date.
Definition: game_stat.cpp:10
bool _init(MYSQL_STMT **statement)
Initializes a MySQL statement object.
Definition: database.cpp:45
bool insertGameStat(Battlefield::GameStat &game_stat)
Inserts a game statistic into the database.
Definition: game_stat.cpp:299
bool _execute(MYSQL_STMT *statement)
Executes a prepared MySQL statement.
Definition: database.cpp:94
std::mutex _mutex
Mutex for thread-safe access to the database connection.
Definition: database.h:42