Legacy protocol compatibility (cl_legacymode)

This commit is contained in:
mittorn 2019-01-25 20:53:08 +07:00
parent 1e5f12a0ae
commit 6a02a571c1
10 changed files with 908 additions and 47 deletions

View File

@ -393,7 +393,7 @@ void CL_ParseEvent( sizebuf_t *msg )
event_index = MSG_ReadUBitLong( msg, MAX_EVENT_BITS );
if( MSG_ReadOneBit( msg ))
packet_index = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS );
packet_index = MSG_ReadUBitLong( msg, cls.legacymode?MAX_LEGACY_ENTITY_BITS:MAX_ENTITY_BITS );
else packet_index = -1;
if( MSG_ReadOneBit( msg ))
@ -492,4 +492,4 @@ void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, floa
args.bparam2 = bparam2;
CL_QueueEvent( flags, eventindex, delay, &args );
}
}

View File

@ -721,7 +721,7 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
CL_WriteDemoJumpTime();
// sentinel count. save it for debug checking
count = ( MSG_ReadUBitLong( msg, MAX_VISIBLE_PACKET_BITS ) + 1 );
count = cls.legacymode?MSG_ReadWord( msg ) : ( MSG_ReadUBitLong( msg, MAX_VISIBLE_PACKET_BITS ) + 1 );
newframe = &cl.frames[cl.parsecountmod];
// allocate parse entities
@ -795,9 +795,8 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta )
while( 1 )
{
newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS );
if( newnum == LAST_EDICT ) break; // end of packet entities
newnum = cls.legacymode ? MSG_ReadWord( msg ) : MSG_ReadUBitLong( msg, MAX_ENTITY_BITS );
if( newnum == (cls.legacymode?0:LAST_EDICT) ) break; // end of packet entities
if( MSG_CheckOverflow( msg ))
Host_Error( "CL_ParsePacketEntities: overflow\n" );
player = CL_IsPlayerIndex( newnum );

View File

@ -69,6 +69,7 @@ convar_t *cl_lw;
convar_t *cl_charset;
convar_t *cl_trace_messages;
convar_t *hud_utf8;
convar_t *cl_legacymode;
//
// userinfo
@ -1015,7 +1016,11 @@ void CL_SendConnectPacket( void )
Info_SetValueForKey( protinfo, "uuid", key, sizeof( protinfo ));
Info_SetValueForKey( protinfo, "qport", qport, sizeof( protinfo ));
Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo );
/// TODO: identification for legacy mode
if( cls.legacymode )
Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", 48, Q_atoi(qport), cls.challenge, cls.userinfo );
else
Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i \"%s\" \"%s\"\n", PROTOCOL_VERSION, cls.challenge, protinfo, cls.userinfo );
cls.timestart = Sys_DoubleTime();
}
@ -1098,9 +1103,10 @@ void CL_CheckForResend( void )
Con_Printf( "Connecting to %s... [retry #%i]\n", cls.servername, cls.connect_retry );
if( cl_test_bandwidth.value )
if( !cls.legacymode && cl_test_bandwidth.value )
Netchan_OutOfBandPrint( NS_CLIENT, adr, "bandwidth %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size );
else Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" );
else
Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" );
}
resource_t *CL_AddResource( resourcetype_t type, const char *name, int size, qboolean bFatalIfMissing, int index )
@ -1371,6 +1377,8 @@ This is also called on Host_Error, so it shouldn't cause any errors
*/
void CL_Disconnect( void )
{
cls.legacymode = cl_legacymode->value;
if( cls.state == ca_disconnected )
return;
@ -1437,12 +1445,15 @@ void CL_LocalServers_f( void )
Con_Printf( "Scanning for servers on the local network area...\n" );
NET_Config( true ); // allow remote
if( cls.state == ca_disconnected )
cls.legacymode = cl_legacymode->value;
// send a broadcast packet
adr.type = NA_BROADCAST;
adr.port = MSG_BigShort( PORT_SERVER );
Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION );
Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", cls.legacymode?PROTOCOL_LEGACY_VERSION:PROTOCOL_VERSION );
}
#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0"
@ -1460,6 +1471,9 @@ void CL_InternetServers_f( void )
NET_Config( true ); // allow remote
if( cls.state == ca_disconnected )
cls.legacymode = cl_legacymode->value;
Con_Printf( "Scanning for servers on the internet area...\n" );
Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining );
Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version
@ -1935,7 +1949,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
else if( clgame.request_type == NET_REQUEST_GAMEUI )
{
NET_Config( true ); // allow remote
Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION );
Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", cls.legacymode?PROTOCOL_LEGACY_VERSION:PROTOCOL_VERSION );
}
}
@ -2016,6 +2030,7 @@ void CL_ReadNetMessage( void )
// run special handler for quake demos
if( cls.demoplayback == DEMO_QUAKE1 )
CL_ParseQuakeMessage( &net_message, true );
else if( cls.legacymode ) CL_ParseLegacyServerMessage( &net_message, true );
else CL_ParseServerMessage( &net_message, true );
cl.send_reply = true;
}
@ -2617,6 +2632,7 @@ void CL_InitLocal( void )
hud_scale = Cvar_Get( "hud_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud at current resolution" );
Cvar_Get( "cl_background", "0", FCVAR_READ_ONLY, "indicate what background map is running" );
cl_showevents = Cvar_Get( "cl_showevents", "0", FCVAR_ARCHIVE, "show events playback" );
cl_legacymode = Cvar_Get( "cl_legacymode", "0", 0, "legacy mode compatibility" );
Cvar_Get( "lastdemo", "", FCVAR_ARCHIVE, "last played demo" );
// these two added to shut up CS 1.5 about 'unknown' commands
@ -2676,6 +2692,8 @@ void CL_InitLocal( void )
Cmd_AddCommand ("reconnect", CL_Reconnect_f, "reconnect to current level" );
Cmd_AddCommand ("rcon", CL_Rcon_f, "sends a command to the server console (rcon_password and rcon_address required)" );
Cmd_AddCommand ("precache", CL_LegacyPrecache_f, "legacy server compatibility" );
}
//============================================================================

