/* ================= G_NewEntity Either finds a free entity, or allocates a new one. The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will never be used by anything else. 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. ================= */ gentity_t *G_NewEntity() { int i, force; gentity_t *newEntity; newEntity = nullptr; // shut up warning i = 0; // shut up warning for ( force = 0; force < 2; force++ ) { // if we go through all entities first and can't find a free one, // then try again a second time, this time ignoring times newEntity = &g_entities[ MAX_CLIENTS ]; for ( i = MAX_CLIENTS; i < level.num_entities; i++, newEntity++ ) { if ( newEntity->inuse ) { continue; } // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if ( !force && newEntity->freetime > level.startTime + 2000 && level.time - newEntity->freetime < 1000 ) { continue; } // reuse this slot G_InitGentity( newEntity ); return newEntity; } if ( i != MAX_GENTITIES ) { break; } } if ( i == ENTITYNUM_MAX_NORMAL ) { for ( i = 0; i < MAX_GENTITIES; i++ ) { G_Printf( "%4i: %s\n", i, g_entities[ i ].classname ); } G_Error( "G_Spawn: no free entities" ); } // open up a new slot level.num_entities++; // let the server system know that there are more entities trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ), &level.clients[ 0 ].ps, sizeof( level.clients[ 0 ] ) ); G_InitGentity( newEntity ); return newEntity; }
/* ================= G_Spawn Either finds a free entity, or allocates a new one. The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will never be used by anything else. 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. ================= */ gentity_t *G_Spawn(void) { int i, force; gentity_t *e; e = NULL; // shut up warning i = 0; // shut up warning for (force = 0; force < 2; force++) { // if we go through all entities and can't find one to free, // override the normal minimum times before use e = &g_entities[MAX_CLIENTS]; for (i = MAX_CLIENTS; i < level.num_entities; i++, e++) { if (e->inuse) { continue; } // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy if (!force && e->freetime > level.startTime + 2000 && level.time - e->freetime < 1000) { continue; } // reuse this slot G_InitGentity(e); return e; } if (i != ENTITYNUM_MAX_NORMAL) { break; } } if (i == ENTITYNUM_MAX_NORMAL) { for (i = 0; i < MAX_GENTITIES; i++) { G_Printf("%4i: %s\n", i, g_entities[i].classname); } G_Error("G_Spawn: no free entities"); } // open up a new slot level.num_entities++; // let the server system know that there are more entities trap_LocateGameData(level.gentities, level.num_entities, sizeof(gentity_t), &level.clients[0].ps, sizeof(level.clients[0])); G_InitGentity(e); return e; }
/* ============ G_InitGame ============ */ void G_InitGame( int levelTime, int randomSeed, int restart ) { int i; G_Printf ("------- Game Initialization -------\n"); G_Printf ("gamename: %s\n", GAMEVERSION); G_Printf ("gamedate: %s\n", __DATE__); srand( randomSeed ); G_RegisterCvars(); G_ProcessIPBans(); G_InitMemory(); // set some level globals memset( &level, 0, sizeof( level ) ); level.time = levelTime; level.startTime = levelTime; level.snd_fry = G_SoundIndex("sound/player/fry.wav"); // FIXME standing in lava / slime if ( g_gametype.integer != GT_SINGLE_PLAYER && g_logfile.string[0] ) { if ( g_logfileSync.integer ) { trap_FS_FOpenFile( g_logfile.string, &level.logFile, FS_APPEND_SYNC ); } else { trap_FS_FOpenFile( g_logfile.string, &level.logFile, FS_APPEND ); } if ( !level.logFile ) { G_Printf( "WARNING: Couldn't open logfile: %s\n", g_logfile.string ); } else { char serverinfo[MAX_INFO_STRING]; trap_GetServerinfo( serverinfo, sizeof( serverinfo ) ); G_LogPrintf("------------------------------------------------------------\n" ); G_LogPrintf("InitGame: %s\n", serverinfo ); } } else { G_Printf( "Not logging to disk.\n" ); } G_InitWorldSession(); // initialize all entities for this game memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) ); level.gentities = g_entities; // initialize all clients for this game level.maxclients = g_maxclients.integer; memset( g_clients, 0, MAX_CLIENTS * sizeof(g_clients[0]) ); level.clients = g_clients; // set client fields on player ents for ( i=0 ; i<level.maxclients ; i++ ) { g_entities[i].client = level.clients + i; } // always leave room for the max number of clients, // even if they aren't all used, so numbers inside that // range are NEVER anything but clients level.num_entities = MAX_CLIENTS; for ( i=0 ; i<MAX_CLIENTS ; i++ ) { g_entities[i].classname = "clientslot"; } // let the server system know where the entites are trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ), &level.clients[0].ps, sizeof( level.clients[0] ) ); // reserve some spots for dead player bodies InitBodyQue(); ClearRegisteredItems(); // parse the key/value pairs and spawn gentities G_SpawnEntitiesFromString(); // general initialization G_FindTeams(); // make sure we have flags for CTF, etc if( g_gametype.integer >= GT_TEAM ) { G_CheckTeamItems(); } SaveRegisteredItems(); G_Printf ("-----------------------------------\n"); if( g_gametype.integer == GT_SINGLE_PLAYER || trap_Cvar_VariableIntegerValue( "com_buildScript" ) ) { G_ModelIndex( SP_PODIUM_MODEL ); } if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { BotAISetup( restart ); BotAILoadMap( restart ); G_InitBots( restart ); } G_RemapTeamShaders(); }