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:
Spoike 2020-02-26 00:37:52 +00:00
parent 62f2a380e1
commit fe28099e68
30 changed files with 2126 additions and 1468 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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++)

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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++;

View File

@ -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)
{

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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 ();

View File

@ -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);

View File

@ -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)
{

View File

@ -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;

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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;

View File

@ -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;
}
/*

View File

@ -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, "&apos;", 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?"&#x1F512;":"", 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?"&#x1F512;":"", 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());

View File

@ -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)
{

View File

@ -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);

View File

@ -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);