/* ======================= CL_SendConnectPacket We have gotten a challenge from the server, so try and connect. ====================== */ void CL_SendConnectPacket (void) { netadr_t adr; int32_t port; if (!NET_StringToAdr (cls.servername, &adr)) { Com_Printf ("Bad server address\n"); cls.connect_time = 0; return; } if (adr.port == 0) adr.port = BigShort (PORT_SERVER); port = Cvar_VariableValue ("qport"); userinfo_modified = false; // if in compatibility mode, lie to server about this // client's protocol, but exclude localhost for this. if (cl_servertrick->value && strcmp(cls.servername, "localhost")) Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", OLD_PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo() ); else Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo() ); }
/* * We have gotten a challenge from the server, so try and * connect. */ void CL_SendConnectPacket(void) { netadr_t adr; int port; memset(&adr, 0, sizeof(adr)); if (!NET_StringToAdr(cls.servername, &adr)) { Com_Printf("Bad server address\n"); cls.connect_time = 0; return; } if (adr.port == 0) { adr.port = BigShort(PORT_SERVER); } port = Cvar_VariableValue("qport"); userinfo_modified = false; Netchan_OutOfBandPrint(NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo()); }
/** * @note Only call @c CL_Connect if there is no connection yet (@c cls.netStream is @c nullptr) * @sa CL_Disconnect * @sa CL_SendChangedUserinfos */ static void CL_Connect (void) { Com_SetUserinfoModified(false); assert(!cls.netStream); if (cls.servername[0] != '\0') { assert(cls.serverport[0] != '\0'); Com_Printf("Connecting to %s %s...\n", cls.servername, cls.serverport); cls.netStream = NET_Connect(cls.servername, cls.serverport, CL_FreeClientStream); } else { Com_Printf("Connecting to localhost...\n"); cls.netStream = NET_ConnectToLoopBack(CL_FreeClientStream); } if (cls.netStream) { char info[MAX_INFO_STRING]; NET_OOB_Printf(cls.netStream, SV_CMD_CONNECT " %i \"%s\"\n", PROTOCOL_VERSION, Cvar_Userinfo(info, sizeof(info))); cls.connectTime = CL_Milliseconds(); } else { if (cls.servername[0] != '\0') { assert(cls.serverport[0]); Com_Printf("Could not connect to %s %s\n", cls.servername, cls.serverport); } else { Com_Printf("Could not connect to localhost\n"); } } }
/** * @brief Send the userinfo to the server (and to all other clients) * when they changed (CVAR_USERINFO) * @sa CL_Connect */ static void CL_SendChangedUserinfos (void) { /* send a userinfo update if needed */ if (cls.state < ca_connected) return; if (!Com_IsUserinfoModified()) return; char info[MAX_INFO_STRING]; const char* userInfo = Cvar_Userinfo(info, sizeof(info)); dbuffer msg(strlen(userInfo) + 2); NET_WriteByte(&msg, clc_userinfo); NET_WriteString(&msg, userInfo); NET_WriteMsg(cls.netStream, msg); Com_SetUserinfoModified(false); }
void CL_Userinfo_f(void) { Com_Printf("User info settings:\n"); Info_Print(Cvar_Userinfo()); }
/* ================= CL_SendCmd ================= */ void CL_SendCmd (void) { sizebuf_t buf; byte data[128]; int i; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int checksumIndex; // clear buffer memset (&buf, 0, sizeof(buf)); // build a command even if not connected // save this command off for prediction i = cls.netchan.outgoing_sequence & (CMD_BACKUP-1); cmd = &cl.cmds[i]; cl.cmd_time[i] = cls.realtime; // for netgraph ping calculation *cmd = CL_CreateCmd (); cl.cmd = *cmd; if (cls.state == ca_disconnected || cls.state == ca_connecting) return; if (cls.state == ca_connected) { if (cls.netchan.message.cursize || curtime - cls.netchan.last_sent > 1000 ) Netchan_Transmit (&cls.netchan, 0, buf.data); return; } // send a userinfo update if needed if (userinfo_modified) { CL_FixUpGender(); userinfo_modified = false; MSG_WriteByte (&cls.netchan.message, clc_userinfo); MSG_WriteString (&cls.netchan.message, Cvar_Userinfo() ); } SZ_Init (&buf, data, sizeof(data)); // Knightmare- removed this, put ESC-only substitute in keys.c /*if (cmd->buttons && cl.cinematictime > 0 && !cl.attractloop && cls.realtime - cl.cinematictime > 1000) { // skip the rest of the cinematic SCR_FinishCinematic (); }*/ // begin a client move command MSG_WriteByte (&buf, clc_move); // save the position for a checksum byte checksumIndex = buf.cursize; MSG_WriteByte (&buf, 0); // let the server know what the last frame we // got was, so the next message can be delta compressed if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting) MSG_WriteLong (&buf, -1); // no compression else MSG_WriteLong (&buf, cl.frame.serverframe); // send this and the previous cmds in the message, so // if the last packet was dropped, it can be recovered i = (cls.netchan.outgoing_sequence-2) & (CMD_BACKUP-1); cmd = &cl.cmds[i]; memset (&nullcmd, 0, sizeof(nullcmd)); MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd); oldcmd = cmd; i = (cls.netchan.outgoing_sequence-1) & (CMD_BACKUP-1); cmd = &cl.cmds[i]; MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); oldcmd = cmd; i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP-1); cmd = &cl.cmds[i]; MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); // calculate a checksum over the move commands buf.data[checksumIndex] = COM_BlockSequenceCRCByte( buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1, cls.netchan.outgoing_sequence); // // deliver the message // Netchan_Transmit (&cls.netchan, buf.cursize, buf.data); }
/* ======================= CL_SendConnectPacket We have gotten a challenge from the server, so try and connect. ====================== */ void CL_SendConnectPacket( void ) { netadr_t adr; int port; if( !NET_StringToAdr( cls.servername, &adr )) { MsgDev( D_INFO, "CL_SendConnectPacket: bad server address\n"); cls.connect_time = 0; return; } if( adr.port == 0 ) adr.port = BF_BigShort( PORT_SERVER ); port = Cvar_VariableValue( "net_qport" ); userinfo->modified = false; Netchan_OutOfBandPrint( NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo( )); }
/* =================== CL_WritePacket Create and send the command packet to the server Including both the reliable commands and the usercmds =================== */ void CL_WritePacket( void ) { sizebuf_t buf; qboolean send_command = false; byte data[MAX_CMD_BUFFER]; int i, from, to, key, size; int numbackup = 2; int numcmds; int newcmds; int cmdnumber; // don't send anything if playing back a demo if( cls.demoplayback || cls.state == ca_cinematic ) return; if( cls.state == ca_disconnected || cls.state == ca_connecting ) return; CL_ComputePacketLoss (); #ifndef _DEBUG if( cl_cmdrate->value < MIN_CMD_RATE ) { Cvar_SetFloat( "cl_cmdrate", MIN_CMD_RATE ); } #endif Q_memset( data, 0, MAX_CMD_BUFFER ); BF_Init( &buf, "ClientData", data, sizeof( data )); // Determine number of backup commands to send along numbackup = bound( 0, cl_cmdbackup->integer, MAX_BACKUP_COMMANDS ); if( cls.state == ca_connected ) numbackup = 0; // Check to see if we can actually send this command // In single player, send commands as fast as possible // Otherwise, only send when ready and when not choking bandwidth if(( cl.maxclients == 1 ) || ( NET_IsLocalAddress( cls.netchan.remote_address ) && !host_limitlocal->integer )) send_command = true; else if(( host.realtime >= cls.nextcmdtime ) && Netchan_CanPacket( &cls.netchan )) send_command = true; if( cl.force_send_usercmd ) { send_command = true; cl.force_send_usercmd = false; } if(( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= CL_UPDATE_MASK ) { if(( host.realtime - cls.netchan.last_received ) > CONNECTION_PROBLEM_TIME ) { Con_NPrintf( 1, "^3Warning:^1 Connection Problem^7\n" ); cl.validsequence = 0; } } if( cl_nodelta->integer ) { cl.validsequence = 0; } // send a userinfo update if needed if( userinfo->modified ) { BF_WriteByte( &cls.netchan.message, clc_userinfo ); BF_WriteString( &cls.netchan.message, Cvar_Userinfo( )); } if( send_command ) { int outgoing_sequence; if( cl_cmdrate->integer > 0 ) cls.nextcmdtime = host.realtime + ( 1.0f / cl_cmdrate->value ); else cls.nextcmdtime = host.realtime; // always able to send right away if( cls.lastoutgoingcommand == -1 ) { outgoing_sequence = cls.netchan.outgoing_sequence; cls.lastoutgoingcommand = cls.netchan.outgoing_sequence; } else outgoing_sequence = cls.lastoutgoingcommand + 1; // begin a client move command BF_WriteByte( &buf, clc_move ); // save the position for a checksum byte key = BF_GetRealBytesWritten( &buf ); BF_WriteByte( &buf, 0 ); // write packet lossage percentation BF_WriteByte( &buf, cls.packet_loss ); // say how many backups we'll be sending BF_WriteByte( &buf, numbackup ); // how many real commands have queued up newcmds = ( cls.netchan.outgoing_sequence - cls.lastoutgoingcommand ); // put an upper/lower bound on this newcmds = bound( 0, newcmds, MAX_TOTAL_CMDS ); if( cls.state == ca_connected ) newcmds = 0; BF_WriteByte( &buf, newcmds ); numcmds = newcmds + numbackup; from = -1; for( i = numcmds - 1; i >= 0; i-- ) { cmdnumber = ( cls.netchan.outgoing_sequence - i ) & CL_UPDATE_MASK; to = cmdnumber; CL_WriteUsercmd( &buf, from, to ); from = to; if( BF_CheckOverflow( &buf )) Host_Error( "CL_Move, overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER ); } // calculate a checksum over the move commands size = BF_GetRealBytesWritten( &buf ) - key - 1; buf.pData[key] = CRC32_BlockSequence( buf.pData + key + 1, size, cls.netchan.outgoing_sequence ); // message we are constructing. i = cls.netchan.outgoing_sequence & CL_UPDATE_MASK; // determine if we need to ask for a new set of delta's. if( cl.validsequence && (cls.state == ca_active) && !( cls.demorecording && cls.demowaiting )) { cl.delta_sequence = cl.validsequence; BF_WriteByte( &buf, clc_delta ); BF_WriteByte( &buf, cl.validsequence & 0xFF ); } else { // request delta compression of entities cl.delta_sequence = -1; } if( BF_CheckOverflow( &buf )) { Host_Error( "CL_Move, overflowed command buffer (%i bytes)\n", MAX_CMD_BUFFER ); } // remember outgoing command that we are sending cls.lastoutgoingcommand = cls.netchan.outgoing_sequence; // composite the rest of the datagram.. if( BF_GetNumBitsWritten( &cls.datagram ) <= BF_GetNumBitsLeft( &buf )) BF_WriteBits( &buf, BF_GetData( &cls.datagram ), BF_GetNumBitsWritten( &cls.datagram )); BF_Clear( &cls.datagram ); // deliver the message (or update reliable) Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf )); } else { // increment sequence number so we can detect that we've held back packets. cls.netchan.outgoing_sequence++; } if( cls.demorecording ) { // Back up one because we've incremented outgoing_sequence each frame by 1 unit cmdnumber = ( cls.netchan.outgoing_sequence - 1 ) & CL_UPDATE_MASK; CL_WriteDemoUserCmd( cmdnumber ); } // update download/upload slider. Netchan_UpdateProgress( &cls.netchan ); }
/* ============== CL_Userinfo_f ============== */ void CL_Userinfo_f( void ) { Msg( "User info settings:\n" ); Info_Print( Cvar_Userinfo( )); Msg( "Total %i symbols\n", Q_strlen( Cvar_Userinfo( ))); }
void CL_SendCmd(void) { sizebuf_t buf; byte data[128]; int i; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int checksumIndex; memset(&buf, 0, sizeof(buf)); /* save this command off for prediction */ i = cls.netchan.outgoing_sequence & (CMD_BACKUP - 1); cmd = &cl.cmds[i]; cl.cmd_time[i] = cls.realtime; /* for netgraph ping calculation */ CL_FinalizeCmd(); cl.cmd = *cmd; if ((cls.state == ca_disconnected) || (cls.state == ca_connecting)) { return; } if (cls.state == ca_connected) { if (cls.netchan.message.cursize || (curtime - cls.netchan.last_sent > 1000)) { Netchan_Transmit(&cls.netchan, 0, buf.data); } return; } /* send a userinfo update if needed */ if (userinfo_modified) { CL_FixUpGender(); userinfo_modified = false; MSG_WriteByte(&cls.netchan.message, clc_userinfo); MSG_WriteString(&cls.netchan.message, Cvar_Userinfo()); } SZ_Init(&buf, data, sizeof(data)); /* begin a client move command */ MSG_WriteByte(&buf, clc_move); /* save the position for a checksum byte */ checksumIndex = buf.cursize; MSG_WriteByte(&buf, 0); /* let the server know what the last frame we got was, so the next message can be delta compressed */ if (cl_nodelta->value || !cl.frame.valid) { MSG_WriteLong(&buf, -1); /* no compression */ } else { MSG_WriteLong(&buf, cl.frame.serverframe); } /* send this and the previous cmds in the message, so if the last packet was dropped, it can be recovered */ i = (cls.netchan.outgoing_sequence - 2) & (CMD_BACKUP - 1); cmd = &cl.cmds[i]; memset(&nullcmd, 0, sizeof(nullcmd)); MSG_WriteDeltaUsercmd(&buf, &nullcmd, cmd); oldcmd = cmd; i = (cls.netchan.outgoing_sequence - 1) & (CMD_BACKUP - 1); cmd = &cl.cmds[i]; MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd); oldcmd = cmd; i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP - 1); cmd = &cl.cmds[i]; MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd); /* calculate a checksum over the move commands */ buf.data[checksumIndex] = COM_BlockSequenceCRCByte(buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1, cls.netchan.outgoing_sequence); /* deliver the message */ Netchan_Transmit(&cls.netchan, buf.cursize, buf.data); /* Reinit the current cmd buffer */ cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)]; memset(cmd, 0, sizeof(*cmd)); }
/** * @brief Prints the current userinfo string to the game console * @sa SV_UserInfo_f */ static void CL_UserInfo_f (void) { Com_Printf("User info settings:\n"); char info[MAX_INFO_STRING]; Info_Print(Cvar_Userinfo(info, sizeof(info))); }