View File

@ -353,6 +353,7 @@ void CL_ParseStaticEntity( sizebuf_t *msg )
R_AddEfrags( ent ); // add link
}
/*
==================
CL_WeaponAnim
@ -1127,7 +1128,7 @@ void CL_ParseClientData( sizebuf_t *msg )
if( !MSG_ReadOneBit( msg )) break;
// read the weapon idx
idx = MSG_ReadUBitLong( msg, MAX_WEAPON_BITS );
idx = MSG_ReadUBitLong( msg, cls.legacymode?MAX_LEGACY_WEAPON_BITS:MAX_WEAPON_BITS );
MSG_ReadWeaponData( msg, &from_wd[idx], &to_wd[idx], cl.mtime[0] );
}
@ -1297,16 +1298,40 @@ void CL_RegisterUserMessage( sizebuf_t *msg )
int svc_num, size;
svc_num = MSG_ReadByte( msg );
size = MSG_ReadWord( msg );
size = cls.legacymode?MSG_ReadByte( msg ):MSG_ReadWord( msg );
pszName = MSG_ReadString( msg );
// important stuff
if( size == 0xFFFF ) size = -1;
if( size == (cls.legacymode?0xFF:0xFFFF) ) size = -1;
svc_num = bound( 0, svc_num, 255 );
CL_LinkUserMessage( pszName, svc_num, size );
}
/*
================
CL_RegisterUserMessage
register new user message or update existing
================
*/
/*
void CL_LegacyRegisterUserMessage( sizebuf_t *msg )
{
char *pszName;
int svc_num, size;
svc_num = MSG_ReadByte( msg );
size = MSG_ReadByte( msg );
pszName = MSG_ReadString( msg );
// important stuff
if( size == 0xFF ) size = -1;
svc_num = bound( 0, svc_num, 255 );
CL_LinkUserMessage( pszName, svc_num, size );
}
*/
/*
================
CL_UpdateUserinfo
@ -1933,7 +1958,12 @@ void CL_ParseUserMessage( sizebuf_t *msg, int svc_num )
iSize = clgame.msg[i].size;
// message with variable sizes receive an actual size as first byte
if( iSize == -1 ) iSize = MSG_ReadWord( msg );
if( iSize == -1 ) iSize = cls.legacymode?MSG_ReadByte( msg ):MSG_ReadWord( msg );
if( iSize >= MAX_USERMSG_LENGTH )
{
Msg("WTF??? %d %d\n", i, svc_num );
return;
}
// parse user message into buffer
MSG_ReadBytes( msg, pbuf, iSize );
@ -2271,3 +2301,778 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message )
}
}
}
/*
==================
CL_ParseBaseline
==================
*/
void CL_LegacyParseBaseline( sizebuf_t *msg )
{
int i, newnum;
entity_state_t nullstate;
qboolean player;
cl_entity_t *ent;
Delta_InitClient (); // finalize client delta's
memset( &nullstate, 0, sizeof( nullstate ));
newnum = MSG_ReadWord( msg );
player = CL_IsPlayerIndex( newnum );
if( newnum >= clgame.maxEntities )
Host_Error( "CL_AllocEdict: no free edicts\n" );
ent = CL_EDICT_NUM( newnum );
memset( &ent->prevstate, 0, sizeof( ent->prevstate ));
ent->index = newnum;
MSG_ReadDeltaEntity( msg, &ent->prevstate, &ent->baseline, newnum, player, 1.0f );
}
/*
==================
CL_ParseServerData
==================
*/
void CL_ParseLegacyServerData( sizebuf_t *msg )
{
string gamefolder;
qboolean background;
int i;
Con_Reportf( "Legacy serverdata packet received.\n" );
cls.timestart = Sys_DoubleTime();
cls.demowaiting = false; // server is changed
//clgame.load_sequence++; // now all hud sprites are invalid
// wipe the client_t struct
if( !cls.changelevel && !cls.changedemo )
CL_ClearState ();
cls.state = ca_connected;
// parse protocol version number
i = MSG_ReadLong( msg );
//cls.serverProtocol = i;
if( i != 48 )
Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION );
cl.servercount = MSG_ReadLong( msg );
cl.checksum = MSG_ReadLong( msg );
cl.playernum = MSG_ReadByte( msg );
cl.maxclients = MSG_ReadByte( msg );
clgame.maxEntities = MSG_ReadWord( msg );
clgame.maxEntities = bound( 600, clgame.maxEntities, 4096 );
clgame.maxModels = 512;
Q_strncpy( clgame.mapname, MSG_ReadString( msg ), MAX_STRING );
Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING );
background = MSG_ReadOneBit( msg );
Q_strncpy( gamefolder, MSG_ReadString( msg ), MAX_STRING );
host.features = (uint)MSG_ReadLong( msg );
// Re-init hud video, especially if we changed game directories
clgame.dllFuncs.pfnVidInit();
if( Con_FixedFont( ))
{
// seperate the printfs so the server message can have a color
Con_Print( "\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" );
Con_Print( va( "%c%s\n\n", 2, clgame.maptitle ));
}
// multiplayer game?
if( cl.maxclients > 1 )
{
// allow console in multiplayer games
host.allow_console = true;
// loading user settings
CSCR_LoadDefaultCVars( "user.scr" );
if( r_decals->value > mp_decals.value )
Cvar_SetValue( "r_decals", mp_decals.value );
}
else Cvar_Reset( "r_decals" );
// set the background state
if( cls.demoplayback && ( cls.demonum != -1 ))
{
// re-init mouse
host.mouse_visible = false;
cl.background = true;
}
else cl.background = background;
if( cl.background ) // tell the game parts about background state
Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY );
else Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY );
if( !cls.changelevel )
{
// continue playing if we are changing level
S_StopBackgroundTrack ();
}
if( !cls.changedemo )
UI_SetActiveMenu( cl.background );
else if( !cls.demoplayback )
Key_SetKeyDest( key_menu );
// don't reset cursor in background mode
if( cl.background )
IN_MouseRestorePos();
// will be changed later
cl.viewentity = cl.playernum + 1;
gameui.globals->maxClients = cl.maxclients;
Q_strncpy( gameui.globals->maptitle, clgame.maptitle, sizeof( gameui.globals->maptitle ));
if( !cls.changelevel && !cls.changedemo )
CL_InitEdicts (); // re-arrange edicts
// get splash name
if( cls.demoplayback && ( cls.demonum != -1 ))
Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", cls.demoname, glState.wideScreen ? "16x9" : "4x3" ));
else Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", clgame.mapname, glState.wideScreen ? "16x9" : "4x3" ));
Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar
if(( cl_allow_levelshots->value && !cls.changelevel ) || cl.background )
{
if( !FS_FileExists( va( "%s.bmp", cl_levelshot_name->string ), true ))
Cvar_Set( "cl_levelshot_name", "*black" ); // render a black screen
cls.scrshot_request = scrshot_plaque; // request levelshot even if exist (check filetime)
}
for( i = 0; i < MAX_CLIENTS; i++ )
COM_ClearCustomizationList( &cl.players[i].customdata, true );
CL_CreateCustomizationList();
memset( &clgame.movevars, 0, sizeof( clgame.movevars ));
memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars ));
memset( &clgame.centerPrint, 0, sizeof( clgame.centerPrint ));
cl.video_prepped = false;
cl.audio_prepped = false;
}
/*
==================
CL_ParseStaticEntity
static client entity
==================
*/
void CL_LegacyParseStaticEntity( sizebuf_t *msg )
{
int i;
entity_state_t state;
cl_entity_t *ent;
memset( &state, 0, sizeof( state ));
state.modelindex = MSG_ReadShort( msg );
state.sequence = MSG_ReadByte( msg );
state.frame = MSG_ReadByte( msg );
state.colormap = MSG_ReadWord( msg );
state.skin = MSG_ReadByte( msg );
for( i = 0; i < 3; i++ )
{
state.origin[i] = MSG_ReadCoord( msg );
state.angles[i] = MSG_ReadBitAngle( msg, 16 );
}
state.rendermode = MSG_ReadByte( msg );
if( state.rendermode != kRenderNormal )
{
state.renderamt = MSG_ReadByte( msg );
state.rendercolor.r = MSG_ReadByte( msg );
state.rendercolor.g = MSG_ReadByte( msg );
state.rendercolor.b = MSG_ReadByte( msg );
state.renderfx = MSG_ReadByte( msg );
}
i = clgame.numStatics;
if( i >= MAX_STATIC_ENTITIES )
{
Con_Printf( S_ERROR "MAX_STATIC_ENTITIES limit exceeded!\n" );
return;
}
ent = &clgame.static_entities[i];
clgame.numStatics++;
// all states are same
ent->baseline = ent->curstate = ent->prevstate = state;
ent->index = 0; // static entities doesn't has the numbers
// statics may be respawned in game e.g. for demo recording
if( cls.state == ca_connected || cls.state == ca_validate )
ent->trivial_accept = INVALID_HANDLE;
// setup the new static entity
VectorCopy( ent->curstate.origin, ent->origin );
VectorCopy( ent->curstate.angles, ent->angles );
ent->model = CL_ModelHandle( state.modelindex );
ent->curstate.framerate = 1.0f;
CL_ResetLatchedVars( ent, true );
if( ent->curstate.rendermode == kRenderNormal && ent->model != NULL )
{
// auto 'solid' faces
if( FBitSet( ent->model->flags, MODEL_TRANSPARENT ) && Host_IsQuakeCompatible( ))
{
ent->curstate.rendermode = kRenderTransAlpha;
ent->curstate.renderamt = 255;
}
}
R_AddEfrags( ent ); // add link
}
void CL_LegacyParseSoundPacket( sizebuf_t *msg, qboolean is_ambient )
{
vec3_t pos;
int chan, sound;
float volume, attn;
int flags, pitch, entnum;
sound_t handle = 0;
flags = MSG_ReadWord( msg );
if( flags & SND_LEGACY_LARGE_INDEX )
{
sound = MSG_ReadWord( msg );
flags &= ~SND_LEGACY_LARGE_INDEX;
}
else
sound = MSG_ReadByte( msg );
chan = MSG_ReadByte( msg );
if( FBitSet( flags, SND_VOLUME ))
volume = (float)MSG_ReadByte( msg ) / 255.0f;
else volume = VOL_NORM;
if( FBitSet( flags, SND_ATTENUATION ))
attn = (float)MSG_ReadByte( msg ) / 64.0f;
else attn = ATTN_NONE;
if( FBitSet( flags, SND_PITCH ))
pitch = MSG_ReadByte( msg );
else pitch = PITCH_NORM;
// entity reletive
entnum = MSG_ReadWord( msg );
// positioned in space
MSG_ReadVec3Coord( msg, pos );
if( FBitSet( flags, SND_SENTENCE ))
{
char sentenceName[32];
//if( FBitSet( flags, SND_SEQUENCE ))
//Q_snprintf( sentenceName, sizeof( sentenceName ), "!#%i", sound + MAX_SOUNDS );
//else
Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound );
handle = S_RegisterSound( sentenceName );
}
else handle = cl.sound_index[sound]; // see precached sound
if( !cl.audio_prepped )
return; // too early
// g-cont. sound and ambient sound have only difference with channel
if( is_ambient )
{
S_AmbientSound( pos, entnum, handle, volume, attn, pitch, flags );
}
else
{
S_StartSound( pos, entnum, chan, handle, volume, attn, pitch, flags );
}
}
/*
================
CL_PrecacheSound
prceache sound from server
================
*/
void CL_LegacyPrecacheSound( sizebuf_t *msg )
{
int soundIndex;
soundIndex = MSG_ReadUBitLong( msg, MAX_SOUND_BITS );
if( soundIndex < 0 || soundIndex >= MAX_SOUNDS )
Host_Error( "CL_PrecacheSound: bad soundindex %i\n", soundIndex );
Q_strncpy( cl.sound_precache[soundIndex], MSG_ReadString( msg ), sizeof( cl.sound_precache[0] ));
// when we loading map all resources is precached sequentially
//if( !cl.audio_prepped ) return;
cl.sound_index[soundIndex] = S_RegisterSound( cl.sound_precache[soundIndex] );
}
void CL_LegacyPrecacheModel( sizebuf_t *msg )
{
int modelIndex;
string model;
modelIndex = MSG_ReadUBitLong( msg, MAX_LEGACY_MODEL_BITS );
if( modelIndex < 0 || modelIndex >= MAX_MODELS )
Host_Error( "CL_PrecacheModel: bad modelindex %i\n", modelIndex );
Q_strncpy( model, MSG_ReadString( msg ), MAX_STRING );
//Q_strncpy( cl.model_precache[modelIndex], BF_ReadString( msg ), sizeof( cl.model_precache[0] ));
// when we loading map all resources is precached sequentially
//if( !cl.video_prepped ) return;
if( modelIndex == 1 && !cl.worldmodel )
{
CL_ClearWorld ();
cl.models[modelIndex] = cl.worldmodel = Mod_LoadWorld( model, true );
return;
}
//Mod_RegisterModel( cl.model_precache[modelIndex], modelIndex );
cl.models[modelIndex] = Mod_ForName( model, false, false );
cl.nummodels = Q_max( cl.nummodels, modelIndex );
}
void CL_LegacyPrecacheEvent( sizebuf_t *msg )
{
int eventIndex;
eventIndex = MSG_ReadUBitLong( msg, MAX_EVENT_BITS );
if( eventIndex < 0 || eventIndex >= MAX_EVENTS )
Host_Error( "CL_PrecacheEvent: bad eventindex %i\n", eventIndex );
Q_strncpy( cl.event_precache[eventIndex], MSG_ReadString( msg ), sizeof( cl.event_precache[0] ));
// can be set now
CL_SetEventIndex( cl.event_precache[eventIndex], eventIndex );
}
void CL_LegacyUpdateUserinfo( sizebuf_t *msg )
{
int slot, id = 0;
qboolean active;
player_info_t *player;
slot = MSG_ReadUBitLong( msg, MAX_CLIENT_BITS );
if( slot >= MAX_CLIENTS )
Host_Error( "CL_ParseServerMessage: svc_updateuserinfo >= MAX_CLIENTS\n" );
//id = MSG_ReadLong( msg ); // unique user ID
player = &cl.players[slot];
active = MSG_ReadOneBit( msg ) ? true : false;
if( active )
{
Q_strncpy( player->userinfo, MSG_ReadString( msg ), sizeof( player->userinfo ));
Q_strncpy( player->name, Info_ValueForKey( player->userinfo, "name" ), sizeof( player->name ));
Q_strncpy( player->model, Info_ValueForKey( player->userinfo, "model" ), sizeof( player->model ));
player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" ));
player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" ));
player->spectator = Q_atoi( Info_ValueForKey( player->userinfo, "*hltv" ));
//MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey ));
if( slot == cl.playernum ) memcpy( &gameui.playerinfo, player, sizeof( player_info_t ));
}
else memset( player, 0, sizeof( *player ));
}
/*
=====================
CL_ParseLegacyServerMessage
dispatch messages
=====================
*/
void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message )
{
size_t bufStart, playerbytes;
int cmd, param1, param2;
int old_background;
const char *s;
cls.starting_count = MSG_GetNumBytesRead( msg ); // updates each frame
CL_Parse_Debug( true ); // begin parsing
if( normal_message )
{
// assume no entity/player update this packet
if( cls.state == ca_active )
{
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].valid = false;
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].choked = false;
}
else
{
CL_ResetFrame( &cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK] );
}
}
// parse the message
while( 1 )
{
if( MSG_CheckOverflow( msg ))
{
Host_Error( "CL_ParseServerMessage: overflow!\n" );
return;
}
// mark start position
bufStart = MSG_GetNumBytesRead( msg );
// end of message (align bits)
if( MSG_GetNumBitsLeft( msg ) < 8 )
break;
cmd = MSG_ReadServerCmd( msg );
// record command for debugging spew on parse problem
CL_Parse_RecordCommand( cmd, bufStart );
// other commands
switch( cmd )
{
case svc_bad:
Host_Error( "svc_bad\n" );
break;
case svc_nop:
// this does nothing
break;
case svc_disconnect:
CL_Drop ();
Host_AbortCurrentFrame ();
break;
case svc_legacy_event:
CL_ParseEvent( msg );
cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_legacy_changing:
old_background = cl.background;
if( MSG_ReadOneBit( msg ))
{
cls.changelevel = true;
S_StopAllSounds( true );
Con_Printf( "Server changing, reconnecting\n" );
if( cls.demoplayback )
{
SCR_BeginLoadingPlaque( cl.background );
cls.changedemo = true;
}
CL_ClearState ();
CL_InitEdicts (); // re-arrange edicts
}
else Con_Printf( "Server disconnected, reconnecting\n" );
if( cls.demoplayback )
{
cl.background = (cls.demonum != -1) ? true : false;
cls.state = ca_connected;
}
else
{
// g-cont. local client skip the challenge
if( SV_Active( ))
cls.state = ca_disconnected;
else cls.state = ca_connecting;
cl.background = old_background;
cls.connect_time = MAX_HEARTBEAT;
}
break;
case svc_setview:
CL_ParseViewEntity( msg );
break;
case svc_sound:
CL_LegacyParseSoundPacket( msg, false );
cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_legacy_ambientsound:
CL_LegacyParseSoundPacket( msg, true );
cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_time:
CL_ParseServerTime( msg );
break;
case svc_print:
Con_Printf( "%s", MSG_ReadString( msg ));
break;
case svc_stufftext:
s = MSG_ReadString( msg );
#ifdef HACKS_RELATED_HLMODS
// dsiable Cry Of Fear antisave protection
if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS )
break; // too early
#endif
if( !Q_strcmp(s, "cmd getresourcelist\n") )
Cbuf_AddText("cmd continueloading\n");
else
{
Con_Reportf( "Stufftext: %s", s );
Cbuf_AddText( s );
}
break;
case svc_setangle:
CL_ParseSetAngle( msg );
break;
case svc_serverdata:
Cbuf_Execute(); // make sure any stuffed commands are done
CL_ParseLegacyServerData( msg );
break;
case svc_lightstyle:
CL_ParseLightStyle( msg );
break;
case svc_updateuserinfo:
CL_LegacyUpdateUserinfo( msg );
break;
case svc_deltatable:
Delta_ParseTableField( msg );
break;
case svc_clientdata:
CL_ParseClientData( msg );
cl.frames[cl.parsecountmod].graphdata.client += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_resource:
CL_ParseResource( msg );
break;
case svc_pings:
CL_UpdateUserPings( msg );
break;
case svc_particle:
CL_ParseParticles( msg );
break;
case svc_restoresound:
CL_ParseRestoreSoundPacket( msg );
cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_spawnstatic:
CL_ParseStaticEntity( msg );
break;
case svc_event_reliable:
CL_ParseReliableEvent( msg );
cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_spawnbaseline:
CL_LegacyParseBaseline( msg );
break;
case svc_temp_entity:
CL_ParseTempEntity( msg );
cl.frames[cl.parsecountmod].graphdata.tentities += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_setpause:
cl.paused = ( MSG_ReadOneBit( msg ) != 0 );
break;
case svc_signonnum:
CL_ParseSignon( msg );
break;
case svc_centerprint:
CL_CenterPrint( MSG_ReadString( msg ), 0.25f );
break;
case svc_intermission:
cl.intermission = 1;
break;
case svc_legacy_modelindex:
CL_LegacyPrecacheModel( msg );
break;
case svc_legacy_soundindex:
CL_LegacyPrecacheSound( msg );
break;
case svc_cdtrack:
param1 = MSG_ReadByte( msg );
param1 = bound( 1, param1, MAX_CDTRACKS ); // tracknum
param2 = MSG_ReadByte( msg );
param2 = bound( 1, param2, MAX_CDTRACKS ); // loopnum
S_StartBackgroundTrack( clgame.cdtracks[param1-1], clgame.cdtracks[param2-1], 0, false );
break;
case svc_restore:
CL_ParseRestore( msg );
break;
case svc_legacy_eventindex:
//CL_ParseFinaleCutscene( msg, 3 );
CL_LegacyPrecacheEvent(msg);
break;
case svc_weaponanim:
param1 = MSG_ReadByte( msg ); // iAnim
param2 = MSG_ReadByte( msg ); // body
CL_WeaponAnim( param1, param2 );
break;
case svc_bspdecal:
CL_ParseStaticDecal( msg );
break;
case svc_roomtype:
param1 = MSG_ReadShort( msg );
Cvar_SetValue( "room_type", param1 );
break;
case svc_addangle:
CL_ParseAddAngle( msg );
break;
case svc_usermessage:
CL_RegisterUserMessage( msg );
break;
case svc_packetentities:
playerbytes = CL_ParsePacketEntities( msg, false );
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;
case svc_deltapacketentities:
playerbytes = CL_ParsePacketEntities( msg, true );
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;
case svc_legacy_chokecount:
{
int i, j;
i = MSG_ReadByte( msg );
j = cls.netchan.incoming_acknowledged - 1;
for( ; i > 0 && j > cls.netchan.outgoing_sequence - CL_UPDATE_BACKUP; j-- )
{
if( cl.frames[j & CL_UPDATE_MASK].receivedtime != -3.0 )
{
cl.frames[j & CL_UPDATE_MASK].receivedtime = -2.0;
i--;
}
}
break;
}
//cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].choked = true;
//cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].receivedtime = -2.0;
break;
case svc_resourcelist:
CL_ParseResourceList( msg );
break;
case svc_deltamovevars:
CL_ParseMovevars( msg );
break;
case svc_resourcerequest:
CL_ParseResourceRequest( msg );
break;
case svc_customization:
CL_ParseCustomization( msg );
break;
case svc_crosshairangle:
CL_ParseCrosshairAngle( msg );
break;
case svc_soundfade:
CL_ParseSoundFade( msg );
break;
case svc_filetxferfailed:
CL_ParseFileTransferFailed( msg );
break;
case svc_hltv:
CL_ParseHLTV( msg );
break;
case svc_director:
CL_ParseDirector( msg );
break;
case svc_voiceinit:
CL_ParseVoiceInit( msg );
break;
case svc_voicedata:
CL_ParseVoiceData( msg );
break;
case svc_resourcelocation:
CL_ParseResLocation( msg );
break;
case svc_querycvarvalue:
CL_ParseCvarValue( msg );
break;
case svc_querycvarvalue2:
CL_ParseCvarValue2( msg );
break;
default:
CL_ParseUserMessage( msg, cmd );
cl.frames[cl.parsecountmod].graphdata.usr += MSG_GetNumBytesRead( msg ) - bufStart;
break;
}
}
cl.frames[cl.parsecountmod].graphdata.msgbytes += MSG_GetNumBytesRead( msg ) - cls.starting_count;
CL_Parse_Debug( false ); // done
// we don't know if it is ok to save a demo message until
// after we have parsed the frame
if( !cls.demoplayback )
{
if( cls.demorecording && !cls.demowaiting )
{
CL_WriteDemoMessage( false, cls.starting_count, msg );
}
else if( cls.state != ca_active )
{
CL_WriteDemoMessage( true, cls.starting_count, msg );
}
}
}
void CL_LegacyPrecache_f( void )
{
int spawncount, i;
model_t *mod;
if( !cls.legacymode )
return;
spawncount = Q_atoi( Cmd_Argv( 1 ));
Con_Printf( "Setting up renderer...\n" );
// load tempent sprites (glowshell, muzzleflashes etc)
CL_LoadClientSprites ();
// invalidate all decal indexes
memset( cl.decal_index, 0, sizeof( cl.decal_index ));
cl.video_prepped = true;
cl.audio_prepped = true;
if( clgame.entities )
clgame.entities->model = cl.worldmodel;
// tell rendering system we have a new set of models.
R_NewMap ();
CL_SetupOverviewParams();
if( clgame.drawFuncs.R_NewMap != NULL )
clgame.drawFuncs.R_NewMap();
// release unused SpriteTextures
for( i = 1, mod = clgame.sprites; i < MAX_CLIENT_SPRITES; i++, mod++ )
{
if( mod->needload == NL_UNREFERENCED && COM_CheckString( mod->name ))
Mod_UnloadSpriteModel( mod );
}
// Mod_FreeUnused ();
if( host_developer.value <= DEV_NONE )
Con_ClearNotify(); // clear any lines of console text
// done with all resources, issue prespawn command.
// Include server count in case server disconnects and changes level during d/l
MSG_BeginClientCmd( &cls.netchan.message, clc_stringcmd );
MSG_WriteString( &cls.netchan.message, va( "begin %i", spawncount ));
cls.signon = SIGNONS;
}

