1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#ifndef ENGINE_CLIENT_CLIENT_H
4#define ENGINE_CLIENT_CLIENT_H
5
6#include <deque>
7#include <memory>
8
9#include <base/hash.h>
10
11#include <engine/client.h>
12#include <engine/client/checksum.h>
13#include <engine/client/friends.h>
14#include <engine/client/ghost.h>
15#include <engine/client/serverbrowser.h>
16#include <engine/client/updater.h>
17#include <engine/editor.h>
18#include <engine/graphics.h>
19#include <engine/shared/config.h>
20#include <engine/shared/demo.h>
21#include <engine/shared/fifo.h>
22#include <engine/shared/http.h>
23#include <engine/shared/network.h>
24#include <engine/textrender.h>
25#include <engine/warning.h>
26
27#include "graph.h"
28#include "smooth_time.h"
29
30class CDemoEdit;
31class IDemoRecorder;
32class CMsgPacker;
33class CUnpacker;
34class IConfigManager;
35class IDiscord;
36class IEngine;
37class IEngineInput;
38class IEngineMap;
39class IEngineSound;
40class IFriends;
41class ILogger;
42class ISteam;
43class INotifications;
44class IStorage;
45class IUpdater;
46
47#define CONNECTLINK_DOUBLE_SLASH "ddnet://"
48#define CONNECTLINK_NO_SLASH "ddnet:"
49
50class CServerCapabilities
51{
52public:
53 bool m_ChatTimeoutCode = false;
54 bool m_AnyPlayerFlag = false;
55 bool m_PingEx = false;
56 bool m_AllowDummy = false;
57 bool m_SyncWeaponInput = false;
58};
59
60class CClient : public IClient, public CDemoPlayer::IListener
61{
62 // needed interfaces
63 IConfigManager *m_pConfigManager = nullptr;
64 CConfig *m_pConfig = nullptr;
65 IConsole *m_pConsole = nullptr;
66 IDiscord *m_pDiscord = nullptr;
67 IEditor *m_pEditor = nullptr;
68 IEngine *m_pEngine = nullptr;
69 IFavorites *m_pFavorites = nullptr;
70 IGameClient *m_pGameClient = nullptr;
71 IEngineGraphics *m_pGraphics = nullptr;
72 IEngineInput *m_pInput = nullptr;
73 IEngineMap *m_pMap = nullptr;
74 IEngineSound *m_pSound = nullptr;
75 ISteam *m_pSteam = nullptr;
76 INotifications *m_pNotifications = nullptr;
77 IStorage *m_pStorage = nullptr;
78 IEngineTextRender *m_pTextRender = nullptr;
79 IUpdater *m_pUpdater = nullptr;
80 CHttp m_Http;
81
82 CNetClient m_aNetClient[NUM_CONNS];
83 CDemoPlayer m_DemoPlayer;
84 CDemoRecorder m_aDemoRecorder[RECORDER_MAX];
85 CDemoEditor m_DemoEditor;
86 CGhostRecorder m_GhostRecorder;
87 CGhostLoader m_GhostLoader;
88 CServerBrowser m_ServerBrowser;
89 CUpdater m_Updater;
90 CFriends m_Friends;
91 CFriends m_Foes;
92
93 char m_aConnectAddressStr[MAX_SERVER_ADDRESSES * NETADDR_MAXSTRSIZE] = "";
94
95 CUuid m_ConnectionId = UUID_ZEROED;
96
97 bool m_HaveGlobalTcpAddr = false;
98 NETADDR m_GlobalTcpAddr = NETADDR_ZEROED;
99
100 uint64_t m_aSnapshotParts[NUM_DUMMIES] = {0, 0};
101 int64_t m_LocalStartTime = 0;
102 int64_t m_GlobalStartTime = 0;
103
104 IGraphics::CTextureHandle m_DebugFont;
105
106 int64_t m_LastRenderTime;
107
108 int m_SnapCrcErrors = 0;
109 bool m_AutoScreenshotRecycle = false;
110 bool m_AutoStatScreenshotRecycle = false;
111 bool m_AutoCSVRecycle = false;
112 bool m_EditorActive = false;
113
114 int m_aAckGameTick[NUM_DUMMIES] = {-1, -1};
115 int m_aCurrentRecvTick[NUM_DUMMIES] = {0, 0};
116 int m_aRconAuthed[NUM_DUMMIES] = {0, 0};
117 char m_aRconUsername[32] = "";
118 char m_aRconPassword[sizeof(g_Config.m_SvRconPassword)] = "";
119 int m_UseTempRconCommands = 0;
120 bool m_ReceivingRconCommands = false;
121 char m_aPassword[sizeof(g_Config.m_Password)] = "";
122 bool m_SendPassword = false;
123
124 // version-checking
125 char m_aVersionStr[10] = "0";
126
127 // pinging
128 int64_t m_PingStartTime = 0;
129
130 char m_aCurrentMap[IO_MAX_PATH_LENGTH] = "";
131 char m_aCurrentMapPath[IO_MAX_PATH_LENGTH] = "";
132
133 char m_aTimeoutCodes[NUM_DUMMIES][32] = {"", ""};
134 bool m_aCodeRunAfterJoin[NUM_DUMMIES] = {false, false};
135 bool m_GenerateTimeoutSeed = true;
136
137 char m_aCmdConnect[256] = "";
138 char m_aCmdPlayDemo[IO_MAX_PATH_LENGTH] = "";
139 char m_aCmdEditMap[IO_MAX_PATH_LENGTH] = "";
140
141 // map download
142 char m_aMapDownloadUrl[256] = "";
143 std::shared_ptr<CHttpRequest> m_pMapdownloadTask = nullptr;
144 char m_aMapdownloadFilename[256] = "";
145 char m_aMapdownloadFilenameTemp[256] = "";
146 char m_aMapdownloadName[256] = "";
147 IOHANDLE m_MapdownloadFileTemp = 0;
148 int m_MapdownloadChunk = 0;
149 int m_MapdownloadCrc = 0;
150 int m_MapdownloadAmount = -1;
151 int m_MapdownloadTotalsize = -1;
152 bool m_MapdownloadSha256Present = false;
153 SHA256_DIGEST m_MapdownloadSha256 = SHA256_ZEROED;
154
155 bool m_MapDetailsPresent = false;
156 char m_aMapDetailsName[256] = "";
157 int m_MapDetailsCrc = 0;
158 SHA256_DIGEST m_MapDetailsSha256 = SHA256_ZEROED;
159 char m_aMapDetailsUrl[256] = "";
160
161 std::shared_ptr<CHttpRequest> m_pDDNetInfoTask = nullptr;
162
163 // time
164 CSmoothTime m_aGameTime[NUM_DUMMIES];
165 CSmoothTime m_PredictedTime;
166
167 // input
168 struct // TODO: handle input better
169 {
170 int m_aData[MAX_INPUT_SIZE]; // the input data
171 int m_Tick; // the tick that the input is for
172 int64_t m_PredictedTime; // prediction latency when we sent this input
173 int64_t m_PredictionMargin; // prediction margin when we sent this input
174 int64_t m_Time;
175 } m_aInputs[NUM_DUMMIES][200];
176
177 int m_aCurrentInput[NUM_DUMMIES] = {0, 0};
178 bool m_LastDummy = false;
179 bool m_DummySendConnInfo = false;
180 bool m_DummyConnected = false;
181 int m_LastDummyConnectTime = 0;
182
183 // graphs
184 CGraph m_InputtimeMarginGraph;
185 CGraph m_GametimeMarginGraph;
186 CGraph m_FpsGraph;
187
188 // the game snapshots are modifiable by the game
189 CSnapshotStorage m_aSnapshotStorage[NUM_DUMMIES];
190 CSnapshotStorage::CHolder *m_aapSnapshots[NUM_DUMMIES][NUM_SNAPSHOT_TYPES];
191
192 int m_aReceivedSnapshots[NUM_DUMMIES] = {0, 0};
193 char m_aaSnapshotIncomingData[NUM_DUMMIES][CSnapshot::MAX_SIZE];
194 int m_aSnapshotIncomingDataSize[NUM_DUMMIES] = {0, 0};
195
196 CSnapshotStorage::CHolder m_aDemorecSnapshotHolders[NUM_SNAPSHOT_TYPES];
197 char m_aaaDemorecSnapshotData[NUM_SNAPSHOT_TYPES][2][CSnapshot::MAX_SIZE];
198
199 CSnapshotDelta m_SnapshotDelta;
200
201 std::deque<std::shared_ptr<CDemoEdit>> m_EditJobs;
202
203 //
204 bool m_CanReceiveServerCapabilities = false;
205 bool m_ServerSentCapabilities = false;
206 CServerCapabilities m_ServerCapabilities;
207
208 CServerInfo m_CurrentServerInfo;
209 int64_t m_CurrentServerInfoRequestTime = -1; // >= 0 should request, == -1 got info
210
211 int m_CurrentServerPingInfoType = -1;
212 int m_CurrentServerPingBasicToken = -1;
213 int m_CurrentServerPingToken = -1;
214 CUuid m_CurrentServerPingUuid = UUID_ZEROED;
215 int64_t m_CurrentServerCurrentPingTime = -1; // >= 0 request running
216 int64_t m_CurrentServerNextPingTime = -1; // >= 0 should request
217
218 // version info
219 struct CVersionInfo
220 {
221 enum
222 {
223 STATE_INIT = 0,
224 STATE_START,
225 STATE_READY,
226 };
227
228 int m_State = STATE_INIT;
229 } m_VersionInfo;
230
231 std::vector<SWarning> m_vWarnings;
232 std::vector<SWarning> m_vQuittingWarnings;
233
234 CFifo m_Fifo;
235
236 IOHANDLE m_BenchmarkFile = 0;
237 int64_t m_BenchmarkStopTime = 0;
238
239 CChecksum m_Checksum;
240 int m_OwnExecutableSize = 0;
241 IOHANDLE m_OwnExecutable = 0;
242
243 // favorite command handling
244 bool m_FavoritesGroup = false;
245 bool m_FavoritesGroupAllowPing = false;
246 int m_FavoritesGroupNum = 0;
247 NETADDR m_aFavoritesGroupAddresses[MAX_SERVER_ADDRESSES];
248
249 void UpdateDemoIntraTimers();
250 int MaxLatencyTicks() const;
251 int PredictionMargin() const;
252
253 std::shared_ptr<ILogger> m_pFileLogger = nullptr;
254 std::shared_ptr<ILogger> m_pStdoutLogger = nullptr;
255
256public:
257 IConfigManager *ConfigManager() { return m_pConfigManager; }
258 CConfig *Config() { return m_pConfig; }
259 IDiscord *Discord() { return m_pDiscord; }
260 IEngine *Engine() { return m_pEngine; }
261 IGameClient *GameClient() { return m_pGameClient; }
262 IEngineGraphics *Graphics() { return m_pGraphics; }
263 IEngineInput *Input() { return m_pInput; }
264 IEngineSound *Sound() { return m_pSound; }
265 ISteam *Steam() { return m_pSteam; }
266 INotifications *Notifications() { return m_pNotifications; }
267 IStorage *Storage() { return m_pStorage; }
268 IEngineTextRender *TextRender() { return m_pTextRender; }
269 IUpdater *Updater() { return m_pUpdater; }
270 IHttp *Http() { return &m_Http; }
271
272 CClient();
273
274 // ----- send functions -----
275 int SendMsg(int Conn, CMsgPacker *pMsg, int Flags) override;
276 // Send via the currently active client (main/dummy)
277 int SendMsgActive(CMsgPacker *pMsg, int Flags) override;
278
279 void SendInfo(int Conn);
280 void SendEnterGame(int Conn);
281 void SendReady(int Conn);
282 void SendMapRequest();
283
284 bool RconAuthed() const override { return m_aRconAuthed[g_Config.m_ClDummy] != 0; }
285 bool UseTempRconCommands() const override { return m_UseTempRconCommands != 0; }
286 void RconAuth(const char *pName, const char *pPassword) override;
287 void Rcon(const char *pCmd) override;
288 bool ReceivingRconCommands() const override { return m_ReceivingRconCommands; }
289
290 bool ConnectionProblems() const override;
291
292 IGraphics::CTextureHandle GetDebugFont() const override { return m_DebugFont; }
293
294 void DirectInput(int *pInput, int Size);
295 void SendInput();
296
297 // TODO: OPT: do this a lot smarter!
298 int *GetInput(int Tick, int IsDummy) const override;
299
300 const char *LatestVersion() const override;
301
302 // ------ state handling -----
303 void SetState(EClientState State);
304
305 // called when the map is loaded and we should init for a new round
306 void OnEnterGame(bool Dummy);
307 void EnterGame(int Conn) override;
308
309 void Connect(const char *pAddress, const char *pPassword = nullptr) override;
310 void DisconnectWithReason(const char *pReason);
311 void Disconnect() override;
312
313 void DummyDisconnect(const char *pReason) override;
314 void DummyConnect() override;
315 bool DummyConnected() const override;
316 bool DummyConnecting() const override;
317 bool DummyAllowed() const override;
318
319 void GetServerInfo(CServerInfo *pServerInfo) const override;
320 void ServerInfoRequest();
321
322 void LoadDebugFont();
323
324 // ---
325
326 int GetPredictionTime() override;
327 void *SnapGetItem(int SnapId, int Index, CSnapItem *pItem) const override;
328 int SnapItemSize(int SnapId, int Index) const override;
329 const void *SnapFindItem(int SnapId, int Type, int Id) const override;
330 int SnapNumItems(int SnapId) const override;
331 void SnapSetStaticsize(int ItemType, int Size) override;
332
333 void Render();
334 void DebugRender();
335
336 void Restart() override;
337 void Quit() override;
338
339 const char *PlayerName() const override;
340 const char *DummyName() const override;
341 const char *ErrorString() const override;
342
343 const char *LoadMap(const char *pName, const char *pFilename, SHA256_DIGEST *pWantedSha256, unsigned WantedCrc);
344 const char *LoadMapSearch(const char *pMapName, SHA256_DIGEST *pWantedSha256, int WantedCrc);
345
346 void ProcessConnlessPacket(CNetChunk *pPacket);
347 void ProcessServerInfo(int Type, NETADDR *pFrom, const void *pData, int DataSize);
348 void ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy);
349
350 int UnpackAndValidateSnapshot(CSnapshot *pFrom, CSnapshot *pTo);
351
352 void ResetMapDownload();
353 void FinishMapDownload();
354
355 void RequestDDNetInfo() override;
356 void ResetDDNetInfoTask();
357 void FinishDDNetInfo();
358 void LoadDDNetInfo();
359
360 const NETADDR &ServerAddress() const override { return *m_aNetClient[CONN_MAIN].ServerAddress(); }
361 int ConnectNetTypes() const override;
362 const char *ConnectAddressString() const override { return m_aConnectAddressStr; }
363 const char *MapDownloadName() const override { return m_aMapdownloadName; }
364 int MapDownloadAmount() const override { return !m_pMapdownloadTask ? m_MapdownloadAmount : (int)m_pMapdownloadTask->Current(); }
365 int MapDownloadTotalsize() const override { return !m_pMapdownloadTask ? m_MapdownloadTotalsize : (int)m_pMapdownloadTask->Size(); }
366
367 void PumpNetwork();
368
369 void OnDemoPlayerSnapshot(void *pData, int Size) override;
370 void OnDemoPlayerMessage(void *pData, int Size) override;
371
372 void Update();
373
374 void RegisterInterfaces();
375 void InitInterfaces();
376
377 void Run();
378
379 bool InitNetworkClient(char *pError, size_t ErrorSize);
380 bool CtrlShiftKey(int Key, bool &Last);
381
382 static void Con_Connect(IConsole::IResult *pResult, void *pUserData);
383 static void Con_Disconnect(IConsole::IResult *pResult, void *pUserData);
384
385 static void Con_DummyConnect(IConsole::IResult *pResult, void *pUserData);
386 static void Con_DummyDisconnect(IConsole::IResult *pResult, void *pUserData);
387 static void Con_DummyResetInput(IConsole::IResult *pResult, void *pUserData);
388
389 static void Con_Quit(IConsole::IResult *pResult, void *pUserData);
390 static void Con_Restart(IConsole::IResult *pResult, void *pUserData);
391 static void Con_DemoPlay(IConsole::IResult *pResult, void *pUserData);
392 static void Con_DemoSpeed(IConsole::IResult *pResult, void *pUserData);
393 static void Con_Minimize(IConsole::IResult *pResult, void *pUserData);
394 static void Con_Ping(IConsole::IResult *pResult, void *pUserData);
395 static void Con_Screenshot(IConsole::IResult *pResult, void *pUserData);
396
397#if defined(CONF_VIDEORECORDER)
398 void StartVideo(const char *pFilename, bool WithTimestamp);
399 static void Con_StartVideo(IConsole::IResult *pResult, void *pUserData);
400 static void Con_StopVideo(IConsole::IResult *pResult, void *pUserData);
401 const char *DemoPlayer_Render(const char *pFilename, int StorageType, const char *pVideoName, int SpeedIndex, bool StartPaused = false) override;
402#endif
403
404 static void Con_Rcon(IConsole::IResult *pResult, void *pUserData);
405 static void Con_RconAuth(IConsole::IResult *pResult, void *pUserData);
406 static void Con_RconLogin(IConsole::IResult *pResult, void *pUserData);
407 static void Con_BeginFavoriteGroup(IConsole::IResult *pResult, void *pUserData);
408 static void Con_EndFavoriteGroup(IConsole::IResult *pResult, void *pUserData);
409 static void Con_AddFavorite(IConsole::IResult *pResult, void *pUserData);
410 static void Con_RemoveFavorite(IConsole::IResult *pResult, void *pUserData);
411 static void Con_Play(IConsole::IResult *pResult, void *pUserData);
412 static void Con_Record(IConsole::IResult *pResult, void *pUserData);
413 static void Con_StopRecord(IConsole::IResult *pResult, void *pUserData);
414 static void Con_AddDemoMarker(IConsole::IResult *pResult, void *pUserData);
415 static void Con_BenchmarkQuit(IConsole::IResult *pResult, void *pUserData);
416 static void ConchainServerBrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
417 static void ConchainFullscreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
418 static void ConchainWindowBordered(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
419 static void ConchainWindowScreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
420 static void ConchainWindowVSync(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
421 static void ConchainWindowResize(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
422 static void ConchainTimeoutSeed(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
423 static void ConchainPassword(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
424 static void ConchainReplays(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
425 static void ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
426 static void ConchainStdoutOutputLevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
427
428 static void Con_DemoSlice(IConsole::IResult *pResult, void *pUserData);
429 static void Con_DemoSliceBegin(IConsole::IResult *pResult, void *pUserData);
430 static void Con_DemoSliceEnd(IConsole::IResult *pResult, void *pUserData);
431 static void Con_SaveReplay(IConsole::IResult *pResult, void *pUserData);
432
433 void RegisterCommands();
434
435 const char *DemoPlayer_Play(const char *pFilename, int StorageType) override;
436 void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) override;
437 void DemoRecorder_HandleAutoStart() override;
438 void DemoRecorder_UpdateReplayRecorder() override;
439 void DemoRecorder_AddDemoMarker(int Recorder);
440 IDemoRecorder *DemoRecorder(int Recorder) override;
441
442 void AutoScreenshot_Start() override;
443 void AutoStatScreenshot_Start() override;
444 void AutoScreenshot_Cleanup();
445 void AutoStatScreenshot_Cleanup();
446
447 void AutoCSV_Start() override;
448 void AutoCSV_Cleanup();
449
450 void ServerBrowserUpdate() override;
451
452 void HandleConnectAddress(const NETADDR *pAddr);
453 void HandleConnectLink(const char *pLink);
454 void HandleDemoPath(const char *pPath);
455 void HandleMapPath(const char *pPath);
456
457 virtual void InitChecksum();
458 virtual int HandleChecksum(int Conn, CUuid Uuid, CUnpacker *pUnpacker);
459
460 // gfx
461 void SwitchWindowScreen(int Index) override;
462 void SetWindowParams(int FullscreenMode, bool IsBorderless) override;
463 void ToggleWindowVSync() override;
464 void Notify(const char *pTitle, const char *pMessage) override;
465 void OnWindowResize() override;
466 void BenchmarkQuit(int Seconds, const char *pFilename);
467
468 void UpdateAndSwap() override;
469
470 // DDRace
471
472 void GenerateTimeoutSeed() override;
473 void GenerateTimeoutCodes(const NETADDR *pAddrs, int NumAddrs);
474
475 int GetCurrentRaceTime() override;
476
477 const char *GetCurrentMap() const override;
478 const char *GetCurrentMapPath() const override;
479 SHA256_DIGEST GetCurrentMapSha256() const override;
480 unsigned GetCurrentMapCrc() const override;
481
482 void RaceRecord_Start(const char *pFilename) override;
483 void RaceRecord_Stop() override;
484 bool RaceRecord_IsRecording() override;
485
486 void DemoSliceBegin() override;
487 void DemoSliceEnd() override;
488 void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser) override;
489 virtual void SaveReplay(int Length, const char *pFilename = "");
490
491 bool EditorHasUnsavedData() const override { return m_pEditor->HasUnsavedData(); }
492
493 IFriends *Foes() override { return &m_Foes; }
494
495 void GetSmoothTick(int *pSmoothTick, float *pSmoothIntraTick, float MixAmount) override;
496
497 void AddWarning(const SWarning &Warning) override;
498 SWarning *GetCurWarning() override;
499 std::vector<SWarning> &&QuittingWarnings() { return std::move(m_vQuittingWarnings); }
500
501 CChecksumData *ChecksumData() override { return &m_Checksum.m_Data; }
502 int UdpConnectivity(int NetType) override;
503
504#if defined(CONF_FAMILY_WINDOWS)
505 void ShellRegister() override;
506 void ShellUnregister() override;
507#endif
508
509 void ShowMessageBox(const char *pTitle, const char *pMessage, EMessageBoxType Type = MESSAGE_BOX_TYPE_ERROR) override;
510 void GetGpuInfoString(char (&aGpuInfo)[256]) override;
511 void SetLoggers(std::shared_ptr<ILogger> &&pFileLogger, std::shared_ptr<ILogger> &&pStdoutLogger);
512};
513
514#endif
515