Add r_meshroll, like r_meshpitch (hexen2 requires it set to -1 - they made the bug worse).

HTTP client code now tracks the reason for failure better (so we can distinguish between dns, no response, disconnects, and tls cert issues).
Downloads menu shows reasons for failure for its various sources.
make hexen2's +infoplaque button work even when tab isn't held! Yes! usability! who'd a thought it?
Try to clean up some client-only build issues.
Added 'librequake' as a recognised game name (mostly so that we can reuse the quake-specific engine compat settings.
.map support now supports patches enough to query+create(+network)+select them. Collisions are basically defective though.
qcc: Remove char as default keyword.
qcc: Fix some recent regressions.
sv: Add 'sv_protocol csqc' setting to force csqc protocols on clients that disabled handshakes, for mods that NEED csqc support. other options include 'fte1' and 'fte2', if you want to force extra stuff. Engines that don't support the selected protocols will crash out so only set these if your mod/map always requires it.
sv: default to qw physics whenever a mod defines the SV_RunClientCommand, even if its an nq mod.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5775 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2020-10-06 03:17:28 +00:00
parent 524fdb3dfd
commit 432bc96456
39 changed files with 1100 additions and 298 deletions

View file

@ -2013,11 +2013,7 @@ void CL_RotateAroundTag(entity_t *ent, int entnum, int parenttagent, int parentt
else
model = NULL;
if (model && model->type == mod_alias)
{
ang[0]*=r_meshpitch.value;
AngleVectors(ang, axis[0], axis[1], axis[2]);
ang[0]*=r_meshpitch.value;
}
AngleVectorsMesh(ang, axis[0], axis[1], axis[2]);
else
AngleVectors(ang, axis[0], axis[1], axis[2]);
VectorInverse(axis[1]);
@ -2160,10 +2156,8 @@ entity_t *V_AddEntity(entity_t *in)
*ent = *in;
ent->angles[0]*=r_meshpitch.value;
AngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
AngleVectorsMesh(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
VectorInverse(ent->axis[1]);
ent->angles[0]*=r_meshpitch.value;
return ent;
}
@ -2190,10 +2184,8 @@ void VQ2_AddLerpEntity(entity_t *in) //a convienience function
ent->framestate.g[FS_REG].lerpfrac = back;
ent->angles[0]*=r_meshpitch.value;
AngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
AngleVectorsMesh(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
VectorInverse(ent->axis[1]);
ent->angles[0]*=r_meshpitch.value;
}
*/
int V_AddLight (int entsource, vec3_t org, float quant, float r, float g, float b)
@ -3333,10 +3325,8 @@ void CL_LinkStaticEntities(void *pvs, int *areas)
//figure out the correct axis for the model
if (clmodel && clmodel->type == mod_alias && (cls.protocol == CP_QUAKEWORLD || cls.protocol == CP_NETQUAKE))
{ //q2 is fixed, but q1 pitches the wrong way
stat->state.angles[0]*=r_meshpitch.value;
AngleVectors(stat->state.angles, stat->ent.axis[0], stat->ent.axis[1], stat->ent.axis[2]);
stat->state.angles[0]*=r_meshpitch.value;
{ //q2 is fixed, but q1 pitches the wrong way, and hexen2 rolls the wrong way too.
AngleVectorsMesh(stat->state.angles, stat->ent.axis[0], stat->ent.axis[1], stat->ent.axis[2]);
}
else
AngleVectors(stat->state.angles, stat->ent.axis[0], stat->ent.axis[1], stat->ent.axis[2]);
@ -4072,8 +4062,9 @@ void CL_LinkPacketEntities (void)
{
VectorCopy(le->angles, angles);
//if (model && model->type == mod_alias)
angles[0]*=r_meshpitch.value; //pflags matches alias models.
AngleVectors(angles, dl->axis[0], dl->axis[1], dl->axis[2]);
AngleVectorsMesh(angles, dl->axis[0], dl->axis[1], dl->axis[2]); //pflags matches alias models.
//else
// AngleVectors(angles, dl->axis[0], dl->axis[1], dl->axis[2]);
VectorInverse(dl->axis[1]);
R_LoadNumberedLightTexture(dl, state->skinnum);
}
@ -4333,10 +4324,8 @@ void CL_LinkPacketEntities (void)
}
}
if (model && model->type == mod_alias)
angles[0]*=r_meshpitch.value; //carmack screwed up when he added alias models - they pitch the wrong way.
VectorCopy(angles, ent->angles);
AngleVectors(angles, ent->axis[0], ent->axis[1], ent->axis[2]);
AngleVectorsMesh(angles, ent->axis[0], ent->axis[1], ent->axis[2]);
VectorInverse(ent->axis[1]);
/*if this entity is in a player's slot...*/
@ -4578,10 +4567,8 @@ void CL_LinkProjectiles (void)
VectorCopy (pr->origin, ent->origin);
VectorCopy (pr->angles, ent->angles);
ent->angles[0]*=r_meshpitch.value;
AngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
AngleVectorsMesh(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
VectorInverse(ent->axis[1]);
ent->angles[0]*=r_meshpitch.value;
}
}
@ -5110,8 +5097,7 @@ void CL_AddFlagModels (entity_t *ent, int team)
newent->angles[2] -= 45;
VectorCopy(newent->angles, angles);
angles[0]*=r_meshpitch.value;
AngleVectors(angles, newent->axis[0], newent->axis[1], newent->axis[2]);
AngleVectorsMesh(angles, newent->axis[0], newent->axis[1], newent->axis[2]);
VectorInverse(newent->axis[1]);
}
@ -5369,7 +5355,10 @@ void CL_LinkPlayers (void)
}
if (model && model->type == mod_alias)
{
angles[0]*=r_meshpitch.value; //carmack screwed up when he added alias models - they pitch the wrong way.
angles[2]*=r_meshroll.value; //hexen2 screwed it up even more - they roll the wrong way.
}
VectorCopy(angles, ent->angles);
AngleVectors(angles, ent->axis[0], ent->axis[1], ent->axis[2]);
VectorInverse(ent->axis[1]);
@ -5707,6 +5696,7 @@ void CL_SetSolidEntities (void)
pent->model = mod;
VectorCopy (state->angles, pent->angles);
pent->angles[0]*=r_meshpitch.value;
pent->angles[2]*=r_meshroll.value;
}
else
{

View file

@ -4679,7 +4679,7 @@ void CL_Status_f(void)
for (i = 0; i < csqc_world.num_edicts; i++)
{
e = EDICT_NUM_PB(csqc_world.progs, i);
if (e && e->ereftype == ER_FREE && sv.time - e->freetime > 0.5)
if (e && e->ereftype == ER_FREE && Sys_DoubleTime() - e->freetime > 0.5)
continue; //free, and older than the zombie time
count++;
}
@ -5972,6 +5972,7 @@ void CL_UpdateHeadAngles(void)
R_ConcatRotations(headchange, tmp, tmp2);
VectorAngles(tmp2[0], tmp2[2], pv->viewangles);
pv->viewangles[0] *= r_meshpitch.value;
pv->viewangles[2] *= r_meshroll.value;
//fall through
default:

View file

@ -4803,11 +4803,7 @@ static void CL_ParseStaticProt (int baselinetype)
VectorCopy (es.origin, ent->origin);
VectorCopy (es.angles, ent->angles);
if (ent->model && ent->model->type == mod_alias)
{
es.angles[0]*=r_meshpitch.value;
AngleVectors(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]);
es.angles[0]*=r_meshpitch.value;
}
AngleVectorsMesh(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]);
else
AngleVectors(es.angles, ent->axis[0], ent->axis[1], ent->axis[2]);
VectorInverse(ent->axis[1]);

View file

@ -1425,6 +1425,7 @@ void CL_PredictMovePNum (int seat)
if (pv->pmovetype != PM_6DOF)
le->angles[0] *= 0.333;
le->angles[0] *= r_meshpitch.value;
le->angles[2] *= r_meshroll.value;
}
}

View file

@ -2496,6 +2496,7 @@ void CL_SpawnSpriteEffect(vec3_t org, vec3_t dir, vec3_t orientationup, model_t
else
ex->angles[1] = 0;
ex->angles[0]*=r_meshpitch.value;
ex->angles[2]*=r_meshroll.value;
}
@ -3205,6 +3206,7 @@ void CL_UpdateExplosions (void)
VectorCopy (ex->angles, ent->angles);
ent->skinnum = ex->skinnum;
ent->angles[0]*=r_meshpitch.value;
ent->angles[2]*=r_meshroll.value;
AngleVectors(ent->angles, ent->axis[0], ent->axis[1], ent->axis[2]);
VectorInverse(ent->axis[1]);
ent->model = ex->model;