View File

@ -2024,7 +2024,7 @@ void CL_ParseTempEntity( sizebuf_t *msg )
{
sizebuf_t buf;
byte pbuf[256];
int iSize = MSG_ReadWord( msg );
int iSize = cls.legacymode?MSG_ReadByte( msg ):MSG_ReadWord( msg );
int type, color, count, flags;
int decalIndex, modelIndex, entityIndex;
float scale, life, frameRate, vel, random;
@ -3124,4 +3124,4 @@ void CL_ClearEffects( void )
CL_ClearViewBeams ();
CL_ClearParticles ();
CL_ClearLightStyles ();
}
}

View File

@ -661,6 +661,7 @@ typedef struct
file_t *demoheader; // contain demo startup info in case we record a demo on this level
qboolean internetservers_wait; // internetservers is waiting for dns request
qboolean internetservers_pending; // internetservers is waiting for dns request
qboolean legacymode; // one-way 48 protocol compatibility
} client_static_t;
#ifdef __cplusplus
@ -873,6 +874,9 @@ _inline cl_entity_t *CL_EDICT_NUM( int n )
// cl_parse.c
//
void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message );
void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message );
void CL_LegacyPrecache_f( void );
void CL_ParseTempEntity( sizebuf_t *msg );
void CL_StartResourceDownloading( const char *pszMessage, qboolean bCustom );
qboolean CL_DispatchUserMessage( const char *pszName, int iSize, void *pbuf );

