/* ================= G_Spawn Either finds a free edict, or allocates a new one. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ edict_t *G_Spawn (void) { int i; edict_t *e; e = &g_edicts[(int)maxclients->value+1]; for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++) { // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) ) { G_InitEdict (e); return e; } } if (i == game.maxentities) gi.error ("ED_Alloc: no free edicts"); globals.num_edicts++; if(developer->value && readout->value) gi.dprintf("num_edicts = %d\n",globals.num_edicts); G_InitEdict (e); return e; }
/* * Either finds a free edict, or allocates a new one. * Try to avoid reusing an entity that was recently freed, because it * can cause the client to think the entity morphed into something else * instead of being removed and recreated, which can cause interpolated * angles and bad trails. */ edict_t * G_Spawn(void) { int i; edict_t *e; e = &g_edicts[(int)maxclients->value + 1]; for (i = maxclients->value + 1; i < globals.num_edicts; i++, e++) { /* the first couple seconds of server time can involve a lot of freeing and allocating, so relax the replacement policy */ if (!e->inuse && ((e->freetime < 2) || (level.time - e->freetime > 0.5))) { G_InitEdict(e); return e; } } if (i == game.maxentities) { gi.error("ED_Alloc: no free edicts"); } globals.num_edicts++; G_InitEdict(e); return e; }
/* =========== ClientBegin called when a client has finished connecting, and is ready to be placed into the game. This will happen every level load. ============ */ void ClientBegin (edict_t *ent) { int i; ent->client = game.clients + (ent - g_edicts - 1); if (deathmatch->value) { ClientBeginDeathmatch (ent); return; } // if there is already a body waiting for us (a loadgame), just // take it, otherwise spawn one from scratch if (ent->inuse == true) { // the client has cleared the client side viewangles upon // connecting to the server, which is different than the // state when the game is saved, so we need to compensate // with deltaangles for (i=0 ; i<3 ; i++) ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->ps.viewangles[i]); } else { // a spawn point will completely reinitialize the entity // except for the persistant data that was initialized at // ClientConnect() time G_InitEdict (ent); ent->classname = "player"; InitClientResp (ent->client); PutClientInServer (ent); } if (level.intermissiontime) { MoveClientToIntermission (ent); } else { // send effect if in a multiplayer game if (game.maxclients > 1) { gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); #ifdef WITH_ACEBOT safe_bprintf #else gi.bprintf #endif (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); } } // make sure all view stuff is valid ClientEndServerFrame (ent); }
/* ===================== ClientBeginDeathmatch A client has just connected to the server in deathmatch mode, so clear everything out before starting them. ===================== */ void ClientBeginDeathmatch (edict_t *ent) { G_InitEdict (ent); InitClientResp (ent->client); // locate ent at a spawn point PutClientInServer (ent); if (level.intermissiontime) { MoveClientToIntermission (ent); } else { // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); } gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); // make sure all view stuff is valid ClientEndServerFrame (ent); }
/////////////////////////////////////////////////////////////////////// // Spawn the bot /////////////////////////////////////////////////////////////////////// void BOT_SpawnBot (char *team, char *name, char *skin, char *userinfo) { edict_t *bot; if( !nav.loaded ) { Com_Printf("Can't spawn bots without a valid navigation file\n"); return; } bot = BOT_FindFreeClient (); if (!bot) { safe_bprintf (PRINT_MEDIUM, "Server is full, increase Maxclients.\n"); return; } //init the bot bot->inuse = true; bot->yaw_speed = 100; // To allow bots to respawn if(userinfo == NULL) BOT_SetName(bot, name, skin, team); else ClientConnect (bot, userinfo); G_InitEdict (bot); G_SpawnAI(bot); //jabot092(2) bot->ai->is_bot = true; InitClientResp (bot->client); PutClientInServer(bot); BOT_StartAsSpectator (bot); //skill bot->ai->pers.skillLevel = (int)(random()*MAX_BOT_SKILL); if (bot->ai->pers.skillLevel > MAX_BOT_SKILL) //fix if off-limits bot->ai->pers.skillLevel = MAX_BOT_SKILL; else if (bot->ai->pers.skillLevel < 0) bot->ai->pers.skillLevel = 0; BOT_DMclass_InitPersistant(bot); AI_ResetWeights(bot); AI_ResetNavigation(bot); bot->think = BOT_JoinGame; bot->nextthink = level.time + (int)(random()*6.0); if( ctf->value && team != NULL ) { if( !Q_stricmp( team, "blue" ) ) bot->think = BOT_JoinBlue; else if( !Q_stricmp( team, "red" ) ) bot->think = BOT_JoinRed; } AI_EnemyAdded (bot); // let the ai know we added another }
/** * @brief Either finds a free edict, or allocates a new one. * @note Try to avoid reusing an entity that was recently freed, because it * can cause the player to think the entity morphed into something else * instead of being removed and recreated, which can cause interpolated * angles and bad trails. * @sa G_InitEdict * @sa G_FreeEdict */ edict_t *G_Spawn (void) { edict_t *ent = G_EdictsGetNewEdict(); if (!ent) gi.Error("G_Spawn: no free edicts"); G_InitEdict(ent); return ent; }
/* * G_ClientBegin * * Called when a client has finished connecting, and is ready * to be placed into the game. This will happen every level load. */ void G_ClientBegin(g_edict_t *ent) { char welcome[256]; int player_num = ent - g_game.edicts - 1; ent->client = g_game.clients + player_num; G_InitEdict(ent); G_InitClientLocals(ent->client); VectorClear(ent->client->cmd_angles); ent->client->persistent.first_frame = g_level.frame_num; // force spectator if match or rounds if (g_level.match || g_level.rounds) ent->client->persistent.spectator = true; else if (g_level.teams || g_level.ctf) { if (g_auto_join->value) G_AddClientToTeam(ent, G_SmallestTeam()->name); else ent->client->persistent.spectator = true; } // spawn them in G_ClientRespawn(ent, true); if (g_level.intermission_time) { G_ClientToIntermission(ent); } else { memset(welcome, 0, sizeof(welcome)); snprintf(welcome, sizeof(welcome), "^2Welcome to ^7http://quake2world.net\n" "^2Gameplay is ^1%s\n", G_GameplayName(g_level.gameplay)); if (g_level.teams) strncat(welcome, "^2Teams are enabled\n", sizeof(welcome)); if (g_level.ctf) strncat(welcome, "^2CTF is enabled\n", sizeof(welcome)); if (g_voting->value) strncat(welcome, "^2Voting is allowed\n", sizeof(welcome)); gi.ClientCenterPrint(ent, "%s", welcome); } // make sure all view stuff is valid G_ClientEndFrame(ent); srand(time(NULL)); // set random seed }
/* ===================== ClientBeginDeathmatch A client has just connected to the server in deathmatch mode, so clear everything out before starting them. ===================== */ void ClientBeginDeathmatch (edict_t *ent) { #ifdef WITH_ACEBOT // ACEBOT_ADD static char current_map[55]; // ACEBOT_END #endif G_InitEdict (ent); #ifdef WITH_ACEBOT // ACEBOT_ADD ACEIT_PlayerAdded(ent); // ACEBOT_END #endif InitClientResp (ent->client); // locate ent at a spawn point PutClientInServer (ent); // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); #ifdef WITH_ACEBOT safe_bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); safe_centerprintf(ent,"\nQ2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2\n\nZaero Bots\n\n\n'sv addbot' to add a new bot.\n\n'sv removebot <name>' to remove bot.\n\n\nhttp://qudos.quakedev.com\n\n\nQ2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2 Q2\n\n"); // If the map changes on us, init and reload the nodes if(strcmp(level.mapname,current_map)) { ACEND_InitNodes(); ACEND_LoadNodes(); if (botauto_respawn->value) { ACESP_LoadBots(); } strcpy(current_map,level.mapname); } safe_bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); // ACEBOT_END #else gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); #endif // make sure all view stuff is valid ClientEndServerFrame (ent); }
void ClientBeginDeathmatch (edict_t *ent) { // STEVE added these 3 local variables FILE *motd_file; char motd[500]; char line[80]; G_InitEdict (ent); InitClientResp (ent->client); // locate ent at a spawn point PutClientInServer (ent); // send effect gi.WriteByte (svc_muzzleflash); gi.WriteShort (ent-g_edicts); gi.WriteByte (MZ_LOGIN); gi.multicast (ent->s.origin, MULTICAST_PVS); gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); // STEVE changed this bit : read the motd from a file if (motd_file = fopen("motd.txt", "r")) { // we successfully opened the file "motd.txt" if ( fgets(motd, 500, motd_file) ) { // we successfully read a line from "motd.txt" into motd // ... read the remaining lines now while ( fgets(line, 80, motd_file) ) { // add each new line to motd, to create a BIG message string. // we are using strcat: STRing conCATenation function here. strcat(motd, line); } // print our message. gi.centerprintf (ent, motd); } // be good now ! ... close the file fclose(motd_file); } // make sure all view stuff is valid ClientEndServerFrame (ent); }
/////////////////////////////////////////////////////////////////////// // Spawn the bot /////////////////////////////////////////////////////////////////////// void ACESP_SpawnBot (char *team, char *name, char *skin, char *userinfo) { edict_t *bot; bot = ACESP_FindFreeClient (); if (!bot) { safe_bprintf (PRINT_MEDIUM, "Server is full, increase Maxclients.\n"); return; } bot->yaw_speed = 100; // yaw speed bot->inuse = true; bot->is_bot = true; // To allow bots to respawn if(userinfo == NULL) ACESP_SetName(bot, name, skin, team); else ClientConnect (bot, userinfo); G_InitEdict (bot); InitClientResp (bot->client); // locate ent at a spawn point /*if(ctf->value) { if (team != NULL && strcmp(team,"red")==0) ACESP_PutClientInServer (bot,false, CTF_TEAM1); else ACESP_PutClientInServer (bot,false, CTF_TEAM2); } else*/ ACESP_PutClientInServer (bot,false,0); // make sure all view stuff is valid ClientEndServerFrame (bot); ACEIT_PlayerAdded (bot); // let the world know we added another ACEAI_PickLongRangeGoal(bot); // pick a new goal }
/////////////////////////////////////////////////////////////////////// // Spawn the bot /////////////////////////////////////////////////////////////////////// void ACESP_SpawnBot(char *team, char *name, char *skin, char *userinfo) { edict_t *bot = ACESP_FindFreeClient(); if (!bot) { safe_bprintf(PRINT_MEDIUM, "Server is full, increase Maxclients.\n"); return; } bot->yaw_speed = 100; // yaw speed bot->inuse = true; bot->is_bot = true; // To allow bots to respawn if (userinfo == NULL) ACESP_SetName(bot, name, skin, team); else ClientConnect (bot, userinfo); G_InitEdict (bot); InitClientResp (bot->client); // locate ent at a spawn point if (ctf->value) { // Knightmare- rewrote this int team1count = 0, team2count = 0, team3count = 0; int jointeam; const float r = random(); for (int i = 1; i <= maxclients->value; i++) { edict_t *player = &g_edicts[i]; if (!player->inuse || !player->client || player == bot) continue; switch (player->client->resp.ctf_team) { case CTF_TEAM1: team1count++; break; case CTF_TEAM2: team2count++; break; case CTF_TEAM3: team3count++; break; } } if (ttctf->value) { if (team != NULL && strcmp(team,"red")==0) jointeam = CTF_TEAM1; else if (team != NULL && strcmp(team,"blue")==0) jointeam = CTF_TEAM2; else if (team != NULL && strcmp(team,"green")==0) jointeam = CTF_TEAM3; // join either of the outnumbered teams else if (team1count == team2count && team1count < team3count) jointeam = (r < 0.5) ? CTF_TEAM1 : CTF_TEAM2; else if (team1count == team3count && team1count < team2count) jointeam = (r < 0.5) ? CTF_TEAM1 : CTF_TEAM3; else if (team2count == team3count && team2count < team1count) jointeam = (r < 0.5) ? CTF_TEAM2 : CTF_TEAM3; // join outnumbered team else if (team1count < team2count && team1count < team3count) jointeam = CTF_TEAM1; else if (team2count < team1count && team2count < team3count) jointeam = CTF_TEAM2; else if (team3count < team1count && team3count < team2count) jointeam = CTF_TEAM3; // pick random team else if (r < 0.33) jointeam = CTF_TEAM1; else if (r < 0.66) jointeam = CTF_TEAM2; else jointeam = CTF_TEAM3; } else { if (team != NULL && strcmp(team,"red")==0) jointeam = CTF_TEAM1; else if (team != NULL && strcmp(team,"blue")==0) jointeam = CTF_TEAM2; // join outnumbered team else if (team1count < team2count) jointeam = CTF_TEAM1; else if (team2count < team1count) jointeam = CTF_TEAM2; // pick random team else if (r < 0.5) jointeam = CTF_TEAM1; else jointeam = CTF_TEAM2; } ACESP_PutClientInServer (bot,false, jointeam); } else ACESP_PutClientInServer (bot,false,0); // make sure all view stuff is valid ClientEndServerFrame(bot); ACEIT_PlayerAdded(bot); // let the world know we added another ACEAI_PickLongRangeGoal(bot); // pick a new goal }
/* * SpawnEntities * * Creates a server's entity / program execution context by * parsing textual entity definitions out of an ent file. */ void G_InitLevel( char *mapname, char *entities, int entstrlen, unsigned int levelTime, unsigned int serverTime, unsigned int realTime ) { char *mapString = NULL; char name[MAX_CONFIGSTRING_CHARS]; int i; edict_t *ent; char *token; const gsitem_t *item; G_asGarbageCollect( true ); GT_asCallShutdown(); G_asCallMapExit(); G_asShutdownMapScript(); GT_asShutdownScript(); G_FreeCallvotes(); game.serverTime = serverTime; game.realtime = realTime; game.levelSpawnCount++; GClip_ClearWorld(); // clear areas links if( !entities ) G_Error( "G_SpawnLevel: NULL entities string\n" ); // make a copy of the raw entities string so it's not freed with the pool mapString = ( char * )G_Malloc( entstrlen + 1 ); memcpy( mapString, entities, entstrlen ); Q_strncpyz( name, mapname, sizeof( name ) ); // clear old data G_LevelInitPool( strlen( mapname ) + 1 + ( entstrlen + 1 ) * 2 + G_LEVELPOOL_BASE_SIZE ); G_StringPoolInit(); memset( &level, 0, sizeof( level_locals_t ) ); memset( &gs.gameState, 0, sizeof( gs.gameState ) ); level.spawnedTimeStamp = game.realtime; level.time = levelTime; level.gravity = g_gravity->value; // get the strings back Q_strncpyz( level.mapname, name, sizeof( level.mapname ) ); level.mapString = ( char * )G_LevelMalloc( entstrlen + 1 ); level.mapStrlen = entstrlen; memcpy( level.mapString, mapString, entstrlen ); G_Free( mapString ); mapString = NULL; // make a copy of the raw entities string for parsing level.map_parsed_ents = ( char * )G_LevelMalloc( entstrlen + 1 ); level.map_parsed_ents[0] = 0; if( !level.time ) memset( game.edicts, 0, game.maxentities * sizeof( game.edicts[0] ) ); else { G_FreeEdict( world ); for( i = gs.maxclients + 1; i < game.maxentities; i++ ) { if( game.edicts[i].r.inuse ) G_FreeEdict( game.edicts + i ); } } game.numentities = gs.maxclients + 1; // link client fields on player ents for( i = 0; i < gs.maxclients; i++ ) { game.edicts[i+1].s.number = i+1; game.edicts[i+1].r.client = &game.clients[i]; game.edicts[i+1].r.inuse = ( trap_GetClientState( i ) >= CS_CONNECTED ) ? true : false; memset( &game.clients[i].level, 0, sizeof( game.clients[0].level ) ); game.clients[i].level.timeStamp = level.time; } // initialize game subsystems trap_ConfigString( CS_MAPNAME, level.mapname ); trap_ConfigString( CS_SKYBOX, "" ); trap_ConfigString( CS_AUDIOTRACK, "" ); trap_ConfigString( CS_STATNUMS, va( "%i %i %i", STAT_SCORE, STAT_HEALTH, STAT_LAST_KILLER ) ); trap_ConfigString( CS_POWERUPEFFECTS, va( "%i %i %i %i", EF_QUAD, EF_SHELL, EF_CARRIER, EF_REGEN ) ); trap_ConfigString( CS_SCB_PLAYERTAB_LAYOUT, "" ); trap_ConfigString( CS_SCB_PLAYERTAB_TITLES, "" ); trap_ConfigString( CS_MATCHNAME, "" ); trap_ConfigString( CS_MATCHSCORE, "" ); // reset map messages for( i = 0; i < MAX_HELPMESSAGES; i++ ) { trap_ConfigString( CS_HELPMESSAGES + i, "" ); } G_InitGameCommands(); G_MapLocations_Init(); G_CallVotes_Init(); G_SpawnQueue_Init(); G_Teams_Init(); // load map script G_asLoadMapScript( level.mapname ); G_Gametype_Init(); // ch : this would be the location to "transfer ratings" G_PrecacheItems(); // set configstrings for items (gametype must be initialized) G_PrecacheMedia(); G_PrecacheGameCommands(); // adding commands after this point won't update them to the client AI_InitLevel(); // load navigation file of the current map // start spawning entities level.canSpawnEntities = true; G_InitBodyQueue(); // reserve some spots for dead player bodies entities = level.mapString; i = 0; ent = NULL; while( 1 ) { level.spawning_entity = NULL; // parse the opening brace token = COM_Parse( &entities ); if( !entities ) break; if( token[0] != '{' ) G_Error( "G_SpawnMapEntities: found %s when expecting {", token ); if( !ent ) { ent = world; G_InitEdict( world ); } else ent = G_Spawn(); ent->spawnString = entities; // keep track of string definition of this entity entities = ED_ParseEdict( entities, ent ); if( !ent->classname ) { i++; // racesow - introducing the freestyle map bug again in // order to make some freestyle maps work if( !level.gametype.freestyleMapFix ) G_FreeEdict( ent ); // !racesow G_FreeEdict( ent ); continue; } if( !G_CanSpawnEntity( ent ) ) { i++; G_FreeEdict( ent ); continue; } if( !G_CallSpawn( ent ) ) { i++; G_FreeEdict( ent ); continue; } // check whether an item is allowed to spawn if( ( item = ent->item ) ) { // not pickable items aren't spawnable if( item->flags & ITFLAG_PICKABLE ) { if( G_Gametype_CanSpawnItem( item ) ) { // override entity's classname with whatever item specifies ent->classname = item->classname; PrecacheItem( item ); continue; } } i++; G_FreeEdict( ent ); continue; } } G_FindTeams(); // is the parsing string sane? assert( (int)level.map_parsed_len < entstrlen ); level.map_parsed_ents[level.map_parsed_len] = 0; // make sure server got the edicts data trap_LocateEntities( game.edicts, sizeof( game.edicts[0] ), game.numentities, game.maxentities ); // items need brush model entities spawned before they are linked G_Items_FinishSpawningItems(); // // initialize game subsystems which require entities initialized // // call gametype specific GT_asCallSpawn(); // call map specific G_asCallMapInit(); AI_InitEntitiesData(); // always start in warmup match state and let the thinking code // revert it to wait state if empty ( so gametype based item masks are setup ) G_Match_LaunchState( MATCH_STATE_WARMUP ); G_asGarbageCollect( true ); // racesow RS_Init(); }
/* * CopyToBodyQue */ static edict_t *CopyToBodyQue( edict_t *ent, edict_t *attacker, int damage ) { edict_t *body; int contents; if( GS_RaceGametype() ) return NULL; contents = G_PointContents( ent->s.origin ); if( contents & CONTENTS_NODROP ) return NULL; G_Client_UnlinkBodies( ent ); // grab a body que and cycle to the next one body = &game.edicts[gs.maxclients + level.body_que + 1]; level.body_que = ( level.body_que + 1 ) % BODY_QUEUE_SIZE; // send an effect on the removed body if( body->s.modelindex && body->s.type == ET_CORPSE ) ThrowSmallPileOfGibs( body, 10 ); GClip_UnlinkEntity( body ); memset( body, 0, sizeof( edict_t ) ); //clean up garbage //init body edict G_InitEdict( body ); body->classname = "body"; body->health = ent->health; body->mass = ent->mass; body->r.owner = ent->r.owner; body->s.type = ent->s.type; body->s.team = ent->s.team; body->s.effects = 0; body->r.svflags = SVF_CORPSE; body->r.svflags &= ~SVF_NOCLIENT; body->activator = ent; if( g_deadbody_followkiller->integer ) body->enemy = attacker; //use flat yaw body->s.angles[PITCH] = 0; body->s.angles[ROLL] = 0; body->s.angles[YAW] = ent->s.angles[YAW]; body->s.modelindex2 = 0; // <- is bodyOwner when in ET_CORPSE, but not in ET_GENERIC or ET_PLAYER body->s.weapon = 0; //copy player position and box size VectorCopy( ent->s.old_origin, body->s.old_origin ); VectorCopy( ent->s.origin, body->s.origin ); VectorCopy( ent->s.origin, body->olds.origin ); VectorCopy( ent->r.mins, body->r.mins ); VectorCopy( ent->r.maxs, body->r.maxs ); VectorCopy( ent->r.absmin, body->r.absmin ); VectorCopy( ent->r.absmax, body->r.absmax ); VectorCopy( ent->r.size, body->r.size ); VectorCopy( ent->velocity, body->velocity ); body->r.maxs[2] = body->r.mins[2] + 8; body->r.solid = SOLID_YES; body->takedamage = DAMAGE_YES; body->r.clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->movetype = MOVETYPE_TOSS; body->die = body_die; body->think = body_think; // body self destruction countdown if( ent->health < GIB_HEALTH || meansOfDeath == MOD_ELECTROBOLT_S /* electrobolt always gibs */ ) { ThrowSmallPileOfGibs( body, damage ); // reset gib impulse VectorClear( body->velocity ); ThrowClientHead( body, damage ); // sets ET_GIB body->s.frame = 0; body->nextThink = level.time + 3000 + random() * 3000; body->deadflag = DEAD_DEAD; } else if( ent->s.type == ET_PLAYER ) { // copy the model body->s.type = ET_CORPSE; body->s.modelindex = ent->s.modelindex; body->s.bodyOwner = ent->s.number; // bodyOwner is the same as modelindex2 body->s.skinnum = ent->s.skinnum; body->s.teleported = true; // launch the death animation on the body { static int i; i = ( i+1 )%3; G_AddEvent( body, EV_DIE, i, true ); switch( i ) { default: case 0: body->s.frame = ( ( BOTH_DEAD1&0x3F )|( BOTH_DEAD1&0x3F )<<6|( 0 &0xF )<<12 ); break; case 1: body->s.frame = ( ( BOTH_DEAD2&0x3F )|( BOTH_DEAD2&0x3F )<<6|( 0 &0xF )<<12 ); break; case 2: body->s.frame = ( ( BOTH_DEAD3&0x3F )|( BOTH_DEAD3&0x3F )<<6|( 0 &0xF )<<12 ); break; } } body->think = body_ready; body->takedamage = DAMAGE_NO; body->r.solid = SOLID_NOT; body->nextThink = level.time + 500; // make damageable in 0.5 seconds } else // wasn't a player, just copy it's model { VectorClear( body->velocity ); body->s.modelindex = ent->s.modelindex; body->s.frame = ent->s.frame; body->nextThink = level.time + 5000 + random()*10000; } GClip_LinkEntity( body ); return body; }
/* * ClientConnect * 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. */ bool ClientConnect( edict_t *ent, char *userinfo, bool fakeClient, bool tvClient ) { char *value; assert( ent ); assert( userinfo && Info_Validate( userinfo ) ); assert( Info_ValueForKey( userinfo, "ip" ) && Info_ValueForKey( userinfo, "socket" ) ); // verify that server gave us valid data if( !Info_Validate( userinfo ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Invalid userinfo" ); return false; } if( !Info_ValueForKey( userinfo, "ip" ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Error: Server didn't provide client IP" ); return false; } if( !Info_ValueForKey( userinfo, "ip" ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "Error: Server didn't provide client socket" ); 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, "rejtype", va( "%i", DROP_TYPE_GENERAL ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); Info_SetValueForKey( userinfo, "rejmsg", "You're banned from this server" ); return false; } // check for a password value = Info_ValueForKey( userinfo, "password" ); if( !fakeClient && ( *password->string && ( !value || strcmp( password->string, value ) ) ) ) { Info_SetValueForKey( userinfo, "rejtype", va( "%i", DROP_TYPE_PASSWORD ) ); Info_SetValueForKey( userinfo, "rejflag", va( "%i", 0 ) ); if( value && value[0] ) { Info_SetValueForKey( userinfo, "rejmsg", "Incorrect password" ); } else { Info_SetValueForKey( userinfo, "rejmsg", "Password required" ); } return false; } // they can connect G_InitEdict( ent ); ent->s.modelindex = 0; ent->r.solid = SOLID_NOT; ent->r.client = game.clients + PLAYERNUM( ent ); ent->r.svflags = ( SVF_NOCLIENT | ( fakeClient ? SVF_FAKECLIENT : 0 ) ); memset( ent->r.client, 0, sizeof( gclient_t ) ); ent->r.client->ps.playerNum = PLAYERNUM( ent ); ent->r.client->connecting = true; ent->r.client->isTV = tvClient == true; ent->r.client->team = TEAM_SPECTATOR; G_Client_UpdateActivity( ent->r.client ); // activity detected ClientUserinfoChanged( ent, userinfo ); if( !fakeClient ) { char message[MAX_STRING_CHARS]; Q_snprintfz( message, sizeof( message ), "%s%s connected", ent->r.client->netname, S_COLOR_WHITE ); G_PrintMsg( NULL, "%s\n", message ); G_Printf( "%s%s connected from %s\n", ent->r.client->netname, S_COLOR_WHITE, ent->r.client->ip ); } // let the gametype scripts know this client just connected G_Gametype_ScoreEvent( ent->r.client, "connect", NULL ); G_CallVotes_ResetClient( PLAYERNUM( ent ) ); return true; }
void ParseEntityString (qboolean respawn) { edict_t *ent; int inhibit; const char *com_token; const char *entities; int i; entities = level.entity_string; ent = NULL; inhibit = 0; i = 0; //mark all the spawned_entities as inuse so G_Spawn doesn't try to pick them during //trigger spawning etc for (i = 0; i < num_spawned_entities; i++) spawned_entities[i]->inuse = true; i = 0; // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (respawn) { ent = spawned_entities[i]; if (ent != world) { //double check we aren't overwriting stuff that we shouldn't be if (ent->enttype == ENT_DOOR_TRIGGER || ent->enttype == ENT_PLAT_TRIGGER) TDM_Error ("ParseEntityString: Trying to reuse an already spawned ent!"); G_FreeEdict (ent); G_InitEdict (ent); } i++; } else { if (!ent) ent = g_edicts; else ent = G_Spawn (); } entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { G_FreeEdict (ent); inhibit++; //have to keep num_spawned in sync for respawn if (!respawn) spawned_entities[num_spawned_entities++] = ent; continue; } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_COOP|SPAWNFLAG_NOT_DEATHMATCH); } else if (respawn) continue; ED_CallSpawn (ent); if (!respawn) { if (num_spawned_entities == MAX_EDICTS * 4) TDM_Error ("Exceeded MAX_EDICTS * 4 entities during entity string parse"); spawned_entities[num_spawned_entities++] = ent; } else { //an elevator or something respawned on top of a player? don't do it for match start, since //players will be respawning anyway. if (tdm_match_status < MM_PLAYING && (ent->solid == SOLID_BBOX || ent->solid == SOLID_BSP)) { edict_t *touch[MAX_EDICTS]; int count; int j; //r1: fix 00000196, can't killbox during map spawn due to risk of killing half-spawned //map entities. do a pseudo-killbox that only whacks players instead. count = gi.BoxEdicts (ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_SOLID); for (j = 0; j < count; j++) { if (touch[j] == ent) continue; //no point killing anything that won't clip (eg corpses) if (touch[j]->solid == SOLID_NOT || touch[j]->enttype == ENT_BODYQUE) continue; if (!touch[j]->client) continue; if (touch[j]->inuse) T_Damage (touch[j], ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); } } } } //gi.dprintf ("%i entities inhibited\n", inhibit); #ifdef DEBUG i = 1; ent = EDICT_NUM(i); while (i < globals.num_edicts) { if (ent->inuse != 0 || ent->inuse != 1) Com_DPrintf("Invalid entity %d\n", i); i++, ent++; } #endif G_FindTeams (); }