BF2MC-Matchmaker
leaderboard_type.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::queryLeaderboardType(Battlefield::RankPlayers& rank_players, const std::string& type,
11  uint32_t limit, uint32_t offset)
12 {
13  std::lock_guard<std::mutex> guard(this->_mutex); // database lock
14 
15  std::string query = "";
16  query += "SELECT ";
17  query += " `rank`, `profileid`, `uniquenick`, `" + type + "` ";
18  query += "FROM ";
19  query += " `Leaderboard_" + type + "` ";
20  query += "ORDER BY ";
21  query += " `rank` ASC, ";
22  query += " `" + type + "` DESC ";
23  query += "LIMIT ? OFFSET ?";
24 
25  int output_rank;
26  int output_profileid;
27  char output_uniquenick[VARCHAR_LEN(32)];
28  uint32_t output_value;
29 
30  // Allocate input binds
31  MYSQL_BIND* input_bind = (MYSQL_BIND *)calloc(2, sizeof(MYSQL_BIND));
32  input_bind[0].buffer_type = MYSQL_TYPE_LONG;
33  input_bind[0].buffer = &limit;
34  input_bind[0].is_unsigned = true;
35  input_bind[1].buffer_type = MYSQL_TYPE_LONG;
36  input_bind[1].buffer = &offset;
37  input_bind[1].is_unsigned = true;
38 
39  // Allocate output binds
40  MYSQL_BIND* output_bind = (MYSQL_BIND *)calloc(4, sizeof(MYSQL_BIND));
41  output_bind[0].buffer_type = MYSQL_TYPE_LONG;
42  output_bind[0].buffer = &output_rank;
43  output_bind[0].is_unsigned = false;
44  output_bind[1].buffer_type = MYSQL_TYPE_LONG;
45  output_bind[1].buffer = &output_profileid;
46  output_bind[1].is_unsigned = false;
47  output_bind[2].buffer_type = MYSQL_TYPE_VAR_STRING;
48  output_bind[2].buffer = &output_uniquenick;
49  output_bind[2].buffer_length = VARCHAR_LEN(32);
50  output_bind[3].buffer_type = MYSQL_TYPE_LONG;
51  output_bind[3].buffer = &output_value;
52  output_bind[3].is_unsigned = true;
53 
54  // Prepare and execute with binds
55  MYSQL_STMT* statement;
56 
57  if(
58  !this->_init(&statement) ||
59  !this->_prepare(statement, query, input_bind) ||
60  !this->_execute(statement, output_bind)
61  )
62  {
63  // Cleanup
64  free(input_bind);
65  free(output_bind);
66 
67  return false;
68  }
69 
70  // Fetch and process rows
71  while (true)
72  {
73  int status = mysql_stmt_fetch(statement);
74 
75  if (status == 1 || status == MYSQL_NO_DATA)
76  break;
77 
78  Battlefield::Player player;
79 
80  player.SetProfileId(output_profileid);
81  player.SetUniquenick(output_uniquenick);
82 
83  if(type == "score")
84  {
85  player.SetScore(static_cast<int32_t>(output_value));
86  }
87  else
88  {
89  auto it = Battlefield::PlayerStats::SetterMap.find(type);
90  if (it != Battlefield::PlayerStats::SetterMap.end()) {
91  (player.*(it->second))(output_value);
92  }
93  }
94 
95  rank_players.insert(std::make_pair(output_rank, player));
96  }
97 
98  // Cleanup
99  mysql_stmt_free_result(statement);
100  mysql_stmt_close(statement);
101  free(input_bind);
102  free(output_bind);
103 
104  return true;
105 }
106 
107 bool Database::queryLeaderboardTypeByProfileid(Battlefield::RankPlayers& rank_players, const std::string& type, int profileid)
108 {
109  std::lock_guard<std::mutex> guard(this->_mutex); // database lock
110 
111  std::string query = "";
112  query += "SELECT `rp`.* ";
113  query += "FROM ";
114  query += " `Leaderboard_" + type + "` AS `rp` ";
115  query += "JOIN ( ";
116  query += " SELECT ";
117  query += " `profileid`, ";
118  query += " `rank` AS `start_rank` ";
119  query += " FROM ";
120  query += " `Leaderboard_" + type + "` ";
121  query += " WHERE ";
122  query += " `profileid` = ? ";
123  query += ") AS `start` ON rp.rank >= CASE WHEN start.start_rank <= 4 THEN 1 ELSE start.start_rank - 4 END ";
124  query += "JOIN ( ";
125  query += " SELECT ";
126  query += " `profileid`, ";
127  query += " `rank` AS `end_rank` ";
128  query += " FROM ";
129  query += " `Leaderboard_" + type + "` ";
130  query += " WHERE ";
131  query += " `profileid` = ? ";
132  query += ") AS `end` ON rp.rank <= CASE WHEN end.end_rank <= 4 THEN 10 ELSE end.end_rank + 5 END ";
133  query += "ORDER BY ";
134  query += " `rank` ASC, ";
135  query += " `" + type + "` DESC";
136 
137  int input_profileid = profileid;
138 
139  int output_rank;
140  int output_profileid;
141  char output_uniquenick[VARCHAR_LEN(32)];
142  uint32_t output_value;
143 
144  // Allocate input binds
145  MYSQL_BIND* input_bind = (MYSQL_BIND *)calloc(2, sizeof(MYSQL_BIND));
146  input_bind[0].buffer_type = MYSQL_TYPE_LONG;
147  input_bind[0].buffer = &input_profileid;
148  input_bind[0].is_unsigned = false;
149  input_bind[1].buffer_type = MYSQL_TYPE_LONG;
150  input_bind[1].buffer = &input_profileid;
151  input_bind[1].is_unsigned = false;
152 
153  // Allocate output binds
154  MYSQL_BIND* output_bind = (MYSQL_BIND *)calloc(4, sizeof(MYSQL_BIND));
155  output_bind[0].buffer_type = MYSQL_TYPE_LONG;
156  output_bind[0].buffer = &output_rank;
157  output_bind[0].is_unsigned = false;
158  output_bind[1].buffer_type = MYSQL_TYPE_LONG;
159  output_bind[1].buffer = &output_profileid;
160  output_bind[1].is_unsigned = false;
161  output_bind[2].buffer_type = MYSQL_TYPE_VAR_STRING;
162  output_bind[2].buffer = &output_uniquenick;
163  output_bind[2].buffer_length = VARCHAR_LEN(32);
164  output_bind[3].buffer_type = MYSQL_TYPE_LONG;
165  output_bind[3].buffer = &output_value;
166  output_bind[3].is_unsigned = true;
167 
168  // Prepare and execute with binds
169  MYSQL_STMT* statement;
170 
171  if(
172  !this->_init(&statement) ||
173  !this->_prepare(statement, query, input_bind) ||
174  !this->_execute(statement, output_bind)
175  )
176  {
177  // Cleanup
178  free(input_bind);
179  free(output_bind);
180 
181  return false;
182  }
183 
184  // Fetch and process rows
185  while (true)
186  {
187  int status = mysql_stmt_fetch(statement);
188 
189  if (status == 1 || status == MYSQL_NO_DATA)
190  break;
191 
192  Battlefield::Player player;
193 
194  player.SetProfileId(output_profileid);
195  player.SetUniquenick(output_uniquenick);
196 
197  if(type == "score")
198  {
199  player.SetScore(static_cast<int32_t>(output_value));
200  }
201  else
202  {
203  auto it = Battlefield::PlayerStats::SetterMap.find(type);
204  if (it != Battlefield::PlayerStats::SetterMap.end()) {
205  (player.*(it->second))(output_value);
206  }
207  }
208 
209  rank_players.insert(std::make_pair(output_rank, player));
210  }
211 
212  // Cleanup
213  mysql_stmt_free_result(statement);
214  mysql_stmt_close(statement);
215  free(input_bind);
216  free(output_bind);
217 
218  return true;
219 }
220 
221 bool Database::queryLeaderboardTypeByFriends(Battlefield::RankPlayers& rank_players, const std::string& type,
222  const std::vector<int>& friends)
223 {
224  std::lock_guard<std::mutex> guard(this->_mutex); // database lock
225 
226  std::string query = "";
227  query += "SELECT ";
228  query += " ROW_NUMBER() OVER (";
229  query += " ORDER BY ";
230  query += " `" + type + "` DESC ";
231  query += " ) AS `rank`, ";
232  query += " Players.profileid AS `profileid`, ";
233  query += " Players.uniquenick AS `uniquenick`, ";
234  query += " PlayerStats." + type + " AS '" + type + "' ";
235  query += "FROM ";
236  query += " `Players`, ";
237  query += " `PlayerStats` ";
238  query += "WHERE ";
239  query += " Players.profileid = PlayerStats.profileid ";
240  query += "AND ";
241  query += " Players.profileid IN (" + Util::ToString(friends) + ") ";
242  query += "ORDER BY ";
243  query += " `rank` ASC, ";
244  query += " `" + type + "` DESC ";
245  query += "LIMIT 10;";
246 
247  int output_rank;
248  int output_profileid;
249  char output_uniquenick[VARCHAR_LEN(32)];
250  uint32_t output_value;
251 
252  // Allocate output binds
253  MYSQL_BIND* output_bind = (MYSQL_BIND *)calloc(4, sizeof(MYSQL_BIND));
254  output_bind[0].buffer_type = MYSQL_TYPE_LONG;
255  output_bind[0].buffer = &output_rank;
256  output_bind[0].is_unsigned = false;
257  output_bind[1].buffer_type = MYSQL_TYPE_LONG;
258  output_bind[1].buffer = &output_profileid;
259  output_bind[1].is_unsigned = false;
260  output_bind[2].buffer_type = MYSQL_TYPE_VAR_STRING;
261  output_bind[2].buffer = &output_uniquenick;
262  output_bind[2].buffer_length = VARCHAR_LEN(32);
263  output_bind[3].buffer_type = MYSQL_TYPE_LONG;
264  output_bind[3].buffer = &output_value;
265  output_bind[3].is_unsigned = true;
266 
267  // Prepare and execute with binds
268  MYSQL_STMT* statement;
269  if(
270  !this->_init(&statement) ||
271  !this->_prepare(statement, query) ||
272  !this->_execute(statement, output_bind)
273  )
274  {
275  // Cleanup
276  free(output_bind);
277 
278  return false;
279  }
280 
281  // Fetch and process rows
282  while (true)
283  {
284  int status = mysql_stmt_fetch(statement);
285 
286  if (status == 1 || status == MYSQL_NO_DATA)
287  break;
288 
289  Battlefield::Player player;
290 
291  player.SetProfileId(output_profileid);
292  player.SetUniquenick(output_uniquenick);
293 
294  if(type == "score")
295  {
296  player.SetScore(static_cast<int32_t>(output_value));
297  }
298  else
299  {
300  auto it = Battlefield::PlayerStats::SetterMap.find(type);
301  if (it != Battlefield::PlayerStats::SetterMap.end()) {
302  (player.*(it->second))(output_value);
303  }
304  }
305 
306  rank_players.insert(std::make_pair(output_rank, player));
307  }
308 
309  // Cleanup
310  mysql_stmt_free_result(statement);
311  mysql_stmt_close(statement);
312  free(output_bind);
313 
314  return true;
315 }
Represents a player with extended statistics.
Definition: player.h:38
bool queryLeaderboardTypeByFriends(Battlefield::RankPlayers &rank_players, const std::string &type, const std::vector< int > &friends)
Queries the leaderboard rank of players by player stat type filtered by friends.
bool _prepare(MYSQL_STMT *statement, const std::string &query)
Prepares a MySQL statement with a query.
Definition: database.cpp:59
bool _init(MYSQL_STMT **statement)
Initializes a MySQL statement object.
Definition: database.cpp:45
bool queryLeaderboardTypeByProfileid(Battlefield::RankPlayers &rank_players, const std::string &type, int profileid)
Queries the leaderboard rank of players by player stat type with a profile ID in the middle.
bool _execute(MYSQL_STMT *statement)
Executes a prepared MySQL statement.
Definition: database.cpp:94
bool queryLeaderboardType(Battlefield::RankPlayers &rank_players, const std::string &type, uint32_t limit=10, uint32_t offset=0)
Queries the leaderboard rank of players by player stat type.
std::mutex _mutex
Mutex for thread-safe access to the database connection.
Definition: database.h:42