ddnet/src/engine/server.h

356 lines
11 KiB
C
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (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. */
2010-05-29 07:25:38 +00:00
#ifndef ENGINE_SERVER_H
#define ENGINE_SERVER_H
#include <optional>
#include <type_traits>
#include <base/hash.h>
2018-12-11 08:23:12 +00:00
#include <base/math.h>
2010-05-29 07:25:38 +00:00
#include "kernel.h"
#include "message.h"
#include <engine/shared/protocol.h>
2013-12-31 05:13:57 +00:00
#include <game/generated/protocol.h>
2020-04-16 08:46:43 +00:00
#include <game/generated/protocol7.h>
#include <game/generated/protocolglue.h>
2010-05-29 07:25:38 +00:00
struct CAntibotRoundData;
// When recording a demo on the server, the ClientID -1 is used
enum
{
SERVER_DEMO_CLIENT = -1
};
2010-05-29 07:25:38 +00:00
class IServer : public IInterface
{
MACRO_INTERFACE("server")
2010-05-29 07:25:38 +00:00
protected:
int m_CurrentGameTick;
public:
/*
Structure: CClientInfo
*/
struct CClientInfo
{
const char *m_pName;
int m_Latency;
bool m_GotDDNetVersion;
int m_DDNetVersion;
const char *m_pDDNetVersionStr;
const CUuid *m_pConnectionID;
2010-05-29 07:25:38 +00:00
};
2010-05-29 07:25:38 +00:00
int Tick() const { return m_CurrentGameTick; }
2023-11-23 14:33:30 +00:00
int TickSpeed() const { return SERVER_TICK_SPEED; }
2010-05-29 07:25:38 +00:00
virtual int Port() const = 0;
2011-12-04 15:51:33 +00:00
virtual int MaxClients() const = 0;
virtual int ClientCount() const = 0;
virtual int DistinctClientCount() const = 0;
virtual const char *ClientName(int ClientID) const = 0;
virtual const char *ClientClan(int ClientID) const = 0;
virtual int ClientCountry(int ClientID) const = 0;
virtual bool ClientIngame(int ClientID) const = 0;
virtual bool ClientAuthed(int ClientID) const = 0;
Handle `CServer::GetClientInfo` return to fix use of undefined value ``` /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:790:2: error: Undefined or garbage value returned to caller [clang-analyzer-core.uninitialized.UndefReturn,-warnings-as-errors] return Info.m_DDNetVersion; ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:785:5: note: Assuming 'ClientID' is not equal to SERVER_DEMO_CLIENT if(ClientID == SERVER_DEMO_CLIENT) ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:785:2: note: Taking false branch if(ClientID == SERVER_DEMO_CLIENT) ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:789:2: note: Calling 'CServer::GetClientInfo' GetClientInfo(ClientID, &Info); ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:646:13: note: Assuming 'ClientID' is >= 0 dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid"); ^ /home/runner/work/ddnet/ddnet/src/base/tl/../system.h:58:38: note: expanded from macro 'dbg_assert' #define dbg_assert(test, msg) assert(test) ^ /usr/include/assert.h:93:27: note: expanded from macro 'assert' (static_cast <bool> (expr) \ ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:646:13: note: Left side of '&&' is true dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid"); ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:646:30: note: Assuming 'ClientID' is < MAX_CLIENTS dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid"); ^ /home/runner/work/ddnet/ddnet/src/base/tl/../system.h:58:38: note: expanded from macro 'dbg_assert' #define dbg_assert(test, msg) assert(test) ^ /usr/include/assert.h:93:27: note: expanded from macro 'assert' (static_cast <bool> (expr) \ ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:646:2: note: '?' condition is true dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid"); ^ /home/runner/work/ddnet/ddnet/src/base/tl/../system.h:58:31: note: expanded from macro 'dbg_assert' #define dbg_assert(test, msg) assert(test) ^ /usr/include/assert.h:93:7: note: expanded from macro 'assert' (static_cast <bool> (expr) \ ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:647:13: note: 'pInfo' is not equal to null dbg_assert(pInfo != 0, "info can not be null"); ^ /home/runner/work/ddnet/ddnet/src/base/tl/../system.h:58:38: note: expanded from macro 'dbg_assert' #define dbg_assert(test, msg) assert(test) ^ /usr/include/assert.h:93:27: note: expanded from macro 'assert' (static_cast <bool> (expr) \ ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:647:2: note: '?' condition is true dbg_assert(pInfo != 0, "info can not be null"); ^ /home/runner/work/ddnet/ddnet/src/base/tl/../system.h:58:31: note: expanded from macro 'dbg_assert' #define dbg_assert(test, msg) assert(test) ^ /usr/include/assert.h:93:7: note: expanded from macro 'assert' (static_cast <bool> (expr) \ ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:649:5: note: Assuming field 'm_State' is not equal to STATE_INGAME if(m_aClients[ClientID].m_State == CClient::STATE_INGAME) ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:649:2: note: Taking false branch if(m_aClients[ClientID].m_State == CClient::STATE_INGAME) ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:667:2: note: Returning without writing to 'pInfo->m_DDNetVersion' return 0; ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:789:2: note: Returning from 'CServer::GetClientInfo' GetClientInfo(ClientID, &Info); ^ /home/runner/work/ddnet/ddnet/src/engine/server/server.cpp:790:2: note: Undefined or garbage value returned to caller return Info.m_DDNetVersion; ^ ```
2022-07-31 10:53:26 +00:00
virtual bool GetClientInfo(int ClientID, CClientInfo *pInfo) const = 0;
virtual void SetClientDDNetVersion(int ClientID, int DDNetVersion) = 0;
virtual void GetClientAddr(int ClientID, char *pAddrStr, int Size) const = 0;
/**
* Returns the version of the client with the given client ID.
*
* @param ClientID the client ID, which must be between 0 and
* MAX_CLIENTS - 1, or equal to SERVER_DEMO_CLIENT for server demos.
*
* @return The version of the client with the given client ID.
* For server demos this is always the latest client version.
* On errors, VERSION_NONE is returned.
*/
virtual int GetClientVersion(int ClientID) const = 0;
2010-05-29 07:25:38 +00:00
virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) = 0;
template<class T, typename std::enable_if<!protocol7::is_sixup<T>::value, int>::type = 0>
inline int SendPackMsg(const T *pMsg, int Flags, int ClientID)
2013-12-31 05:13:57 +00:00
{
int Result = 0;
if(ClientID == -1)
2013-12-31 05:13:57 +00:00
{
for(int i = 0; i < MaxClients(); i++)
2013-12-31 05:13:57 +00:00
if(ClientIngame(i))
Result = SendPackMsgTranslate(pMsg, Flags, i);
}
else
{
Result = SendPackMsgTranslate(pMsg, Flags, ClientID);
2013-12-31 05:13:57 +00:00
}
return Result;
}
template<class T, typename std::enable_if<protocol7::is_sixup<T>::value, int>::type = 1>
inline int SendPackMsg(const T *pMsg, int Flags, int ClientID)
{
int Result = 0;
if(ClientID == -1)
{
for(int i = 0; i < MaxClients(); i++)
if(ClientIngame(i) && IsSixup(i))
Result = SendPackMsgOne(pMsg, Flags, i);
}
else if(IsSixup(ClientID))
Result = SendPackMsgOne(pMsg, Flags, ClientID);
return Result;
2013-12-31 05:13:57 +00:00
}
template<class T>
int SendPackMsgTranslate(const T *pMsg, int Flags, int ClientID)
2013-12-31 05:13:57 +00:00
{
return SendPackMsgOne(pMsg, Flags, ClientID);
}
int SendPackMsgTranslate(const CNetMsg_Sv_Emoticon *pMsg, int Flags, int ClientID)
2013-12-31 05:13:57 +00:00
{
CNetMsg_Sv_Emoticon MsgCopy;
mem_copy(&MsgCopy, pMsg, sizeof(MsgCopy));
return Translate(MsgCopy.m_ClientID, ClientID) && SendPackMsgOne(&MsgCopy, Flags, ClientID);
2013-12-31 05:13:57 +00:00
}
int SendPackMsgTranslate(const CNetMsg_Sv_Chat *pMsg, int Flags, int ClientID)
2013-12-31 05:13:57 +00:00
{
CNetMsg_Sv_Chat MsgCopy;
mem_copy(&MsgCopy, pMsg, sizeof(MsgCopy));
char aBuf[1000];
if(MsgCopy.m_ClientID >= 0 && !Translate(MsgCopy.m_ClientID, ClientID))
2013-12-31 05:13:57 +00:00
{
str_format(aBuf, sizeof(aBuf), "%s: %s", ClientName(MsgCopy.m_ClientID), MsgCopy.m_pMessage);
MsgCopy.m_pMessage = aBuf;
MsgCopy.m_ClientID = VANILLA_MAX_CLIENTS - 1;
2013-12-31 05:13:57 +00:00
}
2020-06-12 12:53:18 +00:00
2020-06-12 13:42:13 +00:00
if(IsSixup(ClientID))
2020-06-12 12:53:18 +00:00
{
protocol7::CNetMsg_Sv_Chat Msg7;
Msg7.m_ClientID = MsgCopy.m_ClientID;
Msg7.m_pMessage = MsgCopy.m_pMessage;
Msg7.m_Mode = MsgCopy.m_Team > 0 ? protocol7::CHAT_TEAM : protocol7::CHAT_ALL;
2020-06-12 12:53:18 +00:00
Msg7.m_TargetID = -1;
return SendPackMsgOne(&Msg7, Flags, ClientID);
}
return SendPackMsgOne(&MsgCopy, Flags, ClientID);
2013-12-31 05:13:57 +00:00
}
int SendPackMsgTranslate(const CNetMsg_Sv_KillMsg *pMsg, int Flags, int ClientID)
2013-12-31 05:13:57 +00:00
{
CNetMsg_Sv_KillMsg MsgCopy;
mem_copy(&MsgCopy, pMsg, sizeof(MsgCopy));
if(!Translate(MsgCopy.m_Victim, ClientID))
return 0;
if(!Translate(MsgCopy.m_Killer, ClientID))
MsgCopy.m_Killer = MsgCopy.m_Victim;
return SendPackMsgOne(&MsgCopy, Flags, ClientID);
2013-12-31 05:13:57 +00:00
}
template<class T>
int SendPackMsgOne(const T *pMsg, int Flags, int ClientID)
2010-05-29 07:25:38 +00:00
{
dbg_assert(ClientID != -1, "SendPackMsgOne called with -1");
CMsgPacker Packer(T::ms_MsgID, false, protocol7::is_sixup<T>::value);
2010-05-29 07:25:38 +00:00
if(pMsg->Pack(&Packer))
return -1;
return SendMsg(&Packer, Flags, ClientID);
}
bool Translate(int &Target, int Client)
2013-12-31 05:13:57 +00:00
{
2020-03-29 02:36:38 +00:00
if(IsSixup(Client))
return true;
if(GetClientVersion(Client) >= VERSION_DDNET_OLD)
2013-12-31 05:13:57 +00:00
return true;
2017-03-21 10:24:44 +00:00
int *pMap = GetIdMap(Client);
bool Found = false;
for(int i = 0; i < VANILLA_MAX_CLIENTS; i++)
2013-12-31 05:13:57 +00:00
{
if(Target == pMap[i])
2013-12-31 05:13:57 +00:00
{
2017-03-21 10:24:44 +00:00
Target = i;
Found = true;
2013-12-31 05:13:57 +00:00
break;
}
}
2017-03-21 10:24:44 +00:00
return Found;
2013-12-31 05:13:57 +00:00
}
bool ReverseTranslate(int &Target, int Client)
2013-12-31 05:13:57 +00:00
{
2020-03-29 02:36:38 +00:00
if(IsSixup(Client))
return true;
if(GetClientVersion(Client) >= VERSION_DDNET_OLD)
2013-12-31 05:13:57 +00:00
return true;
Target = clamp(Target, 0, VANILLA_MAX_CLIENTS - 1);
2017-03-21 10:24:44 +00:00
int *pMap = GetIdMap(Client);
if(pMap[Target] == -1)
2013-12-31 05:13:57 +00:00
return false;
2017-03-21 10:24:44 +00:00
Target = pMap[Target];
2013-12-31 05:13:57 +00:00
return true;
}
virtual void GetMapInfo(char *pMapName, int MapNameSize, int *pMapSize, SHA256_DIGEST *pSha256, int *pMapCrc) = 0;
virtual bool WouldClientNameChange(int ClientID, const char *pNameRequest) = 0;
virtual bool WouldClientClanChange(int ClientID, const char *pClanRequest) = 0;
virtual void SetClientName(int ClientID, const char *pName) = 0;
virtual void SetClientClan(int ClientID, const char *pClan) = 0;
virtual void SetClientCountry(int ClientID, int Country) = 0;
virtual void SetClientScore(int ClientID, std::optional<int> Score) = 0;
virtual void SetClientFlags(int ClientID, int Flags) = 0;
2010-05-29 07:25:38 +00:00
virtual int SnapNewID() = 0;
virtual void SnapFreeID(int ID) = 0;
virtual void *SnapNewItem(int Type, int ID, int Size) = 0;
2010-05-29 07:25:38 +00:00
template<typename T>
T *SnapNewItem(int ID)
{
const int Type = protocol7::is_sixup<T>::value ? -T::ms_MsgID : T::ms_MsgID;
return static_cast<T *>(SnapNewItem(Type, ID, sizeof(T)));
}
2010-05-29 07:25:38 +00:00
virtual void SnapSetStaticsize(int ItemType, int Size) = 0;
enum
{
RCON_CID_SERV = -1,
RCON_CID_VOTE = -2,
};
virtual void SetRconCID(int ClientID) = 0;
virtual int GetAuthedState(int ClientID) const = 0;
virtual const char *GetAuthName(int ClientID) const = 0;
virtual void Kick(int ClientID, const char *pReason) = 0;
2019-02-06 12:06:28 +00:00
virtual void Ban(int ClientID, int Seconds, const char *pReason) = 0;
virtual void RedirectClient(int ClientID, int Port, bool Verbose = false) = 0;
virtual void ChangeMap(const char *pMap) = 0;
2019-02-04 22:09:14 +00:00
virtual void DemoRecorder_HandleAutoStart() = 0;
// DDRace
virtual void SaveDemo(int ClientID, float Time) = 0;
virtual void StartRecord(int ClientID) = 0;
virtual void StopRecord(int ClientID) = 0;
virtual bool IsRecording(int ClientID) = 0;
virtual void StopDemos() = 0;
virtual void GetClientAddr(int ClientID, NETADDR *pAddr) const = 0;
2013-12-31 05:13:57 +00:00
virtual int *GetIdMap(int ClientID) = 0;
2016-09-05 09:38:11 +00:00
virtual bool DnsblWhite(int ClientID) = 0;
virtual bool DnsblPending(int ClientID) = 0;
virtual bool DnsblBlack(int ClientID) = 0;
virtual const char *GetAnnouncementLine(const char *pFileName) = 0;
virtual bool ClientPrevIngame(int ClientID) = 0;
virtual const char *GetNetErrorString(int ClientID) = 0;
virtual void ResetNetErrorString(int ClientID) = 0;
virtual bool SetTimedOut(int ClientID, int OrigID) = 0;
virtual void SetTimeoutProtected(int ClientID) = 0;
2017-10-13 00:25:50 +00:00
virtual void SetErrorShutdown(const char *pReason) = 0;
2019-11-03 00:07:10 +00:00
virtual void ExpireServerInfo() = 0;
virtual void FillAntibot(CAntibotRoundData *pData) = 0;
virtual void SendMsgRaw(int ClientID, const void *pData, int Size, int Flags) = 0;
virtual const char *GetMapName() const = 0;
2020-03-29 02:36:38 +00:00
2020-06-23 15:30:57 +00:00
virtual bool IsSixup(int ClientID) const = 0;
2010-05-29 07:25:38 +00:00
};
class IGameServer : public IInterface
{
MACRO_INTERFACE("gameserver")
2010-05-29 07:25:38 +00:00
protected:
public:
// `pPersistentData` may be null if this is the first time `IGameServer`
// is instantiated.
virtual void OnInit(const void *pPersistentData) = 0;
2010-05-29 07:25:38 +00:00
virtual void OnConsoleInit() = 0;
virtual void OnMapChange(char *pNewMapName, int MapNameSize) = 0;
// `pPersistentData` may be null if this is the last time `IGameServer`
// is destroyed.
virtual void OnShutdown(void *pPersistentData) = 0;
2010-05-29 07:25:38 +00:00
virtual void OnTick() = 0;
virtual void OnPreSnap() = 0;
virtual void OnSnap(int ClientID) = 0;
virtual void OnPostSnap() = 0;
virtual void OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) = 0;
2010-05-29 07:25:38 +00:00
// Called before map reload, for any data that the game wants to
// persist to the next map.
//
// Has the size of the return value of `PersistentClientDataSize()`.
//
// Returns whether the game should be supplied with the data when the
// client connects for the next map.
virtual bool OnClientDataPersist(int ClientID, void *pData) = 0;
// Called when a client connects.
//
// If it is reconnecting to the game after a map change, the
// `pPersistentData` point is nonnull and contains the data the game
// previously stored.
virtual void OnClientConnected(int ClientID, void *pPersistentData) = 0;
2010-05-29 07:25:38 +00:00
virtual void OnClientEnter(int ClientID) = 0;
virtual void OnClientDrop(int ClientID, const char *pReason) = 0;
virtual void OnClientPrepareInput(int ClientID, void *pInput) = 0;
2010-05-29 07:25:38 +00:00
virtual void OnClientDirectInput(int ClientID, void *pInput) = 0;
virtual void OnClientPredictedInput(int ClientID, void *pInput) = 0;
2019-01-29 19:32:11 +00:00
virtual void OnClientPredictedEarlyInput(int ClientID, void *pInput) = 0;
virtual bool IsClientReady(int ClientID) const = 0;
virtual bool IsClientPlayer(int ClientID) const = 0;
virtual int PersistentDataSize() const = 0;
virtual int PersistentClientDataSize() const = 0;
virtual CUuid GameUuid() const = 0;
virtual const char *GameType() const = 0;
virtual const char *Version() const = 0;
virtual const char *NetVersion() const = 0;
2011-08-26 18:03:30 +00:00
// DDRace
virtual void OnPreTickTeehistorian() = 0;
2011-08-26 18:03:30 +00:00
virtual void OnSetAuthed(int ClientID, int Level) = 0;
virtual bool PlayerExists(int ClientID) const = 0;
virtual void TeehistorianRecordAntibot(const void *pData, int DataSize) = 0;
virtual void TeehistorianRecordPlayerJoin(int ClientID, bool Sixup) = 0;
virtual void TeehistorianRecordPlayerDrop(int ClientID, const char *pReason) = 0;
virtual void TeehistorianRecordPlayerRejoin(int ClientID) = 0;
virtual void FillAntibot(CAntibotRoundData *pData) = 0;
/**
* Used to report custom player info to master servers.
*
* @param aBuf Should be the json key values to add, starting with a ',' beforehand, like: ',"skin": "default", "team": 1'
* @param i The client id.
*/
virtual void OnUpdatePlayerServerInfo(char *aBuf, int BufSize, int ID) = 0;
2010-05-29 07:25:38 +00:00
};
extern IGameServer *CreateGameServer();
#endif