@ -48,6 +48,8 @@ extern void CopyToBodyQue( entvars_t* pev );
@@ -48,6 +48,8 @@ extern void CopyToBodyQue( entvars_t* pev );
extern int giPrecacheGrunt ;
extern int gmsgSayText ;
extern cvar_t allow_spectators ;
extern int g_teamplay ;
void LinkUserMessages ( void ) ;
@ -204,6 +206,97 @@ void ClientPutInServer( edict_t *pEntity )
@@ -204,6 +206,97 @@ void ClientPutInServer( edict_t *pEntity )
# include "voice_gamemgr.h"
extern CVoiceGameMgr g_VoiceGameMgr ;
# endif
//-----------------------------------------------------------------------------
// Purpose: determine if a uchar32 represents a valid Unicode code point
//-----------------------------------------------------------------------------
bool Q_IsValidUChar32 ( unsigned int uVal )
{
// Values > 0x10FFFF are explicitly invalid; ditto for UTF-16 surrogate halves,
// values ending in FFFE or FFFF, or values in the 0x00FDD0-0x00FDEF reserved range
return ( uVal < 0x110000u ) & & ( ( uVal - 0x00D800u ) > 0x7FFu ) & & ( ( uVal & 0xFFFFu ) < 0xFFFEu ) & & ( ( uVal - 0x00FDD0u ) > 0x1Fu ) ;
}
// Decode one character from a UTF-8 encoded string. Treats 6-byte CESU-8 sequences
// as a single character, as if they were a correctly-encoded 4-byte UTF-8 sequence.
int Q_UTF8ToUChar32 ( const char * pUTF8_ , unsigned int & uValueOut , bool & bErrorOut )
{
const unsigned char * pUTF8 = ( const unsigned char * ) pUTF8_ ;
int nBytes = 1 ;
unsigned int uValue = pUTF8 [ 0 ] ;
unsigned int uMinValue = 0 ;
// 0....... single byte
if ( uValue < 0x80 )
goto decodeFinishedNoCheck ;
// Expecting at least a two-byte sequence with 0xC0 <= first <= 0xF7 (110...... and 11110...)
if ( ( uValue - 0xC0u ) > 0x37u | | ( pUTF8 [ 1 ] & 0xC0 ) ! = 0x80 )
goto decodeError ;
uValue = ( uValue < < 6 ) - ( 0xC0 < < 6 ) + pUTF8 [ 1 ] - 0x80 ;
nBytes = 2 ;
uMinValue = 0x80 ;
// 110..... two-byte lead byte
if ( ! ( uValue & ( 0x20 < < 6 ) ) )
goto decodeFinished ;
// Expecting at least a three-byte sequence
if ( ( pUTF8 [ 2 ] & 0xC0 ) ! = 0x80 )
goto decodeError ;
uValue = ( uValue < < 6 ) - ( 0x20 < < 12 ) + pUTF8 [ 2 ] - 0x80 ;
nBytes = 3 ;
uMinValue = 0x800 ;
// 1110.... three-byte lead byte
decodeFinished :
if ( uValue > = uMinValue & & Q_IsValidUChar32 ( uValue ) )
{
decodeFinishedNoCheck :
uValueOut = uValue ;
bErrorOut = false ;
return nBytes ;
}
decodeError :
uValueOut = ' ? ' ;
bErrorOut = true ;
return nBytes ;
decodeFinishedMaybeCESU8 :
// Do we have a full UTF-16 surrogate pair that's been UTF-8 encoded afterwards?
// That is, do we have 0xD800-0xDBFF followed by 0xDC00-0xDFFF? If so, decode it all.
if ( ( uValue - 0xD800u ) < 0x400u & & pUTF8 [ 3 ] = = 0xED & & ( unsigned char ) ( pUTF8 [ 4 ] - 0xB0 ) < 0x10 & & ( pUTF8 [ 5 ] & 0xC0 ) = = 0x80 )
{
uValue = 0x10000 + ( ( uValue - 0xD800u ) < < 10 ) + ( ( unsigned char ) ( pUTF8 [ 4 ] - 0xB0 ) < < 6 ) + pUTF8 [ 5 ] - 0x80 ;
nBytes = 6 ;
uMinValue = 0x10000 ;
}
goto decodeFinished ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if UTF-8 string contains invalid sequences.
//-----------------------------------------------------------------------------
bool Q_UnicodeValidate ( const char * pUTF8 )
{
bool bError = false ;
while ( * pUTF8 )
{
unsigned int uVal ;
// Our UTF-8 decoder silently fixes up 6-byte CESU-8 (improperly re-encoded UTF-16) sequences.
// However, these are technically not valid UTF-8. So if we eat 6 bytes at once, it's an error.
int nCharSize = Q_UTF8ToUChar32 ( pUTF8 , uVal , bError ) ;
if ( bError | | nCharSize = = 6 )
return false ;
pUTF8 + = nCharSize ;
}
return true ;
}
//// HOST_SAY
// String comes in as
// say blah blah blah
@ -265,20 +358,13 @@ void Host_Say( edict_t *pEntity, int teamonly )
@@ -265,20 +358,13 @@ void Host_Say( edict_t *pEntity, int teamonly )
p [ strlen ( p ) - 1 ] = 0 ;
}
// make sure the text has content
for ( pc = p ; pc ! = NULL & & * pc ! = 0 ; pc + + )
{
if ( ! isspace ( * pc ) )
{
pc = NULL ; // we've found an alphanumeric character, so text is valid
break ;
}
}
if ( pc ! = NULL )
if ( ! p | | ! p [ 0 ] | | ! Q_UnicodeValidate ( p ) )
return ; // no character found, so say nothing
// turn on color set 2 (color on, no sound)
if ( teamonly )
if ( player - > IsObserver ( ) & & ( teamonly ) )
sprintf ( text , " %c(SPEC) %s: " , 2 , STRING ( pEntity - > v . netname ) ) ;
else if ( teamonly )
sprintf ( text , " %c(TEAM) %s: " , 2 , STRING ( pEntity - > v . netname ) ) ;
else
sprintf ( text , " %c%s: " , 2 , STRING ( pEntity - > v . netname ) ) ;
@ -313,9 +399,14 @@ void Host_Say( edict_t *pEntity, int teamonly )
@@ -313,9 +399,14 @@ void Host_Say( edict_t *pEntity, int teamonly )
if ( g_VoiceGameMgr . PlayerHasBlockedPlayer ( client , player ) )
continue ;
# endif
if ( teamonly & & g_pGameRules - > PlayerRelationship ( client , CBaseEntity : : Instance ( pEntity ) ) ! = GR_TEAMMATE )
if ( ! player - > IsObserver ( ) & & teamonly & & g_pGameRules - > PlayerRelationship ( client , CBaseEntity : : Instance ( pEntity ) ) ! = GR_TEAMMATE )
continue ;
// Spectators can only talk to other specs
if ( player - > IsObserver ( ) & & teamonly )
if ( ! client - > IsObserver ( ) )
continue ;
MESSAGE_BEGIN ( MSG_ONE , gmsgSayText , NULL , client - > pev ) ;
WRITE_BYTE ( ENTINDEX ( pEntity ) ) ;
WRITE_STRING ( text ) ;
@ -459,12 +550,40 @@ void ClientCommand( edict_t *pEntity )
@@ -459,12 +550,40 @@ void ClientCommand( edict_t *pEntity )
{
GetClassPtr ( ( CBasePlayer * ) pev ) - > SelectLastItem ( ) ;
}
else if ( FStrEq ( pcmd , " spectate " ) & & ( pev - > flags & FL_PROXY ) ) // added for proxy support
else if ( FStrEq ( pcmd , " spectate " ) // clients wants to become a spectator
{
CBasePlayer * pPlayer = GetClassPtr ( ( CBasePlayer * ) pev ) ;
// always allow proxies to become a spectator
if ( ( pev - > flags & FL_PROXY ) | | allow_spectators . value )
{
CBasePlayer * pPlayer = GetClassPtr ( ( CBasePlayer * ) pev ) ;
edict_t * pentSpawnSpot = g_pGameRules - > GetPlayerSpawnSpot ( pPlayer ) ;
pPlayer - > StartObserver ( pev - > origin , VARS ( pentSpawnSpot ) - > angles ) ;
// notify other clients of player switching to spectator mode
UTIL_ClientPrintAll ( HUD_PRINTNOTIFY , UTIL_VarArgs ( " %s switched to spectator mode \n " ,
( pev - > netname & & STRING ( pev - > netname ) [ 0 ] ! = 0 ) ? STRING ( pev - > netname ) : " unconnected " ) ) ;
}
else
ClientPrint ( pev , HUD_PRINTCONSOLE , " Spectator mode is disabled. \n " ) ;
}
else if ( FStrEq ( pcmd , " specmode " ) ) // new spectator mode
{
CBasePlayer * pPlayer = GetClassPtr ( ( CBasePlayer * ) pev ) ;
edict_t * pentSpawnSpot = g_pGameRules - > GetPlayerSpawnSpot ( pPlayer ) ;
pPlayer - > StartObserver ( pev - > origin , VARS ( pentSpawnSpot ) - > angles ) ;
if ( pPlayer - > IsObserver ( ) )
pPlayer - > Observer_SetMode ( atoi ( CMD_ARGV ( 1 ) ) ) ;
}
else if ( FStrEq ( pcmd , " closemenus " ) )
{
// just ignore it
}
else if ( FStrEq ( pcmd , " follownext " ) ) // follow next player
{
CBasePlayer * pPlayer = GetClassPtr ( ( CBasePlayer * ) pev ) ;
if ( pPlayer - > IsObserver ( ) )
pPlayer - > Observer_FindNextPlayer ( atoi ( CMD_ARGV ( 1 ) ) ? true : false ) ;
}
else if ( g_pGameRules - > ClientCommand ( GetClassPtr ( ( CBasePlayer * ) pev ) , pcmd ) )
{
@ -524,12 +643,15 @@ void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer )
@@ -524,12 +643,15 @@ void ClientUserInfoChanged( edict_t *pEntity, char *infobuffer )
// Set the name
g_engfuncs . pfnSetClientKeyValue ( ENTINDEX ( pEntity ) , infobuffer , " name " , sName ) ;
char text [ 256 ] ;
snprintf ( text , 256 , " * %s changed name to %s \n " , STRING ( pEntity - > v . netname ) , g_engfuncs . pfnInfoKeyValue ( infobuffer , " name " ) ) ;
MESSAGE_BEGIN ( MSG_ALL , gmsgSayText , NULL ) ;
WRITE_BYTE ( ENTINDEX ( pEntity ) ) ;
WRITE_STRING ( text ) ;
MESSAGE_END ( ) ;
if ( gpGlobals - > maxClients > 1 )
{
char text [ 256 ] ;
snprintf ( text , 256 , " * %s changed name to %s \n " , STRING ( pEntity - > v . netname ) , g_engfuncs . pfnInfoKeyValue ( infobuffer , " name " ) ) ;
MESSAGE_BEGIN ( MSG_ALL , gmsgSayText , NULL ) ;
WRITE_BYTE ( ENTINDEX ( pEntity ) ) ;
WRITE_STRING ( text ) ;
MESSAGE_END ( ) ;
}
// team match?
if ( g_teamplay )
@ -983,7 +1105,7 @@ int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *h
@@ -983,7 +1105,7 @@ int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *h
int i ;
// don't send if flagged for NODRAW and it's not the host getting the message
if ( ( ent - > v . effects = = EF_NODRAW ) & & ( ent ! = host ) )
if ( ( ent - > v . effects & EF_NODRAW ) & & ( ent ! = host ) )
return 0 ;
// Ignore ents without valid / visible models
@ -1553,41 +1675,67 @@ engine sets cd to 0 before calling.
@@ -1553,41 +1675,67 @@ engine sets cd to 0 before calling.
*/
void UpdateClientData ( const struct edict_s * ent , int sendweapons , struct clientdata_s * cd )
{
cd - > flags = ent - > v . flags ;
cd - > health = ent - > v . health ;
if ( ! ent | | ! ent - > pvPrivateData )
return ;
entvars_t * pev = ( entvars_t * ) & ent - > v ;
CBasePlayer * pl = ( CBasePlayer * ) ( CBasePlayer : : Instance ( pev ) ) ;
entvars_t * pevOrg = NULL ;
// if user is spectating different player in First person, override some vars
if ( pl & & pl - > pev - > iuser1 = = OBS_IN_EYE )
{
if ( pl - > m_hObserverTarget )
{
pevOrg = pev ;
pev = pl - > m_hObserverTarget - > pev ;
pl = ( CBasePlayer * ) ( CBasePlayer : : Instance ( pev ) ) ;
}
}
cd - > viewmodel = MODEL_INDEX ( STRING ( ent - > v . viewmodel ) ) ;
cd - > flags = pev - > flags ;
cd - > health = pev - > health ;
cd - > waterlevel = ent - > v . waterlevel ;
cd - > watertype = ent - > v . watertype ;
cd - > weapons = ent - > v . weapons ;
cd - > viewmodel = MODEL_INDEX ( STRING ( pev - > viewmodel ) ) ;
cd - > waterlevel = pev - > waterlevel ;
cd - > watertype = pev - > watertype ;
cd - > weapons = pev - > weapons ;
// Vectors
cd - > origin = ent - > v . origin ;
cd - > velocity = ent - > v . velocity ;
cd - > view_ofs = ent - > v . view_ofs ;
cd - > punchangle = ent - > v . punchangle ;
cd - > origin = pev - > origin ;
cd - > velocity = pev - > velocity ;
cd - > view_ofs = pev - > view_ofs ;
cd - > punchangle = pev - > punchangle ;
cd - > bInDuck = ent - > v . bInDuck ;
cd - > flTimeStepSound = ent - > v . flTimeStepSound ;
cd - > flDuckTime = ent - > v . flDuckTime ;
cd - > flSwimTime = ent - > v . flSwimTime ;
cd - > waterjumptime = ent - > v . teleport_time ;
cd - > bInDuck = pev - > bInDuck ;
cd - > flTimeStepSound = pev - > flTimeStepSound ;
cd - > flDuckTime = pev - > flDuckTime ;
cd - > flSwimTime = pev - > flSwimTime ;
cd - > waterjumptime = pev - > teleport_time ;
strcpy ( cd - > physinfo , ENGINE_GETPHYSINFO ( ent ) ) ;
cd - > maxspeed = ent - > v . maxspeed ;
cd - > fov = ent - > v . fov ;
cd - > weaponanim = ent - > v . weaponanim ;
cd - > maxspeed = pev - > maxspeed ;
cd - > fov = pev - > fov ;
cd - > weaponanim = pev - > weaponanim ;
cd - > pushmsec = ent - > v . pushmsec ;
cd - > pushmsec = pev - > pushmsec ;
// Spectator mode
if ( pevOrg ! = NULL )
{
// don't use spec vars from chased player
cd - > iuser1 = pevOrg - > iuser1 ;
cd - > iuser2 = pevOrg - > iuser2 ;
}
else
{
cd - > iuser1 = pev - > iuser1 ;
cd - > iuser2 = pev - > iuser2 ;
}
# if defined( CLIENT_WEAPONS )
if ( sendweapons )
{
entvars_t * pev = ( entvars_t * ) & ent - > v ;
CBasePlayer * pl = ( CBasePlayer * ) CBasePlayer : : Instance ( pev ) ;
if ( pl )
{
cd - > m_flNextAttack = pl - > m_flNextAttack ;