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