mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
Add ioq3 server fixes and improvement
- Add a rate limit to protect against DoS attacks - Better IPv6 support
This commit is contained in:
parent
9de71e3529
commit
11f5870a8b
10 changed files with 966 additions and 427 deletions
|
@ -875,6 +875,18 @@ void MSG_ReadData( msg_t *msg, void *data, int len ) {
|
|||
}
|
||||
}
|
||||
|
||||
// a string hasher which gives the same hash value even if the
|
||||
// string is later modified via the legacy MSG read/write code
|
||||
int MSG_HashKey(const char *string, int maxlen) {
|
||||
int register hash, i;
|
||||
|
||||
hash = 0;
|
||||
for (i = 0; i < maxlen && string[i] != '\0'; i++) {
|
||||
hash += string[i] * (119 + i);
|
||||
}
|
||||
hash = (hash ^ (hash >> 10) ^ (hash >> 20));
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
|
|
@ -80,6 +80,8 @@ extern "C" {
|
|||
#define CLIENT_WINDOW_TITLE PRODUCT_NAME
|
||||
#define CLIENT_WINDOW_MIN_TITLE PRODUCT_NAME
|
||||
|
||||
#define MAX_MASTER_SERVERS 5 // number of supported master servers
|
||||
|
||||
#define DEMOEXT "dm_" // standard demo extension
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
|
@ -87,6 +87,7 @@ void MSG_WriteBigString (msg_t *sb, const char *s);
|
|||
void MSG_WriteScrambledString(msg_t* sb, const char* s);
|
||||
void MSG_WriteScrambledBigString(msg_t* sb, const char* s);
|
||||
void MSG_WriteAngle16 (msg_t *sb, float f);
|
||||
int MSG_HashKey(const char *string, int maxlen);
|
||||
void MSG_WriteEntityNum(msg_t* sb, short number);
|
||||
|
||||
void MSG_BeginReading (msg_t *sb);
|
||||
|
@ -202,8 +203,8 @@ NET
|
|||
#define MAX_RELIABLE_COMMANDS 512 // max string commands buffered for restransmit
|
||||
|
||||
typedef enum {
|
||||
NA_BOT,
|
||||
NA_BAD, // an address lookup failed
|
||||
NA_BOT,
|
||||
NA_LOOPBACK,
|
||||
NA_BROADCAST,
|
||||
NA_IP,
|
||||
|
@ -373,7 +374,11 @@ enum clc_ops_e {
|
|||
clc_move, // [[usercmd_t]
|
||||
clc_moveNoDelta, // [[usercmd_t]
|
||||
clc_clientCommand, // [string] message
|
||||
clc_EOF
|
||||
clc_EOF,
|
||||
|
||||
// new commands, supported only by ioquake3 protocol but not legacy
|
||||
clc_voipSpeex, // not wrapped in USE_VOIP, so this value is reserved.
|
||||
clc_voipOpus, //
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -21,8 +21,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
*/
|
||||
// server.h
|
||||
|
||||
#ifndef __SERVER_H__
|
||||
#define __SERVER_H__
|
||||
#pragma once
|
||||
|
||||
#include "../qcommon/q_shared.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
|
@ -43,6 +42,21 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef USE_VOIP
|
||||
#define VOIP_QUEUE_LENGTH 64
|
||||
|
||||
typedef struct voipServerPacket_s
|
||||
{
|
||||
int generation;
|
||||
int sequence;
|
||||
int frames;
|
||||
int len;
|
||||
int sender;
|
||||
int flags;
|
||||
byte data[4000];
|
||||
} voipServerPacket_t;
|
||||
#endif
|
||||
|
||||
typedef struct svEntity_s {
|
||||
struct worldSector_s *worldSector;
|
||||
struct svEntity_s *nextEntityInWorldSector;
|
||||
|
@ -93,6 +107,10 @@ typedef struct {
|
|||
int gameClientSize; // will be > sizeof(playerState_t) due to game private data
|
||||
} server_t;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef struct {
|
||||
int areabytes;
|
||||
byte areabits[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
|
||||
|
@ -103,7 +121,7 @@ typedef struct {
|
|||
// order, otherwise the delta compression will fail
|
||||
int messageSent; // time the message was transmitted
|
||||
int messageAcked; // time the message was acked
|
||||
size_t messageSize; // used to rate drop packets
|
||||
int messageSize; // used to rate drop packets
|
||||
} clientSnapshot_t;
|
||||
|
||||
typedef enum {
|
||||
|
@ -129,9 +147,9 @@ typedef struct client_s {
|
|||
char userinfo[MAX_INFO_STRING]; // name, etc
|
||||
|
||||
char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS];
|
||||
int reliableSequence; // last added reliable message, not necesarily sent or acknowledged yet
|
||||
int reliableSequence; // last added reliable message, not necessarily sent or acknowledged yet
|
||||
int reliableAcknowledge; // last acknowledged reliable message
|
||||
int reliableSent; // last sent reliable message, not necesarily acknowledged yet
|
||||
int reliableSent; // last sent reliable message, not necessarily acknowledged yet
|
||||
int messageAcknowledge;
|
||||
|
||||
int gamestateMessageNum; // netchan->outgoingSequence of gamestate
|
||||
|
@ -151,13 +169,13 @@ typedef struct client_s {
|
|||
// downloading
|
||||
char downloadName[MAX_QPATH]; // if not empty string, we are downloading
|
||||
fileHandle_t download; // file being downloaded
|
||||
size_t downloadSize; // total bytes (can't use EOF because of paks)
|
||||
size_t downloadCount; // bytes sent
|
||||
int downloadSize; // total bytes (can't use EOF because of paks)
|
||||
int downloadCount; // bytes sent
|
||||
int downloadClientBlock; // last block we sent to the client, awaiting ack
|
||||
int downloadCurrentBlock; // current block number
|
||||
int downloadXmitBlock; // last block we xmited
|
||||
unsigned char *downloadBlocks[MAX_DOWNLOAD_WINDOW]; // the buffers for the download blocks
|
||||
size_t downloadBlockSize[MAX_DOWNLOAD_WINDOW];
|
||||
int downloadBlockSize[MAX_DOWNLOAD_WINDOW];
|
||||
qboolean downloadEOF; // We have sent the EOF block
|
||||
int downloadSendTime; // time we last got an ack from the client
|
||||
|
||||
|
@ -182,8 +200,17 @@ typedef struct client_s {
|
|||
netchan_buffer_t *netchan_start_queue;
|
||||
netchan_buffer_t **netchan_end_queue;
|
||||
|
||||
#ifdef USE_VOIP
|
||||
qboolean hasVoip;
|
||||
qboolean muteAllVoip;
|
||||
qboolean ignoreVoipFromClient[MAX_CLIENTS];
|
||||
voipServerPacket_t *voipPacket[VOIP_QUEUE_LENGTH];
|
||||
int queuedVoipPackets;
|
||||
int queuedVoipIndex;
|
||||
#endif
|
||||
|
||||
int oldServerTime;
|
||||
qboolean csUpdated[MAX_CONFIGSTRINGS+1];
|
||||
qboolean csUpdated[MAX_CONFIGSTRINGS];
|
||||
|
||||
server_sound_t server_sounds[ MAX_SERVER_SOUNDS ];
|
||||
int number_of_server_sounds;
|
||||
|
@ -208,7 +235,11 @@ typedef struct client_s {
|
|||
// MAX_CHALLENGES is made large to prevent a denial
|
||||
// of service attack that could cycle all of them
|
||||
// out before legitimate users connected
|
||||
#define MAX_CHALLENGES 1024
|
||||
#define MAX_CHALLENGES 2048
|
||||
// Allow a certain amount of challenges to have the same IP address
|
||||
// to make it a bit harder to DOS one single IP address from connecting
|
||||
// while not allowing a single ip to grab all challenge resources
|
||||
#define MAX_CHALLENGES_MULTI (MAX_CHALLENGES / 2)
|
||||
|
||||
#define AUTHORIZE_TIMEOUT 5000
|
||||
|
||||
|
@ -234,21 +265,23 @@ typedef struct {
|
|||
int firstTime; // time the adr was first used, for authorize timeout checks
|
||||
qboolean wasrefused;
|
||||
qboolean connected;
|
||||
|
||||
//
|
||||
// gamespy stuff
|
||||
//
|
||||
unsigned int gamespyId;
|
||||
char gsChallenge[12];
|
||||
cdKeyState_e cdkeyState;
|
||||
} challenge_t;
|
||||
|
||||
|
||||
#define MAX_MASTERS 8 // max recipients for heartbeat packets
|
||||
|
||||
// this structure will be cleared only when the game dll changes
|
||||
typedef struct {
|
||||
qboolean initialized; // sv_init has completed
|
||||
|
||||
int time; // will be strictly increasing across level changes
|
||||
|
||||
int snapFlagServerBit; // ^= SNAPFLAG_SERVERCOUNT every SV_SpawnServer()
|
||||
|
||||
int time; // will be strictly increasing across level changes
|
||||
int startTime;
|
||||
int lastTime;
|
||||
int serverLagTime;
|
||||
|
@ -257,17 +290,20 @@ typedef struct {
|
|||
|
||||
client_t *clients; // [sv_maxclients->integer];
|
||||
int iNumClients;
|
||||
int numSnapshotEntities; // sv_maxclients->integer*PACKET_BACKUP*MAX_PACKET_ENTITIES
|
||||
int numSnapshotEntities; // sv_maxclients->integer*PACKET_BACKUP*MAX_SNAPSHOT_ENTITIES
|
||||
int nextSnapshotEntities; // next snapshotEntities to use
|
||||
entityState_t *snapshotEntities; // [numSnapshotEntities]
|
||||
int nextHeartbeatTime;
|
||||
challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting
|
||||
netadr_t redirectAddress; // for rcon return messages
|
||||
netadr_t authorizeAddress; // for rcon return messages
|
||||
#ifndef STANDALONE
|
||||
netadr_t authorizeAddress; // authorize server address
|
||||
#endif
|
||||
char gameName[ MAX_QPATH ];
|
||||
char mapName[ MAX_QPATH ];
|
||||
char rawServerName[ MAX_QPATH ];
|
||||
int areabits_warning_time;
|
||||
|
||||
qboolean soundsNeedLoad;
|
||||
char tm_filename[ MAX_QPATH ];
|
||||
int tm_loopcount;
|
||||
|
@ -291,13 +327,10 @@ typedef struct
|
|||
|
||||
//=============================================================================
|
||||
|
||||
extern cvar_t *sv_mapname;
|
||||
extern serverStatic_t svs; // persistant server info across maps
|
||||
extern server_t sv; // cleared each map
|
||||
extern game_export_t *ge; // game exports
|
||||
|
||||
#define MAX_MASTER_SERVERS 5
|
||||
|
||||
extern cvar_t *sv_fps;
|
||||
extern cvar_t *sv_timeout;
|
||||
extern cvar_t *sv_zombietime;
|
||||
|
@ -308,11 +341,12 @@ extern cvar_t *sv_maxclients;
|
|||
|
||||
extern cvar_t *sv_privateClients;
|
||||
extern cvar_t *sv_hostname;
|
||||
extern cvar_t *sv_master[ MAX_MASTER_SERVERS ];
|
||||
extern cvar_t *sv_master[MAX_MASTER_SERVERS];
|
||||
extern cvar_t *sv_reconnectlimit;
|
||||
extern cvar_t *sv_showloss;
|
||||
extern cvar_t *sv_padPackets;
|
||||
extern cvar_t *sv_killserver;
|
||||
extern cvar_t *sv_mapname;
|
||||
extern cvar_t *sv_mapChecksum;
|
||||
extern cvar_t *sv_serverid;
|
||||
extern cvar_t *sv_minRate;
|
||||
|
@ -320,8 +354,11 @@ extern cvar_t *sv_maxRate;
|
|||
extern cvar_t *sv_dlRate;
|
||||
extern cvar_t *sv_minPing;
|
||||
extern cvar_t *sv_maxPing;
|
||||
extern cvar_t *g_gametype;
|
||||
extern cvar_t *g_gametypestring;
|
||||
extern cvar_t *sv_pure;
|
||||
extern cvar_t *sv_floodProtect;
|
||||
extern cvar_t *sv_lanForceRate;
|
||||
extern cvar_t *sv_maplist;
|
||||
extern cvar_t *sv_drawentities;
|
||||
extern cvar_t *sv_deeptracedebug;
|
||||
|
@ -330,14 +367,11 @@ extern cvar_t *sv_netprofileoverlay;
|
|||
extern cvar_t *sv_netoptimize;
|
||||
extern cvar_t *sv_netoptimize_vistime;
|
||||
extern cvar_t *g_netoptimize;
|
||||
extern cvar_t *g_gametype;
|
||||
extern cvar_t *g_gametypestring;
|
||||
extern cvar_t *sv_chatter;
|
||||
extern cvar_t *sv_gamename;
|
||||
extern cvar_t *sv_location;
|
||||
extern cvar_t *sv_debug_gamespy;
|
||||
extern cvar_t *sv_gamespy;
|
||||
extern cvar_t *sv_lanForceRate; // dedicated 1 (LAN) server forces local client rates to 99999 (bug #491)
|
||||
#ifndef STANDALONE
|
||||
extern cvar_t *sv_strictAuth;
|
||||
#endif
|
||||
|
@ -351,6 +385,7 @@ extern cvar_t *sv_voip;
|
|||
extern cvar_t *sv_voipProtocol;
|
||||
#endif
|
||||
|
||||
|
||||
//===========================================================
|
||||
|
||||
//
|
||||
|
@ -378,8 +413,8 @@ extern leakyBucket_t outboundLeakyBucket;
|
|||
qboolean SVC_RateLimit( leakyBucket_t *bucket, int burst, int period );
|
||||
qboolean SVC_RateLimitAddress( netadr_t from, int burst, int period );
|
||||
|
||||
void SV_FinalMessage( const char *message );
|
||||
void QDECL SV_SendServerCommand( client_t *cl, const char *fmt, ...);
|
||||
void SV_FinalMessage (const char *message);
|
||||
void QDECL SV_SendServerCommand( client_t *cl, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
|
||||
void SV_AddOperatorCommands (void);
|
||||
|
@ -445,7 +480,7 @@ void SV_SpawnServer( const char *server, qboolean loadgame, qboolean restart, qb
|
|||
// sv_client.c
|
||||
//
|
||||
challenge_t* FindChallenge(netadr_t from, qboolean connecting);
|
||||
void SV_GetChallenge( netadr_t from );
|
||||
void SV_GetChallenge(netadr_t from);
|
||||
|
||||
void SV_DirectConnect( netadr_t from );
|
||||
|
||||
|
@ -455,15 +490,17 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg );
|
|||
void SV_UserinfoChanged( client_t *cl );
|
||||
|
||||
void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd );
|
||||
void SV_FreeClient(client_t *client);
|
||||
void SV_DropClient( client_t *drop, const char *reason );
|
||||
|
||||
void SV_ExecuteClientCommand( client_t *cl, const char *s, qboolean clientOK );
|
||||
void SV_ClientThink( client_t *cl, usercmd_t *cmd );
|
||||
void SV_ClientThink (client_t *cl, usercmd_t *cmd);
|
||||
|
||||
int SV_WriteDownloadToClient( client_t *cl , msg_t *msg );
|
||||
int SV_WriteDownloadToClient(client_t *cl , msg_t *msg);
|
||||
int SV_SendDownloadMessages(void);
|
||||
int SV_SendQueuedMessages(void);
|
||||
|
||||
|
||||
//
|
||||
// sv_ccmds.c
|
||||
//
|
||||
|
@ -524,7 +561,7 @@ void SV_UnlinkEntity( gentity_t *ent );
|
|||
void SV_LinkEntity( gentity_t *ent );
|
||||
// Needs to be called any time an entity changes origin, mins, maxs,
|
||||
// or solid. Automatically unlinks if needed.
|
||||
// sets ent->v.absmin and ent->v.absmax
|
||||
// sets ent->r.absmin and ent->r.absmax
|
||||
// sets ent->leafnums[] for pvs determination even if the entity
|
||||
// is not solid
|
||||
|
||||
|
@ -585,5 +622,3 @@ void SV_ShutdownGamespy();
|
|||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __SERVER_H__
|
||||
|
|
|
@ -46,10 +46,19 @@ to be sent to the authorize server.
|
|||
|
||||
When an authorizeip is returned, a challenge response will be
|
||||
sent to that ip.
|
||||
|
||||
ioquake3: we added a possibility for clients to add a challenge
|
||||
to their packets, to make it more difficult for malicious servers
|
||||
to hi-jack client connections.
|
||||
Also, the auth stuff is completely disabled for com_standalone games
|
||||
as well as IPv6 connections, since there is no way to use the
|
||||
v4-only auth server for these new types of connections.
|
||||
=================
|
||||
*/
|
||||
void SV_GetChallenge( netadr_t from ) {
|
||||
void SV_GetChallenge(netadr_t from)
|
||||
{
|
||||
challenge_t *challenge;
|
||||
qboolean wasfound = qfalse;
|
||||
|
||||
// ignore if we are in single player
|
||||
// Removed in OPM
|
||||
|
@ -58,6 +67,20 @@ void SV_GetChallenge( netadr_t from ) {
|
|||
// return;
|
||||
//}
|
||||
|
||||
// Prevent using getchallenge as an amplifier
|
||||
if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
|
||||
Com_DPrintf( "SV_GetChallenge: rate limit from %s exceeded, dropping request\n",
|
||||
NET_AdrToString( from ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow getchallenge to be DoSed relatively easily, but prevent
|
||||
// excess outbound bandwidth usage when being flooded inbound
|
||||
if ( SVC_RateLimit( &outboundLeakyBucket, 10, 100 ) ) {
|
||||
Com_DPrintf( "SV_GetChallenge: rate limit exceeded, dropping request\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
challenge = FindChallenge(from, qtrue);
|
||||
|
||||
// if they are on a lan address, send the challengeResponse immediately
|
||||
|
@ -89,8 +112,10 @@ void SV_GetChallenge( netadr_t from ) {
|
|||
|
||||
// check client's cd key
|
||||
NET_OutOfBandPrint(NS_SERVER, from, "getKey %s", challenge->gsChallenge);
|
||||
challenge->pingTime = svs.time;
|
||||
}
|
||||
|
||||
#ifndef STANDALONE
|
||||
/*
|
||||
====================
|
||||
SV_AuthorizeIpPacket
|
||||
|
@ -98,8 +123,6 @@ SV_AuthorizeIpPacket
|
|||
A packet has been returned from the authorize server.
|
||||
If we have a challenge adr for that ip, send the
|
||||
challengeResponse to it
|
||||
|
||||
NOTE: This function is deprecated, SV_GamespyAuthorize must be used instead.
|
||||
====================
|
||||
*/
|
||||
void SV_AuthorizeIpPacket( netadr_t from ) {
|
||||
|
@ -107,6 +130,7 @@ void SV_AuthorizeIpPacket( netadr_t from ) {
|
|||
int i;
|
||||
char *s;
|
||||
char *r;
|
||||
challenge_t *challengeptr;
|
||||
|
||||
if ( !NET_CompareBaseAdr( from, svs.authorizeAddress ) ) {
|
||||
Com_Printf( "SV_AuthorizeIpPacket: not from authorize server\n" );
|
||||
|
@ -125,44 +149,47 @@ void SV_AuthorizeIpPacket( netadr_t from ) {
|
|||
return;
|
||||
}
|
||||
|
||||
challengeptr = &svs.challenges[i];
|
||||
|
||||
// send a packet back to the original client
|
||||
svs.challenges[i].pingTime = svs.time;
|
||||
challengeptr->pingTime = svs.time;
|
||||
s = Cmd_Argv( 2 );
|
||||
r = Cmd_Argv( 3 ); // reason
|
||||
|
||||
if ( !Q_stricmp( s, "demo" ) ) {
|
||||
// they are a demo client trying to connect to a real server
|
||||
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nServer is not a demo server\n" );
|
||||
NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\nServer is not a demo server\n" );
|
||||
// clear the challenge record so it won't timeout and let them through
|
||||
Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) );
|
||||
Com_Memset( challengeptr, 0, sizeof( *challengeptr ) );
|
||||
return;
|
||||
}
|
||||
if ( !Q_stricmp( s, "accept" ) ) {
|
||||
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr,
|
||||
"challengeResponse %i", svs.challenges[i].challenge );
|
||||
NET_OutOfBandPrint(NS_SERVER, challengeptr->adr,
|
||||
"challengeResponse %d %d %d", challengeptr->challenge, challengeptr->clientChallenge, com_protocol->integer);
|
||||
return;
|
||||
}
|
||||
if ( !Q_stricmp( s, "unknown" ) ) {
|
||||
if (!r) {
|
||||
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nAwaiting CD key authorization\n" );
|
||||
NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\nAwaiting CD key authorization\n" );
|
||||
} else {
|
||||
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\n%s\n", r);
|
||||
NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\n%s\n", r);
|
||||
}
|
||||
// clear the challenge record so it won't timeout and let them through
|
||||
Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) );
|
||||
Com_Memset( challengeptr, 0, sizeof( *challengeptr ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// authorization failed
|
||||
if (!r) {
|
||||
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\nSomeone is using this CD Key\n" );
|
||||
NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\nSomeone is using this CD Key\n" );
|
||||
} else {
|
||||
NET_OutOfBandPrint( NS_SERVER, svs.challenges[i].adr, "print\n%s\n", r );
|
||||
NET_OutOfBandPrint( NS_SERVER, challengeptr->adr, "print\n%s\n", r );
|
||||
}
|
||||
|
||||
// clear the challenge record so it won't timeout and let them through
|
||||
Com_Memset( &svs.challenges[i], 0, sizeof( svs.challenges[i] ) );
|
||||
Com_Memset( challengeptr, 0, sizeof(*challengeptr) );
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
==================
|
||||
|
@ -265,16 +292,11 @@ A "connect" OOB command has been received
|
|||
==================
|
||||
*/
|
||||
|
||||
#define PB_MESSAGE "PunkBuster Anti-Cheat software must be installed " \
|
||||
"and Enabled in order to join this server. An updated game patch can be downloaded from " \
|
||||
"www.idsoftware.com"
|
||||
|
||||
void SV_DirectConnect( netadr_t from ) {
|
||||
char userinfo[MAX_INFO_STRING];
|
||||
int i;
|
||||
client_t *cl, *newcl;
|
||||
// Make this a temp variable to avoid stack overflow issues
|
||||
static client_t temp;
|
||||
static client_t temp; // static variable, otherwise it causes a stack overflow
|
||||
gentity_t *ent;
|
||||
int clientNum;
|
||||
int version;
|
||||
|
@ -290,7 +312,7 @@ void SV_DirectConnect( netadr_t from ) {
|
|||
#endif
|
||||
challenge_t* ch;
|
||||
|
||||
Com_DPrintf( "SVC_DirectConnect ()\n" );
|
||||
Com_DPrintf ("SVC_DirectConnect ()\n");
|
||||
|
||||
// Check whether this client is banned.
|
||||
if(SV_IsBanned(&from, qfalse))
|
||||
|
@ -299,9 +321,10 @@ void SV_DirectConnect( netadr_t from ) {
|
|||
return;
|
||||
}
|
||||
|
||||
Q_strncpyz( userinfo, Cmd_Argv( 1 ), sizeof( userinfo ) );
|
||||
Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) );
|
||||
|
||||
version = atoi(Info_ValueForKey(userinfo, "protocol"));
|
||||
|
||||
version = atoi( Info_ValueForKey( userinfo, "protocol" ) );
|
||||
#ifdef LEGACY_PROTOCOL
|
||||
if(version > 0 && com_legacyprotocol->integer == version)
|
||||
compat = qtrue;
|
||||
|
@ -351,42 +374,54 @@ void SV_DirectConnect( netadr_t from ) {
|
|||
Info_SetValueForKey( userinfo, "ip", ip );
|
||||
|
||||
// see if the challenge is valid (LAN clients don't need to challenge)
|
||||
if ( !NET_IsLocalAddress (from) ) {
|
||||
if (!NET_IsLocalAddress(from))
|
||||
{
|
||||
int ping;
|
||||
challenge_t *challengeptr;
|
||||
|
||||
for (i=0 ; i<MAX_CHALLENGES ; i++) {
|
||||
if (NET_CompareAdr(from, svs.challenges[i].adr)) {
|
||||
if ( challenge == svs.challenges[i].challenge ) {
|
||||
break; // good
|
||||
for (i=0; i<MAX_CHALLENGES; i++)
|
||||
{
|
||||
if (NET_CompareAdr(from, svs.challenges[i].adr))
|
||||
{
|
||||
if(challenge == svs.challenges[i].challenge)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == MAX_CHALLENGES) {
|
||||
NET_OutOfBandPrint( NS_SERVER, from, "droperror\nNo or bad challenge for address.\n" );
|
||||
|
||||
if (i == MAX_CHALLENGES)
|
||||
{
|
||||
NET_OutOfBandPrint( NS_SERVER, from, "print\nNo or bad challenge for your address.\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
ping = svs.time - svs.challenges[i].pingTime;
|
||||
Com_Printf( "Client %i connecting with %i challenge ping\n", i, ping );
|
||||
svs.challenges[i].connected = qtrue;
|
||||
challengeptr = &svs.challenges[i];
|
||||
|
||||
if(challengeptr->wasrefused)
|
||||
{
|
||||
// Return silently, so that error messages written by the server keep being displayed.
|
||||
return;
|
||||
}
|
||||
|
||||
ping = svs.time - challengeptr->pingTime;
|
||||
|
||||
// never reject a LAN client based on ping
|
||||
if ( !Sys_IsLANAddress( from ) ) {
|
||||
if ( sv_minPing->value && ping < sv_minPing->value ) {
|
||||
// don't let them keep trying until they get a big delay
|
||||
NET_OutOfBandPrint( NS_SERVER, from, "droperror\nServer is for high pings only\n" );
|
||||
NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for high pings only\n" );
|
||||
Com_DPrintf ("Client %i rejected on a too low ping\n", i);
|
||||
// reset the address otherwise their ping will keep increasing
|
||||
// with each connect message and they'd eventually be able to connect
|
||||
svs.challenges[i].adr.port = 0;
|
||||
challengeptr->wasrefused = qtrue;
|
||||
return;
|
||||
}
|
||||
if ( sv_maxPing->value && ping > sv_maxPing->value ) {
|
||||
NET_OutOfBandPrint( NS_SERVER, from, "droperror\nServer is for low pings only\n" );
|
||||
NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for low pings only\n" );
|
||||
Com_DPrintf ("Client %i rejected on a too high ping\n", i);
|
||||
challengeptr->wasrefused = qtrue;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Com_Printf("Client %i connecting with %i challenge ping\n", i, ping);
|
||||
challengeptr->connected = qtrue;
|
||||
}
|
||||
|
||||
newcl = &temp;
|
||||
|
@ -425,7 +460,7 @@ void SV_DirectConnect( netadr_t from ) {
|
|||
|
||||
// check for privateClient password
|
||||
password = Info_ValueForKey( userinfo, "password" );
|
||||
if ( !strcmp( password, sv_privatePassword->string ) ) {
|
||||
if ( *password && !strcmp( password, sv_privatePassword->string ) ) {
|
||||
startIndex = 0;
|
||||
} else {
|
||||
// skip past the reserved slots
|
||||
|
@ -499,10 +534,9 @@ gotnewcl:
|
|||
|
||||
// get the game a chance to reject this connection or modify the userinfo
|
||||
denied = ge->ClientConnect( clientNum, qtrue, qfalse );
|
||||
|
||||
if ( denied ) {
|
||||
NET_OutOfBandPrint( NS_SERVER, from, "droperror\n%s\n", denied );
|
||||
Com_DPrintf( "Game rejected a connection: %s.\n", denied );
|
||||
Com_DPrintf ( "Game rejected a connection: %s.\n", denied );
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -514,7 +548,7 @@ gotnewcl:
|
|||
SV_UserinfoChanged( newcl );
|
||||
|
||||
// send the connect packet to the client
|
||||
NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" );
|
||||
NET_OutOfBandPrint(NS_SERVER, from, "connectResponse %d", challenge);
|
||||
|
||||
Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name );
|
||||
|
||||
|
@ -554,12 +588,12 @@ SV_FreeClient
|
|||
Destructor for data allocated in a client structure
|
||||
=====================
|
||||
*/
|
||||
void SV_FreeClient(client_t* client)
|
||||
void SV_FreeClient(client_t *client)
|
||||
{
|
||||
#ifdef USE_VOIP
|
||||
int index;
|
||||
|
||||
for (index = client->queuedVoipIndex; index < client->queuedVoipPackets; index++)
|
||||
for(index = client->queuedVoipIndex; index < client->queuedVoipPackets; index++)
|
||||
{
|
||||
index %= ARRAY_LEN(client->voipPacket);
|
||||
|
||||
|
@ -585,18 +619,21 @@ or crashing -- SV_FinalMessage() will handle that
|
|||
void SV_DropClient( client_t *drop, const char *reason ) {
|
||||
int i;
|
||||
challenge_t *challenge;
|
||||
const qboolean isBot = drop->netchan.remoteAddress.type == NA_BOT;
|
||||
|
||||
if ( drop->state == CS_ZOMBIE ) {
|
||||
return; // already dropped
|
||||
}
|
||||
|
||||
if (drop->netchan.remoteAddress.type != NA_BOT) {
|
||||
if ( !isBot ) {
|
||||
// see if we already have a challenge for this ip
|
||||
challenge = &svs.challenges[0];
|
||||
|
||||
for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) {
|
||||
if ( NET_CompareAdr( drop->netchan.remoteAddress, challenge->adr ) ) {
|
||||
challenge->connected = qfalse;
|
||||
for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++)
|
||||
{
|
||||
if(NET_CompareAdr(drop->netchan.remoteAddress, challenge->adr))
|
||||
{
|
||||
Com_Memset(challenge, 0, sizeof(*challenge));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -608,12 +645,6 @@ void SV_DropClient( client_t *drop, const char *reason ) {
|
|||
// tell everyone why they got dropped
|
||||
SV_SendServerCommand( NULL, "print \"%s %s\n\"", drop->name, reason );
|
||||
|
||||
|
||||
if (drop->download) {
|
||||
FS_FCloseFile( drop->download );
|
||||
drop->download = 0;
|
||||
}
|
||||
|
||||
// call the prog function for removing a client
|
||||
// this will remove the body, among other things
|
||||
ge->ClientDisconnect( ( gentity_t * )SV_GentityNum( drop - svs.clients ) );
|
||||
|
@ -621,11 +652,18 @@ void SV_DropClient( client_t *drop, const char *reason ) {
|
|||
// add the disconnect command
|
||||
SV_SendServerCommand( drop, "disconnect \"%s\"", reason);
|
||||
|
||||
// nuke user info
|
||||
SV_SetUserinfo( drop - svs.clients, "" );
|
||||
if ( isBot ) {
|
||||
//SV_BotFreeClient( drop - svs.clients );
|
||||
|
||||
// bots shouldn't go zombie, as there's no real net connection.
|
||||
drop->state = CS_FREE;
|
||||
} else {
|
||||
Com_DPrintf( "Going to CS_ZOMBIE for %s\n", drop->name );
|
||||
drop->state = CS_ZOMBIE; // become free in a few seconds
|
||||
}
|
||||
|
||||
// nuke user info
|
||||
SV_SetUserinfo( drop - svs.clients, "" );
|
||||
|
||||
// if this was the last client on the server, send a heartbeat
|
||||
// to the master so it is known the server is empty
|
||||
|
@ -652,7 +690,7 @@ It will be resent if the client acknowledges a later message but has
|
|||
the wrong gamestate.
|
||||
================
|
||||
*/
|
||||
void SV_SendClientGameState( client_t *client ) {
|
||||
static void SV_SendClientGameState( client_t *client ) {
|
||||
int start;
|
||||
entityState_t *base, nullstate;
|
||||
msg_t msg;
|
||||
|
@ -719,7 +757,7 @@ void SV_SendClientGameState( client_t *client ) {
|
|||
MSG_WriteLong( &msg, client - svs.clients);
|
||||
|
||||
// write the checksum feed
|
||||
MSG_WriteLong( &msg, 0);
|
||||
MSG_WriteLong( &msg, sv.checksumFeed);
|
||||
|
||||
// write the server frametime to the client (only on TA/TT)
|
||||
MSG_WriteServerFrameTime(&msg, sv.frameTime);
|
||||
|
@ -754,7 +792,7 @@ void SV_ClientEnterWorld( client_t *client, usercmd_t *cmd ) {
|
|||
client->deltaMessage = -1;
|
||||
client->lastSnapshotTime = 0; // generate a snapshot immediately
|
||||
|
||||
if (cmd)
|
||||
if(cmd)
|
||||
memcpy(&client->lastUsercmd, cmd, sizeof(client->lastUsercmd));
|
||||
else
|
||||
memset(&client->lastUsercmd, '\0', sizeof(client->lastUsercmd));
|
||||
|
@ -798,7 +836,7 @@ static void SV_CloseDownload( client_t *cl ) {
|
|||
// Free the temporary buffer space
|
||||
for (i = 0; i < MAX_DOWNLOAD_WINDOW; i++) {
|
||||
if (cl->downloadBlocks[i]) {
|
||||
Z_Free( cl->downloadBlocks[i] );
|
||||
Z_Free(cl->downloadBlocks[i]);
|
||||
cl->downloadBlocks[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -812,7 +850,7 @@ SV_StopDownload_f
|
|||
Abort a download if in progress
|
||||
==================
|
||||
*/
|
||||
void SV_StopDownload_f( client_t *cl ) {
|
||||
static void SV_StopDownload_f( client_t *cl ) {
|
||||
if (*cl->downloadName)
|
||||
Com_DPrintf( "clientDownload: %d : file \"%s\" aborted\n", (int) (cl - svs.clients), cl->downloadName );
|
||||
|
||||
|
@ -826,7 +864,10 @@ SV_DoneDownload_f
|
|||
Downloads are finished
|
||||
==================
|
||||
*/
|
||||
void SV_DoneDownload_f( client_t *cl ) {
|
||||
static void SV_DoneDownload_f( client_t *cl ) {
|
||||
if ( cl->state == CS_ACTIVE )
|
||||
return;
|
||||
|
||||
Com_DPrintf( "clientDownload: %s Done\n", cl->name);
|
||||
// resend the game state to update any clients that entered during the download
|
||||
SV_SendClientGameState(cl);
|
||||
|
@ -840,7 +881,7 @@ The argument will be the last acknowledged block from the client, it should be
|
|||
the same as cl->downloadClientBlock
|
||||
==================
|
||||
*/
|
||||
void SV_NextDownload_f( client_t *cl )
|
||||
static void SV_NextDownload_f( client_t *cl )
|
||||
{
|
||||
int block = atoi( Cmd_Argv(1) );
|
||||
|
||||
|
@ -869,7 +910,7 @@ void SV_NextDownload_f( client_t *cl )
|
|||
SV_BeginDownload_f
|
||||
==================
|
||||
*/
|
||||
void SV_BeginDownload_f( client_t *cl ) {
|
||||
static void SV_BeginDownload_f( client_t *cl ) {
|
||||
|
||||
// Kill any existing download
|
||||
SV_CloseDownload( cl );
|
||||
|
@ -884,15 +925,13 @@ void SV_BeginDownload_f( client_t *cl ) {
|
|||
SV_WriteDownloadToClient
|
||||
|
||||
Check to see if the client wants a file, open it if needed and start pumping the client
|
||||
Fill up msg with data
|
||||
Fill up msg with data, return number of download blocks added
|
||||
==================
|
||||
*/
|
||||
int SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
|
||||
int SV_WriteDownloadToClient(client_t *cl, msg_t *msg)
|
||||
{
|
||||
int curindex;
|
||||
int rate;
|
||||
int blockspersnap;
|
||||
int idPack = 0, missionPack = 0, unreferenced = 1;
|
||||
int unreferenced = 1;
|
||||
char errorMessage[1024];
|
||||
char pakbuf[MAX_QPATH], *pakptr;
|
||||
int numRefPaks;
|
||||
|
@ -900,10 +939,16 @@ int SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
|
|||
if (!*cl->downloadName)
|
||||
return 0; // Nothing being downloaded
|
||||
|
||||
if (!cl->download) {
|
||||
if(!cl->download)
|
||||
{
|
||||
qboolean idPack = qfalse;
|
||||
#ifndef STANDALONE
|
||||
qboolean missionPack = qfalse;
|
||||
#endif
|
||||
|
||||
// Chop off filename extension.
|
||||
Com_sprintf(pakbuf, sizeof(pakbuf), "%s", cl->downloadName);
|
||||
pakptr = Q_strrchr(pakbuf, '.');
|
||||
pakptr = strrchr(pakbuf, '.');
|
||||
|
||||
if(pakptr)
|
||||
{
|
||||
|
@ -927,8 +972,11 @@ int SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
|
|||
|
||||
// now that we know the file is referenced,
|
||||
// check whether it's legal to download it.
|
||||
missionPack = FS_idPak(pakbuf, "missionpack");
|
||||
idPack = missionPack || FS_idPak(pakbuf, BASEGAME);
|
||||
#ifndef STANDALONE
|
||||
//missionPack = FS_idPak(pakbuf, BASETA, NUM_TA_PAKS);
|
||||
//idPack = missionPack;
|
||||
#endif
|
||||
//idPack = idPack || FS_idPak(pakbuf, BASEGAME, NUM_ID_PAKS);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -936,11 +984,13 @@ int SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
|
|||
}
|
||||
}
|
||||
|
||||
cl->download = 0;
|
||||
|
||||
// We open the file here
|
||||
if ( !(sv_allowDownload->integer & DLF_ENABLE) ||
|
||||
(sv_allowDownload->integer & DLF_NO_UDP) ||
|
||||
idPack || unreferenced ||
|
||||
( cl->downloadSize = FS_SV_FOpenFileRead( cl->downloadName, &cl->download ) ) <= 0 ) {
|
||||
( cl->downloadSize = FS_SV_FOpenFileRead( cl->downloadName, &cl->download ) ) < 0 ) {
|
||||
// cannot auto-download file
|
||||
if(unreferenced)
|
||||
{
|
||||
|
@ -949,18 +999,22 @@ int SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
|
|||
}
|
||||
else if (idPack) {
|
||||
Com_Printf("clientDownload: %d : \"%s\" cannot download id pk3 files\n", (int) (cl - svs.clients), cl->downloadName);
|
||||
if (missionPack) {
|
||||
#ifndef STANDALONE
|
||||
if(missionPack)
|
||||
{
|
||||
Com_sprintf(errorMessage, sizeof(errorMessage), "Cannot autodownload Team Arena file \"%s\"\n"
|
||||
"The Team Arena mission pack can be found in your local game store.", cl->downloadName);
|
||||
}
|
||||
else {
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Com_sprintf(errorMessage, sizeof(errorMessage), "Cannot autodownload id pk3 file \"%s\"", cl->downloadName);
|
||||
}
|
||||
}
|
||||
else if ( !(sv_allowDownload->integer & DLF_ENABLE) ||
|
||||
(sv_allowDownload->integer & DLF_NO_UDP) ) {
|
||||
|
||||
Com_Printf("clientDownload: %d : \"%s\" download disabled", (int) (cl - svs.clients), cl->downloadName);
|
||||
Com_Printf("clientDownload: %d : \"%s\" download disabled\n", (int) (cl - svs.clients), cl->downloadName);
|
||||
if (sv_pure->integer) {
|
||||
Com_sprintf(errorMessage, sizeof(errorMessage), "Could not download \"%s\" because autodownloading is disabled on the server.\n\n"
|
||||
"You will need to get this file elsewhere before you "
|
||||
|
@ -983,6 +1037,10 @@ int SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
|
|||
MSG_WriteString( msg, errorMessage );
|
||||
|
||||
*cl->downloadName = 0;
|
||||
|
||||
if(cl->download)
|
||||
FS_FCloseFile(cl->download);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1001,7 +1059,7 @@ int SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
|
|||
curindex = (cl->downloadCurrentBlock % MAX_DOWNLOAD_WINDOW);
|
||||
|
||||
if (!cl->downloadBlocks[curindex])
|
||||
cl->downloadBlocks[curindex] = Z_Malloc( MAX_DOWNLOAD_BLKSIZE );
|
||||
cl->downloadBlocks[curindex] = Z_Malloc(MAX_DOWNLOAD_BLKSIZE);
|
||||
|
||||
cl->downloadBlockSize[curindex] = FS_Read( cl->downloadBlocks[curindex], MAX_DOWNLOAD_BLKSIZE, cl->download );
|
||||
|
||||
|
@ -1028,44 +1086,14 @@ int SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
|
|||
cl->downloadEOF = qtrue; // We have added the EOF block
|
||||
}
|
||||
|
||||
// Loop up to window size times based on how many blocks we can fit in the
|
||||
// client snapMsec and rate
|
||||
|
||||
// based on the rate, how many bytes can we fit in the snapMsec time of the client
|
||||
// normal rate / snapshotMsec calculation
|
||||
rate = cl->rate;
|
||||
if ( sv_maxRate->integer ) {
|
||||
if ( sv_maxRate->integer < 1000 ) {
|
||||
Cvar_Set( "sv_MaxRate", "1000" );
|
||||
}
|
||||
if ( sv_maxRate->integer < rate ) {
|
||||
rate = sv_maxRate->integer;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rate) {
|
||||
blockspersnap = 1;
|
||||
} else {
|
||||
blockspersnap = ( (rate * cl->snapshotMsec) / 1000 + MAX_DOWNLOAD_BLKSIZE ) /
|
||||
MAX_DOWNLOAD_BLKSIZE;
|
||||
}
|
||||
|
||||
if (blockspersnap < 0)
|
||||
blockspersnap = 1;
|
||||
|
||||
while (blockspersnap--) {
|
||||
|
||||
// Write out the next section of the file, if we have already reached our window,
|
||||
// automatically start retransmitting
|
||||
|
||||
if (cl->downloadClientBlock == cl->downloadCurrentBlock)
|
||||
return 0; // Nothing to transmit
|
||||
|
||||
if (cl->downloadXmitBlock == cl->downloadCurrentBlock) {
|
||||
// Write out the next section of the file, if we have already reached our window,
|
||||
// automatically start retransmitting
|
||||
if (cl->downloadXmitBlock == cl->downloadCurrentBlock)
|
||||
{
|
||||
// We have transmitted the complete window, should we start resending?
|
||||
|
||||
//FIXME: This uses a hardcoded one second timeout for lost blocks
|
||||
//the timeout should be based on client rate somehow
|
||||
if (svs.time - cl->downloadSendTime > 1000)
|
||||
cl->downloadXmitBlock = cl->downloadClientBlock;
|
||||
else
|
||||
|
@ -1080,23 +1108,20 @@ int SV_WriteDownloadToClient( client_t *cl , msg_t *msg )
|
|||
|
||||
// block zero is special, contains file size
|
||||
if ( cl->downloadXmitBlock == 0 )
|
||||
MSG_WriteLong( msg, (int)cl->downloadSize );
|
||||
MSG_WriteLong( msg, cl->downloadSize );
|
||||
|
||||
MSG_WriteShort( msg, ( short )cl->downloadBlockSize[curindex] );
|
||||
MSG_WriteShort( msg, cl->downloadBlockSize[curindex] );
|
||||
|
||||
// Write the block
|
||||
if ( cl->downloadBlockSize[curindex] ) {
|
||||
MSG_WriteData( msg, cl->downloadBlocks[curindex], cl->downloadBlockSize[curindex] );
|
||||
}
|
||||
if(cl->downloadBlockSize[curindex])
|
||||
MSG_WriteData(msg, cl->downloadBlocks[curindex], cl->downloadBlockSize[curindex]);
|
||||
|
||||
Com_DPrintf( "clientDownload: %d : writing block %d\n", (int) (cl - svs.clients), cl->downloadXmitBlock );
|
||||
|
||||
// Move on to the next block
|
||||
// It will get sent with next snap shot. The rate will keep us in line.
|
||||
cl->downloadXmitBlock++;
|
||||
|
||||
cl->downloadSendTime = svs.time;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -1375,7 +1400,7 @@ void SV_UserinfoChanged( client_t *cl ) {
|
|||
char *val;
|
||||
char *ip;
|
||||
int i;
|
||||
size_t len;
|
||||
int len;
|
||||
|
||||
// name for C code
|
||||
Q_strncpyz( cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name) );
|
||||
|
@ -1472,17 +1497,49 @@ SV_UpdateUserinfo_f
|
|||
==================
|
||||
*/
|
||||
static void SV_UpdateUserinfo_f( client_t *cl ) {
|
||||
Q_strncpyz( cl->userinfo, Cmd_Argv( 1 ), sizeof( cl->userinfo ) );
|
||||
Q_strncpyz( cl->userinfo, Cmd_Argv(1), sizeof(cl->userinfo) );
|
||||
|
||||
SV_UserinfoChanged( cl );
|
||||
|
||||
// call prog code to allow overrides
|
||||
ge->ClientUserinfoChanged( ( gentity_t * )SV_GentityNum( cl - svs.clients ), cl->userinfo );
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_VOIP
|
||||
static
|
||||
void SV_UpdateVoipIgnore(client_t *cl, const char *idstr, qboolean ignore)
|
||||
{
|
||||
if ((*idstr >= '0') && (*idstr <= '9')) {
|
||||
const int id = atoi(idstr);
|
||||
if ((id >= 0) && (id < MAX_CLIENTS)) {
|
||||
cl->ignoreVoipFromClient[id] = ignore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SV_Voip_f
|
||||
==================
|
||||
*/
|
||||
static void SV_Voip_f( client_t *cl ) {
|
||||
const char *cmd = Cmd_Argv(1);
|
||||
if (strcmp(cmd, "ignore") == 0) {
|
||||
SV_UpdateVoipIgnore(cl, Cmd_Argv(2), qtrue);
|
||||
} else if (strcmp(cmd, "unignore") == 0) {
|
||||
SV_UpdateVoipIgnore(cl, Cmd_Argv(2), qfalse);
|
||||
} else if (strcmp(cmd, "muteall") == 0) {
|
||||
cl->muteAllVoip = qtrue;
|
||||
} else if (strcmp(cmd, "unmuteall") == 0) {
|
||||
cl->muteAllVoip = qfalse;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
void ( *func )( client_t *cl );
|
||||
void (*func)( client_t *cl );
|
||||
} ucmd_t;
|
||||
|
||||
static ucmd_t ucmds[] = {
|
||||
|
@ -1495,6 +1552,10 @@ static ucmd_t ucmds[] = {
|
|||
{"stopdl", SV_StopDownload_f},
|
||||
{"donedl", SV_DoneDownload_f},
|
||||
|
||||
#ifdef USE_VOIP
|
||||
{"voip", SV_Voip_f},
|
||||
#endif
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -1533,7 +1594,8 @@ void SV_ExecuteClientCommand( client_t *cl, const char *s, qboolean clientOK ) {
|
|||
|
||||
if (clientOK) {
|
||||
// pass unknown strings to the game
|
||||
if (!u->name && sv.state == SS_GAME) {
|
||||
if (!u->name && sv.state == SS_GAME && (cl->state == CS_ACTIVE || cl->state == CS_PRIMED)) {
|
||||
Cmd_Args_Sanitize();
|
||||
ge->ClientCommand( ( gentity_t * )SV_GentityNum( cl - svs.clients ) );
|
||||
|
||||
if (ge->errorMessage)
|
||||
|
@ -1566,6 +1628,7 @@ static qboolean SV_ClientCommand( client_t *cl, msg_t *msg ) {
|
|||
}
|
||||
|
||||
if( developer->integer == 2 ) {
|
||||
// don't spam the console with client commands in production
|
||||
Com_DPrintf( "clientCommand: %s : %i : %s\n", cl->name, seq, s );
|
||||
}
|
||||
|
||||
|
@ -1582,9 +1645,10 @@ static qboolean SV_ClientCommand( client_t *cl, msg_t *msg ) {
|
|||
// the command, we will stop processing the rest of the packet,
|
||||
// including the usercmd. This causes flooders to lag themselves
|
||||
// but not other people
|
||||
// We don't do this when the client hasn't been active yet since its
|
||||
// We don't do this when the client hasn't been active yet since it's
|
||||
// normal to spam a lot of commands when downloading
|
||||
if ( cl->state >= CS_ACTIVE &&
|
||||
if ( !com_cl_running->integer &&
|
||||
cl->state >= CS_ACTIVE &&
|
||||
sv_floodProtect->integer &&
|
||||
svs.time < cl->nextReliableTime ) {
|
||||
// ignore any other text messages from this client but let them keep playing
|
||||
|
@ -1609,8 +1673,7 @@ static qboolean SV_ClientCommand( client_t *cl, msg_t *msg ) {
|
|||
// Actual execution of the command
|
||||
SV_ExecuteClientCommand( cl, s, clientOk );
|
||||
|
||||
// continue procesing
|
||||
return qtrue;
|
||||
return qtrue; // continue procesing
|
||||
}
|
||||
|
||||
|
||||
|
@ -1624,8 +1687,7 @@ SV_ClientThink
|
|||
Also called by bot code
|
||||
==================
|
||||
*/
|
||||
void SV_ClientThink( client_t *cl, usercmd_t *cmd )
|
||||
{
|
||||
void SV_ClientThink (client_t *cl, usercmd_t *cmd) {
|
||||
const char *err;
|
||||
|
||||
cl->lastUsercmd = *cmd;
|
||||
|
@ -1686,7 +1748,7 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) {
|
|||
// also use the message acknowledge
|
||||
key ^= cl->messageAcknowledge;
|
||||
// also use the last acknowledged server command in the key
|
||||
key ^= Com_HashKey(cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32);
|
||||
key ^= MSG_HashKey(cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32);
|
||||
|
||||
MSG_ReadDeltaEyeInfo( msg, &cl->lastEyeinfo, &cl->lastEyeinfo );
|
||||
|
||||
|
@ -1723,7 +1785,7 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) {
|
|||
}
|
||||
|
||||
// a bad cp command was sent, drop the client
|
||||
if (g_gametype->integer != GT_SINGLE_PLAYER && sv_pure->integer != 0 && !com_sv_running->integer && cl->pureAuthentic == 0) {
|
||||
if (sv_pure->integer != 0 && cl->pureAuthentic == 0) {
|
||||
SV_DropClient( cl, "Cannot validate pure client!");
|
||||
return;
|
||||
}
|
||||
|
@ -1750,12 +1812,122 @@ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) {
|
|||
if ( cmds[i].serverTime <= cl->lastUsercmd.serverTime ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SV_ClientThink( cl, &cmds[ i ] );
|
||||
SV_ClientThink (cl, &cmds[ i ]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_VOIP
|
||||
/*
|
||||
==================
|
||||
SV_ShouldIgnoreVoipSender
|
||||
|
||||
Blocking of voip packets based on source client
|
||||
==================
|
||||
*/
|
||||
|
||||
static qboolean SV_ShouldIgnoreVoipSender(const client_t *cl)
|
||||
{
|
||||
if (!sv_voip->integer)
|
||||
return qtrue; // VoIP disabled on this server.
|
||||
else if (!cl->hasVoip) // client doesn't have VoIP support?!
|
||||
return qtrue;
|
||||
|
||||
// !!! FIXME: implement player blacklist.
|
||||
|
||||
return qfalse; // don't ignore.
|
||||
}
|
||||
|
||||
static
|
||||
void SV_UserVoip(client_t *cl, msg_t *msg, qboolean ignoreData)
|
||||
{
|
||||
int sender, generation, sequence, frames, packetsize;
|
||||
uint8_t recips[(MAX_CLIENTS + 7) / 8];
|
||||
int flags;
|
||||
byte encoded[sizeof(cl->voipPacket[0]->data)];
|
||||
client_t *client = NULL;
|
||||
voipServerPacket_t *packet = NULL;
|
||||
int i;
|
||||
|
||||
sender = cl - svs.clients;
|
||||
generation = MSG_ReadByte(msg);
|
||||
sequence = MSG_ReadLong(msg);
|
||||
frames = MSG_ReadByte(msg);
|
||||
MSG_ReadData(msg, recips, sizeof(recips));
|
||||
flags = MSG_ReadByte(msg);
|
||||
packetsize = MSG_ReadShort(msg);
|
||||
|
||||
if (msg->readcount > msg->cursize)
|
||||
return; // short/invalid packet, bail.
|
||||
|
||||
if (packetsize > sizeof (encoded)) { // overlarge packet?
|
||||
int bytesleft = packetsize;
|
||||
while (bytesleft) {
|
||||
int br = bytesleft;
|
||||
if (br > sizeof (encoded))
|
||||
br = sizeof (encoded);
|
||||
MSG_ReadData(msg, encoded, br);
|
||||
bytesleft -= br;
|
||||
}
|
||||
return; // overlarge packet, bail.
|
||||
}
|
||||
|
||||
MSG_ReadData(msg, encoded, packetsize);
|
||||
|
||||
if (ignoreData || SV_ShouldIgnoreVoipSender(cl))
|
||||
return; // Blacklisted, disabled, etc.
|
||||
|
||||
// !!! FIXME: see if we read past end of msg...
|
||||
|
||||
// !!! FIXME: reject if not opus data.
|
||||
// !!! FIXME: decide if this is bogus data?
|
||||
|
||||
// decide who needs this VoIP packet sent to them...
|
||||
for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) {
|
||||
if (client->state != CS_ACTIVE)
|
||||
continue; // not in the game yet, don't send to this guy.
|
||||
else if (i == sender)
|
||||
continue; // don't send voice packet back to original author.
|
||||
else if (!client->hasVoip)
|
||||
continue; // no VoIP support, or unsupported protocol
|
||||
else if (client->muteAllVoip)
|
||||
continue; // client is ignoring everyone.
|
||||
else if (client->ignoreVoipFromClient[sender])
|
||||
continue; // client is ignoring this talker.
|
||||
else if (*cl->downloadName) // !!! FIXME: possible to DoS?
|
||||
continue; // no VoIP allowed if downloading, to save bandwidth.
|
||||
|
||||
if(Com_IsVoipTarget(recips, sizeof(recips), i))
|
||||
flags |= VOIP_DIRECT;
|
||||
else
|
||||
flags &= ~VOIP_DIRECT;
|
||||
|
||||
if (!(flags & (VOIP_SPATIAL | VOIP_DIRECT)))
|
||||
continue; // not addressed to this player.
|
||||
|
||||
// Transmit this packet to the client.
|
||||
if (client->queuedVoipPackets >= ARRAY_LEN(client->voipPacket)) {
|
||||
Com_Printf("Too many VoIP packets queued for client #%d\n", i);
|
||||
continue; // no room for another packet right now.
|
||||
}
|
||||
|
||||
packet = Z_Malloc(sizeof(*packet));
|
||||
packet->sender = sender;
|
||||
packet->frames = frames;
|
||||
packet->len = packetsize;
|
||||
packet->generation = generation;
|
||||
packet->sequence = sequence;
|
||||
packet->flags = flags;
|
||||
memcpy(packet->data, encoded, packetsize);
|
||||
|
||||
client->voipPacket[(client->queuedVoipIndex + client->queuedVoipPackets) % ARRAY_LEN(client->voipPacket)] = packet;
|
||||
client->queuedVoipPackets++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===========================================================================
|
||||
|
||||
|
@ -1777,7 +1949,7 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
|
|||
|
||||
MSG_Bitstream(msg);
|
||||
|
||||
serverId = MSG_ReadLong(msg);
|
||||
serverId = MSG_ReadLong( msg );
|
||||
cl->serverIdAcknowledge = serverId;
|
||||
cl->messageAcknowledge = MSG_ReadLong( msg );
|
||||
|
||||
|
@ -1795,7 +1967,7 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
|
|||
// NOTE: when the client message is fux0red the acknowledgement numbers
|
||||
// can be out of range, this could cause the server to send thousands of server
|
||||
// commands which the server thinks are not yet acknowledged in SV_UpdateServerCommandsToClient
|
||||
if (cl->reliableAcknowledge < cl->reliableSequence - MAX_RELIABLE_COMMANDS) {
|
||||
if ((cl->reliableSequence - cl->reliableAcknowledge >= MAX_RELIABLE_COMMANDS) || (cl->reliableSequence - cl->reliableAcknowledge < 0)) {
|
||||
// usually only hackers create messages like this
|
||||
// it is more annoying for them to let them hanging
|
||||
#ifndef NDEBUG
|
||||
|
@ -1816,24 +1988,36 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
|
|||
// don't drop as long as previous command was a nextdl, after a dl is done, downloadName is set back to ""
|
||||
// but we still need to read the next message to move to next download or send gamestate
|
||||
// I don't like this hack though, it must have been working fine at some point, suspecting the fix is somewhere else
|
||||
if ( serverId != sv.serverId && !*cl->downloadName ) {
|
||||
if ( serverId != sv.serverId && !*cl->downloadName && !strstr(cl->lastClientCommandString, "nextdl") ) {
|
||||
if ( serverId >= sv.restartedServerId && serverId < sv.serverId ) { // TTimo - use a comparison here to catch multiple map_restart
|
||||
// they just haven't caught the map_restart yet
|
||||
//Com_DPrintf("%s : ignoring pre map_restart / outdated client message\n", cl->name);
|
||||
return;
|
||||
}
|
||||
// if we can tell that the client has dropped the last
|
||||
// gamestate we sent them, resend it
|
||||
// also, don't resend the gamestate if the server just restarted
|
||||
if ( serverId != sv.restartedServerId && cl->messageAcknowledge > cl->gamestateMessageNum ) {
|
||||
if ( cl->state != CS_ACTIVE && cl->messageAcknowledge > cl->gamestateMessageNum ) {
|
||||
Com_DPrintf( "%s : dropped gamestate, resending\n", cl->name );
|
||||
SV_SendClientGameState( cl );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// this client has acknowledged the new gamestate so it's
|
||||
// safe to start sending it the real time again
|
||||
if( cl->oldServerTime && serverId == sv.serverId ){
|
||||
Com_DPrintf( "%s acknowledged gamestate\n", cl->name );
|
||||
cl->oldServerTime = 0;
|
||||
}
|
||||
|
||||
// read optional clientCommand strings
|
||||
do {
|
||||
c = MSG_ReadByte( msg );
|
||||
//Com_DPrintf( "SV_ExecuteClientMessage: %i\n", c );
|
||||
|
||||
if ( c == clc_EOF ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( c != clc_clientCommand ) {
|
||||
break;
|
||||
}
|
||||
|
@ -1845,6 +2029,22 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) {
|
|||
}
|
||||
} while ( 1 );
|
||||
|
||||
// skip legacy speex voip data
|
||||
if ( c == clc_voipSpeex ) {
|
||||
#ifdef USE_VOIP
|
||||
SV_UserVoip( cl, msg, qtrue );
|
||||
c = MSG_ReadByte( msg );
|
||||
#endif
|
||||
}
|
||||
|
||||
// read optional voip data
|
||||
if ( c == clc_voipOpus ) {
|
||||
#ifdef USE_VOIP
|
||||
SV_UserVoip( cl, msg, qfalse );
|
||||
c = MSG_ReadByte( msg );
|
||||
#endif
|
||||
}
|
||||
|
||||
// read the usercmd_t
|
||||
if ( c == clc_move ) {
|
||||
SV_UserMove( cl, msg, qtrue );
|
||||
|
|
|
@ -74,11 +74,10 @@ SV_SetConfigstring
|
|||
*/
|
||||
void SV_SetConfigstring (int index, const char *val) {
|
||||
int i;
|
||||
size_t len;
|
||||
client_t *client;
|
||||
|
||||
if ( index < 0 || index >= MAX_CONFIGSTRINGS ) {
|
||||
Com_Error( ERR_DROP, "SV_SetConfigstring: bad index %i\n", index );
|
||||
Com_Error (ERR_DROP, "SV_SetConfigstring: bad index %i\n", index);
|
||||
}
|
||||
|
||||
if ( !val ) {
|
||||
|
@ -98,7 +97,7 @@ void SV_SetConfigstring (int index, const char *val) {
|
|||
// spawning a new server
|
||||
if (sv.state == SS_LOADING2 || sv.state == SS_GAME || sv.restarting ) {
|
||||
|
||||
// send the data to all relevent clients
|
||||
// send the data to all relevant clients
|
||||
for (i = 0, client = svs.clients; i < svs.iNumClients ; i++, client++) {
|
||||
if ( client->state < CS_ACTIVE ) {
|
||||
if ( client->state == CS_PRIMED )
|
||||
|
@ -111,7 +110,6 @@ void SV_SetConfigstring (int index, const char *val) {
|
|||
}
|
||||
|
||||
|
||||
len = strlen( val );
|
||||
SV_SendConfigstring(client, index);
|
||||
}
|
||||
}
|
||||
|
@ -316,7 +314,7 @@ void SV_UpdateConfigstrings(client_t *client)
|
|||
{
|
||||
int index;
|
||||
|
||||
for( index = 0; index <= MAX_CONFIGSTRINGS; index++ ) {
|
||||
for( index = 0; index < MAX_CONFIGSTRINGS; index++ ) {
|
||||
// if the CS hasn't changed since we went to CS_PRIMED, ignore
|
||||
if(!client->csUpdated[index])
|
||||
continue;
|
||||
|
@ -452,6 +450,9 @@ void SV_Startup( void ) {
|
|||
|
||||
SV_InitGamespy();
|
||||
Cvar_Set( "sv_running", "1" );
|
||||
|
||||
// Join the ipv6 multicast group now that a map is running so clients can scan for us on the local network.
|
||||
NET_JoinMulticast6();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -538,6 +539,20 @@ void SV_ClearServer(void) {
|
|||
sv.frameTime = 1.0 / sv_fps->value;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SV_TouchFile
|
||||
================
|
||||
*/
|
||||
static void SV_TouchFile( const char *filename ) {
|
||||
fileHandle_t f;
|
||||
|
||||
FS_FOpenFileRead( filename, &f, qfalse, qtrue );
|
||||
if ( f ) {
|
||||
FS_FCloseFile( f );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SV_SpawnServer
|
||||
|
@ -980,15 +995,16 @@ SV_Init
|
|||
Only called at main exe startup, not for each game
|
||||
===============
|
||||
*/
|
||||
void SV_Init( void ) {
|
||||
void SV_Init (void)
|
||||
{
|
||||
int index;
|
||||
|
||||
SV_AddOperatorCommands();
|
||||
|
||||
// serverinfo vars
|
||||
Cvar_Get( "dmflags", "0", CVAR_SERVERINFO );
|
||||
Cvar_Get( "fraglimit", "20", CVAR_SERVERINFO );
|
||||
Cvar_Get( "timelimit", "0", CVAR_SERVERINFO );
|
||||
Cvar_Get ("dmflags", "0", CVAR_SERVERINFO);
|
||||
Cvar_Get ("fraglimit", "20", CVAR_SERVERINFO);
|
||||
Cvar_Get ("timelimit", "0", CVAR_SERVERINFO);
|
||||
g_gametype = Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_LATCH );
|
||||
g_gametypestring = Cvar_Get( "g_gametypestring", "0", CVAR_SERVERINFO | CVAR_LATCH );
|
||||
Cvar_Get( "sv_keywords", "", CVAR_SERVERINFO );
|
||||
|
@ -1013,12 +1029,11 @@ void SV_Init( void ) {
|
|||
// sv_pure is disabled by default in mohaa
|
||||
// the problem is because of difference in pak files between languages
|
||||
sv_pure = Cvar_Get ("sv_pure", "0", CVAR_SYSTEMINFO );
|
||||
#ifdef USE_VOIP
|
||||
#ifdef USE_VOIP
|
||||
sv_voip = Cvar_Get("sv_voip", "1", CVAR_LATCH);
|
||||
Cvar_CheckRange(sv_voip, 0, 1, qtrue);
|
||||
sv_voipProtocol = Cvar_Get("sv_voipProtocol", sv_voip->integer ? "opus" : "", CVAR_SYSTEMINFO | CVAR_ROM );
|
||||
#endif
|
||||
|
||||
Cvar_Get ("sv_paks", "", CVAR_SYSTEMINFO | CVAR_ROM );
|
||||
Cvar_Get ("sv_pakNames", "", CVAR_SYSTEMINFO | CVAR_ROM );
|
||||
Cvar_Get ("sv_referencedPaks", "", CVAR_SYSTEMINFO | CVAR_ROM );
|
||||
|
@ -1129,8 +1144,14 @@ void SV_Shutdown( const char *finalmsg ) {
|
|||
SV_ClearServer();
|
||||
|
||||
// free server static data
|
||||
if ( svs.clients ) {
|
||||
Z_Free( svs.clients );
|
||||
if(svs.clients)
|
||||
{
|
||||
int index;
|
||||
|
||||
for(index = 0; index < sv_maxclients->integer; index++)
|
||||
SV_FreeClient(&svs.clients[index]);
|
||||
|
||||
Z_Free(svs.clients);
|
||||
}
|
||||
Com_Memset( &svs, 0, sizeof( svs ) );
|
||||
|
||||
|
|
|
@ -223,7 +223,7 @@ void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) {
|
|||
Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) );
|
||||
}
|
||||
|
||||
// send the data to all relevent clients
|
||||
// send the data to all relevant clients
|
||||
for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) {
|
||||
SV_AddServerCommand( client, (char *)message );
|
||||
}
|
||||
|
@ -290,6 +290,167 @@ CONNECTIONLESS COMMANDS
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
// This is deliberately quite large to make it more of an effort to DoS
|
||||
#define MAX_BUCKETS 16384
|
||||
#define MAX_HASHES 1024
|
||||
|
||||
static leakyBucket_t buckets[ MAX_BUCKETS ];
|
||||
static leakyBucket_t *bucketHashes[ MAX_HASHES ];
|
||||
leakyBucket_t outboundLeakyBucket;
|
||||
|
||||
/*
|
||||
================
|
||||
SVC_HashForAddress
|
||||
================
|
||||
*/
|
||||
static long SVC_HashForAddress( netadr_t address ) {
|
||||
byte *ip = NULL;
|
||||
size_t size = 0;
|
||||
int i;
|
||||
long hash = 0;
|
||||
|
||||
switch ( address.type ) {
|
||||
case NA_IP: ip = address.ip; size = 4; break;
|
||||
case NA_IP6: ip = address.ip6; size = 16; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
for ( i = 0; i < size; i++ ) {
|
||||
hash += (long)( ip[ i ] ) * ( i + 119 );
|
||||
}
|
||||
|
||||
hash = ( hash ^ ( hash >> 10 ) ^ ( hash >> 20 ) );
|
||||
hash &= ( MAX_HASHES - 1 );
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SVC_BucketForAddress
|
||||
|
||||
Find or allocate a bucket for an address
|
||||
================
|
||||
*/
|
||||
static leakyBucket_t *SVC_BucketForAddress( netadr_t address, int burst, int period ) {
|
||||
leakyBucket_t *bucket = NULL;
|
||||
int i;
|
||||
long hash = SVC_HashForAddress( address );
|
||||
int now = Sys_Milliseconds();
|
||||
|
||||
for ( bucket = bucketHashes[ hash ]; bucket; bucket = bucket->next ) {
|
||||
switch ( bucket->type ) {
|
||||
case NA_IP:
|
||||
if ( memcmp( bucket->ipv._4, address.ip, 4 ) == 0 ) {
|
||||
return bucket;
|
||||
}
|
||||
break;
|
||||
|
||||
case NA_IP6:
|
||||
if ( memcmp( bucket->ipv._6, address.ip6, 16 ) == 0 ) {
|
||||
return bucket;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for ( i = 0; i < MAX_BUCKETS; i++ ) {
|
||||
int interval;
|
||||
|
||||
bucket = &buckets[ i ];
|
||||
interval = now - bucket->lastTime;
|
||||
|
||||
// Reclaim expired buckets
|
||||
if ( bucket->lastTime > 0 && ( interval > ( burst * period ) ||
|
||||
interval < 0 ) ) {
|
||||
if ( bucket->prev != NULL ) {
|
||||
bucket->prev->next = bucket->next;
|
||||
} else {
|
||||
bucketHashes[ bucket->hash ] = bucket->next;
|
||||
}
|
||||
|
||||
if ( bucket->next != NULL ) {
|
||||
bucket->next->prev = bucket->prev;
|
||||
}
|
||||
|
||||
Com_Memset( bucket, 0, sizeof( leakyBucket_t ) );
|
||||
}
|
||||
|
||||
if ( bucket->type == NA_BAD ) {
|
||||
bucket->type = address.type;
|
||||
switch ( address.type ) {
|
||||
case NA_IP: Com_Memcpy( bucket->ipv._4, address.ip, 4 ); break;
|
||||
case NA_IP6: Com_Memcpy( bucket->ipv._6, address.ip6, 16 ); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
bucket->lastTime = now;
|
||||
bucket->burst = 0;
|
||||
bucket->hash = hash;
|
||||
|
||||
// Add to the head of the relevant hash chain
|
||||
bucket->next = bucketHashes[ hash ];
|
||||
if ( bucketHashes[ hash ] != NULL ) {
|
||||
bucketHashes[ hash ]->prev = bucket;
|
||||
}
|
||||
|
||||
bucket->prev = NULL;
|
||||
bucketHashes[ hash ] = bucket;
|
||||
|
||||
return bucket;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't allocate a bucket for this address
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SVC_RateLimit
|
||||
================
|
||||
*/
|
||||
qboolean SVC_RateLimit( leakyBucket_t *bucket, int burst, int period ) {
|
||||
if ( bucket != NULL ) {
|
||||
int now = Sys_Milliseconds();
|
||||
int interval = now - bucket->lastTime;
|
||||
int expired = interval / period;
|
||||
int expiredRemainder = interval % period;
|
||||
|
||||
if ( expired > bucket->burst || interval < 0 ) {
|
||||
bucket->burst = 0;
|
||||
bucket->lastTime = now;
|
||||
} else {
|
||||
bucket->burst -= expired;
|
||||
bucket->lastTime = now - expiredRemainder;
|
||||
}
|
||||
|
||||
if ( bucket->burst < burst ) {
|
||||
bucket->burst++;
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SVC_RateLimitAddress
|
||||
|
||||
Rate limit for a particular address
|
||||
================
|
||||
*/
|
||||
qboolean SVC_RateLimitAddress( netadr_t from, int burst, int period ) {
|
||||
leakyBucket_t *bucket = SVC_BucketForAddress( from, burst, period );
|
||||
|
||||
return SVC_RateLimit( bucket, burst, period );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
SVC_Status
|
||||
|
@ -314,6 +475,24 @@ void SVC_Status( netadr_t from ) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Prevent using getstatus as an amplifier
|
||||
if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
|
||||
Com_DPrintf( "SVC_Status: rate limit from %s exceeded, dropping request\n",
|
||||
NET_AdrToString( from ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow getstatus to be DoSed relatively easily, but prevent
|
||||
// excess outbound bandwidth usage when being flooded inbound
|
||||
if ( SVC_RateLimit( &outboundLeakyBucket, 10, 100 ) ) {
|
||||
Com_DPrintf( "SVC_Status: rate limit exceeded, dropping request\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// A maximum challenge length of 128 should be more than plenty.
|
||||
if(strlen(Cmd_Argv(1)) > 128)
|
||||
return;
|
||||
|
||||
strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) );
|
||||
|
||||
// echo back the parameter to status. so master servers can use it as a challenge
|
||||
|
@ -361,6 +540,20 @@ void SVC_Info( netadr_t from ) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Prevent using getinfo as an amplifier
|
||||
if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
|
||||
Com_DPrintf( "SVC_Info: rate limit from %s exceeded, dropping request\n",
|
||||
NET_AdrToString( from ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow getinfo to be DoSed relatively easily, but prevent
|
||||
// excess outbound bandwidth usage when being flooded inbound
|
||||
if ( SVC_RateLimit( &outboundLeakyBucket, 10, 100 ) ) {
|
||||
Com_DPrintf( "SVC_Info: rate limit exceeded, dropping request\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether Cmd_Argv(1) has a sane length. This was not done in the original Quake3 version which led
|
||||
* to the Infostring bug discovered by Luigi Auriemma. See http://aluigi.altervista.org/ for the advisory.
|
||||
|
@ -394,6 +587,12 @@ void SVC_Info( netadr_t from ) {
|
|||
Info_SetValueForKey( infostring, "gametypestring", g_gametypestring->string );
|
||||
Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) );
|
||||
|
||||
#ifdef USE_VOIP
|
||||
if (sv_voipProtocol->string && *sv_voipProtocol->string) {
|
||||
Info_SetValueForKey( infostring, "voip", sv_voipProtocol->string );
|
||||
}
|
||||
#endif
|
||||
|
||||
if( sv_minPing->integer ) {
|
||||
Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) );
|
||||
}
|
||||
|
@ -417,7 +616,7 @@ SVC_FlushRedirect
|
|||
|
||||
================
|
||||
*/
|
||||
void SV_FlushRedirect( char *outputbuf ) {
|
||||
static void SV_FlushRedirect( char *outputbuf ) {
|
||||
NET_OutOfBandPrint( NS_SERVER, svs.redirectAddress, "print\n%s", outputbuf );
|
||||
}
|
||||
|
||||
|
@ -430,26 +629,32 @@ Shift down the remaining args
|
|||
Redirect all printfs
|
||||
===============
|
||||
*/
|
||||
void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
|
||||
static void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
|
||||
qboolean valid;
|
||||
unsigned int time;
|
||||
char remaining[1024];
|
||||
// TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc.
|
||||
// (OOB messages are the bottleneck here)
|
||||
#define SV_OUTPUTBUF_LENGTH (1024 - 16)
|
||||
char sv_outputbuf[SV_OUTPUTBUF_LENGTH];
|
||||
static unsigned int lasttime = 0;
|
||||
char *cmd_aux;
|
||||
|
||||
// TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534
|
||||
time = Com_Milliseconds();
|
||||
if ( (unsigned)( time - lasttime ) < 500u ) {
|
||||
// Prevent using rcon as an amplifier and make dictionary attacks impractical
|
||||
if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
|
||||
Com_DPrintf( "SVC_RemoteCommand: rate limit from %s exceeded, dropping request\n",
|
||||
NET_AdrToString( from ) );
|
||||
return;
|
||||
}
|
||||
lasttime = time;
|
||||
|
||||
if ( !strlen( sv_rconPassword->string ) ||
|
||||
strcmp (Cmd_Argv(1), sv_rconPassword->string) ) {
|
||||
static leakyBucket_t bucket;
|
||||
|
||||
// Make DoS via rcon impractical
|
||||
if ( SVC_RateLimit( &bucket, 10, 1000 ) ) {
|
||||
Com_DPrintf( "SVC_RemoteCommand: rate limit exceeded, dropping request\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
valid = qfalse;
|
||||
Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
|
||||
} else {
|
||||
|
@ -524,7 +729,7 @@ void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
|
|||
} else if (!Q_stricmp(c, "getinfo")) {
|
||||
SVC_Info( from );
|
||||
} else if (!Q_stricmp(c, "getchallenge")) {
|
||||
SV_GetChallenge( from );
|
||||
SV_GetChallenge(from);
|
||||
} else if (!Q_stricmp(c, "connect")) {
|
||||
SV_DirectConnect( from );
|
||||
} else if (!Q_stricmp(c, "authorizeThis")) {
|
||||
|
@ -536,8 +741,8 @@ void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
|
|||
// server disconnect messages when their new server sees our final
|
||||
// sequenced messages to the old client
|
||||
} else {
|
||||
Com_DPrintf ("bad connectionless packet from %s:\n%s\n"
|
||||
, NET_AdrToString (from), s);
|
||||
Com_DPrintf ("bad connectionless packet from %s:\n%s\n",
|
||||
NET_AdrToString (from), s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -562,7 +767,7 @@ void SV_PacketEvent( netadr_t from, msg_t *msg ) {
|
|||
// read the qport out of the message so we can fix up
|
||||
// stupid address translating routers
|
||||
MSG_BeginReadingOOB( msg );
|
||||
i=MSG_ReadLong( msg ); // sequence number
|
||||
MSG_ReadLong( msg ); // sequence number
|
||||
qport = MSG_ReadShort( msg ) & 0xffff;
|
||||
|
||||
// find which client the message is from
|
||||
|
@ -593,7 +798,7 @@ void SV_PacketEvent( netadr_t from, msg_t *msg ) {
|
|||
// to make sure they don't need to retransmit the final
|
||||
// reliable message, but they don't do any other processing
|
||||
if (cl->state != CS_ZOMBIE) {
|
||||
cl->lastPacketTime = svs.lastTime; // don't timeout
|
||||
cl->lastPacketTime = svs.time; // don't timeout
|
||||
SV_ExecuteClientMessage( cl, msg );
|
||||
}
|
||||
}
|
||||
|
@ -613,7 +818,7 @@ SV_CalcPings
|
|||
Updates the cl->ping variables
|
||||
===================
|
||||
*/
|
||||
void SV_CalcPings( void ) {
|
||||
static void SV_CalcPings( void ) {
|
||||
int i, j;
|
||||
client_t *cl;
|
||||
int total, count;
|
||||
|
@ -673,7 +878,7 @@ for a few seconds to make sure any final reliable message gets resent
|
|||
if necessary
|
||||
==================
|
||||
*/
|
||||
void SV_CheckTimeouts( void ) {
|
||||
static void SV_CheckTimeouts( void ) {
|
||||
int i;
|
||||
client_t *cl;
|
||||
int droppoint;
|
||||
|
@ -799,6 +1004,11 @@ void SV_Frame( int msec ) {
|
|||
if( !com_sv_running->integer )
|
||||
{
|
||||
// Running as a server, but no map loaded
|
||||
#ifdef DEDICATED
|
||||
// Block until something interesting happens
|
||||
Sys_Sleep(-1);
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1010,7 +1220,7 @@ a client based on its rate settings
|
|||
#define UDPIP_HEADER_SIZE 28
|
||||
#define UDPIP6_HEADER_SIZE 48
|
||||
|
||||
int SV_RateMsec(client_t* client)
|
||||
int SV_RateMsec(client_t *client)
|
||||
{
|
||||
int rate, rateMsec;
|
||||
int messageSize;
|
||||
|
@ -1018,31 +1228,31 @@ int SV_RateMsec(client_t* client)
|
|||
messageSize = client->netchan.lastSentSize;
|
||||
rate = client->rate;
|
||||
|
||||
if (sv_maxRate->integer)
|
||||
if(sv_maxRate->integer)
|
||||
{
|
||||
if (sv_maxRate->integer < 1000)
|
||||
Cvar_Set("sv_MaxRate", "1000");
|
||||
if (sv_maxRate->integer < rate)
|
||||
if(sv_maxRate->integer < 1000)
|
||||
Cvar_Set( "sv_MaxRate", "1000" );
|
||||
if(sv_maxRate->integer < rate)
|
||||
rate = sv_maxRate->integer;
|
||||
}
|
||||
|
||||
if (sv_minRate->integer)
|
||||
if(sv_minRate->integer)
|
||||
{
|
||||
if (sv_minRate->integer < 1000)
|
||||
if(sv_minRate->integer < 1000)
|
||||
Cvar_Set("sv_minRate", "1000");
|
||||
if (sv_minRate->integer > rate)
|
||||
if(sv_minRate->integer > rate)
|
||||
rate = sv_minRate->integer;
|
||||
}
|
||||
|
||||
if (client->netchan.remoteAddress.type == NA_IP6)
|
||||
if(client->netchan.remoteAddress.type == NA_IP6)
|
||||
messageSize += UDPIP6_HEADER_SIZE;
|
||||
else
|
||||
messageSize += UDPIP_HEADER_SIZE;
|
||||
|
||||
rateMsec = messageSize * 1000 / ((int)(rate * com_timescale->value));
|
||||
rateMsec = messageSize * 1000 / ((int) (rate * com_timescale->value));
|
||||
rate = Sys_Milliseconds() - client->netchan.lastSentTime;
|
||||
|
||||
if (rate > rateMsec)
|
||||
if(rate > rateMsec)
|
||||
return 0;
|
||||
else
|
||||
return rateMsec - rate;
|
||||
|
@ -1067,26 +1277,26 @@ int SV_SendQueuedPackets()
|
|||
|
||||
// Send out fragmented packets now that we're idle
|
||||
delayT = SV_SendQueuedMessages();
|
||||
if (delayT >= 0)
|
||||
if(delayT >= 0)
|
||||
timeVal = delayT;
|
||||
|
||||
if (sv_dlRate->integer)
|
||||
if(sv_dlRate->integer)
|
||||
{
|
||||
// Rate limiting. This is very imprecise for high
|
||||
// download rates due to millisecond timedelta resolution
|
||||
dlStart = Sys_Milliseconds();
|
||||
deltaT = dlNextRound - dlStart;
|
||||
|
||||
if (deltaT > 0)
|
||||
if(deltaT > 0)
|
||||
{
|
||||
if (deltaT < timeVal)
|
||||
if(deltaT < timeVal)
|
||||
timeVal = deltaT + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
numBlocks = SV_SendDownloadMessages();
|
||||
|
||||
if (numBlocks)
|
||||
if(numBlocks)
|
||||
{
|
||||
// There are active downloads
|
||||
deltaT = Sys_Milliseconds() - dlStart;
|
||||
|
@ -1094,7 +1304,7 @@ int SV_SendQueuedPackets()
|
|||
delayT = 1000 * numBlocks * MAX_DOWNLOAD_BLKSIZE;
|
||||
delayT /= sv_dlRate->integer * 1024;
|
||||
|
||||
if (delayT <= deltaT + 1)
|
||||
if(delayT <= deltaT + 1)
|
||||
{
|
||||
// Sending the last round of download messages
|
||||
// took too long for given rate, don't wait for
|
||||
|
@ -1103,7 +1313,7 @@ int SV_SendQueuedPackets()
|
|||
// all of the bandwidth. This will result in an
|
||||
// effective maximum rate of 1MB/s per user, but the
|
||||
// low download window size limits this anyways.
|
||||
if (timeVal > 2)
|
||||
if(timeVal > 2)
|
||||
timeVal = 2;
|
||||
|
||||
dlNextRound = dlStart + deltaT + 1;
|
||||
|
@ -1113,7 +1323,7 @@ int SV_SendQueuedPackets()
|
|||
dlNextRound = dlStart + delayT;
|
||||
delayT -= deltaT;
|
||||
|
||||
if (delayT < timeVal)
|
||||
if(delayT < timeVal)
|
||||
timeVal = delayT;
|
||||
}
|
||||
}
|
||||
|
@ -1121,7 +1331,7 @@ int SV_SendQueuedPackets()
|
|||
}
|
||||
else
|
||||
{
|
||||
if (SV_SendDownloadMessages())
|
||||
if(SV_SendDownloadMessages())
|
||||
timeVal = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -259,14 +259,14 @@ void SV_Netchan_Transmit( client_t *client, msg_t *msg)
|
|||
Netchan_SV_Process
|
||||
=================
|
||||
*/
|
||||
qboolean SV_Netchan_Process(client_t* client, msg_t* msg) {
|
||||
qboolean SV_Netchan_Process( client_t *client, msg_t *msg ) {
|
||||
int ret;
|
||||
ret = Netchan_Process(&client->netchan, msg);
|
||||
ret = Netchan_Process( &client->netchan, msg );
|
||||
if (!ret)
|
||||
return qfalse;
|
||||
|
||||
#ifdef LEGACY_PROTOCOL
|
||||
if (client->compat)
|
||||
if(client->compat)
|
||||
SV_Netchan_Decode(client, msg);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ static void SV_EmitPacketEntities( clientSnapshot_t *from, clientSnapshot_t *to,
|
|||
if ( newnum == oldnum ) {
|
||||
// delta update from old position
|
||||
// because the force parm is qfalse, this will not result
|
||||
// in any bytes being emited if the entity has not changed at all
|
||||
// in any bytes being emitted if the entity has not changed at all
|
||||
MSG_WriteDeltaEntity (msg, oldent, newent, qfalse, sv.frameTime);
|
||||
oldindex++;
|
||||
newindex++;
|
||||
|
@ -1049,6 +1049,53 @@ static void SV_BuildClientSnapshot( client_t *client ) {
|
|||
frame->ps.radarInfo = client->radarInfo;
|
||||
}
|
||||
|
||||
#ifdef USE_VOIP
|
||||
/*
|
||||
==================
|
||||
SV_WriteVoipToClient
|
||||
|
||||
Check to see if there is any VoIP queued for a client, and send if there is.
|
||||
==================
|
||||
*/
|
||||
static void SV_WriteVoipToClient(client_t *cl, msg_t *msg)
|
||||
{
|
||||
int totalbytes = 0;
|
||||
int i;
|
||||
voipServerPacket_t *packet;
|
||||
|
||||
if(cl->queuedVoipPackets)
|
||||
{
|
||||
// Write as many VoIP packets as we reasonably can...
|
||||
for(i = 0; i < cl->queuedVoipPackets; i++)
|
||||
{
|
||||
packet = cl->voipPacket[(i + cl->queuedVoipIndex) % ARRAY_LEN(cl->voipPacket)];
|
||||
|
||||
if(!*cl->downloadName)
|
||||
{
|
||||
totalbytes += packet->len;
|
||||
if (totalbytes > (msg->maxsize - msg->cursize) / 2)
|
||||
break;
|
||||
|
||||
MSG_WriteByte(msg, svc_voipOpus);
|
||||
MSG_WriteShort(msg, packet->sender);
|
||||
MSG_WriteByte(msg, (byte) packet->generation);
|
||||
MSG_WriteLong(msg, packet->sequence);
|
||||
MSG_WriteByte(msg, packet->frames);
|
||||
MSG_WriteShort(msg, packet->len);
|
||||
MSG_WriteBits(msg, packet->flags, VOIP_FLAGCNT);
|
||||
MSG_WriteData(msg, packet->data, packet->len);
|
||||
}
|
||||
|
||||
Z_Free(packet);
|
||||
}
|
||||
|
||||
cl->queuedVoipPackets -= i;
|
||||
cl->queuedVoipIndex += i;
|
||||
cl->queuedVoipIndex %= ARRAY_LEN(cl->voipPacket);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
=======================
|
||||
SV_SendMessageToClient
|
||||
|
@ -1056,7 +1103,8 @@ SV_SendMessageToClient
|
|||
Called by SV_SendClientSnapshot and SV_SendClientGameState
|
||||
=======================
|
||||
*/
|
||||
void SV_SendMessageToClient( msg_t *msg, client_t *client ) {
|
||||
void SV_SendMessageToClient(msg_t *msg, client_t *client)
|
||||
{
|
||||
// record information about the message
|
||||
client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSize = msg->cursize;
|
||||
client->frames[client->netchan.outgoingSequence & PACKET_MASK].messageSent = svs.time;
|
||||
|
@ -1088,7 +1136,6 @@ void SV_SendClientSnapshot( client_t *client ) {
|
|||
return;
|
||||
}
|
||||
|
||||
memset(msg_buf, 0, sizeof(msg_buf));
|
||||
MSG_Init (&msg, msg_buf, sizeof(msg_buf));
|
||||
msg.allowoverflow = qtrue;
|
||||
|
||||
|
@ -1115,6 +1162,10 @@ void SV_SendClientSnapshot( client_t *client ) {
|
|||
// Add any download data if the client is downloading
|
||||
SV_WriteDownloadToClient( client, &msg );
|
||||
|
||||
#ifdef USE_VOIP
|
||||
SV_WriteVoipToClient( client, &msg );
|
||||
#endif
|
||||
|
||||
// check for overflow
|
||||
if ( msg.overflowed ) {
|
||||
Com_Printf ("WARNING: msg overflowed for %s\n", client->name);
|
||||
|
@ -1130,16 +1181,19 @@ void SV_SendClientSnapshot( client_t *client ) {
|
|||
SV_SendClientMessages
|
||||
=======================
|
||||
*/
|
||||
void SV_SendClientMessages( void ) {
|
||||
void SV_SendClientMessages(void)
|
||||
{
|
||||
int i;
|
||||
int rate;
|
||||
client_t *c;
|
||||
|
||||
// send a message to each connected client
|
||||
for (i=0, c = svs.clients ; i < sv_maxclients->integer ; i++, c++) {
|
||||
if (!c->state) {
|
||||
for(i=0; i < sv_maxclients->integer; i++)
|
||||
{
|
||||
c = &svs.clients[i];
|
||||
|
||||
if(!c->state)
|
||||
continue; // not connected
|
||||
}
|
||||
|
||||
if(svs.time - c->lastSnapshotTime < c->snapshotMsec * com_timescale->value)
|
||||
continue; // It's not time yet
|
||||
|
|
|
@ -100,7 +100,7 @@ SV_CreateworldSector
|
|||
Builds a uniformly subdivided tree for the given world size
|
||||
===============
|
||||
*/
|
||||
worldSector_t *SV_CreateworldSector( int depth, vec3_t mins, vec3_t maxs ) {
|
||||
static worldSector_t *SV_CreateworldSector( int depth, vec3_t mins, vec3_t maxs ) {
|
||||
worldSector_t *anode;
|
||||
vec3_t size;
|
||||
vec3_t mins1, maxs1, mins2, maxs2;
|
||||
|
@ -388,7 +388,7 @@ SV_AreaEntities_r
|
|||
|
||||
====================
|
||||
*/
|
||||
void SV_AreaEntities_r( worldSector_t *node, areaParms_t *ap ) {
|
||||
static void SV_AreaEntities_r( worldSector_t *node, areaParms_t *ap ) {
|
||||
svEntity_t *check, *next;
|
||||
gentity_t *gcheck;
|
||||
worldSector_t *nodestack[ 8 ];
|
||||
|
@ -499,7 +499,7 @@ void SV_ClipToEntity( trace_t *trace, const vec3_t start, const vec3_t mins, con
|
|||
|
||||
touch = SV_GentityNum( entityNum );
|
||||
|
||||
Com_Memset( trace, 0, sizeof( trace_t ) );
|
||||
Com_Memset(trace, 0, sizeof(trace_t));
|
||||
|
||||
// if it doesn't have any brushes of a type we
|
||||
// are looking for, ignore it
|
||||
|
@ -527,7 +527,7 @@ SV_ClipMoveToEntities
|
|||
|
||||
====================
|
||||
*/
|
||||
void SV_ClipMoveToEntities( moveclip_t *clip ) {
|
||||
static void SV_ClipMoveToEntities( moveclip_t *clip ) {
|
||||
int i, num;
|
||||
int touchlist[MAX_GENTITIES];
|
||||
gentity_t *touch;
|
||||
|
@ -797,7 +797,7 @@ void SV_Trace( trace_t *results, const vec3_t start, const vec3_t mins, const ve
|
|||
moveclip_t clip;
|
||||
int i;
|
||||
|
||||
Com_Memset( &clip, 0, sizeof( moveclip_t ) );
|
||||
Com_Memset ( &clip, 0, sizeof ( moveclip_t ) );
|
||||
|
||||
// clip to world
|
||||
CM_BoxTrace( &clip.trace, start, end, mins, maxs, 0, contentmask, cylinder );
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue