]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/g_world.qc
Move assault winning condition function into the assault file
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / g_world.qc
index a7036ca4f29b70fa19aeb25756d4cfc701de71af..5fc7b9f99042cc71b31e82f02c84c5c7a7b80fb8 100644 (file)
@@ -25,7 +25,7 @@
 #include "../common/monsters/sv_monsters.qh"
 #include "../common/vehicles/all.qh"
 #include "../common/notifications.qh"
-#include "../common/physics.qh"
+#include "../common/physics/player.qh"
 #include "../common/playerstats.qh"
 #include "../common/stats.qh"
 #include "../common/teams.qh"
@@ -480,7 +480,7 @@ void detect_maptype()
                o.y += random() * (world.maxs.y - world.mins.y);
                o.z += random() * (world.maxs.z - world.mins.z);
 
-               tracebox(o, PL_MIN, PL_MAX, o - '0 0 32768', MOVE_WORLDONLY, world);
+               tracebox(o, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), o - '0 0 32768', MOVE_WORLDONLY, world);
                if(trace_fraction == 1)
                        continue;
 
@@ -563,7 +563,71 @@ void WeaponStats_Init();
 void WeaponStats_Shutdown();
 spawnfunc(worldspawn)
 {
-       float fd, l, j, n;
+       server_is_dedicated = boolean(stof(cvar_defstring("is_dedicated")));
+
+       {
+               bool wantrestart = false;
+
+               if (!server_is_dedicated)
+               {
+                       // force unloading of server pk3 files when starting a listen server
+                       // localcmd("\nfs_rescan\n"); // FIXME: does more harm than good, has unintended side effects. What we really want is to unload temporary pk3s only
+                       // restore csqc_progname too
+                       string expect = "csprogs.dat";
+                       wantrestart = cvar_string_normal("csqc_progname") != expect;
+                       cvar_set_normal("csqc_progname", expect);
+               }
+               else
+               {
+                       // Try to use versioned csprogs from pk3
+                       // Only ever use versioned csprogs.dat files on dedicated servers;
+                       // we need to reset csqc_progname on clients ourselves, and it's easier if the client's release name is constant
+                       string pk3csprogs = "csprogs-" WATERMARK ".dat";
+                       // This always works; fall back to it if a versioned csprogs.dat is suddenly missing
+                       string select = "csprogs.dat";
+                       if (fexists(pk3csprogs)) select = pk3csprogs;
+                       if (cvar_string_normal("csqc_progname") != select)
+                       {
+                               cvar_set_normal("csqc_progname", select);
+                               wantrestart = true;
+                       }
+                       // Check for updates on startup
+                       // We do it this way for atomicity so that connecting clients still match the server progs and don't disconnect
+                       int sentinel = fopen("progs.txt", FILE_READ);
+                       if (sentinel >= 0)
+                       {
+                               string switchversion = fgets(sentinel);
+                               fclose(sentinel);
+                               if (switchversion != "" && switchversion != WATERMARK)
+                               {
+                                       LOG_INFOF("Switching progs: " WATERMARK " -> %s\n", switchversion);
+                                       // if it doesn't exist, assume either:
+                                       //   a) the current program was overwritten
+                                       //   b) this is a client only update
+                                       string newprogs = sprintf("progs-%s.dat", switchversion);
+                                       if (fexists(newprogs))
+                                       {
+                                               cvar_set_normal("sv_progs", newprogs);
+                                               wantrestart = true;
+                                       }
+                                       string newcsprogs = sprintf("csprogs-%s.dat", switchversion);
+                                       if (fexists(newcsprogs))
+                                       {
+                                               cvar_set_normal("csqc_progname", newcsprogs);
+                                               wantrestart = true;
+                                       }
+                               }
+                       }
+               }
+               if (wantrestart)
+               {
+                       LOG_INFOF("Restart requested\n");
+                       changelevel(mapname);
+                       // let initialization continue, shutdown depends on it
+               }
+       }
+
+       float fd, l;
        string s;
 
        cvar = cvar_normal;
@@ -586,8 +650,6 @@ spawnfunc(worldspawn)
                ++maxclients;
        }
 
-       server_is_dedicated = (stof(cvar_defstring("is_dedicated")) ? true : false);
-
        // needs to be done so early because of the constants they create
        static_init();
 
@@ -783,31 +845,34 @@ spawnfunc(worldspawn)
        localcmd("\n_sv_hook_gamestart ", GetGametype(), "\n");
 
        // fill sv_curl_serverpackages from .serverpackage files
-       if(autocvar_sv_curl_serverpackages_auto)
+       if (autocvar_sv_curl_serverpackages_auto)
        {
-               s = "";
-               n = tokenize_console(cvar_string("sv_curl_serverpackages"));
-               for(int i = 0; i < n; ++i)
-                       if(substring(argv(i), -18, -1) != "-serverpackage.txt")
-                       if(substring(argv(i), -14, -1) != ".serverpackage") // OLD legacy
-                               s = strcat(s, " ", argv(i));
-               fd = search_begin("*-serverpackage.txt", true, false);
-               if(fd >= 0)
-               {
-                       j = search_getsize(fd);
-                       for(int i = 0; i < j; ++i)
-                               s = strcat(s, " ", search_getfilename(fd, i));
-                       search_end(fd);
-               }
-               fd = search_begin("*.serverpackage", true, false);
-               if(fd >= 0)
+               s = "csprogs-" WATERMARK ".txt";
+               // remove automatically managed files from the list to prevent duplicates
+               for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i)
                {
-                       j = search_getsize(fd);
-                       for(int i = 0; i < j; ++i)
-                               s = strcat(s, " ", search_getfilename(fd, i));
-                       search_end(fd);
+                       string pkg = argv(i);
+                       if (startsWith(pkg, "csprogs-")) continue;
+                       if (endsWith(pkg, "-serverpackage.txt")) continue;
+                       if (endsWith(pkg, ".serverpackage")) continue;  // OLD legacy
+                       s = cons(s, pkg);
                }
-               cvar_set("sv_curl_serverpackages", substring(s, 1, -1));
+               // add automatically managed files to the list
+               #define X(match) MACRO_BEGIN { \
+                       fd = search_begin(match, true, false); \
+                       if (fd >= 0) \
+                       { \
+                               for (int i = 0, j = search_getsize(fd); i < j; ++i) \
+                               { \
+                                       s = cons(s, search_getfilename(fd, i)); \
+                               } \
+                               search_end(fd); \
+                       } \
+               } MACRO_END
+               X("*-serverpackage.txt");
+               X("*.serverpackage");
+               #undef X
+               cvar_set("sv_curl_serverpackages", s);
        }
 
        // MOD AUTHORS: change this, and possibly remove a few of the blocks below to ignore certain changes
@@ -1339,25 +1404,21 @@ void DumpStats(float final)
        if(to_file)
                fputs(file, strcat(s, "\n"));
 
