/* ====================== Host_Name_f ====================== */ void Host_Name_f (void) { char newName[32]; if (Cmd_Argc () == 1) { Con_Printf ("\"name\" is \"%s\"\n", cl_name.string); return; } if (Cmd_Argc () == 2) q_strlcpy(newName, Cmd_Argv(1), sizeof(newName)); else q_strlcpy(newName, Cmd_Args(), sizeof(newName)); newName[15] = 0; // client_t structure actually says name[32]. if (cmd_source == src_command) { if (Q_strcmp(cl_name.string, newName) == 0) return; Cvar_Set ("_cl_name", newName); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; } if (host_client->name[0] && strcmp(host_client->name, "unconnected") ) { if (Q_strcmp(host_client->name, newName) != 0) Con_Printf ("%s renamed to %s\n", host_client->name, newName); } Q_strcpy (host_client->name, newName); host_client->edict->v.netname = PR_SetEngineString(host_client->name); // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatename); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteString (&sv.reliable_datagram, host_client->name); }
void Cam_Lock(int playernum) { char st[32]; if (Cmd_FindAlias("f_trackspectate")) { Cbuf_AddTextEx (&cbuf_main, "f_trackspectate\n"); } if (cl_multiview.value && cls.mvdplayback) return; snprintf(st, sizeof (st), "ptrack %i", playernum); if (cls.mvdplayback) { memcpy(cl.stats, cl.players[playernum].stats, sizeof(cl.stats)); ideal_track = playernum; } last_lock = cls.realtime; if (cls.mvdplayback == QTV_PLAYBACK) { // its not setinfo extension, but adding new extension just for this is stupid IMO QTV_Cmd_Printf(QTV_EZQUAKE_EXT_SETINFO, st); } else { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, st); } spec_track = playernum; locked = false; Sbar_Changed(); if (TP_NeedRefreshSkins()) TP_RefreshSkins(); }
/* =============== CL_Download_f Request a download from the server =============== */ void CL_Download_f (void) { char filename[MAX_OSPATH]; if (Cmd_Argc() != 2) { Com_Printf("Usage: download <filename>\n"); return; } Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1)); if (strstr (filename, "..")) { Com_Printf ("Refusing to download a path with ..\n"); return; } if (FS_LoadFile (filename, NULL) != -1) { // it exists, no need to download Com_Printf("File already exists.\n"); return; } strcpy (cls.downloadname, filename); Com_Printf ("Downloading %s\n", cls.downloadname); // download to a temp name, and only rename // to the real name when done, so if interrupted // a runt file wont be left COM_StripExtension (cls.downloadname, cls.downloadtempname); strcat (cls.downloadtempname, ".tmp"); MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, va("download %s", cls.downloadname)); cls.downloadnumber++; }
void CHelperManager_Encoder::SendMessage_Added_toPlayer ( playerCharacter_t* pHelper, char* name, int level, int job, int worldIdx ) { MSG_BeginWriting ( &netMessage ); MSG_Clear( &netMessage ); { MSG_WriteByte(&netMessage, EXTEND_SECOND); MSG_WriteShort( &netMessage, HELPER_SYSTEM ); MSG_WriteShort( &netMessage, SC_SEND_ADD_toPlayer ); MSG_WriteByte ( &netMessage, level ); MSG_WriteByte ( &netMessage, job ); MSG_WriteByte ( &netMessage, worldIdx ); MSG_WriteString ( &netMessage, name ); if ( pHelper->ready ) { NET_SendMessage ( &pHelper->sock, &netMessage ); } } MSG_EndWriting(&netMessage); }
/* ================== SV_Modellist_f ================== */ static void SV_Modellist_f (void) { int i; if (host_client->state != cs_connected) { Con_Printf ("modellist not valid -- already spawned\n"); return; } // handle the case of a level changing while a client was connecting if ( atoi(Cmd_Argv(1)) != svs.spawncount ) { Con_Printf ("%s from different level\n", __thisfunc__); SV_New_f (); return; } MSG_WriteByte (&host_client->netchan.message, svc_modellist); for (i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++) MSG_WriteString (&host_client->netchan.message, sv.model_precache[i]); MSG_WriteByte (&host_client->netchan.message, 0); }
/* ====================== Host_Name_f ====================== */ void Host_Name_f (void) { char *newName; if (Cmd_Argc () == 1) { Con_Printf ("\"name\" is \"%s\"\n", cl_name.string); return; } if (Cmd_Argc () == 2) newName = Cmd_Argv(1); else newName = Cmd_Args(); newName[15] = 0; if (cmd_source == src_command) { if (Q_strcmp(cl_name.string, newName) == 0) return; Cvar_Set ("_cl_name", newName); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; } if (host_client->name[0] && strcmp(host_client->name, "unconnected") ) if (Q_strcmp(host_client->name, newName) != 0) Con_Printf ("%s renamed to %s\n", host_client->name, newName); Q_strcpy (host_client->name, newName); host_client->edict->v.netname = host_client->name - pr_strings; // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatename); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteString (&sv.reliable_datagram, host_client->name); }
void Cam_Unlock(void) { if (Cmd_FindAlias("f_freeflyspectate")) { Cbuf_AddTextEx (&cbuf_main, "f_freeflyspectate\n"); } if (!autocam) { return; } if (cls.mvdplayback == QTV_PLAYBACK) { // its not setinfo extension, but adding new extension just for this is stupid IMO QTV_Cmd_Printf(QTV_EZQUAKE_EXT_SETINFO, "ptrack"); } else { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, "ptrack"); } autocam = CAM_NONE; locked = false; Sbar_Changed(); if (cls.mvdplayback && cl.teamfortress) { V_TF_ClearGrenadeEffects (); } if (TP_NeedRefreshSkins()) { TP_RefreshSkins(); } }
/* ================== SV_ConSay_f ================== */ void SV_ConSay_f(void) { client_t *client; int j; char *p; char text[1024]; if (Cmd_Argc () < 2) return; strcpy (text, "console: "); p = Cmd_Args(); if (*p == '"') { p++; p[strlen(p)-1] = 0; } strcat(text, p); for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) { if (client->state != cs_spawned) continue; SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text); } if (sv.mvdrecording) { MVDWrite_Begin (dem_all, 0, strlen(text)+3); MSG_WriteByte ((sizebuf_t*)demo.dbuf, svc_print); MSG_WriteByte ((sizebuf_t*)demo.dbuf, PRINT_CHAT); MSG_WriteString ((sizebuf_t*)demo.dbuf, text); } }
/* =================== SV_FullClientUpdate Writes all update values to a sizebuf =================== */ void SV_FullClientUpdate (client_t *client, sizebuf_t *buf) { int i; char info[MAX_INFO_STRING]; i = client - svs.clients; if (client->state == cs_free && sv_fastconnect.value) return; MSG_WriteByte (buf, svc_updatefrags); MSG_WriteByte (buf, i); MSG_WriteShort (buf, client->old_frags); MSG_WriteByte (buf, svc_updateping); MSG_WriteByte (buf, i); MSG_WriteShort (buf, SV_CalcPing (client)); MSG_WriteByte (buf, svc_updatepl); MSG_WriteByte (buf, i); MSG_WriteByte (buf, client->lossage); MSG_WriteByte (buf, svc_updateentertime); MSG_WriteByte (buf, i); MSG_WriteFloat (buf, svs.realtime - client->connection_started); strcpy (info, client->userinfo); Info_RemovePrefixedKeys (info, '_'); // server passwords, etc Info_RemoveKey (info, "pmodel"); Info_RemoveKey (info, "emodel"); MSG_WriteByte (buf, svc_updateuserinfo); MSG_WriteByte (buf, i); MSG_WriteLong (buf, client->userid); MSG_WriteString (buf, info); }
static void _Datagram_SearchForHosts (qboolean xmit) { int ret; int n; int i; struct qsockaddr readaddr; struct qsockaddr myaddr; int control; dfunc.GetSocketAddr (dfunc.controlSock, &myaddr); if (xmit) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize); SZ_Clear(&net_message); } while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0) { if (ret < sizeof(int)) continue; net_message.cursize = ret; // don't answer our own query if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0) continue; // is the cache full? if (hostCacheCount == HOSTCACHESIZE) continue; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) continue; if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) continue; if ((control & NETFLAG_LENGTH_MASK) != ret) continue; if (MSG_ReadByte() != CCREP_SERVER_INFO) continue; dfunc.GetAddrFromName(MSG_ReadString(), &readaddr); // search the cache for this server for (n = 0; n < hostCacheCount; n++) if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0) break; // is it already there? if (n < hostCacheCount) continue; // add it hostCacheCount++; Q_strcpy(hostcache[n].name, MSG_ReadString()); Q_strcpy(hostcache[n].map, MSG_ReadString()); hostcache[n].users = MSG_ReadByte(); hostcache[n].maxusers = MSG_ReadByte(); if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { Q_strcpy(hostcache[n].cname, hostcache[n].name); hostcache[n].cname[14] = 0; Q_strcpy(hostcache[n].name, "*"); Q_strcat(hostcache[n].name, hostcache[n].cname); } Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); hostcache[n].driver = net_driverlevel; hostcache[n].ldriver = net_landriverlevel; Q_strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); // check for a name conflict for (i = 0; i < hostCacheCount; i++) { if (i == n) continue; if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0) { i = Q_strlen(hostcache[n].name); if (i < 15 && hostcache[n].name[i-1] > '8') { hostcache[n].name[i] = '0'; hostcache[n].name[i+1] = 0; } else hostcache[n].name[i-1]++; i = -1; } } } }
/* * SV_New_f * * Sends the first message from the server to a connected client. * This will be sent on the initial connection and upon each server load. */ static void SV_New_f( client_t *client ) { int playernum; unsigned int numpure; purelist_t *purefile; edict_t *ent; int sv_bitflags = 0; Com_DPrintf( "New() from %s\n", client->name ); // if in CS_AWAITING we have sent the response packet the new once already, // but client might have not got it so we send it again if( client->state >= CS_SPAWNED ) { Com_Printf( "New not valid -- already spawned\n" ); return; } // // serverdata needs to go over for all types of servers // to make sure the protocol is right, and to set the gamedir // SV_InitClientMessage( client, &tmpMessage, NULL, 0 ); // send the serverdata MSG_WriteByte( &tmpMessage, svc_serverdata ); MSG_WriteLong( &tmpMessage, APP_PROTOCOL_VERSION ); MSG_WriteLong( &tmpMessage, svs.spawncount ); MSG_WriteShort( &tmpMessage, (unsigned short)svc.snapFrameTime ); MSG_WriteString( &tmpMessage, FS_BaseGameDirectory() ); MSG_WriteString( &tmpMessage, FS_GameDirectory() ); playernum = client - svs.clients; MSG_WriteShort( &tmpMessage, playernum ); // send full levelname MSG_WriteString( &tmpMessage, sv.mapname ); // // game server // if( sv.state == ss_game ) { // set up the entity for the client ent = EDICT_NUM( playernum+1 ); ent->s.number = playernum+1; client->edict = ent; if( sv_pure->integer ) sv_bitflags |= SV_BITFLAGS_PURE; if( client->reliable ) sv_bitflags |= SV_BITFLAGS_RELIABLE; if( SV_Web_Running() ) { const char *baseurl = SV_Web_UpstreamBaseUrl(); sv_bitflags |= SV_BITFLAGS_HTTP; if( baseurl[0] ) sv_bitflags |= SV_BITFLAGS_HTTP_BASEURL; } MSG_WriteByte( &tmpMessage, sv_bitflags ); } if( sv_bitflags & SV_BITFLAGS_HTTP ) { if( sv_bitflags & SV_BITFLAGS_HTTP_BASEURL ) MSG_WriteString( &tmpMessage, sv_http_upstream_baseurl->string ); else MSG_WriteShort( &tmpMessage, sv_http_port->integer ); // HTTP port number } // always write purelist numpure = Com_CountPureListFiles( svs.purelist ); if( numpure > (short)0x7fff ) Com_Error( ERR_DROP, "Error: Too many pure files." ); MSG_WriteShort( &tmpMessage, numpure ); purefile = svs.purelist; while( purefile ) { MSG_WriteString( &tmpMessage, purefile->filename ); MSG_WriteLong( &tmpMessage, purefile->checksum ); purefile = purefile->next; } SV_ClientResetCommandBuffers( client ); SV_SendMessageToClient( client, &tmpMessage ); Netchan_PushAllFragments( &client->netchan ); // don't let it send reliable commands until we get the first configstring request client->state = CS_CONNECTING; }
void CHelperManager_Encoder::SendMessage_RequestSummons(playerCharacter_t* pHelper,char* toName) { int idx=GTH_FindPCByName(toName); if (0 <= idx) { playerCharacter_t* pTaker=gcpTools->GetPlayerRecordPointer(idx); if(NULL == pTaker) return; if( (playerCharacter_t::tagGonryunBattlePractice::MEMBERSHIP_LEADER == pTaker->GonryunBattlePractice.MemberShip) || (playerCharacter_t::tagGonryunBattlePractice::MEMBERSHIP_OPENENT == pTaker->GonryunBattlePractice.MemberShip)) return; if((pTaker->worldIdx == tagGolryunBattle::Golryun_Battle_Map_Index) || (pTaker->worldIdx == DAN_BATTLEMAP_NO)) return; if(BUSY_STATE_NONE != pTaker->busyState) return; PC_SetSummonsInfo(pTaker, pHelper->name, pHelper->worldIdx, pHelper->position); MSG_BeginWriting(&netMessage); MSG_Clear( &netMessage ); { MSG_WriteByte(&netMessage, EXTEND_SECOND); MSG_WriteShort ( &netMessage, HELPER_SYSTEM ); MSG_WriteShort(&netMessage, SC_SPAWN_Req_toTaker); MSG_WriteString(&netMessage, pHelper->name); NET_SendMessage(&pTaker->sock, &netMessage); } MSG_EndWriting(&netMessage); } else { MSG_BeginWriting(&netMessage); MSG_Clear( &netMessage ); { MSG_WriteByte(&netMessage, EXTEND_SECOND); MSG_WriteShort ( &netMessage, HELPER_SYSTEM ); MSG_WriteShort(&netMessage, SS_SPAWN_Req_fromServer); MSG_WriteString(&netMessage, toName); MSG_WriteString(&netMessage, pHelper->name); MSG_WriteByte(&netMessage, pHelper->worldIdx); MSG_WritePosition(&netMessage, pHelper->position); if ( TRUE == g_config.isManager ) { for (int serveridx=1; serveridx < MAX_MEMBER_SERVER; serveridx++) { if ( !g_memberServer[serveridx].active ) continue; NET_SendUnreliableMessage(&g_memberServer[serveridx].sock, &netMessage); } } else { MSG_WriteByte(&netMessage, g_config.gameServerNo); NET_SendUnreliableMessage(&localSocket, &netMessage); } } MSG_EndWriting(&netMessage); } }
/* =================== CL_WritePacket Create and send the command packet to the server Including both the reliable commands and the usercmds During normal gameplay, a client packet will contain something like: 4 sequence number 2 qport 4 serverid 4 acknowledged sequence number 4 clc.serverCommandSequence <optional reliable commands> 1 clc_move or clc_moveNoDelta 1 command count <count * usercmds> =================== */ void CL_WritePacket( void ) { msg_t buf; byte data[MAX_MSGLEN]; int i, j; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int packetNum; int oldPacketNum; int count, key; // don't send anything if playing back a demo if ( clc.demoplaying || cls.state == CA_CINEMATIC ) { return; } Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; MSG_Init( &buf, data, sizeof(data) ); MSG_Bitstream( &buf ); // write the current serverId so the server // can tell if this is from the current gameState MSG_WriteLong( &buf, cl.serverId ); // write the last message we received, which can // be used for delta compression, and is also used // to tell if we dropped a gamestate MSG_WriteLong( &buf, clc.serverMessageSequence ); // write the last reliable message we received MSG_WriteLong( &buf, clc.serverCommandSequence ); // write any unacknowledged clientCommands for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) { MSG_WriteByte( &buf, clc_clientCommand ); MSG_WriteLong( &buf, i ); MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); } // we want to send all the usercmds that were generated in the last // few packet, so even if a couple packets are dropped in a row, // all the cmds will make it to the server if ( cl_packetdup->integer < 0 ) { Cvar_Set( "cl_packetdup", "0" ); } else if ( cl_packetdup->integer > 5 ) { Cvar_Set( "cl_packetdup", "5" ); } oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK; count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber; if ( count > MAX_PACKET_USERCMDS ) { count = MAX_PACKET_USERCMDS; Com_Printf("MAX_PACKET_USERCMDS\n"); } if ( count >= 1 ) { if ( cl_showSend->integer ) { Com_Printf( "(%i)", count ); } // begin a client move command if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting || clc.serverMessageSequence != cl.snap.messageNum ) { MSG_WriteByte (&buf, clc_moveNoDelta); } else { MSG_WriteByte (&buf, clc_move); } // write the command count MSG_WriteByte( &buf, count ); // use the checksum feed in the key key = clc.checksumFeed; // also use the message acknowledge key ^= clc.serverMessageSequence; // also use the last acknowledged server command in the key key ^= Com_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32); // write all the commands, including the predicted command for ( i = 0 ; i < count ; i++ ) { j = (cl.cmdNumber - count + i + 1) & CMD_MASK; cmd = &cl.cmds[j]; MSG_WriteDeltaUsercmdKey (&buf, key, oldcmd, cmd); oldcmd = cmd; } if (cl.gcmdSentValue) { //hmm, just clear here, I guess.. hoping it will resolve issues with gencmd values sometimes not going through. cl.gcmdSendValue = qfalse; cl.gcmdSentValue = qfalse; cl.gcmdValue = 0; } } // // deliver the message // packetNum = clc.netchan.outgoingSequence & PACKET_MASK; cl.outPackets[ packetNum ].p_realtime = cls.realtime; cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime; cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber; clc.lastPacketSentTime = cls.realtime; if ( cl_showSend->integer ) { Com_Printf( "%i ", buf.cursize ); } CL_Netchan_Transmit (&clc.netchan, &buf); // clients never really should have messages large enough // to fragment, but in case they do, fire them all off // at once while ( clc.netchan.unsentFragments ) { CL_Netchan_TransmitNextFragment( &clc.netchan ); } }
/* * SV_NextDownload_f * * Responds to reliable nextdl packet with unreliable download packet * If nextdl packet's offet information is negative, download will be stopped */ static void SV_NextDownload_f( client_t *client ) { int blocksize; int offset; if( !client->download.name ) { Com_Printf( "nextdl message for client with no download active, from: %s\n", client->name ); return; } if( Q_stricmp( client->download.name, Cmd_Argv( 1 ) ) ) { Com_Printf( "nextdl message for wrong filename, from: %s\n", client->name ); return; } offset = atoi( Cmd_Argv( 2 ) ); if( offset > client->download.size ) { Com_Printf( "nextdl message with too big offset, from: %s\n", client->name ); return; } if( offset == -1 ) { Com_Printf( "Upload of %s to %s%s completed\n", client->download.name, client->name, S_COLOR_WHITE ); if( client->download.data ) { FS_FreeBaseFile( client->download.data ); client->download.data = NULL; } Mem_ZoneFree( client->download.name ); client->download.name = NULL; client->download.size = 0; client->download.timeout = 0; return; } if( offset < 0 ) { Com_Printf( "Upload of %s to %s%s failed\n", client->download.name, client->name, S_COLOR_WHITE ); if( client->download.data ) { FS_FreeBaseFile( client->download.data ); client->download.data = NULL; } Mem_ZoneFree( client->download.name ); client->download.name = NULL; client->download.size = 0; client->download.timeout = 0; return; } if( !client->download.data ) { Com_Printf( "Starting server upload of %s to %s\n", client->download.name, client->name ); FS_LoadBaseFile( client->download.name, (void **)&client->download.data, NULL, 0 ); if( !client->download.data ) { Com_Printf( "Error loading %s for uploading\n", client->download.name ); Mem_ZoneFree( client->download.name ); client->download.name = NULL; client->download.size = 0; client->download.timeout = 0; return; } } SV_InitClientMessage( client, &tmpMessage, NULL, 0 ); SV_AddReliableCommandsToMessage( client, &tmpMessage ); blocksize = client->download.size - offset; // jalfixme: adapt download to user rate setting and sv_maxrate setting. if( blocksize > FRAGMENT_SIZE * 2 ) blocksize = FRAGMENT_SIZE * 2; if( offset + blocksize > client->download.size ) blocksize = client->download.size - offset; MSG_WriteByte( &tmpMessage, svc_download ); MSG_WriteString( &tmpMessage, client->download.name ); MSG_WriteLong( &tmpMessage, offset ); MSG_WriteLong( &tmpMessage, blocksize ); MSG_CopyData( &tmpMessage, client->download.data + offset, blocksize ); SV_SendMessageToClient( client, &tmpMessage ); client->download.timeout = svs.realtime + 10000; }
void CHelperManager_Encoder::SendMessage_Remove_Notify_toRemovePlayer( playerCharacter_t* pPlayer,char* premoveName) { int idx=GTH_FindPCByName(premoveName); if (0 <= idx ) { playerCharacter_t* pRemoveUser=gcpTools->GetPlayerRecordPointer(idx); if(NULL == pRemoveUser)return; MSG_BeginWriting(&netMessage); MSG_Clear( &netMessage ); { MSG_WriteByte(&netMessage, EXTEND_SECOND); MSG_WriteShort( &netMessage, HELPER_SYSTEM ); MSG_WriteShort( &netMessage, SC_REMOVE_NOTIFY_toPlayer ); MSG_WriteString(&netMessage, pPlayer->name); NET_SendUnreliableMessage(&pRemoveUser->sock, &netMessage); } MSG_EndWriting(&netMessage); ShowLogInfo("RemoveUser :pRemoveUser->name:%s, pRemoveUser->name:%s", pRemoveUser->name,premoveName); } else { MSG_BeginWriting(&netMessage); MSG_Clear( &netMessage ); { MSG_WriteByte(&netMessage, EXTEND_SECOND); MSG_WriteShort( &netMessage, HELPER_SYSTEM); MSG_WriteShort( &netMessage, SS_REMOVE_NOTIFY_toPlayer ); MSG_WriteString(&netMessage, premoveName); MSG_WriteString(&netMessage, pPlayer->name); if ( TRUE == g_config.isManager ) { for (int serveridx=1; serveridx < MAX_MEMBER_SERVER; serveridx++) { if ( !g_memberServer[serveridx].active ) continue; NET_SendUnreliableMessage(&g_memberServer[serveridx].sock, &netMessage); } } else { MSG_WriteByte(&netMessage, g_config.gameServerNo); NET_SendUnreliableMessage(&localSocket, &netMessage); } } MSG_EndWriting(&netMessage); } }
/* ================= 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); }
/* ============== SV_ServerRecord_f Begins server demo recording. Every entity and every message will be recorded, but no playerinfo will be stored. Primarily for demo merging. ============== */ static void SV_ServerRecord_f (void) { char name[MAX_OSPATH]; byte buf_data[32768]; sizebuf_t buf; int len; int i; size_t size; if (Cmd_Argc() != 2) { Com_Printf ("serverrecord <demoname>\n"); return; } if (svs.demofile) { Com_Printf ("Already recording.\n"); return; } if (sv.state != ss_game) { Com_Printf ("You must be in a level to record.\n"); return; } // // open the demo file // Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1)); FS_CreatePath(name); svs.demofile = fopen (name, "wb"); if (!svs.demofile) { Com_Printf ("ERROR: couldn't open.\n"); return; } Com_Printf ("recording to %s.\n", name); // setup a buffer to catch all multicasts SZ_Init (&svs.demo_multicast, svs.demo_multicast_buf, sizeof(svs.demo_multicast_buf)); // // write a single giant fake message with all the startup info // SZ_Init (&buf, buf_data, sizeof(buf_data)); // // serverdata needs to go over for all types of servers // to make sure the protocol is right, and to set the gamedir // // send the serverdata MSG_WriteByte (&buf, svc_serverdata); MSG_WriteLong (&buf, PROTOCOL_VERSION_DEFAULT); MSG_WriteLong (&buf, svs.spawncount); // 2 means server demo MSG_WriteByte (&buf, 2); // demos are always attract loops MSG_WriteString (&buf, Cvar_VariableString ("gamedir")); MSG_WriteShort (&buf, -1); // send full levelname MSG_WriteString (&buf, sv.configstrings[CS_NAME]); for (i=0 ; i<MAX_CONFIGSTRINGS ; i++) if (sv.configstrings[i][0]) { MSG_WriteByte (&buf, svc_configstring); MSG_WriteShort (&buf, i); MSG_WriteString (&buf, sv.configstrings[i]); } // write it to the demo file Com_DPrintf ("signon message length: %i\n", buf.cursize); len = LittleLong (buf.cursize); size = fwrite (&len, 4, 1, svs.demofile); size = fwrite (buf.data, buf.cursize, 1, svs.demofile); // the rest of the demo file will be individual frames }
/* =================== CL_WritePacket Create and send the command packet to the server Including both the reliable commands and the usercmds During normal gameplay, a client packet will contain something like: 4 sequence number 2 qport 4 serverid 4 acknowledged sequence number 4 clc.serverCommandSequence <optional reliable commands> 1 clc_move or clc_moveNoDelta 1 command count <count * usercmds> =================== */ void CL_WritePacket( void ) { msg_t buf; byte data[MAX_MSGLEN]; int i, j; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int packetNum; int oldPacketNum; int count, key; // don't send anything if playing back a demo if ( clc.demoplaying || clc.state == CA_CINEMATIC ) { return; } Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; MSG_Init( &buf, data, sizeof(data) ); MSG_Bitstream( &buf ); // write the current serverId so the server // can tell if this is from the current gameState MSG_WriteLong( &buf, cl.serverId ); // write the last message we received, which can // be used for delta compression, and is also used // to tell if we dropped a gamestate MSG_WriteLong( &buf, clc.serverMessageSequence ); // write the last reliable message we received MSG_WriteLong( &buf, clc.serverCommandSequence ); // write any unacknowledged clientCommands for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) { MSG_WriteByte( &buf, clc_clientCommand ); MSG_WriteLong( &buf, i ); MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); } // we want to send all the usercmds that were generated in the last // few packet, so even if a couple packets are dropped in a row, // all the cmds will make it to the server if ( cl_packetdup->integer < 0 ) { Cvar_Set( "cl_packetdup", "0" ); } else if ( cl_packetdup->integer > 5 ) { Cvar_Set( "cl_packetdup", "5" ); } oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK; count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber; if ( count > MAX_PACKET_USERCMDS ) { count = MAX_PACKET_USERCMDS; Com_Printf("MAX_PACKET_USERCMDS\n"); } #ifdef USE_VOIP if (clc.voipOutgoingDataSize > 0) { if((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1)) { MSG_WriteByte (&buf, clc_voip); MSG_WriteByte (&buf, clc.voipOutgoingGeneration); MSG_WriteLong (&buf, clc.voipOutgoingSequence); MSG_WriteByte (&buf, clc.voipOutgoingDataFrames); MSG_WriteData (&buf, clc.voipTargets, sizeof(clc.voipTargets)); MSG_WriteByte(&buf, clc.voipFlags); MSG_WriteShort (&buf, clc.voipOutgoingDataSize); MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize); // If we're recording a demo, we have to fake a server packet with // this VoIP data so it gets to disk; the server doesn't send it // back to us, and we might as well eliminate concerns about dropped // and misordered packets here. if(clc.demorecording && !clc.demowaiting) { const int voipSize = clc.voipOutgoingDataSize; msg_t fakemsg; byte fakedata[MAX_MSGLEN]; MSG_Init (&fakemsg, fakedata, sizeof (fakedata)); MSG_Bitstream (&fakemsg); MSG_WriteLong (&fakemsg, clc.reliableAcknowledge); MSG_WriteByte (&fakemsg, svc_voip); MSG_WriteShort (&fakemsg, clc.clientNum); MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration); MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence); MSG_WriteByte (&fakemsg, clc.voipOutgoingDataFrames); MSG_WriteShort (&fakemsg, clc.voipOutgoingDataSize ); MSG_WriteBits (&fakemsg, clc.voipFlags, VOIP_FLAGCNT); MSG_WriteData (&fakemsg, clc.voipOutgoingData, voipSize); MSG_WriteByte (&fakemsg, svc_EOF); CL_WriteDemoMessage (&fakemsg, 0); } clc.voipOutgoingSequence += clc.voipOutgoingDataFrames; clc.voipOutgoingDataSize = 0; clc.voipOutgoingDataFrames = 0; } else { // We have data, but no targets. Silently discard all data clc.voipOutgoingDataSize = 0; clc.voipOutgoingDataFrames = 0; } } #endif if ( count >= 1 ) { if ( cl_showSend->integer ) { Com_Printf( "(%i)", count ); } // begin a client move command if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting || clc.serverMessageSequence != cl.snap.messageNum ) { MSG_WriteByte (&buf, clc_moveNoDelta); } else { MSG_WriteByte (&buf, clc_move); } // write the command count MSG_WriteByte( &buf, count ); // use the checksum feed in the key key = clc.checksumFeed; // also use the message acknowledge key ^= clc.serverMessageSequence; // also use the last acknowledged server command in the key key ^= MSG_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32); // write all the commands, including the predicted command for ( i = 0 ; i < count ; i++ ) { j = (cl.cmdNumber - count + i + 1) & CMD_MASK; cmd = &cl.cmds[j]; MSG_WriteDeltaUsercmdKey (&buf, key, oldcmd, cmd); oldcmd = cmd; } } // // deliver the message // packetNum = clc.netchan.outgoingSequence & PACKET_MASK; cl.outPackets[ packetNum ].p_realtime = cls.realtime; cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime; cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber; clc.lastPacketSentTime = cls.realtime; if ( cl_showSend->integer ) { Com_Printf( "%i ", buf.cursize ); } CL_Netchan_Transmit (&clc.netchan, &buf); }
/* =============== CL_QueueHTTPDownload Called from the precache check to queue a download. Return value of false will cause standard UDP downloading to be used instead. =============== */ qboolean CL_QueueHTTPDownload (const char *quakePath) { size_t len; dlqueue_t *q; qboolean needList; // no http server (or we got booted) if (!cls.downloadServer[0] || abortDownloads || thisMapAbort || !cl_http_downloads->value) return false; needList = false; // first download queued, so we want the mod filelist if (!cls.downloadQueue.next && cl_http_filelists->value) needList = true; q = &cls.downloadQueue; while (q->next) { q = q->next; //avoid sending duplicate requests if (!strcmp (quakePath, q->quakePath)) return true; } // q->next = Z_TagMalloc (sizeof(*q), TAGMALLOC_CLIENT_DOWNLOAD); q->next = Z_TagMalloc (sizeof(*q), 0); q = q->next; q->next = NULL; q->state = DLQ_STATE_NOT_STARTED; Q_strncpyz (q->quakePath, quakePath, sizeof(q->quakePath)-1); if (needList) { //grab the filelist CL_QueueHTTPDownload (va("%s.filelist", cl.gamedir)); //this is a nasty hack to let the server know what we're doing so admins don't //get confused by a ton of people stuck in CNCT state. it's assumed the server //is running r1q2 if we're even able to do http downloading so hopefully this //won't spew an error msg. // MSG_BeginWriting (clc_stringcmd); // MSG_WriteString ("download http\n"); // MSG_EndWriting (&cls.netchan.message); MSG_WriteByte (&cls.netchan.message, clc_stringcmd); MSG_WriteString (&cls.netchan.message, "download http\n"); } //special case for map file lists, i really wanted a server-push mechanism for this, but oh well len = strlen (quakePath); if (cl_http_filelists->value && len > 4 && !Q_stricmp ((char *)(quakePath + len - 4), ".bsp")) { char listPath[MAX_OSPATH]; char filePath[MAX_OSPATH]; Com_sprintf (filePath, sizeof(filePath), "%s/%s", cl.gamedir, quakePath); COM_StripExtension (filePath, listPath); // strncat (listPath, ".filelist"); Q_strncatz (listPath, ".filelist", sizeof(listPath)); CL_QueueHTTPDownload (listPath); } //if a download entry has made it this far, CL_FinishHTTPDownload is guaranteed to be called. pendingCount++; return true; }
// // SV_SendServerInfo // // Sends server info to a launcher // TODO: Clean up and reinvent. void SV_SendServerInfo() { size_t i; SZ_Clear(&ml_message); MSG_WriteLong(&ml_message, CHALLENGE); MSG_WriteLong(&ml_message, SV_NewToken()); // if master wants a key to be presented, present it we will if(MSG_BytesLeft() == 4) MSG_WriteLong(&ml_message, MSG_ReadLong()); MSG_WriteString(&ml_message, (char *)hostname.cstring()); byte playersingame = 0; for (i = 0; i < players.size(); ++i) { if (players[i].ingame()) playersingame++; } MSG_WriteByte(&ml_message, playersingame); MSG_WriteByte(&ml_message, maxclients); MSG_WriteString(&ml_message, level.mapname); size_t numwads = wadnames.size(); if(numwads > 0xff)numwads = 0xff; MSG_WriteByte(&ml_message, numwads - 1); for (i = 1; i < numwads; ++i) MSG_WriteString(&ml_message, wadnames[i].c_str()); MSG_WriteByte(&ml_message, (int)deathmatch); MSG_WriteByte(&ml_message, (int)skill); MSG_WriteByte(&ml_message, (int)teamplay); MSG_WriteByte(&ml_message, (int)ctfmode); for (i = 0; i < players.size(); ++i) { if (players[i].ingame()) { MSG_WriteString(&ml_message, players[i].userinfo.netname); MSG_WriteShort(&ml_message, players[i].fragcount); MSG_WriteLong(&ml_message, players[i].ping); if (teamplay || ctfmode) MSG_WriteByte(&ml_message, players[i].userinfo.team); else MSG_WriteByte(&ml_message, TEAM_NONE); } } for (i = 1; i < numwads; ++i) MSG_WriteString(&ml_message, wadhashes[i].c_str()); MSG_WriteString(&ml_message, website.cstring()); if (ctfmode || teamplay) { MSG_WriteLong(&ml_message, scorelimit); for(size_t i = 0; i < NUMTEAMS; i++) { MSG_WriteByte(&ml_message, TEAMenabled[i]); if (TEAMenabled[i]) MSG_WriteLong(&ml_message, TEAMpoints[i]); } } MSG_WriteShort(&ml_message, VERSION); //bond=========================== MSG_WriteString(&ml_message, (char *)email.cstring()); int timeleft = (int)(timelimit - level.time/(TICRATE*60)); if (timeleft<0) timeleft=0; MSG_WriteShort(&ml_message,(int)timelimit); MSG_WriteShort(&ml_message,timeleft); MSG_WriteShort(&ml_message,(int)fraglimit); MSG_WriteByte(&ml_message,(BOOL)itemsrespawn); MSG_WriteByte(&ml_message,(BOOL)weaponstay); MSG_WriteByte(&ml_message,(BOOL)friendlyfire); MSG_WriteByte(&ml_message,(BOOL)allowexit); MSG_WriteByte(&ml_message,(BOOL)infiniteammo); MSG_WriteByte(&ml_message,(BOOL)nomonsters); MSG_WriteByte(&ml_message,(BOOL)monstersrespawn); MSG_WriteByte(&ml_message,(BOOL)fastmonsters); MSG_WriteByte(&ml_message,(BOOL)allowjump); MSG_WriteByte(&ml_message,(BOOL)allowfreelook); MSG_WriteByte(&ml_message,(BOOL)waddownload); MSG_WriteByte(&ml_message,(BOOL)emptyreset); MSG_WriteByte(&ml_message,(BOOL)cleanmaps); MSG_WriteByte(&ml_message,(BOOL)fragexitswitch); for (i = 0; i < players.size(); ++i) { if (players[i].ingame()) { MSG_WriteShort(&ml_message, players[i].killcount); MSG_WriteShort(&ml_message, players[i].deathcount); int timeingame = (time(NULL) - players[i].JoinTime)/60; if (timeingame<0) timeingame=0; MSG_WriteShort(&ml_message, timeingame); } } //bond=========================== MSG_WriteLong(&ml_message, (DWORD)0x01020304); MSG_WriteShort(&ml_message, (WORD)maxplayers); for (i = 0; i < players.size(); ++i) { if (players[i].ingame()) { MSG_WriteByte(&ml_message, (players[i].spectator ? true : false)); } } MSG_WriteLong(&ml_message, (DWORD)0x01020305); MSG_WriteShort(&ml_message, strlen(password.cstring()) ? 1 : 0); NET_SendPacket(ml_message, net_from); }
static qbool SV_MVD_Record (mvddest_t *dest) { sizebuf_t buf; byte buf_data[MAX_MSGLEN]; int n, i; char *s, info[MAX_INFO_STRING]; client_t *player; char *gamedir; if (!dest) return false; DestFlush(true); if (!sv.mvdrecording) { memset(&demo, 0, sizeof(demo)); for (i = 0; i < UPDATE_BACKUP; i++) { demo.recorder.frames[i] = demo_frames[i]; demo.recorder.frames[i].entities.entities = demo_entities[i]; } MVDBuffer_Init(&demo.dbuffer, demo.buffer, sizeof(demo.buffer)); MVDSetMsgBuf(NULL, &demo.frames[0].buf); demo.datagram.maxsize = sizeof(demo.datagram_data); demo.datagram.data = demo.datagram_data; sv.mvdrecording = true; } // else // SV_WriteRecordMVDMessage(&buf); demo.pingtime = demo.time = sv.time; dest->nextdest = demo.dest; demo.dest = dest; singledest = dest; /*-------------------------------------------------*/ // serverdata // send the info about the new client to all connected clients SZ_Init (&buf, buf_data, sizeof(buf_data)); // send the serverdata gamedir = Info_ValueForKey (svs.info, "*gamedir"); if (!gamedir[0]) gamedir = "qw"; MSG_WriteByte (&buf, svc_serverdata); MSG_WriteLong (&buf, PROTOCOL_VERSION); MSG_WriteLong (&buf, svs.spawncount); MSG_WriteString (&buf, gamedir); MSG_WriteFloat (&buf, sv.time); // send full levelname MSG_WriteString (&buf, sv.mapname); // send the movevars MSG_WriteFloat(&buf, sv.movevars.gravity); MSG_WriteFloat(&buf, sv.movevars.stopspeed); MSG_WriteFloat(&buf, sv.movevars.maxspeed); MSG_WriteFloat(&buf, sv.movevars.spectatormaxspeed); MSG_WriteFloat(&buf, sv.movevars.accelerate); MSG_WriteFloat(&buf, sv.movevars.airaccelerate); MSG_WriteFloat(&buf, sv.movevars.wateraccelerate); MSG_WriteFloat(&buf, sv.movevars.friction); MSG_WriteFloat(&buf, sv.movevars.waterfriction); MSG_WriteFloat(&buf, sv.movevars.entgravity); // send music MSG_WriteByte (&buf, svc_cdtrack); MSG_WriteByte (&buf, 0); // none in demos // send server info string MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", svs.info) ); // flush packet SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); // soundlist MSG_WriteByte (&buf, svc_soundlist); MSG_WriteByte (&buf, 0); n = 0; s = sv.sound_name[n+1]; while (s) { MSG_WriteString (&buf, s); if (buf.cursize > MAX_MSGLEN/2) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); MSG_WriteByte (&buf, svc_soundlist); MSG_WriteByte (&buf, n + 1); } n++; s = sv.sound_name[n+1]; } if (buf.cursize) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } // modellist MSG_WriteByte (&buf, svc_modellist); MSG_WriteByte (&buf, 0); n = 0; s = sv.model_name[n+1]; while (s) { MSG_WriteString (&buf, s); if (buf.cursize > MAX_MSGLEN/2) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); MSG_WriteByte (&buf, svc_modellist); MSG_WriteByte (&buf, n + 1); } n++; s = sv.model_name[n+1]; } if (buf.cursize) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } // baselines { entity_state_t from; edict_t *ent; entity_state_t *state; memset(&from, 0, sizeof(from)); for (n = 0; n < sv.num_edicts; n++) { ent = EDICT_NUM(n); state = &ent->baseline; if (!state->number || !state->modelindex) { //ent doesn't have a baseline continue; } if (!ent) { MSG_WriteByte(&buf, svc_spawnbaseline); MSG_WriteShort (&buf, n); MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); for (i=0 ; i<3 ; i++) { MSG_WriteCoord(&buf, 0); MSG_WriteAngle(&buf, 0); } } else { MSG_WriteByte(&buf, svc_spawnbaseline); MSG_WriteShort (&buf, n); MSG_WriteByte (&buf, state->modelindex&255); MSG_WriteByte (&buf, state->frame); MSG_WriteByte (&buf, (int)state->colormap); MSG_WriteByte (&buf, (int)state->skinnum); for (i=0 ; i<3 ; i++) { MSG_WriteCoord(&buf, state->s_origin[i]); MSG_WriteAngle(&buf, state->s_angles[i]); } } if (buf.cursize > MAX_MSGLEN/2) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } } } //prespawn for (n = 0; n < sv.num_signon_buffers; n++) { if (buf.cursize+sv.signon_buffer_size[n] > MAX_MSGLEN/2) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } SZ_Write (&buf, sv.signon_buffers[n], sv.signon_buffer_size[n]); } if (buf.cursize > MAX_MSGLEN/2) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("cmd spawn %i\n",svs.spawncount) ); if (buf.cursize) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } // send current status of all other players for (i = 0; i < MAX_CLIENTS; i++) { player = svs.clients + i; MSG_WriteByte (&buf, svc_updatefrags); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, player->old_frags); MSG_WriteByte (&buf, svc_updateping); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, SV_CalcPing(player)); MSG_WriteByte (&buf, svc_updatepl); MSG_WriteByte (&buf, i); MSG_WriteByte (&buf, player->lossage); MSG_WriteByte (&buf, svc_updateentertime); MSG_WriteByte (&buf, i); MSG_WriteFloat (&buf, svs.realtime - player->connection_started); Q_strncpyz (info, player->userinfo, MAX_INFO_STRING); Info_RemovePrefixedKeys (info, '_'); // server passwords, etc MSG_WriteByte (&buf, svc_updateuserinfo); MSG_WriteByte (&buf, i); MSG_WriteLong (&buf, player->userid); MSG_WriteString (&buf, info); if (buf.cursize > MAX_MSGLEN/2) { SV_WriteRecordMVDMessage (&buf); SZ_Clear (&buf); } } // send all current light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { MSG_WriteByte (&buf, svc_lightstyle); MSG_WriteByte (&buf, (char)i); MSG_WriteString (&buf, sv.lightstyles[i]); } // get the client to check and download skins // when that is completed, a begin command will be issued MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, "skins\n"); SV_WriteRecordMVDMessage (&buf); SV_WriteSetMVDMessage(); singledest = NULL; // done return true; }
/* ==================== CL_Record_f record <demoname> Begins recording a demo from the current position ==================== */ void CL_Record_f (void) { char name[MAX_OSPATH]; char buf_data[MAX_MSGLEN]; sizebuf_t buf; int i; int len; entity_state_t *ent; entity_state_t nullstate; if (Cmd_Argc() != 2) { Com_Printf ("record <demoname>\n"); return; } if (cls.demorecording) { Com_Printf ("Already recording.\n"); return; } if (cls.state != ca_active) { Com_Printf ("You must be in a level to record.\n"); return; } // // open the demo file // Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1)); Com_Printf ("recording to %s.\n", name); FS_CreatePath (name); cls.demofile = fopen (name, "wb"); if (!cls.demofile) { Com_Printf ("ERROR: couldn't open.\n"); return; } cls.demorecording = true; // don't start saving messages until a non-delta compressed message is received cls.demowaiting = true; // // write out messages to hold the startup information // SZ_Init (&buf, buf_data, sizeof(buf_data)); // send the serverdata MSG_WriteByte (&buf, svc_serverdata); MSG_WriteLong (&buf, PROTOCOL_VERSION); MSG_WriteLong (&buf, 0x10000 + cl.servercount); MSG_WriteByte (&buf, 1); // demos are always attract loops MSG_WriteString (&buf, cl.gamedir); MSG_WriteShort (&buf, cl.playernum); MSG_WriteString (&buf, cl.configstrings[CS_NAME]); // configstrings for (i=0 ; i<MAX_CONFIGSTRINGS ; i++) { if (cl.configstrings[i][0]) { if (buf.cursize + strlen (cl.configstrings[i]) + 32 > buf.maxsize) { // write it out len = LittleLong (buf.cursize); fwrite (&len, 4, 1, cls.demofile); fwrite (buf.data, buf.cursize, 1, cls.demofile); buf.cursize = 0; } MSG_WriteByte (&buf, svc_configstring); MSG_WriteShort (&buf, i); MSG_WriteString (&buf, cl.configstrings[i]); } } // baselines memset (&nullstate, 0, sizeof(nullstate)); for (i=0; i<MAX_EDICTS ; i++) { ent = &cl_entities[i].baseline; if (!ent->modelindex) continue; if (buf.cursize + 64 > buf.maxsize) { // write it out len = LittleLong (buf.cursize); fwrite (&len, 4, 1, cls.demofile); fwrite (buf.data, buf.cursize, 1, cls.demofile); buf.cursize = 0; } MSG_WriteByte (&buf, svc_spawnbaseline); MSG_WriteDeltaEntity (&nullstate, &cl_entities[i].baseline, &buf, true, true); } MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, "precache\n"); // write it to the demo file len = LittleLong (buf.cursize); fwrite (&len, 4, 1, cls.demofile); fwrite (buf.data, buf.cursize, 1, cls.demofile); // the rest of the demo file will be individual frames }
/* ===================== SV_DropClient Called when the player is getting totally kicked off the host if (crash = true), don't bother sending signofs ===================== */ void SV_DropClient(qboolean crash) { prvm_prog_t *prog = SVVM_prog; int i; Con_Printf("Client \"%s\" dropped\n", host_client->name); SV_StopDemoRecording(host_client); // make sure edict is not corrupt (from a level change for example) host_client->edict = PRVM_EDICT_NUM(host_client - svs.clients + 1); if (host_client->netconnection) { // tell the client to be gone if (!crash) { // LordHavoc: no opportunity for resending, so use unreliable 3 times unsigned char bufdata[8]; sizebuf_t buf; memset(&buf, 0, sizeof(buf)); buf.data = bufdata; buf.maxsize = sizeof(bufdata); MSG_WriteByte(&buf, svc_disconnect); NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false); NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false); NetConn_SendUnreliableMessage(host_client->netconnection, &buf, sv.protocol, 10000, 0, false); } } // call qc ClientDisconnect function // LordHavoc: don't call QC if server is dead (avoids recursive // Host_Error in some mods when they run out of edicts) if (host_client->clientconnectcalled && sv.active && host_client->edict) { // call the prog function for removing a client // this will set the body to a dead frame, among other things int saveSelf = PRVM_serverglobaledict(self); host_client->clientconnectcalled = false; PRVM_serverglobalfloat(time) = sv.time; PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict); prog->ExecuteProgram(prog, PRVM_serverfunction(ClientDisconnect), "QC function ClientDisconnect is missing"); PRVM_serverglobaledict(self) = saveSelf; } if (host_client->netconnection) { // break the net connection NetConn_Close(host_client->netconnection); host_client->netconnection = NULL; } // if a download is active, close it if (host_client->download_file) { Con_DPrintf("Download of %s aborted when %s dropped\n", host_client->download_name, host_client->name); FS_Close(host_client->download_file); host_client->download_file = NULL; host_client->download_name[0] = 0; host_client->download_expectedposition = 0; host_client->download_started = false; } // remove leaving player from scoreboard host_client->name[0] = 0; host_client->colors = 0; host_client->frags = 0; // send notification to all clients // get number of client manually just to make sure we get it right... i = host_client - svs.clients; MSG_WriteByte (&sv.reliable_datagram, svc_updatename); MSG_WriteByte (&sv.reliable_datagram, i); MSG_WriteString (&sv.reliable_datagram, host_client->name); MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors); MSG_WriteByte (&sv.reliable_datagram, i); MSG_WriteByte (&sv.reliable_datagram, host_client->colors); MSG_WriteByte (&sv.reliable_datagram, svc_updatefrags); MSG_WriteByte (&sv.reliable_datagram, i); MSG_WriteShort (&sv.reliable_datagram, host_client->frags); // free the client now if (host_client->entitydatabase) EntityFrame_FreeDatabase(host_client->entitydatabase); if (host_client->entitydatabase4) EntityFrame4_FreeDatabase(host_client->entitydatabase4); if (host_client->entitydatabase5) EntityFrame5_FreeDatabase(host_client->entitydatabase5); if (sv.active) { // clear a fields that matter to DP_SV_CLIENTNAME and DP_SV_CLIENTCOLORS, and also frags PRVM_ED_ClearEdict(prog, host_client->edict); } // clear the client struct (this sets active to false) memset(host_client, 0, sizeof(*host_client)); // update server listing on the master because player count changed // (which the master uses for filtering empty/full servers) NetConn_Heartbeat(1); if (sv.loadgame) { for (i = 0;i < svs.maxclients;i++) if (svs.clients[i].active && !svs.clients[i].spawned) break; if (i == svs.maxclients) { Con_Printf("Loaded game, everyone rejoined - unpausing\n"); sv.paused = sv.loadgame = false; // we're basically done with loading now } } }
/* =================== CL_WritePacket Create and send the command packet to the server Including both the reliable commands and the usercmds During normal gameplay, a client packet will contain something like: 4 sequence number 2 qport 4 serverid 4 acknowledged sequence number 4 clc.serverCommandSequence <optional reliable commands> 1 clc_move or clc_moveNoDelta 1 command count <count * usercmds> =================== */ void CL_WritePacket( void ) { msg_t buf; byte data[MAX_MSGLEN]; int i, j; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int packetNum; int oldPacketNum; int count; // don't send anything if playing back a demo // if ( cls.state == CA_CINEMATIC ) if ( cls.state == CA_CINEMATIC || CL_IsRunningInGameCinematic()) { return; } MSG_Init( &buf, data, sizeof(data) ); // write any unacknowledged clientCommands for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) { MSG_WriteByte( &buf, clc_clientCommand ); MSG_WriteLong( &buf, i ); MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); } // we want to send all the usercmds that were generated in the last // few packet, so even if a couple packets are dropped in a row, // all the cmds will make it to the server if ( cl_packetdup->integer < 0 ) { Cvar_Set( "cl_packetdup", "0" ); } else if ( cl_packetdup->integer > 5 ) { Cvar_Set( "cl_packetdup", "5" ); } oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK; count = cl.cmdNumber - cl.packetCmdNumber[ oldPacketNum ]; if ( count > MAX_PACKET_USERCMDS ) { count = MAX_PACKET_USERCMDS; Com_Printf("MAX_PACKET_USERCMDS\n"); } if ( count >= 1 ) { // begin a client move command MSG_WriteByte (&buf, clc_move); // write the last reliable message we received MSG_WriteLong( &buf, clc.serverCommandSequence ); // write the current serverId so the server // can tell if this is from the current gameState MSG_WriteLong (&buf, cl.serverId); // write the current time MSG_WriteLong (&buf, cls.realtime); // let the server know what the last messagenum we // got was, so the next message can be delta compressed // FIXME: this could just be a bit flag, with the message implicit // from the unreliable ack of the netchan if (cl_nodelta->integer || !cl.frame.valid) { MSG_WriteLong (&buf, -1); // no compression } else { MSG_WriteLong (&buf, cl.frame.messageNum); } // write the cmdNumber so the server can determine which ones it // has already received MSG_WriteLong( &buf, cl.cmdNumber ); // write the command count MSG_WriteByte( &buf, count ); // write all the commands, including the predicted command memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; for ( i = 0 ; i < count ; i++ ) { j = (cl.cmdNumber - count + i + 1) & CMD_MASK; cmd = &cl.cmds[j]; MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd); oldcmd = cmd; } } // // deliver the message // packetNum = clc.netchan.outgoingSequence & PACKET_MASK; cl.packetTime[ packetNum ] = cls.realtime; cl.packetCmdNumber[ packetNum ] = cl.cmdNumber; clc.lastPacketSentTime = cls.realtime; Netchan_Transmit (&clc.netchan, buf.cursize, buf.data); }
/* =================== CL_WritePacket Create and send the command packet to the server Including both the reliable commands and the usercmds During normal gameplay, a client packet will contain something like: 4 sequence number 2 qport 4 serverid 4 acknowledged sequence number 4 clc.serverCommandSequence <optional reliable commands> 1 clc_move or clc_moveNoDelta 1 command count <count * usercmds> =================== */ void CL_WritePacket( void ) { msg_t buf; byte data[MAX_MSGLEN]; int i, j; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int packetNum; int oldPacketNum; int count, key; // don't send anything if playing back a demo if ( clc.demoplaying || cls.state == CA_CINEMATIC ) { return; } Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; MSG_Init( &buf, data, sizeof(data) ); MSG_Bitstream( &buf ); // write the current serverId so the server // can tell if this is from the current gameState MSG_WriteLong( &buf, cl.serverId ); // write the last message we received, which can // be used for delta compression, and is also used // to tell if we dropped a gamestate MSG_WriteLong( &buf, clc.serverMessageSequence ); // write the last reliable message we received MSG_WriteLong( &buf, clc.serverCommandSequence ); // write any unacknowledged clientCommands for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) { MSG_WriteByte( &buf, clc_clientCommand ); MSG_WriteLong( &buf, i ); MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] ); } // we want to send all the usercmds that were generated in the last // few packet, so even if a couple packets are dropped in a row, // all the cmds will make it to the server if ( cl_packetdup->integer < 0 ) { Cvar_Set( "cl_packetdup", "0" ); } else if ( cl_packetdup->integer > 5 ) { Cvar_Set( "cl_packetdup", "5" ); } oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK; count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber; if ( count > MAX_PACKET_USERCMDS ) { count = MAX_PACKET_USERCMDS; Com_Printf("MAX_PACKET_USERCMDS\n"); } #ifdef USE_VOIP if (clc.voipOutgoingDataSize > 0) { // only send if data. // Move cl_voipSendTarget from a string to the bitmasks if needed. if (cl_voipSendTarget->modified) { char buffer[32]; const char *target = cl_voipSendTarget->string; if (Q_stricmp(target, "attacker") == 0) { int player = VM_Call( cgvm, CG_LAST_ATTACKER ); Com_sprintf(buffer, sizeof (buffer), "%d", player); target = buffer; } else if (Q_stricmp(target, "crosshair") == 0) { int player = VM_Call( cgvm, CG_CROSSHAIR_PLAYER ); Com_sprintf(buffer, sizeof (buffer), "%d", player); target = buffer; } if ((*target == '\0') || (Q_stricmp(target, "all") == 0)) { const int all = 0x7FFFFFFF; clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = all; } else if (Q_stricmp(target, "none") == 0) { clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0; } else { const char *ptr = target; clc.voipTarget1 = clc.voipTarget2 = clc.voipTarget3 = 0; do { if ((*ptr == ',') || (*ptr == '\0')) { const int val = atoi(target); target = ptr + 1; if ((val >= 0) && (val < 31)) { clc.voipTarget1 |= (1 << (val-0)); } else if ((val >= 31) && (val < 62)) { clc.voipTarget2 |= (1 << (val-31)); } else if ((val >= 62) && (val < 93)) { clc.voipTarget3 |= (1 << (val-62)); } } } while (*(ptr++)); } cl_voipSendTarget->modified = qfalse; } MSG_WriteByte (&buf, clc_EOF); // placate legacy servers. MSG_WriteByte (&buf, clc_extension); MSG_WriteByte (&buf, clc_voip); MSG_WriteByte (&buf, clc.voipOutgoingGeneration); MSG_WriteLong (&buf, clc.voipOutgoingSequence); MSG_WriteByte (&buf, clc.voipOutgoingDataFrames); MSG_WriteLong (&buf, clc.voipTarget1); MSG_WriteLong (&buf, clc.voipTarget2); MSG_WriteLong (&buf, clc.voipTarget3); MSG_WriteShort (&buf, clc.voipOutgoingDataSize); MSG_WriteData (&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize); // If we're recording a demo, we have to fake a server packet with // this VoIP data so it gets to disk; the server doesn't send it // back to us, and we might as well eliminate concerns about dropped // and misordered packets here. if ( clc.demorecording && !clc.demowaiting ) { const int voipSize = clc.voipOutgoingDataSize; msg_t fakemsg; byte fakedata[MAX_MSGLEN]; MSG_Init (&fakemsg, fakedata, sizeof (fakedata)); MSG_Bitstream (&fakemsg); MSG_WriteLong (&fakemsg, clc.reliableAcknowledge); MSG_WriteByte (&fakemsg, svc_EOF); MSG_WriteByte (&fakemsg, svc_extension); MSG_WriteByte (&fakemsg, svc_voip); MSG_WriteShort (&fakemsg, clc.clientNum); MSG_WriteByte (&fakemsg, clc.voipOutgoingGeneration); MSG_WriteLong (&fakemsg, clc.voipOutgoingSequence); MSG_WriteByte (&fakemsg, clc.voipOutgoingDataFrames); MSG_WriteShort (&fakemsg, clc.voipOutgoingDataSize ); MSG_WriteData (&fakemsg, clc.voipOutgoingData, voipSize); MSG_WriteByte (&fakemsg, svc_EOF); CL_WriteDemoMessage (&fakemsg, 0); } clc.voipOutgoingSequence += clc.voipOutgoingDataFrames; clc.voipOutgoingDataSize = 0; clc.voipOutgoingDataFrames = 0; } else #endif if ( count >= 1 ) { if ( cl_showSend->integer ) { Com_Printf( "(%i)", count ); } // begin a client move command if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting || clc.serverMessageSequence != cl.snap.messageNum ) { MSG_WriteByte (&buf, clc_moveNoDelta); } else { MSG_WriteByte (&buf, clc_move); } // write the command count MSG_WriteByte( &buf, count ); // use the checksum feed in the key key = clc.checksumFeed; // also use the message acknowledge key ^= clc.serverMessageSequence; // also use the last acknowledged server command in the key key ^= MSG_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32); // write all the commands, including the predicted command for ( i = 0 ; i < count ; i++ ) { j = (cl.cmdNumber - count + i + 1) & CMD_MASK; cmd = &cl.cmds[j]; MSG_WriteDeltaUsercmdKey (&buf, key, oldcmd, cmd); oldcmd = cmd; } } // // deliver the message // packetNum = clc.netchan.outgoingSequence & PACKET_MASK; cl.outPackets[ packetNum ].p_realtime = cls.realtime; cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime; cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber; clc.lastPacketSentTime = cls.realtime; if ( cl_showSend->integer ) { Com_Printf( "%i ", buf.cursize ); } CL_Netchan_Transmit (&clc.netchan, &buf); // clients never really should have messages large enough // to fragment, but in case they do, fire them all off // at once // TTimo: this causes a packet burst, which is bad karma for winsock // added a WARNING message, we'll see if there are legit situations where this happens while ( clc.netchan.unsentFragments ) { Com_DPrintf( "WARNING: #462 unsent fragments (not supposed to happen!)\n" ); CL_Netchan_TransmitNextFragment( &clc.netchan ); } }
static qsocket_t *_Datagram_Connect (char *host) { struct qsockaddr sendaddr; struct qsockaddr readaddr; qsocket_t *sock; int newsock; int ret; int reps; double start_time; int control; char *reason; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) == -1) return NULL; newsock = dfunc.OpenSocket (0); if (newsock == -1) return NULL; sock = NET_NewQSocket (); if (sock == NULL) goto ErrorReturn2; sock->socket = newsock; sock->landriver = net_landriverlevel; // connect to the host if (dfunc.Connect (newsock, &sendaddr) == -1) goto ErrorReturn; // send the connection request Con_Printf("trying...\n"); SCR_UpdateScreen (); start_time = net_time; for (reps = 0; reps < 3; reps++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_CONNECT); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); SZ_Clear(&net_message); do { ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr); // if we got something, validate it if (ret > 0) { // is it from the right place? if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) { #ifdef DEBUG Con_Printf("wrong reply address\n"); Con_Printf("Expected: %s\n", StrAddr (&sendaddr)); Con_Printf("Received: %s\n", StrAddr (&readaddr)); SCR_UpdateScreen (); #endif ret = 0; continue; } if (ret < sizeof(int)) { ret = 0; continue; } net_message.cursize = ret; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) { ret = 0; continue; } if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) { ret = 0; continue; } if ((control & NETFLAG_LENGTH_MASK) != ret) { ret = 0; continue; } } } while (ret == 0 && (SetNetTime() - start_time) < 2.5); if (ret) break; Con_Printf("still trying...\n"); SCR_UpdateScreen (); start_time = SetNetTime(); } if (ret == 0) { reason = "No Response"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } if (ret == -1) { reason = "Network Error"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } ret = MSG_ReadByte(); if (ret == CCREP_REJECT) { reason = MSG_ReadString(); Con_Printf(reason); Q_strncpy(m_return_reason, reason, 31); goto ErrorReturn; } if (ret == CCREP_ACCEPT) { Q_memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); } else { reason = "Bad Response"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } dfunc.GetNameFromAddr (&sendaddr, sock->address); Con_Printf ("Connection accepted\n"); sock->lastMessageTime = SetNetTime(); // switch the connection to the specified address if (dfunc.Connect (newsock, &sock->addr) == -1) { reason = "Connect to Game failed"; Con_Printf("%s\n", reason); Q_strcpy(m_return_reason, reason); goto ErrorReturn; } m_return_onerror = false; return sock; ErrorReturn: NET_FreeQSocket(sock); ErrorReturn2: dfunc.CloseSocket(newsock); if (m_return_onerror) { key_dest = key_menu; m_state = m_return_state; m_return_onerror = false; } return NULL; }
/* ==================== CL_Record_f record <demoname> <server> ==================== */ void CL_Record_f (void) { int c; char name[MAX_OSPATH]; sizebuf_t buf; char buf_data[MAX_MSGLEN]; int n, i, j; char *s; entity_t *ent; entity_state_t *es, blankes; player_info_t *player; extern char gamedirfile[]; int seq = 1; c = Cmd_Argc(); if (c != 2) { Con_Printf ("record <demoname>\n"); return; } if (cls.state != ca_active) { Con_Printf ("You must be connected to record.\n"); return; } if (cls.demorecording) CL_Stop_f(); sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); // // open the demo file // COM_DefaultExtension (name, ".qwd"); cls.demofile = fopen (name, "wb"); if (!cls.demofile) { Con_Printf ("ERROR: couldn't open.\n"); return; } Con_Printf ("recording to %s.\n", name); cls.demorecording = true; /*-------------------------------------------------*/ // serverdata // send the info about the new client to all connected clients memset(&buf, 0, sizeof(buf)); buf.data = buf_data; buf.maxsize = sizeof(buf_data); // send the serverdata MSG_WriteByte (&buf, svc_serverdata); MSG_WriteLong (&buf, PROTOCOL_VERSION); MSG_WriteLong (&buf, cl.servercount); MSG_WriteString (&buf, gamedirfile); if (cl.spectator) MSG_WriteByte (&buf, cl.playernum | 128); else MSG_WriteByte (&buf, cl.playernum); // send full levelname MSG_WriteString (&buf, cl.levelname); // send the movevars MSG_WriteFloat(&buf, movevars.gravity); MSG_WriteFloat(&buf, movevars.stopspeed); MSG_WriteFloat(&buf, movevars.maxspeed); MSG_WriteFloat(&buf, movevars.spectatormaxspeed); MSG_WriteFloat(&buf, movevars.accelerate); MSG_WriteFloat(&buf, movevars.airaccelerate); MSG_WriteFloat(&buf, movevars.wateraccelerate); MSG_WriteFloat(&buf, movevars.friction); MSG_WriteFloat(&buf, movevars.waterfriction); MSG_WriteFloat(&buf, movevars.entgravity); // send music MSG_WriteByte (&buf, svc_cdtrack); MSG_WriteByte (&buf, 0); // none in demos // send server info string MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) ); // flush packet CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); // soundlist MSG_WriteByte (&buf, svc_soundlist); MSG_WriteByte (&buf, 0); n = 0; s = cl.sound_name[n+1]; while (*s) { MSG_WriteString (&buf, s); if (buf.cursize > MAX_MSGLEN/2) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); MSG_WriteByte (&buf, svc_soundlist); MSG_WriteByte (&buf, n + 1); } n++; s = cl.sound_name[n+1]; } if (buf.cursize) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } // modellist MSG_WriteByte (&buf, svc_modellist); MSG_WriteByte (&buf, 0); n = 0; s = cl.model_name[n+1]; while (*s) { MSG_WriteString (&buf, s); if (buf.cursize > MAX_MSGLEN/2) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, n); CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); MSG_WriteByte (&buf, svc_modellist); MSG_WriteByte (&buf, n + 1); } n++; s = cl.model_name[n+1]; } if (buf.cursize) { MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, 0); CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } // spawnstatic for (i = 0; i < cl.num_statics; i++) { ent = cl_static_entities + i; MSG_WriteByte (&buf, svc_spawnstatic); for (j = 1; j < MAX_MODELS; j++) if (ent->model == cl.model_precache[j]) break; if (j == MAX_MODELS) MSG_WriteByte (&buf, 0); else MSG_WriteByte (&buf, j); MSG_WriteByte (&buf, ent->frame); MSG_WriteByte (&buf, 0); MSG_WriteByte (&buf, ent->skinnum); for (j=0 ; j<3 ; j++) { MSG_WriteCoord (&buf, ent->origin[j]); MSG_WriteAngle (&buf, ent->angles[j]); } if (buf.cursize > MAX_MSGLEN/2) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } } // spawnstaticsound // static sounds are skipped in demos, life is hard // baselines memset(&blankes, 0, sizeof(blankes)); for (i = 0; i < MAX_EDICTS; i++) { es = cl_baselines + i; if (memcmp(es, &blankes, sizeof(blankes))) { MSG_WriteByte (&buf,svc_spawnbaseline); MSG_WriteShort (&buf, i); MSG_WriteByte (&buf, es->modelindex); MSG_WriteByte (&buf, es->frame); MSG_WriteByte (&buf, es->colormap); MSG_WriteByte (&buf, es->skinnum); for (j=0 ; j<3 ; j++) { MSG_WriteCoord(&buf, es->origin[j]); MSG_WriteAngle(&buf, es->angles[j]); } if (buf.cursize > MAX_MSGLEN/2) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } } } MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("cmd spawn %i 0\n", cl.servercount) ); if (buf.cursize) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } // send current status of all other players for (i = 0; i < MAX_CLIENTS; i++) { player = cl.players + i; MSG_WriteByte (&buf, svc_updatefrags); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, player->frags); MSG_WriteByte (&buf, svc_updateping); MSG_WriteByte (&buf, i); MSG_WriteShort (&buf, player->ping); MSG_WriteByte (&buf, svc_updatepl); MSG_WriteByte (&buf, i); MSG_WriteByte (&buf, player->pl); MSG_WriteByte (&buf, svc_updateentertime); MSG_WriteByte (&buf, i); MSG_WriteFloat (&buf, player->entertime); MSG_WriteByte (&buf, svc_updateuserinfo); MSG_WriteByte (&buf, i); MSG_WriteLong (&buf, player->userid); MSG_WriteString (&buf, player->userinfo); if (buf.cursize > MAX_MSGLEN/2) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } } // send all current light styles for (i=0 ; i<MAX_LIGHTSTYLES ; i++) { MSG_WriteByte (&buf, svc_lightstyle); MSG_WriteByte (&buf, (char)i); MSG_WriteString (&buf, cl_lightstyle[i].map); } for (i = 0; i < MAX_CL_STATS; i++) { MSG_WriteByte (&buf, svc_updatestatlong); MSG_WriteByte (&buf, i); MSG_WriteLong (&buf, cl.stats[i]); if (buf.cursize > MAX_MSGLEN/2) { CL_WriteRecordDemoMessage (&buf, seq++); SZ_Clear (&buf); } } #if 0 MSG_WriteByte (&buf, svc_updatestatlong); MSG_WriteByte (&buf, STAT_TOTALMONSTERS); MSG_WriteLong (&buf, cl.stats[STAT_TOTALMONSTERS]); MSG_WriteByte (&buf, svc_updatestatlong); MSG_WriteByte (&buf, STAT_SECRETS); MSG_WriteLong (&buf, cl.stats[STAT_SECRETS]); MSG_WriteByte (&buf, svc_updatestatlong); MSG_WriteByte (&buf, STAT_MONSTERS); MSG_WriteLong (&buf, cl.stats[STAT_MONSTERS]); #endif // get the client to check and download skins // when that is completed, a begin command will be issued MSG_WriteByte (&buf, svc_stufftext); MSG_WriteString (&buf, va("skins\n") ); CL_WriteRecordDemoMessage (&buf, seq++); CL_WriteSetDemoMessage(); // done }
static qsocket_t *_Datagram_CheckNewConnections (void) { struct qsockaddr clientaddr; struct qsockaddr newaddr; int newsock; int acceptsock; qsocket_t *sock; qsocket_t *s; int len; int command; int control; int ret; acceptsock = dfunc.CheckNewConnections(); if (acceptsock == -1) return NULL; SZ_Clear(&net_message); len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr); if (len < sizeof(int)) return NULL; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) return NULL; if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) return NULL; if ((control & NETFLAG_LENGTH_MASK) != len) return NULL; command = MSG_ReadByte(); if (command == CCREQ_SERVER_INFO) { if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_SERVER_INFO); dfunc.GetSocketAddr(acceptsock, &newaddr); MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); MSG_WriteString(&net_message, hostname.string); MSG_WriteString(&net_message, sv.name); MSG_WriteByte(&net_message, net_activeconnections); MSG_WriteByte(&net_message, svs.maxclients); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_PLAYER_INFO) { int playerNumber; int activeNumber; int clientNumber; client_t *client; playerNumber = MSG_ReadByte(); activeNumber = -1; for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++) { if (client->active) { activeNumber++; if (activeNumber == playerNumber) break; } } if (clientNumber == svs.maxclients) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_PLAYER_INFO); MSG_WriteByte(&net_message, playerNumber); MSG_WriteString(&net_message, client->name); MSG_WriteLong(&net_message, client->colors); MSG_WriteLong(&net_message, (int)client->edict->v.frags); MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime)); MSG_WriteString(&net_message, client->netconnection->address); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_RULE_INFO) { char *prevCvarName; cvar_t *var; // find the search start location prevCvarName = MSG_ReadString(); if (*prevCvarName) { var = Cvar_FindVar (prevCvarName); if (!var) return NULL; var = var->next; } else var = cvar_vars; // search for the next server cvar while (var) { if (var->server) break; var = var->next; } // send the response SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_RULE_INFO); if (var) { MSG_WriteString(&net_message, var->name); MSG_WriteString(&net_message, var->string); } *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command != CCREQ_CONNECT) return NULL; if (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Incompatible version.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } #ifdef BAN_TEST // check for a ban if (clientaddr.sa_family == AF_INET) { unsigned long testAddr; testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; if ((testAddr & banMask) == banAddr) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "You have been banned.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } } #endif // see if this guy is already connected for (s = net_activeSockets; s; s = s->next) { if (s->driver != net_driverlevel) continue; ret = dfunc.AddrCompare(&clientaddr, &s->addr); if (ret >= 0) { // is this a duplicate connection reqeust? if (ret == 0 && net_time - s->connecttime < 2.0) { // yes, so send a duplicate reply SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(s->socket, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // it's somebody coming back in from a crash/disconnect // so close the old qsocket and let their retry get them back in NET_Close(s); return NULL; } } // allocate a QSocket sock = NET_NewQSocket (); if (sock == NULL) { // no room; try to let him know SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, "Server is full.\n"); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // allocate a network socket newsock = dfunc.OpenSocket(0); if (newsock == -1) { NET_FreeQSocket(sock); return NULL; } // connect to the client if (dfunc.Connect (newsock, &clientaddr) == -1) { dfunc.CloseSocket(newsock); NET_FreeQSocket(sock); return NULL; } // everything is allocated, just fill in the details sock->socket = newsock; sock->landriver = net_landriverlevel; sock->addr = clientaddr; Q_strcpy(sock->address, dfunc.AddrToString(&clientaddr)); // send him back the info about the server connection he has been allocated SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(newsock, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); // MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return sock; }
/* =============== CL_CheckOrDownloadFile Returns true if the file exists, otherwise it attempts to start a download from the server. =============== */ qboolean CL_CheckOrDownloadFile (const char *filename) { FILE *fp; int length; char *p; char name[MAX_OSPATH]; static char lastfilename[MAX_OSPATH] = {0}; //r1: don't attempt same file many times if (!strcmp (filename, lastfilename)) return true; strcpy (lastfilename, filename); if (strstr (filename, "..")) { Com_Printf ("Refusing to check a path with .. (%s)\n", LOG_CLIENT, filename); return true; } if (strchr (filename, ' ')) { Com_Printf ("Refusing to check a path containing spaces (%s)\n", LOG_CLIENT, filename); return true; } if (strchr (filename, ':')) { Com_Printf ("Refusing to check a path containing a colon (%s)\n", LOG_CLIENT, filename); return true; } if (filename[0] == '/') { Com_Printf ("Refusing to check a path starting with / (%s)\n", LOG_CLIENT, filename); return true; } if (FS_LoadFile (filename, NULL) != -1) { // it exists, no need to download return true; } #ifdef USE_CURL if (CL_QueueHTTPDownload (filename)) { //we return true so that the precache check keeps feeding us more files. //since we have multiple HTTP connections we want to minimize latency //and be constantly sending requests, not one at a time. return true; } else #endif { strcpy (cls.downloadname, filename); //r1: fix \ to / p = cls.downloadname; while ((p = strchr(p, '\\')) != NULL) p[0] = '/'; length = (int)strlen(cls.downloadname); //normalize path p = cls.downloadname; while ((p = strstr (p, "./")) != NULL) { memmove (p, p+2, length - (p - cls.downloadname) - 1); length -= 2; } //r1: verify we are giving the server a legal path if (cls.downloadname[length-1] == '/') { Com_Printf ("Refusing to download bad path (%s)\n", LOG_CLIENT, filename); return true; } // download to a temp name, and only rename // to the real name when done, so if interrupted // a runt file wont be left COM_StripExtension (cls.downloadname, cls.downloadtempname); strcat (cls.downloadtempname, ".tmp"); //ZOID // check to see if we already have a tmp for this file, if so, try to resume // open the file if not opened yet CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); // FS_CreatePath (name); fp = fopen (name, "r+b"); if (fp) { // it exists int len; fseek(fp, 0, SEEK_END); len = ftell(fp); cls.download = fp; // give the server an offset to start the download Com_Printf ("Resuming %s\n", LOG_CLIENT, cls.downloadname); MSG_WriteByte (clc_stringcmd); if (cls.serverProtocol == PROTOCOL_R1Q2) MSG_WriteString (va("download \"%s\" %i udp-zlib", cls.downloadname, len)); else MSG_WriteString (va("download \"%s\" %i", cls.downloadname, len)); } else { Com_Printf ("Downloading %s\n", LOG_CLIENT, cls.downloadname); MSG_WriteByte (clc_stringcmd); if (cls.serverProtocol == PROTOCOL_R1Q2) MSG_WriteString (va("download \"%s\" 0 udp-zlib", cls.downloadname)); else MSG_WriteString (va("download \"%s\"", cls.downloadname)); } MSG_EndWriting (&cls.netchan.message); send_packet_now = true; cls.downloadpending = true; return false; } }
/* ================== Sbar_DeathmatchOverlay ping time frags name ================== */ void Sbar_DeathmatchOverlay(int start) { const qpic_t *pic; int i, k, l; int top, bottom; int x, y, f; char num[12]; player_info_t *s; int total; int minutes; int p; int teamplay; char team[5]; int skip = 10; if (largegame) skip = 8; // request new ping times every two second if (realtime - cl.last_ping_request > 2) { cl.last_ping_request = realtime; MSG_WriteByte(&cls.netchan.message, clc_stringcmd); MSG_WriteString(&cls.netchan.message, "pings"); } teamplay = atoi(Info_ValueForKey(cl.serverinfo, "teamplay")); scr_copyeverything = 1; scr_fullupdate = 0; if (!start) { pic = Draw_CachePic("gfx/ranking.lmp"); Draw_Pic(160 - pic->width / 2, 0, pic); } // scores Sbar_SortFrags(true); // draw the text l = scoreboardlines; if (start) y = start; else y = 24; if (teamplay) { x = 4; // 0 40 64 104 152 192 Draw_String(x, y, "ping pl time frags team name"); y += 8; // Draw_String ( x , y, "---- -- ---- ----- ---- ----------------"); Draw_String(x, y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); y += 8; } else { x = 16; // 0 40 64 104 152 Draw_String(x, y, "ping pl time frags name"); y += 8; // Draw_String ( x , y, "---- -- ---- ----- ----------------"); Draw_String(x, y, "\x1d\x1e\x1e\x1f \x1d\x1f \x1d\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1f \x1d\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1e\x1f"); y += 8; } for (i = 0; i < l && y <= vid.height - 10; i++) { k = fragsort[i]; s = &cl.players[k]; if (!s->name[0]) continue; // draw ping p = s->ping; if (p < 0 || p > 999) p = 999; sprintf(num, "%4i", p); Draw_String(x, y, num); // draw pl p = s->pl; sprintf(num, "%3i", p); if (p > 25) Draw_Alt_String(x + 32, y, num); else Draw_String(x + 32, y, num); if (s->spectator) { Draw_String(x + 40, y, "(spectator)"); // draw name if (teamplay) Draw_String(x + 152 + 40, y, s->name); else Draw_String(x + 152, y, s->name); y += skip; continue; } // draw time if (cl.intermission) total = cl.completed_time - s->entertime; else total = realtime - s->entertime; minutes = (int)total / 60; sprintf(num, "%4i", minutes); Draw_String(x + 64, y, num); // draw background top = Sbar_ColorForMap(s->topcolor); bottom = Sbar_ColorForMap(s->bottomcolor); if (largegame) Draw_Fill(x + 104, y + 1, 40, 3, top); else Draw_Fill(x + 104, y, 40, 4, top); Draw_Fill(x + 104, y + 4, 40, 4, bottom); // draw number f = s->frags; sprintf(num, "%3i", f); Draw_Character(x + 112, y, num[0]); Draw_Character(x + 120, y, num[1]); Draw_Character(x + 128, y, num[2]); if (k == cl.playernum) { Draw_Character(x + 104, y, 16); Draw_Character(x + 136, y, 17); } // team if (teamplay) { team[4] = 0; strncpy(team, Info_ValueForKey(s->userinfo, "team"), 4); Draw_String(x + 152, y, team); } // draw name if (teamplay) Draw_String(x + 152 + 40, y, s->name); else Draw_String(x + 152, y, s->name); y += skip; } if (y >= vid.height - 10) // we ran over the screen size, squish largegame = true; }