1#ifndef GAME_SERVER_SCOREWORKER_H
2#define GAME_SERVER_SCOREWORKER_H
3
4#include <engine/map.h>
5#include <engine/server/databases/connection_pool.h>
6#include <engine/shared/protocol.h>
7#include <engine/shared/uuid_manager.h>
8
9#include <game/server/save.h>
10#include <game/voting.h>
11
12#include <memory>
13#include <optional>
14#include <string>
15#include <utility>
16#include <vector>
17
18class IDbConnection;
19class IGameController;
20
21enum
22{
23 NUM_CHECKPOINTS = MAX_CHECKPOINTS,
24 TIMESTAMP_STR_LENGTH = 20, // 2019-04-02 19:38:36
25};
26
27struct CScorePlayerResult : ISqlResult
28{
29 CScorePlayerResult();
30
31 enum
32 {
33 MAX_MESSAGES = 10,
34 };
35
36 enum Variant
37 {
38 DIRECT,
39 ALL,
40 BROADCAST,
41 MAP_VOTE,
42 PLAYER_INFO,
43 PLAYER_TIMECP,
44 } m_MessageKind;
45 union
46 {
47 char m_aaMessages[MAX_MESSAGES][512];
48 char m_aBroadcast[1024];
49 struct
50 {
51 std::optional<float> m_Time;
52 float m_aTimeCp[NUM_CHECKPOINTS];
53 int m_Birthday; // 0 indicates no birthday
54 char m_aRequestedPlayer[MAX_NAME_LENGTH];
55 } m_Info = {};
56 struct
57 {
58 char m_aReason[VOTE_REASON_LENGTH];
59 char m_aServer[32 + 1];
60 char m_aMap[MAX_MAP_LENGTH + 1];
61 } m_MapVote;
62 } m_Data = {}; // PLAYER_INFO
63
64 void SetVariant(Variant v);
65};
66
67struct CScoreLoadBestTimeResult : ISqlResult
68{
69 std::optional<float> m_CurrentRecord = std::nullopt;
70};
71
72struct CSqlLoadBestTimeRequest : ISqlData
73{
74 CSqlLoadBestTimeRequest(std::shared_ptr<CScoreLoadBestTimeResult> pResult) :
75 ISqlData(std::move(pResult))
76 {
77 }
78
79 // current map
80 char m_aMap[MAX_MAP_LENGTH];
81};
82
83struct CSqlPlayerRequest : ISqlData
84{
85 CSqlPlayerRequest(std::shared_ptr<CScorePlayerResult> pResult) :
86 ISqlData(std::move(pResult))
87 {
88 }
89
90 // object being requested, either map (128 bytes) or player (16 bytes)
91 char m_aName[MAX_MAP_LENGTH];
92 // current map
93 char m_aMap[MAX_MAP_LENGTH];
94 char m_aRequestingPlayer[MAX_NAME_LENGTH];
95 // relevant for /top5 kind of requests
96 int m_Offset;
97 char m_aServer[5];
98};
99
100struct CScoreRandomMapResult : ISqlResult
101{
102 CScoreRandomMapResult(int ClientId) :
103 m_ClientId(ClientId)
104 {
105 m_aMap[0] = '\0';
106 m_aMessage[0] = '\0';
107 }
108 int m_ClientId;
109 char m_aMap[MAX_MAP_LENGTH];
110 char m_aMessage[512];
111};
112
113struct CSqlRandomMapRequest : ISqlData
114{
115 CSqlRandomMapRequest(std::shared_ptr<CScoreRandomMapResult> pResult) :
116 ISqlData(std::move(pResult))
117 {
118 }
119
120 char m_aServerType[32];
121 char m_aCurrentMap[MAX_MAP_LENGTH];
122 char m_aRequestingPlayer[MAX_NAME_LENGTH];
123 int m_MinStars;
124 int m_MaxStars;
125};
126
127struct CSqlScoreData : ISqlData
128{
129 CSqlScoreData(std::shared_ptr<CScorePlayerResult> pResult) :
130 ISqlData(std::move(pResult))
131 {
132 }
133
134 char m_aMap[MAX_MAP_LENGTH];
135 char m_aGameUuid[UUID_MAXSTRSIZE];
136 char m_aName[MAX_MAP_LENGTH];
137
138 int m_ClientId;
139 float m_Time;
140 char m_aTimestamp[TIMESTAMP_STR_LENGTH];
141 float m_aCurrentTimeCp[NUM_CHECKPOINTS];
142 int m_Num;
143 bool m_Search;
144 char m_aRequestingPlayer[MAX_NAME_LENGTH];
145};
146
147struct CScoreSaveResult : ISqlResult
148{
149 CScoreSaveResult(int PlayerId, const char *pPlayerName, const char *pServer) :
150 m_Status(SAVE_FAILED),
151 m_RequestingPlayer(PlayerId)
152 {
153 m_aMessage[0] = '\0';
154 m_aBroadcast[0] = '\0';
155 m_aCode[0] = '\0';
156 m_aGeneratedCode[0] = '\0';
157 str_copy(dst&: m_aRequestingPlayer, src: pPlayerName);
158 str_copy(dst&: m_aServer, src: pServer);
159 }
160 enum
161 {
162 SAVE_SUCCESS,
163 SAVE_WARNING,
164 SAVE_FALLBACKFILE,
165 // load team in the following two cases
166 SAVE_FAILED,
167 LOAD_SUCCESS,
168 LOAD_FAILED,
169 } m_Status;
170 char m_aMessage[512];
171 char m_aBroadcast[512];
172 CSaveTeam m_SavedTeam;
173 int m_RequestingPlayer;
174 char m_aRequestingPlayer[MAX_NAME_LENGTH];
175 CUuid m_SaveId;
176 char m_aServer[5];
177 char m_aCode[128];
178 char m_aGeneratedCode[128];
179};
180
181struct CSqlTeamScoreData : ISqlData
182{
183 CSqlTeamScoreData() :
184 ISqlData(nullptr)
185 {
186 }
187
188 char m_aGameUuid[UUID_MAXSTRSIZE];
189 char m_aMap[MAX_MAP_LENGTH];
190 float m_Time;
191 char m_aTimestamp[TIMESTAMP_STR_LENGTH];
192 unsigned int m_Size;
193 char m_aaNames[MAX_CLIENTS][MAX_NAME_LENGTH];
194 CUuid m_TeamrankUuid;
195};
196
197struct CSqlTeamSaveData : ISqlData
198{
199 CSqlTeamSaveData(std::shared_ptr<CScoreSaveResult> pResult) :
200 ISqlData(std::move(pResult))
201 {
202 }
203
204 char m_aClientName[MAX_NAME_LENGTH];
205 char m_aMap[MAX_MAP_LENGTH];
206 char m_aCode[128];
207 char m_aGeneratedCode[128];
208 char m_aServer[5];
209};
210
211struct CSqlTeamLoadRequest : ISqlData
212{
213 CSqlTeamLoadRequest(std::shared_ptr<CScoreSaveResult> pResult) :
214 ISqlData(std::move(pResult))
215 {
216 }
217
218 char m_aCode[128];
219 char m_aMap[MAX_MAP_LENGTH];
220 char m_aRequestingPlayer[MAX_NAME_LENGTH];
221 // struct holding all player names in the team or an empty string
222 char m_aClientNames[MAX_CLIENTS][MAX_NAME_LENGTH];
223 int m_aClientId[MAX_CLIENTS];
224 int m_NumPlayer;
225};
226
227class CPlayerData
228{
229public:
230 CPlayerData()
231 {
232 Reset();
233 }
234
235 void Reset()
236 {
237 m_BestTime = 0;
238 for(float &BestTimeCp : m_aBestTimeCp)
239 BestTimeCp = 0;
240
241 m_RecordStopTick = -1;
242 }
243
244 void Set(float Time, const float aTimeCp[NUM_CHECKPOINTS])
245 {
246 m_BestTime = Time;
247 for(int i = 0; i < NUM_CHECKPOINTS; i++)
248 m_aBestTimeCp[i] = aTimeCp[i];
249 }
250
251 void SetBestTimeCp(const float aTimeCp[NUM_CHECKPOINTS])
252 {
253 for(int i = 0; i < NUM_CHECKPOINTS; i++)
254 m_aBestTimeCp[i] = aTimeCp[i];
255 }
256
257 float m_BestTime;
258 float m_aBestTimeCp[NUM_CHECKPOINTS];
259
260 int m_RecordStopTick;
261 float m_RecordFinishTime;
262};
263
264struct CTeamrank
265{
266 CUuid m_TeamId;
267 char m_aaNames[MAX_CLIENTS][MAX_NAME_LENGTH];
268 unsigned int m_NumNames;
269 CTeamrank();
270
271 // Assumes that a database query equivalent to
272 //
273 // SELECT TeamId, Name [, ...] -- the order is important
274 // FROM record_teamrace
275 // ORDER BY TeamId, Name
276 //
277 // was executed and that the result line of the first team member is already selected.
278 // Afterwards the team member of the next team is selected.
279 //
280 // Returns true on SQL failure
281 //
282 // if another team can be extracted
283 bool NextSqlResult(IDbConnection *pSqlServer, bool *pEnd, char *pError, int ErrorSize);
284
285 bool SamePlayers(const std::vector<std::string> *pvSortedNames);
286
287 static bool GetSqlTop5Team(IDbConnection *pSqlServer, bool *pEnd, char *pError, int ErrorSize, char (*paMessages)[512], int *StartLine, int Count);
288};
289
290struct CScoreWorker
291{
292 static bool LoadBestTime(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
293
294 static bool RandomMap(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
295 static bool RandomUnfinishedMap(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
296 static bool MapVote(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
297
298 static bool LoadPlayerData(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
299 static bool LoadPlayerTimeCp(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
300 static bool MapInfo(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
301 static bool ShowRank(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
302 static bool ShowTeamRank(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
303 static bool ShowTop(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
304 static bool ShowTeamTop5(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
305 static bool ShowPlayerTeamTop5(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
306 static bool ShowTimes(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
307 static bool ShowPoints(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
308 static bool ShowTopPoints(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
309 static bool GetSaves(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize);
310
311 static bool SaveTeam(IDbConnection *pSqlServer, const ISqlData *pGameData, Write w, char *pError, int ErrorSize);
312 static bool LoadTeam(IDbConnection *pSqlServer, const ISqlData *pGameData, Write w, char *pError, int ErrorSize);
313
314 static bool SaveScore(IDbConnection *pSqlServer, const ISqlData *pGameData, Write w, char *pError, int ErrorSize);
315 static bool SaveTeamScore(IDbConnection *pSqlServer, const ISqlData *pGameData, Write w, char *pError, int ErrorSize);
316};
317
318#endif // GAME_SERVER_SCOREWORKER_H
319