RingRacers/src/d_clisrv.h

733 lines
22 KiB
C

// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2025 by Kart Krew.
// Copyright (C) 2020 by Sonic Team Junior.
// Copyright (C) 2000 by DooM Legacy Team.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file d_clisrv.h
/// \brief high level networking stuff
#ifndef __D_CLISRV__
#define __D_CLISRV__
#include "d_ticcmd.h"
#include "d_net.h"
#include "d_netcmd.h"
#include "d_net.h"
#include "tables.h"
#include "d_player.h"
#include "mserv.h"
#include "k_pwrlv.h" // PWRLV_NUMTYPES
#include "p_saveg.h" // NETSAVEGAMESIZE
#ifdef __cplusplus
extern "C" {
#endif
/*
The 'packet version' is used to distinguish packet formats.
This version is independent of VERSION and SUBVERSION. Different
applications may follow different packet versions.
*/
#define PACKETVERSION 0
// Network play related stuff.
// There is a data struct that stores network
// communication related stuff, and another
// one that defines the actual packets to
// be transmitted.
#define HU_MAXMSGLEN 223
#define MAXSERVERNAME 32
#define MAXSERVERCONTACT 1024
// Networking and tick handling related.
#define BACKUPTICS 512 // more than enough for most timeouts....
#define CLIENTBACKUPTICS 32
#define MAXTEXTCMD 512
// No. of tics your controls can be delayed by.
// TODO: Instead of storing a ton of extra cmds for gentlemens' delay,
// keep them in a linked-list, with timestamps to discard everything that's older than already sent.
// That will support any amount of lag, and be less wasteful for clients who don't use it.
// This just works as a quick implementation.
#define MAXGENTLEMENDELAY TICRATE
//
// Packet structure
//
typedef enum
{
PT_NOTHING, // To send a nop through the network. ^_~
PT_SERVERCFG, // Server config used in start game
// (must stay 1 for backwards compatibility).
// This is a positive response to a CLIENTJOIN request.
PT_CLIENTCMD, // Ticcmd of the client.
PT_CLIENTMIS, // Same as above with but saying resend from.
PT_CLIENT2CMD, // 2 cmds in the packet for splitscreen.
PT_CLIENT2MIS, // Same as above with but saying resend from
PT_NODEKEEPALIVE, // Same but without ticcmd and consistancy
PT_NODEKEEPALIVEMIS,
PT_SERVERTICS, // All cmds for the tic.
PT_SERVERREFUSE, // Server refuses joiner (reason inside).
PT_SERVERSHUTDOWN,
PT_CLIENTQUIT, // Client closes the connection.
PT_ASKINFO, // Anyone can ask info of the server.
PT_SERVERINFO, // Send game & server info (gamespy).
PT_PLAYERINFO, // Send information for players in game (gamespy).
PT_REQUESTFILE, // Client requests a file transfer
PT_ASKINFOVIAMS, // Packet from the MS requesting info be sent to new client.
// If this ID changes, update masterserver definition.
PT_WILLRESENDGAMESTATE, // Hey Client, I am about to resend you the gamestate!
PT_CANRECEIVEGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead.
PT_RECEIVEDGAMESTATE, // Thank you Server, I am ready to play again!
PT_SENDINGLUAFILE, // Server telling a client Lua needs to open a file
PT_ASKLUAFILE, // Client telling the server they don't have the file
PT_HASLUAFILE, // Client telling the server they have the file
// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
// Kart-specific packets
PT_CLIENT3CMD, // 3P
PT_CLIENT3MIS,
PT_CLIENT4CMD, // 4P
PT_CLIENT4MIS,
PT_BASICKEEPALIVE,// Keep the network alive during wipes, as tics aren't advanced and NetUpdate isn't called
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
// allows HSendPacket(*, true, *, *) to return false.
// In addition, this packet can't occupy all the available slots.
PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
PT_FILEACK,
PT_FILERECEIVED,
PT_TEXTCMD, // Extra text commands from the client.
PT_TEXTCMD2, // Splitscreen text commands.
PT_TEXTCMD3, // 3P
PT_TEXTCMD4, // 4P
PT_CLIENTJOIN, // Client wants to join; used in start game.
PT_NODETIMEOUT, // Packet sent to self if the connection times out.
PT_TELLFILESNEEDED, // Client, to server: "what other files do I need starting from this number?"
PT_MOREFILESNEEDED, // Server, to client: "you need these (+ more on top of those)"
PT_LOGIN, // Login attempt from the client.
PT_PING, // Packet sent to tell clients the other client's latency to server.
PT_CLIENTKEY, // "Here's my public key"
PT_SERVERCHALLENGE, // "Prove it"
PT_CHALLENGEALL, // Prove to the other clients you are who you say you are, sign this random bullshit!
PT_RESPONSEALL, // OK, here is my signature on that random bullshit
PT_RESULTSALL, // Here's what everyone responded to PT_CHALLENGEALL with, if this is wrong or you don't receive it disconnect
PT_SAY, // "Hey server, please send this chat message to everyone via XD_SAY"
PT_REQMAPQUEUE, // Client requesting a roundqueue operation
PT_VOICE, // Voice packet for either side
NUMPACKETTYPE
} packettype_t;
typedef enum
{
SIGN_OK,
SIGN_BADTIME, // Timestamp differs by too much, suspect reuse of an old challenge.
SIGN_BADIP // Asked to sign the wrong IP by an external host, suspect reuse of another server's challenge.
} shouldsign_t;
#ifdef PACKETDROP
void Command_Drop(void);
void Command_Droprate(void);
#endif
void Command_Numnodes(void);
#if defined(_MSC_VER)
#pragma pack(1)
#endif
// Client to server packet
struct clientcmd_pak
{
UINT8 client_tic;
UINT8 resendfrom;
INT16 consistancy;
UINT8 wantdelay;
ticcmd_t cmd;
} ATTRPACK;
// Splitscreen packet
// WARNING: must have the same format of clientcmd_pak, for more easy use
struct client2cmd_pak
{
UINT8 client_tic;
UINT8 resendfrom;
INT16 consistancy;
UINT8 wantdelay;
ticcmd_t cmd, cmd2;
} ATTRPACK;
// 3P Splitscreen packet
// WARNING: must have the same format of clientcmd_pak, for more easy use
struct client3cmd_pak
{
UINT8 client_tic;
UINT8 resendfrom;
INT16 consistancy;
UINT8 wantdelay;
ticcmd_t cmd, cmd2, cmd3;
} ATTRPACK;
// 4P Splitscreen packet
// WARNING: must have the same format of clientcmd_pak, for more easy use
struct client4cmd_pak
{
UINT8 client_tic;
UINT8 resendfrom;
INT16 consistancy;
UINT8 wantdelay;
ticcmd_t cmd, cmd2, cmd3, cmd4;
} ATTRPACK;
#ifdef _MSC_VER
#pragma warning(disable : 4200)
#endif
// Server to client packet
// this packet is too large
struct servertics_pak
{
UINT8 starttic;
UINT8 numtics;
UINT8 numslots; // "Slots filled": Highest player number in use plus one.
ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large
} ATTRPACK;
struct serverconfig_pak
{
UINT8 version; // Different versions don't work
UINT8 subversion; // Contains build version
// Server launch stuffs
UINT8 serverplayer;
UINT8 totalslotnum; // "Slots": highest player number in use plus one.
tic_t gametic;
UINT8 clientnode;
UINT8 gamestate;
UINT8 gametype;
UINT8 modifiedgame;
char server_context[8]; // Unique context id, generated at server startup.
// Discord info (always defined for net compatibility)
UINT8 maxplayer;
boolean allownewplayer;
boolean discordinvites;
char server_name[MAXSERVERNAME];
char server_contact[MAXSERVERCONTACT];
} ATTRPACK;
struct filetx_pak
{
UINT8 fileid;
UINT32 filesize;
UINT8 iteration;
UINT32 position;
UINT16 size;
UINT8 data[]; // Size is variable using hardware_MAXPACKETLENGTH
} ATTRPACK;
struct fileacksegment_t
{
UINT32 start;
UINT32 acks;
} ATTRPACK;
struct fileack_pak
{
UINT8 fileid;
UINT8 iteration;
UINT8 numsegments;
fileacksegment_t segments[];
} ATTRPACK;
#ifdef _MSC_VER
#pragma warning(default : 4200)
#endif
#define MAXAPPLICATION 16
struct player_config_t
{
char name[MAXPLAYERNAME+1];
UINT16 skin;
UINT16 color;
INT16 follower;
UINT16 follower_color;
UINT8 weapon_prefs;
UINT8 min_delay;
uint8_t key[PUBKEYLENGTH];
UINT16 pwr[PWRLV_NUMTYPES];
} ATTRPACK;
struct clientconfig_pak
{
UINT8 _255;/* see serverinfo_pak */
UINT8 packetversion;
char application[MAXAPPLICATION];
UINT8 version; // Different versions don't work
UINT8 subversion; // Contains build version
UINT8 localplayers; // number of splitscreen players
UINT8 mode;
char _names_outdated[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME];
UINT8 availabilities[MAXAVAILABILITY];
uint8_t challengeResponse[MAXSPLITSCREENPLAYERS][SIGNATURELENGTH];
player_config_t player_configs[MAXSPLITSCREENPLAYERS];
} ATTRPACK;
#define SV_SPEEDMASK 0x03 // used to send kartspeed
#define SV_DEDICATED 0x40 // server is dedicated
#define SV_VOICEENABLED 0x80 // voice_mute is off/voice chat is enabled
#define SV_LOTSOFADDONS 0x20 // flag used to ask for full file list in d_netfil
#define MAXFILENEEDED 915
#define MAX_MIRROR_LENGTH 256
// This packet is too large
struct serverinfo_pak
{
/*
In the old packet, 'version' is the first field. Now that field is set
to 255 always, so older versions won't be confused with the new
versions or vice-versa.
*/
UINT8 _255;
UINT8 packetversion;
char application[MAXAPPLICATION];
UINT8 version;
UINT8 subversion;
UINT8 commit[GIT_SHA_ABBREV];
UINT8 numberofplayer;
UINT8 maxplayer;
UINT8 refusereason; // 0: joinable, 1: joins disabled, 2: full
char gametypename[24];
UINT8 modifiedgame;
UINT8 cheatsenabled;
UINT8 kartvars; // Previously isdedicated, now appropriated for our own nefarious purposes
UINT8 fileneedednum;
tic_t time;
tic_t leveltime;
char servername[MAXSERVERNAME];
char maptitle[33];
unsigned char mapmd5[16];
UINT8 actnum;
UINT8 iszone;
char httpsource[MAX_MIRROR_LENGTH]; // HTTP URL to download from, always defined for compatibility
INT16 avgpwrlv; // Kart avg power level
UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
} ATTRPACK;
struct serverrefuse_pak
{
char reason[255];
} ATTRPACK;
struct askinfo_pak
{
UINT8 version;
tic_t time; // used for ping evaluation
} ATTRPACK;
struct msaskinfo_pak
{
char clientaddr[22];
tic_t time; // used for ping evaluation
} ATTRPACK;
// Shorter player information for external use.
struct plrinfo
{
UINT8 num;
char name[MAXPLAYERNAME+1];
UINT8 address[4]; // sending another string would run us up against MAXPACKETLENGTH
UINT8 team;
UINT8 deprecated_skin;
UINT8 data; // Color is first four bits, hasflag, isit and issuper have one bit each, the last is unused.
UINT32 score;
UINT16 timeinserver; // In seconds.
} ATTRPACK;
struct filesneededconfig_pak
{
INT32 first;
UINT8 num;
UINT8 more;
UINT8 files[MAXFILENEEDED]; // is filled with writexxx (byteptr.h)
} ATTRPACK;
struct clientkey_pak
{
uint8_t key[MAXSPLITSCREENPLAYERS][PUBKEYLENGTH];
} ATTRPACK;
struct serverchallenge_pak
{
uint8_t secret[CHALLENGELENGTH];
} ATTRPACK;
struct challengeall_pak
{
uint8_t secret[CHALLENGELENGTH];
} ATTRPACK;
struct responseall_pak
{
uint8_t signature[MAXSPLITSCREENPLAYERS][SIGNATURELENGTH];
} ATTRPACK;
struct resultsall_pak
{
uint8_t signature[MAXPLAYERS][SIGNATURELENGTH];
} ATTRPACK;
struct say_pak
{
char message[HU_MAXMSGLEN];
UINT8 target;
UINT8 flags;
UINT8 source;
} ATTRPACK;
struct reqmapqueue_pak
{
UINT16 newmapnum;
UINT16 newgametype;
UINT8 flags;
UINT8 source;
} ATTRPACK;
struct netinfo_pak
{
UINT32 pingtable[MAXPLAYERS+1];
UINT32 packetloss[MAXPLAYERS+1];
UINT32 delay[MAXPLAYERS+1];
} ATTRPACK;
// Sent by both sides. Contains Opus-encoded voice packet
// flags bitset map (left to right, low to high)
// | PPPPPTRR | -- P = Player num, T = Terminal, R = Reserved (0)
// Data following voice header is a single Opus frame
struct voice_pak
{
UINT64 frame;
UINT8 flags;
} ATTRPACK;
#define VOICE_PAK_FLAGS_PLAYERNUM_BITS 0x1F
#define VOICE_PAK_FLAGS_TERMINAL_BIT 0x20
#define VOICE_PAK_FLAGS_RESERVED0_BIT 0x40
#define VOICE_PAK_FLAGS_RESERVED1_BIT 0x80
#define VOICE_PAK_FLAGS_RESERVED_BITS (VOICE_PAK_FLAGS_RESERVED0_BIT | VOICE_PAK_FLAGS_RESERVED1_BIT)
//
// Network packet data
//
struct doomdata_t
{
UINT32 checksum;
UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack
UINT8 ackreturn; // The return of the ack number
UINT8 packettype;
#ifdef SIGNGAMETRAFFIC
uint8_t signature[MAXSPLITSCREENPLAYERS][SIGNATURELENGTH];
#endif
UINT8 reserved; // Padding
union
{
clientcmd_pak clientpak; // 147 bytes
client2cmd_pak client2pak; // 206 bytes
client3cmd_pak client3pak; // 264 bytes(?)
client4cmd_pak client4pak; // 324 bytes(?)
servertics_pak serverpak; // 132495 bytes (more around 360, no?)
serverconfig_pak servercfg; // 773 bytes
UINT8 textcmd[MAXTEXTCMD+2]; // 66049 bytes (wut??? 64k??? More like 258 bytes...)
char filetxpak[sizeof (filetx_pak)];// 139 bytes
char fileack[sizeof (fileack_pak)];
UINT8 filereceived;
clientconfig_pak clientcfg; // 650 bytes
UINT8 md5sum[16];
serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
askinfo_pak askinfo; // 61 bytes
msaskinfo_pak msaskinfo; // 22 bytes
plrinfo playerinfo[MSCOMPAT_MAXPLAYERS];// 576 bytes(?)
INT32 filesneedednum; // 4 bytes
filesneededconfig_pak filesneededcfg; // ??? bytes
netinfo_pak netinfo; // Don't believe their lies
clientkey_pak clientkey; // 32 bytes
serverchallenge_pak serverchallenge; // 256 bytes
challengeall_pak challengeall; // 256 bytes
responseall_pak responseall; // 256 bytes
resultsall_pak resultsall; // 1024 bytes. Also, you really shouldn't trust anything here.
say_pak say; // I don't care anymore.
reqmapqueue_pak reqmapqueue; // Formerly XD_REQMAPQUEUE
voice_pak voice; // Unreliable voice data, variable length
} u; // This is needed to pack diff packet types data together
} ATTRPACK;
#if defined(_MSC_VER)
#pragma pack()
#endif
#define MAXSERVERLIST (MAXNETNODES-1)
#define GTCALC_RACE 0
#define GTCALC_BATTLE 1
#define GTCALC_CUSTOM 2
struct serverelem_t
{
SINT8 node;
serverinfo_pak info;
UINT8 cachedgtcalc;
};
extern serverelem_t serverlist[MAXSERVERLIST];
extern UINT32 serverlistcount, serverlistultimatecount;
extern boolean serverlistmode;
extern INT32 mapchangepending;
// Points inside doomcom
extern doomdata_t *netbuffer;
extern consvar_t cv_stunserver;
extern consvar_t cv_httpsource;
extern consvar_t cv_kicktime;
extern consvar_t cv_showjoinaddress;
extern consvar_t cv_playbackspeed;
#define BASEPACKETSIZE offsetof(doomdata_t, u)
#define FILETXHEADER offsetof(filetx_pak, data)
#define BASESERVERTICSSIZE offsetof(doomdata_t, u.serverpak.cmds[0])
typedef enum
{
KICK_MSG_PLAYER_QUIT = 0, // Player intentionally left
KICK_MSG_KICKED, // Server kick message w/ no reason
KICK_MSG_CUSTOM_KICK, // Server kick message w/ reason
KICK_MSG_VOTE_KICK, // Vote kick message
KICK_MSG_BANNED, // Ban message w/ no reason
KICK_MSG_CUSTOM_BAN, // Ban message w/ custom reason
KICK_MSG_TIMEOUT, // Player's connection timed out
KICK_MSG_PING_HIGH, // Player hit the ping limit
KICK_MSG_GRIEF, // Player was detected by antigrief
KICK_MSG_CON_FAIL, // Player failed to resync game state
KICK_MSG_SIGFAIL, // Player failed signature check
KICK_MSG__MAX // Number of unique messages
} kickmsg_t;
typedef enum
{
KR_KICK = 1, //Kicked by server
KR_PINGLIMIT = 2, //Broke Ping Limit
KR_SYNCH = 3, //Synch Failure
KR_TIMEOUT = 4, //Connection Timeout
KR_BAN = 5, //Banned by server
KR_LEAVE = 6, //Quit the game
} kickreason_t;
/* the max number of name changes in some time period */
#define MAXNAMECHANGES (5)
#define NAMECHANGERATE (60*TICRATE)
extern boolean server;
extern boolean serverrunning;
#define client (!server)
extern boolean dedicated; // For dedicated server
extern UINT16 software_MAXPACKETLENGTH;
extern boolean acceptnewnode;
extern SINT8 servernode;
extern char connectedservername[MAXSERVERNAME];
extern char connectedservercontact[MAXSERVERCONTACT];
extern UINT32 ourIP;
extern uint8_t lastReceivedKey[MAXNETNODES][MAXSPLITSCREENPLAYERS][PUBKEYLENGTH];
extern uint8_t lastSentChallenge[MAXNETNODES][CHALLENGELENGTH];
extern uint8_t lastChallengeAll[CHALLENGELENGTH];
extern uint8_t lastReceivedSignature[MAXPLAYERS][SIGNATURELENGTH];
extern uint8_t knownWhenChallenged[MAXPLAYERS][PUBKEYLENGTH];
extern boolean expectChallenge;
// We give clients a chance to verify each other once per race.
// When is that challenge sent, and when should clients bail if they don't receive the responses?
#define CHALLENGEALL_START (TICRATE*5) // Server sends challenges here.
#define CHALLENGEALL_KICKUNRESPONSIVE (TICRATE*10) // Server kicks players that haven't submitted signatures here.
#define CHALLENGEALL_SENDRESULTS (TICRATE*15) // Server waits for kicks to process until here. (Failing players shouldn't be in-game when results are received, or clients get spooked.)
#define CHALLENGEALL_CLIENTCUTOFF (TICRATE*20) // If challenge process hasn't completed by now, clients who were in-game for CHALLENGEALL_START should leave.
void Command_Ping_f(void);
extern tic_t connectiontimeout;
extern tic_t jointimeout;
extern UINT16 pingmeasurecount;
extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS];
extern UINT32 playerpacketlosstable[MAXPLAYERS];
extern UINT32 playerdelaytable[MAXPLAYERS];
extern tic_t servermaxping;
extern boolean server_lagless;
extern consvar_t cv_mindelay;
extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_maxconnections, cv_joindelay;
extern consvar_t cv_pingtimeout, cv_blamecfail;
extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
#ifdef VANILLAJOINNEXTROUND
extern consvar_t cv_joinnextround;
#endif
extern consvar_t cv_discordinvites;
extern consvar_t cv_allowguests;
extern consvar_t cv_gamestochat;
#ifdef DEVELOP
extern consvar_t cv_badjoin;
extern consvar_t cv_badtraffic;
extern consvar_t cv_badresponse;
extern consvar_t cv_noresponse;
extern consvar_t cv_nochallenge;
extern consvar_t cv_badresults;
extern consvar_t cv_noresults;
extern consvar_t cv_badtime;
extern consvar_t cv_badip;
#endif
// Used in d_net, the only dependence
tic_t ExpandTics(INT32 low, tic_t basetic);
void D_ClientServerInit(void);
void GenerateChallenge(uint8_t *buf);
shouldsign_t ShouldSignChallenge(uint8_t *message);
// Initialise the other field
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(const UINT8 **p, INT32 playernum));
void SendNetXCmdForPlayer(UINT8 playerid, netxcmd_t id, const void *param, size_t nparam);
#define SendNetXCmd(id, param, nparam) SendNetXCmdForPlayer(0, id, param, nparam) // Shortcut for P1
void SendKick(UINT8 playernum, UINT8 msg);
// Create any new ticcmds and broadcast to other players.
void NetKeepAlive(void);
void NetUpdate(void);
void NetVoiceUpdate(void);
void SV_StartSinglePlayerServer(INT32 dogametype, boolean donetgame);
boolean SV_SpawnServer(void);
void SV_StopServer(void);
void SV_ResetServer(void);
/*--------------------------------------------------
boolean K_AddBotFromServer(UINT16 skin, UINT8 difficulty, botStyle_e style, UINT8 *newplayernum);
Adds a new bot, using a server-sided packet sent to all clients.
Using regular K_AddBot wherever possible is better, but this is kept
as a back-up measure if this is the only option.
Input Arguments:-
skin - Skin number that the bot will use.
difficulty - Difficulty level this bot will use.
style - Bot style to spawn this bot with, see botStyle_e.
newplayernum - Pointer to the last valid player slot number.
Is a pointer so that this function can be called multiple times to add more than one bot.
Return:-
true if a bot can be added via a packet later, otherwise false.
--------------------------------------------------*/
boolean K_AddBotFromServer(UINT16 skin, UINT8 difficulty, botStyle_e style, UINT8 *p);
void CL_AddSplitscreenPlayer(void);
void CL_RemoveSplitscreenPlayer(UINT8 p);
void CL_Reset(void);
void CL_ClearPlayer(INT32 playernum);
void CL_RemovePlayer(INT32 playernum, kickreason_t reason);
void CL_QueryServerList(msg_server_t *list);
void CL_UpdateServerList(void);
void CL_TimeoutServerList(void);
// Is there a game running
boolean Playing(void);
// Advance client-to-client pubkey verification flow
void UpdateChallenges(void);
// Broadcasts special packets to other players
// to notify of game exit
void D_QuitNetGame(void);
//? How many ticks to run?
boolean TryRunTics(tic_t realtic);
// extra data for lmps
// these functions scare me. they contain magic.
/*boolean AddLmpExtradata(UINT8 **demo_p, INT32 playernum);
void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum);*/
// translate a playername in a player number return -1 if not found and
// print a error message in the console
SINT8 nametonum(const char *name);
extern char motd[254], server_context[8];
extern UINT8 playernode[MAXPLAYERS];
/* consoleplayer of this player (splitscreen) */
extern UINT8 playerconsole[MAXPLAYERS];
INT32 D_NumPlayers(void);
INT32 D_NumPlayersInRace(void);
boolean D_IsPlayerHumanAndGaming(INT32 player_number);
void D_ResetTiccmds(void);
void D_ResetTiccmdAngle(UINT8 ss, angle_t angle);
ticcmd_t *D_LocalTiccmd(UINT8 ss);
tic_t GetLag(INT32 node);
UINT8 GetFreeXCmdSize(UINT8 playerid);
void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest);
extern UINT8 hu_redownloadinggamestate;
extern UINT8 adminpassmd5[16];
extern boolean adminpasswordset;
extern boolean hu_stopped;
//
// SRB2Kart
//
void HandleSigfail(const char *string);
void DoSayPacket(SINT8 target, UINT8 flags, UINT8 source, char *message);
void DoSayPacketFromCommand(SINT8 target, size_t usedargs, UINT8 flags);
void DoVoicePacket(SINT8 target, UINT64 frame, const UINT8* opusdata, size_t len);
void SendServerNotice(SINT8 target, char *message);
#ifdef __cplusplus
} // extern "C"
#endif
#endif