/* ===================== CL_Disconnect Goes from a connected state to full screen console state Sends a disconnect message to the server This is also called on Host_Error, so it shouldn't cause any errors ===================== */ void CL_Disconnect( void ) { if( cls.state == ca_disconnected ) return; cls.connect_time = 0; cls.changedemo = false; CL_Stop_f(); // send a disconnect message to the server CL_SendDisconnectMessage(); CL_ClearState (); S_StopBackgroundTrack (); SCR_EndLoadingPlaque (); // get rid of loading plaque // clear the network channel, too. Netchan_Clear( &cls.netchan ); cls.state = ca_disconnected; // restore gamefolder here (in case client was connected to another game) CL_ChangeGame( GI->gamefolder, true ); // back to menu if developer mode set to "player" or "mapper" if( host.developer > 2 ) return; UI_SetActiveMenu( true ); }
/* ===================== CL_Disconnect Called when a connection, or cinematic is being terminated. Goes from a connected state to either a menu state or a console state Sends a disconnect message to the server This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors ===================== */ void CL_Disconnect( void ) { if ( !com_cl_running || !com_cl_running->integer ) { return; } if (cls.uiStarted) UI_SetActiveMenu( NULL,NULL ); SCR_StopCinematic (); S_ClearSoundBuffer(); // send a disconnect message to the server // send it a few times in case one is dropped if ( cls.state >= CA_CONNECTED ) { CL_AddReliableCommand( "disconnect" ); CL_WritePacket(); CL_WritePacket(); CL_WritePacket(); } CL_ClearState (); CL_FreeReliableCommands(); extern void CL_FreeServerCommands(void); CL_FreeServerCommands(); memset( &clc, 0, sizeof( clc ) ); cls.state = CA_DISCONNECTED; // allow cheats locally Cvar_Set( "timescale", "1" );//jic we were skipping Cvar_Set( "skippingCinematic", "0" );//jic we were skipping }
/** * @brief Sets the @c cls.state to @c ca_disconnected and informs the server * @sa CL_Drop * @note Goes from a connected state to disconnected state * Sends a disconnect message to the server * This is also called on @c Com_Error, so it shouldn't cause any errors */ void CL_Disconnect (void) { if (cls.state < ca_connecting) return; Com_Printf("Disconnecting...\n"); /* send a disconnect message to the server */ if (!Com_ServerState()) { dbuffer msg; NET_WriteByte(&msg, clc_stringcmd); NET_WriteString(&msg, NET_STATE_DISCONNECT "\n"); NET_WriteMsg(cls.netStream, msg); /* make sure, that this is send */ NET_Wait(0); } NET_StreamFinished(cls.netStream); cls.netStream = nullptr; CL_ClearState(); S_Stop(); R_ShutdownModels(false); R_FreeWorldImages(); CL_SetClientState(ca_disconnected); CL_ClearBattlescapeEvents(); GAME_EndBattlescape(); }
/** * @brief Sets the client state */ void CL_SetClientState (connstate_t state) { Com_DPrintf(DEBUG_CLIENT, "CL_SetClientState: Set new state to %i (old was: %i)\n", state, cls.state); cls.state = state; switch (cls.state) { case ca_uninitialized: Com_Error(ERR_FATAL, "CL_SetClientState: Don't set state ca_uninitialized\n"); break; case ca_active: cls.waitingForStart = 0; break; case ca_connecting: cls.reconnectTime = 0; CL_Connect(); break; case ca_disconnected: cls.waitingForStart = 0; break; case ca_connected: /* wipe the client_state_t struct */ CL_ClearState(); Cvar_Set("cl_ready", "0"); break; default: break; } }
/* ================== CL_ParseServerData ================== */ void CL_ParseServerData (void) { extern cvar_t *fs_gamedirvar; char *str; int i; Com_DPrintf ("Serverdata packet received.\n"); // // wipe the client_state_t struct // CL_ClearState (); cls.state = ca_connected; // parse protocol version number i = MSG_ReadLong (&net_message); cls.serverProtocol = i; // BIG HACK to let demos from release work with the 3.0x patch!!! // Knightmare- also allow connectivity with servers using the old protocol // if (Com_ServerState() && (i < PROTOCOL_VERSION) /*== 35*/) if ( LegacyProtocol() ) {} // do nothing else if (i != PROTOCOL_VERSION) Com_Error (ERR_DROP,"Server returned version %i, not %i", i, PROTOCOL_VERSION); cl.servercount = MSG_ReadLong (&net_message); cl.attractloop = MSG_ReadByte (&net_message); // game directory str = MSG_ReadString (&net_message); strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1); // set gamedir if ( ( (*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)) ) && !cl.attractloop ) // Knightmare- don't allow demos to change this Cvar_Set("game", str); // parse player entity number cl.playernum = MSG_ReadShort (&net_message); // get the full level name str = MSG_ReadString (&net_message); if (cl.playernum == -1) { // playing a cinematic or showing a pic, not a level SCR_PlayCinematic (str); } else { // seperate the printfs so the server message can have a color Com_Printf("\n\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\n"); con.ormask = 128; Com_Printf ("%c"S_COLOR_SHADOW S_COLOR_ALT"%s\n", 2, str); con.ormask = 0; // need to prep refresh at next oportunity cl.refresh_prepped = false; } }
/** * @sa CL_Shutdown * @sa CL_InitAfter */ void CL_Init (void) { /* i18n through gettext */ char languagePath[MAX_OSPATH]; cvar_t* fs_i18ndir; isdown = false; if (sv_dedicated->integer) return; /* nothing running on the client */ OBJZERO(cls); fs_i18ndir = Cvar_Get("fs_i18ndir", "", 0, "System path to language files"); /* i18n through gettext */ setlocale(LC_ALL, "C"); setlocale(LC_MESSAGES, ""); /* use system locale dir if we can't find in gamedir */ if (fs_i18ndir->string[0] != '\0') Q_strncpyz(languagePath, fs_i18ndir->string, sizeof(languagePath)); else #ifdef LOCALEDIR Com_sprintf(languagePath, sizeof(languagePath), LOCALEDIR); #else Com_sprintf(languagePath, sizeof(languagePath), "%s/" BASEDIRNAME "/i18n/", FS_GetCwd()); #endif Com_DPrintf(DEBUG_CLIENT, "...using mo files from %s\n", languagePath); bindtextdomain(TEXT_DOMAIN, languagePath); bind_textdomain_codeset(TEXT_DOMAIN, "UTF-8"); /* load language file */ textdomain(TEXT_DOMAIN); CL_InitMemPools(); /* all archived variables will now be loaded */ Con_Init(); CIN_Init(); VID_Init(); SCR_DrawLoadingScreen(false, 0); S_Init(); SCR_Init(); CL_InitLocal(); Irc_Init(); CL_ViewInit(); CL_ClearState(); /* cvar feedback */ for (const cvar_t* var = Cvar_GetFirst(); var; var = var->next) { if (var->flags & CVAR_R_CONTEXT) Cvar_RegisterChangeListener(var->name, CL_RContextCvarChange); if (var->flags & CVAR_R_IMAGES) Cvar_RegisterChangeListener(var->name, CL_RImagesCvarChange); } }
/* ================== CL_ParseServerData ================== */ void CL_ParseServerData(void) { extern cvar_t * fs_gamedirvar; char * str; int i; Com_DPrintf("Serverdata packet received.\n"); // // wipe the client_state_t struct // CL_ClearState(); cls.state = ca_connected; // parse protocol version number i = MSG_ReadLong(&net_message); cls.serverProtocol = i; // BIG HACK to let demos from release work with the 3.0x patch!!! if (Com_ServerState() && PROTOCOL_VERSION == 34) { } else if (i != PROTOCOL_VERSION) { Com_Error(ERR_DROP, "Server returned version %i, not %i", i, PROTOCOL_VERSION); } cl.servercount = MSG_ReadLong(&net_message); cl.attractloop = MSG_ReadByte(&net_message); // game directory str = MSG_ReadString(&net_message); strncpy(cl.gamedir, str, sizeof(cl.gamedir) - 1); // set gamedir if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string))) Cvar_Set("game", str); // parse player entity number cl.playernum = MSG_ReadShort(&net_message); // get the full level name str = MSG_ReadString(&net_message); if (cl.playernum == -1) { // playing a cinematic or showing a pic, not a level SCR_PlayCinematic(str); } else { // separate the printfs so the server message can have a color Com_Printf("\n\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\n"); Com_Printf("%c%s\n", 2, str); // need to prep refresh at next opportunity cl.refresh_prepped = false; } }
void CClientState::Disconnect(bool bShowMainMenu) { CBaseClientState::Disconnect(bShowMainMenu); // stop any demo activities #ifndef _XBOX demoplayer->StopPlayback(); demorecorder->StopRecording(); #endif S_StopAllSounds( true ); R_DecalTermAll(); if ( m_nMaxClients > 1 ) { if ( EngineVGui()->IsConsoleVisible() == false ) { // start progress bar immediately for multiplayer level transitions EngineVGui()->EnabledProgressBarForNextLoad(); } } CL_ClearState(); #ifndef _XBOX // End any in-progress downloads CL_HTTPStop_f(); #endif // stop loading progress bar if (bShowMainMenu) { SCR_EndLoadingPlaque(); } // notify game ui dll of out-of-in-game status EngineVGui()->NotifyOfServerDisconnect(); if (bShowMainMenu && !engineClient->IsDrawingLoadingImage() && (cl.demonum == -1)) { // we're not in the middle of loading something, so show the UI if ( EngineVGui() ) { EngineVGui()->ActivateGameUI(); } } HostState_OnClientDisconnected(); // if we played a demo from the startdemos list, play next one if (cl.demonum != -1) { CL_NextDemo(); } }
/* ===================== CL_Disconnect Called when a connection, or cinematic is being terminated. Goes from a connected state to either a menu state or a console state Sends a disconnect message to the server This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors ===================== */ void CL_Disconnect( void ) { if ( !com_cl_running || !com_cl_running->integer ) { return; } #ifdef _XBOX Cvar_Set("r_norefresh", "0"); // Make sure to stop all rumbling! - Prevents bug when quitting game during rumble: extern void IN_KillRumbleScripts( void ); IN_KillRumbleScripts(); #endif if (cls.uiStarted) UI_SetActiveMenu( NULL,NULL ); SCR_StopCinematic (); S_ClearSoundBuffer(); #ifdef _XBOX // extern qboolean RE_RegisterImages_LevelLoadEnd(void); // RE_RegisterImages_LevelLoadEnd(); R_DeleteTextures(); #endif // send a disconnect message to the server // send it a few times in case one is dropped if ( cls.state >= CA_CONNECTED ) { CL_AddReliableCommand( "disconnect" ); CL_WritePacket(); CL_WritePacket(); CL_WritePacket(); } CL_ClearState (); CL_FreeReliableCommands(); extern void CL_FreeServerCommands(void); CL_FreeServerCommands(); memset( &clc, 0, sizeof( clc ) ); cls.state = CA_DISCONNECTED; // allow cheats locally Cvar_Set( "timescale", "1" );//jic we were skipping Cvar_Set( "skippingCinematic", "0" );//jic we were skipping }
/* ===================== CL_Disconnect Called when a connection, or cinematic is being terminated. Goes from a connected state to either a menu state or a console state Sends a disconnect message to the server This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors ===================== */ void CL_Disconnect( void ) { int i; if ( !com_cl_running || !com_cl_running->integer ) { return; } if (cls.uiStarted) UI_SetActiveMenu( NULL,NULL ); SCR_StopCinematic (); S_ClearSoundBuffer(); // send a disconnect message to the server // send it a few times in case one is dropped if ( cls.state >= CA_CONNECTED ) { CL_AddReliableCommand( "disconnect" ); CL_WritePacket(); CL_WritePacket(); CL_WritePacket(); } CL_ClearState (); // wipe the client connection for ( i = 0 ; i < MAX_RELIABLE_COMMANDS ; i++ ) { if ( clc.reliableCommands[i] ) { Z_Free( clc.reliableCommands[i] ); } } memset( &clc, 0, sizeof( clc ) ); cls.state = CA_DISCONNECTED; // allow cheats locally Cvar_Set( "timescale", "1" );//jic we were skipping Cvar_Set( "skippingCinematic", "0" );//jic we were skipping }
qboolean demoPlay( const char *fileName ) { demo.play.handle = demoPlayOpen( fileName ); if (demo.play.handle) { demoPlay_t *play = demo.play.handle; clc.demoplaying = qtrue; clc.newDemoPlayer = qtrue; clc.serverMessageSequence = 0; clc.lastExecutedServerCommand = 0; Com_Printf("Opened %s, which has %d seconds and %d frames\n", fileName, (play->endTime - play->startTime) / 1000, play->totalFrames ); Con_Close(); // wipe local client state CL_ClearState(); cls.state = CA_LOADING; // Pump the loop, this may change gamestate! Com_EventLoop(); // starting to load a map so we get out of full screen ui mode Cvar_Set("r_uiFullScreen", "0"); // flush client memory and start loading stuff // this will also (re)load the UI // if this is a local client then only the client part of the hunk // will be cleared, note that this is done after the hunk mark has been set CL_FlushMemory(); // initialize the CGame cls.cgameStarted = qtrue; // Create the gamestate Com_Memcpy( cl.gameState.stringOffsets, play->frame->string.offsets, sizeof( play->frame->string.offsets )); Com_Memcpy( cl.gameState.stringData, play->frame->string.data, play->frame->string.used ); cl.gameState.dataCount = play->frame->string.used; CL_InitCGame(); cls.state = CA_ACTIVE; return qtrue; } else { return qfalse; } }
/* * CL_ParseServerData */ static void CL_ParseServerData( msg_t *msg ) { const char *str, *gamedir; int i, sv_bitflags, numpure; int http_portnum; Com_DPrintf( "Serverdata packet received.\n" ); // wipe the client_state_t struct CL_ClearState(); CL_SetClientState( CA_CONNECTED ); // parse protocol version number i = MSG_ReadLong( msg ); if( i != APP_PROTOCOL_VERSION ) Com_Error( ERR_DROP, "Server returned version %i, not %i", i, APP_PROTOCOL_VERSION ); cl.servercount = MSG_ReadLong( msg ); cl.snapFrameTime = (unsigned int)MSG_ReadShort( msg ); // set extrapolation time to half snapshot time Cvar_ForceSet( "cl_extrapolationTime", va( "%i", (unsigned int)( cl.snapFrameTime * 0.5 ) ) ); cl_extrapolationTime->modified = qfalse; // base game directory str = MSG_ReadString( msg ); if( !str || !str[0] ) Com_Error( ERR_DROP, "Server sent an empty base game directory" ); if( !COM_ValidateRelativeFilename( str ) || strchr( str, '/' ) ) Com_Error( ERR_DROP, "Server sent an invalid base game directory: %s", str ); if( strcmp( FS_BaseGameDirectory(), str ) ) { Com_Error( ERR_DROP, "Server has different base game directory (%s) than the client (%s)", str, FS_BaseGameDirectory() ); } // game directory str = MSG_ReadString( msg ); if( !str || !str[0] ) Com_Error( ERR_DROP, "Server sent an empty game directory" ); if( !COM_ValidateRelativeFilename( str ) || strchr( str, '/' ) ) Com_Error( ERR_DROP, "Server sent an invalid game directory: %s", str ); gamedir = FS_GameDirectory(); if( strcmp( str, gamedir ) ) { // shutdown the cgame module first in case it is running for whatever reason // (happens on wswtv in lobby), otherwise precaches that are going to follow // will probably f**k up (like models trying to load before the world model) CL_GameModule_Shutdown(); if( !FS_SetGameDirectory( str, qtrue ) ) Com_Error( ERR_DROP, "Failed to load game directory set by server: %s", str ); ML_Restart( qtrue ); } // parse player entity number cl.playernum = MSG_ReadShort( msg ); // get the full level name Q_strncpyz( cl.servermessage, MSG_ReadString( msg ), sizeof( cl.servermessage ) ); sv_bitflags = MSG_ReadByte( msg ); if( cls.demo.playing ) { cls.reliable = ( sv_bitflags & SV_BITFLAGS_RELIABLE ); } else { if( cls.reliable != ( ( sv_bitflags & SV_BITFLAGS_RELIABLE ) != 0 ) ) Com_Error( ERR_DROP, "Server and client disagree about connection reliability" ); } // builting HTTP server port if( cls.httpbaseurl ) { Mem_Free( cls.httpbaseurl ); cls.httpbaseurl = NULL; } if( ( sv_bitflags & SV_BITFLAGS_HTTP ) != 0 ) { if( ( sv_bitflags & SV_BITFLAGS_HTTP_BASEURL ) != 0 ) { // read base upstream url cls.httpbaseurl = ZoneCopyString( MSG_ReadString( msg ) ); } else { http_portnum = MSG_ReadShort( msg ) & 0xffff; cls.httpaddress = cls.serveraddress; if( cls.httpaddress.type == NA_IP6 ) { cls.httpaddress.address.ipv6.port = BigShort( http_portnum ); } else { cls.httpaddress.address.ipv4.port = BigShort( http_portnum ); } if( http_portnum ) { if( cls.httpaddress.type == NA_LOOPBACK ) { cls.httpbaseurl = ZoneCopyString( va( "http://localhost:%hu/", http_portnum ) ); } else { cls.httpbaseurl = ZoneCopyString( va( "http://%s/", NET_AddressToString( &cls.httpaddress ) ) ); } } } } // pure list // clean old, if necessary Com_FreePureList( &cls.purelist ); // add new numpure = MSG_ReadShort( msg ); while( numpure > 0 ) { const char *pakname = MSG_ReadString( msg ); const unsigned checksum = MSG_ReadLong( msg ); Com_AddPakToPureList( &cls.purelist, pakname, checksum, NULL ); numpure--; } //assert( numpure == 0 ); // get the configstrings request CL_AddReliableCommand( va( "configstrings %i 0", cl.servercount ) ); cls.sv_pure = ( sv_bitflags & SV_BITFLAGS_PURE ) != 0; cls.sv_tv = ( sv_bitflags & SV_BITFLAGS_TVSERVER ) != 0; #ifdef PURE_CHEAT cls.sv_pure = qfalse; #endif // separate the printfs so the server message can have a color Com_Printf( S_COLOR_WHITE "\n" "=====================================\n" ); Com_Printf( S_COLOR_WHITE "%s\n\n", cl.servermessage ); }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; int cmd; char *s; Con_Close(); UI_UpdateConnectionString( "" ); // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd <= 0 ) { break; } if ( cmd == svc_configstring ) { int len; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadString( msg ); len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; if ( cl_shownet->integer == 3 ) { Com_Printf ("%3i: CS# %d %s (%d)\n",msg->readcount, i,s,len); } } else if ( cmd == svc_baseline ) { assert(0); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } // parse serverId and other cvars CL_SystemInfoChanged(); // reinitialize the filesystem if the game directory has changed #if 0 if ( fs_game->modified ) { } #endif // let the client game init and load data cls.state = CA_LOADING; CL_StartHunkUsers(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
void CL_ParseServerInfo (void) { char *str; int i; int nummodels, numsounds; char model_precache[MAX_MODELS][MAX_QPATH]; char sound_precache[MAX_SOUNDS][MAX_QPATH]; Con_DPrintf ("Serverinfo packet received.\n"); // // wipe the client_state_t struct // CL_ClearState (); // parse protocol version number i = MSG_ReadLong (); if (i != PROTOCOL_VERSION) { Con_Printf ("Server returned version %i, not %i", i, PROTOCOL_VERSION); return; } // parse maxclients cl.maxclients = MSG_ReadByte (); if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD) { Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients); return; } cl.scores = (scoreboard_t*) Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); // parse gametype cl.gametype = MSG_ReadByte (); // parse signon message str = MSG_ReadString (); strncpy (cl.levelname, str, sizeof(cl.levelname)-1); // seperate the printfs so the server message can have a color Con_Printf("\n\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\n"); Con_Printf ("%c%s\n", 2, str); // // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // // precache models memset (cl.model_precache, 0, sizeof(cl.model_precache)); for (nummodels=1 ; ; nummodels++) { str = MSG_ReadString (); if (!str[0]) break; if (nummodels==MAX_MODELS) { Con_Printf ("Server sent too many model precaches\n"); return; } strcpy (model_precache[nummodels], str); Mod_TouchModel (str); } // precache sounds memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); for (numsounds=1 ; ; numsounds++) { str = MSG_ReadString (); if (!str[0]) break; if (numsounds==MAX_SOUNDS) { Con_Printf ("Server sent too many sound precaches\n"); return; } strcpy (sound_precache[numsounds], str); S_TouchSound (str); } // // now we try to load everything else until a cache allocation fails // for (i=1 ; i<nummodels ; i++) { cl.model_precache[i] = Mod_ForName (model_precache[i], false); if (cl.model_precache[i] == NULL) { Con_Printf("Model %s not found\n", model_precache[i]); return; } CL_KeepaliveMessage (); } S_BeginPrecaching (); for (i=1 ; i<numsounds ; i++) { cl.sound_precache[i] = S_PrecacheSound (sound_precache[i]); CL_KeepaliveMessage (); } S_EndPrecaching (); // local state cl_entities[0].model = cl.worldmodel = cl.model_precache[1]; R_NewMap (); Hunk_Check (); // make sure nothing is hurt noclip_anglehack = false; // noclip is turned off at start }
/* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage( sizebuf_t *msg ) { char *s; int i, j, cmd; int param1, param2; int bufStart; cls_message_debug.parsing = true; // begin parsing starting_count = BF_GetNumBytesRead( msg ); // updates each frame // parse the message while( 1 ) { if( BF_CheckOverflow( msg )) { Host_Error( "CL_ParseServerMessage: overflow!\n" ); return; } // mark start position bufStart = BF_GetNumBytesRead( msg ); // end of message if( BF_GetNumBitsLeft( msg ) < 8 ) break; cmd = BF_ReadByte( 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: MsgDev( D_INFO, "Disconnected from server\n" ); CL_Drop (); Host_AbortCurrentFrame (); break; case svc_changing: if( BF_ReadOneBit( msg )) { cls.changelevel = true; S_StopAllSounds(); if( cls.demoplayback ) { SCR_BeginLoadingPlaque( cl.background ); cls.changedemo = true; } } else MsgDev( D_INFO, "Server disconnected, reconnecting\n" ); CL_ClearState (); CL_InitEdicts (); // re-arrange edicts if( cls.demoplayback ) { cl.background = (cls.demonum != -1) ? true : false; cls.state = ca_connected; } else cls.state = ca_connecting; cls.connect_time = MAX_HEARTBEAT; // CL_CheckForResend() will fire immediately break; case svc_setview: cl.refdef.viewentity = BF_ReadWord( msg ); break; case svc_sound: CL_ParseSoundPacket( msg, false ); break; case svc_time: // shuffle timestamps cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = BF_ReadFloat( msg ); break; case svc_print: i = BF_ReadByte( msg ); MsgDev( D_INFO, "^6%s", BF_ReadString( msg )); if( i == PRINT_CHAT ) S_StartLocalSound( "common/menu2.wav", VOL_NORM, false ); break; case svc_stufftext: CL_ParseStuffText( msg ); break; case svc_lightstyle: CL_ParseLightStyle( msg ); break; case svc_setangle: CL_ParseSetAngle( msg ); break; case svc_serverdata: Cbuf_Execute(); // make sure any stuffed commands are done CL_ParseServerData( msg ); break; case svc_addangle: CL_ParseAddAngle( msg ); break; case svc_clientdata: CL_ParseClientData( msg ); break; case svc_packetentities: CL_ParsePacketEntities( msg, false ); break; case svc_deltapacketentities: CL_ParsePacketEntities( msg, true ); break; case svc_updatepings: CL_UpdateUserPings( msg ); break; case svc_usermessage: CL_RegisterUserMessage( msg ); break; case svc_particle: CL_ParseParticles( msg ); break; case svc_restoresound: CL_ParseRestoreSoundPacket( msg ); break; case svc_spawnstatic: CL_ParseStaticEntity( msg ); break; case svc_ambientsound: CL_ParseSoundPacket( msg, true ); break; case svc_crosshairangle: CL_ParseCrosshairAngle( msg ); break; case svc_spawnbaseline: CL_ParseBaseline( msg ); break; case svc_temp_entity: CL_ParseTempEntity( msg ); break; case svc_setpause: cl.refdef.paused = ( BF_ReadOneBit( msg ) != 0 ); break; case svc_deltamovevars: CL_ParseMovevars( msg ); break; case svc_customization: CL_ParseCustomization( msg ); break; case svc_centerprint: CL_CenterPrint( BF_ReadString( msg ), 0.25f ); break; case svc_event: CL_ParseEvent( msg ); break; case svc_event_reliable: CL_ParseReliableEvent( msg ); break; case svc_updateuserinfo: CL_UpdateUserinfo( msg ); break; case svc_intermission: cl.refdef.intermission = true; break; case svc_modelindex: CL_PrecacheModel( msg ); break; case svc_soundindex: CL_PrecacheSound( msg ); break; case svc_soundfade: CL_ParseSoundFade( msg ); break; case svc_cdtrack: param1 = BF_ReadByte( msg ); param1 = bound( 1, param1, MAX_CDTRACKS ); // tracknum param2 = BF_ReadByte( msg ); param2 = bound( 1, param2, MAX_CDTRACKS ); // loopnum S_StartBackgroundTrack( clgame.cdtracks[param1-1], clgame.cdtracks[param2-1], 0 ); break; case svc_serverinfo: CL_ServerInfo( msg ); break; case svc_eventindex: CL_PrecacheEvent( msg ); break; case svc_deltatable: Delta_ParseTableField( msg ); break; case svc_weaponanim: param1 = BF_ReadByte( msg ); // iAnim param2 = BF_ReadByte( msg ); // body CL_WeaponAnim( param1, param2 ); break; case svc_bspdecal: CL_ParseStaticDecal( msg ); break; case svc_roomtype: param1 = BF_ReadShort( msg ); Cvar_SetFloat( "room_type", param1 ); break; case svc_chokecount: i = BF_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; case svc_resourcelist: CL_ParseResourceList( msg ); break; case svc_director: CL_ParseDirector( msg ); break; case svc_studiodecal: CL_ParseStudioDecal( msg ); break; case svc_querycvarvalue: CL_ParseCvarValue( msg ); break; case svc_querycvarvalue2: CL_ParseCvarValue2( msg ); break; default: CL_ParseUserMessage( msg, cmd ); break; } } cls_message_debug.parsing = 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, starting_count, msg ); } else if( cls.state != ca_active ) { CL_WriteDemoMessage( true, starting_count, msg ); } } }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } memset( &nullstate, 0, sizeof( nullstate ) ); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong( msg ); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); // parse serverId and other cvars CL_SystemInfoChanged(); // reinitialize the filesystem if the game directory has changed FS_ConditionalRestart( clc.checksumFeed ); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; char oldGame[MAX_QPATH]; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } Com_Memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong(msg); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); // save old gamedir Cvar_VariableStringBuffer("fs_game", oldGame, sizeof(oldGame)); // parse useful values out of CS_SERVERINFO CL_ParseServerInfo(); // parse serverId and other cvars CL_SystemInfoChanged(); // stop recording now so the demo won't have an unnecessary level load at the end. if(cl_autoRecordDemo->integer && clc.demorecording) CL_StopRecord_f(); // reinitialize the filesystem if the game directory has changed if(!cl_oldGameSet && (Cvar_Flags("fs_game") & CVAR_MODIFIED)) { cl_oldGameSet = qtrue; Q_strncpyz(cl_oldGame, oldGame, sizeof(cl_oldGame)); } FS_ConditionalRestart(clc.checksumFeed, qfalse); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
void CL_ParseServerData(void) { extern cvar_t *fs_gamedirvar; char *str; int i; Com_DPrintf("Serverdata packet received.\n"); /* wipe the client_state_t struct */ CL_ClearState(); cls.state = ca_connected; /* parse protocol version number */ i = MSG_ReadLong(&net_message); cls.serverProtocol = i; /* another demo hack */ if (Com_ServerState() && (PROTOCOL_VERSION == 34)) { } else if (i != PROTOCOL_VERSION) { Com_Error(ERR_DROP, "Server returned version %i, not %i", i, PROTOCOL_VERSION); } cl.servercount = MSG_ReadLong(&net_message); cl.attractloop = MSG_ReadByte(&net_message); /* game directory */ str = MSG_ReadString(&net_message); Q_strlcpy(cl.gamedir, str, sizeof(cl.gamedir)); /* set gamedir */ if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string))) { Cvar_Set("game", str); } /* parse player entity number */ cl.playernum = MSG_ReadShort(&net_message); /* get the full level name */ str = MSG_ReadString(&net_message); if (cl.playernum == -1) { /* playing a cinematic or showing a pic, not a level */ SCR_PlayCinematic(str); } else { /* seperate the printfs so the server * message can have a color */ Com_Printf("\n\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\n"); Com_Printf("%c%s\n", 2, str); /* need to prep refresh at next oportunity */ cl.refresh_prepped = false; } }
/* ================ SV_SpawnServer Change the server to a new map, taking all connected clients along with it. This is only called from the SV_Map_f() function. ================ */ void SV_SpawnServer (char *mapname, qbool devmap, char* entityfile) { extern func_t ED_FindFunctionOffset (char *name); edict_t *ent; int i; extern cvar_t sv_loadentfiles, sv_loadentfiles_dir; char *entitystring; char oldmap[MAP_NAME_LEN]; extern qbool sv_allow_cheats; extern cvar_t sv_cheats, sv_paused, sv_bigcoords; #ifndef SERVERONLY extern void CL_ClearState (void); #endif // store old map name snprintf (oldmap, MAP_NAME_LEN, "%s", sv.mapname); Con_DPrintf ("SpawnServer: %s\n",mapname); #ifndef SERVERONLY // As client+server we do it here. // As serveronly we do it in NET_Init(). NET_InitServer(); #endif SV_SaveSpawnparms (); SV_LoadAccounts(); #ifdef USE_PR2 // remove bot clients for (i = 0; i < MAX_CLIENTS; i++) { if( sv_vm && svs.clients[i].isBot ) { svs.clients[i].old_frags = 0; svs.clients[i].edict->v.frags = 0.0; svs.clients[i].name[0] = 0; svs.clients[i].state = cs_free; Info_RemoveAll(&svs.clients[i]._userinfo_ctx_); Info_RemoveAll(&svs.clients[i]._userinfoshort_ctx_); SV_FullClientUpdate(&svs.clients[i], &sv.reliable_datagram); svs.clients[i].isBot = 0; } } #endif // Shutdown game. PR_GameShutDown(); PR_UnLoadProgs(); svs.spawncount++; // any partially connected client will be restarted #ifndef SERVERONLY com_serveractive = false; #endif sv.state = ss_dead; sv.paused = false; Cvar_SetROM(&sv_paused, "0"); Host_ClearMemory(); #ifdef FTE_PEXT_FLOATCOORDS if (sv_bigcoords.value) { msg_coordsize = 4; msg_anglesize = 2; } else { msg_coordsize = 2; msg_anglesize = 1; } #endif if ((int)coop.value) Cvar_Set (&deathmatch, "0"); current_skill = (int) (skill.value + 0.5); if (current_skill < 0) current_skill = 0; Cvar_Set (&skill, va("%d", current_skill)); if (current_skill > 3) current_skill = 3; if ((sv_cheats.value || devmap) && !sv_allow_cheats) { sv_allow_cheats = true; Info_SetValueForStarKey (svs.info, "*cheats", "ON", MAX_SERVERINFO_STRING); } else if ((!sv_cheats.value && !devmap) && sv_allow_cheats) { sv_allow_cheats = false; Info_SetValueForStarKey (svs.info, "*cheats", "", MAX_SERVERINFO_STRING); } // wipe the entire per-level structure // NOTE: this also set sv.mvdrecording to false, so calling SV_MVD_Record() at end of function memset (&sv, 0, sizeof(sv)); sv.datagram.maxsize = sizeof(sv.datagram_buf); sv.datagram.data = sv.datagram_buf; sv.datagram.allowoverflow = true; sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); sv.reliable_datagram.data = sv.reliable_datagram_buf; sv.multicast.maxsize = sizeof(sv.multicast_buf); sv.multicast.data = sv.multicast_buf; sv.signon.maxsize = sizeof(sv.signon_buffers[0]); sv.signon.data = sv.signon_buffers[0]; sv.num_signon_buffers = 1; sv.time = 1.0; // load progs to get entity field count // which determines how big each edict is // and allocate edicts PR_LoadProgs (); #ifdef WITH_NQPROGS PR_InitPatchTables(); #endif PR_InitProg(); for (i = 0; i < MAX_EDICTS; i++) { ent = EDICT_NUM(i); ent->e = &sv.sv_edicts[i]; // assigning ->e field in each edict_t ent->e->entnum = i; ent->e->area.ed = ent; // yeah, pretty funny, but this help to find which edict_t own this area (link_t) } fofs_items2 = ED_FindFieldOffset ("items2"); // ZQ_ITEMS2 extension fofs_maxspeed = ED_FindFieldOffset ("maxspeed"); fofs_gravity = ED_FindFieldOffset ("gravity"); fofs_movement = ED_FindFieldOffset ("movement"); fofs_vw_index = ED_FindFieldOffset ("vw_index"); fofs_hideentity = ED_FindFieldOffset ("hideentity"); fofs_trackent = ED_FindFieldOffset ("trackent"); // find optional QC-exported functions. // we have it here, so we set it to NULL in case of PR2 progs. mod_SpectatorConnect = ED_FindFunctionOffset ("SpectatorConnect"); mod_SpectatorThink = ED_FindFunctionOffset ("SpectatorThink"); mod_SpectatorDisconnect = ED_FindFunctionOffset ("SpectatorDisconnect"); mod_ChatMessage = ED_FindFunctionOffset ("ChatMessage"); mod_UserInfo_Changed = ED_FindFunctionOffset ("UserInfo_Changed"); mod_ConsoleCmd = ED_FindFunctionOffset ("ConsoleCmd"); mod_UserCmd = ED_FindFunctionOffset ("UserCmd"); mod_localinfoChanged = ED_FindFunctionOffset ("localinfoChanged"); GE_ClientCommand = ED_FindFunctionOffset ("GE_ClientCommand"); GE_PausedTic = ED_FindFunctionOffset ("GE_PausedTic"); GE_ShouldPause = ED_FindFunctionOffset ("GE_ShouldPause"); // leave slots at start for clients only sv.num_edicts = MAX_CLIENTS+1; for (i=0 ; i<MAX_CLIENTS ; i++) { ent = EDICT_NUM(i+1); // restore client name. ent->v.netname = PR_SetString(svs.clients[i].name); // reserve edict. svs.clients[i].edict = ent; //ZOID - make sure we update frags right svs.clients[i].old_frags = 0; } // fill sv.mapname and sv.modelname with new map name strlcpy (sv.mapname, mapname, sizeof(sv.mapname)); snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", sv.mapname); #ifndef SERVERONLY // set cvar Cvar_ForceSet (&host_mapname, mapname); #endif if (!(sv.worldmodel = CM_LoadMap (sv.modelname, false, &sv.map_checksum, &sv.map_checksum2))) // true if bad map { Con_Printf ("Cant load map %s, falling back to %s\n", mapname, oldmap); // fill mapname, sv.mapname and sv.modelname with old map name strlcpy (sv.mapname, oldmap, sizeof(sv.mapname)); snprintf (sv.modelname, sizeof(sv.modelname), "maps/%s.bsp", sv.mapname); mapname = oldmap; // and re-load old map sv.worldmodel = CM_LoadMap (sv.modelname, false, &sv.map_checksum, &sv.map_checksum2); // this should never happen if (!sv.worldmodel) SV_Error ("CM_LoadMap: bad map"); } sv.map_checksum2 = Com_TranslateMapChecksum (sv.mapname, sv.map_checksum2); SV_ClearWorld (); // clear physics interaction links #ifdef USE_PR2 if ( sv_vm ) { sv.sound_precache[0] = ""; sv.model_precache[0] = ""; } else #endif { sv.sound_precache[0] = pr_strings; sv.model_precache[0] = pr_strings; } sv.model_precache[1] = sv.modelname; sv.models[1] = sv.worldmodel; for (i=1 ; i< CM_NumInlineModels() ; i++) { sv.model_precache[1+i] = localmodels[i]; sv.models[i+1] = CM_InlineModel (localmodels[i]); } //check player/eyes models for hacks sv.model_player_checksum = SV_CheckModel("progs/player.mdl"); sv.model_newplayer_checksum = SV_CheckModel("progs/newplayer.mdl"); sv.eyes_player_checksum = SV_CheckModel("progs/eyes.mdl"); // // spawn the rest of the entities on the map // // precache and static commands can be issued during // map initialization sv.state = ss_loading; #ifndef SERVERONLY com_serveractive = true; #endif ent = EDICT_NUM(0); ent->e->free = false; ent->v.model = PR_SetString(sv.modelname); ent->v.modelindex = 1; // world model ent->v.solid = SOLID_BSP; ent->v.movetype = MOVETYPE_PUSH; // information about the server ent->v.netname = PR_SetString(VersionStringFull()); ent->v.targetname = PR_SetString(SERVER_NAME); ent->v.impulse = VERSION_NUM; ent->v.items = pr_numbuiltins - 1; PR_GLOBAL(mapname) = PR_SetString(sv.mapname); // serverflags are for cross level information (sigils) PR_GLOBAL(serverflags) = svs.serverflags; if (pr_nqprogs) { pr_globals[35] = deathmatch.value; pr_globals[36] = coop.value; pr_globals[37] = teamplay.value; NQP_Reset (); } if (pr_nqprogs) { // register the cvars that NetQuake provides for mod use const char **var, *nqcvars[] = {"gamecfg", "scratch1", "scratch2", "scratch3", "scratch4", "saved1", "saved2", "saved3", "saved4", "savedgamecfg", "temp1", NULL}; for (var = nqcvars; *var; var++) Cvar_Create((char *)/*stupid const warning*/ *var, "0", 0); } // run the frame start qc function to let progs check cvars if (!pr_nqprogs) SV_ProgStartFrame (); // ********* External Entity support (.ent file(s) in gamedir/maps) pinched from ZQuake ********* // load and spawn all other entities entitystring = NULL; if ((int)sv_loadentfiles.value) { char ent_path[1024] = {0}; if (!entityfile || !entityfile[0]) entityfile = sv.mapname; // first try maps/sv_loadentfiles_dir/ if (sv_loadentfiles_dir.string[0]) { snprintf(ent_path, sizeof(ent_path), "maps/%s/%s.ent", sv_loadentfiles_dir.string, entityfile); entitystring = (char *) FS_LoadHunkFile(ent_path, NULL); } // try maps/ if not loaded yet. if (!entitystring) { snprintf(ent_path, sizeof(ent_path), "maps/%s.ent", entityfile); entitystring = (char *) FS_LoadHunkFile(ent_path, NULL); } if (entitystring) { Con_DPrintf ("Using entfile %s\n", ent_path); } } if (!entitystring) { entitystring = CM_EntityString(); } PR_LoadEnts(entitystring); // ********* End of External Entity support code ********* // look up some model indexes for specialized message compression SV_FindModelNumbers (); // all spawning is completed, any further precache statements // or prog writes to the signon message are errors sv.state = ss_active; // run two frames to allow everything to settle SV_Physics (); sv.time += 0.1; SV_Physics (); sv.time += 0.1; sv.old_time = sv.time; // save movement vars SV_SetMoveVars(); // create a baseline for more efficient communications SV_CreateBaseline (); sv.signon_buffer_size[sv.num_signon_buffers-1] = sv.signon.cursize; Info_SetValueForKey (svs.info, "map", sv.mapname, MAX_SERVERINFO_STRING); // calltimeofday. { extern void PF_calltimeofday (void); pr_global_struct->time = sv.time; pr_global_struct->self = 0; PF_calltimeofday(); } Con_DPrintf ("Server spawned.\n"); // we change map - clear whole demo struct and sent initial state to all dest if any (for QTV only I thought) SV_MVD_Record(NULL, true); #ifndef SERVERONLY CL_ClearState (); #endif }
/* ================== CL_ParseServerInfo ================== */ static void CL_ParseServerInfo (void) { const char *str; int i; int nummodels, numsounds; char model_precache[MAX_MODELS][MAX_QPATH]; char sound_precache[MAX_SOUNDS][MAX_QPATH]; // rjr edict_t *ent; Con_DPrintf ("Serverinfo packet received.\n"); // // wipe the client_state_t struct // CL_ClearState (); // parse protocol version number cl_protocol = MSG_ReadLong (); switch (cl_protocol) { case PROTOCOL_RAVEN_111: case PROTOCOL_RAVEN_112: case PROTOCOL_UQE_113: Con_Printf ("\nServer using protocol %i\n", cl_protocol); break; default: Con_Printf ("\nServer returned version %i, not %i or %i\n", cl_protocol, PROTOCOL_RAVEN_112, PROTOCOL_UQE_113); return; } // parse maxclients cl.maxclients = MSG_ReadByte (); if (cl.maxclients < 1 || cl.maxclients > MAX_CLIENTS) { Con_Printf("Bad maxclients (%d) from server\n", cl.maxclients); return; } cl.scores = (scoreboard_t *) Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); // parse gametype cl.gametype = MSG_ReadByte (); if (cl.gametype == GAME_DEATHMATCH && cl_protocol > PROTOCOL_RAVEN_111) sv_kingofhill = MSG_ReadShort (); // parse signon message str = MSG_ReadString (); q_strlcpy (cl.levelname, str, sizeof(cl.levelname)); // seperate the printfs so the server message can have a color Con_Printf("\n\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\n"); Con_Printf ("%c%s\n", 2, str); // // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // // precache models memset (cl.model_precache, 0, sizeof(cl.model_precache)); for (nummodels = 1 ; ; nummodels++) { str = MSG_ReadString (); if (!str[0]) break; if (nummodels == MAX_MODELS) { Con_Printf ("Server sent too many model precaches\n"); return; } q_strlcpy (model_precache[nummodels], str, MAX_QPATH); Mod_TouchModel (str); } // precache sounds memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); for (numsounds = 1 ; ; numsounds++) { str = MSG_ReadString (); if (!str[0]) break; if (numsounds == MAX_SOUNDS) { Con_Printf ("Server sent too many sound precaches\n"); return; } q_strlcpy (sound_precache[numsounds], str, MAX_QPATH); S_TouchSound (str); } // // now we try to load everything else until a cache allocation fails // if (precache.integer) { total_loading_size = nummodels + numsounds; current_loading_size = 1; loading_stage = 2; } // copy the naked name of the map file to the cl structure COM_StripExtension (COM_SkipPath(model_precache[1]), cl.mapname, sizeof(cl.mapname)); //always precache the world!!! cl.model_precache[1] = Mod_ForName (model_precache[1], false); for (i = 2; i < nummodels; i++) { if (precache.integer) { cl.model_precache[i] = Mod_ForName (model_precache[i], false); current_loading_size++; D_ShowLoadingSize(); } else cl.model_precache[i] = (model_t *)Mod_FindName (model_precache[i]); if (cl.model_precache[i] == NULL) { Host_Error("Model %s not found", model_precache[i]); return; } CL_KeepaliveMessage (); } player_models[0] = (model_t *)Mod_FindName ("models/paladin.mdl"); // Note: old demo doesnt have necro and crusader classes. add // a GAME_OLD_DEMO flag check ? player_models[1] = (model_t *)Mod_FindName ("models/crusader.mdl"); player_models[2] = (model_t *)Mod_FindName ("models/necro.mdl"); player_models[3] = (model_t *)Mod_FindName ("models/assassin.mdl"); if (gameflags & GAME_PORTALS) player_models[4] = (model_t *)Mod_FindName ("models/succubus.mdl"); S_BeginPrecaching (); for (i = 1; i < numsounds; i++) { cl.sound_precache[i] = S_PrecacheSound (sound_precache[i]); if (precache.integer) { current_loading_size++; D_ShowLoadingSize(); } CL_KeepaliveMessage (); } S_EndPrecaching (); total_loading_size = 0; loading_stage = 0; // local state cl_entities[0].model = cl.worldmodel = cl.model_precache[1]; R_NewMap (); if (!sv.active) { PR_LoadStrings(); } PR_LoadPuzzleStrings(); // mission pack, objectives strings if (gameflags & GAME_PORTALS) PR_LoadInfoStrings(); Hunk_Check (); // make sure nothing is hurt // we connected to the server, make sure the mouse is going - S.A. menu_disabled_mouse = false; IN_ActivateMouse(); }
/* ================== NQD_ParseServerData ================== */ static void NQD_ParseServerData (void) { char *str; int i; int nummodels, numsounds; char mapname[MAX_QPATH]; int cs2; qbool gpl_map; #ifdef GLQUAKE extern qbool r_gpl_map; #endif Com_DPrintf ("Serverdata packet received.\n"); // // wipe the client_state_t struct // CL_ClearState (); // parse protocol version number i = MSG_ReadLong (); if (i != NQ_PROTOCOL_VERSION) Host_Error ("Server returned version %i, not %i", i, NQ_PROTOCOL_VERSION); // parse maxclients nq_maxclients = MSG_ReadByte (); if (nq_maxclients < 1 || nq_maxclients > NQ_MAX_CLIENTS) Host_Error ("Bad maxclients (%u) from server", nq_maxclients); // parse gametype cl.gametype = MSG_ReadByte() ? GAME_DEATHMATCH : GAME_COOP; // parse signon message str = MSG_ReadString (); strlcpy (cl.levelname, str, sizeof(cl.levelname)); // separate the printfs so the server message can have a color Com_Printf("\n\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\n"); Com_Printf ("%c%s\n", 2, str); // // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // // precache models for (nummodels=1 ; ; nummodels++) { str = MSG_ReadString (); if (!str[0]) break; if (nummodels == MAX_MODELS) Host_Error ("Server sent too many model precaches"); strlcpy (cl.model_name[nummodels], str, sizeof(cl.model_name[0])); Mod_TouchModel (str); } // precache sounds for (numsounds=1 ; ; numsounds++) { str = MSG_ReadString (); if (!str[0]) break; if (numsounds == MAX_SOUNDS) Host_Error ("Server sent too many sound precaches"); strlcpy (cl.sound_name[numsounds], str, sizeof(cl.sound_name[0])); S_TouchSound (str); } // // now we try to load everything else until a cache allocation fails // cl.clipmodels[1] = CM_LoadMap (cl.model_name[1], true, NULL, &cl.map_checksum2); COM_StripExtension (COM_SkipPath(cl.model_name[1]), mapname); cs2 = Com_TranslateMapChecksum (mapname, cl.map_checksum2); gpl_map = (cl.map_checksum2 != cs2); cl.map_checksum2 = cs2; #ifdef GLQUAKE r_gpl_map = gpl_map; #endif for (i = 1; i < nummodels; i++) { cl.model_precache[i] = Mod_ForName (cl.model_name[i], false, i == 1); if (cl.model_precache[i] == NULL) Host_Error ("Model %s not found", cl.model_name[i]); if (cl.model_name[i][0] == '*') cl.clipmodels[i] = CM_InlineModel(cl.model_name[i]); } for (i=1 ; i<numsounds ; i++) { cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i]); } // local state if (!cl.model_precache[1]) Host_Error ("NQD_ParseServerData: NULL worldmodel"); COM_StripExtension (COM_SkipPath (cl.model_name[1]), mapname); Cvar_ForceSet (&host_mapname, mapname); CL_ClearParticles (); CL_FindModelNumbers (); R_NewMap (cl.model_precache[1]); TP_NewMap (); Hunk_Check (); // make sure nothing is hurt nq_signon = 0; nq_num_entities = 0; nq_drawpings = false; // unless we have the ProQuake extension cl.servertime_works = true; cl.allow_fbskins = true; r_refdef2.allow_cheats = true; // why not cls.state = ca_onserver; }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } const char* str = MSG_ReadBigString( msg ); std::string s = str; cl.gameState[i] = str; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } memset( &nullstate, 0, sizeof( nullstate ) ); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong( msg ); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); // parse serverId and other cvars CL_SystemInfoChanged(); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }
/* ==================== CL_Init ==================== */ void CL_Init( void ) { Com_Printf( "----- Client Initialization -----\n" ); Con_Init (); CL_ClearState (); cls.state = CA_DISCONNECTED; // no longer CA_UNINITIALIZED cls.keyCatchers = KEYCATCH_CONSOLE; cls.realtime = 0; cls.realtimeFraction=0.0f; // fraction of a msec accumulated CL_InitInput (); #ifndef _XBOX // No terrain on Xbox RM_InitTerrain(); #endif // // register our variables // cl_noprint = Cvar_Get( "cl_noprint", "0", 0 ); cl_timeout = Cvar_Get ("cl_timeout", "125", 0); cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP ); cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP ); cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP ); cl_newClock = Cvar_Get ("cl_newClock", "1", 0); cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP ); cl_avidemo = Cvar_Get ("cl_avidemo", "0", 0); cl_pano = Cvar_Get ("pano", "0", 0); cl_panoNumShots= Cvar_Get ("panoNumShots", "10", CVAR_ARCHIVE); cl_skippingcin = Cvar_Get ("skippingCinematic", "0", CVAR_ROM); cl_endcredits = Cvar_Get ("cg_endcredits", "0", 0); cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ARCHIVE); cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE); cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", CVAR_ARCHIVE); cl_maxpackets = Cvar_Get ("cl_maxpackets", "30", CVAR_ARCHIVE ); cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE ); cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE); cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE); cl_mouseAccel = Cvar_Get ("cl_mouseAccel", "0", CVAR_ARCHIVE); cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE ); cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0); cl_ingameVideo = Cvar_Get ("cl_ingameVideo", "1", CVAR_ARCHIVE); cl_VideoQuality = Cvar_Get ("cl_VideoQuality", "0", CVAR_ARCHIVE); cl_VidFadeUp = Cvar_Get ("cl_VidFadeUp", "1", CVAR_TEMP); cl_VidFadeDown = Cvar_Get ("cl_VidFadeDown", "1", CVAR_TEMP); cl_framerate = Cvar_Get ("cl_framerate", "0", CVAR_TEMP); cl_thumbStickMode = Cvar_Get ("ui_thumbStickMode", "0", CVAR_ARCHIVE); // init autoswitch so the ui will have it correctly even // if the cgame hasn't been started Cvar_Get ("cg_autoswitch", "1", CVAR_ARCHIVE); m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE); m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE); m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE); m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE); m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE); #ifdef _XBOX cl_mapname = Cvar_Get ("cl_mapname", "t3_bounty", CVAR_TEMP); #endif cl_updateInfoString = Cvar_Get( "cl_updateInfoString", "", CVAR_ROM ); // ~ and `, as keys and characters cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60", CVAR_ARCHIVE); // userinfo Cvar_Get ("name", "Jaden", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("sex", "f", CVAR_USERINFO | CVAR_ARCHIVE | CVAR_SAVEGAME | CVAR_NORESTART ); Cvar_Get ("snd", "jaden_fmle", CVAR_USERINFO | CVAR_ARCHIVE | CVAR_SAVEGAME | CVAR_NORESTART );//UI_SetSexandSoundForModel changes to match sounds.cfg for model Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_SAVEGAME | CVAR_NORESTART); // // register our commands // Cmd_AddCommand ("cmd", CL_ForwardToServer_f); Cmd_AddCommand ("configstrings", CL_Configstrings_f); Cmd_AddCommand ("clientinfo", CL_Clientinfo_f); Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f); Cmd_AddCommand ("vid_restart", CL_Vid_Restart_f); Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("cinematic", CL_PlayCinematic_f); Cmd_AddCommand ("ingamecinematic", CL_PlayInGameCinematic_f); Cmd_AddCommand ("uimenu", CL_GenericMenu_f); Cmd_AddCommand ("datapad", CL_DataPad_f); Cmd_AddCommand ("endscreendissolve", CL_EndScreenDissolve_f); #ifdef _IMMERSION Cmd_AddCommand ("ff_restart", CL_FF_Restart_f); #endif // _IMMERSION CL_InitRef(); CL_StartHunkUsers(); SCR_Init (); Cbuf_Execute (); Cvar_Set( "cl_running", "1" ); #ifdef _XBOX Com_Printf( "Initializing Cinematics...\n"); CIN_Init(); #endif Com_Printf( "----- Client Initialization Complete -----\n" ); }
/* ==================== CL_PlayDemo_f playdemo <demoname> ==================== */ void CL_PlayDemo_f( void ) { string filename; string demoname; int i; if( Cmd_Argc() != 2 ) { Msg( "Usage: playdemo <demoname>\n" ); return; } if( cls.demoplayback ) { CL_StopPlayback(); } if( cls.demorecording ) { Msg( "Can't playback during demo record.\n"); return; } Q_strncpy( demoname, Cmd_Argv( 1 ), sizeof( demoname ) - 1 ); Q_snprintf( filename, sizeof( filename ), "demos/%s.dem", demoname ); if( !FS_FileExists( filename, true )) { MsgDev( D_ERROR, "couldn't open %s\n", filename ); cls.demonum = -1; // stop demo loop return; } cls.demofile = FS_Open( filename, "rb", true ); Q_strncpy( cls.demoname, demoname, sizeof( cls.demoname )); Q_strncpy( menu.globals->demoname, demoname, sizeof( menu.globals->demoname )); // read in the m_DemoHeader FS_Read( cls.demofile, &demo.header, sizeof( demoheader_t )); if( demo.header.id != IDEMOHEADER ) { MsgDev( D_ERROR, "%s is not a demo file\n", filename ); FS_Close( cls.demofile ); cls.demofile = NULL; cls.demonum = -1; // stop demo loop return; } if( demo.header.net_protocol != PROTOCOL_VERSION || demo.header.dem_protocol != DEMO_PROTOCOL ) { MsgDev( D_ERROR, "demo protocol outdated\n" "Demo file protocols Network(%i), Demo(%i)\n" "Server protocol is at Network(%i), Demo(%i)\n", demo.header.net_protocol, demo.header.dem_protocol, PROTOCOL_VERSION, DEMO_PROTOCOL ); FS_Close( cls.demofile ); cls.demofile = NULL; cls.demonum = -1; // stop demo loop return; } // now read in the directory structure. FS_Seek( cls.demofile, demo.header.directory_offset, SEEK_SET ); FS_Read( cls.demofile, &demo.directory.numentries, sizeof( int )); if( demo.directory.numentries < 1 || demo.directory.numentries > 1024 ) { MsgDev( D_ERROR, "demo had bogus # of directory entries: %i\n", demo.directory.numentries ); FS_Close( cls.demofile ); cls.demofile = NULL; cls.demonum = -1; // stop demo loop cls.changedemo = false; return; } if( cls.changedemo ) { S_StopAllSounds(); SCR_BeginLoadingPlaque( false ); CL_ClearState (); CL_InitEdicts (); // re-arrange edicts } else { // NOTE: at this point demo is still valid CL_Disconnect(); Host_ShutdownServer(); Con_Close(); UI_SetActiveMenu( false ); } // allocate demo entries demo.directory.entries = Mem_Alloc( cls.mempool, sizeof( demoentry_t ) * demo.directory.numentries ); for( i = 0; i < demo.directory.numentries; i++ ) { FS_Read( cls.demofile, &demo.directory.entries[i], sizeof( demoentry_t )); } demo.entryIndex = 0; demo.entry = &demo.directory.entries[demo.entryIndex]; FS_Seek( cls.demofile, demo.entry->offset, SEEK_SET ); cls.demoplayback = true; cls.state = ca_connected; cl.background = (cls.demonum != -1) ? true : false; demo.starttime = CL_GetDemoPlaybackClock(); // for determining whether to read another message Netchan_Setup( NS_CLIENT, &cls.netchan, net_from, net_qport->integer ); demo.framecount = 0; cls.lastoutgoingcommand = -1; cls.nextcmdtime = host.realtime; // g-cont. is this need? Q_strncpy( cls.servername, demoname, sizeof( cls.servername )); // begin a playback demo }
/* ================== CL_ParseServerData ================== */ void CL_ParseServerData( sizebuf_t *msg ) { string gamefolder; qboolean background; int i; MsgDev( D_NOTE, "Serverdata packet received.\n" ); 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 = BF_ReadLong( msg ); cls.serverProtocol = i; if( i != PROTOCOL_VERSION ) Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION ); cl.servercount = BF_ReadLong( msg ); cl.checksum = BF_ReadLong( msg ); cl.playernum = BF_ReadByte( msg ); cl.maxclients = BF_ReadByte( msg ); clgame.maxEntities = BF_ReadWord( msg ); clgame.maxEntities = bound( 600, clgame.maxEntities, 4096 ); Q_strncpy( clgame.mapname, BF_ReadString( msg ), MAX_STRING ); Q_strncpy( clgame.maptitle, BF_ReadString( msg ), MAX_STRING ); background = BF_ReadOneBit( msg ); Q_strncpy( gamefolder, BF_ReadString( msg ), MAX_STRING ); host.features = (uint)BF_ReadLong( msg ); if( cl.maxclients > 1 && host.developer < 1 ) host.developer++; // 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", CVAR_READ_ONLY ); else Cvar_FullSet( "cl_background", "0", CVAR_READ_ONLY ); if( !cls.changelevel ) { // continue playing if we are changing level S_StopBackgroundTrack (); } #if 0 // NOTE: this is not tested as well. Use with precaution CL_ChangeGame( gamefolder, false ); #endif if( !cls.changedemo ) UI_SetActiveMenu( cl.background ); cl.refdef.viewentity = cl.playernum + 1; // always keep viewent an actual menu.globals->maxClients = cl.maxclients; Q_strncpy( menu.globals->maptitle, clgame.maptitle, sizeof( menu.globals->maptitle )); if( cl.maxclients > 1 && r_decals->value > mp_decals->value ) Cvar_SetFloat( "r_decals", mp_decals->value ); 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_SetFloat( "scr_loading", 0.0f ); // reset progress bar if(( cl_allow_levelshots->integer && !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) } if( scr_dark->integer ) { screenfade_t *sf = &clgame.fade; client_textmessage_t *title; title = CL_TextMessageGet( "GAMETITLE" ); if( title ) { // get settings from titles.txt sf->fadeEnd = title->holdtime + title->fadeout; sf->fadeReset = title->fadeout; } else sf->fadeEnd = sf->fadeReset = 4.0f; sf->fadeFlags = FFADE_IN; sf->fader = sf->fadeg = sf->fadeb = 0; sf->fadealpha = 255; sf->fadeSpeed = (float)sf->fadealpha / sf->fadeReset; sf->fadeReset += cl.time; sf->fadeEnd += sf->fadeReset; Cvar_SetFloat( "v_dark", 0.0f ); } // need to prep refresh at next oportunity cl.video_prepped = false; cl.audio_prepped = false; Q_memset( &clgame.movevars, 0, sizeof( clgame.movevars )); Q_memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars )); }
/* ================== CL_ParseServerInfo ================== */ void CL_ParseServerInfo (void) { const char *str; int i; int nummodels, numsounds; char model_precache[MAX_MODELS][MAX_QPATH]; char sound_precache[MAX_SOUNDS][MAX_QPATH]; Con_DPrintf ("Serverinfo packet received.\n"); // // wipe the client_state_t struct // CL_ClearState (); // parse protocol version number i = MSG_ReadLong (); //johnfitz -- support multiple protocols if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE) { Con_Printf ("\n"); //becuase there's no newline after serverinfo print Host_Error ("Server returned version %i, not %i or %i\n", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE); } cl.protocol = i; //johnfitz // parse maxclients cl.maxclients = MSG_ReadByte (); if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD) { Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients); return; } cl.scores = (scoreboard_t *) Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); // parse gametype cl.gametype = MSG_ReadByte (); // parse signon message str = MSG_ReadString (); q_strlcpy (cl.levelname, str, sizeof(cl.levelname)); // seperate the printfs so the server message can have a color Con_Printf ("\n%s\n", Con_Quakebar(40)); //johnfitz Con_Printf ("%c%s\n", 2, str); //johnfitz -- tell user which protocol this is Con_Printf ("Using protocol %i\n", i); // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // precache models memset (cl.model_precache, 0, sizeof(cl.model_precache)); for (nummodels = 1 ; ; nummodels++) { str = MSG_ReadString (); if (!str[0]) break; if (nummodels==MAX_MODELS) { Con_Printf ("Server sent too many model precaches\n"); return; } q_strlcpy (model_precache[nummodels], str, MAX_QPATH); Mod_TouchModel (str); } //johnfitz -- check for excessive models if (nummodels >= 256) Con_Warning ("%i models exceeds standard limit of 256.\n", nummodels); //johnfitz // precache sounds memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); for (numsounds = 1 ; ; numsounds++) { str = MSG_ReadString (); if (!str[0]) break; if (numsounds==MAX_SOUNDS) { Con_Printf ("Server sent too many sound precaches\n"); return; } q_strlcpy (sound_precache[numsounds], str, MAX_QPATH); S_TouchSound (str); } //johnfitz -- check for excessive sounds if (numsounds >= 256) Con_Warning ("%i sounds exceeds standard limit of 256.\n", numsounds); //johnfitz // // now we try to load everything else until a cache allocation fails // // copy the naked name of the map file to the cl structure -- O.S COM_StripExtension (COM_SkipPath(model_precache[1]), cl.mapname, sizeof(cl.mapname)); for (i = 1; i < nummodels; i++) { cl.model_precache[i] = Mod_ForName (model_precache[i], false); if (cl.model_precache[i] == NULL) { Con_Printf("Model %s not found\n", model_precache[i]); return; } CL_KeepaliveMessage (); } S_BeginPrecaching (); for (i = 1; i < numsounds; i++) { cl.sound_precache[i] = S_PrecacheSound (sound_precache[i]); CL_KeepaliveMessage (); } S_EndPrecaching (); // local state cl_entities[0].model = cl.worldmodel = cl.model_precache[1]; R_NewMap (); //johnfitz -- clear out string; we don't consider identical //messages to be duplicates if the map has changed in between con_lastcenterstring[0] = 0; //johnfitz Hunk_Check (); // make sure nothing is hurt noclip_anglehack = false; // noclip is turned off at start warn_about_nehahra_protocol = true; //johnfitz -- warn about nehahra protocol hack once per server connection //johnfitz -- reset developer stats memset(&dev_stats, 0, sizeof(dev_stats)); memset(&dev_peakstats, 0, sizeof(dev_peakstats)); memset(&dev_overflows, 0, sizeof(dev_overflows)); }
/* ================== CL_ParseServerInfo ================== */ void CL_ParseServerInfo(void) { char *level; const char *mapname; int i, maxlen; int nummodels, numsounds; char model_precache[MAX_MODELS][MAX_QPATH]; char sound_precache[MAX_SOUNDS][MAX_QPATH]; Con_DPrintf("Serverinfo packet received.\n"); // // wipe the client_state_t struct // CL_ClearState(); // parse protocol version number i = MSG_ReadLong(); if (!Protocol_Known(i)) { Con_Printf("Server returned unknown protocol version %i\n", i); return; } cl.protocol = i; // parse maxclients cl.maxclients = MSG_ReadByte(); if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD) { Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients); return; } cl.players = Hunk_AllocName(cl.maxclients * sizeof(*cl.players), "players"); // parse gametype cl.gametype = MSG_ReadByte(); // parse signon message level = cl.levelname; maxlen = sizeof(cl.levelname); snprintf(level, maxlen, "%s", MSG_ReadString()); // seperate the printfs so the server message can have a color Con_Printf("\n\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\n"); Con_Printf("%c%s\n", 2, level); Con_Printf("Using protocol %i\n", cl.protocol); // // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // // precache models memset(cl.model_precache, 0, sizeof(cl.model_precache)); for (nummodels = 1;; nummodels++) { char *in, *model; in = MSG_ReadString(); if (!in[0]) break; if (nummodels == max_models(cl.protocol)) { Host_Error("Server sent too many model precaches (max = %d)", max_models(cl.protocol)); return; } model = model_precache[nummodels]; maxlen = sizeof(model_precache[0]); snprintf(model, maxlen, "%s", in); Mod_TouchModel(model); } // precache sounds memset(cl.sound_precache, 0, sizeof(cl.sound_precache)); for (numsounds = 1;; numsounds++) { char *in, *sound; in = MSG_ReadString(); if (!in[0]) break; if (numsounds == max_sounds(cl.protocol)) { Host_Error("Server sent too many sound precaches (max = %d)", max_sounds(cl.protocol)); return; } sound = sound_precache[numsounds]; maxlen = sizeof(sound_precache[0]); snprintf(sound, maxlen, "%s", in); S_TouchSound(sound); } // copy the naked name of the map file to the cl structure mapname = COM_SkipPath(model_precache[1]); COM_StripExtension(mapname, cl.mapname, sizeof(cl.mapname)); // // now we try to load everything else until a cache allocation fails // for (i = 1; i < nummodels; i++) { cl.model_precache[i] = Mod_ForName(model_precache[i], false); if (cl.model_precache[i] == NULL) { Con_Printf("Model %s not found\n", model_precache[i]); return; } CL_KeepaliveMessage(); } S_BeginPrecaching(); for (i = 1; i < numsounds; i++) { cl.sound_precache[i] = S_PrecacheSound(sound_precache[i]); CL_KeepaliveMessage(); } S_EndPrecaching(); // local state cl_entities[0].model = cl.model_precache[1]; cl.worldmodel = BrushModel(cl_entities[0].model); R_NewMap(); Hunk_Check(); // make sure nothing is hurt noclip_anglehack = false; // noclip is turned off at start }
/* ==================== CL_Init ==================== */ void CL_Init( void ) { Com_Printf( "----- Client Initialization -----\n" ); #ifdef JK2_MODE JK2SP_Register("con_text", SP_REGISTER_REQUIRED); //reference is CON_TEXT JK2SP_Register("keynames", SP_REGISTER_REQUIRED); // reference is KEYNAMES #endif Con_Init (); CL_ClearState (); cls.state = CA_DISCONNECTED; // no longer CA_UNINITIALIZED //cls.keyCatchers = KEYCATCH_CONSOLE; cls.realtime = 0; cls.realtimeFraction=0.0f; // fraction of a msec accumulated CL_InitInput (); // // register our variables // cl_noprint = Cvar_Get( "cl_noprint", "0", 0 ); cl_timeout = Cvar_Get ("cl_timeout", "125", 0); cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP ); cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP ); cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP ); cl_newClock = Cvar_Get ("cl_newClock", "1", 0); cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP ); cl_avidemo = Cvar_Get ("cl_avidemo", "0", 0); cl_pano = Cvar_Get ("pano", "0", 0); cl_panoNumShots= Cvar_Get ("panoNumShots", "10", CVAR_ARCHIVE); cl_skippingcin = Cvar_Get ("skippingCinematic", "0", CVAR_ROM); cl_endcredits = Cvar_Get ("cg_endcredits", "0", 0); cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ARCHIVE); cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE); cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", CVAR_ARCHIVE); cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE ); cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE); cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE); cl_mouseAccel = Cvar_Get ("cl_mouseAccel", "0", CVAR_ARCHIVE); cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE ); cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0); cl_allowAltEnter = Cvar_Get ("cl_allowAltEnter", "1", CVAR_ARCHIVE); cl_inGameVideo = Cvar_Get ("cl_inGameVideo", "1", CVAR_ARCHIVE); cl_framerate = Cvar_Get ("cl_framerate", "0", CVAR_TEMP); // init autoswitch so the ui will have it correctly even // if the cgame hasn't been started Cvar_Get ("cg_autoswitch", "1", CVAR_ARCHIVE); m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE); m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE); m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE); m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE); m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE); // ~ and `, as keys and characters cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60 0xb2", CVAR_ARCHIVE); cl_consoleUseScanCode = Cvar_Get( "cl_consoleUseScanCode", "1", CVAR_ARCHIVE ); // userinfo #ifdef JK2_MODE Cvar_Get ("name", "Kyle", CVAR_USERINFO | CVAR_ARCHIVE ); #else Cvar_Get ("name", "Jaden", CVAR_USERINFO | CVAR_ARCHIVE ); #endif #ifdef JK2_MODE // this is required for savegame compatibility - not ever actually used Cvar_Get ("snaps", "20", CVAR_USERINFO ); Cvar_Get ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_SAVEGAME ); #else Cvar_Get ("sex", "f", CVAR_USERINFO | CVAR_ARCHIVE | CVAR_SAVEGAME | CVAR_NORESTART ); Cvar_Get ("snd", "jaden_fmle", CVAR_USERINFO | CVAR_ARCHIVE | CVAR_SAVEGAME | CVAR_NORESTART );//UI_SetSexandSoundForModel changes to match sounds.cfg for model Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_SAVEGAME | CVAR_NORESTART); #endif // // register our commands // Cmd_AddCommand ("cmd", CL_ForwardToServer_f); Cmd_AddCommand ("configstrings", CL_Configstrings_f); Cmd_AddCommand ("clientinfo", CL_Clientinfo_f); Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f); Cmd_AddCommand ("vid_restart", CL_Vid_Restart_f); Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("cinematic", CL_PlayCinematic_f); Cmd_SetCommandCompletionFunc( "cinematic", CL_CompleteCinematic ); Cmd_AddCommand ("ingamecinematic", CL_PlayInGameCinematic_f); Cmd_AddCommand ("uimenu", CL_GenericMenu_f); Cmd_AddCommand ("datapad", CL_DataPad_f); Cmd_AddCommand ("endscreendissolve", CL_EndScreenDissolve_f); CL_InitRef(); CL_StartHunkUsers(); SCR_Init (); Cbuf_Execute (); Cvar_Set( "cl_running", "1" ); Com_Printf( "----- Client Initialization Complete -----\n" ); }
/* ================== CL_ParseServerData ================== */ qboolean CL_ParseServerData (void) { char *str; int i; int newVersion; cvar_t *gameDirHack; // // wipe the client_state_t struct // CL_ClearState (); cls.state = ca_connected; // parse protocol version number i = MSG_ReadLong (&net_message); cls.serverProtocol = i; cl.servercount = MSG_ReadLong (&net_message); cl.attractloop = MSG_ReadByte (&net_message); if (i != PROTOCOL_ORIGINAL && i != PROTOCOL_R1Q2 && !cl.attractloop) Com_Error (ERR_HARD, "Server is using unknown protocol %d.", i); // game directory str = MSG_ReadString (&net_message); strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1); // set gamedir, f*****g christ this is messy! if ((str[0] && (!fs_gamedirvar->string || !fs_gamedirvar->string[0] || strcmp(fs_gamedirvar->string, str))) || (!str[0] && (fs_gamedirvar->string || fs_gamedirvar->string[0]))) { if (strcmp(fs_gamedirvar->string, str)) { if (cl.attractloop) { Cvar_ForceSet ("game", str); FS_SetGamedir (str); } else { Cvar_Set("game", str); } } } Cvar_ForceSet ("$game", str); gameDirHack = Cvar_FindVar ("game"); gameDirHack->flags |= CVAR_NOSET; // parse player entity number cl.playernum = MSG_ReadShort (&net_message); // get the full level name str = MSG_ReadString (&net_message); if (cls.serverProtocol == PROTOCOL_R1Q2) { cl.enhancedServer = MSG_ReadByte (&net_message); newVersion = MSG_ReadShort (&net_message); if (newVersion != MINOR_VERSION_R1Q2) { if (cl.attractloop) { if (newVersion < MINOR_VERSION_R1Q2) Com_Printf ("This demo was recorded with an earlier version of the R1Q2 protocol. It may not play back properly.\n", LOG_CLIENT); else Com_Printf ("This demo was recorded with a later version of the R1Q2 protocol. It may not play back properly. Please update your R1Q2 client.\n", LOG_CLIENT); } else { if (newVersion > MINOR_VERSION_R1Q2) Com_Printf ("Server reports a higher R1Q2 protocol number than your client supports. Some features will be unavailable until you update your R1Q2 client.\n", LOG_CLIENT); else Com_Printf ("Server reports a lower R1Q2 protocol number. The server admin needs to update their server!\n", LOG_CLIENT); } //cap if server is above us just to be safe if (newVersion > MINOR_VERSION_R1Q2) newVersion = MINOR_VERSION_R1Q2; } if (newVersion >= 1903) { MSG_ReadByte (&net_message); //was ad cl.strafeHack = MSG_ReadByte (&net_message); } else { cl.strafeHack = false; } cls.protocolVersion = newVersion; } else { cl.enhancedServer = false; cl.strafeHack = false; cls.protocolVersion = 0; } Com_DPrintf ("Serverdata packet received. protocol=%d, servercount=%d, attractloop=%d, clnum=%d, game=%s, map=%s, enhanced=%d\n", cls.serverProtocol, cl.servercount, cl.attractloop, cl.playernum, cl.gamedir, str, cl.enhancedServer); if (cl.playernum == -1) { // playing a cinematic or showing a pic, not a level //SCR_PlayCinematic (str); // tell the server to advance to the next map / cinematic MSG_WriteByte (clc_stringcmd); MSG_Print (va("nextserver %i\n", cl.servercount)); MSG_EndWriting (&cls.netchan.message); } else { // seperate the printfs so the server message can have a color Com_Printf("\n\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\n", LOG_CLIENT); Com_Printf ("\2%s\n", LOG_CLIENT, str); // need to prep refresh at next oportunity cl.refresh_prepped = false; } //CL_FixCvarCheats(); return true; }
/* ================== CL_ParseGamestate ================== */ void CL_ParseGamestate( msg_t *msg ) { int i; entityState_t *es; int newnum; entityState_t nullstate; int cmd; char *s; Con_Close(); clc.connectPacketCount = 0; // wipe local client state CL_ClearState(); #ifdef _DONETPROFILE_ int startBytes,endBytes; startBytes=msg->readcount; #endif // a gamestate always marks a server command sequence clc.serverCommandSequence = MSG_ReadLong( msg ); // parse all the configstrings and baselines cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings while ( 1 ) { cmd = MSG_ReadByte( msg ); if ( cmd == svc_EOF ) { break; } if ( cmd == svc_configstring ) { int len, start; start = msg->readcount; i = MSG_ReadShort( msg ); if ( i < 0 || i >= MAX_CONFIGSTRINGS ) { Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" ); } s = MSG_ReadBigString( msg ); if (cl_shownet->integer >= 2) { Com_Printf("%3i: %d: %s\n", start, i, s); } /* if (i == CS_SERVERINFO) { //get the special value here char *f = strstr(s, "g_debugMelee"); if (f) { while (*f && *f != '\\') { //find the \ after it f++; } if (*f == '\\') { //got it int i = 0; f++; while (*f && *f != '\\' && i < 128) { hiddenCvarVal[i] = *f; i++; f++; } hiddenCvarVal[i] = 0; //resume here s = f; } } } */ len = strlen( s ); if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) { Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" ); } // append it to the gameState string buffer cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); } Com_Memset (&nullstate, 0, sizeof(nullstate)); es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" ); } } clc.clientNum = MSG_ReadLong(msg); // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); CL_ParseRMG ( msg ); //rwwRMG - get info for it from the server #ifdef _DONETPROFILE_ endBytes=msg->readcount; // ClReadProf().AddField("svc_gamestate",endBytes-startBytes); #endif // parse serverId and other cvars CL_SystemInfoChanged(); // reinitialize the filesystem if the game directory has changed if( FS_ConditionalRestart( clc.checksumFeed ) ) { // don't set to true because we yet have to start downloading // enabling this can cause double loading of a map when connecting to // a server which has a different game directory set //clc.downloadRestart = qtrue; } // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame CL_InitDownloads(); // make sure the game starts Cvar_Set( "cl_paused", "0" ); }