/* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int teamTask, teamLeader, health; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; qboolean showRenameMsg = qtrue; gclient_t *client; char c1[ MAX_INFO_STRING ]; char c2[ MAX_INFO_STRING ]; char userinfo[ MAX_INFO_STRING ]; team_t team; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate(userinfo) ) strcpy( userinfo, "\\name\\badinfo" ); // check for local client s = Info_ValueForKey( userinfo, "ip" ); if( !strcmp( s, "localhost" ) ) client->pers.localClient = qtrue; // check the item prediction s = Info_ValueForKey( userinfo, "cg_predictItems" ); if( !atoi( s ) ) client->pers.predictItemPickup = qfalse; else client->pers.predictItemPickup = qtrue; // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); ClientCleanName( s, newname, sizeof( newname ) ); if( strcmp( oldname, newname ) ) { if( !strlen( oldname ) && client->pers.connected != CON_CONNECTED ) showRenameMsg = qfalse; // in case we need to revert and there's no oldname ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ) ); if( g_newbieNumbering.integer ) { if( !strcmp( newname, "UnnamedPlayer" ) ) Q_strncpyz( newname, G_NextNewbieName( ent ), sizeof( newname ) ); if( !strcmp( oldname, "UnnamedPlayer" ) ) Q_strncpyz( oldname, G_NextNewbieName( ent ), sizeof( oldname ) ); } if( client->pers.muted ) { trap_SendServerCommand( ent - g_entities, "print \"You cannot change your name while you are muted\n\"" ); revertName = qtrue; } else if( client->pers.nameChangeTime && ( level.time - client->pers.nameChangeTime ) <= ( g_minNameChangePeriod.value * 1000 ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( g_maxNameChanges.integer > 0 && client->pers.nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } else if( client->pers.nlocked ) { trap_SendServerCommand( ent - g_entities, "print \"Your name is locked, you can no longer rename.\n\"" ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, oldname, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", newname ); trap_SetUserinfo( clientNum, userinfo ); if( client->pers.connected == CON_CONNECTED ) { client->pers.nameChangeTime = level.time; client->pers.nameChanges++; } } } if( client->sess.sessionTeam == TEAM_SPECTATOR ) { if( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) Q_strncpyz( client->pers.netname, "scoreboard", sizeof( client->pers.netname ) ); } if( client->pers.connected >= CON_CONNECTING && showRenameMsg ) { if( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " renamed to %s^7\n\"", oldname, client->pers.netname ) ); if( g_decolourLogfiles.integer) { char decoloured[ MAX_STRING_CHARS ] = ""; if( g_decolourLogfiles.integer == 1 ) { Com_sprintf( decoloured, sizeof(decoloured), " (\"%s^7\" -> \"%s^7\")", oldname, client->pers.netname ); G_DecolorString( decoloured, decoloured ); G_LogPrintfColoured( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"%s\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname, decoloured ); } else { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"%s\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname, decoloured ); } } else { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname ); } G_admin_namelog_update( client, qfalse ); } } // set max health health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) client->pers.maxHealth = 100; //hack to force a client update if the config string does not change between spawning if( client->pers.classSelection == PCL_NONE ) client->pers.maxHealth = 0; // set model if( client->ps.stats[ STAT_PCLASS ] == PCL_HUMAN && BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) ) { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( PCL_HUMAN_BSUIT ), BG_FindSkinNameForClass( PCL_HUMAN_BSUIT ) ); } else if( client->pers.classSelection == PCL_NONE ) { //This looks hacky and frankly it is. The clientInfo string needs to hold different //model details to that of the spawning class or the info change will not be //registered and an axis appears instead of the player model. There is zero chance //the player can spawn with the battlesuit, hence this choice. Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( PCL_HUMAN_BSUIT ), BG_FindSkinNameForClass( PCL_HUMAN_BSUIT ) ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( client->pers.classSelection ), BG_FindSkinNameForClass( client->pers.classSelection ) ); } Q_strncpyz( model, buffer, sizeof( model ) ); //don't bother setting model type if spectating if( client->pers.classSelection != PCL_NONE ) { //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_FindModelNameForClass( client->pers.classSelection ) ); if( G_NonSegModel( filename ) ) client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL; else client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL; } // wallwalk follow s = Info_ValueForKey( userinfo, "cg_wwFollow" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW; // wallwalk toggle s = Info_ValueForKey( userinfo, "cg_wwToggle" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( ! *s || atoi( s ) != 0 ) client->pers.teamInfo = qtrue; else client->pers.teamInfo = qfalse; s = Info_ValueForKey( userinfo, "cg_unlagged" ); if( !s[0] || atoi( s ) != 0 ) client->pers.useUnlagged = qtrue; else client->pers.useUnlagged = qfalse; // team task (0 = none, 1 = offence, 2 = defence) teamTask = atoi( Info_ValueForKey( userinfo, "teamtask" ) ); // team Leader (1 = leader, 0 is normal player) teamLeader = client->sess.teamLeader; // colors strcpy( c1, Info_ValueForKey( userinfo, "color1" ) ); strcpy( c2, Info_ValueForKey( userinfo, "color2" ) ); team = client->pers.teamSelection; // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds Com_sprintf( userinfo, sizeof( userinfo ), "n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\" "hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\" "tl\\%d\\ig\\%16s", client->pers.netname, team, model, model, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader, BG_ClientListString( &client->sess.ignoreList ) ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ }
/* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ char *ClientUserinfoChanged( int clientNum ) { gentity_t *ent; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate(userinfo) ) { trap_SendServerCommand( ent - g_entities, "disconnect \"illegal or malformed userinfo\n\"" ); trap_DropClient( ent - g_entities, "dropped: illegal or malformed userinfo"); return "Illegal or malformed userinfo"; } // stickyspec toggle s = Info_ValueForKey( userinfo, "cg_stickySpec" ); client->pers.stickySpec = atoi( s ) != 0; // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); G_ClientCleanName( s, newname, sizeof( newname ) ); if( strcmp( oldname, newname ) ) { if( client->pers.namelog->nameChangeTime && level.time - client->pers.namelog->nameChangeTime <= g_minNameChangePeriod.value * 1000 ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( g_maxNameChanges.integer > 0 && client->pers.namelog->nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( client->pers.namelog->muted ) { trap_SendServerCommand( ent - g_entities, "print \"You cannot change your name while you are muted\n\"" ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer", sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) ); if( client->pers.connected == CON_CONNECTED ) { client->pers.namelog->nameChangeTime = level.time; client->pers.namelog->nameChanges++; } if( *oldname ) { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%c%s%c^7\"\n", clientNum, client->pers.ip.str, client->pers.guid, oldname, client->pers.netname, DECOLOR_OFF, client->pers.netname, DECOLOR_ON ); } } G_namelog_update_name( client ); } if( client->pers.classSelection == PCL_NONE ) { //This looks hacky and frankly it is. The clientInfo string needs to hold different //model details to that of the spawning class or the info change will not be //registered and an axis appears instead of the player model. There is zero chance //the player can spawn with the battlesuit, hence this choice. Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName, BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName, BG_ClassConfig( client->pers.classSelection )->skinName ); //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_ClassConfig( client->pers.classSelection )->modelName ); if( G_NonSegModel( filename ) ) client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL; else client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL; } Q_strncpyz( model, buffer, sizeof( model ) ); // wallwalk follow s = Info_ValueForKey( userinfo, "cg_wwFollow" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW; // wallwalk toggle s = Info_ValueForKey( userinfo, "cg_wwToggle" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE; // always sprint s = Info_ValueForKey( userinfo, "cg_sprintToggle" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE; else client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE; // fly speed s = Info_ValueForKey( userinfo, "cg_flySpeed" ); if( *s ) client->pers.flySpeed = atoi( s ); else client->pers.flySpeed = BG_Class( PCL_NONE )->speed; // disable blueprint errors s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" ); if( atoi( s ) ) client->pers.disableBlueprintErrors = qtrue; else client->pers.disableBlueprintErrors = qfalse; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( atoi( s ) != 0 ) client->pers.teamInfo = qtrue; else client->pers.teamInfo = qfalse; s = Info_ValueForKey( userinfo, "cg_unlagged" ); if( !s[0] || atoi( s ) != 0 ) client->pers.useUnlagged = qtrue; else client->pers.useUnlagged = qfalse; Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ), sizeof( client->pers.voice ) ); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds Com_sprintf( userinfo, sizeof( userinfo ), "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s", client->pers.netname, client->pers.teamSelection, model, Com_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ return NULL; }
/* ================= Mod_LoadAliasModel ================= */ void Mod_LoadAliasModel (model_t *mod, void *buffer) { int i, j; mdl_t *pinmodel; stvert_t *pinstverts; dtriangle_t *pintriangles; int version, numframes; int size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; int start, end, total; if (!strcmp(loadmodel->name, "progs/player.mdl") || !strcmp(loadmodel->name, "progs/eyes.mdl")) { unsigned short crc; byte *p; int len; char st[40]; CRC_Init(&crc); for (len = com_filesize, p = buffer; len; len--, p++) CRC_ProcessByte(&crc, *p); sprintf(st, "%d", (int) crc); Info_SetValueForKey (cls.userinfo, !strcmp(loadmodel->name, "progs/player.mdl") ? pmodel_name : emodel_name, st, MAX_INFO_STRING); if (cls.state >= ca_connected) { MSG_WriteByte (&cls.netchan.message, clc_stringcmd); sprintf(st, "setinfo %s %d", !strcmp(loadmodel->name, "progs/player.mdl") ? pmodel_name : emodel_name, (int)crc); SZ_Print (&cls.netchan.message, st); } } start = Hunk_LowMark (); pinmodel = (mdl_t *)buffer; version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); // // allocate space for a working header, plus all the data except the frames, // skin and group info // size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]); pheader = Hunk_AllocName (size, loadname); mod->flags = LittleLong (pinmodel->flags); // // endian-adjust and copy the data, starting with the alias model header // pheader->boundingradius = LittleFloat (pinmodel->boundingradius); pheader->numskins = LittleLong (pinmodel->numskins); pheader->skinwidth = LittleLong (pinmodel->skinwidth); pheader->skinheight = LittleLong (pinmodel->skinheight); if (pheader->skinheight > MAX_LBM_HEIGHT) Sys_Error ("model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); pheader->numverts = LittleLong (pinmodel->numverts); if (pheader->numverts <= 0) Sys_Error ("model %s has no vertices", mod->name); if (pheader->numverts > MAXALIASVERTS) Sys_Error ("model %s has too many vertices", mod->name); pheader->numtris = LittleLong (pinmodel->numtris); if (pheader->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); pheader->numframes = LittleLong (pinmodel->numframes); numframes = pheader->numframes; if (numframes < 1) Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; mod->synctype = LittleLong (pinmodel->synctype); mod->numframes = pheader->numframes; for (i=0 ; i<3 ; i++) { pheader->scale[i] = LittleFloat (pinmodel->scale[i]); pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); } // // load the skins // pskintype = (daliasskintype_t *)&pinmodel[1]; pskintype = Mod_LoadAllSkins (pheader->numskins, pskintype); // // load base s and t vertices // pinstverts = (stvert_t *)pskintype; for (i=0 ; i<pheader->numverts ; i++) { stverts[i].onseam = LittleLong (pinstverts[i].onseam); stverts[i].s = LittleLong (pinstverts[i].s); stverts[i].t = LittleLong (pinstverts[i].t); } // // load triangle lists // pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts]; for (i=0 ; i<pheader->numtris ; i++) { triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); for (j=0 ; j<3 ; j++) { triangles[i].vertindex[j] = LittleLong (pintriangles[i].vertindex[j]); } } // // load the frames // posenum = 0; pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; for (i=0 ; i<numframes ; i++) { aliasframetype_t frametype; frametype = LittleLong (pframetype->type); if (frametype == ALIAS_SINGLE) { pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]); } else { pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]); } } pheader->numposes = posenum; mod->type = mod_alias; // FIXME: do this right mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; // // build the draw lists // GX_MakeAliasModelDisplayLists (mod, pheader); // // move the complete, relocatable alias model to the cache // end = Hunk_LowMark (); total = end - start; Cache_Alloc (&mod->cache, total, loadname); if (!mod->cache.data) return; memcpy (mod->cache.data, pheader, total); Hunk_FreeToLowMark (start); }
/* ==================== LAN_GetServerInfo ==================== */ static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) { char info[MAX_STRING_CHARS]; serverInfo_t *server = NULL; info[0] = '\0'; switch (source) { case AS_LOCAL : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.localServers[n]; } break; case AS_MPLAYER: case AS_GLOBAL : if (n >= 0 && n < MAX_GLOBAL_SERVERS) { server = &cls.globalServers[n]; } break; case AS_FAVORITES : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.favoriteServers[n]; } break; } if (server && buf) { buf[0] = '\0'; Info_SetValueForKey( info, "hostname", server->hostName); Info_SetValueForKey( info, "mapname", server->mapName); Info_SetValueForKey( info, "clients", va("%i",server->clients)); Info_SetValueForKey( info, "sv_maxclients", va("%i",server->maxClients)); Info_SetValueForKey( info, "ping", va("%i",server->ping)); Info_SetValueForKey( info, "minping", va("%i",server->minPing)); Info_SetValueForKey( info, "maxping", va("%i",server->maxPing)); Info_SetValueForKey( info, "game", server->game); Info_SetValueForKey( info, "gametype", va("%i",server->gameType)); Info_SetValueForKey( info, "nettype", va("%i",server->netType)); Info_SetValueForKey( info, "addr", NET_AdrToStringwPort(server->adr)); Info_SetValueForKey( info, "punkbuster", va("%i", server->punkbuster)); Info_SetValueForKey( info, "g_needpass", va("%i", server->g_needpass)); Info_SetValueForKey( info, "g_humanplayers", va("%i", server->g_humanplayers)); Q_strncpyz(buf, info, buflen); } else { if (buf) { buf[0] = '\0'; } } }
void SV_DirectConnect( netadr_t from ) { char userinfo[MAX_INFO_STRING]; int i; client_t *cl, *newcl; client_t temp; sharedEntity_t *ent; int clientNum; int version; int qport; int challenge; char *password; int startIndex; intptr_t denied; int count; char *ip; const char *stringEd; #ifdef LEGACY_PROTOCOL qboolean compat = qfalse; #endif Com_DPrintf ("SVC_DirectConnect ()\n"); // Check whether this client is banned. if(SV_IsBanned(&from, qfalse)) { NET_OutOfBandPrint(NS_SERVER, from, "print\nYou are banned from this server.\n"); return; } Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) ); version = atoi(Info_ValueForKey(userinfo, "protocol")); #ifdef LEGACY_PROTOCOL if(version > 0 && com_legacyprotocol->integer == version) compat = qtrue; else #endif { if(version != com_protocol->integer) { NET_OutOfBandPrint(NS_SERVER, from, "print\nServer uses protocol version %i " "(yours is %i).\n", com_protocol->integer, version); Com_DPrintf(" rejected connect from version %i\n", version); return; } } challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) ); qport = atoi( Info_ValueForKey( userinfo, "qport" ) ); // quick reject for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( cl->state == CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { if (( svs.time - cl->lastConnectTime) < (sv_reconnectlimit->integer * 1000)) { Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from)); return; } break; } } // don't let "ip" overflow userinfo string if ( NET_IsLocalAddress (from) ) ip = "localhost"; else ip = (char *)NET_AdrToString( from ); if( ( strlen( ip ) + strlen( userinfo ) + 4 ) >= MAX_INFO_STRING ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nUserinfo string length exceeded. " "Try removing setu cvars from your config.\n" ); return; } Info_SetValueForKey( userinfo, "ip", ip ); // see if the challenge is valid (LAN clients don't need to challenge) if (!NET_IsLocalAddress(from)) { int ping; challenge_t *challengeptr; for (i=0; i<MAX_CHALLENGES; i++) { if (NET_CompareAdr(from, svs.challenges[i].adr)) { if(challenge == svs.challenges[i].challenge) break; } } if (i == MAX_CHALLENGES) { NET_OutOfBandPrint( NS_SERVER, from, "print\nNo or bad challenge for your address.\n" ); return; } challengeptr = &svs.challenges[i]; if(challengeptr->wasrefused) { // Return silently, so that error messages written by the server keep being displayed. return; } ping = svs.time - challengeptr->pingTime; // never reject a LAN client based on ping if ( !Sys_IsLANAddress( from ) ) { if ( sv_minPing->value && ping < sv_minPing->value ) { NET_OutOfBandPrint( NS_SERVER, from, "print\n%s", SV_StringEdString("SERVER_FOR_HIGH_PING") ); stringEd = SV_GetString("CLIENT_REJECTED_LOW_PING"); Com_DPrintf (stringEd, i); challengeptr->wasrefused = qtrue; return; } if ( sv_maxPing->value && ping > sv_maxPing->value ) { NET_OutOfBandPrint( NS_SERVER, from, "print\n%s", SV_StringEdString("SERVER_FOR_LOW_PING") ); stringEd = SV_GetString("CLIENT_REJECTED_LOW_PING"); Com_DPrintf (stringEd, i); challengeptr->wasrefused = qtrue; return; } } stringEd = SV_GetString("CLIENT_CONN_WITH_PING"); Com_Printf(stringEd, i, ping); challengeptr->connected = qtrue; } newcl = &temp; Com_Memset (newcl, 0, sizeof(client_t)); // if there is already a slot for this ip, reuse it for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( cl->state == CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { Com_Printf ("%s:reconnect\n", NET_AdrToString (from)); newcl = cl; // this doesn't work because it nukes the players userinfo // // disconnect the client from the game first so any flags the // // player might have are dropped // VM_Call( gvm, GAME_CLIENT_DISCONNECT, newcl - svs.clients ); // goto gotnewcl; } } // find a client slot // if "sv_privateClients" is set > 0, then that number // of client slots will be reserved for connections that // have "password" set to the value of "sv_privatePassword" // Info requests will report the maxclients as if the private // slots didn't exist, to prevent people from trying to connect // to a full server. // This is to allow us to reserve a couple slots here on our // servers so we can play without having to kick people. // check for privateClient password password = Info_ValueForKey( userinfo, "password" ); if ( !strcmp( password, sv_privatePassword->string ) ) { startIndex = 0; } else { // skip past the reserved slots startIndex = sv_privateClients->integer; } newcl = NULL; for ( i = startIndex; i < sv_maxclients->integer ; i++ ) { cl = &svs.clients[i]; if (cl->state == CS_FREE) { newcl = cl; break; } } if ( !newcl ) { if ( NET_IsLocalAddress( from ) ) { count = 0; for ( i = startIndex; i < sv_maxclients->integer ; i++ ) { cl = &svs.clients[i]; if (cl->netchan.remoteAddress.type == NA_BOT) { count++; } } // if they're all bots if (count >= sv_maxclients->integer - startIndex) { SV_DropClient(&svs.clients[sv_maxclients->integer - 1], "only bots on server"); newcl = &svs.clients[sv_maxclients->integer - 1]; } else { Com_Error( ERR_FATAL, "server is full on local connect" ); return; } } else { NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", SV_StringEdString("SERVER_IS_FULL") ); Com_DPrintf ("Rejected a connection.\n"); return; } } // we got a newcl, so reset the reliableSequence and reliableAcknowledge cl->reliableAcknowledge = 0; cl->reliableSequence = 0; gotnewcl: // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = temp; clientNum = newcl - svs.clients; ent = SV_GentityNum( clientNum ); newcl->gentity = ent; // save the challenge newcl->challenge = challenge; // save the address #ifdef LEGACY_PROTOCOL newcl->compat = compat; Netchan_Setup(NS_SERVER, &newcl->netchan, from, qport, challenge, compat); #else Netchan_Setup(NS_SERVER, &newcl->netchan, from, qport, challenge, qfalse); #endif // init the netchan queue newcl->netchan_end_queue = &newcl->netchan_start_queue; // save the userinfo Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) ); // get the game a chance to reject this connection or modify the userinfo denied = VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, qtrue, qfalse ); // firstTime = qtrue if ( denied ) { // we can't just use VM_ArgPtr, because that is only valid inside a VM_Call char *str = VM_ExplicitArgPtr( gvm, denied ); NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", str ); Com_DPrintf ("Game rejected a connection: %s.\n", str); return; } SV_UserinfoChanged( newcl ); // send the connect packet to the client NET_OutOfBandPrint(NS_SERVER, from, "connectResponse %d", challenge); Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name ); newcl->state = CS_CONNECTED; newcl->lastSnapshotTime = 0; newcl->lastPacketTime = svs.time; newcl->lastConnectTime = svs.time; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit newcl->gamestateMessageNum = -1; // if this was the first client on the server, or the last client // the server can hold, send a heartbeat to the master. count = 0; for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } if ( count == 1 || count == sv_maxclients->integer ) { SV_Heartbeat_f(); } }
/* * Called when a player begins connecting to the server. * The game can refuse entrance to a client by returning false. * If the client is allowed, the connection process will continue * and eventually get to ClientBegin(). Changing levels will NOT * cause this to be called again, but loadgames will. */ qboolean ClientConnect(edict_t *ent, char *userinfo) { char *value; if (!ent || !userinfo) { return false; } /* check to see if they are on the banned IP list */ value = Info_ValueForKey(userinfo, "ip"); if (SV_FilterPacket(value)) { Info_SetValueForKey(userinfo, "rejmsg", "Banned."); return false; } /* check for a spectator */ value = Info_ValueForKey(userinfo, "spectator"); if (deathmatch->value && *value && strcmp(value, "0")) { int i, numspec; if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect."); return false; } /* count spectators */ for (i = numspec = 0; i < maxclients->value; i++) { if (g_edicts[i + 1].inuse && g_edicts[i + 1].client->pers.spectator) { numspec++; } } if (numspec >= maxspectators->value) { Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full."); return false; } } else { /* check for a password */ value = Info_ValueForKey(userinfo, "password"); if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect."); return false; } } /* they can connect */ ent->client = game.clients + (ent - g_edicts - 1); /* if there is already a body waiting for us (a loadgame), just take it, otherwise spawn one from scratch */ if (ent->inuse == false) { /* clear the respawning variables */ InitClientResp(ent->client); if (!game.autosaved || !ent->client->pers.weapon) { InitClientPersistant(ent->client); } } ClientUserinfoChanged(ent, userinfo); if (game.maxclients > 1) { gi.dprintf("%s connected\n", ent->client->pers.netname); } ent->svflags = 0; /* make sure we start with known default */ ent->client->pers.connected = true; return true; }
/* ==================== LAN_GetServerInfo ==================== */ static void LAN_GetServerInfo( int source, int n, char *buf, int buflen ) { char info[MAX_STRING_CHARS]; serverInfo_t *server = NULL; info[0] = '\0'; switch (source) { case AS_LOCAL : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.localServers[n]; } break; case AS_MPLAYER: case AS_GLOBAL : if (n >= 0 && n < MAX_GLOBAL_SERVERS) { server = &cls.globalServers[n]; } break; case AS_FAVORITES : if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.favoriteServers[n]; } break; } if (server && buf) { buf[0] = '\0'; Info_SetValueForKey( info, "hostname", server->hostName); Info_SetValueForKey( info, "mapname", server->mapName); Info_SetValueForKey( info, "clients", va("%i",server->clients)); Info_SetValueForKey( info, "sv_maxclients", va("%i",server->maxClients)); Info_SetValueForKey( info, "ping", va("%i",server->ping)); Info_SetValueForKey( info, "minping", va("%i",server->minPing)); Info_SetValueForKey( info, "maxping", va("%i",server->maxPing)); Info_SetValueForKey( info, "nettype", va("%i",server->netType)); Info_SetValueForKey( info, "needpass", va("%i", server->needPassword ) ); Info_SetValueForKey( info, "truejedi", va("%i", server->trueJedi ) ); Info_SetValueForKey( info, "wdisable", va("%i", server->weaponDisable ) ); Info_SetValueForKey( info, "fdisable", va("%i", server->forceDisable ) ); Info_SetValueForKey( info, "game", server->game); Info_SetValueForKey( info, "gametype", va("%i",server->gameType)); Info_SetValueForKey( info, "addr", NET_AdrToString(server->adr)); // Info_SetValueForKey( info, "sv_allowAnonymous", va("%i", server->allowAnonymous)); // Info_SetValueForKey( info, "pure", va("%i", server->pure ) ); Q_strncpyz(buf, info, buflen); } else { if (buf) { buf[0] = '\0'; } } }
static void G_AddBot(const char *name, int skill, const char *team, const char *spawnPoint, int playerClass, int playerWeapon, int characerIndex, const char *respawn, const char *scriptName, int rank, int skills[], qboolean pow) { #define MAX_BOTNAMES 1024 int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; // char *model; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName("wolfbot"); if (!botinfo) { G_Printf(S_COLOR_RED "Error: Bot '%s' not defined\n", name); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey(botinfo, "funname"); if (!botname[0]) { botname = Info_ValueForKey(botinfo, "name"); } Info_SetValueForKey(userinfo, "name", botname); Info_SetValueForKey(userinfo, "rate", "25000"); Info_SetValueForKey(userinfo, "snaps", "20"); Info_SetValueForKey(userinfo, "skill", va("%i", skill)); s = Info_ValueForKey(botinfo, "aifile"); if (!*s) { trap_Printf(S_COLOR_RED "Error: bot has no aifile specified\n"); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(0); // Arnout: 0 means no prefered clientslot if (clientNum == -1) { G_Printf(S_COLOR_RED "Unable to add bot. All player slots are in use.\n"); G_Printf(S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n"); return; } // initialize the bot settings if (!team || !*team) { if (PickTeam(clientNum) == TEAM_AXIS) { team = "red"; } else { team = "blue"; } } Info_SetValueForKey(userinfo, "characterfile", Info_ValueForKey(botinfo, "aifile")); //Info_SetValueForKey( userinfo, "skill", va( "%i", skill ) ); Info_SetValueForKey(userinfo, "team", team); if (spawnPoint && spawnPoint[0]) { Info_SetValueForKey(userinfo, "spawnPoint", spawnPoint); } if (scriptName && scriptName[0]) { Info_SetValueForKey(userinfo, "scriptName", scriptName); } /* if (playerClass > 0) { Info_SetValueForKey( userinfo, "pClass", va("%i", playerClass) ); } if (playerWeapon) { Info_SetValueForKey( userinfo, "pWeapon", va("%i", playerWeapon) ); }*/ // END Mad Doc - TDF key = "wolfbot"; if (!Q_stricmp((char *)name, key)) { // read the botnames file, and pick a name that doesnt exist fileHandle_t f; int len, i, j, k; qboolean setname = qfalse; char botnames[8192], *pbotnames, *listbotnames[MAX_BOTNAMES], *token, *oldpbotnames; int lengthbotnames[MAX_BOTNAMES]; len = trap_FS_FOpenFile("botfiles/botnames.txt", &f, FS_READ); if (len >= 0) { if (len > sizeof(botnames)) { G_Error("botfiles/botnames.txt is too big (max = %i)", (int)sizeof(botnames)); } memset(botnames, 0, sizeof(botnames)); trap_FS_Read(botnames, len, f); pbotnames = botnames; // read them in i = 0; oldpbotnames = pbotnames; while ((token = COM_Parse(&pbotnames))) { if (!token[0]) { break; } listbotnames[i] = strstr(oldpbotnames, token); lengthbotnames[i] = strlen(token); listbotnames[i][lengthbotnames[i]] = 0; oldpbotnames = pbotnames; if (++i == MAX_BOTNAMES) { break; } } // if (i > 2) { j = rand() % (i - 1); // start at a random spot inthe list for (k = j + 1; k != j; k++) { if (k == i) { k = -1; // gets increased on next loop continue; } if (ClientFromName(listbotnames[k]) == -1) { // found an unused name Info_SetValueForKey(userinfo, "name", listbotnames[k]); setname = qtrue; break; } } } // trap_FS_FCloseFile(f); } if (!setname) { Info_SetValueForKey(userinfo, "name", va("wolfbot_%i", clientNum + 1)); } } else { Info_SetValueForKey(userinfo, "name", name); } // if a character was specified, put the index of that character filename in the CS_CHARACTERS table in the userinfo if (characerIndex != -1) { Info_SetValueForKey(userinfo, "ch", va("%i", characerIndex)); } // if a rank was specified, use that /* if (rank != -1) { Info_SetValueForKey(userinfo, "rank", va("%i", rank)); }*/ // END Mad Doc - TDF bot = &g_entities[clientNum]; bot->r.svFlags |= SVF_BOT; if (pow) { bot->r.svFlags |= SVF_POW; } bot->inuse = qtrue; bot->aiName = bot->client->pers.netname; // register the userinfo trap_SetUserinfo(clientNum, userinfo); // have it connect to the game as a normal client if ((s = ClientConnect(clientNum, qtrue, qtrue))) { G_Printf(S_COLOR_RED "Unable to add bot: %s\n", s); return; } SetTeam(bot, (char *)team, qtrue, -1, -1, qfalse); /* if( skills ) { int i; for( i = 0; i < SK_NUM_SKILLS; i++ ) { bot->client->sess.skill[i] = skills[i]; } }*/ return; }
/////////////////////////////////////////////////////////////////////// // Set the name of the bot and update the userinfo /////////////////////////////////////////////////////////////////////// void ACESP_SetName(edict_t *bot, char *name, char *skin, char *team) { float rnd; char userinfo[MAX_INFO_STRING]; char bot_skin[MAX_INFO_STRING]; char bot_name[MAX_INFO_STRING]; // Set the name for the bot. // name if(strlen(name) == 0) SetBotNames(bot_name); else strcpy(bot_name,name); // skin if(strlen(skin) == 0) { // randomly choose skin rnd = random(); if(rnd < 0.05) sprintf(bot_skin,"female/athena"); else if(rnd < 0.1) sprintf(bot_skin,"female/brianna"); else if(rnd < 0.15) sprintf(bot_skin,"female/cobalt"); else if(rnd < 0.2) sprintf(bot_skin,"female/ensign"); else if(rnd < 0.25) sprintf(bot_skin,"female/jezebel"); else if(rnd < 0.3) sprintf(bot_skin,"female/jungle"); else if(rnd < 0.35) sprintf(bot_skin,"female/lotus"); else if(rnd < 0.4) sprintf(bot_skin,"female/stiletto"); else if(rnd < 0.45) sprintf(bot_skin,"female/venus"); else if(rnd < 0.5) sprintf(bot_skin,"female/voodoo"); else if(rnd < 0.55) sprintf(bot_skin,"male/cipher"); else if(rnd < 0.6) sprintf(bot_skin,"male/flak"); else if(rnd < 0.65) sprintf(bot_skin,"male/grunt"); else if(rnd < 0.7) sprintf(bot_skin,"male/howitzer"); else if(rnd < 0.75) sprintf(bot_skin,"male/major"); else if(rnd < 0.8) sprintf(bot_skin,"male/nightops"); else if(rnd < 0.85) sprintf(bot_skin,"male/pointman"); else if(rnd < 0.9) sprintf(bot_skin,"male/psycho"); else if(rnd < 0.95) sprintf(bot_skin,"male/razor"); else sprintf(bot_skin,"male/sniper"); } else strcpy(bot_skin,skin); // initialise userinfo memset (userinfo, 0, sizeof(userinfo)); // add bot's name/skin/hand to userinfo Info_SetValueForKey (userinfo, "name", bot_name); Info_SetValueForKey (userinfo, "skin", bot_skin); Info_SetValueForKey (userinfo, "hand", "2"); // bot is center handed for now! ClientConnect (bot, userinfo); if (botauto_respawn->value) { ACESP_SaveBots(); // make sure to save the bots } }
/* =========== ClientBegin called when a client has finished connecting, and is ready to be placed into the level. This will happen every level load, and on transition between teams, but doesn't happen on respawns ============ */ void ClientBegin( int clientNum, qboolean allowTeamReset ) { gentity_t *ent; gclient_t *client; gentity_t *tent; int flags, i; char userinfo[MAX_INFO_VALUE], *modelname; ent = g_entities + clientNum; if ((ent->r.svFlags & SVF_BOT) && g_gametype.integer >= GT_TEAM) { if (allowTeamReset) { const char *team = "Red"; int preSess; //SetTeam(ent, ""); ent->client->sess.sessionTeam = PickTeam(-1); trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { ent->client->sess.sessionTeam = TEAM_RED; } if (ent->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { team = "Blue"; } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); ent->client->ps.persistant[ PERS_TEAM ] = ent->client->sess.sessionTeam; preSess = ent->client->sess.sessionTeam; G_ReadSessionData( ent->client ); ent->client->sess.sessionTeam = preSess; G_WriteClientSessionData(ent->client); ClientUserinfoChanged( clientNum ); ClientBegin(clientNum, qfalse); return; } } client = level.clients + clientNum; if ( ent->r.linked ) { trap_UnlinkEntity( ent ); } G_InitGentity( ent ); ent->touch = 0; ent->pain = 0; ent->client = client; client->pers.connected = CON_CONNECTED; client->pers.enterTime = level.time; client->pers.teamState.state = TEAM_BEGIN; // save eflags around this, because changing teams will // cause this to happen with a valid entity, and we // want to make sure the teleport bit is set right // so the viewpoint doesn't interpolate through the // world to the new position flags = client->ps.eFlags; i = 0; while (i < NUM_FORCE_POWERS) { if (ent->client->ps.fd.forcePowersActive & (1 << i)) { WP_ForcePowerStop(ent, i); } i++; } i = TRACK_CHANNEL_1; while (i < NUM_TRACK_CHANNELS) { if (ent->client->ps.fd.killSoundEntIndex[i-50] && ent->client->ps.fd.killSoundEntIndex[i-50] < MAX_GENTITIES && ent->client->ps.fd.killSoundEntIndex[i-50] > 0) { G_MuteSound(ent->client->ps.fd.killSoundEntIndex[i-50], CHAN_VOICE); } i++; } i = 0; memset( &client->ps, 0, sizeof( client->ps ) ); client->ps.eFlags = flags; client->ps.hasDetPackPlanted = qfalse; //first-time force power initialization WP_InitForcePowers( ent ); //init saber ent WP_SaberInitBladeData( ent ); // First time model setup for that player. trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); modelname = Info_ValueForKey (userinfo, "model"); SetupGameGhoul2Model(client, modelname); if (ent->client->ghoul2) { ent->bolt_Head = trap_G2API_AddBolt(ent->client->ghoul2, 0, "cranium"); ent->bolt_Waist = trap_G2API_AddBolt(ent->client->ghoul2, 0, "thoracic"); ent->bolt_LArm = trap_G2API_AddBolt(ent->client->ghoul2, 0, "lradius"); ent->bolt_RArm = trap_G2API_AddBolt(ent->client->ghoul2, 0, "rradius"); ent->bolt_LLeg = trap_G2API_AddBolt(ent->client->ghoul2, 0, "ltibia"); ent->bolt_RLeg = trap_G2API_AddBolt(ent->client->ghoul2, 0, "rtibia"); ent->bolt_Motion = trap_G2API_AddBolt(ent->client->ghoul2, 0, "Motion"); } // locate ent at a spawn point ClientSpawn( ent ); if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { // send event tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = ent->s.clientNum; if ( g_gametype.integer != GT_TOURNAMENT ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStripEdString("SVINGAME", "PLENTER")) ); } } G_LogPrintf( "ClientBegin: %i\n", clientNum ); // count current clients and rank for scoreboard CalculateRanks(); G_ClearClientLog(clientNum); }
/* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info( netadr_t from, const Cmd::Args& args ) { int i, count, botCount; char infostring[ MAX_INFO_STRING ]; if ( args.Argc() < 2 ) { return; } const char *challenge = args.Argv(1).c_str(); /* * Check whether Cmd_Argv(1) has a sane length. This was not done in the original Quake3 version which led * to the Infostring bug discovered by Luigi Auriemma. See http://aluigi.altervista.org/ for the advisory. */ // A maximum challenge length of 128 should be more than plenty. if ( strlen( challenge ) > MAX_CHALLENGE_LEN ) { return; } //bani - bugtraq 12534 if ( !SV_VerifyChallenge( challenge ) ) { return; } SV_ResolveMasterServers(); // don't count privateclients botCount = count = 0; for ( i = sv_privateClients->integer; i < sv_maxclients->integer; i++ ) { if ( svs.clients[ i ].state >= CS_CONNECTED ) { if ( SV_IsBot(&svs.clients[ i ]) ) { ++botCount; } else { ++count; } } } infostring[ 0 ] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", challenge, false ); // If the master server listens on IPv4 and IPv6, we want to send the // most recent challenge received from it over the OTHER protocol for ( i = 0; i < MAX_MASTER_SERVERS; i++ ) { // First, see if the challenge was sent by this master server if ( !NET_CompareBaseAdr( from, masterServerAddr[ i ].ipv4 ) && !NET_CompareBaseAdr( from, masterServerAddr[ i ].ipv6 ) ) { continue; } // It was - if the saved challenge is for the other protocol, send it and record the current one if ( challenges[ i ].type == NA_IP || challenges[ i ].type == NA_IP6 ) { if ( challenges[ i ].type != from.type ) { Info_SetValueForKey( infostring, "challenge2", challenges[ i ].text, false ); challenges[ i ].type = from.type; strcpy( challenges[ i ].text, challenge ); break; } } // Otherwise record the current one regardless and check the next server challenges[ i ].type = from.type; strcpy( challenges[ i ].text, challenge ); } Info_SetValueForKey( infostring, "protocol", va( "%i", PROTOCOL_VERSION ), false ); Info_SetValueForKey( infostring, "hostname", sv_hostname->string, false ); Info_SetValueForKey( infostring, "serverload", va( "%i", svs.serverLoad ), false ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string, false ); Info_SetValueForKey( infostring, "clients", va( "%i", count ), false ); Info_SetValueForKey( infostring, "bots", va( "%i", botCount ), false ); Info_SetValueForKey( infostring, "sv_maxclients", va( "%i", sv_maxclients->integer - sv_privateClients->integer ), false ); Info_SetValueForKey( infostring, "pure", va( "%i", sv_pure->integer ), false ); if ( sv_statsURL->string[0] ) { Info_SetValueForKey( infostring, "stats", sv_statsURL->string, false ); } #ifdef USE_VOIP if ( sv_voip->integer ) { Info_SetValueForKey( infostring, "voip", va( "%i", sv_voip->integer ), false ); } #endif if ( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va( "%i", sv_minPing->integer ), false ); } if ( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va( "%i", sv_maxPing->integer ), false ); } Info_SetValueForKey( infostring, "gamename", GAMENAME_STRING, false ); // Arnout: to be able to filter out Quake servers NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); }
/* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info( netadr_t from ) { int i, count, wDisable; char *gamedir; char infostring[MAX_INFO_STRING]; // ignore if we are in single player /* if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { return; } */ #ifdef _XBOX // don't send system link info if in Xbox Live if (logged_on) return; #endif if (Cvar_VariableValue("ui_singlePlayerActive")) { return; } // don't count privateclients count = 0; for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) ); Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va("%i", count) ); Info_SetValueForKey( infostring, "sv_maxclients", va("%i", sv_maxclients->integer - sv_privateClients->integer ) ); Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); Info_SetValueForKey( infostring, "needpass", va("%i", sv_needpass->integer ) ); Info_SetValueForKey( infostring, "truejedi", va("%i", Cvar_VariableIntegerValue( "g_jediVmerc" ) ) ); if ( sv_gametype->integer == GT_DUEL || sv_gametype->integer == GT_POWERDUEL ) { wDisable = Cvar_VariableIntegerValue( "g_duelWeaponDisable" ); } else { wDisable = Cvar_VariableIntegerValue( "g_weaponDisable" ); } Info_SetValueForKey( infostring, "wdisable", va("%i", wDisable ) ); Info_SetValueForKey( infostring, "fdisable", va("%i", Cvar_VariableIntegerValue( "g_forcePowerDisable" ) ) ); //Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); if( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) ); } if( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) ); } gamedir = Cvar_VariableString( "fs_game" ); if( *gamedir ) { Info_SetValueForKey( infostring, "game", gamedir ); } #ifdef USE_CD_KEY Info_SetValueForKey( infostring, "sv_allowAnonymous", va("%i", sv_allowAnonymous->integer) ); #endif #ifdef _XBOX // Include Xbox specific networking info char sxnkid[XNKID_STRING_LEN]; XNKIDToString(SysLink_GetXNKID(), sxnkid); Info_SetValueForKey(infostring, "xnkid", sxnkid); char sxnkey[XNKEY_STRING_LEN]; XNKEYToString(SysLink_GetXNKEY(), sxnkey); Info_SetValueForKey(infostring, "xnkey", sxnkey); char sxnaddr[XNADDR_STRING_LEN]; XnAddrToString(Net_GetXNADDR(), sxnaddr); Info_SetValueForKey(infostring, "xnaddr", sxnaddr); #endif NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); }
/* =============== UI_ParseInfos =============== */ int UI_ParseInfos( char *buf, int max, char *infos[] ) { char *token; int count; char key[ MAX_TOKEN_CHARS ]; char info[ MAX_INFO_STRING ]; count = 0; while ( 1 ) { token = COM_Parse( &buf ); if ( !token[ 0 ] ) { break; } if ( strcmp( token, "{" ) ) { Com_Printf( "Missing { in info file\n" ); break; } if ( count == max ) { Com_Printf( "Max infos exceeded\n" ); break; } info[ 0 ] = '\0'; while ( 1 ) { token = COM_ParseExt( &buf, qtrue ); if ( !token[ 0 ] ) { Com_Printf( "Unexpected end of info file\n" ); break; } if ( !strcmp( token, "}" ) ) { break; } Q_strncpyz( key, token, sizeof( key ) ); token = COM_ParseExt( &buf, qfalse ); if ( !token[ 0 ] ) { strcpy( token, "<NULL>" ); } Info_SetValueForKey( info, key, token ); } //NOTE: extra space for arena number infos[ count ] = UI_Alloc( strlen( info ) + strlen( "\\num\\" ) + strlen( va( "%d", MAX_ARENAS ) ) + 1 ); if ( infos[ count ] ) { strcpy( infos[ count ], info ); count++; } } return count; }
/* * SV_UserinfoChanged * * Pull specific info from a newly changed userinfo string * into a more C friendly form. */ void SV_UserinfoChanged( client_t *client ) { char *val; int ival; assert( client ); assert( Info_Validate( client->userinfo ) ); if( !client->edict || !( client->edict->r.svflags & SVF_FAKECLIENT ) ) { // force the IP key/value pair so the game can filter based on ip if( !Info_SetValueForKey( client->userinfo, "socket", NET_SocketTypeToString( client->netchan.socket->type ) ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (socket)\n" ); return; } if( !Info_SetValueForKey( client->userinfo, "ip", NET_AddressToString( &client->netchan.remoteAddress ) ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (ip)\n" ); return; } } // mm session ival = 0; val = Info_ValueForKey( client->userinfo, "cl_mm_session" ); if( val ) ival = atoi( val ); if( !val || ival != client->mm_session ) Info_SetValueForKey( client->userinfo, "cl_mm_session", va("%d", client->mm_session ) ); // mm login if( client->mm_login[0] != '\0' ) { Info_SetValueForKey( client->userinfo, "cl_mm_login", client->mm_login ); } else { Info_RemoveKey( client->userinfo, "cl_mm_login" ); } // call prog code to allow overrides ge->ClientUserinfoChanged( client->edict, client->userinfo ); if( !Info_Validate( client->userinfo ) ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: Invalid userinfo (after game)" ); return; } // we assume that game module deals with setting a correct name val = Info_ValueForKey( client->userinfo, "name" ); if( !val || !val[0] ) { SV_DropClient( client, DROP_TYPE_GENERAL, "Error: No name set" ); return; } Q_strncpyz( client->name, val, sizeof( client->name ) ); #ifndef RATEKILLED // rate command if( NET_IsLANAddress( &client->netchan.remoteAddress ) ) { client->rate = 99999; // lans should not rate limit } else { val = Info_ValueForKey( client->userinfo, "rate" ); if( val && val[0] ) { int newrate; newrate = atoi( val ); if( sv_maxrate->integer && newrate > sv_maxrate->integer ) newrate = sv_maxrate->integer; else if( newrate > 90000 ) newrate = 90000; if( newrate < 1000 ) newrate = 1000; if( client->rate != newrate ) { client->rate = newrate; Com_Printf( "%s%s has rate %i\n", client->name, S_COLOR_WHITE, client->rate ); } } else client->rate = 5000; } #endif }
/* ================== SV_DirectConnect A "connect" OOB command has been received ================== */ void SV_DirectConnect( netadr_t from ) { char userinfo[MAX_INFO_STRING]; int i; client_t *cl, *newcl; MAC_STATIC client_t temp; sharedEntity_t *ent; int clientNum; int version; int qport; int challenge; char *password; int startIndex; char *denied; int count; Com_DPrintf ("SVC_DirectConnect ()\n"); Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) ); version = atoi( Info_ValueForKey( userinfo, "protocol" ) ); if ( version != PROTOCOL_VERSION ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version %i.\n", PROTOCOL_VERSION ); Com_DPrintf (" rejected connect from version %i\n", version); return; } challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) ); qport = atoi( Info_ValueForKey( userinfo, "qport" ) ); // quick reject for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( cl->state == CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { if (( svs.time - cl->lastConnectTime) < (sv_reconnectlimit->integer * 1000)) { Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from)); return; } break; } } // see if the challenge is valid (LAN clients don't need to challenge) if ( !NET_IsLocalAddress (from) ) { int ping; for (i=0 ; i<MAX_CHALLENGES ; i++) { if (NET_CompareAdr(from, svs.challenges[i].adr)) { if ( challenge == svs.challenges[i].challenge ) { break; // good } } } if (i == MAX_CHALLENGES) { NET_OutOfBandPrint( NS_SERVER, from, "print\nNo or bad challenge for address.\n" ); return; } // force the IP key/value pair so the game can filter based on ip Info_SetValueForKey( userinfo, "ip", NET_AdrToString( from ) ); ping = svs.time - svs.challenges[i].pingTime; Com_Printf( "Client %i connecting with %i challenge ping\n", i, ping ); svs.challenges[i].connected = qtrue; // never reject a LAN client based on ping if ( !Sys_IsLANAddress( from ) ) { if ( sv_minPing->value && ping < sv_minPing->value ) { // don't let them keep trying until they get a big delay NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for high pings only\n" ); Com_DPrintf ("Client %i rejected on a too low ping\n", i); // reset the address otherwise their ping will keep increasing // with each connect message and they'd eventually be able to connect svs.challenges[i].adr.port = 0; return; } if ( sv_maxPing->value && ping > sv_maxPing->value ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is for low pings only\n" ); Com_DPrintf ("Client %i rejected on a too high ping\n", i); return; } } } else { // force the "ip" info key to "localhost" Info_SetValueForKey( userinfo, "ip", "localhost" ); } newcl = &temp; Com_Memset (newcl, 0, sizeof(client_t)); // if there is already a slot for this ip, reuse it for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( cl->state == CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { Com_Printf ("%s:reconnect\n", NET_AdrToString (from)); newcl = cl; // disconnect the client from the game first so any flags the // player might have are dropped VM_Call( gvm, GAME_CLIENT_DISCONNECT, newcl - svs.clients ); // goto gotnewcl; } } // find a client slot // if "sv_privateClients" is set > 0, then that number // of client slots will be reserved for connections that // have "password" set to the value of "sv_privatePassword" // Info requests will report the maxclients as if the private // slots didn't exist, to prevent people from trying to connect // to a full server. // This is to allow us to reserve a couple slots here on our // servers so we can play without having to kick people. // check for privateClient password password = Info_ValueForKey( userinfo, "password" ); if ( !strcmp( password, sv_privatePassword->string ) ) { startIndex = 0; } else { // skip past the reserved slots startIndex = sv_privateClients->integer; } newcl = NULL; for ( i = startIndex; i < sv_maxclients->integer ; i++ ) { cl = &svs.clients[i]; if (cl->state == CS_FREE) { newcl = cl; break; } } if ( !newcl ) { if ( NET_IsLocalAddress( from ) ) { count = 0; for ( i = startIndex; i < sv_maxclients->integer ; i++ ) { cl = &svs.clients[i]; if (cl->netchan.remoteAddress.type == NA_BOT) { count++; } } // if they're all bots if (count >= sv_maxclients->integer - startIndex) { SV_DropClient(&svs.clients[sv_maxclients->integer - 1], "only bots on server"); newcl = &svs.clients[sv_maxclients->integer - 1]; } else { Com_Error( ERR_FATAL, "server is full on local connect\n" ); return; } } else { const char *SV_GetStripEdString(char *refSection, char *refName); NET_OutOfBandPrint( NS_SERVER, from, va("print\n%s\n", SV_GetStripEdString("SVINGAME","SERVER_IS_FULL"))); Com_DPrintf ("Rejected a connection.\n"); return; } } // we got a newcl, so reset the reliableSequence and reliableAcknowledge cl->reliableAcknowledge = 0; cl->reliableSequence = 0; gotnewcl: // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = temp; clientNum = newcl - svs.clients; ent = SV_GentityNum( clientNum ); newcl->gentity = ent; // save the challenge newcl->challenge = challenge; // save the address Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport); // save the userinfo Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) ); // get the game a chance to reject this connection or modify the userinfo denied = (char *)VM_Call( gvm, GAME_CLIENT_CONNECT, clientNum, qtrue, qfalse ); // firstTime = qtrue if ( denied ) { // we can't just use VM_ArgPtr, because that is only valid inside a VM_Call denied = (char *)VM_ExplicitArgPtr( gvm, (int)denied ); NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", denied ); Com_DPrintf ("Game rejected a connection: %s.\n", denied); return; } SV_UserinfoChanged( newcl ); // send the connect packet to the client NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" ); Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name ); newcl->state = CS_CONNECTED; newcl->nextSnapshotTime = svs.time; newcl->lastPacketTime = svs.time; newcl->lastConnectTime = svs.time; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit newcl->gamestateMessageNum = -1; // if this was the first client on the server, or the last client // the server can hold, send a heartbeat to the master. count = 0; for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } if ( count == 1 || count == sv_maxclients->integer ) { SV_Heartbeat_f(); } }
/* ================== SV_DirectConnect A "connect" OOB command has been received ================== */ void SV_DirectConnect( netadr_t from ) { char userinfo[MAX_INFO_STRING]; int i; client_t *cl, *newcl; client_t temp; gentity_t *ent; int clientNum; int version; int qport; //int challenge; char *denied; Com_DPrintf ("SVC_DirectConnect ()\n"); Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) ); version = atoi( Info_ValueForKey( userinfo, "protocol" ) ); if ( version != PROTOCOL_VERSION ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version %i.\n", PROTOCOL_VERSION ); Com_DPrintf (" rejected connect from version %i\n", version); return; } qport = atoi( Info_ValueForKey( userinfo, "qport" ) ); //challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) ); // see if the challenge is valid (local clients don't need to challenge) if ( !NET_IsLocalAddress (from) ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nNo challenge for address.\n" ); return; } else { // force the "ip" info key to "localhost" Info_SetValueForKey( userinfo, "ip", "localhost" ); } newcl = &temp; memset (newcl, 0, sizeof(client_t)); // if there is already a slot for this ip, reuse it for (i=0,cl=svs.clients ; i < 1 ; i++,cl++) { if ( cl->state == CS_FREE ) { continue; } if ( NET_CompareBaseAdr( from, cl->netchan.remoteAddress ) && ( cl->netchan.qport == qport || from.port == cl->netchan.remoteAddress.port ) ) { if (( sv.time - cl->lastConnectTime) < (sv_reconnectlimit->integer * 1000)) { Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (from)); return; } Com_Printf ("%s:reconnect\n", NET_AdrToString (from)); newcl = cl; goto gotnewcl; } } newcl = NULL; for ( i = 0; i < 1 ; i++ ) { cl = &svs.clients[i]; if (cl->state == CS_FREE) { newcl = cl; break; } } if ( !newcl ) { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer is full.\n" ); Com_DPrintf ("Rejected a connection.\n"); return; } gotnewcl: // build a new connection // accept the new client // this is the only place a client_t is ever initialized *newcl = temp; clientNum = newcl - svs.clients; ent = SV_GentityNum( clientNum ); newcl->gentity = ent; // save the address Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport); // save the userinfo Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) ); // get the game a chance to reject this connection or modify the userinfo denied = ge->ClientConnect( clientNum, qtrue, eSavedGameJustLoaded ); // firstTime = qtrue if ( denied ) { NET_OutOfBandPrint( NS_SERVER, from, "print\n%s\n", denied ); Com_DPrintf ("Game rejected a connection: %s.\n", denied); return; } SV_UserinfoChanged( newcl ); // send the connect packet to the client NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" ); newcl->state = CS_CONNECTED; newcl->lastPacketTime = sv.time; newcl->lastConnectTime = sv.time; // when we receive the first packet from the client, we will // notice that it is from a different serverid and that the // gamestate message was not just sent, forcing a retransmit newcl->gamestateMessageNum = -1; }
/*QUAKED script_mover (0.5 0.25 1.0) ? TRIGGERSPAWN SOLID EXPLOSIVEDAMAGEONLY RESURECTABLE COMPASS ALLIED AXIS MOUNTED_GUN Scripted brush entity. A simplified means of moving brushes around based on events. "modelscale" - Scale multiplier (defaults to 1, and scales uniformly) "modelscale_vec" - Set scale per-axis. Overrides "modelscale", so if you have both the "modelscale" is ignored "model2" optional md3 to draw over the solid clip brush "scriptname" name used for scripting purposes (like aiName in AI scripting) "health" optionally make this entity damagable "description" used with health, if the entity is damagable, it draws a healthbar with this description above it. */ void SP_script_mover(gentity_t *ent) { float scale[3] = { 1, 1, 1 }; vec3_t scalevec; char tagname[MAX_QPATH]; char *modelname; char *tagent; char cs[MAX_INFO_STRING]; char *s; if (!ent->model) { G_Error("script_mover must have a \"model\"\n"); } if (!ent->scriptName) { G_Error("script_mover must have a \"scriptname\"\n"); } ent->blocked = script_mover_blocked; // first position at start VectorCopy(ent->s.origin, ent->pos1); // VectorCopy( ent->r.currentOrigin, ent->pos1 ); VectorCopy(ent->pos1, ent->pos2); // don't go anywhere just yet trap_SetBrushModel(ent, ent->model); InitMover(ent); ent->reached = NULL; ent->s.animMovetype = 0; ent->s.density = 0; if (ent->spawnflags & 256) { ent->s.density |= 2; } if (ent->spawnflags & 8) { ent->use = script_mover_use; } if (ent->spawnflags & 16) { ent->s.time2 = 1; } else { ent->s.time2 = 0; } if (ent->spawnflags & 32) { ent->s.teamNum = TEAM_ALLIES; } else if (ent->spawnflags & 64) { ent->s.teamNum = TEAM_AXIS; } else { ent->s.teamNum = TEAM_FREE; } if (ent->spawnflags & 1) { ent->use = script_mover_use; trap_UnlinkEntity(ent); // make sure it's not visible return; } G_SetAngle(ent, ent->s.angles); G_SpawnInt("health", "0", &ent->health); if (ent->health) { ent->takedamage = qtrue; ent->count = ent->health; // client needs to know about it as well ent->s.effect1Time = ent->count; ent->s.dl_intensity = 255; if (G_SpawnString("description", "", &s)) { trap_GetConfigstring(CS_SCRIPT_MOVER_NAMES, cs, sizeof(cs)); Info_SetValueForKey(cs, va("%i", ent - g_entities), s); trap_SetConfigstring(CS_SCRIPT_MOVER_NAMES, cs); } } else { ent->count = 0; } ent->die = script_mover_die; // look for general scaling if (G_SpawnFloat("modelscale", "1", &scale[0])) { scale[2] = scale[1] = scale[0]; } if (G_SpawnString("model2", "", &modelname)) { COM_StripExtension(modelname, tagname); Q_strcat(tagname, MAX_QPATH, ".tag"); ent->tagNumber = trap_LoadTag(tagname); /* if( !(ent->tagNumber = trap_LoadTag( tagname )) ) { Com_Error( ERR_DROP, "Failed to load Tag File (%s)\n", tagname ); }*/ } // look for axis specific scaling if (G_SpawnVector("modelscale_vec", "1 1 1", &scalevec[0])) { VectorCopy(scalevec, scale); } if (scale[0] != 1 || scale[1] != 1 || scale[2] != 1) { ent->s.density |= 1; // scale is stored in 'angles2' VectorCopy(scale, ent->s.angles2); } if (ent->spawnflags & 128) { ent->s.density |= 4; ent->waterlevel = 0; if (G_SpawnString("gun", "", &modelname)) { if (!Q_stricmp(modelname, "browning")) { ent->s.density |= 8; } } G_SpawnString("tagent", "", &tagent); Q_strncpyz(ent->tagBuffer, tagent, 16); ent->s.powerups = -1; } ent->think = script_mover_spawn; ent->nextthink = level.time + FRAMETIME; }
/* ================ G_SetEntState sets the entstate of an entity. ================ */ void G_SetEntState(gentity_t *ent, entState_t state) { if (ent->entstate == state) { G_DPrintf("G_SetEntState: entity %i already in desired state [%i]\n", ent->s.number, state); return; } switch (state) { case STATE_DEFAULT: if (ent->entstate == STATE_UNDERCONSTRUCTION) { ent->clipmask = ent->realClipmask; ent->r.contents = ent->realContents; if (!ent->realNonSolidBModel) { ent->s.eFlags &= ~EF_NONSOLID_BMODEL; } } ent->entstate = STATE_DEFAULT; ent->s.powerups = STATE_DEFAULT; if (ent->s.eType == ET_WOLF_OBJECTIVE) { char cs[MAX_STRING_CHARS]; trap_GetConfigstring(ent->count, cs, sizeof(cs)); ent->count2 &= ~256; Info_SetValueForKey(cs, "t", va("%i", ent->count2)); trap_SetConfigstring(ent->count, cs); } if (ent->s.eType != ET_COMMANDMAP_MARKER) { trap_LinkEntity(ent); } // deal with any entities in the solid { int listedEntities, e; int entityList[MAX_GENTITIES]; gentity_t *check, *block; listedEntities = trap_EntitiesInBox(ent->r.absmin, ent->r.absmax, entityList, MAX_GENTITIES); for (e = 0; e < listedEntities; e++) { check = &g_entities[entityList[e]]; // ignore everything but items, players and missiles (grenades too) if (check->s.eType != ET_MISSILE && check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject) { continue; } if ((block = G_TestEntityPosition(check)) == NULL) { continue; } if (block != ent) { // the entity is blocked by another entity - that block this should take care of this itself continue; } if (check->client || check->s.eType == ET_CORPSE) { // gibs anything player like G_Damage(check, ent, ent, NULL, NULL, 9999, DAMAGE_NO_PROTECTION, MOD_CRUSH_CONSTRUCTIONDEATH_NOATTACKER); } else if (check->s.eType == ET_ITEM && check->item->giType == IT_TEAM) { // see if it's a critical entity, one that we can't just simply kill (basically flags) Team_DroppedFlagThink(check); } else { // remove the landmine from both teamlists if (check->s.eType == ET_MISSILE && check->methodOfDeath == MOD_LANDMINE) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], check - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], check - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } // just get rid of it G_FreeEntity(check); } } } break; case STATE_UNDERCONSTRUCTION: ent->entstate = STATE_UNDERCONSTRUCTION; ent->s.powerups = STATE_UNDERCONSTRUCTION; ent->realClipmask = ent->clipmask; if (ent->s.eType != ET_CONSTRUCTIBLE) // don't make nonsolid as we want to make them partially solid for staged construction { ent->clipmask = 0; } ent->realContents = ent->r.contents; if (ent->s.eType != ET_CONSTRUCTIBLE) { ent->r.contents = 0; } if (ent->s.eFlags & EF_NONSOLID_BMODEL) { ent->realNonSolidBModel = qtrue; } else if (ent->s.eType != ET_CONSTRUCTIBLE) { ent->s.eFlags |= EF_NONSOLID_BMODEL; } if (!Q_stricmp(ent->classname, "misc_mg42")) { // stop using the mg42 mg42_stopusing(ent); } if (ent->s.eType == ET_COMMANDMAP_MARKER) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } trap_LinkEntity(ent); break; case STATE_INVISIBLE: if (ent->entstate == STATE_UNDERCONSTRUCTION) { ent->clipmask = ent->realClipmask; ent->r.contents = ent->realContents; if (!ent->realNonSolidBModel) { ent->s.eFlags &= ~EF_NONSOLID_BMODEL; } } ent->entstate = STATE_INVISIBLE; ent->s.powerups = STATE_INVISIBLE; if (!Q_stricmp(ent->classname, "misc_mg42")) { mg42_stopusing(ent); } else if (ent->s.eType == ET_WOLF_OBJECTIVE) { char cs[MAX_STRING_CHARS]; trap_GetConfigstring(ent->count, cs, sizeof(cs)); ent->count2 |= 256; Info_SetValueForKey(cs, "t", va("%i", ent->count2)); trap_SetConfigstring(ent->count, cs); } if (ent->s.eType == ET_COMMANDMAP_MARKER) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } trap_UnlinkEntity(ent); break; } }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, const char *pclass, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; char userinfo[MAX_INFO_STRING]; int preTeam = 0; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); /* if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } */ key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "munro/main/default"; //RPG-X MODEL SYSTEM } Info_SetValueForKey( userinfo, key, model ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( G_Client_PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); if (g_gametype.integer >= GT_TEAM) { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { bot->client->sess.sessionTeam = G_Client_PickTeam( -1 ); } } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( G_Client_Connect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { team = "Blue"; } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); G_Client_UserinfoChanged( clientNum ); } if( delay == 0 ) { G_Client_Begin( clientNum, qfalse, qfalse, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* ================ SVC_Info New Xbox version - sends a short broadcast packet about our game, only done in system link games, every THREE seconds. ================ */ void SVC_Info( void ) { int i, count, wDisable; char infostring[MAX_INFO_STRING]; // We only send if we're in system link if (xb_gameType->integer != 2) return; // Wait three seconds to send another int now = Sys_Milliseconds(); if (now < svs.syslinkAdvertTime) return; svs.syslinkAdvertTime = now + 3000; // don't count bots! count = 0; for ( i = 0 ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED && svs.clients[i].netchan.remoteAddress.type != NA_BOT ) { count++; } } infostring[0] = 0; Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va("%i", count) ); Info_SetValueForKey( infostring, "sv_maxclients", va("%i", sv_maxclients->integer) ); Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); if ( sv_gametype->integer == GT_DUEL || sv_gametype->integer == GT_POWERDUEL ) { wDisable = Cvar_VariableIntegerValue( "g_duelWeaponDisable" ); } else { wDisable = Cvar_VariableIntegerValue( "g_weaponDisable" ); } Info_SetValueForKey( infostring, "wdisable", va("%i", wDisable ) ); Info_SetValueForKey( infostring, "fdisable", va("%i", Cvar_VariableIntegerValue( "g_forcePowerDisable" ) ) ); #ifdef _XBOX // Include Xbox specific networking info char sxnkid[XNKID_STRING_LEN]; XNKIDToString(SysLink_GetXNKID(), sxnkid); Info_SetValueForKey(infostring, "xnkid", sxnkid); char sxnkey[XNKEY_STRING_LEN]; XNKEYToString(SysLink_GetXNKEY(), sxnkey); Info_SetValueForKey(infostring, "xnkey", sxnkey); char sxnaddr[XNADDR_STRING_LEN]; XnAddrToString(Net_GetXNADDR(), sxnaddr); Info_SetValueForKey(infostring, "xnaddr", sxnaddr); #endif // NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); NET_BroadcastPrint( NS_SERVER, "infoResponse\n%s", infostring ); }
/* ================ SVC_Info Responds with a short info message that should be enough to determine if a user is interested in a server to do a full status ================ */ void SVC_Info( netadr_t from ) { int i, count; char *gamedir; char infostring[MAX_INFO_STRING]; // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { return; } /* * Check whether Cmd_Argv(1) has a sane length. This was not done in the original Quake3 version which led * to the Infostring bug discovered by Luigi Auriemma. See http://aluigi.altervista.org/ for the advisory. */ // A maximum challenge length of 128 should be more than plenty. if(strlen(Cmd_Argv(1)) > 128) return; // don't count privateclients count = 0; for ( i = sv_privateClients->integer ; i < sv_maxclients->integer ; i++ ) { if ( svs.clients[i].state >= CS_CONNECTED ) { count++; } } infostring[0] = 0; // echo back the parameter to status. so servers can use it as a challenge // to prevent timed spoofed reply packets that add ghost servers Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) ); Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va("%i", count) ); Info_SetValueForKey( infostring, "sv_maxclients", va("%i", sv_maxclients->integer - sv_privateClients->integer ) ); Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); if( sv_minPing->integer ) { Info_SetValueForKey( infostring, "minPing", va("%i", sv_minPing->integer) ); } if( sv_maxPing->integer ) { Info_SetValueForKey( infostring, "maxPing", va("%i", sv_maxPing->integer) ); } gamedir = Cvar_VariableString( "fs_game" ); if( *gamedir ) { Info_SetValueForKey( infostring, "game", gamedir ); } NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); }
/* ==================== LAN_GetServerInfo ==================== */ static void LAN_GetServerInfo(int source, int n, char *buf, int buflen) { char info[MAX_STRING_CHARS]; serverInfo_t *server = NULL; info[0] = '\0'; switch (source) { case AS_LOCAL: if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.localServers[n]; } break; case AS_GLOBAL: if (n >= 0 && n < MAX_GLOBAL_SERVERS) { server = &cls.globalServers[n]; } break; case AS_FAVORITES: if (n >= 0 && n < MAX_OTHER_SERVERS) { server = &cls.favoriteServers[n]; } break; } if (server && buf) { buf[0] = '\0'; Info_SetValueForKey(info, "version", server->version); Info_SetValueForKey(info, "hostname", server->hostName); Info_SetValueForKey(info, "serverload", va("%i", server->load)); Info_SetValueForKey(info, "mapname", server->mapName); Info_SetValueForKey(info, "clients", va("%i", server->clients)); Info_SetValueForKey(info, "humans", va("%i", server->humans)); Info_SetValueForKey(info, "sv_maxclients", va("%i", server->maxClients)); Info_SetValueForKey(info, "ping", va("%i", server->ping)); Info_SetValueForKey(info, "minping", va("%i", server->minPing)); Info_SetValueForKey(info, "maxping", va("%i", server->maxPing)); Info_SetValueForKey(info, "game", server->game); Info_SetValueForKey(info, "gametype", va("%i", server->gameType)); Info_SetValueForKey(info, "nettype", va("%i", server->netType)); Info_SetValueForKey(info, "addr", NET_AdrToString(server->adr)); Info_SetValueForKey(info, "friendlyFire", va("%i", server->friendlyFire)); Info_SetValueForKey(info, "maxlives", va("%i", server->maxlives)); Info_SetValueForKey(info, "needpass", va("%i", server->needpass)); Info_SetValueForKey(info, "punkbuster", va("%i", server->punkbuster)); Info_SetValueForKey(info, "gamename", server->gameName); Info_SetValueForKey(info, "g_antilag", va("%i", server->antilag)); Info_SetValueForKey(info, "weaprestrict", va("%i", server->weaprestrict)); Info_SetValueForKey(info, "balancedteams", va("%i", server->balancedteams)); Q_strncpyz(buf, info, buflen); } else { if (buf) { buf[0] = '\0'; } } }
/* ================= SV_UserinfoChanged Pull specific info from a newly changed userinfo string into a more C friendly form. ================= */ void SV_UserinfoChanged( client_t *cl ) { char *val; char *ip; int i; int len; // name for C code Q_strncpyz( cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name) ); // rate command // if the client is on the same subnet as the server and we aren't running an // internet public server, assume they don't need a rate choke if ( Sys_IsLANAddress( cl->netchan.remoteAddress ) && com_dedicated->integer != 2 && sv_lanForceRate->integer == 1) { cl->rate = 99999; // lans should not rate limit } else { val = Info_ValueForKey (cl->userinfo, "rate"); if (strlen(val)) { i = atoi(val); cl->rate = i; if (cl->rate < 1000) { cl->rate = 1000; } else if (cl->rate > 90000) { cl->rate = 90000; } } else { cl->rate = 3000; } } val = Info_ValueForKey (cl->userinfo, "handicap"); if (strlen(val)) { i = atoi(val); if (i<=0 || i>100 || strlen(val) > 4) { Info_SetValueForKey( cl->userinfo, "handicap", "100" ); } } // snaps command val = Info_ValueForKey (cl->userinfo, "snaps"); if(strlen(val)) { i = atoi(val); if(i < 1) i = 1; else if(i > sv_fps->integer) i = sv_fps->integer; i = 1000 / i; } else i = 50; if(i != cl->snapshotMsec) { // Reset last sent snapshot so we avoid desync between server frame time and snapshot send time cl->lastSnapshotTime = 0; cl->snapshotMsec = i; } #ifdef USE_VOIP #ifdef LEGACY_PROTOCOL if(cl->compat) cl->hasVoip = qfalse; else #endif { val = Info_ValueForKey(cl->userinfo, "cl_voip"); cl->hasVoip = atoi(val); } #endif // TTimo // maintain the IP information // the banning code relies on this being consistently present if( NET_IsLocalAddress(cl->netchan.remoteAddress) ) ip = "localhost"; else ip = (char*)NET_AdrToString( cl->netchan.remoteAddress ); val = Info_ValueForKey( cl->userinfo, "ip" ); if( val[0] ) len = strlen( ip ) - strlen( val ) + strlen( cl->userinfo ); else len = strlen( ip ) + 4 + strlen( cl->userinfo ); if( len >= MAX_INFO_STRING ) SV_DropClient( cl, "userinfo string length exceeded" ); else Info_SetValueForKey( cl->userinfo, "ip", ip ); }
void G_LoadArenas( void ) { #if 0 int numdirs; char filename[128]; char filename[MAX_QPATH]; char dirlist[1024]; char* dirptr; int i, n; int dirlen; g_numArenas = 0; // get all arenas from .arena files numdirs = trap->FS_GetFileList("scripts", ".arena", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); Q_strncpyz( filename, "scripts/", sizeof( filename ) ); strcat(filename, dirptr); G_LoadArenasFromFile(filename); } // trap->Print( va( "%i arenas parsed\n", g_numArenas ) ); for( n = 0; n < g_numArenas; n++ ) { Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) ); } G_RefreshNextMap(level.gametype, qfalse); #else // Ensiform's version int numFiles; char filelist[MAPSBUFSIZE]; char filename[MAX_QPATH]; char* fileptr; int i, n; int len; g_numArenas = 0; // get all arenas from .arena files numFiles = trap->FS_GetFileList("scripts", ".arena", filelist, ARRAY_LEN(filelist) ); fileptr = filelist; i = 0; if (numFiles > MAX_MAPS) numFiles = MAX_MAPS; for(; i < numFiles; i++) { len = strlen(fileptr); Com_sprintf(filename, sizeof(filename), "scripts/%s", fileptr); G_LoadArenasFromFile(filename); fileptr += len + 1; } // trap->Print( va( "%i arenas parsed\n", g_numArenas ) ); for( n = 0; n < g_numArenas; n++ ) { Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) ); } G_RefreshNextMap(level.gametype, qfalse); #endif }
/** * @brief A connection request that did not come from the master * @sa CL_ConnectionlessPacket */ static void SVC_DirectConnect (struct net_stream* stream) { Com_DPrintf(DEBUG_SERVER, "SVC_DirectConnect()\n"); if (sv->started || sv->spawned) { Com_Printf("rejected connect because match is already running\n"); NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_GAME_ALREADY_STARTED "\n"); return; } char buf[256]; const char* peername = NET_StreamPeerToName(stream, buf, sizeof(buf), false); const int version = atoi(Cmd_Argv(1)); if (version != PROTOCOL_VERSION) { Com_Printf("rejected connect from version %i - %s\n", version, peername); NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_SERVER_VERSION_MISMATCH "\n"); return; } char userinfo[MAX_INFO_STRING]; Q_strncpyz(userinfo, Cmd_Argv(2), sizeof(userinfo)); if (Q_strnull(userinfo)) { /* catch empty userinfo */ Com_Printf("Empty userinfo from %s\n", peername); NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_CONNECTION_REFUSED "\n"); return; } if (strchr(userinfo, '\xFF')) { /* catch end of message in string exploit */ Com_Printf("Illegal userinfo contained xFF from %s\n", peername); NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_CONNECTION_REFUSED "\n"); return; } if (strlen(Info_ValueForKey(userinfo, "ip"))) { /* catch spoofed ips */ Com_Printf("Illegal userinfo contained ip from %s\n", peername); NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_CONNECTION_REFUSED "\n"); return; } /* force the IP key/value pair so the game can filter based on ip */ Info_SetValueForKey(userinfo, sizeof(userinfo), "ip", peername); /* find a client slot */ client_t* cl = nullptr; while ((cl = SV_GetNextClient(cl)) != nullptr) if (cl->state == cs_free) break; if (cl == nullptr) { NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_SERVER_FULL "\n"); Com_Printf("Rejected a connection - server is full.\n"); return; } /* build a new connection - accept the new client * this is the only place a client_t is ever initialized */ OBJZERO(*cl); const int playernum = cl - SV_GetClient(0); SrvPlayer* player = PLAYER_NUM(playernum); cl->player = player; cl->player->setNum(playernum); bool connected; { const ScopedMutex scopedMutex(svs.serverMutex); connected = svs.ge->ClientConnect(player, userinfo, sizeof(userinfo)); } /* get the game a chance to reject this connection or modify the userinfo */ if (!connected) { const char* rejmsg = Info_ValueForKey(userinfo, "rejmsg"); if (rejmsg[0] != '\0') { NET_OOB_Printf(stream, SV_CMD_PRINT "\n%s\n" REJ_CONNECTION_REFUSED "\n", rejmsg); Com_Printf("Game rejected a connection from %s. Reason: %s\n", peername, rejmsg); } else { NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_CONNECTION_REFUSED "\n"); Com_Printf("Game rejected a connection from %s.\n", peername); } return; } /* new player */ cl->player->setInUse(true); cl->lastmessage = svs.realtime; /* parse some info from the info strings */ Q_strncpyz(cl->userinfo, userinfo, sizeof(cl->userinfo)); SV_UserinfoChanged(cl); /* send the connect packet to the client */ if (sv_http_downloadserver->string[0]) NET_OOB_Printf(stream, CL_CMD_CLIENT_CONNECT " dlserver=%s", sv_http_downloadserver->string); else NET_OOB_Printf(stream, CL_CMD_CLIENT_CONNECT); SV_SetClientState(cl, cs_connected); Q_strncpyz(cl->peername, peername, sizeof(cl->peername)); cl->stream = stream; NET_StreamSetData(stream, cl); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { gentity_t *bot = NULL; int clientNum, preTeam = TEAM_FREE; char userinfo[MAX_INFO_STRING] = {0}, *botinfo = NULL, *key = NULL, *s = NULL, *botname = NULL, *model = NULL; // have the server allocate a client slot clientNum = trap->BotAllocateClient(); if ( clientNum == -1 ) { // trap->Print( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); // trap->Print( S_COLOR_RED "Start server with more 'open' slots.\n" ); trap->SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "UNABLE_TO_ADD_BOT"))); return; } // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { trap->BotFreeClient( clientNum ); trap->Print( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) botname = Info_ValueForKey( botinfo, "name" ); // check for an alternative name if ( altname && altname[0] ) botname = altname; Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "ip", "localhost" ); Info_SetValueForKey( userinfo, "skill", va("%.2f", skill) ); if ( skill >= 1 && skill < 2 ) Info_SetValueForKey( userinfo, "handicap", "50" ); else if ( skill >= 2 && skill < 3 ) Info_SetValueForKey( userinfo, "handicap", "70" ); else if ( skill >= 3 && skill < 4 ) Info_SetValueForKey( userinfo, "handicap", "90" ); else Info_SetValueForKey( userinfo, "handicap", "100" ); key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) model = DEFAULT_MODEL"/default"; Info_SetValueForKey( userinfo, key, model ); key = "sex"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = Info_ValueForKey( botinfo, "gender" ); if ( !*s ) s = "male"; Info_SetValueForKey( userinfo, key, s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "4"; Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "4"; Info_SetValueForKey( userinfo, key, s ); key = "saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = DEFAULT_SABER; Info_SetValueForKey( userinfo, key, s ); key = "saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "none"; Info_SetValueForKey( userinfo, key, s ); //Raz: Added key = "forcepowers"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = DEFAULT_FORCEPOWERS; Info_SetValueForKey( userinfo, key, s ); key = "cg_predictItems"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "1"; Info_SetValueForKey( userinfo, key, s ); key = "char_color_red"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "255"; Info_SetValueForKey( userinfo, key, s ); key = "char_color_green"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "255"; Info_SetValueForKey( userinfo, key, s ); key = "char_color_blue"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "255"; Info_SetValueForKey( userinfo, key, s ); key = "teamtask"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "0"; Info_SetValueForKey( userinfo, key, s ); key = "personality"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) s = "botfiles/default.jkb"; Info_SetValueForKey( userinfo, key, s ); // initialize the bot settings if ( !team || !*team ) { if ( level.gametype >= GT_TEAM ) { if ( PickTeam( clientNum ) == TEAM_RED) team = "red"; else team = "blue"; } else team = "red"; } Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; // bot->r.svFlags |= SVF_BOT; // bot->inuse = qtrue; // register the userinfo trap->SetUserinfo( clientNum, userinfo ); if ( level.gametype >= GT_TEAM ) { if ( team && !Q_stricmp( team, "red" ) ) bot->client->sess.sessionTeam = TEAM_RED; else if ( team && !Q_stricmp( team, "blue" ) ) bot->client->sess.sessionTeam = TEAM_BLUE; else bot->client->sess.sessionTeam = PickTeam( -1 ); } if ( level.gametype == GT_SIEGE ) { bot->client->sess.siegeDesiredTeam = bot->client->sess.sessionTeam; bot->client->sess.sessionTeam = TEAM_SPECTATOR; } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) return; if ( bot->client->sess.sessionTeam != preTeam ) { trap->GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); if ( bot->client->sess.sessionTeam == TEAM_SPECTATOR ) bot->client->sess.sessionTeam = preTeam; if ( bot->client->sess.sessionTeam == TEAM_RED ) team = "Red"; else { if ( level.gametype == GT_SIEGE ) team = (bot->client->sess.sessionTeam == TEAM_BLUE) ? "Blue" : "s"; else team = "Blue"; } Info_SetValueForKey( userinfo, "team", team ); trap->SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); if ( !ClientUserinfoChanged( clientNum ) ) return; } if (level.gametype == GT_DUEL || level.gametype == GT_POWERDUEL) { int loners = 0; int doubles = 0; bot->client->sess.duelTeam = 0; G_PowerDuelCount(&loners, &doubles, qtrue); if (!doubles || loners > (doubles/2)) { bot->client->sess.duelTeam = DUELTEAM_DOUBLE; } else { bot->client->sess.duelTeam = DUELTEAM_LONE; } bot->client->sess.sessionTeam = TEAM_SPECTATOR; SetTeam(bot, "s"); } else { if( delay == 0 ) { ClientBegin( clientNum, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); } }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; // char *headmodel; char userinfo[MAX_INFO_STRING]; int preTeam = 0; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "kyle/default"; } Info_SetValueForKey( userinfo, key, model ); /* key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); */ key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "single_1"; } Info_SetValueForKey( userinfo, key, s ); key = "saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "personality"); if (!*s ) { Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" ); } else { Info_SetValueForKey( userinfo, "personality", s ); } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { // G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); // G_Printf( S_COLOR_RED "Start server with more 'open' slots.\n" ); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "UNABLE_TO_ADD_BOT"))); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } // Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); if (g_gametype.integer >= GT_TEAM) { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { bot->client->sess.sessionTeam = PickTeam( -1 ); } } if (g_gametype.integer == GT_SIEGE) { bot->client->sess.siegeDesiredTeam = bot->client->sess.sessionTeam; bot->client->sess.sessionTeam = TEAM_SPECTATOR; } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { if (g_gametype.integer == GT_SIEGE) { if (bot->client->sess.sessionTeam == TEAM_BLUE) { team = "Blue"; } else { team = "s"; } } else { team = "Blue"; } } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); ClientUserinfoChanged( clientNum ); } if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) { int loners = 0; int doubles = 0; bot->client->sess.duelTeam = 0; G_PowerDuelCount(&loners, &doubles, qtrue); if (!doubles || loners > (doubles/2)) { bot->client->sess.duelTeam = DUELTEAM_DOUBLE; } else { bot->client->sess.duelTeam = DUELTEAM_LONE; } bot->client->sess.sessionTeam = TEAM_SPECTATOR; SetTeam(bot, "s"); } else { if( delay == 0 ) { ClientBegin( clientNum, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); } }
/* =============== UI_LoadArenas =============== */ static void UI_LoadArenas(void) { int numdirs; vmCvar_t arenasFile; char filename[128]; char dirlist[2048]; char* dirptr; int i, n; int dirlen; char *type; char *tag; int singlePlayerNum, specialNum, otherNum; ui_numArenas = 0; trap_Cvar_Register(&arenasFile, "g_arenasFile", "", CVAR_INIT | CVAR_ROM); if (*arenasFile.string) { UI_LoadArenasFromFile(arenasFile.string); } else { UI_LoadArenasFromFile("scripts/arenas.txt"); } // get all arenas from .arena files numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 2048); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen + 1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); UI_LoadArenasFromFile(filename); } trap_Print(va("%i arenas parsed\n", ui_numArenas)); if (outOfMemory) trap_Print(S_COLOR_YELLOW"WARNING: not anough memory in pool to load all arenas\n"); // set initial numbers for (n = 0; n < ui_numArenas; n++) { Info_SetValueForKey(ui_arenaInfos[n], "num", va("%i", n)); } // go through and count single players levels ui_numSinglePlayerArenas = 0; ui_numSpecialSinglePlayerArenas = 0; for (n = 0; n < ui_numArenas; n++) { // determine type type = Info_ValueForKey(ui_arenaInfos[n], "type"); // if no type specified, it will be treated as "ffa" if (!*type) { continue; } if (strstr(type, "single")) { // check for special single player arenas (training, final) tag = Info_ValueForKey(ui_arenaInfos[n], "special"); if (*tag) { ui_numSpecialSinglePlayerArenas++; continue; } ui_numSinglePlayerArenas++; } } n = ui_numSinglePlayerArenas % ARENAS_PER_TIER; if (n != 0) { ui_numSinglePlayerArenas -= n; trap_Print(va("%i arenas ignored to make count divisible by %i\n", n, ARENAS_PER_TIER)); } // go through once more and assign number to the levels singlePlayerNum = 0; specialNum = singlePlayerNum + ui_numSinglePlayerArenas; otherNum = specialNum + ui_numSpecialSinglePlayerArenas; for (n = 0; n < ui_numArenas; n++) { // determine type type = Info_ValueForKey(ui_arenaInfos[n], "type"); // if no type specified, it will be treated as "ffa" if (*type) { if (strstr(type, "single")) { // check for special single player arenas (training, final) tag = Info_ValueForKey(ui_arenaInfos[n], "special"); if (*tag) { Info_SetValueForKey(ui_arenaInfos[n], "num", va("%i", specialNum++)); continue; } Info_SetValueForKey(ui_arenaInfos[n], "num", va("%i", singlePlayerNum++)); continue; } } Info_SetValueForKey(ui_arenaInfos[n], "num", va("%i", otherNum++)); } }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; char *headmodel; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "visor/default"; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* * SVC_DirectConnect * A connection request that did not come from the master */ static void SVC_DirectConnect( const socket_t *socket, const netadr_t *address ) { #ifdef TCP_ALLOW_CONNECT int incoming = 0; #endif char userinfo[MAX_INFO_STRING]; client_t *cl, *newcl; int i, version, game_port, challenge; int previousclients; int session_id; char *session_id_str; unsigned int ticket_id; bool tv_client; unsigned int time; Com_DPrintf( "SVC_DirectConnect (%s)\n", Cmd_Args() ); version = atoi( Cmd_Argv( 1 ) ); if( version != APP_PROTOCOL_VERSION ) { if( version <= 6 ) { // before reject packet was added Netchan_OutOfBandPrint( socket, address, "print\nServer is version %4.2f. Protocol %3i\n", APP_VERSION, APP_PROTOCOL_VERSION ); } else { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer and client don't have the same version\n", DROP_TYPE_GENERAL, 0 ); } Com_DPrintf( " rejected connect from protocol %i\n", version ); return; } game_port = atoi( Cmd_Argv( 2 ) ); challenge = atoi( Cmd_Argv( 3 ) ); tv_client = ( atoi( Cmd_Argv( 5 ) ) & 1 ? true : false ); if( !Info_Validate( Cmd_Argv( 4 ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nInvalid userinfo string\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Connection from %s refused: invalid userinfo string\n", NET_AddressToString( address ) ); return; } Q_strncpyz( userinfo, Cmd_Argv( 4 ), sizeof( userinfo ) ); // force the IP key/value pair so the game can filter based on ip if( !Info_SetValueForKey( userinfo, "socket", NET_SocketTypeToString( socket->type ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (socket)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Connection from %s refused: couldn't set userinfo (socket)\n", NET_AddressToString( address ) ); return; } if( !Info_SetValueForKey( userinfo, "ip", NET_AddressToString( address ) ) ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nError: Couldn't set userinfo (ip)\n", DROP_TYPE_GENERAL, 0 ); Com_DPrintf( "Connection from %s refused: couldn't set userinfo (ip)\n", NET_AddressToString( address ) ); return; } if( Cmd_Argc() >= 7 ) { // we have extended information, ticket-id and session-id Com_Printf("Extended information %s\n", Cmd_Argv(6) ); ticket_id = (unsigned int)atoi( Cmd_Argv(6) ); session_id_str = Info_ValueForKey( userinfo, "cl_mm_session" ); if( session_id_str != NULL ) session_id = atoi( session_id_str ); else session_id = 0; } else { ticket_id = 0; session_id = 0; } #ifdef TCP_ALLOW_CONNECT if( socket->type == SOCKET_TCP ) { // find the connection for( i = 0; i < MAX_INCOMING_CONNECTIONS; i++ ) { if( !svs.incoming[i].active ) continue; if( NET_CompareAddress( &svs.incoming[i].address, address ) && socket == &svs.incoming[i].socket ) break; } if( i == MAX_INCOMING_CONNECTIONS ) { Com_Error( ERR_FATAL, "Incoming connection not found.\n" ); return; } incoming = i; } #endif // see if the challenge is valid for( i = 0; i < MAX_CHALLENGES; i++ ) { if( NET_CompareBaseAddress( address, &svs.challenges[i].adr ) ) { if( challenge == svs.challenges[i].challenge ) { svs.challenges[i].challenge = 0; // wsw : r1q2 : reset challenge svs.challenges[i].time = 0; NET_InitAddress( &svs.challenges[i].adr, NA_NOTRANSMIT ); break; // good } Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nBad challenge\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } } if( i == MAX_CHALLENGES ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nNo challenge for address\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); return; } //r1: limit connections from a single IP if( sv_iplimit->integer ) { previousclients = 0; for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) continue; if( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) ) { //r1: zombies are less dangerous if( cl->state == CS_ZOMBIE ) previousclients++; else previousclients += 2; } } if( previousclients >= sv_iplimit->integer * 2 ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nToo many connections from your host\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); Com_DPrintf( "%s:connect rejected : too many connections\n", NET_AddressToString( address ) ); return; } } newcl = NULL; // if there is already a slot for this ip, reuse it time = Sys_Milliseconds(); for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) continue; if( NET_CompareAddress( address, &cl->netchan.remoteAddress ) || ( NET_CompareBaseAddress( address, &cl->netchan.remoteAddress ) && cl->netchan.game_port == game_port ) ) { if( !NET_IsLocalAddress( address ) && ( time - cl->lastconnect ) < (unsigned)( sv_reconnectlimit->integer * 1000 ) ) { Com_DPrintf( "%s:reconnect rejected : too soon\n", NET_AddressToString( address ) ); return; } Com_Printf( "%s:reconnect\n", NET_AddressToString( address ) ); newcl = cl; break; } } // find a client slot if( !newcl ) { for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( cl->state == CS_FREE ) { newcl = cl; break; } // overwrite fakeclient if no free spots found if( cl->state && cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) newcl = cl; } if( !newcl ) { Netchan_OutOfBandPrint( socket, address, "reject\n%i\n%i\nServer is full\n", DROP_TYPE_GENERAL, DROP_FLAG_AUTORECONNECT ); Com_DPrintf( "Server is full. Rejected a connection.\n" ); return; } if( newcl->state && newcl->edict && ( newcl->edict->r.svflags & SVF_FAKECLIENT ) ) SV_DropClient( newcl, DROP_TYPE_GENERAL, "%s", "Need room for a real player" ); } // get the game a chance to reject this connection or modify the userinfo if( !SV_ClientConnect( socket, address, newcl, userinfo, game_port, challenge, false, tv_client, ticket_id, session_id ) ) { char *rejtype, *rejflag, *rejtypeflag, *rejmsg; rejtype = Info_ValueForKey( userinfo, "rejtype" ); if( !rejtype ) rejtype = "0"; rejflag = Info_ValueForKey( userinfo, "rejflag" ); if( !rejflag ) rejflag = "0"; // hax because Info_ValueForKey can only be called twice in a row rejtypeflag = va( "%s\n%s", rejtype, rejflag ); rejmsg = Info_ValueForKey( userinfo, "rejmsg" ); if( !rejmsg ) rejmsg = "Game module rejected connection"; Netchan_OutOfBandPrint( socket, address, "reject\n%s\n%s\n", rejtypeflag, rejmsg ); Com_DPrintf( "Game rejected a connection.\n" ); return; } // send the connect packet to the client Netchan_OutOfBandPrint( socket, address, "client_connect\n%s", newcl->session ); // free the incoming entry #ifdef TCP_ALLOW_CONNECT if( socket->type == SOCKET_TCP ) { svs.incoming[incoming].active = false; svs.incoming[incoming].socket.open = false; } #endif }