View File

@ -645,7 +645,7 @@ qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes )
char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
{
static char string[2048];
static char string[4096];
int l = 0, c;
do

View File

@ -81,6 +81,10 @@ static const delta_field_t pm_fields[] =
{ PHYS_DEF( skyvec_z ) },
{ PHYS_DEF( fog_settings ) },
{ PHYS_DEF( wateralpha ) },
{ PHYS_DEF( skydir_x ) },
{ PHYS_DEF( skydir_y ) },
{ PHYS_DEF( skydir_z ) },
{ PHYS_DEF( skyangle ) },
{ NULL },
};
@ -511,12 +515,23 @@ void Delta_ParseTableField( sizebuf_t *msg )
tableIndex = MSG_ReadUBitLong( msg, 4 );
dt = Delta_FindStructByIndex( tableIndex );
Assert( dt != NULL );
if( !dt )
Host_Error( "Delta_ParseTableField: not initialized" );
nameIndex = MSG_ReadUBitLong( msg, 8 ); // read field name index
Assert( nameIndex >= 0 && nameIndex < dt->maxFields );
if( !( nameIndex >= 0 && nameIndex < dt->maxFields ) )
{
Con_Reportf( "Delta_ParseTableField: wrong nameIndex %d for table %s, ignoring\n", nameIndex, dt->pName );
MSG_ReadUBitLong( msg, 10 );
MSG_ReadUBitLong( msg, 5 ) + 1;
if( MSG_ReadOneBit( msg ))
MSG_ReadFloat( msg );
if( MSG_ReadOneBit( msg ))
MSG_ReadFloat( msg );
return;
}
pName = dt->pInfo[nameIndex].name;
flags = MSG_ReadUBitLong( msg, 10 );
bits = MSG_ReadUBitLong( msg, 5 ) + 1;
@ -1600,7 +1615,7 @@ void MSG_ReadClientData( sizebuf_t *msg, clientdata_t *from, clientdata_t *to, f
*to = *from;
if( !MSG_ReadOneBit( msg ))
if( !cls.legacymode && !MSG_ReadOneBit( msg ))
return; // we have no changes
// process fields
@ -1839,29 +1854,31 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state
Host_Error( "MSG_ReadDeltaEntity: unknown update type %i\n", fRemoveType );
}
if( MSG_ReadOneBit( msg ))
baseline_offset = MSG_ReadSBitLong( msg, 7 );
if( baseline_offset != 0 )
if( !cls.legacymode )
{
if( delta_type == DELTA_STATIC )
{
int backup = Q_max( 0, clgame.numStatics - abs( baseline_offset ));
from = &clgame.static_entities[backup].baseline;
}
else if( baseline_offset > 0 )
{
int backup = cls.next_client_entities - baseline_offset;
from = &cls.packet_entities[backup % cls.num_client_entities];
}
else
{
baseline_offset = abs( baseline_offset );
if( baseline_offset < cl.instanced_baseline_count )
from = &cl.instanced_baseline[baseline_offset];
}
}
if( MSG_ReadOneBit( msg ))
baseline_offset = MSG_ReadSBitLong( msg, 7 );
if( baseline_offset != 0 )
{
if( delta_type == DELTA_STATIC )
{
int backup = Q_max( 0, clgame.numStatics - abs( baseline_offset ));
from = &clgame.static_entities[backup].baseline;
}
else if( baseline_offset > 0 )
{
int backup = cls.next_client_entities - baseline_offset;
from = &cls.packet_entities[backup % cls.num_client_entities];
}
else
{
baseline_offset = abs( baseline_offset );
if( baseline_offset < cl.instanced_baseline_count )
from = &cl.instanced_baseline[baseline_offset];
}
}
}
// g-cont. probably is redundant
*to = *from;
@ -1869,7 +1886,7 @@ qboolean MSG_ReadDeltaEntity( sizebuf_t *msg, entity_state_t *from, entity_state
to->entityType = MSG_ReadUBitLong( msg, 2 );
to->number = number;
if( FBitSet( to->entityType, ENTITY_BEAM ))
if( cls.legacymode?(to->entityType == ENTITY_BEAM):FBitSet(to->entityType, ENTITY_BEAM) )
{
dt = Delta_FindStruct( "custom_entity_state_t" );
}
@ -2004,4 +2021,4 @@ void Delta_UnsetFieldByIndex( delta_t *pFields, int fieldNumber )
return;
dt->pFields[fieldNumber].bInactive = true;
}
}

