// Nico, delayed map change check function (thread) void *G_delayed_map_change_watcher(void *arg) { int count = 0; int limit = 10; // Nico, in seconds int timeLeft = 0; // Nico, silent GCC (void)arg; while (!level.delayedMapChange.disabledWatcher) { if (level.time && level.delayedMapChange.timeChange) { // There is a delayed change if (level.time >= level.delayedMapChange.timeChange) { // Nico, do we have to wait for some threads to finish their work? while (activeThreadsCounter > 0 && count < limit) { threadingAllowed = qfalse; G_DPrintf("%s: waiting for %d thread%s before changing map\n", GAME_VERSION, activeThreadsCounter, activeThreadsCounter > 1 ? "s" : ""); my_sleep(1000); // Nico, sleep for 1sec count++; } if (count >= limit) { G_Error("%s: warning, threads waiting timeout reached (threads: %d)", GAME_VERSION, activeThreadsCounter); } G_DPrintf("%s: changing map now!\n", GAME_VERSION); trap_SendConsoleCommand(EXEC_APPEND, va("map %s\n", level.delayedMapChange.passedVote)); break; } else { timeLeft = (level.delayedMapChange.timeChange - level.time) / 1000; // Print incomming map change every 5 secs if (timeLeft % 5 == 0) { G_DPrintf("%s: map change to %s in %d sec%s\n", GAME_VERSION, level.delayedMapChange.passedVote, timeLeft, timeLeft > 1 ? "s" : ""); } // Announce incomming map to players switch (timeLeft) { case 600: AP(va("cpm \"%s^w: map will be changed to ^Z%s ^win ^D10 ^wmins\n\"", GAME_VERSION_COLORED, level.delayedMapChange.passedVote)); break; case 300: AP(va("cpm \"%s^w: map will be changed to ^Z%s ^win ^D5 ^wmins\n\"", GAME_VERSION_COLORED, level.delayedMapChange.passedVote)); break; case 120: AP(va("cpm \"%s^w: map will be changed to ^Z%s ^win ^D2 ^wmins\n\"", GAME_VERSION_COLORED, level.delayedMapChange.passedVote)); break; case 60: AP(va("cpm \"%s^w: map will be changed to ^Z%s ^win ^D1 ^wmin\n\"", GAME_VERSION_COLORED, level.delayedMapChange.passedVote)); break; case 30: AP(va("cpm \"%s^w: map will be changed to ^Z%s ^win ^D30 ^wsecs\n\"", GAME_VERSION_COLORED, level.delayedMapChange.passedVote)); break; } } } my_sleep(999); } return NULL; }
/** * Record handler */ static void *recordHandler(void *data) { int code; struct query_s *queryStruct = (struct query_s *)data; gentity_t *ent = queryStruct->ent; int timerunNum; code = API_query(queryStruct->cmd, queryStruct->result, queryStruct->query, sizeof (queryStruct->query)); timerunNum = ent->client->sess.currentTimerunNum; switch (code) { case 1001: // PB if (ent->client->sess.timerunCheckpointWereLoaded[timerunNum]) { memcpy(ent->client->sess.timerunBestCheckpointTimes[timerunNum], ent->client->sess.timerunCheckpointTimes, sizeof (ent->client->sess.timerunCheckpointTimes)); } AP(va("print \"%s^w: %s\n\"", GAME_VERSION_COLORED, queryStruct->result)); // Nico, keep this demo if autodemo is enabled if (ent->client->pers.autoDemo) { saveDemo(ent); } break; case 1002: // SB case 1003: // SB but player was already rec holder case 1004: // SB was tied if (ent->client->sess.timerunCheckpointWereLoaded[timerunNum]) { memcpy(ent->client->sess.timerunBestCheckpointTimes[timerunNum], ent->client->sess.timerunCheckpointTimes, sizeof (ent->client->sess.timerunCheckpointTimes)); } AP(va("bp \"^w%s\n\"", queryStruct->result)); // Nico, keep this demo if autodemo is enabled if (ent->client->pers.autoDemo) { saveDemo(ent); } break; case 1005: // Slow time CP(va("print \"%s^w: %s\n\"", GAME_VERSION_COLORED, queryStruct->result)); // Nico, check player keepDemo setting to see if we keep this one or not if (ent->client->pers.autoDemo && ent->client->pers.keepAllDemos) { saveDemo(ent); } break; default: // Error CP(va("print \"%s^w: error: %s\n\"", GAME_VERSION_COLORED, queryStruct->result)); break; } free(queryStruct->result); free(queryStruct); // Decrease global active thread counter activeThreadsCounter--; G_DPrintf("%s: decreasing threads counter to %d\n", GAME_VERSION, activeThreadsCounter); return NULL; }
/*QUAKED trigger_multiple (.5 .5 .5) ? AXIS_ONLY ALLIED_ONLY NOBOT BOTONLY SOLDIERONLY LTONLY MEDICONLY ENGINEERONLY COVERTOPSONLY "wait" : Seconds between triggerings, 0.5 default, -1 = one time only. "random" wait variance, default is 0 Variable sized repeatable trigger. Must be targeted at one or more entities. so, the basic time between firing is a random time between (wait - random) and (wait + random) */ void SP_trigger_multiple(gentity_t *ent) { gentity_t *target = NULL; G_SpawnFloat("wait", "0.5", &ent->wait); ent->touch = Touch_Multi; ent->use = Use_Multi; ent->s.eType = ET_TRIGGER_MULTIPLE; InitTrigger(ent); // Nico, override wait -1 or wait 9999 on trigger_multiple where target is start timer // Note, this test is in case the start/stop timer or checkpoint entity was defined before the trigger multiple if (g_forceTimerReset.integer) { target = G_FindByTargetname(NULL, ent->target); if (target && ent->wait != 0.5 && (!Q_stricmp(target->classname, "target_startTimer") || !Q_stricmp(target->classname, "target_stopTimer") || !Q_stricmp(target->classname, "target_checkpoint"))) { G_DPrintf("%s: SP_trigger_multiple linked to %s, wait found = %f, overrided to 0.5\n", GAME_VERSION, target->classname, ent->wait); ent->wait = 0.5; } } #ifdef VISIBLE_TRIGGERS ent->r.svFlags &= ~SVF_NOCLIENT; #endif trap_LinkEntity(ent); }
void SP_target_checkpoint(gentity_t *ent) { char *t = NULL; int timerunNum = 0; // Nico, used to look for parent gentity_t *parent = NULL; // Nico, override wait -1 or wait 9999 on timer check entities if (g_forceTimerReset.integer) { parent = G_FindByTarget(NULL, ent->targetname); if (parent && parent->wait != 0.5 && !Q_stricmp(parent->classname, "trigger_multiple")) { G_DPrintf("%s: SP_target_checkpoint, wait found = %f, overrided to 0.5\n", GAME_VERSION, parent->wait); G_SpawnFloat("wait", "0.5", &parent->wait); } } G_SpawnString("name", "default", &t); ent->timerunName = G_NewString(t); // create a timerun with this name if it doesn't exit yet timerunNum = GetTimerunNum(ent->timerunName); if (level.numCheckpoints[timerunNum] >= MAX_TIMERUN_CHECKPOINTS) { G_Error("Exceeded maximum number of 'target_checkpoint' entities in '%s' timerun (max %i)\n", ent->timerunName, MAX_TIMERUN_CHECKPOINTS); return; } ent->count = level.numCheckpoints[timerunNum]; ent->use = target_checkpoint_use; level.numCheckpoints[timerunNum]++; level.isTimerun = qtrue; }
/* ============== G_Script_ParseSpawnbot Parses "Spawnbot" command, precaching a custom character if specified ============== */ void G_Script_ParseSpawnbot(char **ppScript, char params[], int paramsize) { qboolean parsingCharacter = qfalse; char *token; token = COM_ParseExt(ppScript, qfalse); while (token[0]) { // if we are currently parsing a spawnbot command, check the parms for // a custom character, which we will need to precache on the client // did we just see a '/character' parm? if (parsingCharacter) { parsingCharacter = qfalse; G_CharacterIndex(token); if (!BG_FindCharacter(token)) { bg_character_t *character = BG_FindFreeCharacter(token); Q_strncpyz(character->characterFile, token, sizeof(character->characterFile)); if (!G_RegisterCharacter(token, character)) { G_Error("ERROR: G_Script_ParseSpawnbot: failed to load character file '%s'\n", token); } } #ifdef DEBUG G_DPrintf("precached character %s\n", token); #endif } else if (!Q_stricmp(token, "/character")) { parsingCharacter = qtrue; } if (strlen(params)) // add a space between each param { Q_strcat(params, paramsize, " "); } if (strrchr(token, ' ')) // need to wrap this param in quotes since it has more than one word { Q_strcat(params, paramsize, "\""); } Q_strcat(params, paramsize, token); if (strrchr(token, ' ')) // need to wrap this param in quotes since it has more than one word { Q_strcat(params, paramsize, "\""); } token = COM_ParseExt(ppScript, qfalse); } }
/* =============== G_LoadBots =============== */ static void G_LoadBots( void ) { vmCvar_t botsFile; int numdirs; char filename[128]; char dirlist[1024]; char* dirptr; int i; int dirlen; if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { return; } g_numBots = 0; trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM ); if( *botsFile.string ) { G_LoadBotsFromFile(botsFile.string); } else { G_LoadBotsFromFile("scripts/bots.txt"); } // get all bots from .bot files numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); strcpy(filename, "scripts/"); strcat(filename, dirptr); G_LoadBotsFromFile(filename); } G_DPrintf("%i bots parsed\n", g_numBots); }
/* =============== G_LoadArenas =============== */ static void G_LoadArenas( void ) { int numdirs; vmCvar_t arenasFile; char filename[128]; char dirlist[1024]; char* dirptr; int i, n; int dirlen; g_numArenas = 0; trap_Cvar_Register( &arenasFile, "g_arenasFile", "", CVAR_INIT|CVAR_ROM ); if( *arenasFile.string ) { G_LoadArenasFromFile(arenasFile.string); } else { G_LoadArenasFromFile("scripts/arenas.txt"); } // 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/"); strcat(filename, dirptr); G_LoadArenasFromFile(filename); } G_DPrintf("%i arenas parsed\n", g_numArenas); for( n = 0; n < g_numArenas; n++ ) { Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) ); } }
void SP_target_stoptimer(gentity_t *ent) { char *t = NULL; // Nico, used to look for parent gentity_t *parent = NULL; // Nico, override wait -1 or wait 9999 on stop timer entities if (g_forceTimerReset.integer) { parent = G_FindByTarget(NULL, ent->targetname); if (parent && parent->wait != 0.5 && !Q_stricmp(parent->classname, "trigger_multiple")) { G_DPrintf("%s: SP_target_stoptimer, wait found = %f, overrided to 0.5\n", GAME_VERSION, parent->wait); G_SpawnFloat("wait", "0.5", &parent->wait); } } G_SpawnString("name", "default", &t); ent->timerunName = G_NewString(t); // create a timerun with this name if it doesn't exit yet GetTimerunNum(ent->timerunName); G_SpawnInt("mincheckpoints", "0", &ent->count); ent->use = target_stoptimer_use; level.isTimerun = qtrue; }
/* ============== alarmbox_updateparts ============== */ void alarmbox_updateparts(gentity_t *ent, qboolean matestoo) { gentity_t *t, *mate; qboolean alarming = (ent->s.frame == 1); // update teammates if (matestoo) { for (mate = ent->teammaster; mate; mate = mate->teamchain) { if (mate == ent) { continue; } if (!(mate->active)) { // don't update dead alarm boxes, they stay dead continue; } if (!(ent->active)) { // destroyed, so just turn teammates off mate->s.frame = 0; } else { mate->s.frame = ent->s.frame; } alarmbox_updateparts(mate, qfalse); } } // update lights if (!ent->target) { return; } t = NULL; while ((t = G_FindByTargetname(t, ent->target)) != NULL) { if (t == ent) { G_DPrintf("WARNING: Entity used itself.\n"); } else { // give the dlight the sound if (!Q_stricmp(t->classname, "dlight")) { t->soundLoop = ent->soundLoop; if (alarming) { if (!(t->r.linked)) { G_UseEntity(t, ent, 0); } } else { if (t->r.linked) { G_UseEntity(t, ent, 0); } } } // alarmbox can tell script_trigger about activation // (but don't trigger if dying, only activation) else if (!Q_stricmp(t->classname, "target_script_trigger") && ent->active) { // not dead G_UseEntity(t, ent, 0); } } } }
/* =============== G_CallSpawn Finds the spawn function for the entity and calls it, returning qfalse if not found =============== */ qboolean G_CallSpawn(gentity_t *ent) { spawn_t *s; gitem_t *item; if (!ent->classname) { G_DPrintf("G_CallSpawn: NULL classname\n"); return qfalse; } // check item spawn functions for (item = bg_itemlist + 1 ; item->classname ; ++item) { if (!strcmp(item->classname, ent->classname)) { // found it G_SpawnItem(ent, item); G_Script_ScriptParse(ent); G_Script_ScriptEvent(ent, "spawn", ""); return qtrue; } } // check normal spawn functions for (s = spawns ; s->name ; ++s) { if (!strcmp(s->name, ent->classname)) { // found it s->spawn(ent); // RF, entity scripting if (ent->scriptName) { G_Script_ScriptParse(ent); G_Script_ScriptEvent(ent, "spawn", ""); } return qtrue; } } G_DPrintf("%s doesn't have a spawn function\n", ent->classname); return qfalse; }
void Team_ReturnFlagSound(gentity_t *ent, int team) { // play powerup spawn sound to all clients gentity_t *pm; if (ent == NULL) { G_DPrintf("Warning: NULL passed to Team_ReturnFlagSound\n"); return; } pm = G_PopupMessage(PM_OBJECTIVE); pm->s.effect3Time = G_StringIndex(ent->message); pm->s.effect2Time = team; pm->s.density = 1; // 1 = returned }
/** * Get checkpoints handler */ static void *checkpointsHandler(void *data) { int code; struct query_s *queryStruct = (struct query_s *)data; gentity_t *ent = queryStruct->ent; code = API_query(queryStruct->cmd, queryStruct->result, queryStruct->query, sizeof (queryStruct->query)); if (code >= 1000) { int timerunNum = code - 1000; if (queryStruct->result && checkAPIResult(queryStruct->result) && timerunNum >= 0 && timerunNum < MAX_TIMERUNS) { char *pch; int i = 0; // No error, no timeout // Reset client checkpoints memset(ent->client->sess.timerunBestCheckpointTimes[timerunNum], 0, sizeof (ent->client->sess.timerunBestCheckpointTimes[timerunNum])); ent->client->sess.timerunCheckpointsPassed = 0; // Set new checkpoints pch = strtok(queryStruct->result, "O"); while (pch != NULL && i < MAX_TIMERUN_CHECKPOINTS) { ent->client->sess.timerunBestCheckpointTimes[timerunNum][i] = atoi(pch); pch = strtok(NULL, "O"); i++; } // Mark CP were loaded for this run ent->client->sess.timerunCheckpointWereLoaded[timerunNum] = 1; CP(va("print \"%s^w: checkpoints loaded for run #%d!\n\"", GAME_VERSION_COLORED, timerunNum)); } else { CP(va("print \"%s^w: error while loading checkpoints\n\"", GAME_VERSION_COLORED)); } } else if (code == 0) { clientBigDataPrint(ent, queryStruct->result); } else { CP(va("print \"%s^w: error while loading checkpoints\n\"", GAME_VERSION_COLORED)); } free(queryStruct->result); free(queryStruct); // Decrease global active thread counter activeThreadsCounter--; G_DPrintf("%s: decreasing threads counter to %d\n", GAME_VERSION, activeThreadsCounter); return NULL; }
void G_unloadAPI() { if (api_module == NULL) { G_DPrintf("%s: API module is not loaded (G_unloadAPI).\n", GAME_VERSION); } else { API_clean(); #if defined _WIN32 FreeLibrary(api_module); #else dlclose(api_module); #endif G_Printf("%s: API module unloaded!\n", GAME_VERSION); } }
/* =============== SaveRegisteredItems Write the needed items to a config string so the client will know which ones to precache =============== */ void SaveRegisteredItems( void ) { char string[MAX_ITEMS+1]; int i; int count; count = 0; for ( i = 0 ; i < BG_NumItems() ; i++ ) { if ( itemRegistered[i] ) { count++; string[i] = '1'; } else { string[i] = '0'; } } string[ BG_NumItems() ] = 0; G_DPrintf( "%i items registered\n", count ); trap_SetConfigstring(CS_ITEMS, string); }
/** * Random map handler */ static void *randommapHandler(void *data) { int code; struct query_s *queryStruct = (struct query_s *)data; gentity_t *ent = queryStruct->ent; // Nico, note: this is NULL is randomMap was asked by server (timelimit) fileHandle_t f; code = API_query(queryStruct->cmd, queryStruct->result, queryStruct->query, sizeof (queryStruct->query)); if (code == 0 && queryStruct->result && checkAPIResult(queryStruct->result)) { char mapfile[MAX_QPATH] = { 0 }; Com_sprintf(mapfile, sizeof (mapfile), "maps/%s.bsp", queryStruct->result); trap_FS_FOpenFile(mapfile, &f, FS_READ); trap_FS_FCloseFile(f); if (!f) { AP(va("print \"^1Error:^7 the map ^3%s^7 is not on the server.\n\"", queryStruct->result)); } else { // Check from where the map change come // randommap vote or timelimit? if (g_timelimit.integer > 0) { G_delay_map_change(queryStruct->result, g_timelimit.integer); } else { // Nico, delay the map change G_delay_map_change(queryStruct->result, 0); } } } else { CP(va("print \"%s^w: error while getting a random map!\n\"", GAME_VERSION_COLORED)); } free(queryStruct->result); free(queryStruct); // Decrease global active thread counter activeThreadsCounter--; G_DPrintf("%s: decreasing threads counter to %d\n", GAME_VERSION, activeThreadsCounter); return NULL; }
void InitTrigger(gentity_t *self) { if (!VectorCompare(self->s.angles, vec3_origin)) { G_SetMovedir(self->s.angles, self->movedir); } if (self->model) { trap_SetBrushModel(self, self->model); } else { // empty models for ETPro mapscripting G_DPrintf("^6InitTrigger: trap_SetBrushModel(NULL) skipped for scriptName %s\n", self->scriptName); } self->r.contents = CONTENTS_TRIGGER; // replaces the -1 from trap_SetBrushModel self->r.svFlags = SVF_NOCLIENT; }
/** * Check API handler */ static void *checkAPIHandler(void *data) { int code; struct query_s *queryStruct = (struct query_s *)data; code = API_query(queryStruct->cmd, queryStruct->result, queryStruct->query, sizeof (queryStruct->query)); if (code == 0) { G_Printf("%s: %s\n", GAME_VERSION, queryStruct->result); } else { G_Printf("%s: failed to check API (code: %d, result: %s)\n", GAME_VERSION, code, queryStruct->result); } free(queryStruct->result); free(queryStruct); // Decrease global active thread counter activeThreadsCounter--; G_DPrintf("%s: decreasing threads counter to %d\n", GAME_VERSION, activeThreadsCounter); return NULL; }
/** * Map rank command handler */ static void *mapRankHandler(void *data) { int code; struct query_s *queryStruct = (struct query_s *)data; gentity_t *ent = queryStruct->ent; code = API_query(queryStruct->cmd, queryStruct->result, queryStruct->query, sizeof (queryStruct->query)); if (code == 0) { clientBigDataPrint(ent, queryStruct->result); } else { CP(va("print \"%s^w: error while requesting rank\n\"", GAME_VERSION_COLORED)); } free(queryStruct->result); free(queryStruct); // Decrease global active thread counter activeThreadsCounter--; G_DPrintf("%s: decreasing threads counter to %d\n", GAME_VERSION, activeThreadsCounter); return NULL; }
/** * Login handler */ static void *loginHandler(void *data) { int code; int len = 0; struct query_s *queryStruct = (struct query_s *)data; gentity_t *ent = queryStruct->ent; code = API_query(queryStruct->cmd, queryStruct->result, queryStruct->query, sizeof (queryStruct->query)); len = strlen(queryStruct->result); if (code == 0) { if (len > 0 && ent && ent->client) { ent->client->sess.logged = qtrue; CP(va("print \"%s^w: you are now logged in!\n\"", GAME_VERSION_COLORED)); ClientUserinfoChanged(ent->client->ps.clientNum); // Notify client that he is now logged in trap_SendServerCommand(ent - g_entities, "client_login"); // Start recording a new temp demo. trap_SendServerCommand(ent - g_entities, "tempDemoStart"); } else { CP(va("print \"%s^w: login failed!\n\"", GAME_VERSION_COLORED)); } } else { CP(va("print \"%s^w: login failed!\n\"", GAME_VERSION_COLORED)); } free(queryStruct->result); free(queryStruct); // Decrease global active thread counter activeThreadsCounter--; G_DPrintf("%s: decreasing threads counter to %d\n", GAME_VERSION, activeThreadsCounter); return NULL; }
/* ============== ClientEndFrame Called at the end of each server frame for each connected client A fast client will have multiple ClientThink for each ClientEndFrame, while a slow client may have multiple ClientEndFrame between ClientThink. ============== */ void ClientEndFrame( gentity_t *ent ) { int i; if ( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) || ( ent->client->ps.pm_flags & PMF_LIMBO ) ) { // JPW NERVE SpectatorClientEndFrame( ent ); return; } if ( !ent->aiCharacter ) { // turn off any expired powerups for ( i = 0 ; i < MAX_POWERUPS ; i++ ) { if ( i == PW_FIRE || // these aren't dependant on level.time i == PW_ELECTRIC || i == PW_BREATHER || i == PW_NOFATIGUE ) { continue; } if ( ent->client->ps.powerups[ i ] < level.time ) { ent->client->ps.powerups[ i ] = 0; } } } // save network bandwidth #if 0 if ( !g_synchronousClients->integer && ent->client->ps.pm_type == PM_NORMAL ) { // FIXME: this must change eventually for non-sync demo recording VectorClear( ent->client->ps.viewangles ); } #endif // // If the end of unit layout is displayed, don't give // the player any normal movement attributes // if ( level.intermissiontime ) { return; } // burn from lava, etc P_WorldEffects( ent ); // apply all the damage taken this frame P_DamageFeedback( ent ); // add the EF_CONNECTION flag if we haven't gotten commands recently if ( level.time - ent->client->lastCmdTime > 1000 ) { ent->s.eFlags |= EF_CONNECTION; } else { ent->s.eFlags &= ~EF_CONNECTION; } ent->client->ps.stats[STAT_HEALTH] = ent->health; // FIXME: get rid of ent->health... G_SetClientSound( ent ); // set the latest infor // Ridah, fixes jittery zombie movement if ( g_smoothClients.integer ) { BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, ( ( ent->r.svFlags & SVF_CASTAI ) == 0 ) ); } else { BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, ( ( ent->r.svFlags & SVF_CASTAI ) == 0 ) ); } //SendPendingPredictableEvents( &ent->client->ps ); // DHM - Nerve :: If it's been a couple frames since being revived, and props_frame_state // wasn't reset, go ahead and reset it if ( ent->props_frame_state >= 0 && ( ( level.time - ent->s.effect3Time ) > 100 ) ) { ent->props_frame_state = -1; } if ( ent->health > 0 && StuckInClient( ent ) ) { G_DPrintf( "%s is stuck in a client.\n", ent->client->pers.netname ); ent->r.contents = CONTENTS_CORPSE; } if ( g_gametype.integer >= GT_WOLF && ent->health > 0 && ent->r.contents == CONTENTS_CORPSE ) { WolfReviveBbox( ent ); } // DHM - Nerve :: Reset 'count2' for flamethrower if ( !( ent->client->buttons & BUTTON_ATTACK ) ) { ent->count2 = 0; } // dhm }
/* ================ G_SetEntState sets the entstate of an entity. ================ */ void G_SetEntState( gentity_t *ent, entState_t state ) { if ( ent->entstate == state ) { G_DPrintf( "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_TempEntity( check->s.origin, EV_ITEM_POP ); G_FreeEntity( check ); } } } // if this is an mg42, then we should try and calculate mg42 spots again BotCalculateMg42Spots(); 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; } }
/** * Get config handler */ static void *getConfigHandler(void *data) { int code; struct query_s *queryStruct = (struct query_s *)data; int config_strictSaveLoad; int config_physics; int config_disableDrowning; int config_holdDoorsOpen; int config_enableMapEntities; int config_script_size; code = API_query(queryStruct->cmd, queryStruct->result, queryStruct->query, sizeof (queryStruct->query)); if (code != 0) { G_Error("%s: error #1 while getting config from API!\n", GAME_VERSION); } code = sscanf(queryStruct->result, "%10d %10d %10d %10d %10d %10d %*s", // Nico, last field is ignored for the moment &config_strictSaveLoad, &config_physics, &config_disableDrowning, &config_holdDoorsOpen, &config_enableMapEntities, &config_script_size); if (code != 6) { G_Error("%s: error #2 while getting config from API!\n", GAME_VERSION); } if (config_script_size != 0) { int len; level.useAPImapscript = qtrue; level.scriptEntity = G_Alloc(config_script_size + 1); code = sscanf(queryStruct->result, "%*d %*d %*d %*d %*d %*d %512000c", level.scriptEntity); if (code != 1) { G_Error("%s: error #3 while getting config from API!\n", GAME_VERSION); } *(level.scriptEntity + config_script_size) = '\0'; // Nico, check script len len = strlen(level.scriptEntity); if (len != config_script_size) { G_Error("%s: error #4 while getting config from API (%d != %d)!\n", GAME_VERSION, config_script_size, len); } // Nico, do the same as G_Script_ScriptLoad do when loading from a local file trap_Cvar_Set("g_scriptName", ""); G_Script_EventStringInit(); } // Nico, check cvars from API if (config_strictSaveLoad != g_strictSaveLoad.integer) { G_Printf("%s: updating g_strictSaveLoad from %d to %d\n", GAME_VERSION, g_strictSaveLoad.integer, config_strictSaveLoad); trap_Cvar_Set("g_strictSaveLoad", va("%d", config_strictSaveLoad)); } if (config_physics != physics.integer) { G_DPrintf("%s: updating physics from %d to %d\n", GAME_VERSION, physics.integer, config_physics); trap_Cvar_Set("physics", va("%d", config_physics)); } if (config_disableDrowning != g_disableDrowning.integer) { G_DPrintf("%s: updating g_disableDrowning from %d to %d\n", GAME_VERSION, g_disableDrowning.integer, config_disableDrowning); // Nico, update the cvar g_disableDrowning.integer = config_disableDrowning; g_disableDrowning.modificationCount++; trap_Cvar_Set("g_disableDrowning", va("%d", config_disableDrowning)); } if (config_holdDoorsOpen != g_holdDoorsOpen.integer) { G_DPrintf("%s: updating g_holdDoorsOpen from %d to %d\n", GAME_VERSION, g_holdDoorsOpen.integer, config_holdDoorsOpen); // Nico, update this way to make sure the cvar value is updated earlier enough when map elements get spawned g_holdDoorsOpen.integer = config_holdDoorsOpen; g_holdDoorsOpen.modificationCount++; trap_Cvar_Set("g_holdDoorsOpen", va("%d", config_holdDoorsOpen)); } if (config_enableMapEntities != g_enableMapEntities.integer) { G_Printf("%s: updating g_enableMapEntities from %d to %d\n", GAME_VERSION, g_enableMapEntities.integer, config_enableMapEntities); // Nico, update this way to make sure the cvar value is updated earlier enough when map elements get spawned g_enableMapEntities.integer = config_enableMapEntities; g_enableMapEntities.modificationCount++; trap_Cvar_Set("g_enableMapEntities", va("%d", config_enableMapEntities)); } free(queryStruct->result); free(queryStruct); return NULL; }
/** * Asynchronous (using pthreads) API call function * * command: must be a command in apiCommands * result: pointer to an *already allocated* buffer for storing result * ent: entity who made the request * count: number of variadic arguments */ qboolean G_AsyncAPICall(char *command, char *result, gentity_t *ent, int count, ...) { struct query_s *queryStruct; pthread_t thread; pthread_attr_t attr; int returnCode = 0; void *(*handler)(void *) = NULL; va_list ap; int i = 0; if (api_module == NULL || API_query == NULL) { LDE("%s\n", "API module is not loaded"); return qfalse; } // Check if thread limit is reached if (activeThreadsCounter >= THREADS_MAX) { LDE("threads limit (%d) reached: %d\n", THREADS_MAX, activeThreadsCounter); return qfalse; } // Check if we are allowed to start a new thread if (!threadingAllowed) { LDE("%s\n", "starting new threads is forbidden"); return qfalse; } // Check number of arguments in ... (=count) if (count <= 0) { LDE("invalid argument count %d\n", count); return qfalse; } queryStruct = malloc(sizeof (struct query_s)); if (queryStruct == NULL) { LDE("%s\n", "failed to allocate memory"); return qfalse; } va_start(ap, count); // Init query buffer memset(queryStruct->query, 0, QUERY_MAX_SIZE); for (i = 0; i < count; ++i) { char *arg; arg = va_arg(ap, char *); if (!arg) { LDE("empty argument %d with command '%s'\n", i, command); free(queryStruct); va_end(ap); return qfalse; } Q_strcat(queryStruct->query, QUERY_MAX_SIZE, arg); // No trailing / if (i + 1 < count) { Q_strcat(queryStruct->query, QUERY_MAX_SIZE, CHAR_SEPARATOR); } } va_end(ap); Q_strncpyz(queryStruct->cmd, command, sizeof (queryStruct->cmd)); queryStruct->result = result; queryStruct->ent = ent; // Get the command handler handler = getHandlerForCommand(command); if (!handler) { LDE("no handler for command '%s'\n", command); free(queryStruct); return qfalse; } LDI("asynchronous API call with command: '%s', query '%s'\n", command, queryStruct->query); // Create threads as detached as they will never be joined if (pthread_attr_init(&attr)) { LDE("%s\n", "error in pthread_attr_init"); free(queryStruct); return qfalse; } if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) { LDE("%s\n", "error in pthread_attr_setdetachstate"); free(queryStruct); return qfalse; } returnCode = pthread_create(&thread, &attr, handler, (void *)queryStruct); if (returnCode) { LDE("error in pthread_create, return value is %d\n", returnCode); free(queryStruct); return qfalse; } // Nico, increase active threads counter activeThreadsCounter++; G_DPrintf("%s: increasing threads counter to %d\n", GAME_VERSION, activeThreadsCounter); if (pthread_attr_destroy(&attr)) { LDE("%s\n", "error in pthread_attr_destroy"); // Nico, note: I don't free querystruct because it's used in the thread return qfalse; } return qtrue; }
/* =========== 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; char *s; char oldname[MAX_STRING_CHARS]; char userinfo[MAX_INFO_STRING]; gclient_t *client; char *ip = NULL; // Nico, used to store client ip char *rate = NULL; // Nico, used to store client rate char *snaps = NULL; // Nico, used to store client snaps char *name = NULL; // Nico, used to store client name char oldAuthToken[MAX_QPATH]; // Nico, used to see if auth token was changed ent = g_entities + clientNum; client = ent->client; client->ps.clientNum = clientNum; // Nico, flood protection if (ClientIsFlooding(ent)) { G_LogPrintf("Dropping client %d: flooded userinfo\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of flooded userinfo", 0); return; } trap_GetUserinfo(clientNum, userinfo, sizeof (userinfo)); // Nico, perform security checks on userinfo string if (!checkUserinfoString(clientNum, userinfo)) { return; } if (g_developer.integer || *g_log.string || g_dedicated.integer) { G_Printf("Userinfo: %s\n", userinfo); } // check for local client ip = Info_ValueForKey(userinfo, "ip"); Q_strncpyz(client->pers.ip, ip, IP_MAX_LENGTH); if (ip && !strcmp(ip, "localhost")) { client->pers.localClient = qtrue; level.fLocalHost = qtrue; client->sess.referee = RL_REFEREE; } // Nico, store rate and snaps rate = Info_ValueForKey(userinfo, "rate"); client->pers.rate = atoi(rate); snaps = Info_ValueForKey(userinfo, "snaps"); client->pers.snaps = atoi(snaps); // Nico, backup old auth token Q_strncpyz(oldAuthToken, client->pers.authToken, sizeof (oldAuthToken)); s = Info_ValueForKey(userinfo, "cg_uinfo"); sscanf(s, "%i %i %i %i %s %i %i %i %i %i %i %i %i %i", &client->pers.clientFlags, &client->pers.clientTimeNudge, &client->pers.clientMaxPackets, // Nico, max FPS &client->pers.maxFPS, // Nico, auth Token (char *)&client->pers.authToken, // Nico, load view angles on load &client->pers.loadViewAngles, // Nico, load weapon on load &client->pers.loadWeapon, // Nico, load position when player dies &client->pers.autoLoad, // Nico, cgaz &client->pers.cgaz, // Nico, hideme &client->pers.hideme, // Nico, client auto demo record setting &client->pers.autoDemo, // Nico, automatically load checkpoints &client->pers.autoLoadCheckpoints, // Nico, persistant specLock (int *)&client->sess.specLocked, // Nico, keep all demos &client->pers.keepAllDemos ); // Nico, check if auth token was changed if (oldAuthToken[0] != '\0' && Q_stricmp(oldAuthToken, client->pers.authToken) && client->sess.logged) { // Nico, auth token was changed => logout player if he was logged in CP("cp \"You are no longer logged in!\n\""); G_LogPrintf("ClientUserinfoChanged: authToken changed for client %d, forcing logout\n", clientNum); ent->client->sess.logged = qfalse; } client->pers.autoActivate = (client->pers.clientFlags & CGF_AUTOACTIVATE) ? PICKUP_TOUCH : PICKUP_ACTIVATE; client->pers.predictItemPickup = ((client->pers.clientFlags & CGF_PREDICTITEMS) != 0); if (client->pers.clientFlags & CGF_AUTORELOAD) { client->pers.bAutoReloadAux = qtrue; client->pmext.bAutoReload = qtrue; } else { client->pers.bAutoReloadAux = qfalse; client->pmext.bAutoReload = qfalse; } // Nico, pmove_fixed client->pers.pmoveFixed = client->pers.clientFlags & CGF_PMOVEFIXED; // Nico, autologin client->pers.autoLogin = client->pers.clientFlags & CGF_AUTOLOGIN; // // Nico, name handling // // Backup old name Q_strncpyz(oldname, client->pers.netname, sizeof (oldname)); // Get new name from userinfo string name = Info_ValueForKey(userinfo, "name"); // Clean the new name ClientCleanName(name, client->pers.netname, sizeof (client->pers.netname)); // Check it's valid if (CheckName(client->pers.netname) != qtrue) { // Invalid name, restore old name Q_strncpyz(client->pers.netname, oldname, sizeof (client->pers.netname)); Info_SetValueForKey(userinfo, "name", oldname); trap_SetUserinfo(clientNum, userinfo); CPx(clientNum, "print \"^1Invalid name, name change refused\n\""); G_LogPrintf("Client %d name change refused\n", clientNum); } else { // Name is valid // Now, check if name was changed or not if (client->pers.connected == CON_CONNECTED && strcmp(oldname, client->pers.netname) != 0) { // Name was changed // Now, check name changes limit if (g_maxNameChanges.integer > -1 && client->pers.nameChanges >= g_maxNameChanges.integer) { // Nico, limit reached, forbid name change Q_strncpyz(client->pers.netname, oldname, sizeof (client->pers.netname)); Info_SetValueForKey(userinfo, "name", oldname); trap_SetUserinfo(clientNum, userinfo); CPx(clientNum, "print \"^1You had too many namechanges\n\""); G_LogPrintf("Client %d name change refused\n", clientNum); return; } client->pers.nameChanges++; trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname)); } } // // Nico, end of name handling // client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // To communicate it to cgame client->ps.stats[STAT_PLAYER_CLASS] = client->sess.playerType; // Gordon: Not needed any more as it's in clientinfo? // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds s = va("n\\%s\\t\\%i\\c\\%i\\w\\%i\\lw\\%i\\sw\\%i\\mu\\%i\\ref\\%i\\pm\\%i\\l\\%i\\h\\%i\\cc\\%i", client->pers.netname, client->sess.sessionTeam, client->sess.playerType, client->sess.playerWeapon, client->sess.latchPlayerWeapon, client->sess.latchPlayerWeapon2, client->sess.muted ? 1 : 0, client->sess.referee, client->pers.pmoveFixed ? 1 : 0, // Nico, pmove_fixed client->sess.logged ? 1 : 0, // Nico, login status client->pers.hideme, // Nico, hideme client->sess.countryCode// Nico, country code (GeoIP) ); trap_GetConfigstring(CS_PLAYERS + clientNum, oldname, sizeof (oldname)); trap_SetConfigstring(CS_PLAYERS + clientNum, s); if (!Q_stricmp(oldname, s)) { return; } G_LogPrintf("ClientUserinfoChanged: %i %s\n", clientNum, s); G_DPrintf("ClientUserinfoChanged: %i :: %s\n", clientNum, s); }