mirror of
https://github.com/w23/xash3d-fwgs
synced 2024-12-17 06:30:44 +01:00
5e0a0765ce
The `.editorconfig` file in this repo is configured to trim all trailing whitespace regardless of whether the line is modified. Trims all trailing whitespace in the repository to make the codebase easier to work with in editors that respect `.editorconfig`. `git blame` becomes less useful on these lines but it already isn't very useful. Commands: ``` find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ ```
496 lines
9.4 KiB
C
496 lines
9.4 KiB
C
/*
|
|
cl_events.c - client-side event system implementation
|
|
Copyright (C) 2011 Uncle Mike
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "client.h"
|
|
#include "event_flags.h"
|
|
#include "net_encode.h"
|
|
#include "con_nprint.h"
|
|
|
|
/*
|
|
===============
|
|
CL_ResetEvent
|
|
|
|
===============
|
|
*/
|
|
void CL_ResetEvent( event_info_t *ei )
|
|
{
|
|
ei->index = 0;
|
|
memset( &ei->args, 0, sizeof( ei->args ));
|
|
ei->fire_time = 0.0;
|
|
ei->flags = 0;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_CalcPlayerVelocity
|
|
|
|
compute velocity for a given client
|
|
=============
|
|
*/
|
|
void CL_CalcPlayerVelocity( int idx, vec3_t velocity )
|
|
{
|
|
clientdata_t *pcd;
|
|
vec3_t delta;
|
|
double dt;
|
|
|
|
VectorClear( velocity );
|
|
|
|
if( idx <= 0 || idx > cl.maxclients )
|
|
return;
|
|
|
|
if( idx == cl.playernum + 1 )
|
|
{
|
|
pcd = &cl.frames[cl.parsecountmod].clientdata;
|
|
VectorCopy( pcd->velocity, velocity );
|
|
}
|
|
else
|
|
{
|
|
dt = clgame.entities[idx].curstate.animtime - clgame.entities[idx].prevstate.animtime;
|
|
|
|
if( dt != 0.0 )
|
|
{
|
|
VectorSubtract( clgame.entities[idx].curstate.velocity, clgame.entities[idx].prevstate.velocity, delta );
|
|
VectorScale( delta, 1.0f / dt, velocity );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( clgame.entities[idx].curstate.velocity, velocity );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_DescribeEvent
|
|
|
|
=============
|
|
*/
|
|
void CL_DescribeEvent( int slot, int flags, const char *eventname )
|
|
{
|
|
int idx = (slot & 31);
|
|
con_nprint_t info;
|
|
|
|
if( !eventname || !cl_showevents->value )
|
|
return;
|
|
|
|
// mark reliable as green and unreliable as red
|
|
if( FBitSet( flags, FEV_RELIABLE ))
|
|
VectorSet( info.color, 0.0f, 1.0f, 0.0f );
|
|
else VectorSet( info.color, 1.0f, 0.0f, 0.0f );
|
|
|
|
info.time_to_live = 0.5f;
|
|
info.index = idx;
|
|
|
|
Con_NXPrintf( &info, "%i %f %s", slot, cl.time, eventname );
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_SetEventIndex
|
|
|
|
=============
|
|
*/
|
|
void CL_SetEventIndex( const char *szEvName, int ev_index )
|
|
{
|
|
cl_user_event_t *ev;
|
|
int i;
|
|
|
|
if( !szEvName || !*szEvName )
|
|
return; // ignore blank names
|
|
|
|
// search event by name to link with
|
|
for( i = 0; i < MAX_EVENTS; i++ )
|
|
{
|
|
ev = clgame.events[i];
|
|
if( !ev ) break;
|
|
|
|
if( !Q_stricmp( ev->name, szEvName ))
|
|
{
|
|
ev->index = ev_index;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_EventIndex
|
|
|
|
=============
|
|
*/
|
|
word CL_EventIndex( const char *name )
|
|
{
|
|
int i;
|
|
|
|
if( !COM_CheckString( name ))
|
|
return 0;
|
|
|
|
for( i = 1; i < MAX_EVENTS && cl.event_precache[i][0]; i++ )
|
|
{
|
|
if( !Q_stricmp( cl.event_precache[i], name ))
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_RegisterEvent
|
|
|
|
=============
|
|
*/
|
|
void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func )
|
|
{
|
|
cl_user_event_t *ev;
|
|
|
|
if( lastnum == MAX_EVENTS )
|
|
return;
|
|
|
|
// clear existing or allocate new one
|
|
if( !clgame.events[lastnum] )
|
|
clgame.events[lastnum] = Mem_Calloc( cls.mempool, sizeof( cl_user_event_t ));
|
|
else memset( clgame.events[lastnum], 0, sizeof( cl_user_event_t ));
|
|
|
|
ev = clgame.events[lastnum];
|
|
|
|
// NOTE: ev->index will be set later
|
|
Q_strncpy( ev->name, szEvName, MAX_QPATH );
|
|
ev->func = func;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_FireEvent
|
|
|
|
=============
|
|
*/
|
|
qboolean CL_FireEvent( event_info_t *ei, int slot )
|
|
{
|
|
cl_user_event_t *ev;
|
|
const char *name;
|
|
int i, idx;
|
|
|
|
if( !ei || !ei->index )
|
|
return false;
|
|
|
|
// get the func pointer
|
|
for( i = 0; i < MAX_EVENTS; i++ )
|
|
{
|
|
ev = clgame.events[i];
|
|
|
|
if( !ev )
|
|
{
|
|
idx = bound( 1, ei->index, ( MAX_EVENTS - 1 ));
|
|
Con_Reportf( S_ERROR "CL_FireEvent: %s not precached\n", cl.event_precache[idx] );
|
|
break;
|
|
}
|
|
|
|
if( ev->index == ei->index )
|
|
{
|
|
if( ev->func )
|
|
{
|
|
CL_DescribeEvent( slot, ei->flags, cl.event_precache[ei->index] );
|
|
ev->func( &ei->args );
|
|
return true;
|
|
}
|
|
|
|
name = cl.event_precache[ei->index];
|
|
Con_Reportf( S_ERROR "CL_FireEvent: %s not hooked\n", name );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_FireEvents
|
|
|
|
called right before draw frame
|
|
=============
|
|
*/
|
|
void CL_FireEvents( void )
|
|
{
|
|
event_state_t *es;
|
|
event_info_t *ei;
|
|
int i;
|
|
|
|
es = &cl.events;
|
|
|
|
for( i = 0; i < MAX_EVENT_QUEUE; i++ )
|
|
{
|
|
ei = &es->ei[i];
|
|
|
|
if( ei->index == 0 )
|
|
continue;
|
|
|
|
// delayed event!
|
|
if( ei->fire_time && ( ei->fire_time > cl.time ))
|
|
continue;
|
|
|
|
CL_FireEvent( ei, i );
|
|
|
|
// zero out the remaining fields
|
|
CL_ResetEvent( ei );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_FindEvent
|
|
|
|
find first empty event
|
|
=============
|
|
*/
|
|
event_info_t *CL_FindEmptyEvent( void )
|
|
{
|
|
int i;
|
|
event_state_t *es;
|
|
event_info_t *ei;
|
|
|
|
es = &cl.events;
|
|
|
|
// look for first slot where index is != 0
|
|
for( i = 0; i < MAX_EVENT_QUEUE; i++ )
|
|
{
|
|
ei = &es->ei[i];
|
|
if( ei->index != 0 )
|
|
continue;
|
|
return ei;
|
|
}
|
|
|
|
// no slots available
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_FindEvent
|
|
|
|
replace only unreliable events
|
|
=============
|
|
*/
|
|
event_info_t *CL_FindUnreliableEvent( void )
|
|
{
|
|
event_state_t *es;
|
|
event_info_t *ei;
|
|
int i;
|
|
|
|
es = &cl.events;
|
|
|
|
for ( i = 0; i < MAX_EVENT_QUEUE; i++ )
|
|
{
|
|
ei = &es->ei[i];
|
|
if( ei->index != 0 )
|
|
{
|
|
// it's reliable, so skip it
|
|
if( FBitSet( ei->flags, FEV_RELIABLE ))
|
|
continue;
|
|
}
|
|
return ei;
|
|
}
|
|
|
|
// this should never happen
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_QueueEvent
|
|
|
|
=============
|
|
*/
|
|
void CL_QueueEvent( int flags, int index, float delay, event_args_t *args )
|
|
{
|
|
event_info_t *ei;
|
|
|
|
// find a normal slot
|
|
ei = CL_FindEmptyEvent();
|
|
|
|
if( !ei )
|
|
{
|
|
if( FBitSet( flags, FEV_RELIABLE ))
|
|
{
|
|
ei = CL_FindUnreliableEvent();
|
|
}
|
|
|
|
if( !ei ) return;
|
|
}
|
|
|
|
ei->index = index;
|
|
ei->packet_index = 0;
|
|
ei->fire_time = delay ? (cl.time + delay) : 0.0f;
|
|
ei->flags = flags;
|
|
ei->args = *args;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_ParseReliableEvent
|
|
|
|
=============
|
|
*/
|
|
void CL_ParseReliableEvent( sizebuf_t *msg )
|
|
{
|
|
int event_index;
|
|
event_args_t nullargs, args;
|
|
float delay = 0.0f;
|
|
|
|
memset( &nullargs, 0, sizeof( nullargs ));
|
|
|
|
event_index = MSG_ReadUBitLong( msg, MAX_EVENT_BITS );
|
|
|
|
if( MSG_ReadOneBit( msg ))
|
|
delay = (float)MSG_ReadWord( msg ) * (1.0f / 100.0f);
|
|
|
|
// reliable events not use delta-compression just null-compression
|
|
MSG_ReadDeltaEvent( msg, &nullargs, &args );
|
|
|
|
if( args.entindex > 0 && args.entindex <= cl.maxclients )
|
|
args.angles[PITCH] *= -3.0f;
|
|
|
|
CL_QueueEvent( FEV_RELIABLE|FEV_SERVER, event_index, delay, &args );
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
CL_ParseEvent
|
|
|
|
=============
|
|
*/
|
|
void CL_ParseEvent( sizebuf_t *msg )
|
|
{
|
|
int event_index;
|
|
int i, num_events;
|
|
int packet_index;
|
|
event_args_t nullargs, args;
|
|
entity_state_t *state;
|
|
float delay;
|
|
|
|
memset( &nullargs, 0, sizeof( nullargs ));
|
|
memset( &args, 0, sizeof( args ));
|
|
|
|
num_events = MSG_ReadUBitLong( msg, 5 );
|
|
|
|
// parse events queue
|
|
for( i = 0 ; i < num_events; i++ )
|
|
{
|
|
event_index = MSG_ReadUBitLong( msg, MAX_EVENT_BITS );
|
|
|
|
if( MSG_ReadOneBit( msg ))
|
|
packet_index = MSG_ReadUBitLong( msg, cls.legacymode ? MAX_LEGACY_ENTITY_BITS : MAX_ENTITY_BITS );
|
|
else packet_index = -1;
|
|
|
|
if( MSG_ReadOneBit( msg ))
|
|
{
|
|
MSG_ReadDeltaEvent( msg, &nullargs, &args );
|
|
}
|
|
|
|
if( MSG_ReadOneBit( msg ))
|
|
delay = (float)MSG_ReadWord( msg ) * (1.0f / 100.0f);
|
|
else delay = 0.0f;
|
|
|
|
if( packet_index != -1 )
|
|
{
|
|
frame_t *frame = &cl.frames[cl.parsecountmod];
|
|
|
|
if( packet_index < frame->num_entities )
|
|
{
|
|
state = &cls.packet_entities[(frame->first_entity+packet_index)%cls.num_client_entities];
|
|
args.entindex = state->number;
|
|
|
|
if( VectorIsNull( args.origin ))
|
|
VectorCopy( state->origin, args.origin );
|
|
|
|
if( VectorIsNull( args.angles ))
|
|
VectorCopy( state->angles, args.angles );
|
|
|
|
COM_NormalizeAngles( args.angles );
|
|
|
|
if( state->number > 0 && state->number <= cl.maxclients )
|
|
{
|
|
args.angles[PITCH] *= -3.0f;
|
|
CL_CalcPlayerVelocity( state->number, args.velocity );
|
|
args.ducking = ( state->usehull == 1 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( args.entindex != 0 )
|
|
{
|
|
if( args.entindex > 0 && args.entindex <= cl.maxclients )
|
|
args.angles[PITCH] /= -3.0f;
|
|
}
|
|
}
|
|
|
|
// Place event on queue
|
|
CL_QueueEvent( FEV_SERVER, event_index, delay, &args );
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CL_PlaybackEvent
|
|
|
|
=============
|
|
*/
|
|
void GAME_EXPORT CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin,
|
|
float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 )
|
|
{
|
|
event_args_t args;
|
|
|
|
if( FBitSet( flags, FEV_SERVER ))
|
|
return;
|
|
|
|
// first check event for out of bounds
|
|
if( eventindex < 1 || eventindex > MAX_EVENTS )
|
|
{
|
|
Con_DPrintf( S_ERROR "CL_PlaybackEvent: invalid eventindex %i\n", eventindex );
|
|
return;
|
|
}
|
|
|
|
// check event for precached
|
|
if( !CL_EventIndex( cl.event_precache[eventindex] ))
|
|
{
|
|
Con_DPrintf( S_ERROR "CL_PlaybackEvent: event %i was not precached\n", eventindex );
|
|
return;
|
|
}
|
|
|
|
SetBits( flags, FEV_CLIENT ); // it's a client event
|
|
ClearBits( flags, FEV_NOTHOST|FEV_HOSTONLY|FEV_GLOBAL );
|
|
if( delay < 0.0f ) delay = 0.0f; // fixup negative delays
|
|
|
|
memset( &args, 0, sizeof( args ));
|
|
|
|
VectorCopy( origin, args.origin );
|
|
VectorCopy( angles, args.angles );
|
|
VectorCopy( cl.simvel, args.velocity );
|
|
args.entindex = cl.playernum + 1;
|
|
args.ducking = ( cl.local.usehull == 1 );
|
|
|
|
args.fparam1 = fparam1;
|
|
args.fparam2 = fparam2;
|
|
args.iparam1 = iparam1;
|
|
args.iparam2 = iparam2;
|
|
args.bparam1 = bparam1;
|
|
args.bparam2 = bparam2;
|
|
|
|
CL_QueueEvent( flags, eventindex, delay, &args );
|
|
}
|