View file

@ -1998,7 +1998,8 @@ static void CLQ2_AddPacketEntities (q2frame_t *frame)
}
}
ent.angles[0]*=r_meshpitch.value; //q2 has it fixed.
ent.angles[0]*=r_meshpitch.value; //q2 has it fixed. consistency...
ent.angles[2]*=r_meshroll.value; //h2 doesn't. consistency...
if (s1->number == cl.playerview[0].playernum+1) //woo! this is us!
{

View file

@ -2680,9 +2680,7 @@ static void Con_DrawModelPreview(model_t *model, float x, float y, float w, floa
ent.scale = 1;
ent.angles[1] = realtime*90;//mods->yaw;
// ent.angles[0] = realtime*23.4;//mods->pitch;
ent.angles[0]*=r_meshpitch.value;
AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
ent.angles[0]*=r_meshpitch.value;
AngleVectorsMesh(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
VectorInverse(ent.axis[1]);
//ent.origin[2] -= (ent.model->maxs[2]-ent.model->mins[2]) * 0.5 + ent.model->mins[2];
@ -2725,9 +2723,7 @@ static void Con_DrawModelPreview(model_t *model, float x, float y, float w, floa
if (ent.model->camerabone>0 && Mod_GetTag(ent.model, ent.model->camerabone, &ent.framestate, transforms))
{
VectorClear(ent.origin);
ent.angles[0]*=r_meshpitch.value;
AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
ent.angles[0]*=r_meshpitch.value;
AngleVectorsMesh(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
VectorInverse(ent.axis[1]);
scale = 1;
{
@ -2749,9 +2745,7 @@ static void Con_DrawModelPreview(model_t *model, float x, float y, float w, floa
else
{
ent.angles[1] = realtime*90;//mods->yaw;
ent.angles[0]*=r_meshpitch.value;
AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
ent.angles[0]*=r_meshpitch.value;
AngleVectorsMesh(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
VectorScale(ent.axis[0], scale, ent.axis[0]);
VectorScale(ent.axis[1], -scale, ent.axis[1]);
VectorScale(ent.axis[2], scale, ent.axis[2]);

View file

@ -223,7 +223,11 @@ static struct
{
SRCSTAT_UNKNOWN, //we don't know whether the user wants to allow or block this source.
SRCSTAT_DISABLED, //do NOT query this source
SRCSTAT_FAILED, //tried but failed. FIXME: add some reasons.
SRCSTAT_FAILED_DNS, //tried but failed, unresolvable
SRCSTAT_FAILED_NORESP, //tried but failed, no response.
SRCSTAT_FAILED_EOF, //tried but failed, abrupt termination.
SRCSTAT_FAILED_MITM, //tried but failed. misc cert problems.
SRCSTAT_FAILED_HTTP, //tried but failed, misc http failure
SRCSTAT_PENDING, //waiting for response (or queued). don't show package list yet.
SRCSTAT_OBTAINED, //we got a response.
} status;
@ -2081,7 +2085,7 @@ static void PM_ListDownloaded(struct dl_download *dl)
size_t listidx = dl->user_num;
size_t sz = 0;
char *f = NULL;
if (dl->file)
if (dl->file && dl->status != DL_FAILED)
{
sz = VFS_GETLEN(dl->file);
f = BZ_Malloc(sz+1);
@ -2115,10 +2119,20 @@ static void PM_ListDownloaded(struct dl_download *dl)
{
downloadablelist[listidx].status = SRCSTAT_OBTAINED;
PM_ParsePackageList(f, 0, dl->url, downloadablelist[listidx].prefix);
BZ_Free(f);
}
else if (dl->replycode == HTTP_DNSFAILURE)
downloadablelist[listidx].status = SRCSTAT_FAILED_DNS;
else if (dl->replycode == HTTP_NORESPONSE)
downloadablelist[listidx].status = SRCSTAT_FAILED_NORESP;
else if (dl->replycode == HTTP_EOF)
downloadablelist[listidx].status = SRCSTAT_FAILED_EOF;
else if (dl->replycode == HTTP_MITM || dl->replycode == HTTP_UNTRUSTED)
downloadablelist[listidx].status = SRCSTAT_FAILED_MITM;
else if (dl->replycode && dl->replycode < 900)
downloadablelist[listidx].status = SRCSTAT_FAILED_HTTP;
else
downloadablelist[listidx].status = SRCSTAT_FAILED;
downloadablelist[listidx].status = SRCSTAT_FAILED_EOF;
BZ_Free(f);
if (!doautoupdate && !domanifestinstall)
return; //don't spam this.
@ -2252,7 +2266,7 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
if (allowphonehome<=0)
{
downloadablelist[i].status = SRCSTAT_FAILED;
downloadablelist[i].status = SRCSTAT_FAILED_DNS;
continue;
}
downloadablelist[i].curdl = HTTP_CL_Get(va("%s%s"DOWNLOADABLESARGS, downloadablelist[i].url, strchr(downloadablelist[i].url,'?')?"&":"?"), NULL, PM_ListDownloaded);
@ -2267,7 +2281,7 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
else
{
Con_Printf("Could not contact updates server - %s\n", downloadablelist[i].url);
downloadablelist[i].status = SRCSTAT_FAILED;
downloadablelist[i].status = SRCSTAT_FAILED_DNS;
}
}
@ -3030,7 +3044,7 @@ int PM_IsApplying(qboolean listsonly)
int count = 0;
#ifdef WEBCLIENT
package_t *p;
int i;
// int i;
if (!listsonly)
{
for (p = availablepackages; p ; p=p->next)
@ -3039,11 +3053,11 @@ int PM_IsApplying(qboolean listsonly)
count++;
}
}
for (i = 0; i < numdownloadablelists; i++)
/*for (i = 0; i < numdownloadablelists; i++)
{
if (downloadablelist[i].curdl)
count++;
}
}*/
#endif
return count;
}
@ -4541,10 +4555,33 @@ static void MD_Source_Draw (int x, int y, struct menucustom_s *c, struct emenu_s
switch(downloadablelist[c->dint].status)
{
case SRCSTAT_OBTAINED:
case SRCSTAT_PENDING:
Draw_FunStringWidth (x, y, "^&02 ", 48, 2, false); //green
break;
case SRCSTAT_FAILED:
case SRCSTAT_PENDING:
Draw_FunStringWidth (x, y, "^&0E ", 48, 2, false); //yellow
Draw_FunStringWidth (x, y, "??", 48, 2, false); //this should be fast... so if they get a chance to see the ?? then there's something bad happening, and the ?? is appropriate.
break;
case SRCSTAT_FAILED_DNS:
Draw_FunStringWidth (x, y, "^&0E ", 48, 2, false); //yellow
Draw_FunStringWidth (x, y, "DNS", 48, 2, false);
break;
case SRCSTAT_FAILED_NORESP:
Draw_FunStringWidth (x, y, "^&0E ", 48, 2, false); //yellow
Draw_FunStringWidth (x, y, "NR", 48, 2, false);
break;
case SRCSTAT_FAILED_EOF:
Draw_FunStringWidth (x, y, "^&0E ", 48, 2, false); //yellow
Draw_FunStringWidth (x, y, "EOF", 48, 2, false);
break;
case SRCSTAT_FAILED_MITM:
Draw_FunStringWidth (x, y, "^&04 ", 48, 2, false); //red
Draw_FunStringWidth (x, y, "^bMITM", 48, 2, false);
break;
case SRCSTAT_FAILED_HTTP:
Draw_FunStringWidth (x, y, "^&0E ", 48, 2, false); //yellow
Draw_FunStringWidth (x, y, "404", 48, 2, false);
break;
default:
case SRCSTAT_UNKNOWN:
Draw_FunStringWidth (x, y, "^&0E ", 48, 2, false); //yellow
break;
@ -4564,7 +4601,7 @@ static qboolean MD_Source_Key (struct menucustom_s *c, struct emenu_s *m, int ke
{
case SRCSTAT_OBTAINED:
case SRCSTAT_PENDING:
case SRCSTAT_FAILED:
default: //various failures
case SRCSTAT_UNKNOWN:
downloadablelist[c->dint].status = SRCSTAT_DISABLED;
break;
@ -4865,18 +4902,18 @@ static void MD_Download_UpdateStatus(struct emenu_s *m)
if (!info->populated)
{
for (i = 0; i < numdownloadablelists; i++)
y = 48;
/*for (i = 0; i < numdownloadablelists; i++)
{
if (downloadablelist[i].status == SRCSTAT_PENDING)
{
Draw_FunStringWidth(0, vid.height - 8, "Querying for package list", vid.width, 2, false);
Draw_FunStringWidth(0, y, "Querying for package list", vid.width, 2, false);
return;
}
}
}*/
info->populated = true;
MC_AddFrameStart(m, 48);
y = 48;
#ifdef WEBCLIENT
for (i = 0; i < numdownloadablelists; i++)
{

View file

@ -2338,6 +2338,7 @@ qboolean M_Apply_SP_Cheats_H2 (union menuoption_s *op,struct emenu_s *menu,int k
if (key != K_ENTER && key != K_KP_ENTER && key != K_GP_START && key != K_MOUSE1)
return false;
#ifdef HAVE_SERVER
switch(info->skillcombo->selectedoption)
{
case 0:
@ -2356,6 +2357,7 @@ qboolean M_Apply_SP_Cheats_H2 (union menuoption_s *op,struct emenu_s *menu,int k
if ((unsigned)info->mapcombo->selectedoption < countof(maplist_h2))
Cbuf_AddText(va("map %s\n", maplist_h2[info->mapcombo->selectedoption]), RESTRICT_LOCAL);
#endif
M_RemoveMenu(menu);
Cbuf_AddText("menu_spcheats\n", RESTRICT_LOCAL);
@ -2404,8 +2406,8 @@ void M_Menu_Singleplayer_Cheats_Hexen2 (void)
y+=8;
#ifdef HAVE_SERVER
info->skillcombo = MC_AddCombo(menu,16,170, y, "Difficulty", skilloptions, currentskill); y+=8;
#endif
info->mapcombo = MC_AddCombo(menu,16,170, y, "Map", mapoptions_h2, currentmap); y+=8;
#endif
#ifdef HAVE_SERVER
MC_AddCheckBox(menu, 16, 170, y, "Cheats", &sv_cheats,0); y+=8;
#endif
@ -3264,14 +3266,16 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
// ent.angles[1] = realtime*45;//mods->yaw;
// ent.angles[0] = realtime*23.4;//mods->pitch;
ent.angles[0]*=r_meshpitch.value;
AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
ent.angles[0]*=r_meshpitch.value;
VectorInverse(ent.axis[1]);
ent.model = Mod_ForName(mods->modelname, MLV_WARN);
if (!ent.model)
return; //panic!
if (ent.model->type == mod_alias) //should we even bother with this here?
AngleVectorsMesh(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
else
AngleVectors(ent.angles, ent.axis[0], ent.axis[1], ent.axis[2]);
VectorInverse(ent.axis[1]);
ent.scale = max(max(fabs(ent.model->maxs[0]-ent.model->mins[0]), fabs(ent.model->maxs[1]-ent.model->mins[1])), fabs(ent.model->maxs[2]-ent.model->mins[2]));
ent.scale = ent.scale?64.0/ent.scale:1;
// ent.scale = 1;

View file

@ -6827,6 +6827,11 @@ static struct {
{"brush_getfacepoints", PF_brush_getfacepoints, 0},
{"brush_calcfacepoints", PF_brush_calcfacepoints,0},
{"brush_findinvolume", PF_brush_findinvolume, 0},
{"patch_getcp", PF_patch_getcp, 0},
{"patch_getmesh", PF_patch_getmesh, 0},
{"patch_create", PF_patch_create, 0},
// {"patch_calculate", PF_patch_calculate, 0},
#endif
#ifdef ENGINE_ROUTING

View file

@ -223,7 +223,10 @@ static void bonemat_fromentity(world_t *w, wedict_t *ed, float *trans)
a[1] = ed->v->angles[1];
a[2] = ed->v->angles[2];
if (!mod || mod->type == mod_alias)
{
a[0] *= r_meshpitch.value;
a[2] *= r_meshroll.value;
}
AngleVectors(a, d[0], d[1], d[2]);
bonemat_fromqcvectors(trans, d[0], d[1], d[2], ed->v->origin);
}
@ -1684,7 +1687,7 @@ void QCBUILTIN PF_skel_ragedit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
//fixme: respond to renderflags&USEAXIS? scale?
a[0] = wed->v->angles[0] * r_meshpitch.value; /*mod_alias bug*/
a[1] = wed->v->angles[1];
a[2] = wed->v->angles[2];
a[2] = wed->v->angles[2] * r_meshroll.value; /*hexen2 bug*/
AngleVectors(a, d[0], d[1], d[2]);
bonemat_fromqcvectors(emat, d[0], d[1], d[2], wed->v->origin);
skelidx = wed->xv->skeletonindex;

View file

@ -716,6 +716,7 @@ extern cvar_t gl_playermip;
extern cvar_t r_lightmap_saturation;
extern cvar_t r_meshpitch;
extern cvar_t r_meshroll; //gah!
extern cvar_t vid_hardwaregamma;
enum {

View file

@ -2397,18 +2397,6 @@ static void Sbar_Hexen2DrawExtra (playerview_t *pv)
"Demoness"
};
if (pv->sb_hexen2_infoplaque)
{
int i;
Con_Printf("Objectives:\n");
for (i = 0; i < 64; i++)
{
if (pv->stats[STAT_H2_OBJECTIVE1 + i/32] & (1<<(i&31)))
Con_Printf("%s\n", T_GetInfoString(i));
}
pv->sb_hexen2_infoplaque = false;
}
if (!pv->sb_hexen2_extra_info)
{
sbar_rect.y -= 46-SBAR_HEIGHT;
@ -2877,6 +2865,19 @@ void Sbar_Draw (playerview_t *pv)
if (sbar_hexen2)
{
//hexen2 hud
if (pv->sb_hexen2_infoplaque)
{
int i;
Con_Printf("Objectives:\n");
for (i = 0; i < 64; i++)
{
if (pv->stats[STAT_H2_OBJECTIVE1 + i/32] & (1<<(i&31)))
Con_Printf("%s\n", T_GetInfoString(i));
}
pv->sb_hexen2_infoplaque = false;
}
if (sb_lines > 24 || pv->sb_hexen2_extra_info)
{
Sbar_Hexen2DrawExtra(pv);

View file

@ -1310,6 +1310,7 @@ int main (int c, const char **v)
newtime = Sys_DoubleTime ();
time = newtime - oldtime;
#ifdef HAVE_SERVER
if (isDedicated)
{
sleeptime = SV_Frame();
@ -1317,6 +1318,7 @@ int main (int c, const char **v)
NET_Sleep(sleeptime, noconinput?false:true);
}
else
#endif
{
sleeptime = Host_Frame(time);
oldtime = newtime;

View file

@ -366,6 +366,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#undef AVAIL_WASAPI //wasapi is available in the vista sdk, while that's compatible with earlier versions, its not really expected until 2008
#endif
#if !defined(HAVE_SERVER) && !defined(SV_MASTER)
#undef HAVE_HTTPSV
#endif
#ifdef NO_MULTITHREAD
#undef MULTITHREAD
#endif

View file

@ -38,6 +38,7 @@ cvar_t r_meshpitch = CVARCD ("r_meshpitch", "1", r_meshpitch_callback, "Sp
#else
cvar_t r_meshpitch = CVARCD ("r_meshpitch", "-1", r_meshpitch_callback, "Specifies the direction of the pitch angle on mesh models formats, Quake compatibility requires -1.");
#endif
cvar_t r_meshroll = CVARCD ("r_meshroll", "1", r_meshpitch_callback, "Specifies the direction of the roll angle on mesh models formats, also affects gamecode, so do not change from its default.");
cvar_t dpcompat_skinfiles = CVARD ("dpcompat_skinfiles", "0", "When set, uses a nodraw shader for any unmentioned surfaces.");
#ifdef HAVE_CLIENT

View file

@ -64,6 +64,7 @@ static int VectorCompare (const vec3_t v1, const vec3_t v2)
static rbeplugfuncs_t *rbefuncs;
cvar_t r_meshpitch;
cvar_t r_meshroll;
//============================================================================
// physics engine support
@ -2738,6 +2739,7 @@ static void QDECL World_ODE_Start(world_t *world)
world->rbe = &ctx->pub;
r_meshpitch.value = cvarfuncs->GetFloat("r_meshpitch");
r_meshroll.value = cvarfuncs->GetFloat("r_meshroll");
VectorAvg(world->worldmodel->mins, world->worldmodel->maxs, center);
VectorSubtract(world->worldmodel->maxs, center, extents);

View file

@ -580,6 +580,14 @@ typedef struct vfsfile_s
#endif
} vfsfile_t;
#define VFS_ERROR_TRYLATER 0 //nothing to write/read yet.
#define VFS_ERROR_UNSPECIFIED -1 //no reason given
#define VFS_ERROR_NORESPONSE -2 //no reason given
#define VFS_ERROR_EOF -3 //no reason given
#define VFS_ERROR_DNSFAILURE -4 //weird one, but oh well
#define VFS_ERROR_WRONGCERT -5 //server gave a certificate with the wrong name
#define VFS_ERROR_UNTRUSTED -6 //server gave a certificate with the right name, but we don't have a full chain of trust
#define VFS_CLOSE(vf) ((vf)->Close(vf))
#define VFS_TELL(vf) ((vf)->Tell(vf))
#define VFS_GETLEN(vf) ((vf)->GetLen(vf))

View file

@ -369,18 +369,21 @@ showhelp:
// print cvar list header
if (!(listflags & CLF_RAW) && !num)
Con_TPrintf("CVar list:\n");
Con_TPrintf("^aCVar list:\n");
// print group header
if (!(listflags & CLF_RAW) && !gnum)
Con_Printf("%s --\n", grp->name);
Con_Printf("^a%s --\n", grp->name);
// print restriction level
if (listflags & CLF_LEVEL)
Con_Printf("(%i) ", cmd->restriction);
// print cvar name
Con_Printf("%s", cmd->name);
if (!cmd->defaultstr || !strcmp(cmd->string, cmd->defaultstr))
Con_Printf(S_COLOR_GREEN "%s", cmd->name);
else
Con_Printf(S_COLOR_RED "%s", cmd->name);
total++;
// print current value

View file

@ -3650,7 +3650,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
/*quake requires a few settings for compatibility*/
#define QRPCOMPAT "set cl_cursor_scale 0.2\nset cl_cursor_bias_x 7.5\nset cl_cursor_bias_y 0.8\n"
#define QUAKESPASMSUCKS "set mod_h2holey_bugged 1\n"
#define QCFG "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_pakcontents 1\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT QUAKESPASMSUCKS
#define QCFG "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_pakcontents 1\nset allow_download_refpackages 0\nset r_meshpitch -1\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT QUAKESPASMSUCKS
/*NetQuake reconfiguration, to make certain people feel more at home...*/
#define NQCFG "//disablehomedir 1\n//mainconfig ftenq\ncfg_save_auto 1\n" QCFG "set sv_nqplayerphysics 1\nset cl_loopbackprotocol auto\ncl_sbar 1\nset plug_sbar 0\nset sv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\nset m_preset_chosen 1\nset vid_wait 1\nset cl_demoreel 1\n"
#define SPASMCFG NQCFG "fps_preset builtin_spasm\nset cl_demoreel 0\ncl_sbar 2\nset gl_load24bit 1\n"
@ -3666,7 +3666,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
/*some modern non-compat settings*/
#define DMFCFG "set com_parseutf8 1\npm_airstep 1\nsv_demoExtensions 1\n"
/*set some stuff so our regular qw client appears more like hexen2. sv_mintic is required to 'fix' the ravenstaff so that its projectiles don't impact upon each other*/
#define HEX2CFG "set com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset cl_forwardspeed 200\nset cl_backspeed 200\ncl_sidespeed 225\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.015\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\nset sv_walkpitch 0\n"
#define HEX2CFG "set v_gammainverted 1\nset com_parseutf8 -1\nset gl_font gfx/hexen2\nset in_builtinkeymap 0\nset_calc cl_playerclass int (random * 5) + 1\nset cl_forwardspeed 200\nset cl_backspeed 200\ncl_sidespeed 225\nset sv_maxspeed 640\ncl_run 0\nset watervis 1\nset r_lavaalpha 1\nset r_lavastyle -2\nset r_wateralpha 0.5\nset sv_pupglow 1\ngl_shaftlight 0.5\nsv_mintic 0.015\nset r_meshpitch -1\nset r_meshroll -1\nset mod_warnmodels 0\nset cl_model_bobbing 1\nsv_sound_watersplash \"misc/hith2o.wav\"\nsv_sound_land \"fx/thngland.wav\"\nset sv_walkpitch 0\n"
/*yay q2!*/
#define Q2CFG "set v_gammainverted 1\nset com_parseutf8 0\ncom_nogamedirnativecode 0\nset sv_bigcoords 0\nsv_port "STRINGIFY(PORT_Q2SERVER)"\n"
/*Q3's ui doesn't like empty model/headmodel/handicap cvars, even if the gamecode copes*/
@ -3750,6 +3750,7 @@ const gamemode_info_t gamemode_info[] = {
{"-quoth", "quoth", "FTE-Quake", {"id1/pak0.pak","id1/quake.rc"},QCFG, {"id1", "qw", "quoth", "*fte"}, "Quake: Quoth", UPDATEURL(Q1)},
{"-nehahra", "nehahra", "FTE-Quake", {"id1/pak0.pak","id1/quake.rc"},NEHCFG, {"id1", "qw", "nehahra", "*fte"}, "Quake: Seal Of Nehahra", UPDATEURL(Q1)},
//various quake-based standalone mods.
{"-librequake", "librequake","LibreQuake", {"lq1/pak0.pak","lq1/gfx.pk3","lq1/quake.rc"},QCFG, {"lq1"}, "LibreQuake", UPDATEURL(LQ)},
// {"-nexuiz", "nexuiz", "Nexuiz", {"nexuiz.exe"}, NEXCFG, {"data", "*ftedata"},"Nexuiz"},
// {"-xonotic", "xonotic", "Xonotic", {"data/xonotic-data.pk3dir",
// "data/xonotic-*data*.pk3"}, XONCFG, {"data", "*ftedata"},"Xonotic", UPDATEURL(Xonotic)},

View file

@ -335,7 +335,10 @@ void QDECL VectorAngles(const float *forward, const float *up, float *result, qb
yaw *= 180 / M_PI;
roll *= 180 / M_PI;
if (meshpitch)
{
pitch *= r_meshpitch.value;
roll *= r_meshroll.value;
}
if (pitch < 0)
pitch += 360;
if (yaw < 0)
@ -382,6 +385,14 @@ void QDECL AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3
up[2] = cr*cp;
}
}
void AngleVectorsMesh (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
{
vec3_t ang;
ang[0] = angles[0] * r_meshpitch.value;
ang[1] = angles[1];
ang[2] = angles[2] * r_meshroll.value;
AngleVectors (ang, forward, right, up);
}
int VectorCompare (const vec3_t v1, const vec3_t v2)
{

View file

@ -129,6 +129,7 @@ typedef struct {
void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs);
float anglemod (float a);
void QDECL AngleVectors (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
void QDECL AngleVectorsMesh (const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
void QDECL VectorAngles (const float *forward, const float *up, float *angles, qboolean meshpitch); //up may be NULL
void VARGS BOPS_Error (void);
int VARGS BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const struct mplane_s *plane);

View file

@ -430,6 +430,9 @@ typedef struct
qboolean handshaking;
qboolean datagram;
int pullerror; //adding these two because actual networking errors are not getting represented properly, at least with regard to timeouts.
int pusherror;
qboolean challenging; //not sure this is actually needed, but hey.
void *cbctx;
neterr_t(*cbpush)(void *cbctx, const qbyte *data, size_t datasize);
@ -658,7 +661,7 @@ static int SSL_DoHandshake(gnutlsfile_t *file)
if (!file->session)
{
//Sys_Printf("null session\n");
return -1;
return VFS_ERROR_UNSPECIFIED;
}
err = qgnutls_handshake (file->session);
@ -676,9 +679,18 @@ static int SSL_DoHandshake(gnutlsfile_t *file)
qgnutls_perror (err);
}
SSL_Close(&file->funcs);
// Con_Printf("%s: abort\n", file->certname);
return -1;
switch(err)
{
case GNUTLS_E_CERTIFICATE_ERROR: err = VFS_ERROR_UNTRUSTED; break;
case GNUTLS_E_PREMATURE_TERMINATION: err = VFS_ERROR_EOF; break;
case GNUTLS_E_PUSH_ERROR: err = file->pusherror; break;
case GNUTLS_E_PULL_ERROR: err = file->pullerror; break;
default: err = VFS_ERROR_UNSPECIFIED; break;
}
SSL_Close(&file->funcs);
return err;
}
file->handshaking = false;
return 1;
@ -697,7 +709,7 @@ static int QDECL SSL_Read(struct vfsfile_s *f, void *buffer, int bytestoread)
}
if (!bytestoread) //gnutls doesn't like this.
return -1;
return VFS_ERROR_UNSPECIFIED; //caller is expecting data that we can never return, or something.
read = qgnutls_record_recv(file->session, buffer, bytestoread);
if (read < 0)
@ -705,7 +717,7 @@ static int QDECL SSL_Read(struct vfsfile_s *f, void *buffer, int bytestoread)
if (read == GNUTLS_E_PREMATURE_TERMINATION)
{
Con_Printf("TLS Premature Termination from %s\n", file->certname);
return -1;
return VFS_ERROR_EOF;
}
else if (read == GNUTLS_E_REHANDSHAKE)
{
@ -721,7 +733,7 @@ static int QDECL SSL_Read(struct vfsfile_s *f, void *buffer, int bytestoread)
}
}
else if (read == 0)
return -1; //closed by remote connection.
return VFS_ERROR_EOF; //closed by remote connection.
return read;
}
static int QDECL SSL_Write(struct vfsfile_s *f, const void *buffer, int bytestowrite)
@ -744,11 +756,11 @@ static int QDECL SSL_Write(struct vfsfile_s *f, const void *buffer, int bytestow
else
{
Con_DPrintf("TLS Send Error %i (%i bytes)\n", written, bytestowrite);
return -1;
return VFS_ERROR_UNSPECIFIED;
}
}
else if (written == 0)
return -1; //closed by remote connection.
return VFS_ERROR_EOF; //closed by remote connection.
return written;
}
static qboolean QDECL SSL_Seek (struct vfsfile_s *file, qofs_t pos)
@ -775,7 +787,17 @@ static ssize_t SSL_Push(gnutls_transport_ptr_t p, const void *data, size_t size)
int done = VFS_WRITE(file->stream, data, size);
if (done <= 0)
{
qgnutls_transport_set_errno(file->session, (done==0)?EAGAIN:ECONNRESET);
int eno;
file->pusherror = done;
switch(done)
{
case VFS_ERROR_EOF: return 0;
case VFS_ERROR_DNSFAILURE:
case VFS_ERROR_NORESPONSE: eno = ECONNRESET; break;
case VFS_ERROR_TRYLATER: eno = EAGAIN; break;
default: eno = ECONNRESET; break;
}
qgnutls_transport_set_errno(file->session, eno);
return -1;
}
return done;
@ -787,8 +809,17 @@ static ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size)
int done = VFS_READ(file->stream, data, size);
if (done <= 0)
{
//use ECONNRESET instead of returning eof.
qgnutls_transport_set_errno(file->session, (done==0)?EAGAIN:ECONNRESET);
int eno;
file->pullerror = done;
switch(done)
{
case VFS_ERROR_EOF: return 0;
case VFS_ERROR_DNSFAILURE:
case VFS_ERROR_NORESPONSE: eno = ECONNRESET; break;
case VFS_ERROR_TRYLATER: eno = EAGAIN; break;
default: eno = ECONNRESET; break;
}
qgnutls_transport_set_errno(file->session, eno);
return -1;
}
return done;

View file

@ -229,7 +229,7 @@ static int SSPI_CheckNewInCrypt(sslfile_t *f)
{
int newd;
if (!f->stream)
return -1;
return VFS_ERROR_EOF;
newd = VFS_READ(f->stream, f->incrypt.data+f->incrypt.avail, f->incrypt.datasize - f->incrypt.avail);
if (newd < 0)
return newd;

View file

@ -3986,9 +3986,7 @@ typedef struct ftenet_tcp_stream_s {
enum
{
#if defined(HAVE_SERVER) || defined(SV_MASTER)
TCPC_UNKNOWN, //waiting to see what they send us.
#endif
//TCPC_QTV, //included for completeness. qtv handles the sockets itself, we just parse initial handshake and then pass it over (as either a tcp or tls vfsfile_t)
TCPC_QIZMO, //'qizmo\n' handshake, followed by packets prefixed with a 16bit packet length.
#ifdef HAVE_HTTPSV
@ -3996,8 +3994,8 @@ typedef struct ftenet_tcp_stream_s {
TCPC_WEBSOCKETB, //binary encoded data (subprotocol = 'binary')
TCPC_WEBSOCKETNQ, //raw nq msg buffers with no encapsulation or handshake
TCPC_HTTPCLIENT, //we're sending a file to this victim.
TCPC_WEBRTC_HOST, //for brokering webrtc connections, doesn't carry any actual game data itself.
TCPC_WEBRTC_CLIENT, //for brokering webrtc connections, doesn't carry any actual game data itself.
TCPC_WEBRTC_HOST //for brokering webrtc connections, doesn't carry any actual game data itself.
#endif
} clienttype;
qbyte inbuffer[MAX_OVERALLMSGLEN];
@ -5129,6 +5127,7 @@ static qboolean FTENET_TCP_KillStream(ftenet_tcp_connection_t *con, ftenet_tcp_s
if (st->dlfile)
VFS_CLOSE(st->dlfile);
#ifdef HAVE_HTTPSV
if (st->clienttype == TCPC_WEBRTC_CLIENT)
{ //notify its server
ftenet_tcp_stream_t *o;
@ -5165,6 +5164,7 @@ static qboolean FTENET_TCP_KillStream(ftenet_tcp_connection_t *con, ftenet_tcp_s
SVM_RemoveBrokerGame(st->webrtc.resource);
#endif
}
#endif
return false;
}
@ -8823,8 +8823,8 @@ typedef struct {
SOCKET sock;
qboolean conpending;
qboolean readaborted; //some kind of error. don't spam
qboolean writeaborted; //some kind of error. don't spam
int readaborted; //some kind of error. don't spam
int writeaborted; //some kind of error. don't spam
char readbuffer[65536];
int readbuffered;
@ -8871,19 +8871,24 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea
int e = neterrno();
if (e != NET_EWOULDBLOCK && e != NET_EINTR)
{
tf->readaborted = VFS_ERROR_UNSPECIFIED;
switch(e)
{
case NET_ENOTCONN:
Con_Printf("connection to \"%s\" failed\n", tf->peer);
tf->readaborted = VFS_ERROR_NORESPONSE;
break;
case NET_ECONNABORTED:
Con_DPrintf("connection to \"%s\" aborted\n", tf->peer);
tf->readaborted = VFS_ERROR_NORESPONSE;
break;
case NET_ETIMEDOUT:
Con_Printf("connection to \"%s\" timed out\n", tf->peer);
tf->readaborted = VFS_ERROR_NORESPONSE;
break;
case NET_ECONNREFUSED:
Con_DPrintf("connection to \"%s\" refused\n", tf->peer);
tf->readaborted = VFS_ERROR_NORESPONSE;
break;
case NET_ECONNRESET:
Con_DPrintf("connection to \"%s\" reset\n", tf->peer);
@ -8891,14 +8896,13 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea
default:
Con_Printf("tcp socket error %i (%s)\n", e, tf->peer);
}
tf->readaborted = true;
}
//fixme: figure out wouldblock or error
}
else if (len == 0 && trying != 0)
{
//peer disconnected
tf->readaborted = true;
tf->readaborted = VFS_ERROR_EOF;
}
else
{
@ -8910,7 +8914,7 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea
if (bytestoread > tf->readbuffered)
bytestoread = tf->readbuffered;
if (bytestoread < 0)
return -1; //caller error...
return VFS_ERROR_UNSPECIFIED; //caller error...
if (bytestoread > 0)
{
@ -8919,14 +8923,7 @@ int QDECL VFSTCP_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestorea
memmove(tf->readbuffer, tf->readbuffer+bytestoread, tf->readbuffered);
return bytestoread;
}
else
{
if (tf->readaborted)
{
return -1; //signal an error
}
return 0; //signal nothing available
}
else return tf->readaborted;
}
int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)
{
@ -8934,7 +8931,7 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt
int len;
if (tf->writeaborted)
return -1;
return VFS_ERROR_UNSPECIFIED; //a previous write failed.
if (tf->conpending)
{
@ -8955,6 +8952,7 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt
len = send(tf->sock, buffer, bytestoread, 0);
if (len == -1 || len == 0)
{
int reason = VFS_ERROR_UNSPECIFIED;
int e = (len==0)?NET_ECONNABORTED:neterrno();
switch(e)
{
@ -8963,16 +8961,17 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt
return 0; //nothing available yet.
case NET_ETIMEDOUT:
Con_Printf("connection to \"%s\" timed out\n", tf->peer);
return -1; //don't bother trying to read if we never connected.
return VFS_ERROR_NORESPONSE; //don't bother trying to read if we never connected.
case NET_ECONNABORTED:
Con_Printf("connection to \"%s\" aborted\n", tf->peer);
reason = len?VFS_ERROR_NORESPONSE:VFS_ERROR_EOF;
break;
case NET_ENOTCONN:
#ifdef __unix__
case EPIPE:
#endif
Con_Printf("connection to \"%s\" failed\n", tf->peer);
return -1; //don't bother trying to read if we never connected.
return VFS_ERROR_NORESPONSE; //don't bother trying to read if we never connected.
default:
Sys_Printf("tcp socket error %i (%s)\n", e, tf->peer);
break;
@ -8981,7 +8980,7 @@ int QDECL VFSTCP_WriteBytes (struct vfsfile_s *file, const void *buffer, int byt
// instead let the read handling kill it if there's nothing new to be read
VFSTCP_ReadBytes(file, NULL, 0);
tf->writeaborted = true;
return -1;
return reason;
}
return len;
}

View file

@ -6334,9 +6334,7 @@ void QCBUILTIN PF_rotatevectorsbyangles (pubprogfuncs_t *prinst, struct globalva
float *ang = G_VECTOR(OFS_PARM0);
vec3_t src[3], trans[3], res[3];
ang[0]*=r_meshpitch.value;
AngleVectors(ang, trans[0], trans[1], trans[2]);
ang[0]*=r_meshpitch.value;
AngleVectorsMesh(ang, trans[0], trans[1], trans[2]);
VectorInverse(trans[1]);
VectorCopy(w->g.v_forward, src[0]);

View file

@ -341,6 +341,9 @@ void QCBUILTIN PF_brush_selected(pubprogfuncs_t *prinst, struct globalvars_s *pr
void QCBUILTIN PF_brush_getfacepoints(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_brush_calcfacepoints(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_brush_findinvolume(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_getcp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_getmesh(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_patch_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
#endif
void QCBUILTIN PF_touchtriggers(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);

File diff suppressed because it is too large Load diff

View file

@ -618,6 +618,7 @@ void Mod_Init (qboolean initial)
Cvar_Register(&mod_loadentfiles_dir, NULL);
Cvar_Register(&temp_lit2support, NULL);
Cvar_Register (&r_meshpitch, "Gamecode");
Cvar_Register (&r_meshroll, "Gamecode");
Cmd_AddCommandD("sv_saveentfile", Mod_SaveEntFile_f, "Dumps a copy of the map's entities to disk, so that it can be edited and used as a replacement for slightly customised maps.");
Cmd_AddCommandD("mod_showent", Mod_ShowEnt_f, "Allows you to quickly search through a map's entities.");
Cmd_AddCommand("version_modelformats", Mod_PrintFormats_f);

View file

@ -271,18 +271,27 @@ typedef struct
struct patchdata_s
{ //unlit, always...
brushtex_t *tex;
unsigned short xpoints;
unsigned short ypoints;
struct
unsigned short numcp[2];
short subdiv[2]; //<0=regular q3 patch, 0=cp-only, >0=fixed-tessellation.
unsigned short tesssize[2];
struct patchtessvert_s
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
// vec3_t norm;
// vec3_t sdir;
// vec3_t tdir;
} *tessvert; //x+(y*tesssize[0])
vec3_t norm;
vec3_t sdir;
vec3_t tdir;
} verts[1]; //x+(y*xpoints)
//control points
struct patchcpvert_s
{
vec3_t v;
vec2_t tc;
vec4_t rgba;
} cp[1]; //x+(y*numcp[0]) extends past end of patchdata_s
} *patch; //if this is NULL, then its a regular brush. otherwise its a patch.
struct brushface_s
{

View file

@ -510,6 +510,26 @@ static void ExpandBuffer(struct http_dl_ctx_s *con, int quant)
con->bufferlen = newlen;
}
static int VFSError_To_HTTP(int vfserr)
{
switch(vfserr)
{
case VFS_ERROR_TRYLATER:
return 0;
default:
case VFS_ERROR_UNSPECIFIED:
return 0; //don't know, no reason given.
case VFS_ERROR_EOF:
return HTTP_EOF;
case VFS_ERROR_DNSFAILURE:
return HTTP_DNSFAILURE;
case VFS_ERROR_WRONGCERT:
return HTTP_MITM;
case VFS_ERROR_UNTRUSTED:
return HTTP_UNTRUSTED;
}
}
static qboolean HTTP_DL_Work(struct dl_download *dl)
{
struct http_dl_ctx_s *con = dl->ctx;
@ -549,7 +569,11 @@ static qboolean HTTP_DL_Work(struct dl_download *dl)
if (!ammount)
return true;
if (ammount < 0)
{
dl->status = DL_FAILED;
dl->replycode = VFSError_To_HTTP(ammount);
return false;
}
#else
ammount = send(con->sock, con->buffer, con->bufferused, 0);
@ -579,7 +603,11 @@ static qboolean HTTP_DL_Work(struct dl_download *dl)
if (!ammount)
return true;
if (ammount < 0)
{
dl->status = DL_FAILED;
dl->replycode = VFSError_To_HTTP(ammount);
return false;
}
#else
ammount = recv(con->sock, con->buffer+con->bufferused, con->bufferlen-con->bufferused-15, 0);
if (!ammount)

View file

@ -148,6 +148,14 @@ struct dl_download *DL_Create(const char *url);
qboolean DL_CreateThread(struct dl_download *dl, vfsfile_t *file, void (*NotifyFunction)(struct dl_download *dl));
void DL_Close(struct dl_download *dl);
void DL_DeThread(void);
//internal 'http' error codes.
#define HTTP_DNSFAILURE 900 //no ip known
#define HTTP_NORESPONSE 901 //tcp failure
#define HTTP_EOF 902 //unexpected eof
#define HTTP_MITM 903 //wrong cert
#define HTTP_UNTRUSTED 904 //unverifiable cert
#endif
#endif

View file

@ -1904,6 +1904,9 @@ Emits a primitive statement, returning the var it places it's value in
*/
static int QCC_ShouldConvert(QCC_type_t *from, etype_t wanted)
{
if (from->type == ev_boolean)
from = from->parentclass;
/*no conversion needed*/
if (from->type == wanted)
return 0;
@ -2007,7 +2010,7 @@ static QCC_sref_t QCC_SupplyConversion(QCC_sref_t var, etype_t wanted, pbool fa
QCC_PR_ParsePrintSRef(WARN_LAXCAST, var);
}
else
QCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, var, "Implicit type mismatch. Needed %s, got %s.", basictypenames[wanted], basictypenames[var.cast->type]);
QCC_PR_ParseErrorPrintSRef(ERR_TYPEMISMATCH, var, "Implicit type mismatch. Needed %s%s%s, got %s%s%s.", col_type,basictypenames[wanted],col_none, col_type,basictypenames[var.cast->type],col_none);
}
return var;
}
@ -7070,11 +7073,11 @@ QCC_sref_t QCC_PR_GenerateFunctionCallRef (QCC_sref_t newself, QCC_sref_t func,
QCC_ForceUnFreeDef(fparm.sym);
}
fparm.ofs = ofs;
if (!fparm.ofs)
{
// if (!fparm.ofs)
// {
args[parm].firststatement = numstatements;
args[parm].ref = fparm;
}
// }
parm++;
if (ofs+asz == arglist[i]->cast->size)
@ -9187,7 +9190,7 @@ fieldarrayindex:
if (arraysize && makearraypointers)
{
QCC_PR_ParseWarning(0, "Is this still needed?");
// QCC_PR_ParseWarning(0, "Is this still needed?"); //yes, when passing the head of an array to a function that takes a pointer
r = QCC_PR_GenerateAddressOf(retbuf, r);
}
}
@ -9200,6 +9203,13 @@ QCC_sref_t QCC_PR_GenerateVector(QCC_sref_t x, QCC_sref_t y, QCC_sref_t z)
{
QCC_sref_t d;
if (x.cast->type != ev_float && x.cast->type != ev_integer)
x = QCC_EvaluateCast(x, type_float, true);
if (y.cast->type != ev_float && y.cast->type != ev_integer)
y = QCC_EvaluateCast(y, type_float, true);
if (z.cast->type != ev_float && z.cast->type != ev_integer)
z = QCC_EvaluateCast(z, type_float, true);
if ((x.cast->type != ev_float && x.cast->type != ev_integer) ||
(y.cast->type != ev_float && y.cast->type != ev_integer) ||
(z.cast->type != ev_float && z.cast->type != ev_integer))
@ -14800,7 +14810,7 @@ static pbool QCC_CheckUninitialised(int firststatement, int laststatement)
err = QCC_CheckOneUninitialised(firststatement, laststatement, local, local->ofs, local->ofs + local->type->size * (local->arraysize?local->arraysize:1));
if (err > 0)
{
QCC_PR_Warning(WARN_UNINITIALIZED, s_filen, statements[err].linenum, "Potentially uninitialised variable %s", local->name);
QCC_PR_Warning(WARN_UNINITIALIZED, s_filen, statements[err].linenum, "Potentially uninitialised variable %s%s%s", col_symbol,local->name,col_none);
result = true;
// break;
}
@ -17271,10 +17281,16 @@ QCC_sref_t QCC_PR_ParseInitializerType_Internal(int arraysize, QCC_def_t *basede
QCC_sref_t QCC_PR_ParseInitializerTemp(QCC_type_t *type)
{
QCC_sref_t def = QCC_GetTemp(type);
def = QCC_PR_ParseInitializerType_Internal(0, NULL, def, 0);
if (def.cast != type)
QCC_PR_ParseError(ERR_INTERNAL, "QCC_PR_ParseInitializerTemp changed type\n");
return def;
QCC_sref_t imm;
imm = QCC_PR_ParseInitializerType_Internal(0, NULL, def, 0);
if (imm.cast)
{
if (imm.cast != type)
QCC_PR_ParseError(ERR_INTERNAL, "QCC_PR_ParseInitializerTemp changed type\n");
QCC_FreeTemp(def);
return imm; //just use the immediate.
}
return def; //use our silly temp.
}
//returns true where its a const/static initialiser. false if non-const/final initialiser
pbool QCC_PR_ParseInitializerType(int arraysize, QCC_def_t *basedef, QCC_sref_t def, unsigned int flags)

View file

@ -343,11 +343,11 @@ compiler_flag_t compiler_flag[] = {
{&keyword_goto, defaultkeyword, "goto", "Keyword: goto", "Disables the 'goto' keyword."},
{&keyword_int, typekeyword, "int", "Keyword: int", "Disables the 'int' keyword."},
{&keyword_integer, typekeyword, "integer", "Keyword: integer", "Disables the 'integer' keyword."},
{&keyword_double, defaultkeyword, "double", "Keyword: double", "Disables the 'double' keyword."},
{&keyword_long, defaultkeyword, "long", "Keyword: long", "Disables the 'long' keyword."},
{&keyword_short, defaultkeyword, "short", "Keyword: short", "Disables the 'short' keyword."},
{&keyword_char, defaultkeyword, "char", "Keyword: char", "Disables the 'char' keyword."},
{&keyword_signed, defaultkeyword, "signed", "Keyword: signed", "Disables the 'signed' keyword."},
{&keyword_double, nondefaultkeyword, "double", "Keyword: double", "Disables the 'double' keyword."},
{&keyword_long, nondefaultkeyword, "long", "Keyword: long", "Disables the 'long' keyword."},
{&keyword_short, nondefaultkeyword, "short", "Keyword: short", "Disables the 'short' keyword."},
{&keyword_char, nondefaultkeyword, "char", "Keyword: char", "Disables the 'char' keyword."},
{&keyword_signed, nondefaultkeyword, "signed", "Keyword: signed", "Disables the 'signed' keyword."},
{&keyword_unsigned, defaultkeyword, "unsigned", "Keyword: unsigned", "Disables the 'unsigned' keyword."},
{&keyword_noref, defaultkeyword, "noref", "Keyword: noref", "Disables the 'noref' keyword."}, //nowhere else references this, don't warn about it.
{&keyword_unused, nondefaultkeyword, "unused", "Keyword: unused", "Disables the 'unused' keyword. 'unused' means that the variable is unused, you're aware that its unused, and you'd rather not know about all the warnings this results in."},
@ -4335,6 +4335,7 @@ static void QCC_PR_CommandLinePrecompilerOptions (void)
keyword_vector = keyword_entity = keyword_float = keyword_string = false; //not to be confused with actual types, but rather the absence of the keyword local.
keyword_integer = keyword_enumflags = false;
keyword_float = keyword_int = keyword_typedef = keyword_struct = keyword_union = keyword_enum = true;
keyword_double = keyword_long = keyword_short = keyword_char = keyword_signed = keyword_unsigned = true;
keyword_thinktime = keyword_until = keyword_loop = false;
keyword_integer = true;

View file

@ -3006,7 +3006,8 @@ void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m)
// if it is an inline model, get the size information for it
if (m && (m[0] == '*' || (*m&&progstype == PROG_H2)))
{
mod = Mod_ForName (Mod_FixName(m, sv.modelname), MLV_WARN);
mod = SVPR_GetCModel(&sv.world, i);
// mod = Mod_ForName (Mod_FixName(m, sv.modelname), MLV_WARN);
if (mod)
{
while(mod->loadstate == MLS_LOADING)
@ -3014,7 +3015,15 @@ void PF_setmodel_Internal (pubprogfuncs_t *prinst, edict_t *e, const char *m)
VectorCopy (mod->mins, e->v->mins);
VectorCopy (mod->maxs, e->v->maxs);
VectorSubtract (mod->maxs, mod->mins, e->v->size);
#ifdef HEXEN2
if (progstype == PROG_H2 && mod->type == mod_alias && !sv_gameplayfix_setmodelrealbox.ival)
{ //hexen2 expands its mdls by 10
vec3_t hexen2expansion = {10,10,10};
VectorSubtract (mod->mins, hexen2expansion, e->v->mins);
VectorAdd (mod->maxs, hexen2expansion, e->v->maxs);
}
#endif
VectorSubtract (e->v->maxs, e->v->mins, e->v->size);
World_LinkEdict (&sv.world, (wedict_t*)e, false);
}
@ -10985,7 +10994,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"chr2str", PF_chr2str, 0, 0, 0, 223, D("string(float chr, ...)", "The input floats are considered character values, and are concatenated.")},
{"strconv", PF_strconv, 0, 0, 0, 224, D("string(float ccase, float redalpha, float redchars, string str, ...)", "Converts quake chars in the input string amongst different representations.\nccase specifies the new case for letters.\n 0: not changed.\n 1: forced to lower case.\n 2: forced to upper case.\nredalpha and redchars switch between colour ranges.\n 0: no change.\n 1: Forced white.\n 2: Forced red.\n 3: Forced gold(low) (numbers only).\n 4: Forced gold (high) (numbers only).\n 5+6: Forced to white and red alternately.\nYou should not use this builtin in combination with UTF-8.")},
{"strpad", PF_strpad, 0, 0, 0, 225, D("string(float pad, string str1, ...)", "Pads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right.")}, //will be moved
{"infoadd", PF_infoadd, 0, 0, 0, 226, D("string(infostring old, string key, string value)", "Returns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \\ character.")},
{"infoadd", PF_infoadd, 0, 0, 0, 226, D("infostring(infostring old, string key, string value)", "Returns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \\ character.")},
{"infoget", PF_infoget, 0, 0, 0, 227, D("string(infostring info, string key)", "Reads a named value from an infostring. The returned value is a tempstring")},
// {"strcmp", PF_strncmp, 0, 0, 0, 228, D("float(string s1, string s2)", "Compares the two strings exactly. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.")},
{"strncmp", PF_strncmp, 0, 0, 0, 228, D("#define strcmp strncmp\nfloat(string s1, string s2, optional float len, optional float s1ofs, optional float s2ofs)", "Compares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0.\nReturns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher.")},
@ -11105,6 +11114,27 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"brush_findinvolume",PF_brush_findinvolume,0, 0, 0, 0, D("int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults)", "Allows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice.")},
// {"brush_editplane", PF_brush_editplane, 0, 0, 0, 0, D("float(float modelid, int brushid, int faceid, in brushface *face)", "Changes a surface's texture info.")},
// {"brush_transformselected",PF_brush_transformselected,0,0,0, 0, D("int(float modelid, int brushid, float *matrix)", "Transforms selected brushes by the given transform")},
#define qcpatchvert \
"typedef struct\n{\n" \
"\tstring shadername;\n" \
"\tint contents;\n" \
"\tint cpwidth;\n" \
"\tint cpheight;\n" \
"\tint tesswidth;\n" \
"\tint tessheight;\n" \
"\tvector texinfo;/*scalex,y,rot*/\n" \
"} patchinfo_t;\n" \
"typedef struct\n{\n" \
"\tvector xyz;\n" \
"\tvector rgb; float a;\n" \
"\tfloat s, t;\n" \
"} patchvert_t;\n" \
"#define patch_delete(modelidx,patchidx) brush_delete(modelidx,patchidx)\n"
{"patch_getcp", PF_patch_getcp, 0, 0, 0, 0, D(qcpatchvert "int(float modelidx, int patchid, patchvert_t *out_controlverts, int maxcp, patchinfo_t *out_info)", "Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.")},
{"patch_getmesh", PF_patch_getmesh, 0, 0, 0, 0, D("int(float modelidx, int patchid, patchvert_t *out_verts, int maxverts, __out patchinfo_t out_info)", "Queries a patch's information. You must pre-allocate the face array for the builtin to write to. Return value is the total number of control verts that were retrieved, 0 on error.")},
{"patch_create", PF_patch_create, 0, 0, 0, 0, D("int(float modelidx, int oldpatchid, patchvert_t *in_controlverts, patchinfo_t in_info)", "Inserts a new patch into the model. Return value is the new patch's id.")},
// {"patch_calculate", PF_patch_calculate, 0, 0, 0, 0, D("int(patchvert_t *in_controlverts, patchvert_t *out_renderverts, int maxout, __inout patchinfo_t inout_info)", "Calculates the geometry of a hyperthetical patch.")},
#endif
#ifdef ENGINE_ROUTING

View file

@ -163,7 +163,7 @@ cvar_t sv_csqc_progname = CVARAF("sv_csqc_progname", "csprogs.dat", /*dp*/"csqc_
cvar_t pausable = CVAR("pausable", "");
cvar_t sv_banproxies = CVARD("sv_banproxies", "0", "If enabled, anyone connecting via known proxy software will be refused entry. This should aid with blocking aimbots, but is only reliable for certain public proxies.");
cvar_t sv_specprint = CVARD("sv_specprint", "3", "Bitfield that controls which player events spectators see when tracking that player.\n&1: spectators will see centerprints.\n&2: spectators will see sprints (pickup messages etc).\n&4: spectators will receive console commands, this is potentially risky.\nIndividual spectators can use 'setinfo sp foo' to limit this setting.");
cvar_t sv_protocol = CVARD("sv_protocol", "", "Specifies which protocol extensions to force. recognised values: csqc");
//
// game rules mirrored in svs.info
@ -1945,6 +1945,56 @@ void SV_ClientProtocolExtensionsChanged(client_t *client)
extern cvar_t pr_maxedicts;
client_t *seat;
extern cvar_t sv_protocol;
char *s = sv_protocol.string;
while ((s = COM_Parse(s)))
{
if (!strcasecmp(com_token, "fte2"))
{ //fancy stuff
client->fteprotocolextensions
|= PEXT_CSQC /*mods break without*/
| PEXT_CHUNKEDDOWNLOADS /*much faster downloads+redirects*/
;
client->fteprotocolextensions2
|= PEXT2_PRYDONCURSOR /*mods might break without*/
// | PEXT2_VOICECHAT /*entirely optional*/
| PEXT2_SETANGLEDELTA /*mostly just nice to have*/
| PEXT2_REPLACEMENTDELTAS /*carries quite a bit of extra info*/
| PEXT2_MAXPLAYERS /*not supporting the extra players is bad*/
| PEXT2_PREDINFO /*fixes some repdelta issues (especially for nq)*/
| PEXT2_NEWSIZEENCODING /*more accurate sizes, for awkward mods*/
| PEXT2_INFOBLOBS /*allows mods to send infoblobs to csqc (for avatar images or whatever)*/
;
}
if (!strcasecmp(com_token, "fte1"))
{ //older stuff. most of this was replaced by replacementdeltas.
client->fteprotocolextensions
|= PEXT_SETVIEW
| PEXT_SCALE
| PEXT_TRANS
| PEXT_ACCURATETIMINGS
| PEXT_SOUNDDBL
| PEXT_MODELDBL
| PEXT_ENTITYDBL
| PEXT_ENTITYDBL2
| PEXT_FLOATCOORDS
| PEXT_COLOURMOD
| PEXT_SPAWNSTATIC2
| PEXT_256PACKETENTITIES
| PEXT_SETATTACHMENT
| PEXT_CHUNKEDDOWNLOADS
| PEXT_CSQC
| PEXT_DPFLAGS
;
}
if (!strcasecmp(com_token, "csqc"))
{ //JUST csqc.
client->fteprotocolextensions
|= PEXT_CSQC
;
}
}
client->fteprotocolextensions &= Net_PextMask(PROTOCOL_VERSION_FTE1, ISNQCLIENT(client));
client->fteprotocolextensions2 &= Net_PextMask(PROTOCOL_VERSION_FTE2, ISNQCLIENT(client));
client->ezprotocolextensions1 &= Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, ISNQCLIENT(client)) & EZPEXT1_SERVERADVERTISE;
@ -5408,6 +5458,7 @@ void SV_InitLocal (void)
if (isDedicated)
sv_public.enginevalue = "1";
Cvar_Register (&sv_protocol, cvargroup_servercontrol);
Cvar_Register (&sv_guidhash, cvargroup_servercontrol);
Cvar_Register (&sv_serverip, cvargroup_servercontrol);
Cvar_Register (&sv_public, cvargroup_servercontrol);

View file

@ -323,8 +323,7 @@ float World_changeyaw (wedict_t *ent)
vec3_t vang;
/*calc current view matrix relative to the surface*/
ent->v->angles[PITCH] *= r_meshpitch.value;
AngleVectors(ent->v->angles, view[0], view[1], view[2]);
AngleVectorsMesh(ent->v->angles, view[0], view[1], view[2]);
VectorNegate(view[1], view[1]);
World_GetEntGravityAxis(ent, surf);

View file

@ -151,6 +151,7 @@ SV_CheckVelocity
void WPhys_CheckVelocity (world_t *w, wedict_t *ent)
{
int i;
#ifdef HAVE_SERVER
extern cvar_t sv_nqplayerphysics;
if (sv_nqplayerphysics.ival)
@ -175,6 +176,7 @@ void WPhys_CheckVelocity (world_t *w, wedict_t *ent)
}
}
else
#endif
{ //bound radially (for sanity)
for (i=0 ; i<3 ; i++)
{

View file

@ -38,7 +38,9 @@ void QDECL SV_NQPhysicsUpdate(cvar_t *var, char *oldvalue)
{
if (!strcmp(var->string, "auto") || !strcmp(var->string, ""))
{ //prediction requires nq physics, so use it by default in multiplayer.
if (progstype <= PROG_QW || (!isDedicated && sv.allocated_client_slots > 1))
if ( progstype <= PROG_QW || //none or qw use qw physics by default
(!isDedicated && sv.allocated_client_slots > 1) || //multiplayer dedicated servers use qw physics for nq mods too. server admins are expected to be able to spend a little more time to configure things properly.
(svprogfuncs&&PR_FindFunction(svprogfuncs, "SV_RunClientCommand", PR_ANY))) //mods that use explicit custom player physics/pred ALWAYS want qw physics (just hope noone forces it off)
var->ival = 0;
else
var->ival = 1;
@ -68,7 +70,7 @@ cvar_t sv_cheatspeedchecktime = CVARD("sv_cheatspeedchecktime", "30", "The inter
#endif
cvar_t sv_playermodelchecks = CVAR("sv_playermodelchecks", "0");
cvar_t sv_ping_ignorepl = CVARD("sv_ping_ignorepl", "0", "If 1, ping times reported for players will ignore the effects of packetloss on ping times. 0 is slightly more honest, but less useful for connection diagnosis.");
cvar_t sv_protocol_nq = CVARD("sv_protocol_nq", "", "Specifies the default protocol to use for new NQ clients. This is only relevent for clients that do not report their supported protocols. Supported values are\n0 = autodetect\n15 = vanilla\n666 = fitzquake\n999 = rmq protocol\nThe sv_bigcoords cvar forces upgrades as required.");
cvar_t sv_protocol_nq = CVARD("sv_protocol_nq", "", "Specifies the default protocol to use for new NQ clients. This is only relevent for clients that do not report their supported protocols. Supported values are\n0 = autodetect\n15 = vanilla\n666 = fitzquake\n999 = rmq protocol\nThe sv_bigcoords cvar forces upgrades as required.");
cvar_t sv_minpitch = CVARAFD("minpitch", "", "sv_minpitch", CVAR_SERVERINFO, "Assumed to be -70");
cvar_t sv_maxpitch = CVARAFD("maxpitch", "", "sv_maxpitch", CVAR_SERVERINFO, "Assumed to be 80");
@ -1224,7 +1226,7 @@ void SV_SendClientPrespawnInfo(client_t *client)
if (client->fteprotocolextensions & PEXT_SOUNDDBL)
maxclientsupportedsounds = MAX_PRECACHE_SOUNDS;
#endif
#ifdef PEXT_SOUNDDBL
#ifdef PEXT2_REPLACEMENTDELTAS
if (client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
maxclientsupportedsounds = MAX_PRECACHE_SOUNDS;
#endif
@ -6154,13 +6156,13 @@ void SV_Pext_f(void)
}
}
SV_ClientProtocolExtensionsChanged(host_client);
if (!host_client->supportedprotocols && Cmd_Argc() == 1)
Con_DPrintf("%s reports no extended capabilities.\n", host_client->name);
else
Con_DPrintf("%s now using pext: %x, %x, %x\n", host_client->name, host_client->fteprotocolextensions, host_client->fteprotocolextensions2, host_client->ezprotocolextensions1);
SV_ClientProtocolExtensionsChanged(host_client);
#ifdef NQPROT
if (ISNQCLIENT(host_client))
SVNQ_New_f();