ddnet/src/engine/client/client.h
heinrich5991 8996d152ab Allow multiple connect addresses
This allows the client to connect to servers that have both an IPv4 and
an IPv6 address, even if the client is only connected to one of the two.
The one faster to answer will be picked.

Timeout codes are now generated based on *all* of the server's
addresses.

Fixes #5158.
2022-07-10 16:57:10 +02:00

552 lines
18 KiB
C++

/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef ENGINE_CLIENT_CLIENT_H
#define ENGINE_CLIENT_CLIENT_H
#include <list>
#include <memory>
#include <base/hash.h>
#include <engine/client.h>
#include <engine/client/checksum.h>
#include <engine/client/friends.h>
#include <engine/client/ghost.h>
#include <engine/client/serverbrowser.h>
#include <engine/client/updater.h>
#include <engine/editor.h>
#include <engine/engine.h>
#include <engine/graphics.h>
#include <engine/shared/config.h>
#include <engine/shared/demo.h>
#include <engine/shared/fifo.h>
#include <engine/shared/http.h>
#include <engine/shared/network.h>
#include <engine/warning.h>
class CDemoEdit;
class IDemoRecorder;
class CMsgPacker;
class CUnpacker;
class IConfigManager;
class IDiscord;
class IEngineInput;
class IEngineMap;
class IEngineSound;
class IFriends;
class ISteam;
class IStorage;
class IUpdater;
#define CONNECTLINK "ddnet:"
class CGraph
{
public:
enum
{
// restrictions: Must be power of two
MAX_VALUES = 128,
};
float m_Min, m_Max;
float m_MinRange, m_MaxRange;
float m_aValues[MAX_VALUES];
float m_aColors[MAX_VALUES][3];
int m_Index;
void Init(float Min, float Max);
void ScaleMax();
void ScaleMin();
void Add(float v, float r, float g, float b);
bool InsertAt(int i, float v, float r, float g, float b);
void Render(IGraphics *pGraphics, IGraphics::CTextureHandle FontTexture, float x, float y, float w, float h, const char *pDescription);
};
class CSmoothTime
{
int64_t m_Snap;
int64_t m_Current;
int64_t m_Target;
int64_t m_SnapMargin;
int64_t m_CurrentMargin;
int64_t m_TargetMargin;
CGraph m_Graph;
int m_SpikeCounter;
float m_aAdjustSpeed[2]; // 0 = down, 1 = up
public:
void Init(int64_t Target);
void SetAdjustSpeed(int Direction, float Value);
int64_t Get(int64_t Now);
void UpdateInt(int64_t Target);
void Update(CGraph *pGraph, int64_t Target, int TimeLeft, int AdjustDirection);
int64_t GetMargin(int64_t Now);
void UpdateMargin(int64_t TargetMargin);
};
class CServerCapabilities
{
public:
bool m_ChatTimeoutCode;
bool m_AnyPlayerFlag;
bool m_PingEx;
bool m_AllowDummy;
bool m_SyncWeaponInput;
};
class CClient : public IClient, public CDemoPlayer::IListener
{
// needed interfaces
IEngine *m_pEngine;
IEditor *m_pEditor;
IEngineInput *m_pInput;
IEngineGraphics *m_pGraphics;
IEngineSound *m_pSound;
IFavorites *m_pFavorites;
IGameClient *m_pGameClient;
IEngineMap *m_pMap;
IConfigManager *m_pConfigManager;
CConfig *m_pConfig;
IConsole *m_pConsole;
IStorage *m_pStorage;
IUpdater *m_pUpdater;
IDiscord *m_pDiscord;
ISteam *m_pSteam;
CNetClient m_aNetClient[NUM_CONNS];
CDemoPlayer m_DemoPlayer;
CDemoRecorder m_aDemoRecorder[RECORDER_MAX];
CDemoEditor m_DemoEditor;
CGhostRecorder m_GhostRecorder;
CGhostLoader m_GhostLoader;
CServerBrowser m_ServerBrowser;
CUpdater m_Updater;
CFriends m_Friends;
CFriends m_Foes;
char m_aConnectAddressStr[MAX_SERVER_ADDRESSES * NETADDR_MAXSTRSIZE];
CUuid m_ConnectionID;
bool m_HaveGlobalTcpAddr = false;
NETADDR m_GlobalTcpAddr;
uint64_t m_aSnapshotParts[NUM_DUMMIES];
int64_t m_LocalStartTime;
IGraphics::CTextureHandle m_DebugFont;
int m_DebugSoundIndex = 0;
int64_t m_LastRenderTime;
float m_RenderFrameTimeLow;
float m_RenderFrameTimeHigh;
int m_RenderFrames;
int m_SnapCrcErrors;
bool m_AutoScreenshotRecycle;
bool m_AutoStatScreenshotRecycle;
bool m_AutoCSVRecycle;
bool m_EditorActive;
bool m_SoundInitFailed;
bool m_ResortServerBrowser;
int m_aAckGameTick[NUM_DUMMIES];
int m_aCurrentRecvTick[NUM_DUMMIES];
int m_aRconAuthed[NUM_DUMMIES];
char m_aRconPassword[32];
int m_UseTempRconCommands;
char m_aPassword[32];
bool m_SendPassword;
bool m_ButtonRender = false;
// version-checking
char m_aVersionStr[10];
// pinging
int64_t m_PingStartTime;
char m_aCurrentMap[IO_MAX_PATH_LENGTH];
char m_aCurrentMapPath[IO_MAX_PATH_LENGTH];
char m_aTimeoutCodes[NUM_DUMMIES][32];
bool m_aCodeRunAfterJoin[NUM_DUMMIES];
bool m_GenerateTimeoutSeed;
//
char m_aCmdConnect[256];
char m_aCmdPlayDemo[IO_MAX_PATH_LENGTH];
char m_aCmdEditMap[IO_MAX_PATH_LENGTH];
// map download
std::shared_ptr<CHttpRequest> m_pMapdownloadTask;
char m_aMapdownloadFilename[256];
char m_aMapdownloadFilenameTemp[256];
char m_aMapdownloadName[256];
IOHANDLE m_MapdownloadFileTemp;
int m_MapdownloadChunk;
int m_MapdownloadCrc;
int m_MapdownloadAmount;
int m_MapdownloadTotalsize;
bool m_MapdownloadSha256Present;
SHA256_DIGEST m_MapdownloadSha256;
bool m_MapDetailsPresent;
char m_aMapDetailsName[256];
int m_MapDetailsCrc;
SHA256_DIGEST m_MapDetailsSha256;
char m_aDDNetInfoTmp[64];
std::shared_ptr<CHttpRequest> m_pDDNetInfoTask;
// time
CSmoothTime m_aGameTime[NUM_DUMMIES];
CSmoothTime m_PredictedTime;
// input
struct // TODO: handle input better
{
int m_aData[MAX_INPUT_SIZE]; // the input data
int m_Tick; // the tick that the input is for
int64_t m_PredictedTime; // prediction latency when we sent this input
int64_t m_PredictionMargin; // prediction margin when we sent this input
int64_t m_Time;
} m_aInputs[NUM_DUMMIES][200];
int m_aCurrentInput[NUM_DUMMIES];
bool m_LastDummy;
bool m_DummySendConnInfo;
// graphs
CGraph m_InputtimeMarginGraph;
CGraph m_GametimeMarginGraph;
CGraph m_FpsGraph;
// the game snapshots are modifiable by the game
CSnapshotStorage m_aSnapshotStorage[NUM_DUMMIES];
CSnapshotStorage::CHolder *m_aapSnapshots[NUM_DUMMIES][NUM_SNAPSHOT_TYPES];
int m_aReceivedSnapshots[NUM_DUMMIES];
char m_aaSnapshotIncomingData[NUM_DUMMIES][CSnapshot::MAX_SIZE];
int m_aSnapshotIncomingDataSize[NUM_DUMMIES];
CSnapshotStorage::CHolder m_aDemorecSnapshotHolders[NUM_SNAPSHOT_TYPES];
char *m_aaapDemorecSnapshotData[NUM_SNAPSHOT_TYPES][2][CSnapshot::MAX_SIZE];
CSnapshotDelta m_SnapshotDelta;
std::list<std::shared_ptr<CDemoEdit>> m_lpEditJobs;
//
bool m_CanReceiveServerCapabilities;
bool m_ServerSentCapabilities;
CServerCapabilities m_ServerCapabilities;
bool ShouldSendChatTimeoutCodeHeuristic();
CServerInfo m_CurrentServerInfo;
int64_t m_CurrentServerInfoRequestTime; // >= 0 should request, == -1 got info
int m_CurrentServerPingInfoType;
int m_CurrentServerPingBasicToken;
int m_CurrentServerPingToken;
CUuid m_CurrentServerPingUuid;
int64_t m_CurrentServerCurrentPingTime; // >= 0 request running
int64_t m_CurrentServerNextPingTime; // >= 0 should request
// version info
struct CVersionInfo
{
enum
{
STATE_INIT = 0,
STATE_START,
STATE_READY,
};
int m_State;
class CHostLookup m_VersionServeraddr;
} m_VersionInfo;
static void GraphicsThreadProxy(void *pThis) { ((CClient *)pThis)->GraphicsThread(); }
void GraphicsThread();
std::vector<SWarning> m_vWarnings;
#if defined(CONF_FAMILY_UNIX)
CFifo m_Fifo;
#endif
IOHANDLE m_BenchmarkFile;
int64_t m_BenchmarkStopTime;
CChecksum m_Checksum;
int m_OwnExecutableSize = 0;
IOHANDLE m_OwnExecutable;
// favorite command handling
bool m_FavoritesGroup = false;
bool m_FavoritesGroupAllowPing = false;
int m_FavoritesGroupNum = 0;
NETADDR m_aFavoritesGroupAddresses[MAX_SERVER_ADDRESSES];
void UpdateDemoIntraTimers();
int MaxLatencyTicks() const;
int PredictionMargin() const;
public:
IEngine *Engine() { return m_pEngine; }
IEngineGraphics *Graphics() { return m_pGraphics; }
IEngineInput *Input() { return m_pInput; }
IEngineSound *Sound() { return m_pSound; }
IGameClient *GameClient() { return m_pGameClient; }
IConfigManager *ConfigManager() { return m_pConfigManager; }
CConfig *Config() { return m_pConfig; }
IStorage *Storage() { return m_pStorage; }
IUpdater *Updater() { return m_pUpdater; }
IDiscord *Discord() { return m_pDiscord; }
ISteam *Steam() { return m_pSteam; }
CClient();
// ----- send functions -----
int SendMsg(int Conn, CMsgPacker *pMsg, int Flags) override;
// Send via the currently active client (main/dummy)
int SendMsgActive(CMsgPacker *pMsg, int Flags) override;
void SendInfo();
void SendEnterGame(int Conn);
void SendReady();
void SendMapRequest();
bool RconAuthed() const override { return m_aRconAuthed[g_Config.m_ClDummy] != 0; }
bool UseTempRconCommands() const override { return m_UseTempRconCommands != 0; }
void RconAuth(const char *pName, const char *pPassword) override;
void Rcon(const char *pCmd) override;
bool ConnectionProblems() const override;
bool SoundInitFailed() const override { return m_SoundInitFailed; }
IGraphics::CTextureHandle GetDebugFont() const override { return m_DebugFont; }
void DirectInput(int *pInput, int Size);
void SendInput();
// TODO: OPT: do this a lot smarter!
int *GetInput(int Tick, int IsDummy) const override;
const char *LatestVersion() const override;
// ------ state handling -----
void SetState(EClientState s);
// called when the map is loaded and we should init for a new round
void OnEnterGame(bool Dummy);
void EnterGame(int Conn) override;
void Connect(const char *pAddress, const char *pPassword = nullptr) override;
void DisconnectWithReason(const char *pReason);
void Disconnect() override;
void DummyDisconnect(const char *pReason) override;
void DummyConnect() override;
bool DummyConnected() override;
bool DummyConnecting() override;
bool DummyAllowed() override;
int m_DummyConnected;
int m_LastDummyConnectTime;
void GetServerInfo(CServerInfo *pServerInfo) const override;
void ServerInfoRequest();
int LoadData();
// ---
int GetPredictionTime() override;
void *SnapGetItem(int SnapID, int Index, CSnapItem *pItem) const override;
int SnapItemSize(int SnapID, int Index) const override;
void SnapInvalidateItem(int SnapID, int Index) override;
void *SnapFindItem(int SnapID, int Type, int ID) const override;
int SnapNumItems(int SnapID) const override;
void SnapSetStaticsize(int ItemType, int Size) override;
void Render();
void DebugRender();
void Restart() override;
void Quit() override;
const char *PlayerName() const override;
const char *DummyName() const override;
const char *ErrorString() const override;
const char *LoadMap(const char *pName, const char *pFilename, SHA256_DIGEST *pWantedSha256, unsigned WantedCrc);
const char *LoadMapSearch(const char *pMapName, SHA256_DIGEST *pWantedSha256, int WantedCrc);
void ProcessConnlessPacket(CNetChunk *pPacket);
void ProcessServerInfo(int Type, NETADDR *pFrom, const void *pData, int DataSize);
void ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy);
int UnpackAndValidateSnapshot(CSnapshot *pFrom, CSnapshot *pTo);
void ResetMapDownload();
void FinishMapDownload();
void RequestDDNetInfo() override;
void ResetDDNetInfo();
bool IsDDNetInfoChanged();
void FinishDDNetInfo();
void LoadDDNetInfo();
const NETADDR &ServerAddress() const override { return *m_aNetClient[CONN_MAIN].ServerAddress(); }
int ConnectNetTypes() const override;
const char *ConnectAddressString() const override { return m_aConnectAddressStr; }
const char *MapDownloadName() const override { return m_aMapdownloadName; }
int MapDownloadAmount() const override { return !m_pMapdownloadTask ? m_MapdownloadAmount : (int)m_pMapdownloadTask->Current(); }
int MapDownloadTotalsize() const override { return !m_pMapdownloadTask ? m_MapdownloadTotalsize : (int)m_pMapdownloadTask->Size(); }
void PumpNetwork();
void OnDemoPlayerSnapshot(void *pData, int Size) override;
void OnDemoPlayerMessage(void *pData, int Size) override;
void Update();
void RegisterInterfaces();
void InitInterfaces();
void Run();
bool CtrlShiftKey(int Key, bool &Last);
static void Con_Connect(IConsole::IResult *pResult, void *pUserData);
static void Con_Disconnect(IConsole::IResult *pResult, void *pUserData);
static void Con_DummyConnect(IConsole::IResult *pResult, void *pUserData);
static void Con_DummyDisconnect(IConsole::IResult *pResult, void *pUserData);
static void Con_DummyResetInput(IConsole::IResult *pResult, void *pUserData);
static void Con_Quit(IConsole::IResult *pResult, void *pUserData);
static void Con_DemoPlay(IConsole::IResult *pResult, void *pUserData);
static void Con_DemoSpeed(IConsole::IResult *pResult, void *pUserData);
static void Con_Minimize(IConsole::IResult *pResult, void *pUserData);
static void Con_Ping(IConsole::IResult *pResult, void *pUserData);
static void Con_Screenshot(IConsole::IResult *pResult, void *pUserData);
static void Con_Reset(IConsole::IResult *pResult, void *pUserData);
#if defined(CONF_VIDEORECORDER)
static void StartVideo(IConsole::IResult *pResult, void *pUserData, const char *pVideoName);
static void Con_StartVideo(IConsole::IResult *pResult, void *pUserData);
static void Con_StopVideo(IConsole::IResult *pResult, void *pUserData);
const char *DemoPlayer_Render(const char *pFilename, int StorageType, const char *pVideoName, int SpeedIndex) override;
#endif
static void Con_Rcon(IConsole::IResult *pResult, void *pUserData);
static void Con_RconAuth(IConsole::IResult *pResult, void *pUserData);
static void Con_RconLogin(IConsole::IResult *pResult, void *pUserData);
static void Con_BeginFavoriteGroup(IConsole::IResult *pResult, void *pUserData);
static void Con_EndFavoriteGroup(IConsole::IResult *pResult, void *pUserData);
static void Con_AddFavorite(IConsole::IResult *pResult, void *pUserData);
static void Con_RemoveFavorite(IConsole::IResult *pResult, void *pUserData);
static void Con_Play(IConsole::IResult *pResult, void *pUserData);
static void Con_Record(IConsole::IResult *pResult, void *pUserData);
static void Con_StopRecord(IConsole::IResult *pResult, void *pUserData);
static void Con_AddDemoMarker(IConsole::IResult *pResult, void *pUserData);
static void Con_BenchmarkQuit(IConsole::IResult *pResult, void *pUserData);
static void ConchainServerBrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainFullscreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainWindowBordered(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainWindowScreen(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainWindowVSync(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainTimeoutSeed(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainPassword(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainReplays(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void Con_DemoSlice(IConsole::IResult *pResult, void *pUserData);
static void Con_DemoSliceBegin(IConsole::IResult *pResult, void *pUserData);
static void Con_DemoSliceEnd(IConsole::IResult *pResult, void *pUserData);
static void Con_SaveReplay(IConsole::IResult *pResult, void *pUserData);
void RegisterCommands();
const char *DemoPlayer_Play(const char *pFilename, int StorageType) override;
void DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int Recorder) override;
void DemoRecorder_HandleAutoStart() override;
void DemoRecorder_StartReplayRecorder();
void DemoRecorder_Stop(int Recorder, bool RemoveFile = false) override;
void DemoRecorder_AddDemoMarker(int Recorder);
IDemoRecorder *DemoRecorder(int Recorder) override;
void AutoScreenshot_Start() override;
void AutoStatScreenshot_Start() override;
void AutoScreenshot_Cleanup();
void AutoStatScreenshot_Cleanup();
void AutoCSV_Start() override;
void AutoCSV_Cleanup();
void ServerBrowserUpdate() override;
void HandleConnectAddress(const NETADDR *pAddr);
void HandleConnectLink(const char *pLink);
void HandleDemoPath(const char *pPath);
void HandleMapPath(const char *pPath);
virtual void InitChecksum();
virtual int HandleChecksum(int Conn, CUuid Uuid, CUnpacker *pUnpacker);
// gfx
void SwitchWindowScreen(int Index) override;
void SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing) override;
void ToggleWindowVSync() override;
void LoadFont() override;
void Notify(const char *pTitle, const char *pMessage) override;
void BenchmarkQuit(int Seconds, const char *pFilename);
void UpdateAndSwap() override;
// DDRace
void GenerateTimeoutSeed() override;
void GenerateTimeoutCodes(const NETADDR *pAddrs, int NumAddrs);
int GetCurrentRaceTime() override;
const char *GetCurrentMap() const override;
const char *GetCurrentMapPath() const override;
SHA256_DIGEST GetCurrentMapSha256() const override;
unsigned GetCurrentMapCrc() const override;
void RaceRecord_Start(const char *pFilename) override;
void RaceRecord_Stop() override;
bool RaceRecord_IsRecording() override;
void DemoSliceBegin() override;
void DemoSliceEnd() override;
void DemoSlice(const char *pDstPath, CLIENTFUNC_FILTER pfnFilter, void *pUser) override;
virtual void SaveReplay(int Length, const char *pFilename = "");
bool EditorHasUnsavedData() const override { return m_pEditor->HasUnsavedData(); }
IFriends *Foes() override { return &m_Foes; }
void GetSmoothTick(int *pSmoothTick, float *pSmoothIntraTick, float MixAmount) override;
SWarning *GetCurWarning() override;
CChecksumData *ChecksumData() override { return &m_Checksum.m_Data; }
bool InfoTaskRunning() override { return m_pDDNetInfoTask != nullptr; }
int UdpConnectivity(int NetType) override;
};
#endif