ddnet/src/engine/server/server.h

491 lines
14 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_SERVER_H
#define ENGINE_SERVER_SERVER_H
#include <base/hash.h>
2022-06-17 16:52:05 +00:00
#include <engine/console.h>
2010-05-29 07:25:38 +00:00
#include <engine/server.h>
#include <engine/shared/demo.h>
#include <engine/shared/econ.h>
2016-05-02 21:36:21 +00:00
#include <engine/shared/fifo.h>
#include <engine/shared/netban.h>
#include <engine/shared/network.h>
#include <engine/shared/protocol.h>
#include <engine/shared/snapshot.h>
#include <engine/shared/uuid_manager.h>
#include <list>
2022-06-17 16:52:05 +00:00
#include <memory>
#include <vector>
#include "antibot.h"
2017-03-02 15:16:29 +00:00
#include "authmanager.h"
#include "name_ban.h"
2017-03-02 15:16:29 +00:00
#if defined(CONF_UPNP)
#include "upnp.h"
2020-04-19 13:14:21 +00:00
#endif
2022-06-17 16:52:05 +00:00
class CConfig;
class CHostLookup;
class CLogMessage;
2022-06-17 16:52:05 +00:00
class CMsgPacker;
class CPacker;
class IEngineMap;
2010-05-29 07:25:38 +00:00
class CSnapIDPool
{
enum
{
MAX_IDS = 32 * 1024,
2010-05-29 07:25:38 +00:00
};
2022-05-02 18:31:17 +00:00
// State of a Snap ID
enum
{
ID_FREE = 0,
ID_ALLOCATED = 1,
ID_TIMED = 2,
};
2010-05-29 07:25:38 +00:00
class CID
{
public:
short m_Next;
2018-07-10 09:29:02 +00:00
short m_State; // 0 = free, 1 = allocated, 2 = timed
2010-05-29 07:25:38 +00:00
int m_Timeout;
};
CID m_aIDs[MAX_IDS];
2010-05-29 07:25:38 +00:00
int m_FirstFree;
int m_FirstTimed;
int m_LastTimed;
int m_Usage;
int m_InUsage;
public:
2010-05-29 07:25:38 +00:00
CSnapIDPool();
2010-05-29 07:25:38 +00:00
void Reset();
void RemoveFirstTimeout();
int NewID();
void TimeoutIDs();
void FreeID(int ID);
2010-05-29 07:25:38 +00:00
};
2011-12-29 22:36:53 +00:00
class CServerBan : public CNetBan
{
class CServer *m_pServer;
template<class T>
int BanExt(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason);
2011-12-29 22:36:53 +00:00
public:
class CServer *Server() const { return m_pServer; }
2017-03-21 10:24:44 +00:00
void InitServerBan(class IConsole *pConsole, class IStorage *pStorage, class CServer *pServer);
2011-12-29 22:36:53 +00:00
int BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason) override;
int BanRange(const CNetRange *pRange, int Seconds, const char *pReason) override;
2011-12-29 22:36:53 +00:00
static void ConBanExt(class IConsole::IResult *pResult, void *pUser);
2020-08-19 09:38:49 +00:00
static void ConBanRegion(class IConsole::IResult *pResult, void *pUser);
static void ConBanRegionRange(class IConsole::IResult *pResult, void *pUser);
2011-12-29 22:36:53 +00:00
};
2010-05-29 07:25:38 +00:00
class CServer : public IServer
{
friend class CServerLogger;
2010-05-29 07:25:38 +00:00
class IGameServer *m_pGameServer;
class CConfig *m_pConfig;
2010-05-29 07:25:38 +00:00
class IConsole *m_pConsole;
class IStorage *m_pStorage;
class IEngineAntibot *m_pAntibot;
Add HTTP masterserver registering and HTTP masterserver Registering ----------- The idea is that game servers push their server info to the masterservers every 15 seconds or when the server info changes, but not more than once per second. The game servers do not support the old registering protocol anymore, the backward compatibility is handled by the masterserver. The register call is a HTTP POST to a URL like `https://master1.ddnet.tw/ddnet/15/register` and looks like this: ```json POST /ddnet/15/register HTTP/1.1 Address: tw-0.6+udp://connecting-address.invalid:8303 Secret: 81fa3955-6f83-4290-818d-31c0906b1118 Challenge-Secret: 81fa3955-6f83-4290-818d-31c0906b1118:tw0.6/ipv6 Info-Serial: 0 { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } ``` The `Address` header declares that the server wants to register itself as a `tw-0.6+udp` server, i.e. a server speaking a Teeworlds-0.6-compatible protocol. The free-form `Secret` header is used as a server identity, the server list will be deduplicated via this secret. The free-form `Challenge-Secret` is sent back via UDP for a port forward check. This might have security implications as the masterserver can be asked to send a UDP packet containing some user-controlled bytes. This is somewhat mitigated by the fact that it can only go to an attacker-controlled IP address. The `Info-Serial` header is an integer field that should increase each time the server info (in the body) changes. The masterserver uses that field to ensure that it doesn't use old server infos. The body is a free-form JSON object set by the game server. It should contain certain keys in the correct form to be accepted by clients. The body is optional if the masterserver already confirmed the reception of the info with the given `Info-Serial`. Not shown in this payload is the `Connless-Token` header that is used for Teeworlds 0.7 style communication. Also not shown is the `Challenge-Token` that should be included once the server receives the challenge token via UDP. The masterserver responds with a `200 OK` with a body like this: ``` {"status":"success"} ``` The `status` field can be `success` if the server was successfully registered on the masterserver, `need_challenge` if the masterserver wants the correct `Challenge-Token` header before the register process is successful, `need_info` if the server sent an empty body but the masterserver doesn't actually know the server info. It can also be `error` if the request was malformed, only in this case an HTTP status code except `200 OK` is sent. Synchronization --------------- The masterserver keeps state and outputs JSON files every second. ```json { "servers": [ { "addresses": [ "tw-0.6+udp://127.0.0.1:8303", "tw-0.6+udp://[::1]:8303" ], "info_serial": 0, "info": { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } } ] } ``` `servers.json` (or configured by `--out`) is a server list that is compatible with DDNet 15.5+ clients. It is a JSON object containing a single key `servers` with a list of game servers. Each game server is represented by a JSON object with an `addresses` key containing a list of all known addresses of the server and an `info` key containing the free-form server info sent by the game server. The free-form `info` JSON object re-encoded by the master server and thus canonicalized and stripped of any whitespace characters outside strings. ```json { "kind": "mastersrv", "now": 1816002, "secrets": { "tw-0.6+udp://127.0.0.1:8303": { "ping_time": 1811999, "secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb" }, "tw-0.6+udp://[::1]:8303": { "ping_time": 1811999, "secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb" } }, "servers": { "42d8f991-f2fa-46e5-a9ae-ebcc93846feb": { "info_serial": 0, "info": { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } } } } ``` `--write-dump` outputs a JSON file compatible with `--read-dump-dir`, this can be used to synchronize servers across different masterservers. `--read-dump-dir` is also used to ingest servers from the backward compatibility layer that pings each server for their server info using the old protocol. The `kind` field describe that this is `mastersrv` output and not from a `backcompat`. This is used for prioritizing `mastersrv` information over `backcompat` information. The `now` field contains an integer describing the current time in milliseconds relative an unspecified epoch that is fixed for each JSON file. This is done instead of using the current time as the epoch for better compression of non-changing data. `secrets` is a map from each server address and to a JSON object containing the last ping time (`ping_time`) in milliseconds relative to the same epoch as before, and the server secret (`secret`) that is used to unify server infos from different addresses of the same logical server. `servers` is a map from the aforementioned `secret`s to the corresponding `info_serial` and `info`. ```json [ "tw-0.6+udp://127.0.0.1:8303", "tw-0.6+udp://[::1]:8303" ] ``` `--write-addresses` outputs a JSON file containing all addresses corresponding to servers that are registered to HTTP masterservers. It does not contain the servers that are obtained via backward compatibility measures. This file can be used by an old-style masterserver to also list new-style servers without the game servers having to register there. An implementation of this can be found at https://github.com/heinrich5991/teeworlds/tree/mastersrv_6_backcompat for Teeworlds 0.5/0.6 masterservers and at https://github.com/heinrich5991/teeworlds/tree/mastersrv_7_backcompat for Teeworlds 0.7 masterservers. All these JSON files can be sent over the network in an efficient way using https://github.com/heinrich5991/twmaster-collect. It establishes a zstd-compressed TCP connection authenticated by a string token that is sent in plain-text. It watches the specified file and transmits it every time it changes. Due to the zstd-compression, the data sent over the network is similar to the size of a diff. Implementation -------------- The masterserver implementation was done in Rust. The current gameserver register implementation doesn't support more than one masterserver for registering.
2022-05-19 20:03:17 +00:00
class IRegister *m_pRegister;
2020-04-19 13:14:21 +00:00
#if defined(CONF_UPNP)
CUPnP m_UPnP;
#endif
#if defined(CONF_FAMILY_UNIX)
UNIXSOCKETADDR m_ConnLoggingDestAddr;
bool m_ConnLoggingSocketCreated;
UNIXSOCKET m_ConnLoggingSocket;
#endif
class CDbConnectionPool *m_pConnectionPool;
2010-05-29 07:25:38 +00:00
public:
class IGameServer *GameServer() { return m_pGameServer; }
class CConfig *Config() { return m_pConfig; }
const CConfig *Config() const { return m_pConfig; }
2010-05-29 07:25:38 +00:00
class IConsole *Console() { return m_pConsole; }
class IStorage *Storage() { return m_pStorage; }
class IEngineAntibot *Antibot() { return m_pAntibot; }
class CDbConnectionPool *DbPool() { return m_pConnectionPool; }
2010-05-29 07:25:38 +00:00
enum
{
MAX_RCONCMD_SEND = 16,
};
2010-05-29 07:25:38 +00:00
class CClient
{
public:
enum
{
STATE_EMPTY = 0,
STATE_PREAUTH,
2010-05-29 07:25:38 +00:00
STATE_AUTH,
STATE_CONNECTING,
STATE_READY,
STATE_INGAME,
SNAPRATE_INIT = 0,
2010-05-29 07:25:38 +00:00
SNAPRATE_FULL,
2016-09-05 09:38:11 +00:00
SNAPRATE_RECOVER,
DNSBL_STATE_NONE = 0,
2016-09-05 09:38:11 +00:00
DNSBL_STATE_PENDING,
DNSBL_STATE_BLACKLISTED,
DNSBL_STATE_WHITELISTED,
2010-05-29 07:25:38 +00:00
};
2010-05-29 07:25:38 +00:00
class CInput
{
public:
int m_aData[MAX_INPUT_SIZE];
int m_GameTick; // the tick that was chosen for the input
};
2010-05-29 07:25:38 +00:00
// connection state info
int m_State;
int m_Latency;
int m_SnapRate;
2022-05-27 17:40:27 +00:00
double m_Traffic;
2021-06-23 05:05:49 +00:00
int64_t m_TrafficSince;
2013-08-04 02:24:03 +00:00
2010-05-29 07:25:38 +00:00
int m_LastAckedSnapshot;
int m_LastInputTick;
CSnapshotStorage m_Snapshots;
2010-05-29 07:25:38 +00:00
CInput m_LatestInput;
CInput m_aInputs[200]; // TODO: handle input better
int m_CurrentInput;
2010-05-29 07:25:38 +00:00
char m_aName[MAX_NAME_LENGTH];
char m_aClan[MAX_CLAN_LENGTH];
int m_Country;
2010-05-29 07:25:38 +00:00
int m_Score;
int m_Authed;
2017-03-02 15:16:29 +00:00
int m_AuthKey;
int m_AuthTries;
int m_NextMapChunk;
int m_Flags;
bool m_ShowIps;
const IConsole::CCommandInfo *m_pRconCmdToSend;
bool m_HasPersistentData;
void *m_pPersistentData;
2010-05-29 07:25:38 +00:00
void Reset();
2013-12-31 05:13:57 +00:00
// DDRace
NETADDR m_Addr;
bool m_GotDDNetVersionPacket;
bool m_DDNetVersionSettled;
int m_DDNetVersion;
char m_aDDNetVersionStr[64];
CUuid m_ConnectionID;
2016-09-05 09:38:11 +00:00
// DNSBL
int m_DnsblState;
std::shared_ptr<CHostLookup> m_pDnsblLookup;
2020-03-29 02:36:38 +00:00
bool m_Sixup;
2010-05-29 07:25:38 +00:00
};
2010-05-29 07:25:38 +00:00
CClient m_aClients[MAX_CLIENTS];
2020-10-27 17:57:14 +00:00
int m_aIdMap[MAX_CLIENTS * VANILLA_MAX_CLIENTS];
2010-05-29 07:25:38 +00:00
CSnapshotDelta m_SnapshotDelta;
CSnapshotBuilder m_SnapshotBuilder;
CSnapIDPool m_IDPool;
CNetServer m_NetServer;
2011-07-30 11:40:01 +00:00
CEcon m_Econ;
2016-05-02 21:36:21 +00:00
#if defined(CONF_FAMILY_UNIX)
CFifo m_Fifo;
#endif
2011-12-29 22:36:53 +00:00
CServerBan m_ServerBan;
2010-05-29 07:25:38 +00:00
IEngineMap *m_pMap;
2021-06-23 05:05:49 +00:00
int64_t m_GameStartTime;
2010-05-29 07:25:38 +00:00
//int m_CurrentGameTick;
enum
{
UNINITIALIZED = 0,
RUNNING = 1,
STOPPING = 2
};
2010-05-29 07:25:38 +00:00
int m_RunServer;
bool m_MapReload;
2015-10-22 15:27:30 +00:00
bool m_ReloadedWhenEmpty;
int m_RconClientID;
int m_RconAuthLevel;
2011-07-30 11:40:01 +00:00
int m_PrintCBIndex;
char m_aShutdownReason[128];
2010-05-29 07:25:38 +00:00
2020-06-19 21:52:13 +00:00
enum
{
MAP_TYPE_SIX = 0,
MAP_TYPE_SIXUP,
NUM_MAP_TYPES
2020-06-19 21:52:13 +00:00
};
char m_aCurrentMap[IO_MAX_PATH_LENGTH];
SHA256_DIGEST m_aCurrentMapSha256[NUM_MAP_TYPES];
unsigned m_aCurrentMapCrc[NUM_MAP_TYPES];
unsigned char *m_apCurrentMapData[NUM_MAP_TYPES];
unsigned int m_aCurrentMapSize[NUM_MAP_TYPES];
CDemoRecorder m_aDemoRecorder[MAX_CLIENTS + 1];
2017-03-02 15:16:29 +00:00
CAuthManager m_AuthManager;
2021-06-23 05:05:49 +00:00
int64_t m_ServerInfoFirstRequest;
int m_ServerInfoNumRequests;
2017-10-13 00:25:50 +00:00
char m_aErrorShutdownReason[128];
std::vector<CNameBan> m_vNameBans;
2010-05-29 07:25:38 +00:00
CServer();
~CServer();
bool IsClientNameAvailable(int ClientID, const char *pNameRequest);
bool SetClientNameImpl(int ClientID, const char *pNameRequest, bool Set);
2010-05-29 07:25:38 +00:00
bool WouldClientNameChange(int ClientID, const char *pNameRequest) override;
void SetClientName(int ClientID, const char *pName) override;
void SetClientClan(int ClientID, char const *pClan) override;
void SetClientCountry(int ClientID, int Country) override;
void SetClientScore(int ClientID, int Score) override;
void SetClientFlags(int ClientID, int Flags) override;
2010-05-29 07:25:38 +00:00
void Kick(int ClientID, const char *pReason) override;
void Ban(int ClientID, int Seconds, const char *pReason) override;
2010-05-29 07:25:38 +00:00
void DemoRecorder_HandleAutoStart() override;
bool DemoRecorder_IsRecording() override;
2010-05-29 07:25:38 +00:00
//int Tick()
2021-06-23 05:05:49 +00:00
int64_t TickStartTime(int Tick);
2010-05-29 07:25:38 +00:00
//int TickSpeed()
int Init();
void SendLogLine(const CLogMessage *pMessage);
void SetRconCID(int ClientID) override;
int GetAuthedState(int ClientID) const override;
const char *GetAuthName(int ClientID) const override;
void GetMapInfo(char *pMapName, int MapNameSize, int *pMapSize, SHA256_DIGEST *pMapSha256, int *pMapCrc) override;
int GetClientInfo(int ClientID, CClientInfo *pInfo) const override;
void SetClientDDNetVersion(int ClientID, int DDNetVersion) override;
void GetClientAddr(int ClientID, char *pAddrStr, int Size) const override;
const char *ClientName(int ClientID) const override;
const char *ClientClan(int ClientID) const override;
int ClientCountry(int ClientID) const override;
bool ClientIngame(int ClientID) const override;
bool ClientAuthed(int ClientID) const override;
int Port() const override;
int MaxClients() const override;
int ClientCount() const override;
int DistinctClientCount() const override;
int GetClientVersion(int ClientID) const override;
int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) override;
2010-05-29 07:25:38 +00:00
void DoSnapshot();
2020-03-29 02:36:38 +00:00
static int NewClientCallback(int ClientID, void *pUser, bool Sixup);
static int NewClientNoAuthCallback(int ClientID, void *pUser);
static int DelClientCallback(int ClientID, const char *pReason, void *pUser);
2010-05-29 07:25:38 +00:00
2015-08-23 15:51:28 +00:00
static int ClientRejoinCallback(int ClientID, void *pUser);
void SendRconType(int ClientID, bool UsernameReq);
void SendCapabilities(int ClientID);
void SendMap(int ClientID);
void SendMapData(int ClientID, int Chunk);
void SendConnectionReady(int ClientID);
void SendRconLine(int ClientID, const char *pLine);
// Accepts -1 as ClientID to mean "all clients with at least auth level admin"
void SendRconLogLine(int ClientID, const CLogMessage *pMessage);
void SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID);
void SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID);
void UpdateClientRconCommands();
2010-05-29 07:25:38 +00:00
void ProcessClientPacket(CNetChunk *pPacket);
class CCache
{
2019-11-02 23:33:30 +00:00
public:
class CCacheChunk
{
public:
CCacheChunk(const void *pData, int Size);
CCacheChunk(const CCacheChunk &) = delete;
std::vector<uint8_t> m_vData;
2019-11-02 23:33:30 +00:00
};
2020-10-27 17:57:14 +00:00
std::list<CCacheChunk> m_Cache;
2019-11-02 23:33:30 +00:00
2019-11-03 00:53:50 +00:00
CCache();
~CCache();
2019-11-02 23:33:30 +00:00
void AddChunk(const void *pData, int Size);
void Clear();
};
2020-10-27 17:57:14 +00:00
CCache m_aServerInfoCache[3 * 2];
CCache m_aSixupServerInfoCache[2];
2019-11-03 00:07:10 +00:00
bool m_ServerInfoNeedsUpdate;
2019-11-02 23:33:30 +00:00
void ExpireServerInfo() override;
2019-11-02 23:33:30 +00:00
void CacheServerInfo(CCache *pCache, int Type, bool SendClients);
void CacheServerInfoSixup(CCache *pCache, bool SendClients);
void SendServerInfo(const NETADDR *pAddr, int Token, int Type, bool SendClients);
void GetServerInfoSixup(CPacker *pPacker, int Token, bool SendClients);
bool RateLimitServerInfoConnless();
void SendServerInfoConnless(const NETADDR *pAddr, int Token, int Type);
Add HTTP masterserver registering and HTTP masterserver Registering ----------- The idea is that game servers push their server info to the masterservers every 15 seconds or when the server info changes, but not more than once per second. The game servers do not support the old registering protocol anymore, the backward compatibility is handled by the masterserver. The register call is a HTTP POST to a URL like `https://master1.ddnet.tw/ddnet/15/register` and looks like this: ```json POST /ddnet/15/register HTTP/1.1 Address: tw-0.6+udp://connecting-address.invalid:8303 Secret: 81fa3955-6f83-4290-818d-31c0906b1118 Challenge-Secret: 81fa3955-6f83-4290-818d-31c0906b1118:tw0.6/ipv6 Info-Serial: 0 { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } ``` The `Address` header declares that the server wants to register itself as a `tw-0.6+udp` server, i.e. a server speaking a Teeworlds-0.6-compatible protocol. The free-form `Secret` header is used as a server identity, the server list will be deduplicated via this secret. The free-form `Challenge-Secret` is sent back via UDP for a port forward check. This might have security implications as the masterserver can be asked to send a UDP packet containing some user-controlled bytes. This is somewhat mitigated by the fact that it can only go to an attacker-controlled IP address. The `Info-Serial` header is an integer field that should increase each time the server info (in the body) changes. The masterserver uses that field to ensure that it doesn't use old server infos. The body is a free-form JSON object set by the game server. It should contain certain keys in the correct form to be accepted by clients. The body is optional if the masterserver already confirmed the reception of the info with the given `Info-Serial`. Not shown in this payload is the `Connless-Token` header that is used for Teeworlds 0.7 style communication. Also not shown is the `Challenge-Token` that should be included once the server receives the challenge token via UDP. The masterserver responds with a `200 OK` with a body like this: ``` {"status":"success"} ``` The `status` field can be `success` if the server was successfully registered on the masterserver, `need_challenge` if the masterserver wants the correct `Challenge-Token` header before the register process is successful, `need_info` if the server sent an empty body but the masterserver doesn't actually know the server info. It can also be `error` if the request was malformed, only in this case an HTTP status code except `200 OK` is sent. Synchronization --------------- The masterserver keeps state and outputs JSON files every second. ```json { "servers": [ { "addresses": [ "tw-0.6+udp://127.0.0.1:8303", "tw-0.6+udp://[::1]:8303" ], "info_serial": 0, "info": { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } } ] } ``` `servers.json` (or configured by `--out`) is a server list that is compatible with DDNet 15.5+ clients. It is a JSON object containing a single key `servers` with a list of game servers. Each game server is represented by a JSON object with an `addresses` key containing a list of all known addresses of the server and an `info` key containing the free-form server info sent by the game server. The free-form `info` JSON object re-encoded by the master server and thus canonicalized and stripped of any whitespace characters outside strings. ```json { "kind": "mastersrv", "now": 1816002, "secrets": { "tw-0.6+udp://127.0.0.1:8303": { "ping_time": 1811999, "secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb" }, "tw-0.6+udp://[::1]:8303": { "ping_time": 1811999, "secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb" } }, "servers": { "42d8f991-f2fa-46e5-a9ae-ebcc93846feb": { "info_serial": 0, "info": { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } } } } ``` `--write-dump` outputs a JSON file compatible with `--read-dump-dir`, this can be used to synchronize servers across different masterservers. `--read-dump-dir` is also used to ingest servers from the backward compatibility layer that pings each server for their server info using the old protocol. The `kind` field describe that this is `mastersrv` output and not from a `backcompat`. This is used for prioritizing `mastersrv` information over `backcompat` information. The `now` field contains an integer describing the current time in milliseconds relative an unspecified epoch that is fixed for each JSON file. This is done instead of using the current time as the epoch for better compression of non-changing data. `secrets` is a map from each server address and to a JSON object containing the last ping time (`ping_time`) in milliseconds relative to the same epoch as before, and the server secret (`secret`) that is used to unify server infos from different addresses of the same logical server. `servers` is a map from the aforementioned `secret`s to the corresponding `info_serial` and `info`. ```json [ "tw-0.6+udp://127.0.0.1:8303", "tw-0.6+udp://[::1]:8303" ] ``` `--write-addresses` outputs a JSON file containing all addresses corresponding to servers that are registered to HTTP masterservers. It does not contain the servers that are obtained via backward compatibility measures. This file can be used by an old-style masterserver to also list new-style servers without the game servers having to register there. An implementation of this can be found at https://github.com/heinrich5991/teeworlds/tree/mastersrv_6_backcompat for Teeworlds 0.5/0.6 masterservers and at https://github.com/heinrich5991/teeworlds/tree/mastersrv_7_backcompat for Teeworlds 0.7 masterservers. All these JSON files can be sent over the network in an efficient way using https://github.com/heinrich5991/twmaster-collect. It establishes a zstd-compressed TCP connection authenticated by a string token that is sent in plain-text. It watches the specified file and transmits it every time it changes. Due to the zstd-compression, the data sent over the network is similar to the size of a diff. Implementation -------------- The masterserver implementation was done in Rust. The current gameserver register implementation doesn't support more than one masterserver for registering.
2022-05-19 20:03:17 +00:00
void UpdateRegisterServerInfo();
2019-11-03 00:07:10 +00:00
void UpdateServerInfo(bool Resend = false);
2010-05-29 07:25:38 +00:00
void PumpNetwork(bool PacketWaiting);
2010-05-29 07:25:38 +00:00
void ChangeMap(const char *pMap) override;
const char *GetMapName() const override;
2010-05-29 07:25:38 +00:00
int LoadMap(const char *pMapName);
void SaveDemo(int ClientID, float Time) override;
void StartRecord(int ClientID) override;
void StopRecord(int ClientID) override;
bool IsRecording(int ClientID) override;
2010-05-29 07:25:38 +00:00
int Run();
static void ConTestingCommands(IConsole::IResult *pResult, void *pUser);
static void ConRescue(IConsole::IResult *pResult, void *pUser);
static void ConKick(IConsole::IResult *pResult, void *pUser);
2011-12-29 22:36:53 +00:00
static void ConStatus(IConsole::IResult *pResult, void *pUser);
static void ConShutdown(IConsole::IResult *pResult, void *pUser);
static void ConRecord(IConsole::IResult *pResult, void *pUser);
static void ConStopRecord(IConsole::IResult *pResult, void *pUser);
static void ConMapReload(IConsole::IResult *pResult, void *pUser);
2011-12-26 21:07:57 +00:00
static void ConLogout(IConsole::IResult *pResult, void *pUser);
static void ConShowIps(IConsole::IResult *pResult, void *pUser);
2017-03-02 15:16:29 +00:00
static void ConAuthAdd(IConsole::IResult *pResult, void *pUser);
static void ConAuthAddHashed(IConsole::IResult *pResult, void *pUser);
static void ConAuthUpdate(IConsole::IResult *pResult, void *pUser);
static void ConAuthUpdateHashed(IConsole::IResult *pResult, void *pUser);
static void ConAuthRemove(IConsole::IResult *pResult, void *pUser);
static void ConAuthList(IConsole::IResult *pResult, void *pUser);
static void ConNameBan(IConsole::IResult *pResult, void *pUser);
static void ConNameUnban(IConsole::IResult *pResult, void *pUser);
static void ConNameBans(IConsole::IResult *pResult, void *pUser);
// console commands for sqlmasters
static void ConAddSqlServer(IConsole::IResult *pResult, void *pUserData);
static void ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData);
static void ConchainLoglevel(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
2010-05-29 07:25:38 +00:00
static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainCommandAccessUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
2017-03-02 15:16:29 +00:00
void LogoutClient(int ClientID, const char *pReason);
void LogoutKey(int Key, const char *pReason);
void ConchainRconPasswordChangeGeneric(int Level, const char *pCurrent, IConsole::IResult *pResult);
static void ConchainRconPasswordChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainRconModPasswordChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainRconHelperPasswordChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainMapUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
2021-12-21 11:23:17 +00:00
static void ConchainSixupUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
2011-01-06 03:46:10 +00:00
#if defined(CONF_FAMILY_UNIX)
static void ConchainConnLoggingServerChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
#endif
2010-05-29 07:25:38 +00:00
void RegisterCommands();
int SnapNewID() override;
void SnapFreeID(int ID) override;
void *SnapNewItem(int Type, int ID, int Size) override;
void SnapSetStaticsize(int ItemType, int Size) override;
// DDRace
void GetClientAddr(int ClientID, NETADDR *pAddr) const override;
int m_aPrevStates[MAX_CLIENTS];
const char *GetAnnouncementLine(char const *pFileName) override;
unsigned m_AnnouncementLastLine;
2013-12-31 05:13:57 +00:00
int *GetIdMap(int ClientID) override;
2016-09-05 09:38:11 +00:00
void InitDnsbl(int ClientID);
bool DnsblWhite(int ClientID) override
2016-09-05 09:38:11 +00:00
{
return m_aClients[ClientID].m_DnsblState == CClient::DNSBL_STATE_NONE ||
m_aClients[ClientID].m_DnsblState == CClient::DNSBL_STATE_WHITELISTED;
2016-09-05 09:38:11 +00:00
}
bool DnsblPending(int ClientID) override
{
return m_aClients[ClientID].m_DnsblState == CClient::DNSBL_STATE_PENDING;
}
bool DnsblBlack(int ClientID) override
{
return m_aClients[ClientID].m_DnsblState == CClient::DNSBL_STATE_BLACKLISTED;
}
void AuthRemoveKey(int KeySlot);
bool ClientPrevIngame(int ClientID) override { return m_aPrevStates[ClientID] == CClient::STATE_INGAME; }
const char *GetNetErrorString(int ClientID) override { return m_NetServer.ErrorString(ClientID); }
void ResetNetErrorString(int ClientID) override { m_NetServer.ResetErrorString(ClientID); }
bool SetTimedOut(int ClientID, int OrigID) override;
void SetTimeoutProtected(int ClientID) override { m_NetServer.SetTimeoutProtected(ClientID); }
2017-10-13 00:25:50 +00:00
void SendMsgRaw(int ClientID, const void *pData, int Size, int Flags) override;
2017-10-13 00:25:50 +00:00
bool ErrorShutdown() const { return m_aErrorShutdownReason[0] != 0; }
void SetErrorShutdown(const char *pReason) override;
bool IsSixup(int ClientID) const override { return ClientID != SERVER_DEMO_CLIENT && m_aClients[ClientID].m_Sixup; }
2020-03-29 02:36:38 +00:00
#ifdef CONF_FAMILY_UNIX
enum CONN_LOGGING_CMD
{
OPEN_SESSION = 1,
CLOSE_SESSION = 2,
};
2020-10-27 17:57:14 +00:00
void SendConnLoggingCommand(CONN_LOGGING_CMD Cmd, const NETADDR *pAddr);
#endif
2010-05-29 07:25:38 +00:00
};
#endif