Skip to content

Commit

Permalink
Add optimized client-side prediction
Browse files Browse the repository at this point in the history
Locally predict weapon/ammo/armor/health/powerups pickups
Fix picked up items appearing for a fraction of second right after pickup
Delay item pickup after respawn
Filter jump/pain events to avoid duplicates/sound distortion
Try to play potentially dropped events
Print ping in top-left corner of lagometer, fix "snc" label position
Code cleanup
  • Loading branch information
ec- committed Aug 2, 2017
1 parent e15b282 commit 62f995d
Show file tree
Hide file tree
Showing 16 changed files with 833 additions and 134 deletions.
34 changes: 33 additions & 1 deletion code/cgame/cg_draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,7 @@ void CG_AddLagometerFrameInfo( void ) {
lagometer.frameCount++;
}


/*
==============
CG_AddLagometerSnapshotInfo
Expand All @@ -1619,6 +1620,7 @@ void CG_AddLagometerSnapshotInfo( snapshot_t *snap ) {
lagometer.snapshotCount++;
}


/*
==============
CG_DrawDisconnect
Expand Down Expand Up @@ -1767,7 +1769,11 @@ static void CG_DrawLagometer( void ) {
trap_R_SetColor( NULL );

if ( cg_nopredict.integer || cg_synchronousClients.integer ) {
CG_DrawBigString( ax, ay, "snc", 1.0 );
CG_DrawStringExt( 640 - 16, y, "snc", g_color_table[ColorIndex(COLOR_WHITE)], qfalse, qfalse, 5, 10, 0 );
}

if ( !cg.demoPlayback ) {
CG_DrawStringExt( x+1, y, va( "%ims", cg.meanPing ), g_color_table[ColorIndex(COLOR_WHITE)], qfalse, qfalse, 5, 10, 0 );
}

CG_DrawDisconnect();
Expand Down Expand Up @@ -2578,6 +2584,28 @@ static void CG_DrawTourneyScoreboard( void ) {
#endif
}


static void CG_CalculatePing( void ) {
int count, i, v;

cg.meanPing = 0;

for ( i = 0, count = 0; i < LAG_SAMPLES; i++ ) {

v = lagometer.snapshotSamples[i];
if ( v >= 0 ) {
cg.meanPing += v;
count++;
}

}

if ( count ) {
cg.meanPing /= count;
}
}


/*
=====================
CG_DrawActive
Expand All @@ -2592,6 +2620,10 @@ void CG_DrawActive( stereoFrame_t stereoView ) {
return;
}

if ( !cg.demoPlayback ) {
CG_CalculatePing();
}

// optionally draw the tournement scoreboard instead
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR &&
( cg.snap->ps.pm_flags & PMF_SCOREBOARD ) ) {
Expand Down
4 changes: 2 additions & 2 deletions code/cgame/cg_ents.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ CG_Item
static void CG_Item( centity_t *cent ) {
refEntity_t ent;
entityState_t *es;
gitem_t *item;
const gitem_t *item;
int msec;
float frac;
float scale;
Expand All @@ -221,7 +221,7 @@ static void CG_Item( centity_t *cent ) {
}

// if set to invisible, skip
if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) ) {
if ( !es->modelindex || ( es->eFlags & EF_NODRAW ) || cent->delaySpawn > cg.time ) {
return;
}

Expand Down
53 changes: 47 additions & 6 deletions code/cgame/cg_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,9 @@ static void CG_UseItem( centity_t *cent ) {
break;
#endif
}

}


/*
================
CG_ItemPickup
Expand Down Expand Up @@ -464,6 +464,12 @@ void CG_PainEvent( centity_t *cent, int health ) {

// don't do more than two pain sounds a second
if ( cg.time - cent->pe.painTime < 500 ) {
cent->pe.painIgnore = qfalse;
return;
}

if ( cent->pe.painIgnore ) {
cent->pe.painIgnore = qfalse;
return;
}

Expand Down Expand Up @@ -504,15 +510,16 @@ also called by CG_CheckPlayerstateEvents
==============
*/
#define DEBUGNAME(x) if(cg_debugEvents.integer){CG_Printf(x"\n");}
void CG_EntityEvent( centity_t *cent, vec3_t position ) {
void CG_EntityEvent( centity_t *cent, vec3_t position, int entityNum ) {
entityState_t *es;
int event;
entity_event_t event;
vec3_t dir;
const char *s;
int clientNum;
clientInfo_t *ci;
vec3_t vec;
float fovOffset;
centity_t *ce;

es = &cent->currentState;
event = es->event & ~EV_EVENT_BITS;
Expand Down Expand Up @@ -586,6 +593,8 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
DEBUGNAME("EV_FALL_MEDIUM");
// use normal pain sound
trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) );
cent->pe.painIgnore = qtrue;
cent->pe.painTime = cg.time; // don't play a pain sound right after this
if ( clientNum == cg.predictedPlayerState.clientNum ) {
// smooth landing z changes
cg.landChange = -16;
Expand All @@ -595,6 +604,7 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
case EV_FALL_FAR:
DEBUGNAME("EV_FALL_FAR");
trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) );
cent->pe.painIgnore = qtrue;
cent->pe.painTime = cg.time; // don't play a pain sound right after this
if ( clientNum == cg.predictedPlayerState.clientNum ) {
// smooth landing z changes
Expand Down Expand Up @@ -662,7 +672,9 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {

case EV_JUMP:
DEBUGNAME("EV_JUMP");
trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) );
// pain event with fast sequential jump just creates sound distortion
if ( cg.time - cent->pe.painTime > 50 )
trap_S_StartSound (NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) );
break;
case EV_TAUNT:
DEBUGNAME("EV_TAUNT");
Expand Down Expand Up @@ -722,6 +734,17 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
if ( index < 1 || index >= bg_numItems ) {
break;
}