-       FOR_EACH_CLIENT(other)
-       {
-               if ((IS_REAL_CLIENT(other)) || (IS_BOT_CLIENT(other) && autocvar_sv_logscores_bots))
-               {
-                       s = strcat(":player:see-labels:", GetPlayerScoreString(other, 0), ":");
-                       s = strcat(s, ftos(rint(time - other.jointime)), ":");
-                       if(IS_PLAYER(other) || MUTATOR_CALLHOOK(GetPlayerStatus, other, s))
-                               s = strcat(s, ftos(other.team), ":");
-                       else
-                               s = strcat(s, "spectator:");
+       FOREACH_CLIENT(IS_REAL_CLIENT(it) || (IS_BOT_CLIENT(it) && autocvar_sv_logscores_bots), LAMBDA(
+               s = strcat(":player:see-labels:", GetPlayerScoreString(it, 0), ":");
+               s = strcat(s, ftos(rint(time - it.jointime)), ":");
+               if(IS_PLAYER(it) || MUTATOR_CALLHOOK(GetPlayerStatus, it, s))
+                       s = strcat(s, ftos(it.team), ":");
+               else
+                       s = strcat(s, "spectator:");
 
-                       if(to_console)
-                               LOG_INFO(s, other.netname, "\n");
-                       if(to_eventlog)
-                               GameLogEcho(strcat(s, ftos(other.playerid), ":", other.netname));
-                       if(to_file)
-                               fputs(file, strcat(s, other.netname, "\n"));
-               }
-       }
+               if(to_console)
+                       LOG_INFO(s, it.netname, "\n");
+               if(to_eventlog)
+                       GameLogEcho(strcat(s, ftos(it.playerid), ":", it.netname));
+               if(to_file)
+                       fputs(file, strcat(s, it.netname, "\n"));
+       ));
 
        if(teamplay)
        {
@@ -1409,18 +1470,21 @@ void FixIntermissionClient(entity e)
                        if(e.(weaponentity))
                        {
                                e.(weaponentity).effects = EF_NODRAW;
-                               if (e.(weaponentity).(weaponentity))
-                                       e.(weaponentity).(weaponentity).effects = EF_NODRAW;
+                               if (e.(weaponentity).weaponchild)
+                                       e.(weaponentity).weaponchild.effects = EF_NODRAW;
                        }
                }
                if(IS_REAL_CLIENT(e))
                {
                        stuffcmd(e, "\nscr_printspeed 1000000\n");
-                       string list = autocvar_sv_intermission_cdtrack;
-                       for(string it; (it = car(list)); list = cdr(list))
-                               RandomSelection_Add(world, 0, it, 1, 1);
-                       if(RandomSelection_chosen_string && RandomSelection_chosen_string != "")
-                               stuffcmd(e, strcat("\ncd loop ", RandomSelection_chosen_string, "\n"));
+                       RandomSelection_Init();
+                       FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, LAMBDA(
+                               RandomSelection_Add(NULL, 0, it, 1, 1);
+                       ));
+                       if (RandomSelection_chosen_string != "")
+                       {
+                               stuffcmd(e, sprintf("\ncd loop %s\n", RandomSelection_chosen_string));
+                       }
                        msg_entity = e;
                        WriteByte(MSG_ONE, SVC_INTERMISSION);
                }
@@ -1467,11 +1531,11 @@ void NextLevel()
 
        GameLogClose();
 
-       FOR_EACH_PLAYER(other) {
-               FixIntermissionClient(other);
-               if(other.winning)
-                       bprint(other.netname, " ^7wins.\n");
-       }
+       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+               FixIntermissionClient(it);
+               if(it.winning)
+                       bprint(it.netname, " ^7wins.\n");
+       ));
 
        entity oldself = self;
        target_music_kill();
@@ -1497,7 +1561,7 @@ void CheckRules_Player()
        if (gameover)   // someone else quit the game already
                return;
 
-       if(self.deadflag == DEAD_NO)
+       if(!IS_DEAD(self))
                self.play_time += frametime;
 
        // fixme: don't check players; instead check spawnfunc_dom_team and spawnfunc_ctf_team entities
