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