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