@@ -1566,71 +1630,22 @@ float GetWinningCode(float fraglimitreached, float equality)
 // set the .winning flag for exactly those players with a given field value
 void SetWinners(.float field, float value)
 {
-       entity head;
-       FOR_EACH_PLAYER(head)
-               head.winning = (head.(field) == value);
+       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.winning = (it.(field) == value)));
 }
 
 // set the .winning flag for those players with a given field value
 void AddWinners(.float field, float value)
 {
-       entity head;
-       FOR_EACH_PLAYER(head)
-               if (head.(field) == value)
-                       head.winning = 1;
+       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+               if(it.(field) == value)
+                       it.winning = 1;
+       ));
 }
 
 // clear the .winning flags
 void ClearWinners()
 {
-       entity head;
-       FOR_EACH_PLAYER(head)
-               head.winning = 0;
-}
-
-// Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives)
-// they win. Otherwise the defending team wins once the timelimit passes.
-void assault_new_round();
-float WinningCondition_Assault()
-{SELFPARAM();
-       float status;
-
-       WinningConditionHelper(); // set worldstatus
-
-       status = WINNING_NO;
-       // as the timelimit has not yet passed just assume the defending team will win
-       if(assault_attacker_team == NUM_TEAM_1)
-       {
-               SetWinners(team, NUM_TEAM_2);
-       }
-       else
-       {
-               SetWinners(team, NUM_TEAM_1);
-       }
-
-       entity ent;
-       ent = find(world, classname, "target_assault_roundend");
-       if(ent)
-       {
-               if(ent.winning) // round end has been triggered by attacking team
-               {
-                       bprint("ASSAULT: round completed...\n");
-                       SetWinners(team, assault_attacker_team);
-
-                       TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 666 - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 0));
-
-                       if(ent.cnt == 1 || autocvar_g_campaign) // this was the second round
-                       {
-                               status = WINNING_YES;
-                       }
-                       else
-                       {
-                               WITH(entity, self, ent, assault_new_round());
-                       }
-               }
-       }
-
-       return status;
+       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.winning = 0));
 }
 
 void ShuffleMaplist()
@@ -1712,8 +1727,6 @@ float WinningCondition_Scores(float limit, float leadlimit)
 
 float WinningCondition_RanOutOfSpawns()
 {
-       entity head;
-
        if(have_team_spawns <= 0)
                return WINNING_NO;
 
@@ -1725,29 +1738,25 @@ float WinningCondition_RanOutOfSpawns()
 
        team1_score = team2_score = team3_score = team4_score = 0;
 
-       FOR_EACH_PLAYER(head) if(head.deadflag == DEAD_NO)
-       {
-               if(head.team == NUM_TEAM_1)
-                       team1_score = 1;
-               else if(head.team == NUM_TEAM_2)
-                       team2_score = 1;
-               else if(head.team == NUM_TEAM_3)
-                       team3_score = 1;
-               else if(head.team == NUM_TEAM_4)
-                       team4_score = 1;
-       }
+       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), LAMBDA(
+               switch(it.team)
+               {
+                       case NUM_TEAM_1: team1_score = 1; break;
+                       case NUM_TEAM_2: team2_score = 1; break;
+                       case NUM_TEAM_3: team3_score = 1; break;
+                       case NUM_TEAM_4: team4_score = 1; break;
+               }
+       ));
 
-       for(head = world; (head = find(head, classname, "info_player_deathmatch")) != world; )
-       {
-               if(head.team == NUM_TEAM_1)
-                       team1_score = 1;
-               else if(head.team == NUM_TEAM_2)
-                       team2_score = 1;
-               else if(head.team == NUM_TEAM_3)
-                       team3_score = 1;
-               else if(head.team == NUM_TEAM_4)
-                       team4_score = 1;
-       }
+       FOREACH_ENTITY_CLASS("info_player_deathmatch", true, LAMBDA(
+               switch(it.team)
+               {
+                       case NUM_TEAM_1: team1_score = 1; break;
+                       case NUM_TEAM_2: team2_score = 1; break;
+                       case NUM_TEAM_3: team3_score = 1; break;
+                       case NUM_TEAM_4: team4_score = 1; break;
+               }
+       ));
 
        ClearWinners();
        if(team1_score + team2_score + team3_score + team4_score == 0)
@@ -1861,16 +1870,14 @@ void CheckRules_World()
                                float totalplayers;
                                float playerswithlaps;
                                float readyplayers;
