| 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_H |
| 4 | #define ENGINE_CLIENT_H |
| 5 | #include "graphics.h" |
| 6 | #include "kernel.h" |
| 7 | #include "message.h" |
| 8 | |
| 9 | #include <base/hash.h> |
| 10 | |
| 11 | #include <engine/client/enums.h> |
| 12 | #include <engine/friends.h> |
| 13 | #include <engine/shared/translation_context.h> |
| 14 | |
| 15 | #include <generated/protocol.h> |
| 16 | #include <generated/protocol7.h> |
| 17 | |
| 18 | #include <functional> |
| 19 | #include <optional> |
| 20 | |
| 21 | #define CONNECTLINK_DOUBLE_SLASH "ddnet://" |
| 22 | #define CONNECTLINK_NO_SLASH "ddnet:" |
| 23 | |
| 24 | struct SWarning; |
| 25 | |
| 26 | enum |
| 27 | { |
| 28 | RECORDER_MANUAL = 0, |
| 29 | RECORDER_AUTO = 1, |
| 30 | RECORDER_RACE = 2, |
| 31 | RECORDER_REPLAYS = 3, |
| 32 | RECORDER_MAX = 4, |
| 33 | }; |
| 34 | |
| 35 | typedef bool (*CLIENTFUNC_FILTER)(const void *pData, int DataSize, void *pUser); |
| 36 | struct CChecksumData; |
| 37 | |
| 38 | class IClient : public IInterface |
| 39 | { |
| 40 | MACRO_INTERFACE("client" ) |
| 41 | public: |
| 42 | /* Constants: Client States |
| 43 | STATE_OFFLINE - The client is offline. |
| 44 | STATE_CONNECTING - The client is trying to connect to a server. |
| 45 | STATE_LOADING - The client has connected to a server and is loading resources. |
| 46 | STATE_ONLINE - The client is connected to a server and running the game. |
| 47 | STATE_DEMOPLAYBACK - The client is playing a demo |
| 48 | STATE_QUITTING - The client is quitting. |
| 49 | */ |
| 50 | |
| 51 | enum EClientState |
| 52 | { |
| 53 | STATE_OFFLINE = 0, |
| 54 | STATE_CONNECTING, |
| 55 | STATE_LOADING, |
| 56 | STATE_ONLINE, |
| 57 | STATE_DEMOPLAYBACK, |
| 58 | STATE_QUITTING, |
| 59 | STATE_RESTARTING, |
| 60 | }; |
| 61 | |
| 62 | /** |
| 63 | * More precise state for @see STATE_LOADING |
| 64 | * Sets what is actually happening in the client right now |
| 65 | */ |
| 66 | enum ELoadingStateDetail |
| 67 | { |
| 68 | LOADING_STATE_DETAIL_INITIAL, |
| 69 | LOADING_STATE_DETAIL_LOADING_MAP, |
| 70 | LOADING_STATE_DETAIL_LOADING_DEMO, |
| 71 | LOADING_STATE_DETAIL_SENDING_READY, |
| 72 | LOADING_STATE_DETAIL_GETTING_READY, |
| 73 | }; |
| 74 | |
| 75 | enum ELoadingCallbackDetail |
| 76 | { |
| 77 | LOADING_CALLBACK_DETAIL_MAP, |
| 78 | LOADING_CALLBACK_DETAIL_DEMO, |
| 79 | }; |
| 80 | typedef std::function<void(ELoadingCallbackDetail Detail)> TLoadingCallback; |
| 81 | CTranslationContext m_TranslationContext; |
| 82 | |
| 83 | protected: |
| 84 | // quick access to state of the client |
| 85 | EClientState m_State = IClient::STATE_OFFLINE; |
| 86 | ELoadingStateDetail m_LoadingStateDetail = LOADING_STATE_DETAIL_INITIAL; |
| 87 | int64_t m_StateStartTime; |
| 88 | |
| 89 | // quick access to time variables |
| 90 | int m_aPrevGameTick[NUM_DUMMIES] = {0, 0}; |
| 91 | int m_aCurGameTick[NUM_DUMMIES] = {0, 0}; |
| 92 | float m_aGameIntraTick[NUM_DUMMIES] = {0.0f, 0.0f}; |
| 93 | float m_aGameTickTime[NUM_DUMMIES] = {0.0f, 0.0f}; |
| 94 | float m_aGameIntraTickSincePrev[NUM_DUMMIES] = {0.0f, 0.0f}; |
| 95 | |
| 96 | int m_aPredTick[NUM_DUMMIES] = {0, 0}; |
| 97 | float m_aPredIntraTick[NUM_DUMMIES] = {0.0f, 0.0f}; |
| 98 | |
| 99 | float m_LocalTime = 0.0f; |
| 100 | float m_GlobalTime = 0.0f; |
| 101 | float m_RenderFrameTime = 0.0001f; |
| 102 | float m_FrameTimeAverage = 0.0001f; |
| 103 | |
| 104 | TLoadingCallback m_LoadingCallback = nullptr; |
| 105 | |
| 106 | char m_aNews[3000] = "" ; |
| 107 | int m_Points = -1; |
| 108 | int64_t m_ReconnectTime = 0; |
| 109 | |
| 110 | public: |
| 111 | class CSnapItem |
| 112 | { |
| 113 | public: |
| 114 | int m_Type; |
| 115 | int m_Id; |
| 116 | const void *m_pData; |
| 117 | int m_DataSize; |
| 118 | }; |
| 119 | |
| 120 | enum |
| 121 | { |
| 122 | CONN_MAIN = 0, |
| 123 | CONN_DUMMY, |
| 124 | CONN_CONTACT, |
| 125 | NUM_CONNS, |
| 126 | }; |
| 127 | |
| 128 | enum |
| 129 | { |
| 130 | CONNECTIVITY_UNKNOWN, |
| 131 | CONNECTIVITY_CHECKING, |
| 132 | CONNECTIVITY_UNREACHABLE, |
| 133 | CONNECTIVITY_REACHABLE, |
| 134 | // Different global IP address has been detected for UDP and |
| 135 | // TCP connections. |
| 136 | CONNECTIVITY_DIFFERING_UDP_TCP_IP_ADDRESSES, |
| 137 | }; |
| 138 | |
| 139 | // |
| 140 | EClientState State() const { return m_State; } |
| 141 | ELoadingStateDetail LoadingStateDetail() const { return m_LoadingStateDetail; } |
| 142 | int64_t StateStartTime() const { return m_StateStartTime; } |
| 143 | void SetLoadingStateDetail(ELoadingStateDetail LoadingStateDetail) { m_LoadingStateDetail = LoadingStateDetail; } |
| 144 | |
| 145 | void SetLoadingCallback(TLoadingCallback &&Func) { m_LoadingCallback = std::move(Func); } |
| 146 | |
| 147 | // tick time access |
| 148 | int PrevGameTick(int Conn) const { return m_aPrevGameTick[Conn]; } |
| 149 | int GameTick(int Conn) const { return m_aCurGameTick[Conn]; } |
| 150 | int PredGameTick(int Conn) const { return m_aPredTick[Conn]; } |
| 151 | float IntraGameTick(int Conn) const { return m_aGameIntraTick[Conn]; } |
| 152 | float PredIntraGameTick(int Conn) const { return m_aPredIntraTick[Conn]; } |
| 153 | float IntraGameTickSincePrev(int Conn) const { return m_aGameIntraTickSincePrev[Conn]; } |
| 154 | float GameTickTime(int Conn) const { return m_aGameTickTime[Conn]; } |
| 155 | int GameTickSpeed() const { return SERVER_TICK_SPEED; } |
| 156 | |
| 157 | // other time access |
| 158 | float RenderFrameTime() const { return m_RenderFrameTime; } |
| 159 | float LocalTime() const { return m_LocalTime; } |
| 160 | float GlobalTime() const { return m_GlobalTime; } |
| 161 | float FrameTimeAverage() const { return m_FrameTimeAverage; } |
| 162 | |
| 163 | // actions |
| 164 | virtual void Connect(const char *pAddress, const char *pPassword = nullptr) = 0; |
| 165 | virtual void Disconnect() = 0; |
| 166 | |
| 167 | // dummy |
| 168 | virtual void DummyDisconnect(const char *pReason) = 0; |
| 169 | virtual void DummyConnect() = 0; |
| 170 | virtual bool DummyConnected() const = 0; |
| 171 | virtual bool DummyConnecting() const = 0; |
| 172 | virtual bool DummyConnectingDelayed() const = 0; |
| 173 | virtual bool DummyAllowed() const = 0; |
| 174 | |
| 175 | virtual void Restart() = 0; |
| 176 | virtual void Quit() = 0; |
| 177 | virtual const char *DemoPlayer_Play(const char *pFilename, int StorageType) = 0; |
| 178 | #if defined(CONF_VIDEORECORDER) |
| 179 | virtual const char *DemoPlayer_Render(const char *pFilename, int StorageType, const char *pVideoName, int SpeedIndex, bool StartPaused = false) = 0; |
| 180 | #endif |
| 181 | virtual void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder, bool Verbose = false) = 0; |
| 182 | virtual void DemoRecorder_HandleAutoStart() = 0; |
| 183 | virtual void DemoRecorder_UpdateReplayRecorder() = 0; |
| 184 | virtual class IDemoRecorder *DemoRecorder(int Recorder) = 0; |
| 185 | virtual void AutoScreenshot_Start() = 0; |
| 186 | virtual void AutoStatScreenshot_Start() = 0; |
| 187 | virtual void AutoCSV_Start() = 0; |
| 188 | virtual void ServerBrowserUpdate() = 0; |
| 189 | |
| 190 | // gfx |
| 191 | virtual void Notify(const char *pTitle, const char *pMessage) = 0; |
| 192 | virtual void OnWindowResize() = 0; |
| 193 | |
| 194 | virtual void UpdateAndSwap() = 0; |
| 195 | |
| 196 | // networking |
| 197 | virtual void EnterGame(int Conn) = 0; |
| 198 | |
| 199 | // |
| 200 | virtual const NETADDR &ServerAddress() const = 0; |
| 201 | virtual int ConnectNetTypes() const = 0; |
| 202 | virtual const char *ConnectAddressString() const = 0; |
| 203 | virtual const char *MapDownloadName() const = 0; |
| 204 | virtual int MapDownloadAmount() const = 0; |
| 205 | virtual int MapDownloadTotalsize() const = 0; |
| 206 | |
| 207 | // input |
| 208 | virtual int *GetInput(int Tick, int IsDummy = 0) const = 0; |
| 209 | |
| 210 | // remote console |
| 211 | virtual void RconAuth(const char *pUsername, const char *pPassword, bool Dummy) = 0; |
| 212 | virtual bool RconAuthed() const = 0; |
| 213 | virtual bool UseTempRconCommands() const = 0; |
| 214 | virtual void Rcon(const char *pLine) = 0; |
| 215 | virtual bool ReceivingRconCommands() const = 0; |
| 216 | virtual float GotRconCommandsPercentage() const = 0; |
| 217 | virtual bool ReceivingMaplist() const = 0; |
| 218 | virtual float GotMaplistPercentage() const = 0; |
| 219 | virtual const std::vector<std::string> &MaplistEntries() const = 0; |
| 220 | |
| 221 | // server info |
| 222 | virtual void GetServerInfo(class CServerInfo *pServerInfo) const = 0; |
| 223 | virtual bool ServerCapAnyPlayerFlag() const = 0; |
| 224 | |
| 225 | virtual int GetPredictionTime() = 0; |
| 226 | virtual int GetPredictionTick() = 0; |
| 227 | |
| 228 | // snapshot interface |
| 229 | |
| 230 | enum |
| 231 | { |
| 232 | SNAP_CURRENT = 0, |
| 233 | SNAP_PREV = 1, |
| 234 | NUM_SNAPSHOT_TYPES = 2, |
| 235 | }; |
| 236 | |
| 237 | // TODO: Refactor: should redo this a bit i think, too many virtual calls |
| 238 | virtual int SnapNumItems(int SnapId) const = 0; |
| 239 | virtual const void *SnapFindItem(int SnapId, int Type, int Id) const = 0; |
| 240 | virtual CSnapItem SnapGetItem(int SnapId, int Index) const = 0; |
| 241 | |
| 242 | virtual void SnapSetStaticsize(int ItemType, int Size) = 0; |
| 243 | virtual void SnapSetStaticsize7(int ItemType, int Size) = 0; |
| 244 | |
| 245 | virtual int SendMsg(int Conn, CMsgPacker *pMsg, int Flags) = 0; |
| 246 | virtual int SendMsgActive(CMsgPacker *pMsg, int Flags) = 0; |
| 247 | |
| 248 | template<class T> |
| 249 | int SendPackMsgActive(T *pMsg, int Flags, bool NoTranslate = false) |
| 250 | { |
| 251 | CMsgPacker Packer(T::ms_MsgId, false, NoTranslate); |
| 252 | if(pMsg->Pack(&Packer)) |
| 253 | return -1; |
| 254 | return SendMsgActive(pMsg: &Packer, Flags); |
| 255 | } |
| 256 | |
| 257 | template<class T> |
| 258 | int SendPackMsg(int Conn, T *pMsg, int Flags, bool NoTranslate = false) |
| 259 | { |
| 260 | CMsgPacker Packer(T::ms_MsgId, false, NoTranslate); |
| 261 | if(pMsg->Pack(&Packer)) |
| 262 | return -1; |
| 263 | return SendMsg(Conn, pMsg: &Packer, Flags); |
| 264 | } |
| 265 | |
| 266 | // |
| 267 | virtual const char *PlayerName() const = 0; |
| 268 | virtual const char *DummyName() = 0; |
| 269 | virtual const char *ErrorString() const = 0; |
| 270 | virtual const char *LatestVersion() const = 0; |
| 271 | virtual bool ConnectionProblems() const = 0; |
| 272 | |
| 273 | virtual IGraphics::CTextureHandle GetDebugFont() const = 0; // TODO: remove this function |
| 274 | |
| 275 | // DDRace |
| 276 | |
| 277 | virtual const char *GetCurrentMap() const = 0; |
| 278 | virtual const char *GetCurrentMapPath() const = 0; |
| 279 | virtual SHA256_DIGEST GetCurrentMapSha256() const = 0; |
| 280 | virtual unsigned GetCurrentMapCrc() const = 0; |
| 281 | |
| 282 | const char *News() const { return m_aNews; } |
| 283 | int Points() const { return m_Points; } |
| 284 | int64_t ReconnectTime() const { return m_ReconnectTime; } |
| 285 | void SetReconnectTime(int64_t ReconnectTime) { m_ReconnectTime = ReconnectTime; } |
| 286 | |
| 287 | virtual bool IsSixup() const = 0; |
| 288 | |
| 289 | virtual void RaceRecord_Start(const char *pFilename) = 0; |
| 290 | virtual void RaceRecord_Stop() = 0; |
| 291 | virtual bool RaceRecord_IsRecording() = 0; |
| 292 | |
| 293 | virtual void DemoSliceBegin() = 0; |
| 294 | virtual void DemoSliceEnd() = 0; |
| 295 | virtual void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser) = 0; |
| 296 | |
| 297 | enum class EInfoState |
| 298 | { |
| 299 | LOADING, |
| 300 | SUCCESS, |
| 301 | ERROR, |
| 302 | }; |
| 303 | virtual EInfoState InfoState() const = 0; |
| 304 | virtual void RequestDDNetInfo() = 0; |
| 305 | virtual bool EditorHasUnsavedData() const = 0; |
| 306 | |
| 307 | virtual void GenerateTimeoutSeed() = 0; |
| 308 | |
| 309 | virtual IFriends *Foes() = 0; |
| 310 | |
| 311 | virtual void GetSmoothTick(int *pSmoothTick, float *pSmoothIntraTick, float MixAmount) = 0; |
| 312 | |
| 313 | virtual void AddWarning(const SWarning &Warning) = 0; |
| 314 | virtual std::optional<SWarning> CurrentWarning() = 0; |
| 315 | |
| 316 | virtual CChecksumData *ChecksumData() = 0; |
| 317 | virtual int UdpConnectivity(int NetType) = 0; |
| 318 | |
| 319 | /** |
| 320 | * Opens a link in the browser. |
| 321 | * |
| 322 | * @param pLink The link to open in a browser. |
| 323 | * |
| 324 | * @return `true` on success, `false` on failure. |
| 325 | * |
| 326 | * @remark This may not be called with untrusted input or it'll result in arbitrary code execution, especially on Windows. |
| 327 | */ |
| 328 | virtual bool ViewLink(const char *pLink) = 0; |
| 329 | /** |
| 330 | * Opens a file or directory with the default program. |
| 331 | * |
| 332 | * @param pFilename The file or folder to open with the default program. |
| 333 | * |
| 334 | * @return `true` on success, `false` on failure. |
| 335 | * |
| 336 | * @remark This may not be called with untrusted input or it'll result in arbitrary code execution, especially on Windows. |
| 337 | */ |
| 338 | virtual bool ViewFile(const char *pFilename) = 0; |
| 339 | |
| 340 | #if defined(CONF_FAMILY_WINDOWS) |
| 341 | virtual void ShellRegister() = 0; |
| 342 | virtual void ShellUnregister() = 0; |
| 343 | #endif |
| 344 | |
| 345 | virtual std::optional<int> ShowMessageBox(const IGraphics::CMessageBox &MessageBox) = 0; |
| 346 | virtual void GetGpuInfoString(char (&aGpuInfo)[512]) = 0; |
| 347 | }; |
| 348 | |
| 349 | class IGameClient : public IInterface |
| 350 | { |
| 351 | MACRO_INTERFACE("gameclient" ) |
| 352 | protected: |
| 353 | public: |
| 354 | virtual void OnConsoleInit() = 0; |
| 355 | |
| 356 | virtual void OnRconType(bool UsernameReq) = 0; |
| 357 | virtual void OnRconLine(const char *pLine) = 0; |
| 358 | virtual void OnInit() = 0; |
| 359 | virtual void InvalidateSnapshot() = 0; |
| 360 | virtual void OnNewSnapshot() = 0; |
| 361 | virtual void OnEnterGame() = 0; |
| 362 | virtual void OnShutdown() = 0; |
| 363 | virtual void OnRender() = 0; |
| 364 | virtual void OnUpdate() = 0; |
| 365 | virtual void OnStateChange(int NewState, int OldState) = 0; |
| 366 | virtual void OnConnected() = 0; |
| 367 | virtual void OnMessage(int MsgId, CUnpacker *pUnpacker, int Conn, bool Dummy) = 0; |
| 368 | virtual void OnPredict() = 0; |
| 369 | virtual void OnActivateEditor() = 0; |
| 370 | virtual void OnWindowResize() = 0; |
| 371 | |
| 372 | virtual int OnSnapInput(int *pData, bool Dummy, bool Force) = 0; |
| 373 | virtual void OnDummySwap() = 0; |
| 374 | virtual void SendDummyInfo(bool Start) = 0; |
| 375 | |
| 376 | virtual const char *GetItemName(int Type) const = 0; |
| 377 | virtual const char *Version() const = 0; |
| 378 | virtual const char *NetVersion() const = 0; |
| 379 | virtual const char *NetVersion7() const = 0; |
| 380 | virtual int DDNetVersion() const = 0; |
| 381 | virtual const char *DDNetVersionStr() const = 0; |
| 382 | |
| 383 | virtual void OnDummyDisconnect() = 0; |
| 384 | virtual void DummyResetInput() = 0; |
| 385 | virtual void Echo(const char *pString) = 0; |
| 386 | |
| 387 | virtual bool CanDisplayWarning() const = 0; |
| 388 | virtual void RenderShutdownMessage() = 0; |
| 389 | |
| 390 | virtual CNetObjHandler *GetNetObjHandler() = 0; |
| 391 | virtual protocol7::CNetObjHandler *GetNetObjHandler7() = 0; |
| 392 | |
| 393 | virtual int ClientVersion7() const = 0; |
| 394 | |
| 395 | virtual void ApplySkin7InfoFromSnapObj(const protocol7::CNetObj_De_ClientInfo *pObj, int ClientId) = 0; |
| 396 | virtual int OnDemoRecSnap7(class CSnapshot *pFrom, class CSnapshot *pTo, int Conn) = 0; |
| 397 | virtual int TranslateSnap(class CSnapshot *pSnapDstSix, class CSnapshot *pSnapSrcSeven, int Conn, bool Dummy) = 0; |
| 398 | virtual void ProcessDemoSnapshot(class CSnapshot *pSnap) = 0; |
| 399 | |
| 400 | virtual void InitializeLanguage() = 0; |
| 401 | |
| 402 | virtual void ForceUpdateConsoleRemoteCompletionSuggestions() = 0; |
| 403 | }; |
| 404 | |
| 405 | extern IGameClient *CreateGameClient(); |
| 406 | #endif |
| 407 | |