if ( entityNum >= 0 ) {
// our predicted entity
ce = cg_entities + entityNum;
if ( ce->delaySpawn > cg.time && ce->delaySpawnPlayed ) {
break; // delay item pickup
}
} else {
ce = NULL;
}

item = &bg_itemlist[ index ];

// powerups and team items will have a separate global sound, this one
Expand Down Expand Up @@ -753,6 +776,10 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
if ( es->number == cg.snap->ps.clientNum ) {
CG_ItemPickup( index );
}

if ( ce ) {
ce->delaySpawnPlayed = qtrue;
}
}
break;

Expand All @@ -767,6 +794,17 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
if ( index < 1 || index >= bg_numItems ) {
break;
}

if ( entityNum >= 0 ) {
// our predicted entity
ce = cg_entities + entityNum;
if ( ce->delaySpawn > cg.time && ce->delaySpawnPlayed ) {
break;
}
} else {
ce = NULL;
}

item = &bg_itemlist[ index ];
// powerup pickups are global
if( item->pickup_sound ) {
Expand All @@ -777,6 +815,10 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) {
if ( es->number == cg.snap->ps.clientNum ) {
CG_ItemPickup( index );
}

if ( ce ) {
ce->delaySpawnPlayed = qtrue;
}
}
break;

Expand Down Expand Up @@ -1277,6 +1319,5 @@ void CG_CheckEvents( centity_t *cent ) {
BG_EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, cent->lerpOrigin );
CG_SetEntitySoundPosition( cent );

CG_EntityEvent( cent, cent->lerpOrigin );
CG_EntityEvent( cent, cent->lerpOrigin, -1 );
}

27 changes: 21 additions & 6 deletions code/cgame/cg_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ typedef struct {
lerpFrame_t legs, torso, flag;
int painTime;
int painDirection; // flip from 0 to 1
qboolean painIgnore;
int lightningFiring;

// railgun trail spawning
Expand Down Expand Up @@ -177,6 +178,8 @@ typedef struct centity_s {
int trailTime; // so missile trails can handle dropped initial packets
int dustTrailTime;
int miscTime;
int delaySpawn;
qboolean delaySpawnPlayed;

int snapShotTime; // last time this entity was found in a snapshot

Expand Down Expand Up @@ -446,7 +449,11 @@ typedef struct {
// occurs, and they will have visible effects for #define STEP_TIME or whatever msec after

#define MAX_PREDICTED_EVENTS 16


#define PICKUP_PREDICTION_DELAY 200

#define NUM_SAVED_STATES ( CMD_BACKUP + 2 )

typedef struct {
int clientFrame; // incremented each frame

Expand Down Expand Up @@ -648,6 +655,15 @@ typedef struct {
char testModelName[MAX_QPATH];
qboolean testGun;

// optimized prediction
int lastPredictedCommand;
int lastServerTime;
playerState_t savedPmoveStates[ NUM_SAVED_STATES ];
int stateHead, stateTail;

int meanPing;
int timeResidual;
int allowPickupPrediction;
} cg_t;


Expand Down Expand Up @@ -875,7 +891,6 @@ typedef struct {
sfxHandle_t respawnSound;
sfxHandle_t talkSound;
sfxHandle_t landSound;
sfxHandle_t fallSound;
sfxHandle_t jumpPadSound;

sfxHandle_t oneMinuteSound;
Expand Down Expand Up @@ -976,8 +991,6 @@ typedef struct {
sfxHandle_t scoutSound;
#endif
qhandle_t cursor;
qhandle_t selectCursor;
qhandle_t sizeCursor;

sfxHandle_t regenSound;
sfxHandle_t protectSound;
Expand Down Expand Up @@ -1198,6 +1211,7 @@ extern vmCvar_t cg_recordSPDemo;
extern vmCvar_t cg_recordSPDemoName;
extern vmCvar_t cg_obeliskRespawnDelay;
#endif
extern const char *eventnames[EV_MAX];

//
// cg_main.c
Expand Down Expand Up @@ -1334,13 +1348,14 @@ void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec
void CG_PredictPlayerState( void );
void CG_LoadDeferredPlayers( void );

void CG_PlayDroppedEvents( playerState_t *ps, playerState_t *ops );

//
// cg_events.c
//
void CG_CheckEvents( centity_t *cent );
const char *CG_PlaceString( int rank );
void CG_EntityEvent( centity_t *cent, vec3_t position );
const char *CG_PlaceString( int rank );
void CG_EntityEvent( centity_t *cent, vec3_t position, int entityNum );
void CG_PainEvent( centity_t *cent, int health );


Expand Down
8 changes: 4 additions & 4 deletions code/cgame/cg_marks.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,11 @@ cparticle_t *active_particles, *free_particles;
cparticle_t particles[MAX_PARTICLES];
const int cl_numparticles = MAX_PARTICLES;

qboolean initparticles = qfalse;
vec3_t pvforward, pvright, pvup;
vec3_t rforward, rright, rup;
qboolean initparticles = qfalse;
vec3_t pvforward, pvright, pvup;
vec3_t rforward, rright, rup;

float oldtime;
int oldtime;

/*
===============
Expand Down

0 comments on commit 62f995d

Please sign in to comment.