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 int m_ExpectedRconCommands = -1;
121 int m_GotRconCommands = 0;
122 char m_aPassword[sizeof(g_Config.m_Password)] = "";
123 bool m_SendPassword = false;
124
125 // version-checking
126 char m_aVersionStr[10] = "0";
127
128 // pinging
129 int64_t m_PingStartTime = 0;
130
131 char m_aCurrentMap[IO_MAX_PATH_LENGTH] = "";
132 char m_aCurrentMapPath[IO_MAX_PATH_LENGTH] = "";
133
134 char m_aTimeoutCodes[NUM_DUMMIES][32] = {"", ""};
135 bool m_aCodeRunAfterJoin[NUM_DUMMIES] = {false, false};
136 bool m_GenerateTimeoutSeed = true;
137
138 char m_aCmdConnect[256] = "";
139 char m_aCmdPlayDemo[IO_MAX_PATH_LENGTH] = "";
140 char m_aCmdEditMap[IO_MAX_PATH_LENGTH] = "";
141
142 // map download
143 char m_aMapDownloadUrl[256] = "";
144 std::shared_ptr<CHttpRequest> m_pMapdownloadTask = nullptr;
145 char m_aMapdownloadFilename[256] = "";
146 char m_aMapdownloadFilenameTemp[256] = "";
147 char m_aMapdownloadName[256] = "";
148 IOHANDLE m_MapdownloadFileTemp = 0;
149 int m_MapdownloadChunk = 0;
150 int m_MapdownloadCrc = 0;
151 int m_MapdownloadAmount = -1;
152 int m_MapdownloadTotalsize = -1;
153 bool m_MapdownloadSha256Present = false;
154 SHA256_DIGEST m_MapdownloadSha256 = SHA256_ZEROED;
155
156 bool m_MapDetailsPresent = false;
157 char m_aMapDetailsName[256] = "";
158 int m_MapDetailsCrc = 0;
159 SHA256_DIGEST m_MapDetailsSha256 = SHA256_ZEROED;
160 char m_aMapDetailsUrl[256] = "";
161
162 std::shared_ptr<CHttpRequest> m_pDDNetInfoTask = nullptr;
163
164 // time
165 CSmoothTime m_aGameTime[NUM_DUMMIES];
166 CSmoothTime m_PredictedTime;
167
168 // input
169 struct // TODO: handle input better
170 {
171 int m_aData[MAX_INPUT_SIZE]; // the input data
172 int m_Tick; // the tick that the input is for
173 int64_t m_PredictedTime; // prediction latency when we sent this input
174 int64_t m_PredictionMargin; // prediction margin when we sent this input
175 int64_t m_Time;
176 } m_aInputs[NUM_DUMMIES][200];
177
178 int m_aCurrentInput[NUM_DUMMIES] = {0, 0};
179 bool m_LastDummy = false;
180 bool m_DummySendConnInfo = false;
181 bool m_DummyConnected = false;
182 int m_LastDummyConnectTime = 0;
183
184 // graphs
185 CGraph m_InputtimeMarginGraph;
186 CGraph m_GametimeMarginGraph;
187 CGraph m_FpsGraph;
188
189 // the game snapshots are modifiable by the game
190 CSnapshotStorage m_aSnapshotStorage[NUM_DUMMIES];
191 CSnapshotStorage::CHolder *m_aapSnapshots[NUM_DUMMIES][NUM_SNAPSHOT_TYPES];
192
193 int m_aReceivedSnapshots[NUM_DUMMIES] = {0, 0};
194 char m_aaSnapshotIncomingData[NUM_DUMMIES][CSnapshot::MAX_SIZE];
195 int m_aSnapshotIncomingDataSize[NUM_DUMMIES] = {0, 0};
196
197 CSnapshotStorage::CHolder m_aDemorecSnapshotHolders[NUM_SNAPSHOT_TYPES];
198 char m_aaaDemorecSnapshotData[NUM_SNAPSHOT_TYPES][2][CSnapshot::MAX_SIZE];
199
200 CSnapshotDelta m_SnapshotDelta;
201
202 std::deque<std::shared_ptr<CDemoEdit>> m_EditJobs;
203
204 //
205 bool m_CanReceiveServerCapabilities = false;
206 bool m_ServerSentCapabilities = false;
207 CServerCapabilities m_ServerCapabilities;
208
209 CServerInfo m_CurrentServerInfo;
210 int64_t m_CurrentServerInfoRequestTime = -1; // >= 0 should request, == -1 got info
211
212 int m_CurrentServerPingInfoType = -1;
213 int m_CurrentServerPingBasicToken = -1;
214 int m_CurrentServerPingToken = -1;
215 CUuid m_CurrentServerPingUuid = UUID_ZEROED;
216 int64_t m_CurrentServerCurrentPingTime = -1; // >= 0 request running
217 int64_t m_CurrentServerNextPingTime = -1; // >= 0 should request
218
219 // version info
220 struct CVersionInfo
221 {
222 enum
223 {
224 STATE_INIT = 0,
225 STATE_START,
226 STATE_READY,
227 };
228
229 int m_State = STATE_INIT;
230 } m_VersionInfo;
231
232 std::vector<SWarning> m_vWarnings;
233 std::vector<SWarning> m_vQuittingWarnings;
234
235 CFifo m_Fifo;
236
237 IOHANDLE m_BenchmarkFile = 0;
238 int64_t m_BenchmarkStopTime = 0;
239
240 CChecksum m_Checksum;
241 int m_OwnExecutableSize = 0;
242 IOHANDLE m_OwnExecutable = 0;
243
244 // favorite command handling
245 bool m_FavoritesGroup = false;
246 bool m_FavoritesGroupAllowPing = false;
247 int m_FavoritesGroupNum = 0;
248 NETADDR m_aFavoritesGroupAddresses[MAX_SERVER_ADDRESSES];
249
250 void UpdateDemoIntraTimers();
251 int MaxLatencyTicks() const;
252 int PredictionMargin() const;
253
254 std::shared_ptr<ILogger> m_pFileLogger = nullptr;
255 std::shared_ptr<ILogger> m_pStdoutLogger = nullptr;
256
257public:
258 IConfigManager *ConfigManager() { return m_pConfigManager; }
259 CConfig *Config() { return m_pConfig; }
260 IDiscord *Discord() { return m_pDiscord; }
261 IEngine *Engine() { return m_pEngine; }
262 IGameClient *GameClient() { return m_pGameClient; }
263 IEngineGraphics *Graphics() { return m_pGraphics; }
264 IEngineInput *Input() { return m_pInput; }
265 IEngineSound *Sound() { return m_pSound; }
266 ISteam *Steam() { return m_pSteam; }
267 INotifications *Notifications() { return m_pNotifications; }
268 IStorage *Storage() { return m_pStorage; }
269 IEngineTextRender *TextRender() { return m_pTextRender; }
270 IUpdater *Updater() { return m_pUpdater; }
271 IHttp *Http() { return &m_Http; }
272
273 CClient();
274
275 // ----- send functions -----
276 int SendMsg(int Conn, CMsgPacker *pMsg, int Flags) override;
277 // Send via the currently active client (main/dummy)
278 int SendMsgActive(CMsgPacker *pMsg, int Flags) override;
279
280 void SendInfo(int Conn);
281 void SendEnterGame(int Conn);
282 void SendReady(int Conn);
283 void SendMapRequest();
284
285 bool RconAuthed() const override { return m_aRconAuthed[g_Config.m_ClDummy] != 0; }
286 bool UseTempRconCommands() const override { return m_UseTempRconCommands != 0; }
287 void RconAuth(const char *pName, const char *pPassword) override;
288 void Rcon(const char *pCmd) override;
289 bool ReceivingRconCommands() const override { return m_ExpectedRconCommands > 0; }
290 float GotRconCommandsPercentage() const override;
291
292 bool ConnectionProblems() const override;
293
294 IGraphics::CTextureHandle GetDebugFont() const override { return m_DebugFont; }
295
296 void DirectInput(int *pInput, int Size);
297 void SendInput();
298
299 // TODO: OPT: do this a lot smarter!
300 int *GetInput(int Tick, int IsDummy) const override;
301
302 const char *LatestVersion() const override;
303
304 // ------ state handling -----
305 void SetState(EClientState State);
306
307 // called when the map is loaded and we should init for a new round
308 void OnEnterGame(bool Dummy);
309 void EnterGame(int Conn) override;
310
311 void Connect(const char *pAddress, const char *pPassword = nullptr) override;
312 void DisconnectWithReason(const char *pReason);
313 void Disconnect() override;
314
315 void DummyDisconnect(const char *pReason) override;
316 void DummyConnect() override;
317 bool DummyConnected() const override;
318 bool DummyConnecting() const override;
319 bool DummyAllowed() const override;
320
321 void GetServerInfo(CServerInfo *pServerInfo) const override;
322 void ServerInfoRequest();
323
324 void LoadDebugFont();
325
326 // ---
327
328 int GetPredictionTime() override;
329 void *SnapGetItem(int SnapId, int Index, CSnapItem *pItem) const override;
330 int SnapItemSize(int SnapId, int Index) const override;
331 const void *SnapFindItem(int SnapId, int Type, int Id) const override;
332 int SnapNumItems(int SnapId) const override;
333 void SnapSetStaticsize(int ItemType, int Size) override;
334
335 void Render();
336 void DebugRender();
337
338 void Restart() override;
339 void Quit() override;
340
341 const char *PlayerName() const override;
342 const char *DummyName() const override;
343 const char *ErrorString() const override;
344
345 const char *LoadMap(const char *pName, const char *pFilename, SHA256_DIGEST *pWantedSha256, unsigned WantedCrc);
346 const char *LoadMapSearch(const char *pMapName, SHA256_DIGEST *pWantedSha256, int WantedCrc);
347
348 void ProcessConnlessPacket(CNetChunk *pPacket);
349 void ProcessServerInfo(int Type, NETADDR *pFrom, const void *pData, int DataSize);
350 void ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy);
351
352 int UnpackAndValidateSnapshot(CSnapshot *pFrom, CSnapshot *pTo);
353
354 void ResetMapDownload();
355 void FinishMapDownload();
356
357 void RequestDDNetInfo() override;
358 void ResetDDNetInfoTask();
359 void FinishDDNetInfo();
360 void LoadDDNetInfo();
361
362 const NETADDR &ServerAddress() const override { return *m_aNetClient[CONN_MAIN].ServerAddress(); }
363 int ConnectNetTypes() const override;
364 const char *ConnectAddressString() const override { return m_aConnectAddressStr; }
365 const char *MapDownloadName() const override { return m_aMapdownloadName; }
366 int MapDownloadAmount() const override { return !m_pMapdownloadTask ? m_MapdownloadAmount : (int)m_pMapdownloadTask->Current(); }
367 int MapDownloadTotalsize() const override { return !m_pMapdownloadTask ? m_MapdownloadTotalsize : (int)m_pMapdownloadTask->Size(); }
368
369 void PumpNetwork();
370
371 void OnDemoPlayerSnapshot(void *pData, int Size) override;
372 void OnDemoPlayerMessage(void *pData, int Size) override;
373
374 void Update();
375
376 void RegisterInterfaces();
377 void InitInterfaces();
378
379 void Run();
380
381 bool InitNetworkClient(char *pError, size_t ErrorSize);
382 bool CtrlShiftKey(int Key, bool &Last);
383
384 static void Con_Connect(IConsole::IResult *pResult, void *pUserData);
385 static void Con_Disconnect(IConsole::IResult *pResult, void *pUserData);
386
387 static void Con_DummyConnect(IConsole::IResult *pResult, void *pUserData);
388 static void Con_DummyDisconnect(IConsole::IResult *pResult, void *pUserData);
389 static void Con_DummyResetInput(IConsole::IResult *pResult, void *pUserData);
390
391 static void Con_Quit(IConsole::IResult *pResult, void *pUserData);
392 static void Con_Restart(IConsole::IResult *pResult, void *pUserData);
393 static void Con_DemoPlay(IConsole::IResult *pResult, void *pUserData);
394 static void Con_DemoSpeed(IConsole::IResult *pResult, void *pUserData);
395 static void Con_Minimize(IConsole::IResult *pResult, void *pUserData);
396 static void Con_Ping(IConsole::IResult *pResult, void *pUserData);
397 static void Con_Screenshot(IConsole::IResult *pResult, void *pUserData);
398
399#if defined(CONF_VIDEORECORDER)
400 void StartVideo(const char *pFilename, bool WithTimestamp);
401 static void Con_StartVideo(IConsole::IResult *pResult, void *pUserData);
402 static void Con_StopVideo(IConsole::IResult *pResult, void *pUserData);
403 const char *DemoPlayer_Render(const char *pFilename, int StorageType, const char *pVideoName, int SpeedIndex, bool StartPaused = false) override;
404#endif
405
406 static void Con_Rcon(IConsole::IResult *pResult, void *pUserData);
407 static void Con_RconAuth(IConsole::IResult *pResult, void *pUserData);
408 static void Con_RconLogin(IConsole::IResult *pResult, void *pUserData);
409 static void Con_BeginFavoriteGroup(IConsole::IResult *pResult, void *pUserData);
410 static void Con_EndFavoriteGroup(IConsole::IResult *pResult, void *pUserData);
411 static void Con_AddFavorite(IConsole::IResult *pResult, void *pUserData);
412 static void Con_RemoveFavorite(IConsole::IResult *pResult, void *pUserData);
413 static void Con_Play(IConsole::IResult *pResult, void *pUserData);
414 static void Con_Record(IConsole::IResult *pResult, void *pUserData);
415 static void Con_StopRecord(IConsole::IResult *pResult, void *pUserData);
416 static void Con_AddDemoMarker(IConsole::IResult *pResult, void *pUserData);
417 static void Con_BenchmarkQuit(IConsole::IResult *pResult, void *pUserData);
418 static void ConchainServerBrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
419 static void ConchainFullscreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
420 static void ConchainWindowBordered(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
421 static void ConchainWindowScreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
422 static void ConchainWindowVSync(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
423 static void ConchainWindowResize(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
424 static void ConchainTimeoutSeed(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
425 static void ConchainPassword(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
426 static void ConchainReplays(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
427 static void ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
428 static void ConchainStdoutOutputLevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
429
430 static void Con_DemoSlice(IConsole::IResult *pResult, void *pUserData);
431 static void Con_DemoSliceBegin(IConsole::IResult *pResult, void *pUserData);
432 static void Con_DemoSliceEnd(IConsole::IResult *pResult, void *pUserData);
433 static void Con_SaveReplay(IConsole::IResult *pResult, void *pUserData);
434
435 void RegisterCommands();
436
437 const char *DemoPlayer_Play(const char *pFilename, int StorageType) override;
438 void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) override;
439 void DemoRecorder_HandleAutoStart() override;
440 void DemoRecorder_UpdateReplayRecorder() override;
441 void DemoRecorder_AddDemoMarker(int Recorder);
442 IDemoRecorder *DemoRecorder(int Recorder) override;
443
444 void AutoScreenshot_Start() override;
445 void AutoStatScreenshot_Start() override;
446 void AutoScreenshot_Cleanup();
447 void AutoStatScreenshot_Cleanup();
448
449 void AutoCSV_Start() override;
450 void AutoCSV_Cleanup();
451
452 void ServerBrowserUpdate() override;
453
454 void HandleConnectAddress(const NETADDR *pAddr);
455 void HandleConnectLink(const char *pLink);
456 void HandleDemoPath(const char *pPath);
457 void HandleMapPath(const char *pPath);
458
459 virtual void InitChecksum();
460 virtual int HandleChecksum(int Conn, CUuid Uuid, CUnpacker *pUnpacker);
461
462 // gfx
463 void SwitchWindowScreen(int Index) override;
464 void SetWindowParams(int FullscreenMode, bool IsBorderless) override;
465 void ToggleWindowVSync() override;
466 void Notify(const char *pTitle, const char *pMessage) override;
467 void OnWindowResize() override;
468 void BenchmarkQuit(int Seconds, const char *pFilename);
469
470 void UpdateAndSwap() override;
471
472 // DDRace
473
474 void GenerateTimeoutSeed() override;
475 void GenerateTimeoutCodes(const NETADDR *pAddrs, int NumAddrs);
476
477 int GetCurrentRaceTime() override;
478
479 const char *GetCurrentMap() const override;
480 const char *GetCurrentMapPath() const override;
481 SHA256_DIGEST GetCurrentMapSha256() const override;
482 unsigned GetCurrentMapCrc() const override;
483
484 void RaceRecord_Start(const char *pFilename) override;
485 void RaceRecord_Stop() override;
486 bool RaceRecord_IsRecording() override;
487
488 void DemoSliceBegin() override;
489 void DemoSliceEnd() override;
490 void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser) override;
491 virtual void SaveReplay(int Length, const char *pFilename = "");
492
493 bool EditorHasUnsavedData() const override { return m_pEditor->HasUnsavedData(); }
494
495 IFriends *Foes() override { return &m_Foes; }
496
497 void GetSmoothTick(int *pSmoothTick, float *pSmoothIntraTick, float MixAmount) override;
498
499 void AddWarning(const SWarning &Warning) override;
500 SWarning *GetCurWarning() override;
501 std::vector<SWarning> &&QuittingWarnings() { return std::move(m_vQuittingWarnings); }
502
503 CChecksumData *ChecksumData() override { return &m_Checksum.m_Data; }
504 int UdpConnectivity(int NetType) override;
505
506 bool ViewLink(const char *pLink) override;
507 bool ViewFile(const char *pFilename) override;
508
509#if defined(CONF_FAMILY_WINDOWS)
510 void ShellRegister() override;
511 void ShellUnregister() override;
512#endif
513
514 void ShowMessageBox(const char *pTitle, const char *pMessage, EMessageBoxType Type = MESSAGE_BOX_TYPE_ERROR) override;
515 void GetGpuInfoString(char (&aGpuInfo)[256]) override;
516 void SetLoggers(std::shared_ptr<ILogger> &&pFileLogger, std::shared_ptr<ILogger> &&pStdoutLogger);
517};
518
519#endif
520