Switch to using epoll on linux, because we can.
Rework q3bsp_mergedlightmaps as q3bsp_mergelightmaps. Now a boolean filling to the gpu's limit. Now also fills horizontally too. ftemaster now provides needpass info for sv_public 2 servers. fix (most?) ftemaster crashes. ftemaster now supports protocol name aliases (allowing for more friendly game names in its html). ftemaster now pings the servers from a different port. This should highlight/exclude servers that are unreachable for nat/firewall reasons. Fix memory leak from mvd recording. Servers should now cope better with ctrl-z and related fg/bg unix shell commands. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5638 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
62f2a380e1
commit
fe28099e68
30 changed files with 2126 additions and 1468 deletions
|
@ -976,24 +976,6 @@ readit:
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
CL_GetMessage
|
||||
|
||||
Handles recording and playback of demos, on top of NET_ code
|
||||
====================
|
||||
*/
|
||||
qboolean CL_GetMessage (void)
|
||||
{
|
||||
if (cls.demoplayback != DPB_NONE)
|
||||
return CL_GetDemoMessage ();
|
||||
|
||||
if (NET_GetPacket (cls.sockets, 0) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
CL_Stop_f
|
||||
|
|
|
@ -743,6 +743,12 @@ char *CL_TryingToConnect(void)
|
|||
return cls.servername;
|
||||
}
|
||||
|
||||
#ifdef NQPROT
|
||||
static void CL_NullReadPacket(void)
|
||||
{ //just drop it all
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_CheckForResend
|
||||
|
@ -972,9 +978,10 @@ void CL_CheckForResend (void)
|
|||
NET_AdrToString(data, sizeof(data), &connectinfo.adr);
|
||||
|
||||
/*eat up the server's packets, to clear any lingering loopback packets (like disconnect commands... yes this might cause packetloss for other clients)*/
|
||||
while(NET_GetPacket (svs.sockets, 0) >= 0)
|
||||
{
|
||||
}
|
||||
svs.sockets->ReadGamePacket = CL_NullReadPacket;
|
||||
NET_ReadPackets(svs.sockets);
|
||||
svs.sockets->ReadGamePacket = SV_ReadPacket;
|
||||
|
||||
net_message.packing = SZ_RAWBYTES;
|
||||
net_message.cursize = 0;
|
||||
MSG_BeginReading(net_message.prim);
|
||||
|
@ -3814,6 +3821,134 @@ void CLNQ_ConnectionlessPacket(void)
|
|||
|
||||
void CL_MVDUpdateSpectator (void);
|
||||
void CL_WriteDemoMessage (sizebuf_t *msg, int payloadoffset);
|
||||
|
||||
void CL_ReadPacket(void)
|
||||
{
|
||||
if (!qrenderer)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
if (*(int *)net_message.data != -1)
|
||||
if (NET_DTLS_Decode(cls.sockets))
|
||||
if (!net_message.cursize)
|
||||
return;
|
||||
#endif
|
||||
|
||||
#ifdef NQPROT
|
||||
if (cls.demoplayback == DPB_NETQUAKE)
|
||||
{
|
||||
MSG_BeginReading (cls.netchan.netprim);
|
||||
cls.netchan.last_received = realtime;
|
||||
CLNQ_ParseServerMessage ();
|
||||
|
||||
if (!cls.demoplayback)
|
||||
CL_NextDemo();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef Q2CLIENT
|
||||
if (cls.demoplayback == DPB_QUAKE2)
|
||||
{
|
||||
MSG_BeginReading (cls.netchan.netprim);
|
||||
cls.netchan.last_received = realtime;
|
||||
CLQ2_ParseServerMessage ();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
//
|
||||
// remote command packet
|
||||
//
|
||||
if (*(int *)net_message.data == -1)
|
||||
{
|
||||
CL_ConnectionlessPacket ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (net_message.cursize < 6 && (cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV)) //MVDs don't have the whole sequence header thing going on
|
||||
{
|
||||
char adr[MAX_ADR_SIZE];
|
||||
Con_TPrintf ("%s: Runt packet\n", NET_AdrToString(adr, sizeof(adr), &net_from));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls.state == ca_disconnected)
|
||||
{ //connect to nq servers, but don't get confused with sequenced packets.
|
||||
if (NET_WasSpecialPacket(cls.sockets))
|
||||
return;
|
||||
#ifdef NQPROT
|
||||
CLNQ_ConnectionlessPacket ();
|
||||
#endif
|
||||
return; //ignore it. We arn't connected.
|
||||
}
|
||||
|
||||
//
|
||||
// packet from server
|
||||
//
|
||||
if (!cls.demoplayback &&
|
||||
!NET_CompareAdr (&net_from, &cls.netchan.remote_address))
|
||||
{
|
||||
char adr[MAX_ADR_SIZE];
|
||||
if (NET_WasSpecialPacket(cls.sockets))
|
||||
return;
|
||||
Con_DPrintf ("%s:sequenced packet from wrong server\n"
|
||||
,NET_AdrToString(adr, sizeof(adr), &net_from));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls.netchan.pext_stunaware) //should be safe to do this here.
|
||||
if (NET_WasSpecialPacket(cls.sockets))
|
||||
return;
|
||||
|
||||
switch(cls.protocol)
|
||||
{
|
||||
case CP_NETQUAKE:
|
||||
#ifdef NQPROT
|
||||
if(NQNetChan_Process(&cls.netchan))
|
||||
{
|
||||
MSG_ChangePrimitives(cls.netchan.netprim);
|
||||
CL_WriteDemoMessage (&net_message, msg_readcount);
|
||||
CLNQ_ParseServerMessage ();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case CP_PLUGIN:
|
||||
break;
|
||||
case CP_QUAKE2:
|
||||
#ifdef Q2CLIENT
|
||||
if (!Netchan_Process(&cls.netchan))
|
||||
return; // wasn't accepted for some reason
|
||||
CLQ2_ParseServerMessage ();
|
||||
break;
|
||||
#endif
|
||||
case CP_QUAKE3:
|
||||
#ifdef Q3CLIENT
|
||||
CLQ3_ParseServerMessage();
|
||||
#endif
|
||||
break;
|
||||
case CP_QUAKEWORLD:
|
||||
if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
|
||||
{
|
||||
MSG_BeginReading(cls.netchan.netprim);
|
||||
cls.netchan.last_received = realtime;
|
||||
cls.netchan.outgoing_sequence = cls.netchan.incoming_sequence;
|
||||
}
|
||||
else if (!Netchan_Process(&cls.netchan))
|
||||
return; // wasn't accepted for some reason
|
||||
|
||||
CL_WriteDemoMessage (&net_message, msg_readcount);
|
||||
|
||||
if (cls.netchan.incoming_sequence > cls.netchan.outgoing_sequence)
|
||||
{ //server should not be responding to packets we have not sent yet
|
||||
Con_DPrintf("Server is from the future! (%i packets)\n", cls.netchan.incoming_sequence - cls.netchan.outgoing_sequence);
|
||||
cls.netchan.outgoing_sequence = cls.netchan.incoming_sequence;
|
||||
}
|
||||
MSG_ChangePrimitives(cls.netchan.netprim);
|
||||
CLQW_ParseServerMessage ();
|
||||
break;
|
||||
case CP_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
=================
|
||||
CL_ReadPackets
|
||||
|
@ -3821,145 +3956,14 @@ CL_ReadPackets
|
|||
*/
|
||||
void CL_ReadPackets (void)
|
||||
{
|
||||
char adr[MAX_ADR_SIZE];
|
||||
|
||||
if (!qrenderer)
|
||||
return;
|
||||
|
||||
// while (NET_GetPacket ())
|
||||
for(;;)
|
||||
if (cls.demoplayback != DPB_NONE)
|
||||
{
|
||||
if (!CL_GetMessage())
|
||||
#ifndef HAVE_DTLS
|
||||
break;
|
||||
#else
|
||||
{
|
||||
NET_DTLS_Timeouts(cls.sockets);
|
||||
break;
|
||||
}
|
||||
|
||||
if (*(int *)net_message.data != -1)
|
||||
if (NET_DTLS_Decode(cls.sockets))
|
||||
if (!net_message.cursize)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
#ifdef NQPROT
|
||||
if (cls.demoplayback == DPB_NETQUAKE)
|
||||
{
|
||||
MSG_BeginReading (cls.netchan.netprim);
|
||||
cls.netchan.last_received = realtime;
|
||||
CLNQ_ParseServerMessage ();
|
||||
|
||||
if (!cls.demoplayback)
|
||||
CL_NextDemo();
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#ifdef Q2CLIENT
|
||||
if (cls.demoplayback == DPB_QUAKE2)
|
||||
{
|
||||
MSG_BeginReading (cls.netchan.netprim);
|
||||
cls.netchan.last_received = realtime;
|
||||
CLQ2_ParseServerMessage ();
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
//
|
||||
// remote command packet
|
||||
//
|
||||
if (*(int *)net_message.data == -1)
|
||||
{
|
||||
CL_ConnectionlessPacket ();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (net_message.cursize < 6 && (cls.demoplayback != DPB_MVD && cls.demoplayback != DPB_EZTV)) //MVDs don't have the whole sequence header thing going on
|
||||
{
|
||||
Con_TPrintf ("%s: Runt packet\n", NET_AdrToString(adr, sizeof(adr), &net_from));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cls.state == ca_disconnected)
|
||||
{ //connect to nq servers, but don't get confused with sequenced packets.
|
||||
if (NET_WasSpecialPacket(cls.sockets))
|
||||
continue;
|
||||
#ifdef NQPROT
|
||||
CLNQ_ConnectionlessPacket ();
|
||||
#endif
|
||||
continue; //ignore it. We arn't connected.
|
||||
}
|
||||
|
||||
//
|
||||
// packet from server
|
||||
//
|
||||
if (!cls.demoplayback &&
|
||||
!NET_CompareAdr (&net_from, &cls.netchan.remote_address))
|
||||
{
|
||||
if (NET_WasSpecialPacket(cls.sockets))
|
||||
continue;
|
||||
Con_DPrintf ("%s:sequenced packet from wrong server\n"
|
||||
,NET_AdrToString(adr, sizeof(adr), &net_from));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cls.netchan.pext_stunaware) //should be safe to do this here.
|
||||
if (NET_WasSpecialPacket(cls.sockets))
|
||||
continue;
|
||||
|
||||
switch(cls.protocol)
|
||||
{
|
||||
case CP_NETQUAKE:
|
||||
#ifdef NQPROT
|
||||
if(NQNetChan_Process(&cls.netchan))
|
||||
{
|
||||
MSG_ChangePrimitives(cls.netchan.netprim);
|
||||
CL_WriteDemoMessage (&net_message, msg_readcount);
|
||||
CLNQ_ParseServerMessage ();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case CP_PLUGIN:
|
||||
break;
|
||||
case CP_QUAKE2:
|
||||
#ifdef Q2CLIENT
|
||||
if (!Netchan_Process(&cls.netchan))
|
||||
continue; // wasn't accepted for some reason
|
||||
CLQ2_ParseServerMessage ();
|
||||
break;
|
||||
#endif
|
||||
case CP_QUAKE3:
|
||||
#ifdef Q3CLIENT
|
||||
CLQ3_ParseServerMessage();
|
||||
#endif
|
||||
break;
|
||||
case CP_QUAKEWORLD:
|
||||
if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
|
||||
{
|
||||
MSG_BeginReading(cls.netchan.netprim);
|
||||
cls.netchan.last_received = realtime;
|
||||
cls.netchan.outgoing_sequence = cls.netchan.incoming_sequence;
|
||||
}
|
||||
else if (!Netchan_Process(&cls.netchan))
|
||||
continue; // wasn't accepted for some reason
|
||||
|
||||
CL_WriteDemoMessage (&net_message, msg_readcount);
|
||||
|
||||
if (cls.netchan.incoming_sequence > cls.netchan.outgoing_sequence)
|
||||
{ //server should not be responding to packets we have not sent yet
|
||||
Con_DPrintf("Server is from the future! (%i packets)\n", cls.netchan.incoming_sequence - cls.netchan.outgoing_sequence);
|
||||
cls.netchan.outgoing_sequence = cls.netchan.incoming_sequence;
|
||||
}
|
||||
MSG_ChangePrimitives(cls.netchan.netprim);
|
||||
CLQW_ParseServerMessage ();
|
||||
break;
|
||||
case CP_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
|
||||
// if (cls.demoplayback && cls.state >= ca_active && !CL_DemoBehind())
|
||||
// return;
|
||||
while(CL_GetDemoMessage())
|
||||
CL_ReadPacket();
|
||||
}
|
||||
else
|
||||
NET_ReadPackets(cls.sockets);
|
||||
NET_DTLS_Timeouts(cls.sockets);
|
||||
|
||||
//
|
||||
// check timeout
|
||||
|
|
|
@ -1246,6 +1246,7 @@ void CL_ClearState (qboolean gamestart);
|
|||
void CLQ2_ClearState(void);
|
||||
|
||||
void CL_ReadPackets (void);
|
||||
void CL_ReadPacket(void);
|
||||
|
||||
int CL_ReadFromServer (void);
|
||||
void CL_WriteToServer (usercmd_t *cmd);
|
||||
|
@ -1273,7 +1274,7 @@ int CL_RemoveClientCommands(char *command);
|
|||
// cl_demo.c
|
||||
//
|
||||
void CL_StopPlayback (void);
|
||||
qboolean CL_GetMessage (void);
|
||||
qboolean CL_GetDemoMessage (void);
|
||||
void CL_WriteDemoCmd (usercmd_t *pcmd);
|
||||
void CL_Demo_ClientCommand(char *commandtext); //for QTV.
|
||||
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
#include "shader.h"
|
||||
#include "glquake.h" //we need some of the gl format enums
|
||||
|
||||
//#define PURGEIMAGES //somewhat experimental still. we're still flushing more than we should.
|
||||
|
||||
#if defined(NPFTE) || defined(IMGTOOL)
|
||||
//#define Con_Printf(f, ...)
|
||||
//hope you're on a littleendian machine
|
||||
|
@ -29,6 +27,14 @@ cvar_t r_dodgytgafiles = CVARD("r_dodgytgafiles", "0", "Many old glquake engines
|
|||
cvar_t r_dodgypcxfiles = CVARD("r_dodgypcxfiles", "0", "When enabled, this will ignore the palette stored within pcx files, for compatibility with quake2.");
|
||||
#endif
|
||||
cvar_t r_dodgymiptex = CVARD("r_dodgymiptex", "1", "When enabled, this will force regeneration of mipmaps, discarding mips1-4 like glquake did. This may eg solve fullbright issues with some maps, but may reduce distant detail levels.");
|
||||
static void QDECL R_Image_BuggyCvar (struct cvar_s *var, char *oldvalue)
|
||||
{ //force these cvars to value 1 if they're empty.
|
||||
//cvars using this should be changed to 0 by default, once our engine bugs are debugged/fixed.
|
||||
if (!*var->string)
|
||||
var->ival = true;
|
||||
}
|
||||
cvar_t r_keepimages = CVARCD("r_keepimages", "", R_Image_BuggyCvar, "Retain unused images in memory for slightly faster map loading. FIXME: a setting of 0 may be crashy! (empty is treated as 1 for now)");
|
||||
cvar_t r_ignoremapprefixes = CVARCD("r_ignoremapprefixes", "", R_Image_BuggyCvar, "Ignores when textures were loaded from map-specific paths. FIXME: empty is currently interpretted as 1 because the alternative is too memory hungary with r_keepimages 1.");
|
||||
|
||||
char *r_defaultimageextensions =
|
||||
#ifdef IMAGEFMT_DDS
|
||||
|
@ -13181,9 +13187,7 @@ image_t *Image_FindTexture(const char *identifier, const char *subdir, unsigned
|
|||
{
|
||||
if (!((tex->flags ^ flags) & (IF_CLAMP|IF_PALETTIZE|IF_PREMULTIPLYALPHA)))
|
||||
{
|
||||
#ifdef PURGEIMAGES
|
||||
if (!strcmp(subdir, tex->subpath?tex->subpath:""))
|
||||
#endif
|
||||
if (r_ignoremapprefixes.ival || !strcmp(subdir, tex->subpath?tex->subpath:""))
|
||||
{
|
||||
tex->regsequence = r_regsequence;
|
||||
return tex;
|
||||
|
@ -13423,7 +13427,7 @@ void Image_Upload (texid_t tex, uploadfmt_t fmt, void *data, void *palette, in
|
|||
size_t i;
|
||||
|
||||
//skip if we're not actually changing the data/size/format.
|
||||
if (!data && tex->format == fmt && tex->width == width && tex->height == height && tex->depth == 1)
|
||||
if (!data && tex->format == fmt && tex->width == width && tex->height == height && tex->depth == 1 && tex->status == TEX_LOADED)
|
||||
return;
|
||||
|
||||
mips.extrafree = NULL;
|
||||
|
@ -13562,10 +13566,9 @@ void Image_DestroyTexture(image_t *tex)
|
|||
void Shader_TouchTextures(void);
|
||||
void Image_Purge(void)
|
||||
{
|
||||
#ifdef PURGEIMAGES
|
||||
image_t *tex, *a;
|
||||
int loaded = 0, total = 0;
|
||||
size_t mem = 0;
|
||||
image_t *tex;
|
||||
if (r_keepimages.ival)
|
||||
return;
|
||||
Shader_TouchTextures();
|
||||
for (tex = imagelist; tex; tex = tex->next)
|
||||
{
|
||||
|
@ -13574,7 +13577,6 @@ void Image_Purge(void)
|
|||
if (tex->regsequence != r_regsequence)
|
||||
Image_UnloadTexture(tex);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -13638,7 +13640,10 @@ void Image_List_f(void)
|
|||
imgmem = blockbytes * (tex->width+blockwidth-1)/blockwidth * (tex->height+blockheight-1)/blockheight;
|
||||
if (!(tex->flags & IF_NOMIPMAP))
|
||||
imgmem += imgmem/3; //mips take about a third extra mem.
|
||||
Con_Printf("^2loaded (%i*%i ^4%s^2, %3fKB->%3fKB)\n", tex->width, tex->height, Image_FormatName(tex->format), loc.len/(1024.0), imgmem/(1024.0));
|
||||
if (tex->depth != 1)
|
||||
Con_Printf("^2loaded (%i*%i*%i ^4%s^2, %3fKB->%3fKB)\n", tex->width, tex->height, tex->depth, Image_FormatName(tex->format), loc.len/(1024.0), imgmem/(1024.0));
|
||||
else
|
||||
Con_Printf("^2loaded (%i*%i ^4%s^2, %3fKB->%3fKB)\n", tex->width, tex->height, Image_FormatName(tex->format), loc.len/(1024.0), imgmem/(1024.0));
|
||||
if (tex->aliasof)
|
||||
{
|
||||
aliasedmem += imgmem;
|
||||
|
|
|
@ -167,7 +167,7 @@ static net_masterlist_t net_masterlist[] = {
|
|||
// {MP_QUAKEWORLD, CVARFC("net_qwmasterextraHistoric", "master.teamdamage.com:27000", CVAR_NOSAVE, Net_Masterlist_Callback), "master.teamdamage.com"},
|
||||
|
||||
//Total conversions will need to define their own in defaults.cfg or whatever.
|
||||
{MP_DPMASTER, CVARFC("net_masterextra1", "frag-net.com:27950 198.58.111.37:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara
|
||||
{MP_DPMASTER, CVARFC("net_masterextra1", "master.frag-net.com:27950 198.58.111.37:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Eukara
|
||||
// {MP_DPMASTER, CVARFC("net_masterextra1", ""/*"ghdigital.com:27950 207.55.114.154:27950"*/, CVAR_NOSAVE, Net_Masterlist_Callback)}, //(was 69.59.212.88) admin: LordHavoc
|
||||
{MP_DPMASTER, CVARFC("net_masterextra2", "dpmaster.deathmask.net:27950 107.161.23.68:27950 [2604:180::4ac:98c1]:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: Willis
|
||||
{MP_DPMASTER, CVARFC("net_masterextra3", "dpmaster.tchr.no:27950 92.62.40.73:27950", CVAR_NOSAVE, Net_Masterlist_Callback)}, //admin: tChr
|
||||
|
@ -2724,7 +2724,7 @@ void MasterInfo_Refresh(qboolean doreset)
|
|||
url = va("http://%s/raw/%s", fs_manifest->rtcbroker+6, com_token);
|
||||
else
|
||||
url = va("http://%s/raw/%s", fs_manifest->rtcbroker, com_token);
|
||||
Master_AddMasterHTTP(url, MT_MASTERHTTP, MP_QUAKEWORLD, "Public Servers Potentially Behind A NAT.");
|
||||
Master_AddMasterHTTP(url, MT_MASTERHTTP, MP_DPMASTER, "Public Servers Potentially Behind A NAT.");
|
||||
}
|
||||
|
||||
for (i = 0; net_masterlist[i].cv.name; i++)
|
||||
|
|
|
@ -327,6 +327,8 @@ cvar_t r_stereo_method = CVARFD("r_stereo_method", "0", CVAR_ARCHIVE, "Valu
|
|||
|
||||
extern cvar_t r_dodgytgafiles;
|
||||
extern cvar_t r_dodgypcxfiles;
|
||||
extern cvar_t r_keepimages;
|
||||
extern cvar_t r_ignoremapprefixes;
|
||||
extern cvar_t r_dodgymiptex;
|
||||
extern char *r_defaultimageextensions;
|
||||
extern cvar_t r_imageextensions;
|
||||
|
@ -865,6 +867,8 @@ void Renderer_Init(void)
|
|||
Cmd_AddCommand("sky", R_ForceSky_f); //QS compat
|
||||
Cmd_AddCommand("loadsky", R_ForceSky_f);//DP compat
|
||||
|
||||
Cvar_Register(&r_keepimages, GRAPHICALNICETIES);
|
||||
Cvar_Register(&r_ignoremapprefixes, GRAPHICALNICETIES);
|
||||
#ifdef IMAGEFMT_TGA
|
||||
Cvar_Register(&r_dodgytgafiles, "Hacky bug workarounds");
|
||||
#endif
|
||||
|
|
|
@ -885,7 +885,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
|
|||
else //we don't want to play anything more.
|
||||
break;
|
||||
if (!queuedbufs)
|
||||
{ //queue 0.1 secs if we're starting/resetting a new stream this is to try to cover up discintinuities caused by packetloss or whatever
|
||||
{ //queue 0.1 secs if we're starting/resetting a new stream this is to try to cover up discontinuities caused by packetloss or whatever
|
||||
sfxcache_t silence;
|
||||
silence.speed = snd_speed;
|
||||
silence.width = 2;
|
||||
|
|
|
@ -3447,7 +3447,6 @@ static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, mod
|
|||
else
|
||||
{
|
||||
Q1MDL_LoadPose(galias, pq1inmodel, verts, normals, svec, tvec, pinframe, seamremaps, mdltype, bbox);
|
||||
pinframe += pq1inmodel->numverts;
|
||||
|
||||
#ifdef _DEBUG
|
||||
if ((bbox[3] > frameinfo->bboxmax.v[0] || bbox[4] > frameinfo->bboxmax.v[1] || bbox[5] > frameinfo->bboxmax.v[2] ||
|
||||
|
@ -3461,6 +3460,7 @@ static void *Q1MDL_LoadFrameGroup (galiasinfo_t *galias, dmdl_t *pq1inmodel, mod
|
|||
Con_DPrintf(CON_WARNING"%s has incorrect frame bounds\n", loadmodel->name);
|
||||
galias->warned = true;
|
||||
}
|
||||
pinframe += pq1inmodel->numverts;
|
||||
}
|
||||
|
||||
#ifndef SERVERONLY
|
||||
|
|
|
@ -528,6 +528,7 @@ struct vfsfile_s;
|
|||
#define FSLF_DONTREFERENCE (1u<<5) //don't add any reference flags to packages
|
||||
#define FSLF_IGNOREPURE (1u<<6) //use only the client's package list, ignore any lists obtained from the server (including any reordering)
|
||||
#define FSLF_IGNORELINKS (1u<<7) //ignore any pak/pk3 symlinks. system ones may still be followed.
|
||||
#define FSLF_QUIET (1u<<8) //don't spam warnings about any dodgy paths.
|
||||
|
||||
//if loc is valid, loc->search is always filled in, the others are filled on success.
|
||||
//standard return value is 0 on failure, or depth on success.
|
||||
|
|
|
@ -193,7 +193,7 @@ int fs_hash_files;
|
|||
|
||||
|
||||
|
||||
const char *FS_GetCleanPath(const char *pattern, char *outbuf, int outlen);
|
||||
static const char *FS_GetCleanPath(const char *pattern, qboolean silent, char *outbuf, int outlen);
|
||||
void FS_RegisterDefaultFileSystems(void);
|
||||
static void COM_CreatePath (char *path);
|
||||
ftemanifest_t *FS_ReadDefaultManifest(char *newbasedir, size_t newbasedirsize, qboolean fixedbasedir);
|
||||
|
@ -1355,7 +1355,7 @@ int FS_FLocateFile(const char *filename, unsigned int lflags, flocation_t *loc)
|
|||
loc->search = NULL;
|
||||
loc->len = -1;
|
||||
|
||||
filename = FS_GetCleanPath(filename, cleanpath, sizeof(cleanpath));
|
||||
filename = FS_GetCleanPath(filename, (lflags&FSLF_QUIET), cleanpath, sizeof(cleanpath));
|
||||
if (!filename)
|
||||
{
|
||||
pf = NULL;
|
||||
|
@ -1703,7 +1703,7 @@ void FS_ReferenceControl(unsigned int refflag, unsigned int resetflags)
|
|||
}
|
||||
|
||||
//outbuf might not be written into
|
||||
const char *FS_GetCleanPath(const char *pattern, char *outbuf, int outlen)
|
||||
static const char *FS_GetCleanPath(const char *pattern, qboolean silent, char *outbuf, int outlen)
|
||||
{
|
||||
const char *s;
|
||||
char *o;
|
||||
|
@ -1870,7 +1870,7 @@ qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out
|
|||
}
|
||||
else
|
||||
{
|
||||
fname = FS_GetCleanPath(fname, cleanname, sizeof(cleanname));
|
||||
fname = FS_GetCleanPath(fname, false, cleanname, sizeof(cleanname));
|
||||
if (!fname)
|
||||
return false;
|
||||
}
|
||||
|
@ -1986,7 +1986,7 @@ vfsfile_t *FS_OpenWithFriends(const char *fname, char *sysname, size_t sysnamesi
|
|||
int i;
|
||||
char cleanname[MAX_QPATH];
|
||||
|
||||
fname = FS_GetCleanPath(fname, cleanname, sizeof(cleanname));
|
||||
fname = FS_GetCleanPath(fname, false, cleanname, sizeof(cleanname));
|
||||
if (!fname)
|
||||
return NULL;
|
||||
|
||||
|
@ -2070,7 +2070,7 @@ vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_rela
|
|||
|
||||
//blanket-bans
|
||||
|
||||
filename = FS_GetCleanPath(filename, cleanname, sizeof(cleanname));
|
||||
filename = FS_GetCleanPath(filename, false, cleanname, sizeof(cleanname));
|
||||
if (!filename)
|
||||
return NULL;
|
||||
|
||||
|
@ -3230,7 +3230,6 @@ static void FS_ExtractDir(char *in, char *out, int outlen)
|
|||
|
||||
qboolean FS_PathURLCache(const char *url, char *path, size_t pathsize)
|
||||
{
|
||||
const char *FS_GetCleanPath(const char *pattern, char *outbuf, int outlen);
|
||||
char tmp[MAX_QPATH];
|
||||
char *o = tmp;
|
||||
const char *i = url;
|
||||
|
@ -3259,7 +3258,7 @@ qboolean FS_PathURLCache(const char *url, char *path, size_t pathsize)
|
|||
}
|
||||
*o = 0;
|
||||
|
||||
if (!FS_GetCleanPath(tmp, path, pathsize))
|
||||
if (!FS_GetCleanPath(tmp, false, path, pathsize))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -6193,7 +6192,7 @@ static int QDECL FS_EnumerateFMFs(const char *fname, qofs_t fsize, time_t mtime,
|
|||
return true;
|
||||
}
|
||||
|
||||
//callback must call FS_Manifest_Free.
|
||||
//callback must call FS_Manifest_Free or return false.
|
||||
int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), void *usr)
|
||||
{
|
||||
int i;
|
||||
|
|
|
@ -45,7 +45,7 @@ typedef struct dvpkfile_s
|
|||
unsigned char archiveindex[2];
|
||||
unsigned char archiveoffset[4];
|
||||
unsigned char archivesize[4];
|
||||
unsigned char sentinal[2];//=0xffff;
|
||||
unsigned char sentinel[2];//=0xffff;
|
||||
} dvpkfile_t;
|
||||
|
||||
typedef struct
|
||||
|
@ -362,8 +362,8 @@ static unsigned int FSVPK_WalkTree(vpk_t *vpk, const char *start, const char *en
|
|||
start += sizeof(*file)+preloadsize;
|
||||
if (start > end)
|
||||
return 0; //truncated...
|
||||
if (file->sentinal[0] != 0xff || file->sentinal[1] != 0xff)
|
||||
return 0; //sentinal failure
|
||||
if (file->sentinel[0] != 0xff || file->sentinel[1] != 0xff)
|
||||
return 0; //sentinel failure
|
||||
// Con_Printf("Found file %s%s%s%s%s\n", path, *path?"/":"", name, *ext?".":"", ext);
|
||||
if (!vpk)
|
||||
files++;
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
//#define Q3SURF_DUST 0x00040000
|
||||
cvar_t q3bsp_surf_meshcollision_flag = CVARD("q3bsp_surf_meshcollision_flag", "0x80000000", "The surfaceparm flag(s) that enables q3bsp trisoup collision");
|
||||
cvar_t q3bsp_surf_meshcollision_force = CVARD("q3bsp_surf_meshcollision_force", "0", "Force mesh-based collisions on all q3bsp trisoup surfaces.");
|
||||
cvar_t q3bsp_mergeq3lightmaps = CVARD("q3bsp_mergedlightmaps", "16", "Specifies the maximum number of lightmaps that may be merged for performance reasons. Unfortunately this breaks tcgen on lightmap passes - if you care, set this to 1.");
|
||||
cvar_t q3bsp_mergeq3lightmaps = CVARD("q3bsp_mergelightmaps", "1", "Specifies whether to merge lightmaps into atlases in order to boost performance. Unfortunately this breaks tcgen on lightmap passes - if you care, set this to 0.");
|
||||
cvar_t q3bsp_bihtraces = CVARFD("_q3bsp_bihtraces", "0", CVAR_RENDERERLATCH, "Uses runtime-generated bih collision culling for faster traces.");
|
||||
|
||||
#if Q3SURF_NODRAW != TI_NODRAW
|
||||
|
@ -3787,12 +3787,16 @@ static void CModQ3_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l)
|
|||
qbyte *in = mod_base + l->fileofs;
|
||||
qbyte *out;
|
||||
unsigned int samples = l->filelen;
|
||||
int m, s;
|
||||
int mapsize = loadmodel->lightmaps.width*loadmodel->lightmaps.height*3;
|
||||
int m, s, t;
|
||||
int mapstride = loadmodel->lightmaps.width*3;
|
||||
int mapsize = mapstride*loadmodel->lightmaps.height;
|
||||
int maps;
|
||||
int merge;
|
||||
int mergestride;
|
||||
|
||||
extern cvar_t gl_overbright;
|
||||
extern qbyte lmgamma[256];
|
||||
float scale = (1<<(2-gl_overbright.ival));
|
||||
|
||||
loadmodel->lightmaps.fmt = LM_L8;
|
||||
|
||||
//round up the samples, in case the last one is partial.
|
||||
|
@ -3803,7 +3807,8 @@ static void CModQ3_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l)
|
|||
gl_overbright.flags |= CVAR_RENDERERLATCH;
|
||||
BuildLightMapGammaTable(1, (1<<(2-gl_overbright.ival)));
|
||||
|
||||
loadmodel->lightmaps.merge = 0;
|
||||
loadmodel->lightmaps.mergew = 0;
|
||||
loadmodel->lightmaps.mergeh = 0;
|
||||
|
||||
loadmodel->engineflags |= MDLF_NEEDOVERBRIGHT;
|
||||
|
||||
|
@ -3816,11 +3821,28 @@ static void CModQ3_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l)
|
|||
maps /= 2;
|
||||
|
||||
{
|
||||
int limit = min(sh_config.texture2d_maxsize / loadmodel->lightmaps.height, q3bsp_mergeq3lightmaps.ival);
|
||||
loadmodel->lightmaps.merge = 1;
|
||||
while (loadmodel->lightmaps.merge*2 <= limit && loadmodel->lightmaps.merge < maps)
|
||||
loadmodel->lightmaps.merge *= 2;
|
||||
int limitw = sh_config.texture2d_maxsize / loadmodel->lightmaps.width;
|
||||
int limith = sh_config.texture2d_maxsize / loadmodel->lightmaps.height;
|
||||
if (!q3bsp_mergeq3lightmaps.ival)
|
||||
{
|
||||
limitw = 1;
|
||||
limith = 1;
|
||||
}
|
||||
loadmodel->lightmaps.mergeh = loadmodel->lightmaps.mergew = 1;
|
||||
while (loadmodel->lightmaps.mergew*loadmodel->lightmaps.mergeh < maps)
|
||||
{ //this could probably be smarter.
|
||||
if (loadmodel->lightmaps.mergew*2 <= limitw && loadmodel->lightmaps.mergew < loadmodel->lightmaps.mergeh)
|
||||
loadmodel->lightmaps.mergew *= 2;
|
||||
else if (loadmodel->lightmaps.mergeh*2 <= limith)
|
||||
loadmodel->lightmaps.mergeh *= 2;
|
||||
else if (loadmodel->lightmaps.mergew*2 <= limitw)
|
||||
loadmodel->lightmaps.mergew *= 2;
|
||||
else
|
||||
break; //can't expand in either direction.
|
||||
}
|
||||
}
|
||||
merge = loadmodel->lightmaps.mergew*loadmodel->lightmaps.mergeh;
|
||||
mergestride = loadmodel->lightmaps.mergew*mapstride;
|
||||
|
||||
//q3bsp itself does not support deluxemapping.
|
||||
//the way it works is by interleaving the data in lightmap+deluxemap pairs.
|
||||
|
@ -3833,16 +3855,19 @@ static void CModQ3_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l)
|
|||
//if we have deluxemapping data then we split it here. beware externals.
|
||||
if (loadmodel->lightmaps.deluxemapping)
|
||||
{
|
||||
m = loadmodel->lightmaps.merge;
|
||||
m = merge;
|
||||
while (m < maps)
|
||||
m += loadmodel->lightmaps.merge;
|
||||
m += merge;
|
||||
loadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, mapsize*m*2);
|
||||
loadmodel->lightdatasize = mapsize*m*2;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadmodel->lightdatasize = samples;
|
||||
loadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, samples);
|
||||
m = merge;
|
||||
while (m < maps)
|
||||
m += merge;
|
||||
loadmodel->lightdata = ZG_Malloc(&loadmodel->memgroup, mapsize*m);
|
||||
loadmodel->lightdatasize = mapsize*m;
|
||||
}
|
||||
|
||||
if (!loadmodel->lightdata)
|
||||
|
@ -3854,57 +3879,65 @@ static void CModQ3_LoadLighting (model_t *loadmodel, qbyte *mod_base, lump_t *l)
|
|||
{
|
||||
out = loadmodel->lightdata;
|
||||
//figure out which merged lightmap we're putting it into
|
||||
out += (m/loadmodel->lightmaps.merge)*loadmodel->lightmaps.merge*mapsize * (loadmodel->lightmaps.deluxemapping?2:1);
|
||||
out += (m/merge)*merge*mapsize * (loadmodel->lightmaps.deluxemapping?2:1);
|
||||
//and the submap
|
||||
out += (m%loadmodel->lightmaps.merge)*mapsize;
|
||||
s = m%merge;
|
||||
t = s/loadmodel->lightmaps.mergew;
|
||||
s = s%loadmodel->lightmaps.mergew;
|
||||
out += s*mapstride;
|
||||
out += t*mergestride*loadmodel->lightmaps.height;
|
||||
|
||||
#if 1
|
||||
//q3bsp has 4-fold overbrights, so if we're not using overbrights then we basically need to scale the values up by 4
|
||||
//this will require clamping, which can result in oversaturation of channels, meaning discolouration
|
||||
for(s = 0; s < mapsize; )
|
||||
for (t = 0; t < loadmodel->lightmaps.height; t++)
|
||||
{
|
||||
float scale = (1<<(2-gl_overbright.ival));
|
||||
float i;
|
||||
vec3_t l;
|
||||
l[0] = *in++;
|
||||
l[1] = *in++;
|
||||
l[2] = *in++;
|
||||
VectorScale(l, scale, l); //it should be noted that this maths is wrong if you're trying to use srgb lightmaps.
|
||||
i = max(l[0], max(l[1], l[2]));
|
||||
if (i > 255)
|
||||
VectorScale(l, 255/i, l); //clamp the brightest channel, scaling the others down to retain chromiance.
|
||||
out[s++] = l[0];
|
||||
out[s++] = l[1];
|
||||
out[s++] = l[2];
|
||||
for (s = 0; s < loadmodel->lightmaps.width; s++)
|
||||
{
|
||||
float i;
|
||||
vec3_t l;
|
||||
l[0] = *in++;
|
||||
l[1] = *in++;
|
||||
l[2] = *in++;
|
||||
VectorScale(l, scale, l); //it should be noted that this maths is wrong if you're trying to use srgb lightmaps.
|
||||
i = max(l[0], max(l[1], l[2]));
|
||||
if (i > 255)
|
||||
VectorScale(l, 255/i, l); //clamp the brightest channel, scaling the others down to retain chromiance.
|
||||
*out++ = l[0];
|
||||
*out++ = l[1];
|
||||
*out++ = l[2];
|
||||
}
|
||||
out += mergestride-mapstride;
|
||||
}
|
||||
#else
|
||||
for(s = 0; s < mapsize; s++)
|
||||
out[s] = lmgamma[*in++];
|
||||
#endif
|
||||
|
||||
if (r_lightmap_saturation.value != 1.0f)
|
||||
SaturateR8G8B8(out, mapsize, r_lightmap_saturation.value);
|
||||
|
||||
if (loadmodel->lightmaps.deluxemapping)
|
||||
{
|
||||
out+= loadmodel->lightmaps.merge*mapsize;
|
||||
out -= mergestride*loadmodel->lightmaps.height;
|
||||
out += merge*mapsize;
|
||||
|
||||
//no gamma for deluxemap
|
||||
for(s = 0; s < mapsize; s+=3)
|
||||
for (t = 0; t < loadmodel->lightmaps.height; t++)
|
||||
{
|
||||
*out++ = in[0];
|
||||
*out++ = in[1];
|
||||
*out++ = in[2];
|
||||
in += 3;
|
||||
for (s = 0; s < loadmodel->lightmaps.width; s++)
|
||||
{
|
||||
*out++ = in[0];
|
||||
*out++ = in[1];
|
||||
*out++ = in[2];
|
||||
in += 3;
|
||||
}
|
||||
out += mergestride-mapstride;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*for (; m%loadmodel->lightmaps.merge; m++)
|
||||
/*for (; m%merge; m++)
|
||||
{
|
||||
out = loadmodel->lightdata;
|
||||
//figure out which merged lightmap we're putting it into
|
||||
out += (m/loadmodel->lightmaps.merge)*loadmodel->lightmaps.merge*mapsize * (loadmodel->lightmaps.deluxemapping?2:1);
|
||||
out += (m/merge)*merge*mapsize * (loadmodel->lightmaps.deluxemapping?2:1);
|
||||
//and the submap
|
||||
out += (m%loadmodel->lightmaps.merge)*mapsize;
|
||||
out += (m%merge)*mapsize;
|
||||
|
||||
for(s = 0; s < mapsize; s+=3)
|
||||
{
|
||||
|
|
|
@ -139,7 +139,7 @@ void UDP_CloseSocket (int socket);
|
|||
void NET_Shutdown (void);
|
||||
qboolean NET_GetRates(struct ftenet_connections_s *collection, float *pi, float *po, float *bi, float *bo);
|
||||
qboolean NET_UpdateRates(struct ftenet_connections_s *collection, qboolean inbound, size_t size); //for demos to not be weird
|
||||
int NET_GetPacket (struct ftenet_connections_s *col, int firstsock);
|
||||
void NET_ReadPackets (struct ftenet_connections_s *collection);
|
||||
neterr_t NET_SendPacket (struct ftenet_connections_s *col, int length, const void *data, netadr_t *to);
|
||||
int NET_LocalAddressForRemote(struct ftenet_connections_s *collection, netadr_t *remote, netadr_t *local, int idx);
|
||||
void NET_PrintAddresses(struct ftenet_connections_s *collection);
|
||||
|
|
|
@ -17,7 +17,7 @@ typedef struct
|
|||
#include "zlib.h"
|
||||
#endif
|
||||
#ifdef SUPPORT_ICE
|
||||
cvar_t net_ice_exchangeprivateips = CVARD("net_ice_exchangeprivateips", "", "Boolean. When set to 0, hides private IP addresses from your peers. Only addresses determined from the other side of your router will be shared. Setting it to 0 may be desirable but it can cause connections to fail when your router does not support hairpinning, whereas 1 fixes that at the cost of exposing private IP addresses.");
|
||||
cvar_t net_ice_exchangeprivateips = CVARFD("net_ice_exchangeprivateips", "", CVAR_NOTFROMSERVER, "Boolean. When set to 0, hides private IP addresses from your peers. Only addresses determined from the other side of your router will be shared. Setting it to 0 may be desirable but it can cause connections to fail when your router does not support hairpinning, whereas 1 fixes that at the cost of exposing private IP addresses.");
|
||||
/*
|
||||
Interactive Connectivity Establishment (rfc 5245)
|
||||
find out your peer's potential ports.
|
||||
|
@ -280,14 +280,20 @@ static struct icestate_s *QDECL ICE_Create(void *module, const char *conname, co
|
|||
case ICEP_VIDEO:
|
||||
collection = cls.sockets;
|
||||
if (!collection)
|
||||
{
|
||||
NET_InitClient(false);
|
||||
collection = cls.sockets;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifndef SERVERONLY
|
||||
case ICEP_QWCLIENT:
|
||||
collection = cls.sockets;
|
||||
if (!collection)
|
||||
{
|
||||
NET_InitClient(false);
|
||||
collection = cls.sockets;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifndef CLIENTONLY
|
||||
|
@ -296,6 +302,8 @@ static struct icestate_s *QDECL ICE_Create(void *module, const char *conname, co
|
|||
break;
|
||||
#endif
|
||||
}
|
||||
if (!collection)
|
||||
return NULL; //not initable or something
|
||||
|
||||
if (!conname)
|
||||
{
|
||||
|
@ -319,13 +327,6 @@ static struct icestate_s *QDECL ICE_Create(void *module, const char *conname, co
|
|||
|
||||
con->mode = mode;
|
||||
|
||||
if (!collection)
|
||||
{
|
||||
con->connections = collection = FTENET_CreateCollection(true);
|
||||
FTENET_AddToCollection(collection, "UDP", "0", NA_IP, NP_DGRAM);
|
||||
FTENET_AddToCollection(collection, "natpmp", "natpmp://5351", NA_IP, NP_NATPMP);
|
||||
}
|
||||
|
||||
con->next = icelist;
|
||||
icelist = con;
|
||||
|
||||
|
@ -1855,6 +1856,7 @@ static void FTENET_ICE_Heartbeat(ftenet_ice_connection_t *b)
|
|||
Info_SetValueForKey(info, "hostname", hostname.string, sizeof(info));
|
||||
Info_SetValueForKey(info, "modname", FS_GetGamedir(true), sizeof(info));
|
||||
Info_SetValueForKey(info, "mapname", InfoBuf_ValueForKey(&svs.info, "map"), sizeof(info));
|
||||
Info_SetValueForKey(info, "needpass", InfoBuf_ValueForKey(&svs.info, "needpass"), sizeof(info));
|
||||
|
||||
FTENET_ICE_SplurgeCmd(b, ICEMSG_SERVERINFO, -1, info);
|
||||
}
|
||||
|
@ -1904,7 +1906,7 @@ static qboolean FTENET_ICE_GetPacket(ftenet_generic_connection_t *gcon)
|
|||
if (b->timeout > realtime)
|
||||
return false;
|
||||
b->generic.thesocket = TCP_OpenStream(&b->brokeradr); //save this for select.
|
||||
b->broker = FS_OpenTCPSocket(b->generic.thesocket, true, b->brokername);
|
||||
b->broker = FS_WrapTCPSocket(b->generic.thesocket, true, b->brokername);
|
||||
|
||||
#ifdef HAVE_SSL
|
||||
//convert to tls...
|
||||
|
@ -2188,7 +2190,7 @@ static qboolean FTENET_ICE_ChangeLocalAddress(struct ftenet_generic_connection_s
|
|||
return true;
|
||||
}
|
||||
|
||||
ftenet_generic_connection_t *FTENET_ICE_EstablishConnection(qboolean isserver, const char *address, netadr_t adr)
|
||||
ftenet_generic_connection_t *FTENET_ICE_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr)
|
||||
{
|
||||
ftenet_ice_connection_t *newcon;
|
||||
const char *path;
|
||||
|
@ -2246,7 +2248,7 @@ ftenet_generic_connection_t *FTENET_ICE_EstablishConnection(qboolean isserver, c
|
|||
newcon->generic.GetLocalAddresses = FTENET_ICE_GetLocalAddresses;
|
||||
newcon->generic.ChangeLocalAddress = FTENET_ICE_ChangeLocalAddress;
|
||||
|
||||
newcon->generic.islisten = isserver;
|
||||
newcon->generic.islisten = col->islisten;
|
||||
|
||||
return &newcon->generic;
|
||||
}
|
||||
|
|
|
@ -442,15 +442,16 @@ static void SSL_Close(vfsfile_t *vfs)
|
|||
qgnutls_deinit(file->session);
|
||||
file->session = NULL;
|
||||
}
|
||||
}
|
||||
static qboolean QDECL SSL_CloseFile(vfsfile_t *vfs)
|
||||
{
|
||||
gnutlsfile_t *file = (void*)vfs;
|
||||
SSL_Close(vfs);
|
||||
if (file->stream)
|
||||
{
|
||||
VFS_CLOSE(file->stream);
|
||||
file->stream = NULL;
|
||||
}
|
||||
}
|
||||
static qboolean QDECL SSL_CloseFile(vfsfile_t *vfs)
|
||||
{
|
||||
SSL_Close(vfs);
|
||||
Z_Free(vfs);
|
||||
return true;
|
||||
}
|
||||
|
@ -617,14 +618,15 @@ static int QDECL SSL_CheckCert(gnutls_session_t session)
|
|||
}
|
||||
|
||||
//return 1 to read data.
|
||||
//-1 or 0 for error or not ready
|
||||
//-1 for error
|
||||
//0 for not ready
|
||||
static int SSL_DoHandshake(gnutlsfile_t *file)
|
||||
{
|
||||
int err;
|
||||
//session was previously closed = error
|
||||
if (!file->session)
|
||||
{
|
||||
Sys_Printf("null session\n");
|
||||
//Sys_Printf("null session\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -710,7 +712,7 @@ static int QDECL SSL_Write(struct vfsfile_s *f, const void *buffer, int bytestow
|
|||
return 0;
|
||||
else
|
||||
{
|
||||
Con_Printf("TLS Send Warning %i (%i bytes)\n", written, bytestowrite);
|
||||
Con_DPrintf("TLS Send Error %i (%i bytes)\n", written, bytestowrite);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -740,14 +742,11 @@ static ssize_t SSL_Push(gnutls_transport_ptr_t p, const void *data, size_t size)
|
|||
gnutlsfile_t *file = p;
|
||||
// Sys_Printf("SSL_Push: %u\n", size);
|
||||
int done = VFS_WRITE(file->stream, data, size);
|
||||
if (!done)
|
||||
if (done <= 0)
|
||||
{
|
||||
qgnutls_transport_set_errno(file->session, EAGAIN);
|
||||
qgnutls_transport_set_errno(file->session, (done==0)?EAGAIN:ECONNRESET);
|
||||
return -1;
|
||||
}
|
||||
qgnutls_transport_set_errno(file->session, done<0?errno:0);
|
||||
// if (done < 0)
|
||||
// return 0;
|
||||
return done;
|
||||
}
|
||||
static ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size)
|
||||
|
@ -755,16 +754,12 @@ static ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size)
|
|||
gnutlsfile_t *file = p;
|
||||
// Sys_Printf("SSL_Pull: %u\n", size);
|
||||
int done = VFS_READ(file->stream, data, size);
|
||||
if (!done)
|
||||
if (done <= 0)
|
||||
{
|
||||
qgnutls_transport_set_errno(file->session, EAGAIN);
|
||||
//use ECONNRESET instead of returning eof.
|
||||
qgnutls_transport_set_errno(file->session, (done==0)?EAGAIN:ECONNRESET);
|
||||
return -1;
|
||||
}
|
||||
qgnutls_transport_set_errno(file->session, done<0?errno:0);
|
||||
// if (done < 0)
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
return done;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -142,6 +142,14 @@
|
|||
#include <libc.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
//requires linux 2.6.27 up (and equivelent libc)
|
||||
//note that BSD does tend to support the api, but emulated.
|
||||
//this works around the select FD limit, and supposedly has better performance.
|
||||
#define HAVE_EPOLL
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
|
||||
#if defined(__MORPHOS__) && !defined(ixemul)
|
||||
#define closesocket CloseSocket
|
||||
#define ioctlsocket IoctlSocket
|
||||
|
@ -173,6 +181,7 @@
|
|||
#define neterrno() WSAGetLastError()
|
||||
//this madness is because winsock defines its own errors instead of using system error codes.
|
||||
//*AND* microsoft then went and defined names for all the unix ones too... with different values! oh the insanity of it all!
|
||||
#define NET_EINTR WSAEINTR
|
||||
#define NET_EWOULDBLOCK WSAEWOULDBLOCK
|
||||
#define NET_EINPROGRESS WSAEINPROGRESS
|
||||
#define NET_EMSGSIZE WSAEMSGSIZE
|
||||
|
@ -193,6 +202,7 @@
|
|||
|
||||
#ifndef NET_EWOULDBLOCK
|
||||
//assume unix codes instead, so our prefix still works.
|
||||
#define NET_EINTR EINTR
|
||||
#define NET_EWOULDBLOCK EWOULDBLOCK
|
||||
#define NET_EINPROGRESS EINPROGRESS
|
||||
#define NET_EMSGSIZE EMSGSIZE
|
||||
|
@ -280,6 +290,13 @@ typedef struct
|
|||
extern icefuncs_t iceapi;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EPOLL
|
||||
typedef struct epollctx_s
|
||||
{
|
||||
void (*Polled) (struct epollctx_s *ctx, unsigned int events);
|
||||
} epollctx_t;
|
||||
#endif
|
||||
|
||||
//address flags
|
||||
#define ADDR_NATPMP (1u<<0)
|
||||
#define ADDR_UPNPIGP (1u<<1)
|
||||
|
@ -294,13 +311,22 @@ typedef struct ftenet_generic_connection_s {
|
|||
qboolean (*GetPacket)(struct ftenet_generic_connection_s *con);
|
||||
neterr_t (*SendPacket)(struct ftenet_generic_connection_s *con, int length, const void *data, netadr_t *to);
|
||||
void (*Close)(struct ftenet_generic_connection_s *con);
|
||||
#ifdef HAVE_PACKET
|
||||
#if defined(HAVE_PACKET) && !defined(HAVE_EPOLL)
|
||||
int (*SetFDSets) (struct ftenet_generic_connection_s *con, fd_set *readfdset, fd_set *writefdset); /*set for connections which have multiple sockets (ie: listening tcp connections)*/
|
||||
#endif
|
||||
void (*PrintStatus)(struct ftenet_generic_connection_s *con);
|
||||
|
||||
netadrtype_t addrtype[FTENET_ADDRTYPES];
|
||||
netproto_t prot; //if there's some special weirdness
|
||||
netadrtype_t addrtype[FTENET_ADDRTYPES]; //which address families it accepts
|
||||
qboolean islisten;
|
||||
|
||||
int connum;
|
||||
struct ftenet_connections_s *owner;
|
||||
|
||||
#ifdef HAVE_EPOLL
|
||||
epollctx_t epoll;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PACKET
|
||||
SOCKET thesocket;
|
||||
#else
|
||||
|
@ -357,6 +383,8 @@ typedef struct ftenet_connections_s
|
|||
float bytesoutrate;
|
||||
ftenet_generic_connection_t *conn[MAX_CONNECTIONS];
|
||||
|
||||
void (*ReadGamePacket) (void);
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
struct dtlspeer_s *dtls; //linked list. linked lists are shit, but at least it keeps pointers valid when things are resized.
|
||||
const dtlsfuncs_t *dtlsfuncs;
|
||||
|
@ -376,7 +404,7 @@ void ICE_Tick(void);
|
|||
qboolean ICE_WasStun(ftenet_connections_t *col);
|
||||
void QDECL ICE_AddLCandidateConn(ftenet_connections_t *col, netadr_t *addr, int type);
|
||||
void QDECL ICE_AddLCandidateInfo(struct icestate_s *con, netadr_t *adr, int adrno, int type);
|
||||
ftenet_generic_connection_t *FTENET_ICE_EstablishConnection(qboolean isserver, const char *address, netadr_t adr);
|
||||
ftenet_generic_connection_t *FTENET_ICE_EstablishConnection(ftenet_connections_t *col, const char *address, netadr_t adr);
|
||||
enum icemsgtype_s
|
||||
{ //shared by rtcpeers+broker
|
||||
ICEMSG_PEERDROP=0, //other side dropped connection
|
||||
|
@ -399,7 +427,7 @@ enum websocketpackettype_e
|
|||
WS_PACKETTYPE_PONG=10,
|
||||
};
|
||||
|
||||
ftenet_connections_t *FTENET_CreateCollection(qboolean listen);
|
||||
ftenet_connections_t *FTENET_CreateCollection(qboolean listen, void (*ReadPacket) (void));
|
||||
void FTENET_CloseCollection(ftenet_connections_t *col);
|
||||
qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot);
|
||||
int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, unsigned int *adrflags, netadr_t *addresses, const char **adrparams, int maxaddresses);
|
||||
|
@ -408,7 +436,7 @@ void *TLS_GetKnownCertificate(const char *certname, size_t *size);
|
|||
vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server);
|
||||
int TLS_GetChannelBinding(vfsfile_t *stream, qbyte *data, size_t *datasize); //datasize should be preinitialised to the max length allowed. -1 for not implemented. 0 for peer problems. 1 for success
|
||||
#ifdef HAVE_PACKET
|
||||
vfsfile_t *FS_OpenTCPSocket(SOCKET socket, qboolean conpending, const char *peername); //conpending allows us to reject any writes until the connection has succeeded
|
||||
vfsfile_t *FS_WrapTCPSocket(SOCKET socket, qboolean conpending, const char *peername); //conpending allows us to reject any writes until the connection has succeeded. considers the socket owned (so be sure to stop using the direct socket at least before the VFS_CLOSE call).
|
||||
#endif
|
||||
vfsfile_t *FS_OpenTCP(const char *name, int defaultport);
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#endif
|
||||
|
||||
#if ZONEDEBUG>0 || HUNKDEBUG>0 || TEMPDEBUG>0
|
||||
qbyte sentinalkey;
|
||||
qbyte sentinelkey;
|
||||
#endif
|
||||
|
||||
#define TAGLESS 1
|
||||
|
@ -568,16 +568,16 @@ void Hunk_TempFree(void)
|
|||
buf = (qbyte *)(hnktemps+1);
|
||||
for (i = 0; i < TEMPDEBUG; i++)
|
||||
{
|
||||
if (buf[i] != sentinalkey)
|
||||
Sys_Error ("Hunk_Check: corrupt sentinal");
|
||||
if (buf[i] != sentinelkey)
|
||||
Sys_Error ("Hunk_Check: corrupt sentinel");
|
||||
}
|
||||
buf+=TEMPDEBUG;
|
||||
//app data
|
||||
buf += hnktemps->len;
|
||||
for (i = 0; i < TEMPDEBUG; i++)
|
||||
{
|
||||
if (buf[i] != sentinalkey)
|
||||
Sys_Error ("Hunk_Check: corrupt sentinal");
|
||||
if (buf[i] != sentinelkey)
|
||||
Sys_Error ("Hunk_Check: corrupt sentinel");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -605,10 +605,10 @@ void *Hunk_TempAllocMore (size_t size)
|
|||
nt->len = size;
|
||||
hnktemps = nt;
|
||||
buf = (void *)(nt+1);
|
||||
memset(buf, sentinalkey, TEMPDEBUG);
|
||||
memset(buf, sentinelkey, TEMPDEBUG);
|
||||
buf = (char *)buf + TEMPDEBUG;
|
||||
memset(buf, 0, size);
|
||||
memset((char *)buf + size, sentinalkey, TEMPDEBUG);
|
||||
memset((char *)buf + size, sentinelkey, TEMPDEBUG);
|
||||
return buf;
|
||||
#else
|
||||
hnktemps_t *nt;
|
||||
|
@ -717,7 +717,7 @@ void Memory_Init (void)
|
|||
|
||||
#if ZONEDEBUG>0 || HUNKDEBUG>0 || TEMPDEBUG>0||CACHEDEBUG>0
|
||||
srand(time(0));
|
||||
sentinalkey = rand() & 0xff;
|
||||
sentinelkey = rand() & 0xff;
|
||||
#endif
|
||||
|
||||
Cache_Init ();
|
||||
|
|
|
@ -5571,6 +5571,7 @@ static void BE_UpdateLightmaps(void)
|
|||
{
|
||||
extern cvar_t r_lightmap_nearest;
|
||||
TEXASSIGN(lm->lightmap_texture, Image_CreateTexture(va("***lightmap %i***", lmidx), NULL, (r_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP));
|
||||
lm->lightmap_texture->format = lm->fmt;
|
||||
qglGenTextures(1, &lm->lightmap_texture->num);
|
||||
GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture);
|
||||
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
|
|
@ -1578,7 +1578,7 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
|
|||
}
|
||||
|
||||
error = FT_Err_Cannot_Open_Resource;
|
||||
if (FS_FLocateFile(fontfilename, FSLF_IFFOUND, &loc) || FS_FLocateFile(va("%s.ttf", fontfilename), FSLF_IFFOUND, &loc) || FS_FLocateFile(va("%s.otf", fontfilename), FSLF_IFFOUND, &loc))
|
||||
if (FS_FLocateFile(fontfilename, FSLF_IFFOUND|FSLF_QUIET, &loc) || FS_FLocateFile(va("%s.ttf", fontfilename), FSLF_IFFOUND|FSLF_QUIET, &loc) || FS_FLocateFile(va("%s.otf", fontfilename), FSLF_IFFOUND|FSLF_QUIET, &loc))
|
||||
{
|
||||
if (*loc.rawname && !loc.offset)
|
||||
{
|
||||
|
|
|
@ -2579,10 +2579,16 @@ static void Mod_Batches_BuildModelMeshes(model_t *mod, int maxverts, int maxindi
|
|||
surf->lightmaptexturenums[sty] /= 2;
|
||||
if (mesh->lmst_array[sty])
|
||||
{
|
||||
int soffset = surf->lightmaptexturenums[sty] % mod->lightmaps.mergew;
|
||||
int toffset = surf->lightmaptexturenums[sty] / mod->lightmaps.mergew;
|
||||
float smul = 1.0/mod->lightmaps.mergew;
|
||||
float tmul = 1.0/mod->lightmaps.mergeh;
|
||||
for (i = 0; i < mesh->numvertexes; i++)
|
||||
{
|
||||
mesh->lmst_array[sty][i][1] += surf->lightmaptexturenums[sty] % lmmerge;
|
||||
mesh->lmst_array[sty][i][1] /= lmmerge;
|
||||
mesh->lmst_array[sty][i][0] += soffset;
|
||||
mesh->lmst_array[sty][i][0] *= smul;
|
||||
mesh->lmst_array[sty][i][1] += toffset;
|
||||
mesh->lmst_array[sty][i][1] *= tmul;
|
||||
}
|
||||
}
|
||||
surf->lightmaptexturenums[sty] /= lmmerge;
|
||||
|
@ -2670,9 +2676,9 @@ static int Mod_Batches_Generate(model_t *mod)
|
|||
vec4_t plane;
|
||||
image_t *envmap;
|
||||
|
||||
int merge = mod->lightmaps.merge;
|
||||
int merge = mod->lightmaps.mergew*mod->lightmaps.mergeh;
|
||||
if (!merge)
|
||||
merge = 1;
|
||||
merge = mod->lightmaps.mergew = mod->lightmaps.mergeh = 1; //no division by 0 please...
|
||||
if (mod->lightmaps.deluxemapping)
|
||||
{
|
||||
mod->lightmaps.count = ((mod->lightmaps.count+1)/2+merge-1) & ~(merge-1);
|
||||
|
@ -2684,7 +2690,8 @@ static int Mod_Batches_Generate(model_t *mod)
|
|||
mod->lightmaps.count = (mod->lightmaps.count+merge-1) & ~(merge-1);
|
||||
mod->lightmaps.count /= merge;
|
||||
}
|
||||
mod->lightmaps.height *= merge;
|
||||
mod->lightmaps.width *= mod->lightmaps.mergew;
|
||||
mod->lightmaps.height *= mod->lightmaps.mergeh;
|
||||
|
||||
mod->numbatches = 0;
|
||||
|
||||
|
|
|
@ -1022,7 +1022,8 @@ typedef struct model_s
|
|||
{
|
||||
int first; //once built...
|
||||
int count; //num lightmaps
|
||||
int merge; //merge this many source lightmaps together. woo.
|
||||
int mergew; //merge this many source lightmaps together. woo.
|
||||
int mergeh; //merge this many source lightmaps together. woo.
|
||||
int width; //x size of lightmaps
|
||||
int height; //y size of lightmaps
|
||||
int surfstyles; //numbers of style per surface.
|
||||
|
|
|
@ -578,8 +578,10 @@ void GLBE_UploadAllLightmaps(void)
|
|||
}
|
||||
|
||||
//for completeness.
|
||||
lm->lightmap_texture->format = lm->fmt;
|
||||
lm->lightmap_texture->width = lm->width;
|
||||
lm->lightmap_texture->height = lm->height;
|
||||
lm->lightmap_texture->depth = 1;
|
||||
lm->lightmap_texture->status = TEX_LOADED;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1056,7 +1056,7 @@ void HTTPDL_Establish(struct dl_download *dl)
|
|||
//https uses a different default port
|
||||
if (NET_StringToAdr2(con->server, https?443:80, &adr, 1, NULL))
|
||||
con->sock = TCP_OpenStream(&adr);
|
||||
con->stream = FS_OpenTCPSocket(con->sock, true, con->server);
|
||||
con->stream = FS_WrapTCPSocket(con->sock, true, con->server);
|
||||
}
|
||||
#ifdef HAVE_SSL
|
||||
if (https)
|
||||
|
|
|
@ -1142,6 +1142,7 @@ void SV_AutoAddPenalty (client_t *cl, unsigned int banflag, int duration, char *
|
|||
NORETURN void VARGS SV_Error (char *error, ...) LIKEPRINTF(1);
|
||||
void SV_Shutdown (void);
|
||||
float SV_Frame (void);
|
||||
void SV_ReadPacket(void);
|
||||
void SV_FinalMessage (char *message);
|
||||
void SV_DropClient (client_t *drop);
|
||||
struct quakeparms_s;
|
||||
|
|
|
@ -2760,7 +2760,7 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
|
|||
case GT_PROGS:
|
||||
if (info->protocol == SCP_QUAKE2)
|
||||
{
|
||||
SV_RejectMessage(info->protocol, "This is a Quake server.");
|
||||
SV_RejectMessage(info->protocol, "This is a %s server.", fs_manifest->formalname);
|
||||
Con_DPrintf ("* Rejected q2 client.\n");
|
||||
return;
|
||||
}
|
||||
|
@ -2790,7 +2790,7 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
|
|||
case GT_QUAKE2:
|
||||
if (info->protocol != SCP_QUAKE2)
|
||||
{
|
||||
SV_RejectMessage(info->protocol, "This is a Quake2 server.");
|
||||
SV_RejectMessage(info->protocol, "This is a %s server.", fs_manifest->formalname);
|
||||
Con_DPrintf ("* Rejected non-q2 client.\n");
|
||||
return;
|
||||
}
|
||||
|
@ -3843,7 +3843,10 @@ qboolean SV_ConnectionlessPacket (void)
|
|||
Con_Printf("%s: %s\n", NET_AdrToString (adr, sizeof(adr), &net_from), s);
|
||||
|
||||
if (!strcmp(c, "ping") || ( c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n')) )
|
||||
SVC_Ping ();
|
||||
{ //only continue respond to these if we're actually public. qwfwd likes spamming us endlessly even if we stop heartbeating (which leaves us discoverable to others, too).
|
||||
if (sv_public.ival >= 0)
|
||||
SVC_Ping ();
|
||||
}
|
||||
else if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n') )
|
||||
SVC_ACK ();
|
||||
else if (!strcmp(c,"status"))
|
||||
|
@ -4384,6 +4387,189 @@ void SV_OpenRoute_f(void)
|
|||
}
|
||||
|
||||
//============================================================================
|
||||
static int inboundsequence; //so we can detect frames when we didn't get any packets, even when packets come from epoll
|
||||
void SV_ReadPacket(void)
|
||||
{
|
||||
int i;
|
||||
client_t *cl;
|
||||
int qport;
|
||||
char *banreason;
|
||||
|
||||
// check for connectionless packet (0xffffffff) first
|
||||
if (*(unsigned int *)net_message.data == ~0)
|
||||
{
|
||||
banreason = SV_BannedReason (&net_from);
|
||||
if (banreason)
|
||||
{
|
||||
static unsigned int lt;
|
||||
unsigned int ct = Sys_Milliseconds();
|
||||
if (ct - lt > 5*1000)
|
||||
{
|
||||
if (*banreason)
|
||||
Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, com_language, "You are banned: %s\n", banreason);
|
||||
else
|
||||
Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, com_language, "You are banned\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
SV_ConnectionlessPacket();
|
||||
return;
|
||||
}
|
||||
#ifdef HAVE_DTLS
|
||||
else
|
||||
{
|
||||
if (NET_DTLS_Decode(svs.sockets))
|
||||
{
|
||||
if (!net_message.cursize)
|
||||
return;
|
||||
if (*(unsigned int *)net_message.data == ~0)
|
||||
{
|
||||
SV_ConnectionlessPacket();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q3SERVER
|
||||
if (svs.gametype == GT_QUAKE3)
|
||||
{
|
||||
if (SVQ3_HandleClient())
|
||||
inboundsequence++;
|
||||
else if (NET_WasSpecialPacket(svs.sockets))
|
||||
return;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// read the qport out of the message so we can fix up
|
||||
// stupid address translating routers
|
||||
MSG_BeginReading (svs.netprim);
|
||||
MSG_ReadLong (); // sequence number
|
||||
MSG_ReadLong (); // sequence number
|
||||
qport = MSG_ReadShort () & 0xffff;
|
||||
|
||||
// check for packets from connected clients
|
||||
for (i=0, cl=svs.clients ; i<svs.allocated_client_slots ; i++,cl++)
|
||||
{
|
||||
if (cl->state == cs_free)
|
||||
continue;
|
||||
if (!NET_CompareBaseAdr (&net_from, &cl->netchan.remote_address))
|
||||
continue;
|
||||
#ifdef NQPROT
|
||||
if (ISNQCLIENT(cl) && cl->netchan.remote_address.port == net_from.port)
|
||||
{
|
||||
if (cl->state >= cs_connected)
|
||||
{
|
||||
if (cl->delay > 0)
|
||||
goto dominping;
|
||||
|
||||
if (NQNetChan_Process(&cl->netchan))
|
||||
{
|
||||
inboundsequence++;
|
||||
svs.stats.packets++;
|
||||
SVNQ_ExecuteClientMessage(cl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q3SERVER
|
||||
if (ISQ3CLIENT(cl))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if (cl->netchan.qport != qport)
|
||||
continue;
|
||||
if (cl->netchan.remote_address.port != net_from.port)
|
||||
{
|
||||
Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n");
|
||||
cl->netchan.remote_address.port = net_from.port;
|
||||
}
|
||||
|
||||
if (cl->delay > 0)
|
||||
{
|
||||
#ifdef NQPROT
|
||||
dominping:
|
||||
#endif
|
||||
if (cl->state < cs_connected)
|
||||
break;
|
||||
if (net_message.cursize > sizeof(svs.free_lagged_packet->data))
|
||||
{
|
||||
Con_Printf("packet too large for minping\n");
|
||||
cl->delay -= 0.001;
|
||||
break; //drop this packet
|
||||
}
|
||||
|
||||
if (!svs.free_lagged_packet) //kinda nasty
|
||||
svs.free_lagged_packet = Z_Malloc(sizeof(*svs.free_lagged_packet));
|
||||
|
||||
if (!cl->laggedpacket)
|
||||
cl->laggedpacket_last = cl->laggedpacket = svs.free_lagged_packet;
|
||||
else
|
||||
{
|
||||
cl->laggedpacket_last->next = svs.free_lagged_packet;
|
||||
cl->laggedpacket_last = cl->laggedpacket_last->next;
|
||||
}
|
||||
svs.free_lagged_packet = svs.free_lagged_packet->next;
|
||||
cl->laggedpacket_last->next = NULL;
|
||||
|
||||
cl->laggedpacket_last->time = realtime + cl->delay;
|
||||
memcpy(cl->laggedpacket_last->data, net_message.data, net_message.cursize);
|
||||
cl->laggedpacket_last->length = net_message.cursize;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (Netchan_Process(&cl->netchan))
|
||||
{ // this is a valid, sequenced packet, so process it
|
||||
inboundsequence++;
|
||||
svs.stats.packets++;
|
||||
if (cl->state >= cs_connected)
|
||||
{
|
||||
if (cl->send_message)
|
||||
cl->chokecount++;
|
||||
else
|
||||
cl->send_message = true; // reply at end of frame
|
||||
|
||||
#ifdef Q2SERVER
|
||||
if (cl->protocol == SCP_QUAKE2)
|
||||
SVQ2_ExecuteClientMessage(cl);
|
||||
else
|
||||
#endif
|
||||
SV_ExecuteClientMessage (cl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != svs.allocated_client_slots)
|
||||
return;
|
||||
|
||||
#ifdef QWOVERQ3
|
||||
if (sv_listen_q3.ival && SVQ3_HandleClient())
|
||||
{
|
||||
received++;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NQPROT
|
||||
if (SVNQ_ConnectionlessPacket())
|
||||
return;
|
||||
#endif
|
||||
if (SV_BannedReason (&net_from))
|
||||
return;
|
||||
|
||||
if (NET_WasSpecialPacket(svs.sockets))
|
||||
return;
|
||||
|
||||
// packet is not from a known client
|
||||
if (sv_showconnectionlessmessages.ival)
|
||||
Con_Printf ("%s:sequenced packet without connection\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); //hack: com_token cos we need some random temp buffer.
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
|
@ -4399,12 +4585,9 @@ qboolean SV_ReadPackets (float *delay)
|
|||
{
|
||||
int i;
|
||||
client_t *cl;
|
||||
int qport;
|
||||
laggedpacket_t *lp;
|
||||
char *banreason;
|
||||
qboolean received = false;
|
||||
int giveup = 5000; /*we're fucked if we need this to be this high, but at least we can retain some clients if we're really running that slow*/
|
||||
int cookie = 0;
|
||||
|
||||
static int oldinboundsequence;
|
||||
|
||||
SV_KillExpiredBans();
|
||||
|
||||
|
@ -4443,7 +4626,7 @@ qboolean SV_ReadPackets (float *delay)
|
|||
{
|
||||
if (NQNetChan_Process(&cl->netchan))
|
||||
{
|
||||
received++;
|
||||
inboundsequence++;
|
||||
svs.stats.packets++;
|
||||
SVNQ_ExecuteClientMessage(cl);
|
||||
}
|
||||
|
@ -4455,7 +4638,7 @@ qboolean SV_ReadPackets (float *delay)
|
|||
/*QW*/
|
||||
if (Netchan_Process(&cl->netchan))
|
||||
{ // this is a valid, sequenced packet, so process it
|
||||
received++;
|
||||
inboundsequence++;
|
||||
svs.stats.packets++;
|
||||
if (cl->state >= cs_connected)
|
||||
{ //make sure they didn't already disconnect
|
||||
|
@ -4477,194 +4660,17 @@ qboolean SV_ReadPackets (float *delay)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef SERVER_DEMO_PLAYBACK
|
||||
while (giveup-- > 0 && SV_GetPacket()>=0)
|
||||
#else
|
||||
while (giveup-- > 0 && (cookie=NET_GetPacket (svs.sockets, cookie)) >= 0)
|
||||
#endif
|
||||
{
|
||||
// check for connectionless packet (0xffffffff) first
|
||||
if (*(unsigned int *)net_message.data == ~0)
|
||||
{
|
||||
banreason = SV_BannedReason (&net_from);
|
||||
if (banreason)
|
||||
{
|
||||
static unsigned int lt;
|
||||
unsigned int ct = Sys_Milliseconds();
|
||||
if (ct - lt > 5*1000)
|
||||
{
|
||||
if (*banreason)
|
||||
Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, com_language, "You are banned: %s\n", banreason);
|
||||
else
|
||||
Netchan_OutOfBandTPrintf(NS_SERVER, &net_from, com_language, "You are banned\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
SV_ConnectionlessPacket();
|
||||
continue;
|
||||
}
|
||||
#ifdef HAVE_DTLS
|
||||
else
|
||||
{
|
||||
if (NET_DTLS_Decode(svs.sockets))
|
||||
{
|
||||
if (!net_message.cursize)
|
||||
continue;
|
||||
if (*(unsigned int *)net_message.data == ~0)
|
||||
{
|
||||
SV_ConnectionlessPacket();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q3SERVER
|
||||
if (svs.gametype == GT_QUAKE3)
|
||||
{
|
||||
received++;
|
||||
if (SVQ3_HandleClient())
|
||||
;
|
||||
else if (NET_WasSpecialPacket(svs.sockets))
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
// read the qport out of the message so we can fix up
|
||||
// stupid address translating routers
|
||||
MSG_BeginReading (svs.netprim);
|
||||
MSG_ReadLong (); // sequence number
|
||||
MSG_ReadLong (); // sequence number
|
||||
qport = MSG_ReadShort () & 0xffff;
|
||||
|
||||
// check for packets from connected clients
|
||||
for (i=0, cl=svs.clients ; i<svs.allocated_client_slots ; i++,cl++)
|
||||
{
|
||||
if (cl->state == cs_free)
|
||||
continue;
|
||||
if (!NET_CompareBaseAdr (&net_from, &cl->netchan.remote_address))
|
||||
continue;
|
||||
#ifdef NQPROT
|
||||
if (ISNQCLIENT(cl) && cl->netchan.remote_address.port == net_from.port)
|
||||
{
|
||||
if (cl->state >= cs_connected)
|
||||
{
|
||||
if (cl->delay > 0)
|
||||
goto dominping;
|
||||
|
||||
if (NQNetChan_Process(&cl->netchan))
|
||||
{
|
||||
received++;
|
||||
svs.stats.packets++;
|
||||
SVNQ_ExecuteClientMessage(cl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef Q3SERVER
|
||||
if (ISQ3CLIENT(cl))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if (cl->netchan.qport != qport)
|
||||
continue;
|
||||
if (cl->netchan.remote_address.port != net_from.port)
|
||||
{
|
||||
Con_DPrintf ("SV_ReadPackets: fixing up a translated port\n");
|
||||
cl->netchan.remote_address.port = net_from.port;
|
||||
}
|
||||
|
||||
if (cl->delay > 0)
|
||||
{
|
||||
#ifdef NQPROT
|
||||
dominping:
|
||||
#endif
|
||||
if (cl->state < cs_connected)
|
||||
break;
|
||||
if (net_message.cursize > sizeof(svs.free_lagged_packet->data))
|
||||
{
|
||||
Con_Printf("packet too large for minping\n");
|
||||
cl->delay -= 0.001;
|
||||
break; //drop this packet
|
||||
}
|
||||
|
||||
if (!svs.free_lagged_packet) //kinda nasty
|
||||
svs.free_lagged_packet = Z_Malloc(sizeof(*svs.free_lagged_packet));
|
||||
|
||||
if (!cl->laggedpacket)
|
||||
cl->laggedpacket_last = cl->laggedpacket = svs.free_lagged_packet;
|
||||
else
|
||||
{
|
||||
cl->laggedpacket_last->next = svs.free_lagged_packet;
|
||||
cl->laggedpacket_last = cl->laggedpacket_last->next;
|
||||
}
|
||||
svs.free_lagged_packet = svs.free_lagged_packet->next;
|
||||
cl->laggedpacket_last->next = NULL;
|
||||
|
||||
cl->laggedpacket_last->time = realtime + cl->delay;
|
||||
memcpy(cl->laggedpacket_last->data, net_message.data, net_message.cursize);
|
||||
cl->laggedpacket_last->length = net_message.cursize;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (Netchan_Process(&cl->netchan))
|
||||
{ // this is a valid, sequenced packet, so process it
|
||||
received++;
|
||||
svs.stats.packets++;
|
||||
if (cl->state >= cs_connected)
|
||||
{
|
||||
if (cl->send_message)
|
||||
cl->chokecount++;
|
||||
else
|
||||
cl->send_message = true; // reply at end of frame
|
||||
|
||||
#ifdef Q2SERVER
|
||||
if (cl->protocol == SCP_QUAKE2)
|
||||
SVQ2_ExecuteClientMessage(cl);
|
||||
else
|
||||
#endif
|
||||
SV_ExecuteClientMessage (cl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != svs.allocated_client_slots)
|
||||
continue;
|
||||
|
||||
#ifdef QWOVERQ3
|
||||
if (sv_listen_q3.ival && SVQ3_HandleClient())
|
||||
{
|
||||
received++;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NQPROT
|
||||
if (SVNQ_ConnectionlessPacket())
|
||||
continue;
|
||||
#endif
|
||||
if (SV_BannedReason (&net_from))
|
||||
continue;
|
||||
|
||||
if (NET_WasSpecialPacket(svs.sockets))
|
||||
continue;
|
||||
|
||||
// packet is not from a known client
|
||||
if (sv_showconnectionlessmessages.ival)
|
||||
Con_Printf ("%s:sequenced packet without connection\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); //hack: com_token cos we need some random temp buffer.
|
||||
}
|
||||
NET_ReadPackets(svs.sockets);
|
||||
|
||||
#ifdef HAVE_DTLS
|
||||
NET_DTLS_Timeouts(svs.sockets);
|
||||
#endif
|
||||
|
||||
return received;
|
||||
|
||||
if (inboundsequence == oldinboundsequence)
|
||||
return false; //nothing new.
|
||||
oldinboundsequence = inboundsequence;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -10,21 +10,25 @@
|
|||
#endif
|
||||
|
||||
#include "netinc.h"
|
||||
#include "fs.h"
|
||||
|
||||
//quakeworld protocol
|
||||
// heartbeat: "a"
|
||||
// query: "c\n%i<sequence>\n%i<numplayers>\n"
|
||||
//queryresponse: "d\naaaappaaaapp"
|
||||
#define QUAKEWORLDPROTOCOLNAME "FTE-Quake"
|
||||
|
||||
//quake2 protocol
|
||||
// heartbeat: "heartbeat\n%s<serverinfo>\n%i<frags> %i<ping> \"%s\"<name>\n<repeat>"
|
||||
// query: "query\0"
|
||||
//queryresponse: "servers\naaaappaaaapp"
|
||||
#define QUAKE2PROTOCOLNAME "Quake2"
|
||||
|
||||
//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"
|
||||
#define QUAKE3PROTOCOLNAME "Quake3"
|
||||
|
||||
enum gametypes_e
|
||||
{
|
||||
|
@ -39,6 +43,7 @@ typedef struct svm_server_s {
|
|||
int protover;
|
||||
unsigned int clients;
|
||||
unsigned int maxclients;
|
||||
qboolean needpass;
|
||||
char hostname[48]; //just for our own listings.
|
||||
char mapname[16]; //just for our own listings.
|
||||
char gamedir[16]; //again...
|
||||
|
@ -56,7 +61,8 @@ typedef struct svm_game_s {
|
|||
svm_server_t *firstserver;
|
||||
size_t numservers;
|
||||
qboolean persistent;
|
||||
char name[1];
|
||||
char *aliases; //list of terminated names, terminated with a double-null
|
||||
char name[1]; //eg: Quake
|
||||
} svm_game_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -93,11 +99,15 @@ 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_heartbeattimeout = CVARD("sv_heartbeattimeout", "300", "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", STRINGIFY(PORT_QWMASTER)" "STRINGIFY(PORT_ICEBROKER), SVM_Port_Callback);
|
||||
static cvar_t sv_masterport_tcp = CVARC("sv_masterport_tcp", STRINGIFY(PORT_ICEBROKER), 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.");
|
||||
static cvar_t sv_maxservers = CVARD("sv_maxservers", "10000", "Limits the number of servers (total from all games) that may be known. This is to reduce denial of service attacks.");
|
||||
static cvar_t sv_hideinactivegames = CVARD("sv_hideinactivegames", "1", "Don't show known games that currently have no servers in html listings.");
|
||||
static cvar_t sv_sortlist = CVARD("sv_sortlist", "3", "Controls sorting of the http output:\n0: don't bother\n1: clients then address\n2: hostname then address\n3: clients then hostname then address\n4: just address");
|
||||
static cvar_t sv_hostname = CVARD("hostname", "Unnamed FTE-Master", "Controls sorting of the http output:\n0: don't bother\n1: clients then address\n2: hostname then address\n3: clients then hostname then address\n4: just address");
|
||||
static char *master_css;
|
||||
|
||||
static unsigned int SVM_GenerateBrokerKey(const char *brokerid)
|
||||
{
|
||||
|
@ -148,35 +158,54 @@ static svm_game_t *SVM_FindGame(const char *game, int create)
|
|||
{
|
||||
svm_game_t *g, **link;
|
||||
const char *sanitise;
|
||||
const char *a;
|
||||
for (g = svm.firstgame; g; g = g->next)
|
||||
{
|
||||
if (!Q_strcasecmp(game, g->name))
|
||||
return g;
|
||||
|
||||
if (g->aliases)
|
||||
{
|
||||
for (a = g->aliases; *a; a+=strlen(a)+1)
|
||||
{
|
||||
if (!Q_strcasecmp(game, a))
|
||||
return g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (create)
|
||||
{
|
||||
if (svm.numgames >= sv_maxgames.ival)
|
||||
if (create != 2)
|
||||
{
|
||||
Con_DPrintf("game limit exceeded\n");
|
||||
return NULL;
|
||||
}
|
||||
//block some chars that may cause issues/exploits. sorry.
|
||||
for (sanitise = game; *sanitise; sanitise++)
|
||||
{
|
||||
if ((*sanitise >= 'a' && *sanitise <= 'z') || //allow lowercase
|
||||
(*sanitise >= 'A' && *sanitise <= 'Z') || //allow uppercase
|
||||
(*sanitise >= '0' && *sanitise <= '9') || //allow numbers (but not leeding, see below)
|
||||
(*sanitise == '-' || *sanitise == '_')) //allow a little punctuation, to make up for the lack of spaces.
|
||||
continue;
|
||||
return NULL;
|
||||
if (svm.numgames >= sv_maxgames.ival)
|
||||
{
|
||||
Con_DPrintf("game limit exceeded\n");
|
||||
return NULL;
|
||||
}
|
||||
//block some chars that may cause issues/exploits. sorry.
|
||||
for (sanitise = game; *sanitise; sanitise++)
|
||||
{
|
||||
if ((*sanitise >= 'a' && *sanitise <= 'z') || //allow lowercase
|
||||
(*sanitise >= 'A' && *sanitise <= 'Z') || //allow uppercase
|
||||
(*sanitise >= '0' && *sanitise <= '9') || //allow numbers (but not leeding, see below)
|
||||
(*sanitise == '-' || *sanitise == '_')) //allow a little punctuation, to make up for the lack of spaces.
|
||||
continue;
|
||||
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)
|
||||
{
|
||||
char *n;
|
||||
strcpy(g->name, game);
|
||||
for (n = g->name; *n; n++)
|
||||
{ //some extra fixups, because formalnames are messy.
|
||||
if (*n == ' ' || *n == '\t' || *n == ':' || *n == '?' || *n == '#' || *n == '.')
|
||||
*n = '_';
|
||||
}
|
||||
g->persistent = create==2;
|
||||
g->next = NULL;
|
||||
|
||||
|
@ -191,6 +220,62 @@ static svm_game_t *SVM_FindGame(const char *game, int create)
|
|||
return g;
|
||||
}
|
||||
|
||||
static int QDECL SVM_SortOrder(const void *v1, const void *v2)
|
||||
{
|
||||
svm_server_t const*const s1 = *(svm_server_t const*const*const)v1;
|
||||
svm_server_t const*const s2 = *(svm_server_t const*const*const)v2;
|
||||
int t, i;
|
||||
if (sv_sortlist.ival&8)
|
||||
return s1->expiretime > s2->expiretime;
|
||||
|
||||
if (sv_sortlist.ival&1)
|
||||
if ((t=(s2->clients-s1->clients)))
|
||||
return (t>0)?1:-1;
|
||||
if (sv_sortlist.ival&2)
|
||||
if ((t=strcmp(s1->hostname, s2->hostname)))
|
||||
return (t>0)?1:-1;
|
||||
|
||||
//sort by scheme, address family, and ip
|
||||
if ((t=(s1->adr.prot-s2->adr.prot)))
|
||||
return (t>0)?1:-1;
|
||||
if ((t=(s1->adr.type-s2->adr.type)))
|
||||
return (t>0)?1:-1;
|
||||
if (s1->adr.type==NA_IP)
|
||||
i = sizeof(s1->adr.address.ip);
|
||||
else if (s1->adr.type==NA_IPV6)
|
||||
i = sizeof(s1->adr.address.ip6);
|
||||
else i = 0;
|
||||
for(t = 0; t < i; t++)
|
||||
if (s1->adr.address.ip6[i] != s2->adr.address.ip6[i])
|
||||
return (s2->adr.address.ip6[i]>s1->adr.address.ip6[i])?1:-1;
|
||||
|
||||
//and now do port numbers too.
|
||||
t = BigShort(s1->adr.port) - BigShort(s2->adr.port);
|
||||
if (t)
|
||||
return (t>0)?1:-1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SVM_SortServers(svm_game_t *game)
|
||||
{
|
||||
svm_server_t **serverlink, *s;
|
||||
svm_server_t **sv = malloc(sizeof(*sv)*game->numservers);
|
||||
int i;
|
||||
if (!sv_sortlist.ival)
|
||||
return;
|
||||
|
||||
for (i=0, s = game->firstserver; s; s = s->next)
|
||||
sv[i++] = s;
|
||||
qsort(sv, i, sizeof(*sv), SVM_SortOrder);
|
||||
|
||||
for (i = 0, serverlink = &game->firstserver; i < game->numservers; i++)
|
||||
{
|
||||
*serverlink = sv[i];
|
||||
serverlink = &sv[i]->next;
|
||||
}
|
||||
*serverlink = NULL;
|
||||
}
|
||||
|
||||
static void SVM_RemoveOldServers(void)
|
||||
{
|
||||
svm_game_t **gamelink, *g;
|
||||
|
@ -289,7 +374,7 @@ int SVM_AddIPAddresses(sizebuf_t *sb, int first, int ver, const char *gamename,
|
|||
return number;
|
||||
}
|
||||
|
||||
static char *QuakeCharsToHTML(char *outhtml, size_t outsize, const char *quake)
|
||||
static char *QuakeCharsToHTML(char *outhtml, size_t outsize, const char *quake, qboolean deunderscore)
|
||||
{
|
||||
char *ret = outhtml;
|
||||
conchar_t chars[8192], *c=chars, *end;
|
||||
|
@ -371,6 +456,8 @@ static char *QuakeCharsToHTML(char *outhtml, size_t outsize, const char *quake)
|
|||
Q_strncpyz(outhtml, "'", outsize);
|
||||
b=strlen(outhtml);
|
||||
}
|
||||
else if (codepoint == '_' && deunderscore)
|
||||
*outhtml = ' ', b =1;
|
||||
else
|
||||
b = utf8_encode(outhtml, codepoint, outsize);
|
||||
if (b > 0)
|
||||
|
@ -383,10 +470,12 @@ static char *QuakeCharsToHTML(char *outhtml, size_t outsize, const char *quake)
|
|||
return ret;
|
||||
}
|
||||
|
||||
vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
|
||||
static void SVM_Init(void)
|
||||
{
|
||||
static const char *thecss =
|
||||
"<style type=\"text/css\">"
|
||||
master_css = FS_MallocFile("master.css", FS_ROOT, NULL);
|
||||
if (!master_css)
|
||||
master_css = Z_StrDup(
|
||||
"<style type=\"text/css\">"
|
||||
"body {"
|
||||
"background-color: #303030;"
|
||||
"color: #998080;"
|
||||
|
@ -407,7 +496,12 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
|
|||
"table { width: 100%; border-collapse: collapse; }"
|
||||
"th { text-align: left; }"
|
||||
"tr:hover { background-color: #202020; }"
|
||||
"</style>";
|
||||
"</style>"
|
||||
);
|
||||
}
|
||||
|
||||
vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
|
||||
{
|
||||
char tmpbuf[256];
|
||||
char hostname[1024];
|
||||
const char *url;
|
||||
|
@ -415,19 +509,24 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
|
|||
svm_server_t *server;
|
||||
vfsfile_t *f = NULL;
|
||||
unsigned clients = 0, maxclients=0, totalclients=0;
|
||||
if (!master_css)
|
||||
SVM_Init();
|
||||
if (!strcmp(fname, "index.html"))
|
||||
{
|
||||
f = VFSPIPE_Open(1, false);
|
||||
VFS_PRINTF(f, "%s", thecss);
|
||||
VFS_PRINTF(f, "<h1>FTE-Master</h1>\n");
|
||||
VFS_PRINTF(f, "%s", master_css);
|
||||
VFS_PRINTF(f, "<h1>%s</h1>\n", sv_hostname.string);
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
VFS_PRINTF(f, "<tr><th>Active Games</th><th>Players</th><th>Server Count</th></tr>\n");
|
||||
for (game = svm.firstgame; game; game = game->next)
|
||||
{
|
||||
for (clients=0, server = game->firstserver; server; server = server->next)
|
||||
clients += server->clients;
|
||||
if (game->numservers) //only show active servers
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"game/%s\">%s</a></td><td>%u player(s)</td><td>%u server(s)</td></tr>\n", game->name, game->name, clients, (unsigned)game->numservers);
|
||||
if (game->numservers || !sv_hideinactivegames.ival) //only show active servers
|
||||
{
|
||||
QuakeCharsToHTML(tmpbuf, sizeof(tmpbuf), game->name, true);
|
||||
VFS_PRINTF(f, "<tr><td><a href=\"game/%s\">%s</a></td><td>%u player(s)</td><td>%u server(s)</td></tr>\n", game->name, tmpbuf, clients, (unsigned)game->numservers);
|
||||
}
|
||||
totalclients += clients;
|
||||
}
|
||||
VFS_PRINTF(f, "</table>\n");
|
||||
|
@ -439,7 +538,7 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
|
|||
int count;
|
||||
|
||||
f = VFSPIPE_Open(1, false);
|
||||
VFS_PRINTF(f, "%s", thecss);
|
||||
VFS_PRINTF(f, "%s", master_css);
|
||||
VFS_PRINTF(f, "<h1>Single Server Info</h1>\n");
|
||||
|
||||
VFS_PRINTF(f, "<table border=1>\n");
|
||||
|
@ -450,7 +549,10 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
|
|||
{
|
||||
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);
|
||||
{
|
||||
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s%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->needpass?"🔒":"", 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]));
|
||||
}
|
||||
|
@ -462,11 +564,17 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
|
|||
game = SVM_FindGame(gamename, false);
|
||||
|
||||
f = VFSPIPE_Open(1, false);
|
||||
VFS_PRINTF(f, "%s", thecss);
|
||||
VFS_PRINTF(f, "<h1>Servers for %s</h1>\n", gamename);
|
||||
VFS_PRINTF(f, "%s", master_css);
|
||||
|
||||
if (!strcmp(gamename, "UNKNOWN"))
|
||||
VFS_PRINTF(f, "<h1>Firewalled/NATed/Unresponsive/Congested (Misconfigured) Servers</h1>\n");
|
||||
else
|
||||
VFS_PRINTF(f, "<h1>Servers for %s</h1>\n", QuakeCharsToHTML(tmpbuf, sizeof(tmpbuf), gamename, true));
|
||||
|
||||
if(game)
|
||||
{
|
||||
SVM_SortServers(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)
|
||||
|
@ -478,8 +586,8 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
|
|||
}
|
||||
else
|
||||
url = NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr);
|
||||
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname);
|
||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", url, hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||
QuakeCharsToHTML(hostname, sizeof(hostname), server->hostname, false);
|
||||
VFS_PRINTF(f, "<tr><td>%s</td><td>%s%s</td><td>%s</td><td>%s</td><td>%u/%u</td></tr>\n", url, server->needpass?"🔒":"", hostname, server->gamedir, server->mapname, server->clients, server->maxclients);
|
||||
clients += server->clients;
|
||||
maxclients += server->maxclients;
|
||||
}
|
||||
|
@ -499,7 +607,7 @@ vfsfile_t *SVM_GenerateIndex(const char *requesthost, const char *fname)
|
|||
for (server = (game?game->firstserver:NULL); server; server = server->next)
|
||||
{
|
||||
if (server->brokerid)
|
||||
VFS_PRINTF(f, "rtc:///%s \\maxclients\\%u\\clients\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s\n", server->brokerid, server->maxclients, server->clients, server->hostname, server->gamedir, server->mapname);
|
||||
VFS_PRINTF(f, "rtc:///%s \\maxclients\\%u\\clients\\%u\\hostname\\%s\\modname\\%s\\mapname\\%s%s\n", server->brokerid, server->maxclients, server->clients, server->hostname, server->gamedir, server->mapname, server->needpass?"\\needpass\\1":"");
|
||||
else
|
||||
VFS_PRINTF(f, "%s\n", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &server->adr));
|
||||
}
|
||||
|
@ -555,13 +663,14 @@ void SVM_RemoveBrokerGame(const char *brokerid)
|
|||
if (s->brokerid == brokerid)
|
||||
{
|
||||
*link = s->next;
|
||||
Hash_RemoveDataKey(&svm.serverhash, SVM_GenerateBrokerKey(brokerid), s);
|
||||
Z_Free(s);
|
||||
game->numservers--;
|
||||
svm.numservers--;
|
||||
return;
|
||||
}
|
||||
else
|
||||
link = &(*link)->next;
|
||||
link = &s->next;
|
||||
}
|
||||
|
||||
Con_Printf("SVM_RemoveBrokerGame: failed to remove brokered server: %s\n", brokerid);
|
||||
|
@ -608,9 +717,23 @@ void SVM_AddBrokerGame(const char *brokerid, const char *info)
|
|||
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;
|
||||
svm_game_t *game;
|
||||
|
||||
if (!gamename)
|
||||
{ //no gamename is a placeholder server, to say that there's a server there but it isn't responding to our getinfos... (ie: to list misconfigured servers too)
|
||||
if (server)
|
||||
{ //it still exists, renew it, but don't otherwise care too much.
|
||||
server->expiretime = validuntil;
|
||||
return NULL;
|
||||
}
|
||||
game = SVM_FindGame("UNKNOWN", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
game = SVM_FindGame(gamename, true);
|
||||
if (!game)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (server && server->game != game)
|
||||
{
|
||||
|
@ -664,10 +787,22 @@ void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen);
|
|||
void SVM_GenChallenge(char *out, size_t outsize, netadr_t *foradr)
|
||||
{ //this function needs to return some sort of unguessable string so that you can't spoof the server with fake responses
|
||||
char adr[64];
|
||||
const unsigned char *strings[] = {"somethingrandom", (const unsigned char *)NET_AdrToString(adr, sizeof(adr), foradr)};
|
||||
size_t lengths[] = {strlen(strings[0]), strlen(strings[1])};
|
||||
static char randumb[16];
|
||||
const unsigned char *strings[] = {randumb, (const unsigned char *)NET_AdrToString(adr, sizeof(adr), foradr)};
|
||||
size_t lengths[] = {sizeof(randumb)-1, strlen(strings[1])};
|
||||
char digest[4*5];
|
||||
int digestsize = SHA1_m(digest, sizeof(digest), countof(lengths), strings, lengths);
|
||||
int digestsize;
|
||||
|
||||
if (!*randumb)
|
||||
{
|
||||
int i;
|
||||
srand(time(NULL)); //lame
|
||||
for (i = 0; i < sizeof(randumb)-1; i++)
|
||||
while (!randumb[i])
|
||||
randumb[i] = rand();
|
||||
}
|
||||
|
||||
digestsize = SHA1_m(digest, sizeof(digest), countof(lengths), strings, lengths);
|
||||
|
||||
#ifdef TCPCONNECT
|
||||
tobase64(out, outsize, digest, min(16, digestsize)); //truncate it, so its not excessive
|
||||
|
@ -677,212 +812,295 @@ void SVM_GenChallenge(char *out, size_t outsize, netadr_t *foradr)
|
|||
#endif
|
||||
}
|
||||
|
||||
void SVM_Think(int port)
|
||||
//switch net_from's reported connection, so we reply from a different udp socket from the one a packet was received from.
|
||||
static void SVM_SwitchQuerySocket(void)
|
||||
{
|
||||
char *s;
|
||||
int cookie = 0;
|
||||
int giveup = 500;
|
||||
|
||||
while (giveup-- > 0 && (cookie=NET_GetPacket (svm_sockets, cookie)) >= 0)
|
||||
size_t c;
|
||||
//switch the info query to our other udp socket, so any firewall/nat over the server blocks it.
|
||||
//this is to prevent people from thinking that the server is actually accessible.
|
||||
for (c = 0; c < countof(svm_sockets->conn); c++)
|
||||
{
|
||||
net_message.data[net_message.cursize] = '\0'; //null term all strings.
|
||||
if (!svm_sockets->conn[c])
|
||||
continue; //that one's dead, jim
|
||||
if (c+1 == net_from.connum)
|
||||
continue; //ignore this one, its the one we received the packet from
|
||||
//make sure its a datagram connection, and not some tcp weirdness.
|
||||
if (svm_sockets->conn[c]->prot == NP_DGRAM &&
|
||||
(svm_sockets->conn[c]->addrtype[0] == net_from.type || svm_sockets->conn[c]->addrtype[1] == net_from.type))
|
||||
{ //okay, looks like we should be able to respond on this one. lets see if their firewall stops us from finding out more about them.
|
||||
net_from.connum = c+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//well that's annoying. why is our networking code not doing this? LAME!
|
||||
if (net_from.type == NA_IPV6 &&
|
||||
!*(int*)&net_from.address.ip6[0] &&
|
||||
!*(int*)&net_from.address.ip6[4] &&
|
||||
!*(short*)&net_from.address.ip6[8] &&
|
||||
*(short*)&net_from.address.ip6[10]==(short)0xffff)
|
||||
{ //convert this ipv4-mapped-ipv6 address back to actual ipv4, so we don't get confused about stuff.
|
||||
net_from.type = NA_IP;
|
||||
*(int*)&net_from.address.ip[0] = *(int*)&net_from.address.ip6[12];
|
||||
//and null it out, just in case.
|
||||
*(int*)&net_from.address.ip6[8]=0;
|
||||
*(int*)&net_from.address.ip6[12]=0;
|
||||
}
|
||||
static void SVM_ProcessUDPPacket(void)
|
||||
{
|
||||
char *s, *line;
|
||||
|
||||
if (NET_WasSpecialPacket(svm_sockets))
|
||||
continue;
|
||||
|
||||
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");
|
||||
const char *resp=ext?"getserversExtResponse":"getserversResponse";
|
||||
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)
|
||||
{ //not a number, must have been a game name.
|
||||
s = COM_Parse(s);
|
||||
ver = strtol(com_token, NULL, 0);
|
||||
}
|
||||
else //first arg was a number. that means its vanilla quake3.
|
||||
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);
|
||||
else
|
||||
{
|
||||
char buf[256];
|
||||
Con_DPrintf("Unknown request filter: %s\n", COM_QuotedString(com_token, buf, sizeof(buf), false));
|
||||
}
|
||||
}
|
||||
if (!ipv4 && !ipv6)
|
||||
ipv4 = ipv6 = true; //neither specified? use both
|
||||
|
||||
|
||||
svm.total.queries++;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer)-2;
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
SZ_Write(&sb, resp, strlen(resp)); //WriteString, but without the null.
|
||||
SVM_AddIPAddresses(&sb, 0, ver, game, ipv4, ipv6, empty, full, true, gametype);
|
||||
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;
|
||||
char ourchallenge[256];
|
||||
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
||||
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, va("getinfo %s\n", ourchallenge));
|
||||
sb.cursize--;
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(com_token, "infoResponse"))
|
||||
{
|
||||
char ourchallenge[256];
|
||||
int clients;
|
||||
const char *game, *chal;
|
||||
svm_server_t *srv;
|
||||
s = MSG_ReadStringLine();
|
||||
svm.total.heartbeats++;
|
||||
chal = Info_ValueForKey(s, "challenge");
|
||||
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
||||
if (!strcmp(chal, ourchallenge))
|
||||
{
|
||||
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, 0, "Quake2", true, false, true, true, false, -1);
|
||||
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, 0, "QuakeWorld", true, false, true, true, false, -1);
|
||||
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++;
|
||||
//we shouldn't be taking anything else...
|
||||
if (net_from.prot != NP_DGRAM)
|
||||
{
|
||||
Con_DPrintf("master: ignoring non-datagram message\n");
|
||||
return;
|
||||
}
|
||||
|
||||
net_message.data[net_message.cursize] = '\0'; //null term all strings.
|
||||
|
||||
//well that's annoying. why is our networking code not doing this? LAME!
|
||||
if (net_from.type == NA_IPV6 &&
|
||||
!*(int*)&net_from.address.ip6[0] &&
|
||||
!*(int*)&net_from.address.ip6[4] &&
|
||||
!*(short*)&net_from.address.ip6[8] &&
|
||||
*(short*)&net_from.address.ip6[10]==(short)0xffff)
|
||||
{ //convert this ipv4-mapped-ipv6 address back to actual ipv4, so we don't get confused about stuff.
|
||||
net_from.type = NA_IP;
|
||||
*(int*)&net_from.address.ip[0] = *(int*)&net_from.address.ip6[12];
|
||||
//and null it out, just in case.
|
||||
*(int*)&net_from.address.ip6[8]=0;
|
||||
*(int*)&net_from.address.ip6[12]=0;
|
||||
}
|
||||
|
||||
if (NET_WasSpecialPacket(svm_sockets))
|
||||
{
|
||||
Con_DPrintf("master: ignoring special packet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
svm.time = Sys_DoubleTime();
|
||||
|
||||
MSG_BeginReading(msg_nullnetprim);
|
||||
if (MSG_ReadLong() != -1 || msg_badread)
|
||||
{ //go back to start...
|
||||
MSG_BeginReading(msg_nullnetprim);
|
||||
}
|
||||
line = MSG_ReadStringLine();
|
||||
s = COM_Parse(line);
|
||||
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");
|
||||
const char *resp=ext?"getserversExtResponse":"getserversResponse";
|
||||
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)
|
||||
{ //not a number, must have been a game name.
|
||||
s = COM_Parse(s);
|
||||
ver = strtol(com_token, NULL, 0);
|
||||
}
|
||||
else //first arg was a number. that means its vanilla quake3.
|
||||
Q_strncpyz(game, QUAKE3PROTOCOLNAME, 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);
|
||||
else
|
||||
{
|
||||
char buf[256];
|
||||
Con_DPrintf("Unknown request filter: %s\n", COM_QuotedString(com_token, buf, sizeof(buf), false));
|
||||
}
|
||||
}
|
||||
if (!ipv4 && !ipv6)
|
||||
ipv4 = ipv6 = true; //neither specified? use both
|
||||
|
||||
|
||||
svm.total.queries++;
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer)-2;
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
SZ_Write(&sb, resp, strlen(resp)); //WriteString, but without the null.
|
||||
SVM_AddIPAddresses(&sb, 0, ver, game, ipv4, ipv6, empty, full, true, gametype);
|
||||
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(QUAKE2PROTOCOLNAME, &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;
|
||||
char ourchallenge[256];
|
||||
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
||||
svm.total.queries++;
|
||||
|
||||
//placeholder listing...
|
||||
SVM_Heartbeat(NULL, &net_from, 0, svm.time + sv_heartbeattimeout.ival);
|
||||
SVM_SwitchQuerySocket();
|
||||
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer);
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteString(&sb, va("getinfo %s\n", ourchallenge));
|
||||
sb.cursize--;
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(com_token, "infoResponse"))
|
||||
{
|
||||
char ourchallenge[256];
|
||||
int clients;
|
||||
const char *game, *chal;
|
||||
svm_server_t *srv;
|
||||
s = MSG_ReadStringLine();
|
||||
svm.total.heartbeats++;
|
||||
chal = Info_ValueForKey(s, "challenge");
|
||||
SVM_GenChallenge(ourchallenge, sizeof(ourchallenge), &net_from);
|
||||
if (!strcmp(chal, ourchallenge))
|
||||
{
|
||||
clients = atoi(Info_ValueForKey(s, "clients"));
|
||||
game = Info_ValueForKey(s, "gamename");
|
||||
if (!*game)
|
||||
game = QUAKE3PROTOCOLNAME;
|
||||
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"));
|
||||
srv->needpass = !!atoi(Info_ValueForKey(s, "needpass"));
|
||||
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, 0, QUAKE2PROTOCOLNAME, true, false, true, true, false, -1);
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
else if (*com_token == S2M_HEARTBEAT) //sequence, players
|
||||
{ //quakeworld heartbeat
|
||||
int players;
|
||||
sizebuf_t sb;
|
||||
s = MSG_ReadStringLine();
|
||||
//sequence = atoi(s);
|
||||
s = MSG_ReadStringLine();
|
||||
players = atoi(s);
|
||||
svm.total.heartbeats++;
|
||||
|
||||
|
||||
//placeholder listing...
|
||||
SVM_Heartbeat(NULL, &net_from, players, svm.time + sv_heartbeattimeout.ival);
|
||||
SVM_SwitchQuerySocket();
|
||||
|
||||
//send it a proper query. We'll fill in the other details on response.
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
sb.maxsize = sizeof(net_message_buffer);
|
||||
sb.data = net_message_buffer;
|
||||
MSG_WriteLong(&sb, -1);
|
||||
MSG_WriteString(&sb, va("status %i\n", 1));
|
||||
sb.cursize--;
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
else if (*com_token == A2C_PRINT)
|
||||
{ //quakeworld response from 'status' requests, providing for actual info (and so that we know its reachable from other addresses)
|
||||
//there's no challenge, these could easily be spoofed. :(
|
||||
int clients;
|
||||
const char *game;
|
||||
svm_server_t *srv;
|
||||
s = ++line;
|
||||
|
||||
clients = atoi(Info_ValueForKey(s, "clients"));
|
||||
game = Info_ValueForKey(s, "gamename");
|
||||
if (!*game)
|
||||
game = QUAKEWORLDPROTOCOLNAME;
|
||||
srv = SVM_Heartbeat(game, &net_from, clients, svm.time + sv_heartbeattimeout.ival);
|
||||
if (srv)
|
||||
{
|
||||
if (developer.ival)
|
||||
Info_Print(s, "\t");
|
||||
srv->protover = 3;//atoi(Info_ValueForKey(s, "protocol"));
|
||||
srv->maxclients = atoi(Info_ValueForKey(s, "maxclients"));
|
||||
srv->needpass = !!atoi(Info_ValueForKey(s, "needpass"));
|
||||
Q_strncpyz(srv->hostname, Info_ValueForKey(s, "hostname"), sizeof(srv->hostname));
|
||||
Q_strncpyz(srv->gamedir, Info_ValueForKey(s, "*gamedir"), sizeof(srv->gamedir));
|
||||
Q_strncpyz(srv->mapname, Info_ValueForKey(s, "map"), sizeof(srv->mapname));
|
||||
}
|
||||
}
|
||||
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, 0, QUAKEWORLDPROTOCOLNAME, true, false, true, true, false, -1);
|
||||
NET_SendPacket(svm_sockets, sb.cursize, sb.data, &net_from);
|
||||
}
|
||||
else if (*com_token == A2A_PING)
|
||||
{ //quakeworld server ping request... because we can.
|
||||
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 if (*com_token == S2M_SHUTDOWN)
|
||||
{ //quakeworld server shutting down...
|
||||
//this isn't actually useful. we can't use it because we can't protect against spoofed denial-of-service attacks.
|
||||
//we could only use this by sending it a few pings to see if it is actually still responding. which is unreliable (especially if we're getting spammed by packet floods).
|
||||
}
|
||||
else
|
||||
svm.total.junk++;
|
||||
}
|
||||
|
||||
void SVM_Think(int port)
|
||||
{
|
||||
NET_ReadPackets (svm_sockets);
|
||||
SVM_RemoveOldServers();
|
||||
}
|
||||
#else
|
||||
|
@ -928,10 +1146,73 @@ static void SVM_Status_f(void)
|
|||
Con_Printf("Queries/min: %f\n", (s1->queries-s2->queries)/period);
|
||||
}
|
||||
|
||||
static void SVM_RegisterAlias(svm_game_t *game, char *aliasname)
|
||||
{
|
||||
const char *a;
|
||||
size_t l;
|
||||
svm_game_t *aliasgame;
|
||||
if (!game)
|
||||
return;
|
||||
|
||||
//make sure we never have dupes. they confuse EVERYTHING.
|
||||
aliasgame = SVM_FindGame(aliasname, false);
|
||||
if (aliasgame == game)
|
||||
return; //already in there somehow.
|
||||
if (aliasgame)
|
||||
{
|
||||
Con_Printf("game alias of %s is already registered\n", aliasname);
|
||||
return;
|
||||
}
|
||||
game->persistent = true; //don't forget us!
|
||||
|
||||
if (!*aliasname)
|
||||
return;
|
||||
|
||||
a = game->aliases;
|
||||
if (a) for (; *a; a+=strlen(a)+1);
|
||||
l = a-game->aliases;
|
||||
game->aliases = BZ_Realloc(game->aliases, l+strlen(aliasname)+2);
|
||||
memcpy(game->aliases+l, aliasname, strlen(aliasname)+1);
|
||||
l += strlen(aliasname)+1;
|
||||
game->aliases[l] = 0;
|
||||
}
|
||||
static qboolean SVM_FoundManifest(void *usr, ftemanifest_t *man)
|
||||
{
|
||||
svm_game_t *game;
|
||||
const char *g;
|
||||
if (man->protocolname)
|
||||
{ //FIXME: we ought to do this for each manifest we could find.
|
||||
g = man->protocolname;
|
||||
|
||||
#if 1
|
||||
game = SVM_FindGame(man->formalname, 2);
|
||||
#else
|
||||
g = COM_Parse(g);
|
||||
game = SVM_FindGame(com_token, 2);
|
||||
#endif
|
||||
while (*g)
|
||||
{
|
||||
g = COM_Parse(g);
|
||||
SVM_RegisterAlias(game, com_token);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void SVM_GameAlias_f(void)
|
||||
{
|
||||
svm_game_t *game = SVM_FindGame(Cmd_Argv(1), 2);
|
||||
if (!game)
|
||||
{
|
||||
Con_Printf("Unable to register game %s\n", Cmd_Argv(1));
|
||||
return;
|
||||
}
|
||||
SVM_RegisterAlias(game, Cmd_Argv(2));
|
||||
}
|
||||
void SV_Init (struct quakeparms_s *parms)
|
||||
{
|
||||
int manarg;
|
||||
char *g;
|
||||
|
||||
COM_InitArgv (parms->argc, parms->argv);
|
||||
|
||||
|
@ -957,8 +1238,9 @@ void SV_Init (struct quakeparms_s *parms)
|
|||
|
||||
Cmd_AddCommand ("quit", SV_Quit_f);
|
||||
Cmd_AddCommand ("status", SVM_Status_f);
|
||||
Cmd_AddCommand ("gamealias", SVM_GameAlias_f);
|
||||
|
||||
svm_sockets = FTENET_CreateCollection(true);
|
||||
svm_sockets = FTENET_CreateCollection(true, SVM_ProcessUDPPacket);
|
||||
Hash_InitTable(&svm.serverhash, 1024, Z_Malloc(Hash_BytesForBuckets(1024)));
|
||||
|
||||
Cvar_Register(&sv_masterport, "server control variables");
|
||||
|
@ -966,6 +1248,9 @@ void SV_Init (struct quakeparms_s *parms)
|
|||
Cvar_Register(&sv_heartbeattimeout, "server control variables");
|
||||
Cvar_Register(&sv_maxgames, "server control variables");
|
||||
Cvar_Register(&sv_maxservers, "server control variables");
|
||||
Cvar_Register(&sv_hideinactivegames, "server control variables");
|
||||
Cvar_Register(&sv_sortlist, "server control variables");
|
||||
Cvar_Register(&sv_hostname, "server control variables");
|
||||
|
||||
Cvar_ParseWatches();
|
||||
host_initialized = true;
|
||||
|
@ -987,12 +1272,8 @@ void SV_Init (struct quakeparms_s *parms)
|
|||
Cvar_ForceCallback(&sv_masterport);
|
||||
Cvar_ForceCallback(&sv_masterport_tcp);
|
||||
|
||||
if (fs_manifest->protocolname)
|
||||
for (g = fs_manifest->protocolname; *g; )
|
||||
{
|
||||
g = COM_Parse(g);
|
||||
SVM_FindGame(com_token, 2);
|
||||
}
|
||||
SVM_FoundManifest(NULL, fs_manifest);
|
||||
FS_EnumerateKnownGames(SVM_FoundManifest, NULL);
|
||||
|
||||
Con_Printf ("Exe: %s\n", version_string());
|
||||
|
||||
|
|
|
@ -85,6 +85,8 @@ int demomsgtype;
|
|||
int demomsgto;
|
||||
static char demomsgbuf[MAX_OVERALLMSGLEN];
|
||||
|
||||
static void SV_MVD_Stopped(void);
|
||||
|
||||
static mvddest_t *singledest; //used when a stream is starting up so redundant data doesn't get dumped into other streams
|
||||
static struct reversedest_s
|
||||
{
|
||||
|
@ -557,7 +559,8 @@ hashedpassword:
|
|||
{
|
||||
if (p->hasauthed == true)
|
||||
{
|
||||
SV_MVD_Record(SV_MVD_InitStream(clientstream, userinfo));
|
||||
if (!SV_MVD_Record(SV_MVD_InitStream(clientstream, userinfo)))
|
||||
return QTV_ERROR;
|
||||
return QTV_ACCEPT;
|
||||
}
|
||||
}
|
||||
|
@ -573,7 +576,8 @@ hashedpassword:
|
|||
e = NULL;
|
||||
dst = SV_MVD_InitStream(clientstream, userinfo);
|
||||
dst->droponmapchange = p->isreverse;
|
||||
SV_MVD_Record(dst);
|
||||
if (!SV_MVD_Record(dst))
|
||||
return QTV_ERROR;
|
||||
return QTV_ACCEPT;
|
||||
}
|
||||
else
|
||||
|
@ -1531,7 +1535,7 @@ void SV_MVDStop (enum mvdclosereason_e reason, qboolean mvdonly)
|
|||
// stop and remove
|
||||
|
||||
if (!demo.dest)
|
||||
sv.mvdrecording = false;
|
||||
SV_MVD_Stopped();
|
||||
|
||||
if (reason == MVD_CLOSE_DISCONNECTED)
|
||||
SV_BroadcastPrintf (PRINT_CHAT, "QTV disconnected\n");
|
||||
|
@ -1554,7 +1558,7 @@ void SV_MVDStop (enum mvdclosereason_e reason, qboolean mvdonly)
|
|||
DestCloseAllFlush(reason, mvdonly);
|
||||
|
||||
if (!demo.dest) //might still be streaming qtv.
|
||||
sv.mvdrecording = false;
|
||||
SV_MVD_Stopped();
|
||||
|
||||
Cvar_ForceSet(Cvar_Get("serverdemo", "", CVAR_NOSET, ""), "");
|
||||
}
|
||||
|
@ -1709,6 +1713,17 @@ qboolean SV_MVD_Record (mvddest_t *dest)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void SV_MVD_Stopped(void)
|
||||
{ //all recording has stopped. clean up any demo.recorder state
|
||||
if (demo.recorder.frameunion.frames)
|
||||
{
|
||||
Z_Free(demo.recorder.frameunion.frames);
|
||||
demo.recorder.frameunion.frames = NULL;
|
||||
}
|
||||
sv.mvdrecording = false;
|
||||
memset(&demo, 0, sizeof(demo));
|
||||
}
|
||||
|
||||
void SV_EnableClientsCSQC(void);
|
||||
void SV_MVD_SendInitialGamestate(mvddest_t *dest)
|
||||
{
|
||||
|
|
|
@ -63,9 +63,10 @@ cvar_t sys_extrasleep = CVAR("sys_extrasleep","0");
|
|||
cvar_t sys_colorconsole = CVAR("sys_colorconsole", "1");
|
||||
cvar_t sys_linebuffer = CVARC("sys_linebuffer", "1", Sys_Linebuffer_Callback);
|
||||
|
||||
qboolean stdin_ready;
|
||||
static qboolean stdin_ready;
|
||||
static qboolean noconinput = false;
|
||||
|
||||
struct termios orig, changes;
|
||||
static struct termios orig, changes;
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
@ -216,7 +217,11 @@ void Sys_Error (const char *error, ...)
|
|||
COM_WorkerAbort(string);
|
||||
printf ("Fatal error: %s\n",string);
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSADRAIN, &orig);
|
||||
if (!noconinput)
|
||||
{
|
||||
tcsetattr(STDIN_FILENO, TCSADRAIN, &orig);
|
||||
fcntl (STDIN_FILENO, F_SETFL, fcntl (STDIN_FILENO, F_GETFL, 0) & ~FNDELAY);
|
||||
}
|
||||
|
||||
//we used to fire sigsegv. this resulted in people reporting segfaults and not the error message that appeared above. resulting in wasted debugging.
|
||||
//abort should trigger a SIGABRT and still give us the same stack trace. should be more useful that way.
|
||||
|
@ -497,12 +502,14 @@ Sys_Quit
|
|||
*/
|
||||
void Sys_Quit (void)
|
||||
{
|
||||
tcsetattr(STDIN_FILENO, TCSADRAIN, &orig);
|
||||
if (!noconinput)
|
||||
{
|
||||
tcsetattr(STDIN_FILENO, TCSADRAIN, &orig);
|
||||
fcntl (STDIN_FILENO, F_SETFL, fcntl (STDIN_FILENO, F_GETFL, 0) & ~FNDELAY);
|
||||
}
|
||||
exit (0); // appkit isn't running
|
||||
}
|
||||
|
||||
static int do_stdin = 1;
|
||||
|
||||
#if 1
|
||||
static char *Sys_LineInputChar(char *line)
|
||||
{
|
||||
|
@ -570,7 +577,11 @@ it to the host command processor
|
|||
================
|
||||
*/
|
||||
void Sys_Linebuffer_Callback (struct cvar_s *var, char *oldvalue)
|
||||
{
|
||||
{ //reconfigures the tty to send a char at a time (or line at a time)
|
||||
|
||||
if (noconinput)
|
||||
return; //oh noes! we already hungup!
|
||||
|
||||
changes = orig;
|
||||
if (var->value)
|
||||
{
|
||||
|
@ -598,32 +609,64 @@ char *Sys_ConsoleInput (void)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (!stdin_ready || !do_stdin)
|
||||
if (!stdin_ready || noconinput==true)
|
||||
return NULL; // the select didn't say it was ready
|
||||
stdin_ready = false;
|
||||
|
||||
if (sys_linebuffer.value == 0)
|
||||
//libraries and muxers and things can all screw with our stdin blocking state.
|
||||
//if a server sits around waiting for its never-coming stdin then we're screwed.
|
||||
//and don't assume that it won't block just because select told us it was readable, select lies.
|
||||
//so force it non-blocking so we don't get any nasty surprises.
|
||||
#if defined(__linux__)
|
||||
{
|
||||
text[0] = getc(stdin);
|
||||
text[1] = 0;
|
||||
len = 1;
|
||||
return Sys_LineInputChar(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = read (0, text, sizeof(text)-1);
|
||||
if (len == 0)
|
||||
int fl = fcntl (STDIN_FILENO, F_GETFL, 0);
|
||||
if (!(fl & FNDELAY))
|
||||
{
|
||||
// end of file
|
||||
do_stdin = 0;
|
||||
fcntl(STDIN_FILENO, F_SETFL, fl | FNDELAY);
|
||||
// Sys_Printf(CON_WARNING "stdin flags became blocking - gdb bug?\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
len = read (STDIN_FILENO, text, sizeof(text)-1);
|
||||
if (len < 0)
|
||||
{
|
||||
int err = errno;
|
||||
switch(err)
|
||||
{
|
||||
case EINTR: //unix sucks
|
||||
case EAGAIN: //a select fuckup?
|
||||
break;
|
||||
case EIO:
|
||||
noconinput |= 2;
|
||||
stdin_ready = true;
|
||||
return NULL;
|
||||
default:
|
||||
Con_Printf("error %i reading from stdin\n", err);
|
||||
noconinput = true; //we don't know what it was, but don't keep triggering it.
|
||||
return NULL;
|
||||
}
|
||||
if (len < 1)
|
||||
return NULL;
|
||||
text[len-1] = 0; // rip off the /n and terminate
|
||||
|
||||
return text;
|
||||
}
|
||||
if (noconinput&2)
|
||||
{ //posix job stuff sucks - there's no way to detect when we're directly pushed to the foreground after being backgrounded.
|
||||
Con_Printf("Welcome back!\n");
|
||||
noconinput &= ~2;
|
||||
}
|
||||
|
||||
/*if (len == 0)
|
||||
{
|
||||
// end of file? doesn't really make sense. depend upon sighup instead
|
||||
Con_Printf("EOF reading from stdin\n");
|
||||
noconinput = true;
|
||||
return NULL;
|
||||
}*/
|
||||
if (len < 1)
|
||||
return NULL;
|
||||
text[len-1] = 0; // rip off the /n and terminate
|
||||
|
||||
if (sys_linebuffer.value == 0)
|
||||
return Sys_LineInputChar(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -867,6 +910,16 @@ static int Sys_CheckChRoot(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
static void SigCont(int code)
|
||||
{ //lets us know when we regained foreground focus.
|
||||
int fl = fcntl (STDIN_FILENO, F_GETFL, 0);
|
||||
if (!(fl & FNDELAY))
|
||||
fcntl(STDIN_FILENO, F_SETFL, fl | FNDELAY);
|
||||
noconinput &= ~2;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
=============
|
||||
main
|
||||
|
@ -898,6 +951,8 @@ int main(int argc, char *argv[])
|
|||
useansicolours = false;
|
||||
else
|
||||
useansicolours = (isatty(STDOUT_FILENO) || COM_CheckParm("-colour") || COM_CheckParm("-color"));
|
||||
if (COM_CheckParm("-nostdin"))
|
||||
noconinput = true;
|
||||
|
||||
switch(Sys_CheckChRoot())
|
||||
{
|
||||
|
@ -952,6 +1007,12 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
signal(SIGTTIN, SIG_IGN); //have to ignore this if we want to not lock up when running backgrounded.
|
||||
signal(SIGCONT, SigCont);
|
||||
signal(SIGCHLD, SIG_IGN); //mapcluster stuff might leak zombie processes if we don't do this.
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef SUBSERVERS
|
||||
if (COM_CheckParm("-clusterslave"))
|
||||
|
@ -970,8 +1031,8 @@ int main(int argc, char *argv[])
|
|||
//
|
||||
while (1)
|
||||
{
|
||||
if (do_stdin)
|
||||
stdin_ready = NET_Sleep(maxsleep, true);
|
||||
if (noconinput != true)
|
||||
stdin_ready |= NET_Sleep(maxsleep, true);
|
||||
else
|
||||
{
|
||||
NET_Sleep(maxsleep, false);
|
||||
|
|
|
@ -169,7 +169,6 @@ xfont_t *XS_CreateFont(int id, xclient_t *owner, char *fontname);
|
|||
void XS_CreateInitialResources(void);
|
||||
void XS_DestroyResource(xresource_t *res);
|
||||
void XS_DestroyResourcesOfClient(xclient_t *cl);
|
||||
void XS_CheckResourceSentinals(void);
|
||||
|
||||
|
||||
void XW_ExposeWindow(xwindow_t *root, int x, int y, int width, int height);
|
||||
|
|
Loading…
Reference in a new issue