cl_main.c
来自「3D 游戏界的大牛人 John Carmack 终于放出了 Q3 的源代码」· C语言 代码 · 共 2,607 行 · 第 1/5 页
C
2,607 行
if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) {
// if running a local server, kill it
SV_Shutdown( "Server quit\n" );
}
// make sure a local server is killed
Cvar_Set( "sv_killserver", "1" );
SV_Frame( 0 );
CL_Disconnect( qtrue );
Con_Close();
/* MrE: 2000-09-13: now called in CL_DownloadsComplete
CL_FlushMemory( );
*/
Q_strncpyz( cls.servername, server, sizeof(cls.servername) );
if (!NET_StringToAdr( cls.servername, &clc.serverAddress) ) {
Com_Printf ("Bad server address\n");
cls.state = CA_DISCONNECTED;
return;
}
if (clc.serverAddress.port == 0) {
clc.serverAddress.port = BigShort( PORT_SERVER );
}
Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", cls.servername,
clc.serverAddress.ip[0], clc.serverAddress.ip[1],
clc.serverAddress.ip[2], clc.serverAddress.ip[3],
BigShort( clc.serverAddress.port ) );
// if we aren't playing on a lan, we need to authenticate
// with the cd key
if ( NET_IsLocalAddress( clc.serverAddress ) ) {
cls.state = CA_CHALLENGING;
} else {
cls.state = CA_CONNECTING;
}
cls.keyCatchers = 0;
clc.connectTime = -99999; // CL_CheckForResend() will fire immediately
clc.connectPacketCount = 0;
// server connection string
Cvar_Set( "cl_currentServerAddress", server );
}
/*
=====================
CL_Rcon_f
Send the rest of the command line over as
an unconnected command.
=====================
*/
void CL_Rcon_f( void ) {
char message[1024];
netadr_t to;
if ( !rcon_client_password->string ) {
Com_Printf ("You must set 'rconpassword' before\n"
"issuing an rcon command.\n");
return;
}
message[0] = -1;
message[1] = -1;
message[2] = -1;
message[3] = -1;
message[4] = 0;
strcat (message, "rcon ");
strcat (message, rcon_client_password->string);
strcat (message, " ");
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
strcat (message, Cmd_Cmd()+5);
if ( cls.state >= CA_CONNECTED ) {
to = clc.netchan.remoteAddress;
} else {
if (!strlen(rconAddress->string)) {
Com_Printf ("You must either be connected,\n"
"or set the 'rconAddress' cvar\n"
"to issue rcon commands\n");
return;
}
NET_StringToAdr (rconAddress->string, &to);
if (to.port == 0) {
to.port = BigShort (PORT_SERVER);
}
}
NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to);
}
/*
=================
CL_SendPureChecksums
=================
*/
void CL_SendPureChecksums( void ) {
const char *pChecksums;
char cMsg[MAX_INFO_VALUE];
int i;
// if we are pure we need to send back a command with our referenced pk3 checksums
pChecksums = FS_ReferencedPakPureChecksums();
// "cp"
// "Yf"
Com_sprintf(cMsg, sizeof(cMsg), "Yf ");
Q_strcat(cMsg, sizeof(cMsg), va("%d ", cl.serverId) );
Q_strcat(cMsg, sizeof(cMsg), pChecksums);
for (i = 0; i < 2; i++) {
cMsg[i] += 10;
}
CL_AddReliableCommand( cMsg );
}
/*
=================
CL_ResetPureClientAtServer
=================
*/
void CL_ResetPureClientAtServer( void ) {
CL_AddReliableCommand( va("vdr") );
}
/*
=================
CL_Vid_Restart_f
Restart the video subsystem
we also have to reload the UI and CGame because the renderer
doesn't know what graphics to reload
=================
*/
void CL_Vid_Restart_f( void ) {
// don't let them loop during the restart
S_StopAllSounds();
// shutdown the UI
CL_ShutdownUI();
// shutdown the CGame
CL_ShutdownCGame();
// shutdown the renderer and clear the renderer interface
CL_ShutdownRef();
// client is no longer pure untill new checksums are sent
CL_ResetPureClientAtServer();
// clear pak references
FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF );
// reinitialize the filesystem if the game directory or checksum has changed
FS_ConditionalRestart( clc.checksumFeed );
cls.rendererStarted = qfalse;
cls.uiStarted = qfalse;
cls.cgameStarted = qfalse;
cls.soundRegistered = qfalse;
// unpause so the cgame definately gets a snapshot and renders a frame
Cvar_Set( "cl_paused", "0" );
// if not running a server clear the whole hunk
if ( !com_sv_running->integer ) {
// clear the whole hunk
Hunk_Clear();
}
else {
// clear all the client data on the hunk
Hunk_ClearToMark();
}
// initialize the renderer interface
CL_InitRef();
// startup all the client stuff
CL_StartHunkUsers();
// start the cgame if connected
if ( cls.state > CA_CONNECTED && cls.state != CA_CINEMATIC ) {
cls.cgameStarted = qtrue;
CL_InitCGame();
// send pure checksums
CL_SendPureChecksums();
}
}
/*
=================
CL_Snd_Restart_f
Restart the sound subsystem
The cgame and game must also be forced to restart because
handles will be invalid
=================
*/
void CL_Snd_Restart_f( void ) {
S_Shutdown();
S_Init();
CL_Vid_Restart_f();
}
/*
==================
CL_PK3List_f
==================
*/
void CL_OpenedPK3List_f( void ) {
Com_Printf("Opened PK3 Names: %s\n", FS_LoadedPakNames());
}
/*
==================
CL_PureList_f
==================
*/
void CL_ReferencedPK3List_f( void ) {
Com_Printf("Referenced PK3 Names: %s\n", FS_ReferencedPakNames());
}
/*
==================
CL_Configstrings_f
==================
*/
void CL_Configstrings_f( void ) {
int i;
int ofs;
if ( cls.state != CA_ACTIVE ) {
Com_Printf( "Not connected to a server.\n");
return;
}
for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
ofs = cl.gameState.stringOffsets[ i ];
if ( !ofs ) {
continue;
}
Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs );
}
}
/*
==============
CL_Clientinfo_f
==============
*/
void CL_Clientinfo_f( void ) {
Com_Printf( "--------- Client Information ---------\n" );
Com_Printf( "state: %i\n", cls.state );
Com_Printf( "Server: %s\n", cls.servername );
Com_Printf ("User info settings:\n");
Info_Print( Cvar_InfoString( CVAR_USERINFO ) );
Com_Printf( "--------------------------------------\n" );
}
//====================================================================
/*
=================
CL_DownloadsComplete
Called when all downloading has been completed
=================
*/
void CL_DownloadsComplete( void ) {
// if we downloaded files we need to restart the file system
if (clc.downloadRestart) {
clc.downloadRestart = qfalse;
FS_Restart(clc.checksumFeed); // We possibly downloaded a pak, restart the file system to load it
// inform the server so we get new gamestate info
CL_AddReliableCommand( "donedl" );
// by sending the donedl command we request a new gamestate
// so we don't want to load stuff yet
return;
}
// let the client game init and load data
cls.state = CA_LOADING;
// Pump the loop, this may change gamestate!
Com_EventLoop();
// if the gamestate was changed by calling Com_EventLoop
// then we loaded everything already and we don't want to do it again.
if ( cls.state != CA_LOADING ) {
return;
}
// 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;
CL_InitCGame();
// set pure checksums
CL_SendPureChecksums();
CL_WritePacket();
CL_WritePacket();
CL_WritePacket();
}
/*
=================
CL_BeginDownload
Requests a file to download from the server. Stores it in the current
game directory.
=================
*/
void CL_BeginDownload( const char *localName, const char *remoteName ) {
Com_DPrintf("***** CL_BeginDownload *****\n"
"Localname: %s\n"
"Remotename: %s\n"
"****************************\n", localName, remoteName);
Q_strncpyz ( clc.downloadName, localName, sizeof(clc.downloadName) );
Com_sprintf( clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName );
// Set so UI gets access to it
Cvar_Set( "cl_downloadName", remoteName );
Cvar_Set( "cl_downloadSize", "0" );
Cvar_Set( "cl_downloadCount", "0" );
Cvar_SetValue( "cl_downloadTime", cls.realtime );
clc.downloadBlock = 0; // Starting new file
clc.downloadCount = 0;
CL_AddReliableCommand( va("download %s", remoteName) );
}
/*
=================
CL_NextDownload
A download completed or failed
=================
*/
void CL_NextDownload(void) {
char *s;
char *remoteName, *localName;
// We are looking to start a download here
if (*clc.downloadList) {
s = clc.downloadList;
// format is:
// @remotename@localname@remotename@localname, etc.
if (*s == '@')
s++;
remoteName = s;
if ( (s = strchr(s, '@')) == NULL ) {
CL_DownloadsComplete();
return;
}
*s++ = 0;
localName = s;
if ( (s = strchr(s, '@')) != NULL )
*s++ = 0;
else
s = localName + strlen(localName); // point at the nul byte
CL_BeginDownload( localName, remoteName );
clc.downloadRestart = qtrue;
// move over the rest
memmove( clc.downloadList, s, strlen(s) + 1);
return;
}
CL_DownloadsComplete();
}
/*
=================
CL_InitDownloads
After receiving a valid game state, we valid the cgame and local zip files here
and determine if we need to download them
=================
*/
void CL_InitDownloads(void) {
char missingfiles[1024];
if ( !cl_allowDownload->integer )
{
// autodownload is disabled on the client
// but it's possible that some referenced files on the server are missing
if (FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) )
{
// NOTE TTimo I would rather have that printed as a modal message box
// but at this point while joining the game we don't know wether we will successfully join or not
Com_Printf( "\nWARNING: You are missing some files referenced by the server:\n%s"
"You might not be able to join the game\n"
"Go to the setting menu to turn on autodownload, or get the file elsewhere\n\n", missingfiles );
}
}
else if ( FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ) , qtrue ) ) {
Com_Printf("Need paks: %s\n", clc.downloadList );
if ( *clc.downloadList ) {
// if autodownloading is not enabled on the server
cls.state = CA_CONNECTED;
CL_NextDownload();
return;
}
}
CL_DownloadsComplete();
}
/*
=================
CL_CheckForResend
Resend a connect message if the last one has timed out
=================
*/
void CL_CheckForResend( void ) {
int port, i;
char info[MAX_INFO_STRING];
char data[MAX_INFO_STRING];
// don't send anything if playing back a demo
if ( clc.demoplaying ) {
return;
}
// resend if we haven't gotten a reply yet
if ( cls.state != CA_CONNECTING && cls.state != CA_CHALLENGING ) {
return;
}
if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) {
return;
}
clc.connectTime = cls.realtime; // for retransmit requests
clc.connectPacketCount++;
switch ( cls.state ) {
case CA_CONNECTING:
// requesting a challenge
if ( !Sys_IsLANAddress( clc.serverAddress ) ) {
CL_RequestAuthorization();
}
NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "getchallenge");
break;
case CA_CHALLENGING:
// sending back the challenge
port = Cvar_VariableValue ("net_qport");
Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) );
Info_SetValueForKey( info, "protocol", va("%i", PROTOCOL_VERSION ) );
Info_SetValueForKey( info, "qport", va("%i", port ) );
Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) );
strcpy(data, "connect ");
// TTimo adding " " around the userinfo string to avoid truncated userinfo on the server
// (Com_TokenizeString tokenizes around spaces)
data[8] = '"';
for(i=0;i<strlen(info);i++) {
data[9+i] = info[i]; // + (clc.challenge)&0x3;
}
data[9+i] = '"';
data[10+i] = 0;
// NOTE TTimo don't forget to set the right data length!
NET_OutOfBandData( NS_CLIENT, clc.serverAddress, &data[0], i+10 );
// the most current userinfo has been sent, so watch for any
// newer changes to userinfo variables
cvar_modifiedFlags &= ~CVAR_USERINFO;
break;
default:
Com_Error( ERR_FATAL, "CL_CheckForResend: bad cls.state" );
}
}
/*
===================
CL_DisconnectPacket
Sometimes the server can drop the client and the netchan based
disconnect can be lost. If the client continues to send packets
to the server, the server will send out of band disconnect packets
to the client so it doesn't have to wait for the full timeout period.
===================
*/
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?