Weapon preselect/hiding stuff.

Attempt to track disconnection reasons.
Attempt to mimic QS for lightmap extent calculations.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5357 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2018-12-06 04:55:35 +00:00
parent 3f2302ce62
commit 98303e606e
26 changed files with 404 additions and 140 deletions

View File

@ -130,7 +130,7 @@ typedef struct {
char *(*parsetoken) (const char *data, char *out, int outlen, enum com_tokentype_e *toktype);
int (*isserver) (void);
int (*getclientstate) (void);
int (*getclientstate) (char const**disconnectionreason);
void (*localsound) (const char *sample, int channel, float volume);
// file input / search crap
@ -139,7 +139,7 @@ typedef struct {
char *(*fgets) (struct vfsfile_s *fhandle, char *out, size_t outsize); //returns output buffer, or NULL
void (*fprintf) (struct vfsfile_s *fhandle, const char *s, ...);
void (*enumeratefiles) (const char *match, int (QDECL *callback)(const char *fname, qofs_t fsize, time_t mtime, void *ctx, struct searchpathfuncs_s *package), void *ctx);
char *(*path_get) (void);
qboolean (QDECL *nativepath)(const char *fname, enum fs_relative relativeto, char *out, int outlen); //Converts a relative path to a printable system path. All paths are considered to be utf-8. WARNING: This means that windows users will need to use _wfopen etc if they use the resulting path of this function in any system calls. WARNING: this function can and WILL fail for dodgy paths (eg blocking writes to "../engine.dll")
// Drawing stuff
void (*drawsetcliparea) (float x, float y, float width, float height);

View File

@ -2223,7 +2223,7 @@ void CL_ReRecord_f (void)
Q_snprintfz (name, sizeof(name), "%s", s);
CL_Disconnect();
CL_Disconnect(NULL);
//
// open the demo file

View File

@ -5379,12 +5379,14 @@ void CL_LinkPlayers (void)
void CL_LinkViewModel(void)
{
#ifdef QUAKESTATS
extern cvar_t r_viewpreselgun;
entity_t ent;
unsigned int plnum;
unsigned int playereffects;
float alpha;
playerview_t *pv = r_refdef.playerview;
const char *preselectedmodelname;
extern cvar_t cl_gunx, cl_guny, cl_gunz;
extern cvar_t cl_gunanglex, cl_gunangley, cl_gunanglez;
@ -5465,6 +5467,12 @@ void CL_LinkViewModel(void)
ent.flags |= RF_TRANSLUCENT;
}
preselectedmodelname = r_viewpreselgun.ival?IN_GetPreselectedViewmodelName(pv-cl.playerview):NULL;
if (preselectedmodelname)
ent.model = Mod_ForName(preselectedmodelname, MLV_SILENT);
else
ent.model = NULL;
if (!ent.model)
ent.model = cl.model_precache[pv->stats[STAT_WEAPONMODELI]];
if (!ent.model)
{

View File

@ -29,19 +29,19 @@ float in_sensitivityscale = 1;
static void QDECL CL_SpareMsec_Callback (struct cvar_s *var, char *oldvalue);
#ifdef NQPROT
cvar_t cl_movement = CVARD("cl_movement","1", "Specifies whether to send movement sequence info over DPP7 protocols (other protocols are unaffected). Unlike cl_nopred, this can result in different serverside behaviour.");
static cvar_t cl_movement = CVARD("cl_movement","1", "Specifies whether to send movement sequence info over DPP7 protocols (other protocols are unaffected). Unlike cl_nopred, this can result in different serverside behaviour.");
#endif
cvar_t cl_nodelta = CVAR("cl_nodelta","0");
cvar_t cl_c2sdupe = CVAR("cl_c2sdupe", "0");
static cvar_t cl_c2sdupe = CVAR("cl_c2sdupe", "0");
cvar_t cl_c2spps = CVAR("cl_c2spps", "0");
cvar_t cl_c2sImpulseBackup = CVAR("cl_c2sImpulseBackup","3");
cvar_t cl_netfps = CVAR("cl_netfps", "150");
cvar_t cl_sparemsec = CVARC("cl_sparemsec", "10", CL_SpareMsec_Callback);
cvar_t cl_queueimpulses = CVAR("cl_queueimpulses", "0");
cvar_t cl_smartjump = CVAR("cl_smartjump", "1");
cvar_t cl_iDrive = CVARFD("cl_iDrive", "1", CVAR_SEMICHEAT, "Effectively releases movement keys when the opposing key is pressed. This avoids dead-time when both keys are pressed. This can be emulated with various scripts, but that's messy.");
static cvar_t cl_iDrive = CVARFD("cl_iDrive", "1", CVAR_SEMICHEAT, "Effectively releases movement keys when the opposing key is pressed. This avoids dead-time when both keys are pressed. This can be emulated with various scripts, but that's messy.");
cvar_t cl_run = CVARD("cl_run", "0", "Enables autorun, inverting the state of the +speed key.");
cvar_t cl_fastaccel = CVARD("cl_fastaccel", "1", "Begin moving at full speed instantly, instead of waiting a frame or so.");
extern cvar_t cl_rollspeed;
@ -52,6 +52,9 @@ cvar_t in_xflip = {"in_xflip", "0"};
cvar_t prox_inmenu = CVAR("prox_inmenu", "0");
static int preselectedweapons[MAX_SPLITS];
static int preselectedweapon[MAX_SPLITS][32];
usercmd_t cl_pendingcmd[MAX_SPLITS];
/*kinda a hack...*/
@ -163,21 +166,12 @@ qboolean cursor_active;
static void KeyDown (kbutton_t *b, kbutton_t *anti)
static qboolean KeyDown_Scan (kbutton_t *b, kbutton_t *anti, int k)
{
int k;
char *c;
int pnum = CL_TargettedSplit(false);
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
k = -1; // typed manually at the console for continuous down
if (k == b->down[pnum][0] || k == b->down[pnum][1])
return; // repeating key
return false; // repeating key
if (!b->down[pnum][0])
b->down[pnum][0] = k;
@ -186,11 +180,11 @@ static void KeyDown (kbutton_t *b, kbutton_t *anti)
else
{
Con_DPrintf ("Three keys down for a button!\n");
return;
return false;
}
if (b->state[pnum] & 1)
return; // still down
return false; // still down
b->state[pnum] |= 1 + 2; // down + impulse down
if (anti && (anti->state[pnum] & 1) && cl_iDrive.ival)
@ -200,24 +194,36 @@ static void KeyDown (kbutton_t *b, kbutton_t *anti)
anti->state[pnum] &= ~1; // now up
anti->state[pnum] |= 4; // impulse up
}
return true;
}
static void KeyUp (kbutton_t *b)
static void KeyDown (kbutton_t *b, kbutton_t *anti)
{
int k;
char *c;
int pnum = CL_TargettedSplit(false);
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
k = -1; // typed manually at the console for continuous down
KeyDown_Scan(b, anti, k);
}
static qboolean KeyUp_Scan (kbutton_t *b, int k)
{
int pnum = CL_TargettedSplit(false);
if (k < 0)
{ // typed manually at the console, assume for unsticking, so clear all
b->suppressed[pnum] = NULL;
b->down[pnum][0] = b->down[pnum][1] = 0;
b->state[pnum] = 4; // impulse up
return;
if (b->state[pnum] & ~4)
{
b->state[pnum] = 4; // impulse up
return true;
}
return false;
}
if (b->down[pnum][0] == k)
@ -225,12 +231,12 @@ static void KeyUp (kbutton_t *b)
else if (b->down[pnum][1] == k)
b->down[pnum][1] = 0;
else
return; // key up without coresponding down (menu pass through)
return false; // key up without coresponding down (menu pass through)
if (b->down[pnum][0] || b->down[pnum][1])
return; // some other key is still holding it down
return false; // some other key is still holding it down
if (!(b->state[pnum] & 1))
return; // still up (this should not happen)
return false; // still up (this should not happen)
b->state[pnum] &= ~1; // now up
b->state[pnum] |= 4; // impulse up
@ -240,7 +246,242 @@ static void KeyUp (kbutton_t *b)
b->suppressed[pnum]->state[pnum] |= 1 + 2;
b->suppressed[pnum] = NULL;
}
return true;
}
static qboolean KeyUp (kbutton_t *b)
{
int k;
char *c;
c = Cmd_Argv(1);
if (c[0])
k = atoi(c);
else
k = -1;
return KeyUp_Scan(b, k);
}
#ifdef QUAKESTATS
static cvar_t cl_weaponhide = CVARD("cl_weaponhide", "0", "HACK: Attempt to switch weapon to another in order to cheat your killer out of any possible weapon upgrades.\n0: original behaviour\n1: switch away when +attack/+fire is released\n2: switch away only in deathmatch\n");
static cvar_t cl_weaponhide_preference = CVARAD("cl_weaponhide_preference", "2 1", "cl_weaponhide_axe", "The weapon you would like to try to switch to when cl_weaponhide is active");
static cvar_t cl_weaponpreselect = CVARD("cl_weaponpreselect", "0", "HACK: Controls the interaction between the ^aweapon^a and ^a+attack^a commands (does not affect ^aimpulse^a).\n0: weapon switch happens instantly\n1: weapon switch happens on next attack\n2: instant only when already firing, otherwise delayed\n3: delay until new attack only in deathmatch 1\n4: delay until any attack only in deathmatch 1");
static cvar_t cl_weaponforgetorder = CVARD("cl_weaponforgetorder", "0", "The 'weapon' command will lock in its weapon choice, instead of choosing a different weapon between select+fire.");
cvar_t r_viewpreselgun = CVARD("r_viewpreselgun", "0", "HACK: Display the preselected weaponmodel, instead of the current weaponmodel.");
//hacks, because we have to guess what the mod is doing. we'll probably get it wrong, which sucks.
static qboolean IN_HaveWeapon(int pnum, int imp)
{
unsigned int items = cl.playerview[pnum].stats[STAT_ITEMS];
switch (imp)
{
case 1: return (items & IT_AXE)?true:false;
case 2: return (items & IT_SHOTGUN && cl.playerview[pnum].stats[STAT_SHELLS] >= 1)?true:false;
case 3: return (items & IT_SUPER_SHOTGUN && cl.playerview[pnum].stats[STAT_SHELLS] >= 2)?true:false;
case 4: return (items & IT_NAILGUN && cl.playerview[pnum].stats[STAT_NAILS] >= 1)?true:false;
case 5: return (items & IT_SUPER_NAILGUN && cl.playerview[pnum].stats[STAT_NAILS] >= 2)?true:false;
case 6: return (items & IT_GRENADE_LAUNCHER && cl.playerview[pnum].stats[STAT_ROCKETS] >= 1)?true:false;
case 7: return (items & IT_ROCKET_LAUNCHER && cl.playerview[pnum].stats[STAT_ROCKETS] >= 1)?true:false;
case 8: return (items & IT_LIGHTNING && cl.playerview[pnum].stats[STAT_CELLS] >= 1)?true:false;
}
return false;
}
static int IN_BestWeapon_Pre(unsigned int pnum);
//if we're using weapon preselection, then we probably also want to show which weapon will be selected, instead of showing the shotgun the whole time.
//this of course requires more hacks.
const char *IN_GetPreselectedViewmodelName(unsigned int pnum)
{
static const char *nametable[] =
{
NULL, //can't cope with a 0
"progs/v_axe.mdl",
"progs/v_shot.mdl",
"progs/v_shot2.mdl",
"progs/v_nail.mdl",
"progs/v_nail2.mdl",
"progs/v_rock.mdl",
"progs/v_rock2.mdl",
"progs/v_light.mdl",
};
if (r_viewpreselgun.ival && cl_weaponpreselect.ival && pnum < countof(preselectedweapons) && preselectedweapons[pnum])
{
int best = IN_BestWeapon_Pre(pnum);
if (best < countof(nametable))
return nametable[best];
}
return NULL;
}
//FIXME: make a script to decide if a weapon is held? call into the csqc?
static int IN_BestWeapon_Args(unsigned int pnum, int firstarg, int argcount)
{
int i, imp;
unsigned int best = 0;
for (i = firstarg + argcount; --i >= firstarg; )
{
imp = Q_atoi(Cmd_Argv(i));
if (IN_HaveWeapon(pnum, imp))
best = imp;
}
return best;
}
static int IN_BestWeapon_Pre(unsigned int pnum)
{
int i, imp;
unsigned int best = 0;
for (i = preselectedweapons[pnum]; i-- > 0; )
{
imp = preselectedweapon[pnum][i];
if (IN_HaveWeapon(pnum, imp))
best = imp;
}
return best;
}
static void IN_DoPostSelect(void)
{
if (cl_weaponpreselect.ival)
{
int pnum = CL_TargettedSplit(false);
int best = IN_BestWeapon_Pre(pnum);
if (best)
{
in_impulse[pnum][(in_nextimpulse[pnum])%IN_IMPULSECACHE] = best;
in_impulsespending[pnum]=1;
}
}
}
//The weapon command autoselects a prioritised weapon like multi-arg impulse does.
//however, it potentially makes the switch only on the next +attack.
static void IN_Weapon (void)
{
int newimp;
int pnum = CL_TargettedSplit(false);
int mode, best, i;
for (i = 1; i < Cmd_Argc() && i <= countof(preselectedweapon[pnum]); i++)
preselectedweapon[pnum][i-1] = atoi(Cmd_Argv(i));
preselectedweapons[pnum] = i-1;
best = IN_BestWeapon_Pre(pnum);
if (best)
{
newimp = best;
if (cl_weaponforgetorder.ival)
{ //make sure the +attack sticks with the selected weapon.
preselectedweapon[pnum][0] = best;
preselectedweapons[pnum] = 1;
}
}
else
return; //no new weapon...
mode = cl_weaponpreselect.ival;
if (mode == 3)
mode = (cl.deathmatch==1)?1:0;
else if (mode == 4)
mode = (cl.deathmatch==1)?2:0;
if (mode == 1)
return; //don't change yet.
if (mode == 2 && !(in_attack.state[pnum]&3))
return; //2 changes instantly only when already firing.
if (cl_queueimpulses.ival)
{
if (in_impulsespending[pnum]>=IN_IMPULSECACHE)
{
Con_Printf("Too many impulses, ignoring %i\n", newimp);
return;
}
in_impulse[pnum][(in_nextimpulse[pnum]+in_impulsespending[pnum])%IN_IMPULSECACHE] = newimp;
in_impulsespending[pnum]++;
}
else
{
in_impulse[pnum][(in_nextimpulse[pnum])%IN_IMPULSECACHE] = newimp;
in_impulsespending[pnum]=1;
}
}
//+fire 8 7 [keycode]
//does impulse 8 or 7 (according to held weapons) along with a +attack
static void IN_FireDown(void)
{
int pnum = CL_TargettedSplit(false);
int k;
int impulse;
impulse = Cmd_Argc()-1;
k = atoi(Cmd_Argv(impulse));
if (k >= 32)
impulse--; //scancode, don't treat that arg as a weapon number
else
k = -1;
impulse = IN_BestWeapon_Args(pnum, 1, impulse);
if (impulse)
{
in_impulse[pnum][(in_nextimpulse[pnum])%IN_IMPULSECACHE] = impulse;
in_impulsespending[pnum]=1;
}
else
IN_DoPostSelect();
KeyDown_Scan(&in_attack, NULL, k);
}
static void IN_DoWeaponHide(void)
{
if (cl_weaponhide.ival && !(cl_weaponhide.ival==2 && cl.deathmatch==1))
{
int impulse, best = 0;
int pnum = CL_TargettedSplit(false);
char tok[64];
char *l = cl_weaponhide_preference.string;
if (!strcmp(l, "0")) l = "2"; //for compat with ezquake's cl_weaponhide_axe cvar.
while(l && *l)
{
l = COM_ParseOut(l, tok, sizeof(tok));
impulse = atoi(tok);
if (IN_HaveWeapon(pnum, impulse))
best = impulse;
}
if (best)
{ //looks like we're switching away
in_impulse[pnum][(in_nextimpulse[pnum])%IN_IMPULSECACHE] = best;
in_impulsespending[pnum]=1;
}
}
}
//-fire should trigger an impulse 1 or something.
static void IN_FireUp(void)
{
int k;
int impulse;
//any args are used in the +fire version and linger through to the -fire.
//the only useful one is the keynum.
impulse = Cmd_Argc()-1;
k = atoi(Cmd_Argv(impulse));
if (k >= 32)
impulse--; //scancode, don't treat that arg as a weapon number
else
k = -1;
if (KeyUp_Scan(&in_attack, k))
IN_DoWeaponHide();
}
#else
#define IN_DoPostSelect()
#define IN_DoWeaponHide()
#endif
static void IN_KLookDown (void) {KeyDown(&in_klook, NULL);}
static void IN_KLookUp (void) {KeyUp(&in_klook);}
@ -282,8 +523,8 @@ static void IN_SpeedUp(void) {KeyUp(&in_speed);}
static void IN_StrafeDown(void) {KeyDown(&in_strafe, NULL);}
static void IN_StrafeUp(void) {KeyUp(&in_strafe);}
static void IN_AttackDown(void) {KeyDown(&in_attack, NULL);}
static void IN_AttackUp(void) {KeyUp(&in_attack);}
static void IN_AttackDown(void) {IN_DoPostSelect(); KeyDown(&in_attack, NULL);}
static void IN_AttackUp(void) {if (KeyUp(&in_attack)) IN_DoWeaponHide();}
static void IN_UseDown (void) {KeyDown(&in_use, NULL);}
static void IN_UseUp (void) {KeyUp(&in_use);}
@ -397,8 +638,6 @@ void IN_WriteButtons(vfsfile_t *f, qboolean all)
//FIXME: save device remappings to config.
}
//is this useful?
//This function incorporates Tonik's impulse 8 7 6 5 4 3 2 1 to select the prefered weapon on the basis of having it.
//It also incorporates split screen input as well as impulse buffering
void IN_Impulse (void)
@ -411,52 +650,7 @@ void IN_Impulse (void)
#ifdef QUAKESTATS
if (Cmd_Argc() > 2)
{
int best, i, imp, items;
items = cl.playerview[pnum].stats[STAT_ITEMS];
best = 0;
for (i = Cmd_Argc() - 1; i > 0; i--)
{
imp = Q_atoi(Cmd_Argv(i));
if (imp < 1 || imp > 8)
continue;
switch (imp)
{
case 1:
if (items & IT_AXE)
best = 1;
break;
case 2:
if (items & IT_SHOTGUN && cl.playerview[pnum].stats[STAT_SHELLS] >= 1)
best = 2;
break;
case 3:
if (items & IT_SUPER_SHOTGUN && cl.playerview[pnum].stats[STAT_SHELLS] >= 2)
best = 3;
break;
case 4:
if (items & IT_NAILGUN && cl.playerview[pnum].stats[STAT_NAILS] >= 1)
best = 4;
break;
case 5:
if (items & IT_SUPER_NAILGUN && cl.playerview[pnum].stats[STAT_NAILS] >= 2)
best = 5;
break;
case 6:
if (items & IT_GRENADE_LAUNCHER && cl.playerview[pnum].stats[STAT_ROCKETS] >= 1)
best = 6;
break;
case 7:
if (items & IT_ROCKET_LAUNCHER && cl.playerview[pnum].stats[STAT_ROCKETS] >= 1)
best = 7;
break;
case 8:
if (items & IT_LIGHTNING && cl.playerview[pnum].stats[STAT_CELLS] >= 1)
best = 8;
}
}
int best = IN_BestWeapon_Args(pnum, 1, Cmd_Argc() - 1);
if (best)
newimp = best;
}
@ -2378,19 +2572,33 @@ void CL_InitInput (void)
Cmd_AddCommand ("-use", IN_UseUp);
Cmd_AddCommand ("+jump", IN_JumpDown);
Cmd_AddCommand ("-jump", IN_JumpUp);
Cmd_AddCommand ("impulse", IN_Impulse);
Cmd_AddCommandD("weapon", IN_Impulse, "Partial implementation for compatibility"); //for pseudo-compat with ezquake.
Cmd_AddCommandD("impulse", IN_Impulse, "Sends an impulse number to the server (read: weapon change).");
Cmd_AddCommand ("+klook", IN_KLookDown);
Cmd_AddCommand ("-klook", IN_KLookUp);
Cmd_AddCommand ("+mlook", IN_MLookDown);
Cmd_AddCommand ("-mlook", IN_MLookUp);
#ifdef QUAKESTATS
//for pseudo-compat with ezquake.
//this stuff is kinda hacky and exploits instand weapon switching to basically try to cheat.
//for some reason this crap is standard, so not a cheat, despite obviously being a cheat.
Cmd_AddCommandD("weapon", IN_Weapon, "Configures weapon priorities for the next +attack as an alternative for the impulse command");
Cmd_AddCommandD("+fire", IN_FireDown, "'+fire 8 7' will fire lg if you have it and fall back on rl if you don't, and just fire your current weapon if neither are held. Releasing fire will then switch away to exploit a bug in most mods to deny your weapon upgrades to your killer.");
Cmd_AddCommand ("-fire", IN_FireUp);
Cvar_Register (&cl_weaponhide, inputnetworkcvargroup);
Cvar_Register (&cl_weaponhide_preference, inputnetworkcvargroup);
Cvar_Register (&cl_weaponpreselect, inputnetworkcvargroup);
Cvar_Register (&cl_weaponforgetorder, inputnetworkcvargroup);
Cvar_Register (&r_viewpreselgun, inputnetworkcvargroup);
#endif
for (i = 0; i < countof(in_button); i++)
{
static char bcmd[countof(in_button)][2][10];
Q_snprintfz(bcmd[i][0], sizeof(bcmd[sp][0]), "+button%i", i+3);
Q_snprintfz(bcmd[i][1], sizeof(bcmd[sp][1]), "-button%i", i+3);
Cmd_AddCommand (bcmd[i][0], IN_ButtonNDown);
Cmd_AddCommandD(bcmd[i][0], IN_ButtonNDown, "This auxilliary command has mod-specific behaviour (often none).");
Cmd_AddCommand (bcmd[i][1], IN_ButtonNUp);
}
}

View File

@ -46,7 +46,7 @@ int startuppending;
void Host_FinishLoading(void);
cvar_t cl_crypt_rcon = CVARFD("cl_crypt_rcon", "1", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "Controls whether to send a hash instead of sending rcon passwords as plain-text. Set to 1 for security, or 0 for backwards compatibility.\nYour command and any responses will still be sent as plain text.\nInstead, it is recommended to use rcon ONLY via dtls/tls/wss connections."); //CVAR_NOTFROMSERVER prevents evil servers from degrading it to send plain-text passwords.
cvar_t cl_crypt_rcon = CVARFD("cl_crypt_rcon", "1", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "Controls whether to send a hash instead of sending your rcon password as plain-text. Set to 1 for security, or 0 for backwards compatibility.\nYour command and any responses will still be sent as plain text.\nInstead, it is recommended to use rcon ONLY via dtls/tls/wss connections."); //CVAR_NOTFROMSERVER prevents evil servers from degrading it to send plain-text passwords.
cvar_t rcon_password = CVARF("rcon_password", "", CVAR_NOUNSAFEEXPAND);
cvar_t rcon_address = CVARF("rcon_address", "", CVAR_NOUNSAFEEXPAND);
@ -55,6 +55,8 @@ cvar_t cl_timeout = CVAR("cl_timeout", "60");
cvar_t cl_shownet = CVARD("cl_shownet","0", "Debugging var. 0 shows nothing. 1 shows incoming packet sizes. 2 shows individual messages. 3 shows entities too."); // can be 0, 1, or 2
cvar_t cl_disconnectreason = CVARFD("_cl_disconnectreason", "", CVAR_NOSAVE, "This cvar contains the reason for the last disconnection, so that mod menus can know why things failed.");
cvar_t cl_pure = CVARD("cl_pure", "0", "0=standard quake rules.\n1=clients should prefer files within packages present on the server.\n2=clients should use *only* files within packages present on the server.\nDue to quake 1.01/1.06 differences, a setting of 2 is only reliable with total conversions.\nIf sv_pure is set, the client will prefer the highest value set.");
cvar_t cl_sbar = CVARFC("cl_sbar", "0", CVAR_ARCHIVE, CL_Sbar_Callback);
cvar_t cl_hudswap = CVARF("cl_hudswap", "0", CVAR_ARCHIVE);
@ -766,6 +768,8 @@ void CL_CheckForResend (void)
Cvar_ForceSet(&cl_servername, cls.servername);
if (!NET_StringToAdr(cls.servername, 0, &connectinfo.adr))
return; //erk?
if (*cl_disconnectreason.string)
Cvar_Set(&cl_disconnectreason, "");
connectinfo.trying = true;
connectinfo.istransfer = false;
connectinfo.adr.prot = NP_DGRAM;
@ -1190,6 +1194,8 @@ void CL_BeginServerConnect(const char *host, int port, qboolean noproxy)
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr);
#endif
memset(&connectinfo, 0, sizeof(connectinfo));
if (*cl_disconnectreason.string)
Cvar_Set(&cl_disconnectreason, "");
connectinfo.trying = true;
connectinfo.defaultport = port;
connectinfo.protocol = CP_UNKNOWN;
@ -1210,6 +1216,8 @@ void CL_BeginServerReconnect(void)
NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr);
connectinfo.dtlsupgrade = 0;
#endif
if (*cl_disconnectreason.string)
Cvar_Set(&cl_disconnectreason, "");
connectinfo.trying = true;
connectinfo.istransfer = false;
connectinfo.time = 0;
@ -1242,6 +1250,7 @@ void CL_Transfer_f(void)
connectinfo.istransfer = true;
Q_strncpyz(connectinfo.guid, oldguid, sizeof(oldguid)); //retain the same guid on transfers
}
Cvar_Set(&cl_disconnectreason, "Transferring....");
connectinfo.trying = true;
connectinfo.defaultport = cl_defaultport.value;
connectinfo.protocol = CP_UNKNOWN;
@ -1275,7 +1284,7 @@ void CL_Connect_f (void)
#ifndef CLIENTONLY
if (sv.state == ss_clustermode)
CL_Disconnect ();
CL_Disconnect (NULL);
else
#endif
CL_Disconnect_f ();
@ -1311,7 +1320,7 @@ static void CL_ConnectBestRoute_f (void)
#ifndef CLIENTONLY
if (sv.state == ss_clustermode)
CL_Disconnect ();
CL_Disconnect (NULL);
else
#endif
CL_Disconnect_f ();
@ -1774,11 +1783,14 @@ Sends a disconnect message to the server
This is also called on Host_Error, so it shouldn't cause any errors
=====================
*/
void CL_Disconnect (void)
void CL_Disconnect (const char *reason)
{
qbyte final[12];
int i;
if (reason)
Cvar_Set(&cl_disconnectreason, reason);
connectinfo.trying = false;
SCR_SetLoadingStage(0);
@ -1918,7 +1930,7 @@ void CL_Disconnect_f (void)
SV_UnspawnServer();
#endif
CL_Disconnect ();
CL_Disconnect (NULL);
connectinfo.trying = false;
@ -2756,7 +2768,7 @@ void CL_Stopdemo_f (void)
if (cls.demoplayback == DPB_NONE)
return;
CL_StopPlayback ();
CL_Disconnect ();
CL_Disconnect (NULL);
}
@ -2802,7 +2814,7 @@ void CL_Changing_f (void)
=================
CL_Reconnect_f
The server is changing levels
User command, or NQ protocol command (messy).
=================
*/
void CL_Reconnect_f (void)
@ -2831,7 +2843,7 @@ void CL_Reconnect_f (void)
return;
}
CL_Disconnect();
CL_Disconnect(NULL);
CL_BeginServerReconnect();
}
@ -3225,6 +3237,8 @@ void CL_ConnectionlessPacket (void)
Con_TPrintf ("print\n");
s = MSG_ReadString ();
if (connectinfo.trying && NET_CompareBaseAdr(&connectinfo.adr, &net_from) == false)
Cvar_Set(&cl_disconnectreason, s);
Con_Printf ("%s", s);
return;
}
@ -3406,7 +3420,7 @@ client_connect: //fixme: make function
#ifndef CLIENTONLY
if (sv.state != ss_clustermode)
#endif
CL_Disconnect ();
CL_Disconnect (NULL);
}
else
{
@ -3515,24 +3529,33 @@ client_connect: //fixme: make function
if (c == 'p')
{
if (!strncmp(net_message.data+4, "print\n", 6))
{
{ //quake2+quake3 send rejects this way
Con_TPrintf ("print\n");
Con_Printf ("%s", net_message.data+10);
if (connectinfo.trying && NET_CompareBaseAdr(&connectinfo.adr, &net_from) == false)
Cvar_Set(&cl_disconnectreason, net_message.data+10);
return;
}
}
if (c == A2C_PRINT)
{
{ //closest quakeworld has to a reject message
Con_TPrintf ("print\n");
s = MSG_ReadString ();
Con_Printf ("%s", s);
if (connectinfo.trying && NET_CompareBaseAdr(&connectinfo.adr, &net_from) == false)
Cvar_Set(&cl_disconnectreason, s);
return;
}
if (c == 'r')//dp's reject
{
if (c == 'r')
{ //darkplaces-style rejects
s = MSG_ReadString ();
Con_Printf("r%s\n", s);
if (connectinfo.trying && NET_CompareBaseAdr(&connectinfo.adr, &net_from) == false)
Cvar_Set(&cl_disconnectreason, s);
return;
}
@ -3627,6 +3650,9 @@ void CLNQ_ConnectionlessPacket(void)
case CCREP_REJECT:
s = MSG_ReadString();
Con_Printf("Connect failed\n%s\n", s);
if (connectinfo.trying && NET_CompareBaseAdr(&connectinfo.adr, &net_from) == false)
Cvar_Set(&cl_disconnectreason, s);
return;
}
}
@ -3789,7 +3815,7 @@ void CL_ReadPackets (void)
#endif
{
Con_TPrintf ("\nServer connection timed out.\n");
CL_Disconnect ();
CL_Disconnect ("Connection Timed Out");
return;
}
}
@ -4396,6 +4422,7 @@ void CL_Init (void)
Cvar_Register (&cfg_save_name, cl_controlgroup);
Cvar_Register (&cl_disconnectreason, cl_controlgroup);
Cvar_Register (&cl_proxyaddr, cl_controlgroup);
Cvar_Register (&cl_sendguid, cl_controlgroup);
Cvar_Register (&cl_defaultport, cl_controlgroup);
@ -4729,7 +4756,7 @@ NORETURN void VARGS Host_EndGame (const char *message, ...)
SCR_EndLoadingPlaque();
CL_Disconnect ();
CL_Disconnect (string);
SV_UnspawnServer();
connectinfo.trying = false;
@ -4762,7 +4789,7 @@ void VARGS Host_Error (const char *error, ...)
COM_AssertMainThread(string);
Con_TPrintf ("Host_Error: %s\n", string);
CL_Disconnect ();
CL_Disconnect (string);
cls.demonum = -1;
inerror = false;
@ -6169,6 +6196,10 @@ void CL_ExecInitialConfigs(char *resetcommand)
Cbuf_AddText("alias restart_ents \"changelevel . .\"\n",RESTRICT_LOCAL);
Cbuf_AddText("alias restart map_restart\n",RESTRICT_LOCAL);
Cbuf_AddText("alias startmap_sp \"map start\"\n", RESTRICT_LOCAL);
#ifdef QUAKESTATS
Cbuf_AddText("alias +attack2 +button3\n", RESTRICT_LOCAL);
Cbuf_AddText("alias -attack2 -button3\n", RESTRICT_LOCAL);
#endif
Cbuf_AddText("cl_warncmd 0\n", RESTRICT_LOCAL);
Cbuf_AddText("cvar_purgedefaults\n", RESTRICT_LOCAL); //reset cvar defaults to their engine-specified values. the tail end of 'exec default.cfg' will update non-cheat defaults to mod-specified values.
Cbuf_AddText("cvarreset *\n", RESTRICT_LOCAL); //reset all cvars to their current (engine) defaults

View File

@ -1666,7 +1666,7 @@ void CL_RequestNextDownload (void)
}
#endif
SCR_SetLoadingStage(LS_NONE);
CL_Disconnect();
CL_Disconnect("Game Content differs from server");
return;
}
}
@ -7442,7 +7442,7 @@ isilegible:
case svcq2_reconnect: //8. this is actually kinda weird to have
Con_TPrintf ("reconnecting...\n");
#if 1
CL_Disconnect();
CL_Disconnect("Reconnect request");
CL_BeginServerReconnect();
return;
#else
@ -7802,7 +7802,7 @@ void CLNQ_ParseServerMessage (void)
break;
case svc_disconnect:
CL_Disconnect();
CL_Disconnect("Server disconnected");
return;
case svc_centerprint:

View File

@ -1035,6 +1035,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
// Con_Printf("ui_getclientstate\n");
VALIDATEPOINTER(arg[0], sizeof(uiClientState_t));
{
extern cvar_t cl_disconnectreason;
uiClientState_t *state = VM_POINTER(arg[0]);
state->connectPacketCount = 0;//clc.connectPacketCount;
@ -1061,7 +1062,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con
}
Q_strncpyz( state->servername, cls.servername, sizeof( state->servername ) );
Q_strncpyz( state->updateInfoString, "FTE!", sizeof( state->updateInfoString ) ); //warning/motd message from update server
Q_strncpyz( state->messageString, "", sizeof( state->messageString ) ); //error message from game server
Q_strncpyz( state->messageString, cl_disconnectreason.string, sizeof( state->messageString ) ); //error message from game server
state->clientNum = cl.playerview[0].playernum;
}
break;

View File

@ -1082,7 +1082,7 @@ void CL_CheckServerPacks(void);
void CL_EstablishConnection (char *host);
void CL_Disconnect (void);
void CL_Disconnect (const char *reason);
void CL_Disconnect_f (void);
void CL_Reconnect_f (void);
void CL_NextDemo (void);
@ -1153,6 +1153,9 @@ extern float in_sensitivityscale;
void CL_MakeActive(char *gamename);
void CL_UpdateWindowTitle(void);
#ifdef QUAKESTATS
const char *IN_GetPreselectedViewmodelName(unsigned int pnum);
#endif
void CL_InitInput (void);
void CL_SendCmd (double frametime, qboolean mainloop);
void CL_SendMove (usercmd_t *cmd);

View File

@ -1040,7 +1040,6 @@ void CLQ3_SendConnectPacket(netadr_t *to, int challenge, int qport)
{
char infostr[1024];
char data[2048];
char adrbuf[MAX_ADR_SIZE];
sizebuf_t msg;
static const char *nonq3[] = {"challenge", "qport", "protocol", "ip", "chat", NULL};
@ -1054,7 +1053,7 @@ void CLQ3_SendConnectPacket(netadr_t *to, int challenge, int qport)
msg.overflowed = msg.allowoverflow = 0;
msg.maxsize = sizeof(data);
MSG_WriteLong(&msg, -1);
MSG_WriteString(&msg, va("connect \"\\challenge\\%i\\qport\\%i\\protocol\\%i\\ip\\%s%s\"", challenge, qport, PROTOCOL_VERSION_Q3, NET_AdrToString (adrbuf, sizeof(adrbuf), &net_local_cl_ipadr), infostr));
MSG_WriteString(&msg, va("connect \"\\challenge\\%i\\qport\\%i\\protocol\\%i%s\"", challenge, qport, PROTOCOL_VERSION_Q3, infostr));
#ifdef HUFFNETWORK
Huff_EncryptPacket(&msg, 12);
if (!Huff_CompressionCRC(HUFFCRC_QUAKE3))

View File

@ -46,7 +46,7 @@ char *r_defaultimageextensions =
;
static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt);
static void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue);
cvar_t r_imageexensions = CVARCD("r_imageexensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which fte should attempt to load.");
cvar_t r_imageextensions = CVARCD("r_imageextensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which fte should attempt to load.");
cvar_t r_image_downloadsizelimit = CVARFD("r_image_downloadsizelimit", "131072", CVAR_NOTFROMSERVER, "The maximum allowed file size of images loaded from a web-based url. 0 disables completely, while empty imposes no limit.");
extern cvar_t scr_sshot_compression;
extern cvar_t gl_lerpimages;

View File

@ -60,22 +60,21 @@ static int MN_GetServerState(void)
return 1;
return 2;
}
static int MN_GetClientState(void)
static int MN_GetClientState(char const ** disconnect_reason)
{
extern cvar_t cl_disconnectreason;
*disconnect_reason = NULL;
if (cls.state >= ca_active)
return 2;
if (cls.state != ca_disconnected)
return 1;
*disconnect_reason = (const char*)cl_disconnectreason.string;
return 0;
}
static void MN_fclose(vfsfile_t *f)
{
VFS_CLOSE(f);
}
static const char *MN_path_get(void)
{
return host_parms.binarydir;
}
static shader_t *MN_CachePic(const char *picname)
{
return R2D_SafeCachePic(picname);
@ -345,7 +344,7 @@ qboolean MN_Init(void)
VFS_GETS,
VFS_PRINTF,
COM_EnumerateFiles,
MN_path_get,
FS_NativePath,
// Drawing stuff
MN_DrawSetClipArea,

View File

@ -1035,7 +1035,7 @@ qboolean MC_Quit_Key (int key, menu_t *menu)
case 'y':
M_RemoveMenu(menu);
Key_Dest_Add(kdm_console);
CL_Disconnect ();
CL_Disconnect (NULL);
Sys_Quit ();
break;
@ -1065,7 +1065,7 @@ qboolean MC_SaveQuit_Key (int key, menu_t *menu)
case 'N':
M_RemoveMenu(menu);
#ifndef FTE_TARGET_WEB
CL_Disconnect ();
CL_Disconnect (NULL);
Sys_Quit ();
#endif
break;
@ -1075,7 +1075,7 @@ qboolean MC_SaveQuit_Key (int key, menu_t *menu)
M_RemoveMenu(menu);
Cmd_ExecuteString("cfg_save", RESTRICT_LOCAL);
#ifndef FTE_TARGET_WEB
CL_Disconnect ();
CL_Disconnect (NULL);
Sys_Quit ();
#endif
break;
@ -1125,7 +1125,7 @@ void M_Menu_Quit_f (void)
{
case 0:
#ifndef FTE_TARGET_WEB
CL_Disconnect ();
CL_Disconnect (NULL);
Sys_Quit ();
#endif
break;

View File

@ -300,7 +300,7 @@ extern cvar_t r_dodgytgafiles;
extern cvar_t r_dodgypcxfiles;
extern cvar_t r_dodgymiptex;
extern char *r_defaultimageextensions;
extern cvar_t r_imageexensions;
extern cvar_t r_imageextensions;
extern cvar_t r_image_downloadsizelimit;
extern cvar_t r_drawentities;
extern cvar_t r_drawviewmodel;
@ -845,9 +845,9 @@ void Renderer_Init(void)
Cvar_Register(&r_dodgytgafiles, "Hacky bug workarounds");
Cvar_Register(&r_dodgypcxfiles, "Hacky bug workarounds");
Cvar_Register(&r_dodgymiptex, "Hacky bug workarounds");
r_imageexensions.enginevalue = r_defaultimageextensions;
Cvar_Register(&r_imageexensions, GRAPHICALNICETIES);
r_imageexensions.callback(&r_imageexensions, NULL);
r_imageextensions.enginevalue = r_defaultimageextensions;
Cvar_Register(&r_imageextensions, GRAPHICALNICETIES);
r_imageextensions.callback(&r_imageextensions, NULL);
Cvar_Register(&r_image_downloadsizelimit, GRAPHICALNICETIES);
Cvar_Register(&r_loadlits, GRAPHICALNICETIES);
Cvar_Register(&r_lightstylesmooth, GRAPHICALNICETIES);
@ -1587,7 +1587,7 @@ TRACE(("dbg: R_ApplyRenderer: isDedicated = true\n"));
{
int os = sv.state;
sv.state = ss_dead; //prevents server from being killed off too.
CL_Disconnect();
CL_Disconnect("Graphics rendering disabled");
sv.state = os;
}
Sys_InitTerminal();
@ -1775,7 +1775,7 @@ TRACE(("dbg: R_ApplyRenderer: done the models\n"));
// Con_Printf ("\nThe required model file '%s' could not be found.\n\n", cl.model_name[i]);
// Con_Printf ("You may need to download or purchase a client pack in order to play on this server.\n\n");
CL_Disconnect ();
CL_Disconnect ("Worldmodel missing after video reload");
#ifdef VM_UI
UI_Reset();
#endif

View File

@ -968,7 +968,7 @@ static void S_Info (void)
static qboolean OpenAL_InitLibrary(void)
{
#if FTE_TARGET_WEB
#ifdef FTE_TARGET_WEB
firefoxstaticsounds = !!strstr(emscripten_run_script_string("navigator.userAgent"), "Firefox");
if (firefoxstaticsounds)
Con_DPrintf("Firefox detected - disabling static sounds to avoid SORRY, I CAN'T HEAR YOU\n");
@ -1024,6 +1024,8 @@ static qboolean OpenAL_InitLibrary(void)
openallib_tried = true;
#ifdef _WIN32
openallib = Sys_LoadLibrary("OpenAL32", openalfuncs);
if (!openallib)
openallib = Sys_LoadLibrary("soft_oal", openalfuncs);
#else
openallib = Sys_LoadLibrary("libopenal.so.1", openalfuncs);
if (!openallib)

View File

@ -517,7 +517,7 @@ void Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, cha
{
callback(ctx, clipboard_buffer);
}
void Sys_SaveClipboard(clipboardtype_t cbt, char *text)
void Sys_SaveClipboard(clipboardtype_t cbt, const char *text)
{
Q_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE);
}

View File

@ -566,7 +566,7 @@ void VARGS VFS_PRINTF(vfsfile_t *vf, const char *fmt, ...) LIKEPRINTF(2);
enum fs_relative{
FS_BINARYPATH, //for dlls and stuff
FS_ROOT, //./ (the root basepath or root homepath.)
FS_ROOT, //./ (effective -homedir if enabled, otherwise effective -basedir arg)
FS_SYSTEM, //a system path. absolute paths are explicitly allowed and expected, but not required.
//after this point, all types must be relative to a gamedir

View File

@ -102,10 +102,8 @@ void CalcSurfaceExtents (model_t *mod, msurface_t *s)
for (j=0 ; j<2 ; j++)
{
val = v->position[0] * tex->vecs[j][0] +
v->position[1] * tex->vecs[j][1] +
v->position[2] * tex->vecs[j][2] +
tex->vecs[j][3];
//doubles should replicate win32/x87 compiler 80-bit precision better. We have to hope that win64 compilers use the same precision.
val = DotProduct_Double(v->position, tex->vecs[j]) + tex->vecs[j][3];
if (val < mins[j])
mins[j] = val;
if (val > maxs[j])

View File

@ -762,7 +762,7 @@ static void CertLog_Add_Prompted(void *vctx, int button)
CertLog_Write();
}
else
CL_Disconnect();
CL_Disconnect("Server certificate rejected");
certlog_curprompt = NULL;
}

View File

@ -68,6 +68,7 @@ extern vec3_t vec3_origin;
#define FloatInterpolate(a, bness, b, c) ((c) = (a) + (b - a)*bness)
#define DotProduct_Double(x,y) ((double)(x)[0]*(double)(y)[0]+(double)(x)[1]*(double)(y)[1]+(double)(x)[2]*(double)(y)[2]) //cast to doubles, to try to replicate x87 precision in 64bit sse builds etc. there'll still be a precision difference though.
#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
#define DotProduct2(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1])
#define DotProduct4(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]+(x)[3]*(y)[3])

View File

@ -5056,15 +5056,29 @@ static int PF_Write_BoundForNetwork(pubprogfuncs_t *prinst, int minv, int v, int
{
if (v > maxv)
{
int mask = (minv<0)?maxv*2+1:maxv;
int n = (v&mask);
if (minv < 0 && n > maxv)
n |= ~mask; //sign-extend it
if (developer.ival)
PR_RunWarning(prinst, "Write*: value %i is outside of the required %i to %i range\n", v, minv, maxv);
v = maxv;
{
Con_Printf("Write*: value %i is outside of the required %i to %i range, truncating to %i\n", v, minv, maxv, n);
PR_StackTrace(prinst, false);
}
return n;
}
if (v < minv)
{
int mask = (minv<0)?maxv*2+1:maxv;
int n = (v&mask);
if (minv < 0 && n > maxv)
n |= ~mask; //sign-extend it
if (developer.ival)
PR_RunWarning(prinst, "Write*: value %i is outside of the required %i to %i range\n", v, minv, maxv);
v = minv;
{
Con_Printf("Write*: value %i is outside of the required %i to %i range, truncating to %i\n", v, minv, maxv, n);
PR_StackTrace(prinst, false);
}
return n;
}
return v;
}

View File

@ -224,7 +224,7 @@ static qboolean SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version)
#ifndef SERVERONLY
if (cl->netchan.remote_address.type == NA_LOOPBACK)
CL_Disconnect();
CL_Disconnect(NULL);
else
#endif
{

View File

@ -735,7 +735,7 @@ void SV_Map_f (void)
}
if (!sv.state && cls.state)
CL_Disconnect();
CL_Disconnect(NULL);
#endif
if (!isrestart)

View File

@ -265,7 +265,7 @@ void MSV_MapCluster_f(void)
return;
#ifndef SERVERONLY
CL_Disconnect();
CL_Disconnect(NULL);
#endif
if (sv.state)

View File

@ -3199,7 +3199,7 @@ static int SV_LocateDownload(const char *name, flocation_t *loc, char **replacem
{
if (!allow_download_anymap.value && !Q_strncasecmp(name, "maps/", 5))
{
Sys_Printf ("%s denied download of %s - it is in a pak\n", host_client->name, name+8);
Sys_Printf ("%s denied download of %s - it is in a pak\n", host_client->name, name);
return DLERR_PERMISSIONS;
}
}
@ -3242,7 +3242,7 @@ static int SV_LocateDownload(const char *name, flocation_t *loc, char **replacem
{ //if its in a pak file, don't allow downloads if we don't allow the contents of paks to be sent.
if (!allow_download_pakcontents.value)
{
Sys_Printf ("%s denied download of %s - it is in a pak\n", host_client->name, name+8);
Sys_Printf ("%s denied download of %s - it is in a pak\n", host_client->name, name);
return DLERR_PERMISSIONS;
}
}

View File

@ -3391,7 +3391,7 @@ void SVQ3_DirectConnect(void) //Actually connect the client, use up a slot, and
{
InfoBuf_FromString(&cl->userinfo, userinfo, false);
reason = NET_AdrToString(adr, sizeof(adr), &net_from);
InfoBuf_SetKey(&cl->userinfo, "ip", reason);
InfoBuf_SetKey(&cl->userinfo, "ip", reason); //q3 gamecode needs to know the client's ip (server's perception of the client, NOT QW client's perception of the server/proxy)
ret = VM_Call(q3gamevm, GAME_CLIENT_CONNECT, (int)(cl-svs.clients), false, false);
if (!ret)

View File

@ -243,7 +243,7 @@ void Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, cha
{
callback(ctx, clipboard_buffer);
}
void Sys_SaveClipboard(clipboardtype_t cbt, char *text)
void Sys_SaveClipboard(clipboardtype_t cbt, const char *text)
{
free(clipboard_buffer);
clipboard_buffer = strdup(text);