//================ // SV_MMHeartbeat // Send matchmaker a heartbeat with status //================ void SV_MMHeartbeat( void ) { static qboolean canhost = qfalse; netadr_t address; socket_t *socket; if( !sv_public->integer ) return; // never go public when not acting as a game server if( sv.state > ss_game ) return; if( !SV_MM_Initialized() || SV_MM_IsLocked() ) return; svc.last_mmheartbeat -= svc.snapFrameTime; if( svc.last_mmheartbeat > 0 && canhost == SV_MM_CanHost() ) return; if ( !SV_MM_NetAddress( &address ) ) return; svc.last_mmheartbeat = MM_HEARTBEAT_SECONDS * 1000; canhost = SV_MM_CanHost(); socket = ( address.type == NA_IP6 ? &svs.socket_udp6 : &svs.socket_udp ); Netchan_OutOfBandPrint( socket, &address, "heartbeat %s %d %d %s", canhost ? "yes":"no", APP_PROTOCOL_VERSION, SV_MM_GetSlotCount(), SV_MM_Salt() ); Com_Printf( "Sending heartbeat to matchmaker\n" ); }
static char *SV_ShortInfoString( void ) { static char string[MAX_STRING_SVCINFOSTRING]; char hostname[64]; char entry[20]; size_t len; int i, count, bots; const char *password; bots = 0; count = 0; for( i = 0; i < sv_maxclients->integer; i++ ) { if( svs.clients[i].state >= CS_CONNECTED ) { if( svs.clients[i].edict->r.svflags & SVF_FAKECLIENT || svs.clients[i].tvclient ) bots++; count++; } } //format: //" \377\377\377\377info\\n\\server_name\\m\\map name\\u\\clients/maxclients\\g\\gametype\\s\\skill\\EOT " Q_strncpyz( hostname, sv_hostname->string, sizeof( hostname ) ); Q_snprintfz( string, sizeof( string ), "\\\\n\\\\%s\\\\m\\\\%8s\\\\u\\\\%2i/%2i\\\\", hostname, sv.mapname, count > 99 ? 99 : count, sv_maxclients->integer > 99 ? 99 : sv_maxclients->integer ); len = strlen( string ); Q_snprintfz( entry, sizeof( entry ), "g\\\\%6s\\\\", Cvar_String( "g_gametype" ) ); if( MAX_SVCINFOSTRING_LEN - len > strlen( entry ) ) { Q_strncatz( string, entry, sizeof( string ) ); len = strlen( string ); } if( Q_stricmp( FS_GameDirectory(), FS_BaseGameDirectory() ) ) { Q_snprintfz( entry, sizeof( entry ), "mo\\\\%8s\\\\", FS_GameDirectory() ); if( MAX_SVCINFOSTRING_LEN - len > strlen( entry ) ) { Q_strncatz( string, entry, sizeof( string ) ); len = strlen( string ); } } if( Cvar_Value( "g_instagib" ) ) { Q_snprintfz( entry, sizeof( entry ), "ig\\\\1\\\\" ); if( MAX_SVCINFOSTRING_LEN - len > strlen( entry ) ) { Q_strncatz( string, entry, sizeof( string ) ); len = strlen( string ); } } Q_snprintfz( entry, sizeof( entry ), "s\\\\%1d\\\\", sv_skilllevel->integer ); if( MAX_SVCINFOSTRING_LEN - len > strlen( entry ) ) { Q_strncatz( string, entry, sizeof( string ) ); len = strlen( string ); } password = Cvar_String( "password" ); if( password[0] != '\0' ) { Q_snprintfz( entry, sizeof( entry ), "p\\\\1\\\\" ); if( MAX_SVCINFOSTRING_LEN - len > strlen( entry ) ) { Q_strncatz( string, entry, sizeof( string ) ); len = strlen( string ); } } if( bots ) { Q_snprintfz( entry, sizeof( entry ), "b\\\\%2i\\\\", bots > 99 ? 99 : bots ); if( MAX_SVCINFOSTRING_LEN - len > strlen( entry ) ) { Q_strncatz( string, entry, sizeof( string ) ); len = strlen( string ); } } if( SV_MM_Initialized() ) { Q_snprintfz( entry, sizeof( entry ), "mm\\\\1\\\\" ); if( MAX_SVCINFOSTRING_LEN - len > strlen( entry ) ) { Q_strncatz( string, entry, sizeof( string ) ); len = strlen( string ); } } if( Cvar_Value( "g_race_gametype" ) ) { Q_snprintfz( entry, sizeof( entry ), "r\\\\1\\\\" ); if( MAX_SVCINFOSTRING_LEN - len > strlen( entry ) ) { Q_strncatz( string, entry, sizeof( string ) ); len = strlen( string ); } } // finish it Q_strncatz( string, "EOT", sizeof( string ) ); return string; }
/* * SV_ReadPackets */ static void SV_ReadPackets( void ) { int i, socketind, ret; client_t *cl; #ifdef TCP_SUPPORT socket_t newsocket; netadr_t mmserver; #endif int game_port; socket_t *socket; netadr_t address; static msg_t msg; static qbyte msgData[MAX_MSGLEN]; socket_t* sockets [] = { &svs.socket_loopback, &svs.socket_udp, &svs.socket_udp6, }; #ifdef TCP_SUPPORT if( SV_MM_Initialized() ) SV_MM_NetAddress( &mmserver ); if( svs.socket_tcp.open ) { while( qtrue ) { // find a free slot for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) break; } if( i == MAX_INCOMING_CONNECTIONS ) break; if( ( ret = NET_Accept( &svs.socket_tcp, &newsocket, &address ) ) == 0 ) break; if( ret == -1 ) { Com_Printf( "NET_Accept: Error: %s\n", NET_ErrorString() ); continue; } Com_Printf( "New TCP connection from %s\n", NET_AddressToString( &address ) ); svs.incoming[i].active = qtrue; svs.incoming[i].socket = newsocket; svs.incoming[i].address = address; svs.incoming[i].time = svs.realtime; } } for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) continue; ret = NET_GetPacket( &svs.incoming[i].socket, &address, &msg ); if( ret == -1 ) { Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() ); NET_CloseSocket( &svs.incoming[i].socket ); svs.incoming[i].active = qfalse; } else if( ret == 1 ) { if( SV_MM_Initialized() && NET_CompareBaseAddress( &mmserver, &address ) ) { Com_DPrintf( "TCP packet from matchmaker\n" ); SV_MM_SetConnection( &svs.incoming[i] ); SV_MM_Packet( &msg ); SV_MM_SetConnection( NULL ); continue; } if( *(int *)msg.data != -1 ) { Com_Printf( "Sequence packet without connection\n" ); NET_CloseSocket( &svs.incoming[i].socket ); svs.incoming[i].active = qfalse; continue; } Com_Printf( "Connectionless TCP packet from: %s\n", NET_AddressToString( &address ) ); SV_ConnectionlessPacket( &svs.incoming[i].socket, &address, &msg ); } } #endif MSG_Init( &msg, msgData, sizeof( msgData ) ); for( socketind = 0; socketind < sizeof( sockets ) / sizeof( sockets[0] ); socketind++ ) { socket = sockets[socketind]; if( !socket->open ) continue; while( ( ret = NET_GetPacket( socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "NET_GetPacket: Error: %s\n", NET_ErrorString() ); continue; } // check for connectionless packet (0xffffffff) first if( *(int *)msg.data == -1 ) { SV_ConnectionlessPacket( socket, &address, &msg ); continue; } // read the game port out of the message so we can fix up // stupid address translating routers MSG_BeginReading( &msg ); MSG_ReadLong( &msg ); // sequence number MSG_ReadLong( &msg ); // sequence number game_port = MSG_ReadShort( &msg ) & 0xffff; // data follows // check for packets from connected clients for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { unsigned short addr_port; if( cl->state == CS_FREE || cl->state == CS_ZOMBIE ) continue; if( cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) continue; if( !NET_CompareBaseAddress( &address, &cl->netchan.remoteAddress ) ) continue; if( cl->netchan.game_port != game_port ) continue; addr_port = NET_GetAddressPort( &address ); if( NET_GetAddressPort( &cl->netchan.remoteAddress ) != addr_port ) { Com_Printf( "SV_ReadPackets: fixing up a translated port\n" ); NET_SetAddressPort( &cl->netchan.remoteAddress, addr_port ); } if( SV_ProcessPacket( &cl->netchan, &msg ) ) // this is a valid, sequenced packet, so process it { cl->lastPacketReceivedTime = svs.realtime; SV_ParseClientMessage( cl, &msg ); } break; } } } // handle clients with individual sockets for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( cl->state == CS_ZOMBIE || cl->state == CS_FREE ) continue; if( !cl->individual_socket ) continue; // not while, we only handle one packet per client at a time here if( ( ret = NET_GetPacket( cl->netchan.socket, &address, &msg ) ) != 0 ) { if( ret == -1 ) { Com_Printf( "Error receiving packet from %s: %s\n", NET_AddressToString( &cl->netchan.remoteAddress ), NET_ErrorString() ); if( cl->reliable ) SV_DropClient( cl, DROP_TYPE_GENERAL, "Error receiving packet: %s", NET_ErrorString() ); } else { if( SV_ProcessPacket( &cl->netchan, &msg ) ) { // this is a valid, sequenced packet, so process it cl->lastPacketReceivedTime = svs.realtime; SV_ParseClientMessage( cl, &msg ); } } } } }