Reworked networking a little, separating out common code for separate server/master processes.
Reworked glsl bones, so they work based upon the shader's version instead of the driver's version (more robust). Fix te_teleport shader. Track angles for antilag. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5372 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
18280be6e9
commit
c6ed692871
56 changed files with 2026 additions and 964 deletions
|
@ -10116,7 +10116,7 @@ static void QCBUILTIN PF_SendPacket(pubprogfuncs_t *prinst, struct globalvars_s
|
|||
char *send = Z_Malloc(4+strlen(contents));
|
||||
send[0] = send[1] = send[2] = send[3] = 0xff;
|
||||
memcpy(send+4, contents, strlen(contents));
|
||||
G_FLOAT(OFS_RETURN) = NET_SendPacket(NS_SERVER, 4+strlen(contents), send, &to);
|
||||
G_FLOAT(OFS_RETURN) = NET_SendPacket(svs.sockets, 4+strlen(contents), send, &to);
|
||||
Z_Free(send);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -334,7 +334,7 @@ typedef struct
|
|||
// received from client
|
||||
|
||||
// reply
|
||||
double senttime; //time we sent this frame to the client, for ping calcs
|
||||
double senttime; //time we sent this frame to the client, for ping calcs (realtime)
|
||||
int sequence; //the outgoing sequence - without mask, meaning we know if its current or stale
|
||||
float ping_time; //how long it took for the client to ack it, may be negative
|
||||
float move_msecs; //
|
||||
|
@ -365,8 +365,9 @@ typedef struct
|
|||
float pmwaterjumptime;
|
||||
usercmd_t cmd;
|
||||
//these are old positions of players, to give more accurate victim positions
|
||||
vec3_t playerpositions[MAX_CLIENTS]; //where each player was in this frame, for antilag
|
||||
qboolean playerpresent[MAX_CLIENTS]; //whether the player was actually present
|
||||
laggedentinfo_t laggedplayer[MAX_CLIENTS];
|
||||
unsigned int numlaggedplayers;
|
||||
float laggedtime; //sv.time of when this frame was sent
|
||||
} client_frame_t;
|
||||
|
||||
#ifdef Q2SERVER
|
||||
|
@ -524,6 +525,7 @@ typedef struct client_s
|
|||
laggedentinfo_t laggedents[MAX_CLIENTS];
|
||||
unsigned int laggedents_count;
|
||||
float laggedents_frac;
|
||||
float laggedents_time;
|
||||
|
||||
// spawn parms are carried from level to level
|
||||
float spawn_parms[NUM_SPAWN_PARMS];
|
||||
|
@ -939,7 +941,6 @@ typedef struct
|
|||
|
||||
struct netprim_s netprim;
|
||||
|
||||
int language; //the server operators language
|
||||
laggedpacket_t *free_lagged_packet;
|
||||
packet_entities_t entstatebuffer; /*just a temp buffer*/
|
||||
|
||||
|
@ -1325,6 +1326,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client);
|
|||
|
||||
//sv_master.c
|
||||
void SVM_Think(int port);
|
||||
vfsfile_t *SVM_GenerateIndex(const char *fname);
|
||||
|
||||
|
||||
//
|
||||
|
@ -1333,6 +1335,9 @@ void SVM_Think(int port);
|
|||
typedef enum {RD_NONE, RD_CLIENT, RD_PACKET, RD_PACKET_LOG, RD_OBLIVION, RD_MASTER} redirect_t; //oblivion is provided so people can read the output before the buffer is wiped.
|
||||
void SV_BeginRedirect (redirect_t rd, int lang);
|
||||
void SV_EndRedirect (void);
|
||||
extern char sv_redirected_buf[8000];
|
||||
extern redirect_t sv_redirected;
|
||||
extern int sv_redirectedlang;
|
||||
|
||||
|
||||
qboolean PR_GameCodePacket(char *s);
|
||||
|
|
|
@ -63,7 +63,7 @@ crosses a waterline.
|
|||
=============================================================================
|
||||
*/
|
||||
|
||||
int needcleanup;
|
||||
static int needcleanup;
|
||||
|
||||
//int fatbytes;
|
||||
|
||||
|
@ -106,9 +106,9 @@ void SV_ExpandNackFrames(client_t *client, int require)
|
|||
// because there can be a lot of nails, there is a special
|
||||
// network protocol for them
|
||||
#define MAX_NAILS 32
|
||||
edict_t *nails[MAX_NAILS];
|
||||
int numnails;
|
||||
int nailcount = 0;
|
||||
static edict_t *nails[MAX_NAILS];
|
||||
static int numnails;
|
||||
static int nailcount = 0;
|
||||
extern int sv_nailmodel, sv_supernailmodel, sv_playermodel;
|
||||
|
||||
#ifdef SERVER_DEMO_PLAYBACK
|
||||
|
@ -1564,6 +1564,7 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb
|
|||
frame->numresend = outno;
|
||||
frame->sequence = sequence;
|
||||
|
||||
frame->laggedtime = sv.time;
|
||||
for (i = 0; i < to->num_entities; i++)
|
||||
{
|
||||
n = &to->entities[i];
|
||||
|
@ -1582,9 +1583,10 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb
|
|||
age = sv.time - sv.world.physicstime;
|
||||
age = bound(0, age, 0.1);
|
||||
|
||||
VectorMA(n->origin, (sv.time - cl->localtime)/8.0, n->u.q1.velocity, frame->playerpositions[j]);
|
||||
VectorMA(n->origin, (sv.time - cl->localtime)/8.0, n->u.q1.velocity, frame->laggedplayer[j].origin);
|
||||
VectorCopy(n->angles, frame->laggedplayer[j].angles);
|
||||
//FIXME: add framestate_t info.
|
||||
frame->playerpresent[j] = true;
|
||||
frame->laggedplayer[j].present = true;
|
||||
}
|
||||
|
||||
return overflow;
|
||||
|
@ -2762,13 +2764,14 @@ void SV_WritePlayersToClient (client_t *client, client_frame_t *frame, edict_t *
|
|||
clst.lastcmd = NULL;
|
||||
clst.velocity = NULL;
|
||||
clst.localtime = sv.time;
|
||||
VectorCopy(clst.origin, frame->playerpositions[j]);
|
||||
VectorCopy(clst.origin, frame->laggedplayer[j].origin);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorMA(clst.origin, (sv.time - clst.localtime), clst.velocity, frame->playerpositions[j]);
|
||||
VectorMA(clst.origin, (sv.time - clst.localtime), clst.velocity, frame->laggedplayer[j].origin);
|
||||
}
|
||||
frame->playerpresent[j] = true;
|
||||
VectorCopy(clst.angles, frame->laggedplayer[j].angles);
|
||||
frame->laggedplayer[j].present = true;
|
||||
SV_WritePlayerToClient(msg, &clst);
|
||||
}
|
||||
|
||||
|
@ -2983,7 +2986,7 @@ typedef struct gibfilter_s {
|
|||
int minframe;
|
||||
int maxframe;
|
||||
} gibfilter_t;
|
||||
gibfilter_t *gibfilter;
|
||||
static gibfilter_t *gibfilter;
|
||||
void SV_GibFilterPurge(void)
|
||||
{
|
||||
gibfilter_t *gf;
|
||||
|
@ -3881,9 +3884,7 @@ svc_playerinfo messages
|
|||
*/
|
||||
void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignorepvs)
|
||||
{
|
||||
#ifdef NQPROT
|
||||
int e;
|
||||
#endif
|
||||
int i;
|
||||
packet_entities_t *pack;
|
||||
edict_t *clent;
|
||||
client_frame_t *frame;
|
||||
|
@ -3893,7 +3894,8 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore
|
|||
|
||||
// this is the frame we are creating
|
||||
frame = &client->frameunion.frames[client->netchan.incoming_sequence & UPDATE_MASK];
|
||||
memset(frame->playerpresent, 0, sizeof(frame->playerpresent));
|
||||
for (i = 0; i < sv.allocated_client_slots; i++)
|
||||
frame->laggedplayer[i].present = 0;
|
||||
|
||||
// find the client's PVS
|
||||
if (ignorepvs)
|
||||
|
@ -3977,6 +3979,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg, qboolean ignore
|
|||
SVDP_EmitEntitiesUpdate(client, frame, pack, msg);
|
||||
else
|
||||
{
|
||||
int e;
|
||||
for (e = 0; e < pack->num_entities; e++)
|
||||
{
|
||||
if (pack->entities[e].number > sv.allocated_client_slots)
|
||||
|
|
|
@ -27,7 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#define INVIS_CHAR2 (char)138
|
||||
#define INVIS_CHAR3 (char)160
|
||||
|
||||
#ifdef SERVERONLY
|
||||
#ifndef HAVE_CLIENT
|
||||
double host_frametime;
|
||||
double realtime; // without any filtering or bounding
|
||||
qboolean host_initialized; // true if into command execution (compatability)
|
||||
|
@ -35,13 +35,6 @@ quakeparms_t host_parms;
|
|||
int host_hunklevel;
|
||||
#endif
|
||||
|
||||
// callbacks
|
||||
void SV_Tcpport_Callback(struct cvar_s *var, char *oldvalue);
|
||||
void SV_Tcpport6_Callback(struct cvar_s *var, char *oldvalue);
|
||||
void SV_Port_Callback(struct cvar_s *var, char *oldvalue);
|
||||
void SV_PortIPv6_Callback(struct cvar_s *var, char *oldvalue);
|
||||
void SV_PortIPX_Callback(struct cvar_s *var, char *oldvalue);
|
||||
|
||||
client_t *host_client; // current client
|
||||
|
||||
// bound the size of the physics time tic
|
||||
|
@ -63,12 +56,9 @@ cvar_t zombietime = CVARD("zombietime", "2", "Client slots will not be reuse
|
|||
cvar_t sv_crypt_rcon = CVARFD("sv_crypt_rcon", "", CVAR_ARCHIVE, "Controls whether the rcon password must be hashed or not. Hashed passwords also partially prevent replay attacks, but does NOT prevent malicious actors from reading the commands/results.\n0: completely insecure. ONLY allows plain-text passwords. Do not use.\n1: Mandatory hashing (recommended).\nEmpty: Allow either, whether the password is secure or not is purely the client's responsibility/fault. Only use this for comptibility with old clients.");
|
||||
cvar_t sv_crypt_rcon_clockskew = CVARFD("sv_timestamplen", "60", CVAR_ARCHIVE, "Limits clock skew to reduce (delayed) replay attacks");
|
||||
#ifdef SERVERONLY
|
||||
cvar_t developer = CVAR("developer","0"); // show extra messages
|
||||
|
||||
cvar_t rcon_password = CVARF("rcon_password", "", CVAR_NOUNSAFEEXPAND); // password for remote server commands
|
||||
cvar_t password = CVARF("password", "", CVAR_NOUNSAFEEXPAND); // password for entering the game
|
||||
#else
|
||||
extern cvar_t developer;
|
||||
extern cvar_t rcon_password;
|
||||
extern cvar_t password;
|
||||
#endif
|
||||
|
@ -108,6 +98,7 @@ cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0");
|
|||
cvar_t sv_reconnectlimit = CVARD("sv_reconnectlimit", "0", "Blocks dupe connection within the specified length of time .");
|
||||
extern cvar_t net_enable_dtls;
|
||||
cvar_t sv_reportheartbeats = CVARD("sv_reportheartbeats", "2", "Print a notice each time a heartbeat is sent to a master server. When set to 2, the message will be displayed once.");
|
||||
cvar_t sv_heartbeat_interval = CVARD("sv_heartbeat_interval", "110", "Interval between heartbeats. Low values are abusive, high values may cause NAT/ghost issues.");
|
||||
cvar_t sv_highchars = CVAR("sv_highchars", "1");
|
||||
cvar_t sv_maxrate = CVARD("sv_maxrate", "50000", "This controls the maximum number of bytes any indivual player may receive (when not downloading). The individual user's rate will also be controlled by the user's rate cvar.");
|
||||
cvar_t sv_maxdrate = CVARAFD("sv_maxdrate", "500000",
|
||||
|
@ -272,7 +263,9 @@ void SV_Shutdown (void)
|
|||
#endif
|
||||
Cvar_Shutdown();
|
||||
Cmd_Shutdown();
|
||||
#ifdef PACKAGEMANAGER
|
||||
PM_Shutdown();
|
||||
#endif
|
||||
|
||||
|
||||
InfoBuf_Clear(&svs.info, true);
|
||||
|
@ -1290,7 +1283,7 @@ static void SVC_GetInfo (char *challenge, int fullstatus)
|
|||
}
|
||||
}
|
||||
|
||||
NET_SendPacket (NS_SERVER, resp-response, response, &net_from);
|
||||
NET_SendPacket (svs.sockets, resp-response, response, &net_from);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1378,7 +1371,7 @@ static void SVC_Log (void)
|
|||
else if (seq == svs.logsequence)
|
||||
{ //current log isn't available as its not complete yet.
|
||||
data[0] = A2A_NACK;
|
||||
NET_SendPacket (NS_SERVER, 1, data, &net_from);
|
||||
NET_SendPacket (svs.sockets, 1, data, &net_from);
|
||||
return;
|
||||
}
|
||||
else if (seq > svs.logsequence) //future logs are not valid either. reply with the last that was. this is for compat, just in case.
|
||||
|
@ -1392,7 +1385,7 @@ static void SVC_Log (void)
|
|||
if (!fraglog_public.ival)
|
||||
{ //frag logs are not public (for DoS protection perhaps?.
|
||||
data[0] = A2A_NACK;
|
||||
NET_SendPacket (NS_SERVER, 1, data, &net_from);
|
||||
NET_SendPacket (svs.sockets, 1, data, &net_from);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1404,7 +1397,7 @@ static void SVC_Log (void)
|
|||
Q_snprintfz(data, sizeof(data), "stdlog %i %s\n%s", seq, av, (char *)svs.log_buf[seq&(FRAGLOG_BUFFERS-1)]);
|
||||
else
|
||||
Q_snprintfz(data, sizeof(data), "stdlog %i\n%s", seq, (char *)svs.log_buf[seq&(FRAGLOG_BUFFERS-1)]);
|
||||
NET_SendPacket (NS_SERVER, strlen(data)+1, data, &net_from);
|
||||
NET_SendPacket (svs.sockets, strlen(data)+1, data, &net_from);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1420,7 +1413,7 @@ void SVC_Ping (void)
|
|||
|
||||
data = A2A_ACK;
|
||||
|
||||
NET_SendPacket (NS_SERVER, 1, &data, &net_from);
|
||||
NET_SendPacket (svs.sockets, 1, &data, &net_from);
|
||||
}
|
||||
|
||||
//from net_from
|
||||
|
@ -1777,7 +1770,7 @@ void VARGS SV_RejectMessage(int protocol, char *format, ...)
|
|||
vsnprintf (string+5,sizeof(string)-1-5, format,argptr);
|
||||
len = strlen(string+4)+1+4;
|
||||
*(int*)string = BigLong(NETFLAG_CTL|len);
|
||||
NET_SendPacket(NS_SERVER, len, string, &net_from);
|
||||
NET_SendPacket(svs.sockets, len, string, &net_from);
|
||||
return;
|
||||
case SCP_DARKPLACES6:
|
||||
case SCP_DARKPLACES7:
|
||||
|
@ -1840,7 +1833,7 @@ void SV_AcceptMessage(client_t *newcl)
|
|||
MSG_WriteByte(&sb, 0/*flags*/);
|
||||
}
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL|sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
return;
|
||||
}
|
||||
case SCP_DARKPLACES6:
|
||||
|
@ -3484,7 +3477,7 @@ void SVC_RemoteCommand (void)
|
|||
Con_TPrintf ("Bad rcon from %s:\t%s\n"
|
||||
, NET_AdrToString (adr, sizeof(adr), &net_from), net_message.data+4);
|
||||
|
||||
SV_BeginRedirect (RD_PACKET, svs.language);
|
||||
SV_BeginRedirect (RD_PACKET, com_language);
|
||||
|
||||
Con_TPrintf ("Bad rcon_password. Passwords might be logged. Be careful.\n");
|
||||
}
|
||||
|
@ -3501,7 +3494,7 @@ void SVC_RemoteCommand (void)
|
|||
Con_TPrintf ("Rcon from %s:\t%s\n"
|
||||
, NET_AdrToString (adr, sizeof(adr), &net_from), net_message.data+4);
|
||||
|
||||
SV_BeginRedirect (RD_PACKET_LOG, svs.language);
|
||||
SV_BeginRedirect (RD_PACKET_LOG, com_language);
|
||||
|
||||
remaining[0] = 0;
|
||||
|
||||
|
@ -3725,7 +3718,7 @@ qboolean SV_ConnectionlessPacket (void)
|
|||
|
||||
if (secure.value) //FIXME: possible problem for nq clients when enabled
|
||||
{
|
||||
Netchan_OutOfBandTPrintf (NS_SERVER, &net_from, svs.language, "%c\nThis server requires client validation.\nPlease use the "FULLENGINENAME" validation program\n", A2C_PRINT);
|
||||
Netchan_OutOfBandTPrintf (NS_SERVER, &net_from, com_language, "%c\nThis server requires client validation.\nPlease use the "FULLENGINENAME" validation program\n", A2C_PRINT);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3891,7 +3884,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
SZ_Clear(&sb);
|
||||
MSG_WriteLong(&sb, BigLong(NETFLAG_ACK | 8));
|
||||
MSG_WriteLong(&sb, sequence);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3907,7 +3900,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteString(&sb, va("cmd challengeconnect %i %i\n", SV_NewChallenge(), MOD_PROQUAKE));
|
||||
|
||||
*(int*)sb.data = BigLong(NETFLAG_UNRELIABLE|sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3935,7 +3928,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteByte(&sb, CCREP_REJECT);
|
||||
MSG_WriteString(&sb, "Incorrect game\n");
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
return false; //not our game.
|
||||
}
|
||||
if (MSG_ReadByte() != NQ_NETCHAN_VERSION)
|
||||
|
@ -3945,7 +3938,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteByte(&sb, CCREP_REJECT);
|
||||
MSG_WriteString(&sb, "Incorrect version\n");
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
return false; //not our version...
|
||||
}
|
||||
|
||||
|
@ -3957,7 +3950,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteByte(&sb, CCREP_REJECT);
|
||||
MSG_WriteString(&sb, *banreason?va("You are banned: %s\n", banreason):"You are banned\n");
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
return false; //not our version...
|
||||
}
|
||||
|
||||
|
@ -3982,7 +3975,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteByte(&sb, CCREP_REJECT);
|
||||
MSG_WriteString(&sb, "NQ clients are not supported with hexen2 gamecode\n");
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
return false; //not our version...
|
||||
}
|
||||
if (sv_listen_nq.ival == 2)
|
||||
|
@ -3995,7 +3988,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteByte(&sb, MOD_PROQUAKE);
|
||||
MSG_WriteByte(&sb, MOD_PROQUAKE_VERSION);
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL|sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
|
||||
|
||||
SZ_Clear(&sb);
|
||||
|
@ -4006,7 +3999,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteString(&sb, va("cmd challengeconnect %i %i %i %i %i\n", SV_NewChallenge(), mod, modver, flags, passwd));
|
||||
|
||||
*(int*)sb.data = BigLong(NETFLAG_UNRELIABLE|sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
/*don't worry about repeating, the nop case above will recover it*/
|
||||
}
|
||||
else
|
||||
|
@ -4048,7 +4041,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteByte (&sb, maxclients.value);
|
||||
MSG_WriteByte (&sb, NQ_NETCHAN_VERSION);
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
return true;
|
||||
case CCREQ_PLAYER_INFO:
|
||||
if (sv_showconnectionlessmessages.ival)
|
||||
|
@ -4082,7 +4075,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteString (&sb, SV_PlayerPublicAddress(cl)); /*player's address, leave blank, don't spam that info as it can result in personal attacks exploits*/
|
||||
}
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
return true;
|
||||
case CCREQ_RULE_INFO:
|
||||
if (sv_showconnectionlessmessages.ival)
|
||||
|
@ -4132,7 +4125,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
|
|||
MSG_WriteString (&sb, rval);
|
||||
}
|
||||
*(int*)sb.data = BigLong(NETFLAG_CTL+sb.cursize);
|
||||
NET_SendPacket(NS_SERVER, sb.cursize, sb.data, &net_from);
|
||||
NET_SendPacket(svs.sockets, sb.cursize, sb.data, &net_from);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -4285,7 +4278,7 @@ qboolean SV_ReadPackets (float *delay)
|
|||
#ifdef SERVER_DEMO_PLAYBACK
|
||||
while (giveup-- > 0 && SV_GetPacket()>=0)
|
||||
#else
|
||||
while (giveup-- > 0 && (cookie=NET_GetPacket (NS_SERVER, cookie)) >= 0)
|
||||
while (giveup-- > 0 && (cookie=NET_GetPacket (svs.sockets, cookie)) >= 0)
|
||||
#endif
|
||||
{
|
||||
// check for connectionless packet (0xffffffff) first
|
||||
|
@ -4299,9 +4292,9 @@ qboolean SV_ReadPackets (float *delay)
|
|||
if (ct - lt > 5*1000)
|
||||
{
|
||||
if (*banreason)
|
||||
Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, svs.language, "You are banned: %s\n", banreason);
|
||||
Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, com_language, "You are banned: %s\n", banreason);
|
||||
else
|
||||
Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, svs.language, "You are banned\n");
|
||||
Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, com_language, "You are banned\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -4454,7 +4447,7 @@ dominping:
|
|||
if (SV_BannedReason (&net_from))
|
||||
continue;
|
||||
|
||||
if (NET_WasSpecialPacket(NS_SERVER))
|
||||
if (NET_WasSpecialPacket(svs.sockets))
|
||||
continue;
|
||||
|
||||
// packet is not from a known client
|
||||
|
@ -5125,8 +5118,6 @@ void SV_InitLocal (void)
|
|||
if (isDedicated)
|
||||
#endif
|
||||
{
|
||||
Cvar_Register (&developer, cvargroup_servercontrol);
|
||||
|
||||
Cvar_Register (&password, cvargroup_servercontrol);
|
||||
Cvar_Register (&rcon_password, cvargroup_servercontrol);
|
||||
|
||||
|
@ -5563,7 +5554,7 @@ void SV_ExtractFromUserinfo (client_t *cl, qboolean verbose)
|
|||
}
|
||||
|
||||
val = InfoBuf_ValueForKey (&cl->userinfo, "lang");
|
||||
cl->language = *val?TL_FindLanguage(val):svs.language;
|
||||
cl->language = *val?TL_FindLanguage(val):com_language;
|
||||
|
||||
val = InfoBuf_ValueForKey (&cl->userinfo, "nogib");
|
||||
cl->gibfilter = !!atoi(val);
|
||||
|
|
|
@ -11,213 +11,729 @@
|
|||
|
||||
#include "netinc.h"
|
||||
|
||||
//quake1 protocol
|
||||
//
|
||||
//
|
||||
//quakeworld protocol
|
||||
// heartbeat: "a"
|
||||
// query: "c\n%i<sequence>\n%i<numplayers>\n"
|
||||
//queryresponse: "d\naaaappaaaapp"
|
||||
|
||||
//quake2 protocol
|
||||
//client sends "query\0"
|
||||
//server sends "0xff0xff0xff0xffservers" follwed by lots of ips.
|
||||
// heartbeat: "heartbeat\n%s<serverinfo>\n%i<frags> %i<ping> \"%s\"<name>\n<repeat>"
|
||||
// query: "query\0"
|
||||
//queryresponse: "servers\naaaappaaaapp"
|
||||
|
||||
#define SVM_Q1HEARTBEATTIME 330
|
||||
#define SVM_Q2HEARTBEATTIME 330
|
||||
//quake3/dpmaster protocol
|
||||
// heartbeat: "heartbeat DarkPlaces\n"
|
||||
// query: "getservers[Ext<ipv6>] [%s<game>] %u<version> [empty] [full] [ipv6]"
|
||||
//queryresponse: "getservers[Ext]Response\\aaaapp/aaaaaaaaaaaapp\\EOF"
|
||||
|
||||
enum gametypes_e
|
||||
{
|
||||
GT_FFA=0,
|
||||
GT_TOURNEY=1,
|
||||
GT_TEAM=3,
|
||||
GT_CTF=4,
|
||||
};
|
||||
typedef struct svm_server_s {
|
||||
netadr_t adr;
|
||||
int clients;
|
||||
int protover;
|
||||
unsigned int clients;
|
||||
unsigned int maxclients;
|
||||
char hostname[48]; //just for our own listings.
|
||||
char mapname[16]; //just for our own listings.
|
||||
char gamedir[16]; //again...
|
||||
unsigned short gametype;
|
||||
float expiretime;
|
||||
|
||||
bucket_t bucket;
|
||||
struct svm_game_s *game;
|
||||
struct svm_server_s *next;
|
||||
} svm_server_t;
|
||||
|
||||
typedef struct {
|
||||
SOCKET socketudp;
|
||||
float time;
|
||||
int port;
|
||||
typedef struct svm_game_s {
|
||||
struct svm_game_s *next;
|
||||
|
||||
svm_server_t *firstserver;
|
||||
int numservers;
|
||||
size_t numservers;
|
||||
char name[1];
|
||||
} svm_game_t;
|
||||
|
||||
typedef struct {
|
||||
float time;
|
||||
|
||||
svm_game_t *firstgame;
|
||||
size_t numgames;
|
||||
|
||||
hashtable_t serverhash;
|
||||
size_t numservers;
|
||||
|
||||
struct rates_s
|
||||
{
|
||||
double timestamp;
|
||||
size_t heartbeats;
|
||||
size_t queries;
|
||||
size_t junk;
|
||||
|
||||
size_t drops;
|
||||
size_t adds;
|
||||
} total, stamps[60];
|
||||
size_t stampring;
|
||||
double nextstamp;
|
||||
} masterserver_t;
|
||||
|
||||
masterserver_t svm = {INVALID_SOCKET};
|
||||
static masterserver_t svm;
|
||||
ftenet_connections_t *svm_sockets;
|
||||
|
||||
void SVM_RemoveOldServers(void)
|
||||
static void QDECL SVM_Tcpport_Callback(struct cvar_s *var, char *oldvalue)
|
||||
{
|
||||
svm_server_t *server, *next, *prev=NULL;
|
||||
for (server = svm.firstserver; server; server = next)
|
||||
FTENET_AddToCollection(svm_sockets, var->name, var->string, NA_IP, NP_STREAM);
|
||||
}
|
||||
static void QDECL SVM_Port_Callback(struct cvar_s *var, char *oldvalue)
|
||||
{
|
||||
FTENET_AddToCollection(svm_sockets, var->name, var->string, NA_IP, NP_DGRAM);
|
||||
}
|
||||
static cvar_t sv_heartbeattimeout = CVARD("sv_heartbeattimeout", "600", "How many seconds a server should remain listed after its latest heartbeat. Larger values can avoid issues from packetloss, but can also make dos attacks easier.");
|
||||
static cvar_t sv_masterport = CVARC("sv_masterport", "27000 28000", SVM_Port_Callback);
|
||||
static cvar_t sv_masterport_tcp = CVARC("sv_masterport_tcp", "27000 28000", SVM_Tcpport_Callback);
|
||||
static cvar_t sv_maxgames = CVARD("sv_maxgames", "100", "Limits the number of games that may be known. This is to reduce denial of service attacks.");
|
||||
static cvar_t sv_maxservers = CVARD("sv_maxservers", "1000", "Limits the number of servers (total from all games) that may be known. This is to reduce denial of service attacks.");
|
||||
|
||||
//returns a hash key for a server's address.
|
||||
static unsigned int SVM_GenerateAddressKey(const netadr_t *adr)
|
||||
{
|
||||
unsigned int key;
|
||||
switch(adr->type)
|
||||
{
|
||||
next = server->next;
|
||||
if (server->expiretime < svm.time)
|
||||
case NA_IP:
|
||||
key = *(const unsigned int*)adr->address.ip;
|
||||
key ^= 0xffff0000; //match ipv6's ipv4-mapped addresses.
|
||||
key ^= adr->port;
|
||||
break;
|
||||
case NA_IPV6:
|
||||
key = *(const unsigned int*)(adr->address.ip6+0);
|
||||
key ^= *(const unsigned int*)(adr->address.ip6+4);
|
||||
key ^= *(const unsigned int*)(adr->address.ip6+8);
|
||||
key ^= *(const unsigned int*)(adr->address.ip6+12);
|
||||
key ^= adr->port;
|
||||
break;
|
||||
default:
|
||||
key = 0;
|
||||
break;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
static svm_server_t *SVM_GetServer(netadr_t *adr)
|
||||
{
|
||||
svm_server_t *server;
|
||||
unsigned int key = SVM_GenerateAddressKey(adr);
|
||||
|
||||
server = Hash_GetKey(&svm.serverhash, key);
|
||||
while (server && !NET_CompareAdr(&server->adr, adr))
|
||||
{
|
||||
server = Hash_GetNextKey(&svm.serverhash, key, server);
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
static svm_game_t *SVM_FindGame(const char *game, qboolean create)
|
||||
{
|
||||
svm_game_t *g;
|
||||
for (g = svm.firstgame; g; g = g->next)
|
||||
{
|
||||
if (!Q_strcasecmp(game, g->name))
|
||||
return g;
|
||||
}
|
||||
|
||||
if (create)
|
||||
{
|
||||
if (svm.numgames >= sv_maxgames.ival)
|
||||
{
|
||||
BZ_Free(server);
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
Con_DPrintf("game limit exceeded\n");
|
||||
return NULL;
|
||||
}
|
||||
//block some chars that may cause issues/exploits. sorry.
|
||||
if (strchr(game, '.') || strchr(game, '\"') || strchr(game, '/') || strchr(game, '?') || strchr(game, '&') || strchr(game, '+') || strchr(game, '\'') || strchr(game, '<') || strchr(game, '>'))
|
||||
return NULL;
|
||||
if (!*game || (*game >= '0' && *game <= '9'))
|
||||
return NULL; //must not start with a number either.
|
||||
g = ZF_Malloc(sizeof(*g) + strlen(game));
|
||||
if (g)
|
||||
{
|
||||
strcpy(g->name, game);
|
||||
g->next = svm.firstgame;
|
||||
svm.firstgame = g;
|
||||
svm.numgames++;
|
||||
Con_DPrintf("Creating game \"%s\"\n", g->name);
|
||||
}
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
static void SVM_RemoveOldServers(void)
|
||||
{
|
||||
svm_game_t **gamelink, *g;
|
||||
svm_server_t **serverlink, *s;
|
||||
for (gamelink = &svm.firstgame; (g=*gamelink); )
|
||||
{
|
||||
for (serverlink = &g->firstserver; (s=*serverlink); )
|
||||
{
|
||||
if (s->expiretime < svm.time)
|
||||
{
|
||||
if (developer.ival)
|
||||
{
|
||||
char buf[256];
|
||||
Con_Printf("timeout: %s\n", NET_AdrToString(buf, sizeof(buf), &s->adr));
|
||||
}
|
||||
|
||||
Hash_RemoveDataKey(&svm.serverhash, SVM_GenerateAddressKey(&s->adr), s);
|
||||
|
||||
svm.total.drops++;
|
||||
*serverlink = s->next;
|
||||
BZ_Free(s);
|
||||
g->numservers--;
|
||||
svm.numservers--;
|
||||
}
|
||||
else
|
||||
svm.firstserver = next;
|
||||
serverlink = &s->next;
|
||||
}
|
||||
|
||||
if (!g->firstserver)
|
||||
{
|
||||
Con_DPrintf("game \"%s\" has no active servers\n", g->name);
|
||||
*gamelink = g->next;
|
||||
Z_Free(g);
|
||||
svm.numgames--;
|
||||
}
|
||||
else
|
||||
prev = server;
|
||||
gamelink = &g->next;
|
||||
}
|
||||
}
|
||||
|
||||
int SVM_AddIPAddresses(sizebuf_t *sb, int first)
|
||||
int SVM_AddIPAddresses(sizebuf_t *sb, int first, const char *gamename, int v4, int v6, qboolean prefixes)
|
||||
{
|
||||
int number = 0;
|
||||
svm_server_t *server;
|
||||
|
||||
for (server = svm.firstserver; server; server = server->next)
|
||||
int prefix;
|
||||
int len;
|
||||
svm_game_t *game = SVM_FindGame(gamename, false);
|
||||
if (game)
|
||||
{
|
||||
if (number == first)
|
||||
break;
|
||||
for (server = game->firstserver; server; server = server->next)
|
||||
{
|
||||
if (number == first)
|
||||
break;
|
||||
|
||||
first--;
|
||||
first--;
|
||||
}
|
||||
|
||||
for (; server; server = server->next)
|
||||
{
|
||||
switch(server->adr.type)
|
||||
{
|
||||
case NA_IP:
|
||||
if (!v4)
|
||||
continue;
|
||||
prefix = '\\';
|
||||
len = 4;
|
||||
break;
|
||||
case NA_IPV6:
|
||||
if (!v6)
|
||||
continue;
|
||||
prefix = '/';
|
||||
len = 16;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prefixes)
|
||||
MSG_WriteByte(sb, prefixes);
|
||||
|
||||
SZ_Write(sb, server->adr.address.ip, len);
|
||||
MSG_WriteShort(sb, server->adr.port);
|
||||
|
||||
number++;
|
||||
}
|
||||
}
|
||||
|
||||
for (; server; server = server->next)
|
||||
{
|
||||
if (sb->cursize + 6 >= sb->maxsize)
|
||||
break;
|
||||
|
||||
MSG_WriteByte(sb, server->adr.address.ip[0]);
|
||||
MSG_WriteByte(sb, server->adr.address.ip[1]);
|
||||
MSG_WriteByte(sb, server->adr.address.ip[2]);
|
||||
MSG_WriteByte(sb, server->adr.address.ip[3]);
|
||||
MSG_WriteShort(sb, server->adr.port);
|
||||
|
||||
number++;
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
void SVM_Heartbeat(netadr_t *adr, int numclients, float validuntil)
|
||||
vfsfile_t *SVM_GenerateIndex(const char *fname)
|
||||
{
|
||||
static const char *thecss =
|
||||
"<style type=\"text/css\">"
|
||||
"body {"
|
||||
"background-color: #303030;"
|
||||
"color: #998080;"
|
||||
"font-family: verdanah, trebuchet ms, arial, sans-serif;"
|
||||
"}"
|
||||
"a { text-decoration: none; }"
|
||||
"a:link { color: #308090; }"
|
||||
"a:visited { color: #308090; }"
|
||||
"a:hover { color: #308090; text-decoration: underline; }"
|
||||
".header { text-align: center; }"
|
||||
".footer { text-align: center; }"
|
||||
".main { width: 1024px; margin: 0 auto; }"
|
||||
"H1 {"
|
||||
"text-align: center;"
|
||||
"background-color: #202020;"
|
||||
"color: #808080;"
|
||||
"}"
|
||||
"table { width: 100%; border-collapse: collapse; }"
|
||||
"th { text-align: left; }"
|
||||
"tr:hover { background-color: #202020; }"
|
||||
"</style>";
|
||||
char tmpbuf[256];
|
||||
svm_game_t *game;
|
||||
svm_server_t *server;
|
||||
|
||||
for (server = svm.firstserver; server; server = server->next)
|
||||
vfsfile_t *f = NULL;
|
||||
unsigned clients = 0, maxclients=0;
|
||||
if (!strcmp(fname, "index.html"))
|
||||
{
|
||||
if (NET_CompareAdr(&server->adr, adr))
|
||||
break;
|
||||
f = VFSPIPE_Open(1, false);
|
||||
VFS_PRINTF(f, "%s", thecss);
|
||||
VFS_PRINTF(f, "<h1>FTE-Master</h1>\n");
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
for (game = svm.firstgame; game; game = game->next)
|
||||
{
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"game/%s.html\">%s</a></td><td%u server(s)</td></tr>\n", game->name, game->name, (unsigned)game->numservers);
|
||||
clients += game->numservers;
|
||||
}
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
VFS_PRINTF(f, "%u game(s), %u server(s)\n", (unsigned)svm.numgames, clients);
|
||||
}
|
||||
else if (!strncmp(fname, "server/", 7))
|
||||
{
|
||||
netadr_t adr[64];
|
||||
int count;
|
||||
|
||||
f = VFSPIPE_Open(1, false);
|
||||
VFS_PRINTF(f, "%s", thecss);
|
||||
VFS_PRINTF(f, "<h1>Single Server Info</h1>\n", tmpbuf);
|
||||
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Game</th><th>Address</th><th>Hostname</th><th>Mod dir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||
count = NET_StringToAdr2(fname+7, 0, adr, countof(adr));
|
||||
while(count-->0)
|
||||
{
|
||||
server = SVM_GetServer(&adr[count]);
|
||||
if (server)
|
||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", server->game?server->game->name:"Unknown", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr), server->hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||
else
|
||||
VFS_PRINTF(f, "<tr><td>?</td><td>%s</td><td>?</td><td>?</td><td>?</td><td>?/?</td></tr>\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &adr[count]));
|
||||
}
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
}
|
||||
else if (!strncmp(fname, "game/", 5))
|
||||
{
|
||||
COM_StripExtension(fname+5, tmpbuf, sizeof(tmpbuf));
|
||||
game = SVM_FindGame(tmpbuf, false);
|
||||
|
||||
f = VFSPIPE_Open(1, false);
|
||||
VFS_PRINTF(f, "%s", thecss);
|
||||
VFS_PRINTF(f, "<h1>Servers for %s</h1>\n", tmpbuf);
|
||||
|
||||
if(game)
|
||||
{
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Address</th><th>Hostname</th><th>Gamedir</th><th>Mapname</th><th>Players</th></tr>\n");
|
||||
for (server = game->firstserver; server; server = server->next)
|
||||
{
|
||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr), server->hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||
clients += server->clients;
|
||||
maxclients += server->maxclients;
|
||||
}
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
VFS_PRINTF(f, "%u server(s), %u/%u client(s)\n", (unsigned)game->numservers, clients, maxclients);
|
||||
}
|
||||
else
|
||||
VFS_PRINTF(f, "No servers known for %s\n", tmpbuf);
|
||||
}
|
||||
else if (!strncmp(fname, "raw/", 4))
|
||||
{ //just spews all
|
||||
COM_StripExtension(fname+4, tmpbuf, sizeof(tmpbuf));
|
||||
game = SVM_FindGame(tmpbuf, false);
|
||||
|
||||
f = VFSPIPE_Open(1, false);
|
||||
for (server = (game?game->firstserver:NULL); server; server = server->next)
|
||||
VFS_PRINTF(f, "%s\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr));
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
static svm_server_t *SVM_Heartbeat(const char *gamename, netadr_t *adr, int numclients, float validuntil)
|
||||
{
|
||||
svm_server_t *server = SVM_GetServer(adr);
|
||||
svm_game_t *game = SVM_FindGame(gamename, true);
|
||||
if (!game)
|
||||
return NULL;
|
||||
|
||||
if (server && server->game != game)
|
||||
{
|
||||
server->expiretime = realtime - 1;
|
||||
server = NULL;
|
||||
}
|
||||
|
||||
if (!server) //not found
|
||||
{
|
||||
if (svm.numservers >= sv_maxservers.ival)
|
||||
{
|
||||
Con_DPrintf("server limit exceeded\n");
|
||||
return NULL;
|
||||
}
|
||||
if (developer.ival)
|
||||
{
|
||||
char buf[256];
|
||||
Con_Printf("heartbeat(new - %s): %s\n", game->name, NET_AdrToString(buf, sizeof(buf), adr));
|
||||
}
|
||||
|
||||
server = Z_Malloc(sizeof(svm_server_t));
|
||||
server->next = svm.firstserver;
|
||||
svm.firstserver = server;
|
||||
server->game = game;
|
||||
server->next = game->firstserver;
|
||||
game->firstserver = server;
|
||||
game->numservers++;
|
||||
svm.numservers++;
|
||||
|
||||
server->adr = *adr;
|
||||
|
||||
svm.total.adds++;
|
||||
|
||||
Hash_AddKey(&svm.serverhash, SVM_GenerateAddressKey(adr), server, &server->bucket);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (developer.ival)
|
||||
{
|
||||
char buf[256];
|
||||
Con_Printf("heartbeat(refresh): %s\n", NET_AdrToString(buf, sizeof(buf), &server->adr));
|
||||
}
|
||||
}
|
||||
|
||||
server->clients = numclients;
|
||||
server->expiretime = validuntil;
|
||||
}
|
||||
|
||||
|
||||
void SVM_Init(int port)
|
||||
{
|
||||
if (svm.socketudp == INVALID_SOCKET)
|
||||
svm.socketudp = UDP_OpenSocket(port, false);
|
||||
}
|
||||
|
||||
void SVM_ShutDown (void)
|
||||
{
|
||||
if (svm.socketudp != INVALID_SOCKET)
|
||||
{
|
||||
UDP_CloseSocket(svm.socketudp);
|
||||
svm.socketudp = INVALID_SOCKET;
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
void SVM_Think(int port)
|
||||
{
|
||||
char *s;
|
||||
struct sockaddr_qstorage addr;
|
||||
int addrlen;
|
||||
netadr_t netaddr;
|
||||
if (!port)
|
||||
int cookie = 0;
|
||||
int giveup = 500;
|
||||
|
||||
while (giveup-- > 0 && (cookie=NET_GetPacket (svm_sockets, cookie)) >= 0)
|
||||
{
|
||||
SVM_ShutDown();
|
||||
return;
|
||||
net_message.data[net_message.cursize] = '\0'; //null term all strings.
|
||||
|
||||
svm.time = Sys_DoubleTime();
|
||||
|
||||
MSG_BeginReading(msg_nullnetprim);
|
||||
if (MSG_ReadLong() != -1 || msg_badread)
|
||||
{ //go back to start...
|
||||
MSG_BeginReading(msg_nullnetprim);
|
||||
}
|
||||
s = MSG_ReadStringLine();
|
||||
s = COM_Parse(s);
|
||||
if (!strcmp(com_token, "getservers") || !strcmp(com_token, "getserversExt"))
|
||||
{ //q3
|
||||
sizebuf_t sb;
|
||||
int ver;
|
||||
char *eos;
|
||||
char game[64];
|
||||
qboolean ext = !strcmp(com_token, "getserversExt");
|
||||
qboolean empty = false;
|
||||
qboolean full = false;
|
||||
qboolean ipv4 = !ext;
|
||||
qboolean ipv6 = false;
|
||||
int gametype = -1;
|
||||
s = COM_ParseOut(s, game, sizeof(game));
|
||||
ver = strtol(game, &eos, 0);
|
||||
if (*eos)
|
||||
{
|
||||
s = COM_Parse(s);
|
||||
ver = strtol(com_token, NULL, 0);
|
||||
}
|
||||
else
|
||||
Q_strncpyz(game, "Quake3", sizeof(game));
|
||||
for(;s&&*s;)
|
||||
{
|
||||
s = COM_Parse(s);
|
||||
if (!strcmp(com_token, "empty"))
|
||||
empty = true;
|
||||
else if (!strcmp(com_token, "full"))
|
||||
full = true;
|
||||
|
||||
else if (!strcmp(com_token, "ipv4"))
|
||||
ipv4 = true;
|
||||
else if (!strcmp(com_token, "ipv6"))
|
||||
ipv6 = true;
|
||||
|
||||
else if (!strcmp(com_token, "ffa"))
|
||||
gametype = GT_FFA;
|
||||
else if (!strcmp(com_token, "tourney"))
|
||||
gametype = GT_TOURNEY;
|
||||
else if (!strcmp(com_token, "team"))
|
||||
gametype = GT_TEAM;
|
||||
else if (!strcmp(com_token, "ctf"))
|
||||
gametype = GT_CTF;
|
||||
else if (!strncmp(com_token, "gametype=", 9))
|
||||
gametype = atoi(com_token+9);
|
||||
}
|
||||
svm.total.queries++;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer)-2;
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
|
||||
if (!ipv4 && !ipv6)
|
||||
ipv4 = ipv6 = true; //neither specified? use both
|
||||
if (ext)
|
||||
{ //ipv6 and ipv4 addresses
|
||||
MSG_WriteString(&sb, "getserversExtResponse");
|
||||
SVM_AddIPAddresses(&sb, 0, game, ipv4, ipv6, true);
|
||||
}
|
||||
else
|
||||
{ //ipv4 only
|
||||
MSG_WriteString(&sb, "getserversResponse");
|
||||
SVM_AddIPAddresses(&sb, 0, game, ipv4, ipv6, true);
|
||||
}
|
||||
sb.maxsize+=2;
|
||||
MSG_WriteByte(&sb, '\\'); //otherwise the last may be considered invalid and ignored.
|
||||
// MSG_WriteByte(&sb, 'E');
|
||||
// MSG_WriteByte(&sb, 'O');
|
||||
// MSG_WriteByte(&sb, 'T');
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
else if (!strcmp(com_token, "heartbeat"))
|
||||
{ //quake2 heartbeat. Serverinfo and players should follow.
|
||||
if (*s == '\n' && s[1] == '\\')
|
||||
{ //there's some serverinfo there, must be q2...
|
||||
svm.total.heartbeats++;
|
||||
SVM_Heartbeat("Quake2", &net_from, 0, svm.time + sv_heartbeattimeout.ival);
|
||||
}
|
||||
else
|
||||
{ //dp/q3/etc are annoying, but we can query from an emphemerial socket to check NAT rules.
|
||||
sizebuf_t sb;
|
||||
svm.total.queries++;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer);
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteString(&sb, "getinfo CAKE\n");
|
||||
sb.cursize--;
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(com_token, "infoResponse"))
|
||||
{
|
||||
int clients;
|
||||
const char *game, *chal;
|
||||
svm_server_t *srv;
|
||||
s = MSG_ReadStringLine();
|
||||
svm.total.heartbeats++;
|
||||
chal = Info_ValueForKey(s, "challenge");
|
||||
if (!strcmp(chal, "CAKE"))
|
||||
{
|
||||
clients = atoi(Info_ValueForKey(s, "clients"));
|
||||
game = Info_ValueForKey(s, "gamename");
|
||||
srv = SVM_Heartbeat(game, &net_from, clients, svm.time + sv_heartbeattimeout.ival);
|
||||
if (srv)
|
||||
{
|
||||
if (developer.ival)
|
||||
Info_Print(s, "\t");
|
||||
srv->protover = atoi(Info_ValueForKey(s, "protocol"));
|
||||
srv->maxclients = atoi(Info_ValueForKey(s, "sv_maxclients"));
|
||||
Q_strncpyz(srv->hostname, Info_ValueForKey(s, "hostname"), sizeof(srv->hostname));
|
||||
Q_strncpyz(srv->gamedir, Info_ValueForKey(s, "modname"), sizeof(srv->gamedir));
|
||||
Q_strncpyz(srv->mapname, Info_ValueForKey(s, "mapname"), sizeof(srv->mapname));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp(com_token, "query"))
|
||||
{ //quake2 server listing request
|
||||
sizebuf_t sb;
|
||||
svm.total.queries++;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer);
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteString(&sb, "servers\n");
|
||||
sb.cursize--;
|
||||
SVM_AddIPAddresses(&sb, 0, "Quake2", true, false, false);
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
else if (*com_token == S2M_HEARTBEAT) //sequence, players
|
||||
{ //quakeworld heartbeat
|
||||
int players;
|
||||
s = MSG_ReadStringLine();
|
||||
//sequence = atoi(s);
|
||||
s = MSG_ReadStringLine();
|
||||
players = atoi(s);
|
||||
svm.total.heartbeats++;
|
||||
SVM_Heartbeat("QuakeWorld", &net_from, players, svm.time + sv_heartbeattimeout.ival);
|
||||
}
|
||||
else if (*com_token == C2M_MASTER_REQUEST)
|
||||
{ //quakeworld server listing request
|
||||
sizebuf_t sb;
|
||||
svm.total.queries++;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer);
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteByte(&sb, M2C_MASTER_REPLY);
|
||||
MSG_WriteByte(&sb, '\n');
|
||||
SVM_AddIPAddresses(&sb, 0, "QuakeWorld", true, false, false);
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
else if (*com_token == A2A_PING)
|
||||
{ //quakeworld server listing request
|
||||
sizebuf_t sb;
|
||||
svm.total.queries++;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer);
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteByte(&sb, A2A_ACK);
|
||||
MSG_WriteByte(&sb, '\n');
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
else
|
||||
svm.total.junk++;
|
||||
}
|
||||
|
||||
if (port != svm.port)
|
||||
{
|
||||
SVM_ShutDown(); //shut down (to cause a restart)
|
||||
svm.port = port;
|
||||
}
|
||||
|
||||
SVM_Init(port);
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
net_message.cursize = recvfrom(svm.socketudp, net_message_buffer, sizeof(net_message_buffer)-1, 0, (struct sockaddr *)&addr, &addrlen);
|
||||
if (net_message.cursize <= 0)
|
||||
{
|
||||
addrlen = neterrno();
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
net_message.data[net_message.cursize] = '\0'; //null term all strings.
|
||||
SockadrToNetadr(&addr, &netaddr);
|
||||
svm.time = Sys_DoubleTime();
|
||||
|
||||
SVM_RemoveOldServers();
|
||||
|
||||
MSG_BeginReading(msg_nullnetprim);
|
||||
if (MSG_ReadLong() != -1 || msg_badread)
|
||||
{ //go back to start...
|
||||
MSG_BeginReading(msg_nullnetprim);
|
||||
}
|
||||
s = MSG_ReadStringLine();
|
||||
s = COM_Parse(s);
|
||||
if (!strcmp(com_token, "getservers"))
|
||||
{
|
||||
sizebuf_t sb;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer)-2;
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteString(&sb, "getserversResponse\\");
|
||||
sb.cursize--;
|
||||
SVM_AddIPAddresses(&sb, 0);
|
||||
sb.maxsize+=2;
|
||||
MSG_WriteShort(&sb, 0);
|
||||
sendto(svm.socketudp, sb.data, sb.cursize, 0, (struct sockaddr *)&addr, sizeof(addr));
|
||||
}
|
||||
else if (!strcmp(com_token, "heartbeat"))
|
||||
{ //quake2 heartbeat. Serverinfo and players follow.
|
||||
SVM_Heartbeat(&netaddr, 0, svm.time + SVM_Q2HEARTBEATTIME);
|
||||
}
|
||||
else if (!strcmp(com_token, "query"))
|
||||
{ //quake2 server listing request
|
||||
sizebuf_t sb;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer);
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteString(&sb, "servers\n");
|
||||
sb.cursize--;
|
||||
// MSG_WriteLong(&sb, 0);
|
||||
// MSG_WriteShort(&sb, 0);
|
||||
SVM_AddIPAddresses(&sb, 0);
|
||||
sendto(svm.socketudp, sb.data, sb.cursize, 0, (struct sockaddr *)&addr, sizeof(addr));
|
||||
}
|
||||
else if (*com_token == S2M_HEARTBEAT) //sequence, players
|
||||
{ //quakeworld heartbeat
|
||||
SVM_Heartbeat(&netaddr, 0, svm.time + SVM_Q1HEARTBEATTIME);
|
||||
}
|
||||
else if (*com_token == S2C_CHALLENGE)
|
||||
{ //quakeworld server listing request
|
||||
sizebuf_t sb;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer);
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteByte(&sb, M2C_MASTER_REPLY);
|
||||
MSG_WriteByte(&sb, '\n');
|
||||
SVM_AddIPAddresses(&sb, 0);
|
||||
sendto(svm.socketudp, sb.data, sb.cursize, 0, (struct sockaddr *)&addr, sizeof(addr));
|
||||
}
|
||||
}
|
||||
#else
|
||||
void SVM_Think(int port){}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef MASTERONLY
|
||||
static void SV_Quit_f (void)
|
||||
{
|
||||
Con_TPrintf ("Shutting down.\n");
|
||||
Sys_Quit ();
|
||||
}
|
||||
static void SVM_Status_f(void)
|
||||
{
|
||||
svm_game_t *g;
|
||||
svm_server_t *s;
|
||||
unsigned clients;
|
||||
struct rates_s *s1, *s2;
|
||||
float period;
|
||||
size_t r;
|
||||
|
||||
NET_PrintAddresses(svm_sockets);
|
||||
NET_PrintConnectionsStatus(svm_sockets);
|
||||
Con_Printf("Game count: %u\n", (unsigned)svm.numgames);
|
||||
for (g = svm.firstgame; g; g = g->next)
|
||||
{
|
||||
clients = 0;
|
||||
for (s = g->firstserver; s; s = s->next)
|
||||
clients += s->clients;
|
||||
Con_Printf("Game %s: %u servers, %u clients\n", g->name, (unsigned)g->numservers, clients);
|
||||
}
|
||||
|
||||
s1 = &svm.total;
|
||||
r = (svm.stampring >= countof(svm.stamps)-1)?svm.stampring-countof(svm.stamps)-1:0;
|
||||
s2 = &svm.stamps[r%countof(svm.stamps)];
|
||||
|
||||
period = s1->timestamp-s2->timestamp;
|
||||
period/=60;
|
||||
if (!period)
|
||||
period=1;
|
||||
Con_Printf("Heartbeats/min: %f\n", (s1->heartbeats-s2->heartbeats)/period);
|
||||
Con_Printf("Queries/min: %f\n", (s1->queries-s2->queries)/period);
|
||||
}
|
||||
|
||||
void SV_Init (struct quakeparms_s *parms)
|
||||
{
|
||||
int manarg;
|
||||
|
||||
COM_InitArgv (parms->argc, parms->argv);
|
||||
|
||||
host_parms = *parms;
|
||||
|
||||
Cvar_Init();
|
||||
|
||||
Memory_Init();
|
||||
|
||||
Sys_Init();
|
||||
|
||||
COM_ParsePlusSets(false);
|
||||
|
||||
Cbuf_Init ();
|
||||
Cmd_Init ();
|
||||
|
||||
NET_Init ();
|
||||
COM_Init ();
|
||||
|
||||
#ifdef WEBSERVER
|
||||
IWebInit();
|
||||
#endif
|
||||
|
||||
Cmd_AddCommand ("quit", SV_Quit_f);
|
||||
Cmd_AddCommand ("status", SVM_Status_f);
|
||||
|
||||
svm_sockets = FTENET_CreateCollection(true);
|
||||
Hash_InitTable(&svm.serverhash, 1024, Z_Malloc(Hash_BytesForBuckets(1024)));
|
||||
|
||||
Cvar_Register(&sv_masterport, "server control variables");
|
||||
Cvar_Register(&sv_masterport_tcp, "server control variables");
|
||||
Cvar_Register(&sv_heartbeattimeout, "server control variables");
|
||||
Cvar_Register(&sv_maxgames, "server control variables");
|
||||
Cvar_Register(&sv_maxservers, "server control variables");
|
||||
|
||||
Cvar_ParseWatches();
|
||||
host_initialized = true;
|
||||
|
||||
manarg = COM_CheckParm("-manifest");
|
||||
if (manarg && manarg < com_argc-1 && com_argv[manarg+1])
|
||||
{
|
||||
char *man = FS_MallocFile(com_argv[manarg+1], FS_SYSTEM, NULL);
|
||||
|
||||
FS_ChangeGame(FS_Manifest_Parse(NULL, man), true, true);
|
||||
BZ_Free(man);
|
||||
}
|
||||
else
|
||||
FS_ChangeGame(NULL, true, true);
|
||||
|
||||
Cmd_StuffCmds();
|
||||
Cbuf_Execute ();
|
||||
|
||||
Cvar_ForceCallback(&sv_masterport);
|
||||
Cvar_ForceCallback(&sv_masterport_tcp);
|
||||
|
||||
|
||||
Con_TPrintf ("Exe: %s %s\n", __DATE__, __TIME__);
|
||||
|
||||
Con_Printf ("%s\n", version_string());
|
||||
|
||||
Con_TPrintf ("======== %s Initialized ========\n", "FTEMaster");
|
||||
}
|
||||
float SV_Frame (void)
|
||||
{
|
||||
realtime = Sys_DoubleTime();
|
||||
while (1)
|
||||
{
|
||||
const char *cmd = Sys_ConsoleInput ();
|
||||
if (!cmd)
|
||||
break;
|
||||
Log_String(LOG_CONSOLE, cmd);
|
||||
Cbuf_AddText (cmd, RESTRICT_LOCAL);
|
||||
Cbuf_AddText ("\n", RESTRICT_LOCAL);
|
||||
}
|
||||
Cbuf_Execute ();
|
||||
|
||||
SVM_Think(sv_masterport.ival);
|
||||
|
||||
//record lots of info over multiple frames, for smoother stats info.
|
||||
svm.total.timestamp = realtime;
|
||||
if (svm.nextstamp < realtime)
|
||||
{
|
||||
svm.stamps[svm.stampring%countof(svm.stamps)] = svm.total;
|
||||
svm.stampring++;
|
||||
svm.nextstamp = realtime+60;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
#endif
|
|
@ -41,7 +41,7 @@ Con_Printf redirection
|
|||
=============================================================================
|
||||
*/
|
||||
|
||||
char sv_redirected_buf[8000];
|
||||
char sv_redirected_buf[countof(sv_redirected_buf)];
|
||||
|
||||
redirect_t sv_redirected;
|
||||
int sv_redirectedlang;
|
||||
|
@ -74,7 +74,7 @@ void SV_FlushRedirect (void)
|
|||
send[4] = A2C_PRINT;
|
||||
memcpy (send+5, sv_redirected_buf, strlen(sv_redirected_buf)+1);
|
||||
|
||||
NET_SendPacket (NS_SERVER, strlen(send)+1, send, &net_from);
|
||||
NET_SendPacket (svs.sockets, strlen(send)+1, send, &net_from);
|
||||
}
|
||||
#ifdef SUBSERVERS
|
||||
else if (sv_redirected == RD_MASTER)
|
||||
|
@ -145,208 +145,6 @@ void SV_EndRedirect (void)
|
|||
sv_redirected = RD_NONE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
Con_Printf
|
||||
|
||||
Handles cursor positioning, line wrapping, etc
|
||||
================
|
||||
*/
|
||||
#define MAXPRINTMSG 4096
|
||||
// FIXME: make a buffer size safe vsprintf?
|
||||
#ifdef SERVERONLY
|
||||
vfsfile_t *con_pipe;
|
||||
vfsfile_t *Con_POpen(char *conname)
|
||||
{
|
||||
if (!conname || !*conname)
|
||||
{
|
||||
if (con_pipe)
|
||||
VFS_CLOSE(con_pipe);
|
||||
con_pipe = VFSPIPE_Open(2, false);
|
||||
return con_pipe;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void Con_PrintFromThread (void *ctx, void *data, size_t a, size_t b)
|
||||
{
|
||||
Con_Printf("%s", (char*)data);
|
||||
BZ_Free(data);
|
||||
}
|
||||
void VARGS Con_Printf (const char *fmt, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char msg[MAXPRINTMSG];
|
||||
|
||||
va_start (argptr,fmt);
|
||||
vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
if (!Sys_IsMainThread())
|
||||
{
|
||||
COM_AddWork(WG_MAIN, Con_PrintFromThread, NULL, Z_StrDup(msg), 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// add to redirected message
|
||||
if (sv_redirected)
|
||||
{
|
||||
if (strlen (msg) + strlen(sv_redirected_buf) > sizeof(sv_redirected_buf) - 1)
|
||||
SV_FlushRedirect ();
|
||||
strcat (sv_redirected_buf, msg);
|
||||
if (sv_redirected != -1)
|
||||
return;
|
||||
}
|
||||
|
||||
Sys_Printf ("%s", msg); // also echo to debugging console
|
||||
Con_Log(msg); // log to console
|
||||
|
||||
if (con_pipe)
|
||||
VFS_PUTS(con_pipe, msg);
|
||||
}
|
||||
void Con_TPrintf (translation_t stringnum, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char msg[MAXPRINTMSG];
|
||||
const char *fmt;
|
||||
|
||||
if (!Sys_IsMainThread())
|
||||
{ //shouldn't be redirected anyway...
|
||||
fmt = langtext(stringnum,svs.language);
|
||||
va_start (argptr,stringnum);
|
||||
vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
|
||||
va_end (argptr);
|
||||
COM_AddWork(WG_MAIN, Con_PrintFromThread, NULL, Z_StrDup(msg), 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// add to redirected message
|
||||
if (sv_redirected)
|
||||
{
|
||||
fmt = langtext(stringnum,sv_redirectedlang);
|
||||
va_start (argptr,stringnum);
|
||||
vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
if (strlen (msg) + strlen(sv_redirected_buf) > sizeof(sv_redirected_buf) - 1)
|
||||
SV_FlushRedirect ();
|
||||
strcat (sv_redirected_buf, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
fmt = langtext(stringnum,svs.language);
|
||||
|
||||
va_start (argptr,stringnum);
|
||||
vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
Sys_Printf ("%s", msg); // also echo to debugging console
|
||||
Con_Log(msg); // log to console
|
||||
|
||||
if (con_pipe)
|
||||
VFS_PUTS(con_pipe, msg);
|
||||
}
|
||||
/*
|
||||
================
|
||||
Con_DPrintf
|
||||
|
||||
A Con_Printf that only shows up if the "developer" cvar is set
|
||||
================
|
||||
*/
|
||||
static void Con_DPrintFromThread (void *ctx, void *data, size_t a, size_t b)
|
||||
{
|
||||
Con_DLPrintf(a, "%s", (char*)data);
|
||||
BZ_Free(data);
|
||||
}
|
||||
void Con_DPrintf (const char *fmt, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char msg[MAXPRINTMSG];
|
||||
extern cvar_t log_developer;
|
||||
|
||||
if (!developer.value && !log_developer.value)
|
||||
return;
|
||||
|
||||
va_start (argptr,fmt);
|
||||
vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
if (!Sys_IsMainThread())
|
||||
{
|
||||
COM_AddWork(WG_MAIN, Con_DPrintFromThread, NULL, Z_StrDup(msg), 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// add to redirected message
|
||||
if (sv_redirected)
|
||||
{
|
||||
if (strlen (msg) + strlen(sv_redirected_buf) > sizeof(sv_redirected_buf) - 1)
|
||||
SV_FlushRedirect ();
|
||||
strcat (sv_redirected_buf, msg);
|
||||
if (sv_redirected != -1)
|
||||
return;
|
||||
}
|
||||
|
||||
if (developer.value)
|
||||
Sys_Printf ("%s", msg); // also echo to debugging console
|
||||
|
||||
if (log_developer.value)
|
||||
Con_Log(msg); // log to console
|
||||
}
|
||||
void Con_DLPrintf (int level, const char *fmt, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char msg[MAXPRINTMSG];
|
||||
extern cvar_t log_developer;
|
||||
|
||||
if (developer.ival < level && !log_developer.value)
|
||||
return;
|
||||
|
||||
va_start (argptr,fmt);
|
||||
vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
if (!Sys_IsMainThread())
|
||||
{
|
||||
COM_AddWork(WG_MAIN, Con_DPrintFromThread, NULL, Z_StrDup(msg), level, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// add to redirected message
|
||||
if (sv_redirected)
|
||||
{
|
||||
if (strlen (msg) + strlen(sv_redirected_buf) > sizeof(sv_redirected_buf) - 1)
|
||||
SV_FlushRedirect ();
|
||||
strcat (sv_redirected_buf, msg);
|
||||
if (sv_redirected != -1)
|
||||
return;
|
||||
}
|
||||
|
||||
if (developer.ival >= level)
|
||||
Sys_Printf ("%s", msg); // also echo to debugging console
|
||||
|
||||
if (log_developer.value)
|
||||
Con_Log(msg); // log to console
|
||||
}
|
||||
|
||||
//for spammed warnings, so they don't spam prints with every single frame/call. the timer arg should be a static local.
|
||||
void VARGS Con_ThrottlePrintf (float *timer, int developerlevel, const char *fmt, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char msg[MAXPRINTMSG];
|
||||
|
||||
va_start (argptr,fmt);
|
||||
vsnprintf (msg,sizeof(msg)-1, fmt,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
if (developerlevel)
|
||||
Con_DLPrintf (developerlevel, "%s", msg);
|
||||
else
|
||||
Con_Printf("%s", msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
|
@ -624,7 +422,7 @@ void VARGS SV_BroadcastTPrintf (int level, translation_t stringnum, ...)
|
|||
client_t *cl;
|
||||
int i;
|
||||
int oldlang=-1;
|
||||
const char *fmt = langtext(stringnum, oldlang=svs.language);
|
||||
const char *fmt = langtext(stringnum, oldlang=com_language);
|
||||
|
||||
va_start (argptr,stringnum);
|
||||
vsnprintf (string,sizeof(string)-1, fmt,argptr);
|
||||
|
|
|
@ -765,7 +765,7 @@ static int Sys_CheckChRoot(void)
|
|||
freeaddrinfo(info);
|
||||
}
|
||||
|
||||
#ifdef SQL
|
||||
#if defined(SQL) && defined(HAVE_SERVER)
|
||||
SQL_Available();
|
||||
#endif
|
||||
#ifdef HAVE_GNUTLS
|
||||
|
|
|
@ -194,7 +194,7 @@ qboolean SV_CheckRealIP(client_t *client, qboolean force)
|
|||
if (client->realip_status == 1)
|
||||
{
|
||||
msg = va("\xff\xff\xff\xff%c %i", A2A_PING, client->realip_ping);
|
||||
NET_SendPacket(NS_SERVER, strlen(msg), msg, &client->realip);
|
||||
NET_SendPacket(svs.sockets, strlen(msg), msg, &client->realip);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2566,7 +2566,7 @@ void VARGS OutofBandPrintf(netadr_t *where, char *fmt, ...)
|
|||
vsnprintf (send+5, sizeof(send)-5, fmt, argptr);
|
||||
va_end (argptr);
|
||||
|
||||
NET_SendPacket (NS_SERVER, strlen(send)+1, send, where);
|
||||
NET_SendPacket (svs.sockets, strlen(send)+1, send, where);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4980,9 +4980,9 @@ void Cmd_SetPos_f(void)
|
|||
if (!svprogfuncs)
|
||||
return;
|
||||
|
||||
if (Cmd_Argc() != 4)
|
||||
if (Cmd_Argc() != 4 && Cmd_Argc() != 7)
|
||||
{
|
||||
SV_ClientPrintf(host_client, PRINT_HIGH, "setpos %f %f %f\n", sv_player->v->origin[0], sv_player->v->origin[1], sv_player->v->origin[2]);
|
||||
SV_ClientPrintf(host_client, PRINT_HIGH, "setpos %f %f %f %f %f %f\n", sv_player->v->origin[0], sv_player->v->origin[1], sv_player->v->origin[2], sv_player->v->v_angle[0], sv_player->v->v_angle[1], sv_player->v->v_angle[2]);
|
||||
return;
|
||||
}
|
||||
SV_LogPlayer(host_client, "setpos cheat");
|
||||
|
@ -4999,6 +4999,14 @@ void Cmd_SetPos_f(void)
|
|||
sv_player->v->origin[1] = atof(Cmd_Argv(2));
|
||||
sv_player->v->origin[2] = atof(Cmd_Argv(3));
|
||||
World_LinkEdict (&sv.world, (wedict_t*)sv_player, false);
|
||||
|
||||
if (Cmd_Argc() > 4)
|
||||
{
|
||||
sv_player->v->angles[0] = atof(Cmd_Argv(4));
|
||||
sv_player->v->angles[1] = atof(Cmd_Argv(5));
|
||||
sv_player->v->angles[2] = atof(Cmd_Argv(6));
|
||||
sv_player->v->fixangle = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SV_SetUpClientEdict (client_t *cl, edict_t *ent)
|
||||
|
@ -7657,7 +7665,7 @@ void SV_ExecuteClientMessage (client_t *cl)
|
|||
vec3_t o;
|
||||
int checksumIndex;
|
||||
qbyte checksum, calculatedChecksum;
|
||||
int seq_hash, i;
|
||||
int seq_hash;
|
||||
|
||||
// calc ping time
|
||||
if (cl->frameunion.frames)
|
||||
|
@ -7694,14 +7702,9 @@ void SV_ExecuteClientMessage (client_t *cl)
|
|||
#ifdef warningmsg
|
||||
#pragma warningmsg("FIXME: make antilag optionally support non-player ents too")
|
||||
#endif
|
||||
for (i = 0; i < sv.allocated_client_slots; i++)
|
||||
{
|
||||
cl->laggedents[i].present = frame->playerpresent[i];
|
||||
if (cl->laggedents[i].present)
|
||||
VectorCopy(frame->playerpositions[i], cl->laggedents[i].laggedpos);
|
||||
}
|
||||
cl->laggedents_count = sv.allocated_client_slots;
|
||||
|
||||
memcpy(cl->laggedents, frame->laggedplayer, sizeof(*cl->laggedents)*cl->laggedents_count);
|
||||
cl->laggedents_time = frame->laggedtime;
|
||||
cl->laggedents_frac = !*sv_antilag_frac.string?1:sv_antilag_frac.value;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -649,12 +649,12 @@ static int SVQ3_PointContents(vec3_t pos, int entnum)
|
|||
mod = Q3G_GetCModel(es->s.modelindex);
|
||||
if (!mod)
|
||||
continue;
|
||||
World_TransformedTrace(mod, 0, 0, pos, pos, vec3_origin, vec3_origin, false, &tr, es->r.currentOrigin, es->r.currentAngles, 0xffffffff);
|
||||
World_TransformedTrace(mod, 0, NULL, pos, pos, vec3_origin, vec3_origin, false, &tr, es->r.currentOrigin, es->r.currentAngles, 0xffffffff);
|
||||
}
|
||||
else
|
||||
{
|
||||
mod = CM_TempBoxModel(es->r.mins, es->r.maxs);
|
||||
World_TransformedTrace(mod, 0, 0, pos, pos, vec3_origin, vec3_origin, false, &tr, es->r.currentOrigin, vec3_origin, 0xffffffff);
|
||||
World_TransformedTrace(mod, 0, NULL, pos, pos, vec3_origin, vec3_origin, false, &tr, es->r.currentOrigin, vec3_origin, 0xffffffff);
|
||||
}
|
||||
|
||||
cont |= tr.contents;
|
||||
|
@ -3405,7 +3405,7 @@ void SVQ3_DirectConnect(void) //Actually connect the client, use up a slot, and
|
|||
{
|
||||
Con_Printf("%s\n", reason);
|
||||
reason = va("\377\377\377\377print\n%s", reason);
|
||||
NET_SendPacket (NS_SERVER, strlen(reason), reason, &net_from);
|
||||
NET_SendPacket (svs.sockets, strlen(reason), reason, &net_from);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3422,7 +3422,7 @@ void SVQ3_DirectConnect(void) //Actually connect the client, use up a slot, and
|
|||
|
||||
cl->gamestatesequence = -1;
|
||||
|
||||
NET_SendPacket (NS_SERVER, 19, "\377\377\377\377connectResponse", &net_from);
|
||||
NET_SendPacket (svs.sockets, 19, "\377\377\377\377connectResponse", &net_from);
|
||||
|
||||
cl->frameunion.q3frames = BZ_Malloc(Q3UPDATE_BACKUP*sizeof(*cl->frameunion.q3frames));
|
||||
}
|
||||
|
|
|
@ -1241,7 +1241,7 @@ Handles selection or creation of a clipping hull, and offseting (and
|
|||
eventually rotation) of the end points
|
||||
==================
|
||||
*/
|
||||
static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hullnum, qboolean hitmodel, qboolean capsule, unsigned int hitcontentsmask) //hullnum overrides min/max for q1 style bsps
|
||||
static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, vec3_t eang, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int hullnum, qboolean hitmodel, qboolean capsule, unsigned int hitcontentsmask) //hullnum overrides min/max for q1 style bsps
|
||||
{
|
||||
trace_t trace;
|
||||
model_t *model;
|
||||
|
@ -1285,16 +1285,16 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v
|
|||
if (ent->v->solid == SOLID_PORTAL)
|
||||
{
|
||||
//solid_portal cares only about origins and as such has no mins/max
|
||||
World_TransformedTrace(model, 0, &framestate, start, end, vec3_origin, vec3_origin, capsule, &trace, eorg, ent->v->angles, hitcontentsmask);
|
||||
World_TransformedTrace(model, 0, &framestate, start, end, vec3_origin, vec3_origin, capsule, &trace, eorg, eang, hitcontentsmask);
|
||||
if (trace.startsolid) //portals should not block traces. this prevents infinite looping
|
||||
trace.startsolid = false;
|
||||
hitmodel = false;
|
||||
}
|
||||
else if (ent->v->solid != SOLID_BSP)
|
||||
{
|
||||
ent->v->angles[0]*=r_meshpitch.value; //carmack made bsp models rotate wrongly.
|
||||
World_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, ent->v->angles, hitcontentsmask);
|
||||
ent->v->angles[0]*=r_meshpitch.value;
|
||||
eang[0]*=r_meshpitch.value; //carmack made bsp models rotate wrongly.
|
||||
World_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, eang, hitcontentsmask);
|
||||
eang[0]*=r_meshpitch.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1315,7 +1315,7 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v
|
|||
}
|
||||
if (hitcontentsmask & forcedcontents)
|
||||
{
|
||||
World_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, ent->v->angles, ~0u);
|
||||
World_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, eang, ~0u);
|
||||
if (trace.contents)
|
||||
trace.contents = forcedcontents;
|
||||
}
|
||||
|
@ -1330,7 +1330,7 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v
|
|||
}
|
||||
}
|
||||
else
|
||||
World_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, ent->v->angles, hitcontentsmask);
|
||||
World_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, eang, hitcontentsmask);
|
||||
}
|
||||
|
||||
// if using hitmodel, we know it hit the bounding box, so try a proper trace now.
|
||||
|
@ -1342,7 +1342,7 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v
|
|||
if (model && model->funcs.NativeTrace && model->loadstate == MLS_LOADED)
|
||||
{
|
||||
//do the second trace, using the actual mesh.
|
||||
World_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, ent->v->angles, hitcontentsmask);
|
||||
World_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, eang, hitcontentsmask);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1687,9 +1687,9 @@ void WorldQ2_ClipMoveToEntities (world_t *w, moveclip_t *clip )
|
|||
angles = vec3_origin; // boxes don't rotate
|
||||
|
||||
if (touch->svflags & SVF_MONSTER)
|
||||
World_TransformedTrace (model, 0, 0, clip->start, clip->end, clip->mins2, clip->maxs2, false, &trace, touch->s.origin, angles, clip->hitcontentsmask);
|
||||
World_TransformedTrace (model, 0, NULL, clip->start, clip->end, clip->mins2, clip->maxs2, false, &trace, touch->s.origin, angles, clip->hitcontentsmask);
|
||||
else
|
||||
World_TransformedTrace (model, 0, 0, clip->start, clip->end, clip->mins, clip->maxs, false, &trace, touch->s.origin, angles, clip->hitcontentsmask);
|
||||
World_TransformedTrace (model, 0, NULL, clip->start, clip->end, clip->mins, clip->maxs, false, &trace, touch->s.origin, angles, clip->hitcontentsmask);
|
||||
|
||||
if (trace.allsolid || trace.startsolid ||
|
||||
trace.fraction < clip->trace.fraction)
|
||||
|
@ -1875,9 +1875,9 @@ static void World_ClipToEverything (world_t *w, moveclip_t *clip)
|
|||
}
|
||||
|
||||
if ((int)touch->v->flags & FL_MONSTER)
|
||||
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
|
||||
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
|
||||
else
|
||||
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
|
||||
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
|
||||
if (trace.allsolid || trace.startsolid ||
|
||||
trace.fraction < clip->trace.fraction)
|
||||
{
|
||||
|
@ -2034,9 +2034,9 @@ static void World_ClipToLinks (world_t *w, areagridlink_t *node, moveclip_t *cli
|
|||
}
|
||||
|
||||
if ((int)touch->v->flags & FL_MONSTER)
|
||||
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
|
||||
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
|
||||
else
|
||||
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
|
||||
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, touch->v->angles, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->capsule, clip->hitcontentsmask);
|
||||
|
||||
if (trace.fraction < clip->trace.fraction)
|
||||
{
|
||||
|
@ -2561,18 +2561,18 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
|
|||
if (type & MOVE_OTHERONLY)
|
||||
{
|
||||
wedict_t *other = WEDICT_NUM_UB(w->progs, *w->g.other);
|
||||
return World_ClipMoveToEntity (w, other, other->v->origin, start, mins, maxs, end, hullnum, type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);
|
||||
return World_ClipMoveToEntity (w, other, other->v->origin, other->v->angles, start, mins, maxs, end, hullnum, type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);
|
||||
}
|
||||
#ifndef NOLEGACY
|
||||
if ((type&MOVE_WORLDONLY) == MOVE_WORLDONLY)
|
||||
{ //for compat with DP
|
||||
wedict_t *other = w->edicts;
|
||||
return World_ClipMoveToEntity (w, other, other->v->origin, start, mins, maxs, end, hullnum, type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);
|
||||
return World_ClipMoveToEntity (w, other, other->v->origin, other->v->angles, start, mins, maxs, end, hullnum, type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);
|
||||
}
|
||||
#endif
|
||||
|
||||
// clip to world
|
||||
clip.trace = World_ClipMoveToEntity (w, w->edicts, w->edicts->v->origin, start, mins, maxs, end, hullnum, false, clip.capsule, clip.hitcontentsmask);
|
||||
clip.trace = World_ClipMoveToEntity (w, w->edicts, w->edicts->v->origin, w->edicts->v->angles, start, mins, maxs, end, hullnum, false, clip.capsule, clip.hitcontentsmask);
|
||||
|
||||
clip.start = start;
|
||||
clip.end = end;
|
||||
|
@ -2622,6 +2622,7 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
|
|||
w->lagents = svs.clients[passedict->entnum-1].laggedents;
|
||||
w->maxlagents = svs.clients[passedict->entnum-1].laggedents_count;
|
||||
w->lagentsfrac = svs.clients[passedict->entnum-1].laggedents_frac;
|
||||
w->lagentstime = svs.clients[passedict->entnum-1].laggedents_time;
|
||||
}
|
||||
else if (passedict->v->owner)
|
||||
{
|
||||
|
@ -2631,6 +2632,7 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
|
|||
w->lagents = svs.clients[passedict->v->owner-1].laggedents;
|
||||
w->maxlagents = svs.clients[passedict->v->owner-1].laggedents_count;
|
||||
w->lagentsfrac = svs.clients[passedict->v->owner-1].laggedents_frac;
|
||||
w->lagentstime = svs.clients[passedict->entnum-1].laggedents_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2640,7 +2642,8 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
|
|||
{
|
||||
trace_t trace;
|
||||
wedict_t *touch;
|
||||
vec3_t lp;
|
||||
vec3_t lp, la;
|
||||
int j;
|
||||
|
||||
#ifdef USEAREAGRID
|
||||
World_ClipToAllLinks (w, &clip);
|
||||
|
@ -2686,7 +2689,18 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
|
|||
continue;
|
||||
}
|
||||
|
||||
VectorInterpolate(touch->v->origin, w->lagentsfrac, w->lagents[i].laggedpos, lp);
|
||||
VectorInterpolate(touch->v->origin, w->lagentsfrac, w->lagents[i].origin, lp);
|
||||
//I hate working with angles
|
||||
VectorSubtract(w->lagents[i].angles, touch->v->angles, la);
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
la[j] = (360.0/65536) * ((int)(la[j]*(65536/360.0)) & 65535);
|
||||
if (la[j]<-180)
|
||||
la[j] += 360;
|
||||
if (la[j]>180)
|
||||
la[j] -= 360;
|
||||
}
|
||||
VectorMA(touch->v->angles, 1, la, la);
|
||||
|
||||
if (clip.boxmins[0] > lp[0]+touch->v->maxs[0]
|
||||
|| clip.boxmins[1] > lp[1]+touch->v->maxs[1]
|
||||
|
@ -2707,7 +2721,13 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
|
|||
continue; // don't clip against owner
|
||||
}
|
||||
|
||||
trace = World_ClipMoveToEntity (w, touch, lp, clip.start, clip.mins, clip.maxs, clip.end, clip.hullnum, clip.type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);
|
||||
if ((clip.type & MOVE_HITMODEL) && w->Event_Backdate)
|
||||
{
|
||||
w->Event_Backdate(w, touch, w->lagentstime);
|
||||
trace = World_ClipMoveToEntity (w, touch, lp, la, clip.start, clip.mins, clip.maxs, clip.end, clip.hullnum, clip.type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);
|
||||
}
|
||||
else
|
||||
trace = World_ClipMoveToEntity (w, touch, lp, la, clip.start, clip.mins, clip.maxs, clip.end, clip.hullnum, clip.type & MOVE_HITMODEL, clip.capsule, clip.hitcontentsmask);
|
||||
|
||||
if (trace.allsolid || trace.startsolid || trace.fraction < clip.trace.fraction)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue