cvar_t cl_sound_ric1 = {0, "cl_sound_ric1", "weapons/ric1.wav", "sound to play with 5% chance during TE_SPIKE/TE_SUPERSPIKE (empty cvar disables sound)"};
cvar_t cl_sound_ric2 = {0, "cl_sound_ric2", "weapons/ric2.wav", "sound to play with 5% chance during TE_SPIKE/TE_SUPERSPIKE (empty cvar disables sound)"};
cvar_t cl_sound_ric3 = {0, "cl_sound_ric3", "weapons/ric3.wav", "sound to play with 10% chance during TE_SPIKE/TE_SUPERSPIKE (empty cvar disables sound)"};
+
+#define RIC_GUNSHOT 1
+#define RIC_GUNSHOTQUAD 2
+cvar_t cl_sound_ric_gunshot = {0, "cl_sound_ric_gunshot", "0", "specifies if and when the related cl_sound_ric and cl_sound_tink sounds apply to TE_GUNSHOT/TE_GUNSHOTQUAD, 0 = no sound, 1 = TE_GUNSHOT, 2 = TE_GUNSHOTQUAD, 3 = TE_GUNSHOT and TE_GUNSHOTQUAD"};
cvar_t cl_sound_r_exp3 = {0, "cl_sound_r_exp3", "weapons/r_exp3.wav", "sound to play during TE_EXPLOSION and related effects (empty cvar disables sound)"};
cvar_t cl_serverextension_download = {0, "cl_serverextension_download", "0", "indicates whether the server supports the download command"};
-cvar_t cl_joinbeforedownloadsfinish = {0, "cl_joinbeforedownloadsfinish", "1", "if non-zero the game will begin after the map is loaded before other downloads finish"};
-cvar_t cl_nettimesyncmode = {0, "cl_nettimesyncmode", "1", "selects method of time synchronization in client with regard to server packets, values are: 0 = no sync, 1 = exact sync (reset timing each packet), 2 = loose sync (reset timing only if it is out of bounds), 3 = tight sync and bounding, 4 = bounded loose sync (reset if significantly wrong, otherwise bound)"};
+cvar_t cl_joinbeforedownloadsfinish = {CVAR_SAVE, "cl_joinbeforedownloadsfinish", "1", "if non-zero the game will begin after the map is loaded before other downloads finish"};
+cvar_t cl_nettimesyncfactor = {CVAR_SAVE, "cl_nettimesyncfactor", "0", "rate at which client time adapts to match server time, 1 = instantly, 0.125 = slowly, 0 = not at all (bounding still applies)"};
+cvar_t cl_nettimesyncboundmode = {CVAR_SAVE, "cl_nettimesyncboundmode", "6", "method of restricting client time to valid values, 0 = no correction, 1 = tight bounding (jerky with packet loss), 2 = loose bounding (corrects it if out of bounds), 3 = leniant bounding (ignores temporary errors due to varying framerate), 4 = slow adjustment method from Quake3, 5 = slighttly nicer version of Quake3 method, 6 = bounding + Quake3"};
+cvar_t cl_nettimesyncboundtolerance = {CVAR_SAVE, "cl_nettimesyncboundtolerance", "0.25", "how much error is tolerated by bounding check, as a fraction of frametime, 0.25 = up to 25% margin of error tolerated, 1 = use only new time, 0 = use only old time (same effect as setting cl_nettimesyncfactor to 1)"};
cvar_t cl_iplog_name = {CVAR_SAVE, "cl_iplog_name", "darkplaces_iplog.txt", "name of iplog file containing player addresses for iplog_list command and automatic ip logging when parsing status command"};
static qboolean QW_CL_CheckOrDownloadFile(const char *filename);
msg.data = buf;
msg.maxsize = sizeof(buf);
MSG_WriteChar(&msg, clc_nop);
- NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol);
+ NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000);
}
}
data = entdata;
if (!data)
return;
- if (!COM_ParseTokenConsole(&data))
+ if (!COM_ParseToken_Simple(&data, false))
return; // error
if (com_token[0] != '{')
return; // error
while (1)
{
- if (!COM_ParseTokenConsole(&data))
+ if (!COM_ParseToken_Simple(&data, false))
return; // error
if (com_token[0] == '}')
break; // end of worldspawn
strlcpy (key, com_token, sizeof (key));
while (key[strlen(key)-1] == ' ') // remove trailing spaces
key[strlen(key)-1] = 0;
- if (!COM_ParseTokenConsole(&data))
+ if (!COM_ParseToken_Simple(&data, false))
return; // error
strlcpy (value, com_token, sizeof (value));
if (!strcmp("sky", key))
cls.qw_downloadtype = dl_none;
+ // parse the Q3 shader files
+ Mod_LoadQ3Shaders();
+
// touch all of the precached models that are still loaded so we can free
// anything that isn't needed
- Mod_ClearUsed();
+ if (!sv.active)
+ Mod_ClearUsed();
for (i = 1;i < MAX_MODELS && cl.model_name[i][0];i++)
Mod_FindName(cl.model_name[i]);
// precache any models used by the client (this also marks them used)
{
// loading models
+ // parse the Q3 shader files
+ if (cl.loadmodel_current < 2)
+ Mod_LoadQ3Shaders();
+
for (;cl.loadmodel_current < cl.loadmodel_total;cl.loadmodel_current++)
{
if (cl.model_precache[cl.loadmodel_current] && cl.model_precache[cl.loadmodel_current]->Draw)
str = MSG_ReadString ();
strlcpy (cl.levelname, str, sizeof(cl.levelname));
- // get the movevars
- cl.qw_movevars_gravity = MSG_ReadFloat();
- cl.qw_movevars_stopspeed = MSG_ReadFloat();
- cl.qw_movevars_maxspeed = MSG_ReadFloat();
- cl.qw_movevars_spectatormaxspeed = MSG_ReadFloat();
- cl.qw_movevars_accelerate = MSG_ReadFloat();
- cl.qw_movevars_airaccelerate = MSG_ReadFloat();
- cl.qw_movevars_wateraccelerate = MSG_ReadFloat();
- cl.qw_movevars_friction = MSG_ReadFloat();
- cl.qw_movevars_waterfriction = MSG_ReadFloat();
- cl.qw_movevars_entgravity = MSG_ReadFloat();
+ // get the movevars that are defined in the qw protocol
+ cl.movevars_gravity = MSG_ReadFloat();
+ cl.movevars_stopspeed = MSG_ReadFloat();
+ cl.movevars_maxspeed = MSG_ReadFloat();
+ cl.movevars_spectatormaxspeed = MSG_ReadFloat();
+ cl.movevars_accelerate = MSG_ReadFloat();
+ cl.movevars_airaccelerate = MSG_ReadFloat();
+ cl.movevars_wateraccelerate = MSG_ReadFloat();
+ cl.movevars_friction = MSG_ReadFloat();
+ cl.movevars_waterfriction = MSG_ReadFloat();
+ cl.movevars_entgravity = MSG_ReadFloat();
+
+ // other movevars not in the protocol...
+ cl.movevars_wallfriction = 0;
+ cl.movevars_timescale = 1;
+ cl.movevars_jumpvelocity = 270;
+ cl.movevars_edgefriction = 2;
+ cl.movevars_maxairspeed = 30;
+ cl.movevars_stepheight = 18;
+ cl.movevars_airaccel_qw = 1;
+ cl.movevars_airaccel_sideways_friction = 0;
// seperate the printfs so the server message can have a color
Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n\2%s\n", str);
// touch all of the precached models that are still loaded so we can free
// anything that isn't needed
- Mod_ClearUsed();
+ if (!sv.active)
+ Mod_ClearUsed();
for (i = 1;i < nummodels;i++)
Mod_FindName(cl.model_name[i]);
// precache any models used by the client (this also marks them used)
ent->render.framelerp = 1;
// reset various persistent stuff
ent->persistent.muzzleflash = 0;
- VectorCopy(ent->state_current.origin, ent->persistent.trail_origin);
+ ent->persistent.trail_allowed = false;
}
else if (DotProduct(odelta, odelta) > 1000*1000 || (cl.fixangle[0] && !cl.fixangle[1]))
{
VectorCopy(ent->state_current.angles, ent->persistent.oldangles);
VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
VectorCopy(ent->state_current.angles, ent->persistent.newangles);
+ ent->persistent.trail_allowed = false;
}
else if (ent->state_current.flags & RENDER_STEP)
{
VectorSet(pos2, pos[0] + radius, pos[1] + radius, pos[2] + radius);
VectorSet(pos, pos[0] - radius, pos[1] - radius, pos[2] - radius);
CL_ParticleEffect(EFFECT_TE_GUNSHOT, radius, pos, pos2, vec3_origin, vec3_origin, NULL, 0);
+ if(cl_sound_ric_gunshot.integer & RIC_GUNSHOT)
+ {
+ if (rand() % 5)
+ S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
+ else
+ {
+ rnd = rand() & 3;
+ if (rnd == 1)
+ S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
+ else if (rnd == 2)
+ S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+ else
+ S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
+ }
+ }
break;
case QW_TE_BLOOD:
MSG_ReadVector(pos, cls.protocol);
CL_FindNonSolidLocation(pos, pos, 4);
CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
+ if(cl_sound_ric_gunshot.integer & RIC_GUNSHOT)
+ {
+ if (rand() % 5)
+ S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
+ else
+ {
+ rnd = rand() & 3;
+ if (rnd == 1)
+ S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
+ else if (rnd == 2)
+ S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+ else
+ S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
+ }
+ }
break;
case TE_GUNSHOTQUAD:
MSG_ReadVector(pos, cls.protocol);
CL_FindNonSolidLocation(pos, pos, 4);
CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
+ if(cl_sound_ric_gunshot.integer & RIC_GUNSHOTQUAD)
+ {
+ if (rand() % 5)
+ S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
+ else
+ {
+ rnd = rand() & 3;
+ if (rnd == 1)
+ S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
+ else if (rnd == 2)
+ S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
+ else
+ S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
+ }
+ }
break;
case TE_EXPLOSION:
CL_ParticleEffect(effectindex, count, origin, origin, velocity, velocity, NULL, 0);
}
+void CL_ParsePointParticles1(void)
+{
+ int effectindex;
+ vec3_t origin;
+ effectindex = (unsigned short)MSG_ReadShort();
+ MSG_ReadVector(origin, cls.protocol);
+ CL_ParticleEffect(effectindex, 1, origin, origin, vec3_origin, vec3_origin, NULL, 0);
+}
+
typedef struct cl_iplog_item_s
{
char *address;
return true;
}
+extern cvar_t slowmo;
static void CL_NetworkTimeReceived(double newtime)
{
- if (cls.timedemo || (cl.islocalgame && !sv_fixedframeratesingleplayer.integer) || cl.mtime[0] == newtime || cls.signon < SIGNONS)
- cl.time = cl.mtime[1] = cl.mtime[0] = newtime;
- else
- {
- cl.mtime[1] = max(cl.mtime[0], newtime - 0.1);
- cl.mtime[0] = newtime;
+ double timehigh;
+ cl.mtime[1] = cl.mtime[0];
+ cl.mtime[0] = newtime;
+ if (cls.timedemo || (cl.islocalgame && !sv_fixedframeratesingleplayer.integer) || cl.mtime[1] == cl.mtime[0] || cls.signon < SIGNONS)
+ cl.time = cl.mtime[1] = newtime;
+ else if (cls.protocol != PROTOCOL_QUAKEWORLD && !cls.demoplayback)
+ {
+ cl.mtime[1] = max(cl.mtime[1], cl.mtime[0] - 0.1);
if (developer.integer >= 100 && vid_activewindow)
{
if (cl.time < cl.mtime[1] - (cl.mtime[0] - cl.mtime[1]))
else if (cl.time > cl.mtime[0] + (cl.mtime[0] - cl.mtime[1]))
Con_Printf("--- cl.time > cl.mtime[0] (%f > %f ... %f)\n", cl.time, cl.mtime[1], cl.mtime[0]);
}
- if (cl_nettimesyncmode.integer == 2)
+ cl.time += (cl.mtime[1] - cl.time) * bound(0, cl_nettimesyncfactor.value, 1);
+ timehigh = cl.mtime[1] + (cl.mtime[0] - cl.mtime[1]) * cl_nettimesyncboundtolerance.value;
+ if (cl_nettimesyncboundmode.integer == 1)
+ cl.time = bound(cl.mtime[1], cl.time, cl.mtime[0]);
+ else if (cl_nettimesyncboundmode.integer == 2)
{
- if (cl.time < cl.mtime[1] || cl.time > cl.mtime[0] + (cl.mtime[0] - cl.mtime[1]))
+ if (cl.time < cl.mtime[1] || cl.time > timehigh)
cl.time = cl.mtime[1];
}
- else if (cl_nettimesyncmode.integer == 4)
+ else if (cl_nettimesyncboundmode.integer == 3)
{
- // if it's significantly out of bounds, reset it to cl.mtime[1]
- if (cl.time > cl.mtime[0] + (cl.mtime[0] - cl.mtime[1]))
+ if ((cl.time < cl.mtime[1] && cl.oldtime < cl.mtime[1]) || (cl.time > timehigh && cl.oldtime > timehigh))
cl.time = cl.mtime[1];
- // clamp it to the valid range
+ }
+ else if (cl_nettimesyncboundmode.integer == 4)
+ {
+ if (fabs(cl.time - cl.mtime[1]) > 0.5)
+ cl.time = cl.mtime[1]; // reset
+ else if (fabs(cl.time - cl.mtime[1]) > 0.1)
+ cl.time += 0.5 * (cl.mtime[1] - cl.time); // fast
+ else if (cl.time > cl.mtime[1])
+ cl.time -= 0.002 * cl.movevars_timescale; // fall into the past by 2ms
+ else
+ cl.time += 0.001 * cl.movevars_timescale; // creep forward 1ms
+ }
+ else if (cl_nettimesyncboundmode.integer == 5)
+ {
+ if (fabs(cl.time - cl.mtime[1]) > 0.5)
+ cl.time = cl.mtime[1]; // reset
+ else if (fabs(cl.time - cl.mtime[1]) > 0.1)
+ cl.time += 0.5 * (cl.mtime[1] - cl.time); // fast
+ else
+ cl.time = bound(cl.time - 0.002 * cl.movevars_timescale, cl.mtime[1], cl.time + 0.001 * cl.movevars_timescale);
+ }
+ else if (cl_nettimesyncboundmode.integer == 6)
+ {
cl.time = bound(cl.mtime[1], cl.time, cl.mtime[0]);
+ cl.time = bound(cl.time - 0.002 * cl.movevars_timescale, cl.mtime[1], cl.time + 0.001 * cl.movevars_timescale);
}
- else if (cl_nettimesyncmode.integer)
- cl.time = cl.mtime[1];
}
// this packet probably contains a player entity update, so we will need
// to update the prediction
=====================
*/
int parsingerror = false;
+extern void CL_UpdateMoveVars(void);
void CL_ParseServerMessage(void)
{
int cmd;
// fade weapon view kick
cl.qw_weaponkick = min(cl.qw_weaponkick + 10 * bound(0, cl.time - cl.oldtime, 0.1), 0);
+ cls.servermovesequence = cls.netcon->qw.incoming_sequence;
+
while (1)
{
if (msg_badread)
break;
case qw_svc_maxspeed:
- cl.qw_movevars_maxspeed = MSG_ReadFloat();
+ cl.movevars_maxspeed = MSG_ReadFloat();
break;
case qw_svc_entgravity:
- cl.qw_movevars_entgravity = MSG_ReadFloat();
+ cl.movevars_entgravity = MSG_ReadFloat();
+ if (!cl.movevars_entgravity)
+ cl.movevars_entgravity = 1.0f;
break;
case qw_svc_setpause:
}
}
- // fully kill the still slightly dead qw player entities each frame
- for (i = 1;i < cl.maxclients;i++)
- if (!cl.entities_active[i])
- cl.entities[i].state_current.active = false;
+ // fully kill the still slightly dead qw player entities each frame,
+ // but only if a player update was received
+ for (i = 1;i <= cl.maxclients;i++)
+ if (cl.entities_active[i])
+ break;
+ if (i <= cl.maxclients)
+ {
+ // kill all non-updated entities this frame
+ for (i = 1;i <= cl.maxclients;i++)
+ if (!cl.entities_active[i])
+ cl.entities[i].state_current.active = false;
+ }
+ else
+ {
+ // no update this frame, restore the cl.entities_active for good measure
+ for (i = 1;i <= cl.maxclients;i++)
+ cl.entities_active[i] = cl.entities[i].state_current.active;
+ }
}
else
{
case svc_pointparticles:
CL_ParsePointParticles();
break;
+ case svc_pointparticles1:
+ CL_ParsePointParticles1();
+ break;
}
}
}
EntityFrameQuake_ISeeDeadEntities();
+ CL_UpdateMoveVars();
+
parsingerror = false;
// LordHavoc: this was at the start of the function before cl_autodemo was
Cvar_RegisterVariable(&cl_sound_ric1);
Cvar_RegisterVariable(&cl_sound_ric2);
Cvar_RegisterVariable(&cl_sound_ric3);
+ Cvar_RegisterVariable(&cl_sound_ric_gunshot);
Cvar_RegisterVariable(&cl_sound_r_exp3);
Cvar_RegisterVariable(&cl_joinbeforedownloadsfinish);
// server extension cvars set by commands issued from the server during connect
Cvar_RegisterVariable(&cl_serverextension_download);
- Cvar_RegisterVariable(&cl_nettimesyncmode);
+ Cvar_RegisterVariable(&cl_nettimesyncfactor);
+ Cvar_RegisterVariable(&cl_nettimesyncboundmode);
+ Cvar_RegisterVariable(&cl_nettimesyncboundtolerance);
Cvar_RegisterVariable(&cl_iplog_name);
Cmd_AddCommand("nextul", QW_CL_NextUpload, "sends next fragment of current upload buffer (screenshot for example)");