-                               entity head;
                                totalplayers = playerswithlaps = readyplayers = 0;
-                               FOR_EACH_PLAYER(head)
-                               {
+                               FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
                                        ++totalplayers;
-                                       if(PlayerScore_Add(head, SP_RACE_FASTEST, 0))
+                                       if(PlayerScore_Add(it, SP_RACE_FASTEST, 0))
                                                ++playerswithlaps;
-                                       if(head.ready)
+                                       if(it.ready)
                                                ++readyplayers;
-                               }
+                               ));
 
                                // at least 2 of the players have completed a lap: start the RACE
                                // otherwise, the players should end the qualifying on their own
@@ -1959,36 +1966,36 @@ void EndFrame()
        anticheat_endframe();
 
        float altime;
-       entity e_;
-       FOR_EACH_REALCLIENT(e_)
-       {
-               entity e = IS_SPEC(e_) ? e_.enemy : e_;
+       FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
+               entity e = IS_SPEC(it) ? it.enemy : it;
                if(e.typehitsound)
-                       e_.typehit_time = time;
+                       it.typehit_time = time;
                else if(e.damage_dealt)
                {
-                       e_.hit_time = time;
-                       e_.damage_dealt_total += ceil(e.damage_dealt);
+                       it.hit_time = time;
+                       it.damage_dealt_total += ceil(e.damage_dealt);
                }
-       }
+       ));
        altime = time + frametime * (1 + autocvar_g_antilag_nudge);
        // add 1 frametime because after this, engine SV_Physics
        // increases time by a frametime and then networks the frame
        // add another frametime because client shows everything with
        // 1 frame of lag (cl_nolerp 0). The last +1 however should not be
        // needed!
-       FOR_EACH_CLIENT(e_)
-       {
-               e_.typehitsound = false;
-               e_.damage_dealt = 0;
-               setself(e_);
-               antilag_record(e_, altime);
-       }
-       FOR_EACH_MONSTER(e_)
-       {
-               setself(e_);
-               antilag_record(e_, altime);
-       }
+       FOREACH_CLIENT(true, LAMBDA(
+               it.typehitsound = false;
+               it.damage_dealt = 0;
+               setself(it);
+               antilag_record(it, altime);
+       ));
+       FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(
+               setself(it);
+               antilag_record(it, altime);
+       ));
+       FOREACH_CLIENT(PS(it), LAMBDA(
+               PlayerState s = PS(it);
+               s.ps_push(s, it);
+       ));
 }
 
 
@@ -2021,10 +2028,8 @@ float RedirectionThink()
        redirection_nextthink = time + 1;
 
        clients_found = 0;
-       entity e;
-       FOR_EACH_REALCLIENT(e)
-       {
-               setself(e);
+       FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
+               setself(it);
                // TODO add timer
                LOG_INFO("Redirecting: sending connect command to ", self.netname, "\n");
                if(redirection_target == "self")
@@ -2032,7 +2037,7 @@ float RedirectionThink()
                else
                        stuffcmd(self, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
                ++clients_found;
-       }
+       ));
 
        LOG_INFO("Redirecting: ", ftos(clients_found), " clients left.\n");
 
@@ -2067,7 +2072,7 @@ void Shutdown()
        if(world_initialized > 0)
        {
                world_initialized = 0;
-               LOG_INFO("Saving persistent data...\n");
+               LOG_TRACE("Saving persistent data...\n");
                Ban_SaveBans();
 
                // playerstats with unfinished match
@@ -2090,7 +2095,7 @@ void Shutdown()
                CheatShutdown(); // must be after cheatcount check
                db_close(ServerProgsDB);
                db_close(TemporaryDB);
-               LOG_INFO("done!\n");
+               LOG_TRACE("Saving persistent data... done!\n");
                // tell the bot system the game is ending now
                bot_endgame();