From 6a02a571c1e5e3ff3d2e79c5063ce7708461809d Mon Sep 17 00:00:00 2001 From: mittorn Date: Fri, 25 Jan 2019 20:53:08 +0700 Subject: [PATCH] Legacy protocol compatibility (cl_legacymode) --- engine/client/cl_events.c | 4 +- engine/client/cl_frame.c | 7 +- engine/client/cl_main.c | 30 +- engine/client/cl_parse.c | 813 ++++++++++++++++++++++++++++++++++++- engine/client/cl_tent.c | 4 +- engine/client/client.h | 4 + engine/common/net_buffer.c | 2 +- engine/common/net_encode.c | 67 +-- engine/common/protocol.h | 14 + pm_shared/pm_movevars.h | 6 +- 10 files changed, 906 insertions(+), 45 deletions(-) diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c index 2d9bb4f3..75c0516b 100644 --- a/engine/client/cl_events.c +++ b/engine/client/cl_events.c @@ -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 ); -} \ No newline at end of file +} diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index be1f5a73..2d2f4e70 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -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 ); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index f9992548..4e35d462 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -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" ); + } //============================================================================ diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index fee43b53..955e223b 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -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; +} diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 20d548e2..d9314195 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -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 (); -} \ No newline at end of file +} diff --git a/engine/client/client.h b/engine/client/client.h index 18fcd300..522d5268 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -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 ); diff --git a/engine/common/net_buffer.c b/engine/common/net_buffer.c index b6355403..abba581c 100644 --- a/engine/common/net_buffer.c +++ b/engine/common/net_buffer.c @@ -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 diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 043baf10..556e9bec 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -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 ) + if( MSG_ReadOneBit( msg )) + baseline_offset = MSG_ReadSBitLong( msg, 7 ); + + if( baseline_offset != 0 ) { - int backup = cls.next_client_entities - baseline_offset; - from = &cls.packet_entities[backup % cls.num_client_entities]; + 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]; + } } - 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; -} \ No newline at end of file +} diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 1bff7a55..6564b571 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -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 diff --git a/pm_shared/pm_movevars.h b/pm_shared/pm_movevars.h index 4cf2ba3f..713a7f89 100644 --- a/pm_shared/pm_movevars.h +++ b/pm_shared/pm_movevars.h @@ -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 \ No newline at end of file +#endif