View File

@ -243,4 +243,18 @@ GNU General Public License for more details.
extern const char *svc_strings[svc_lastmsg+1];
extern const char *clc_strings[clc_lastmsg+1];
// legacy protocol definitons
#define PROTOCOL_LEGACY_VERSION 48
#define svc_legacy_modelindex 31 // [index][modelpath]
#define svc_legacy_soundindex 28 // [index][soundpath]
#define svc_legacy_eventindex 34 // [index][eventname]
#define svc_legacy_ambientsound 29
#define svc_legacy_chokecount 42 // old client specified count, new just sends svc_choke
#define svc_legacy_event 27 // playback event queue
#define svc_legacy_changing 3 // changelevel by server request
#define SND_LEGACY_LARGE_INDEX (1<<2) // a send sound as short
#define MAX_LEGACY_ENTITY_BITS 12
#define MAX_LEGACY_WEAPON_BITS 5
#define MAX_LEGACY_MODEL_BITS 11
#endif//NET_PROTOCOL_H

View File

@ -43,8 +43,12 @@ struct movevars_s
int features; // engine features that shared across network
int fog_settings; // Global fog settings (packed color+density)
float wateralpha; // World water alpha 1.0 - solid 0.0 - transparent
float skydir_x; // skybox rotate direction
float skydir_y; //
float skydir_z; //
float skyangle; // skybox rotate angle
};
extern movevars_t movevars;
#endif
#endif