]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/g_world.qc
Step 5: complete
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / g_world.qc
index b49b2160ed0a2860f2d98b81d90c0f473760fed9..8e2f74c7209c150eac17a1e9b74aa5b72205e841 100644 (file)
@@ -24,7 +24,7 @@
 #include "../common/monsters/all.qh"
 #include "../common/monsters/sv_monsters.qh"
 #include "../common/vehicles/all.qh"
-#include "../common/notifications.qh"
+#include "../common/notifications/all.qh"
 #include "../common/physics/player.qh"
 #include "../common/playerstats.qh"
 #include "../common/stats.qh"
 #include "../common/util.qh"
 #include "../common/items/all.qh"
 #include "../common/weapons/all.qh"
+#include "../common/state.qh"
 
 const float LATENCY_THINKRATE = 10;
 .float latency_sum;
 .float latency_cnt;
 .float latency_time;
 entity pingplreport;
-void PingPLReport_Think()
-{SELFPARAM();
+void PingPLReport_Think(entity this)
+{
        float delta;
        entity e;
 
        delta = 3 / maxclients;
        if(delta < sys_frametime)
                delta = 0;
-       self.nextthink = time + delta;
+       this.nextthink = time + delta;
 
-       e = edict_num(self.cnt + 1);
+       e = edict_num(this.cnt + 1);
        if(IS_REAL_CLIENT(e))
        {
                WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
-               WriteByte(MSG_BROADCAST, self.cnt);
+               WriteByte(MSG_BROADCAST, this.cnt);
                WriteShort(MSG_BROADCAST, max(1, e.ping));
                WriteByte(MSG_BROADCAST, ceil(e.ping_packetloss * 255));
                WriteByte(MSG_BROADCAST, ceil(e.ping_movementloss * 255));
@@ -71,18 +72,17 @@ void PingPLReport_Think()
        else
        {
                WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
-               WriteByte(MSG_BROADCAST, self.cnt);
+               WriteByte(MSG_BROADCAST, this.cnt);
                WriteShort(MSG_BROADCAST, 0);
                WriteByte(MSG_BROADCAST, 0);
                WriteByte(MSG_BROADCAST, 0);
        }
-       self.cnt = (self.cnt + 1) % maxclients;
+       this.cnt = (this.cnt + 1) % maxclients;
 }
 void PingPLReport_Spawn()
 {
-       pingplreport = new(pingplreport);
-       make_pure(pingplreport);
-       pingplreport.think = PingPLReport_Think;
+       pingplreport = new_pure(pingplreport);
+       setthink(pingplreport, PingPLReport_Think);
        pingplreport.nextthink = time;
 }
 
@@ -104,8 +104,8 @@ void SetDefaultAlpha()
        }
 }
 
-void GotoFirstMap()
-{SELFPARAM();
+void GotoFirstMap(entity this)
+{
        float n;
        if(autocvar__sv_init)
        {
@@ -129,11 +129,11 @@ void GotoFirstMap()
 
        if(time < 5)
        {
-               self.nextthink = time;
+               this.nextthink = time;
        }
        else
        {
-               self.nextthink = time + 1;
+               this.nextthink = time + 1;
                LOG_INFO("Waiting for _sv_init being set to 1 by initialization scripts...\n");
        }
 }
@@ -232,7 +232,6 @@ void cvar_changes_init()
                BADPREFIX("g_chat_flood_");
                BADPREFIX("g_ghost_items");
                BADPREFIX("g_playerstats_");
-               BADPREFIX("g_respawn_ghosts");
                BADPREFIX("g_voice_flood_");
                BADPREFIX("log_file");
                BADPREFIX("rcon_");
@@ -269,7 +268,9 @@ void cvar_changes_init()
                BADCVAR("g_nexball");
                BADCVAR("g_onslaught");
                BADCVAR("g_race");
+               BADCVAR("g_race_laps_limit");
                BADCVAR("g_race_qualifying_timelimit");
+               BADCVAR("g_race_qualifying_timelimit_override");
                BADCVAR("g_tdm");
                BADCVAR("g_tdm_teams");
                BADCVAR("leadlimit");
@@ -300,47 +301,53 @@ void cvar_changes_init()
 
                // now check if the changes are actually gameplay relevant
 
-               // does nothing visible
+               // does nothing gameplay relevant
                BADCVAR("captureleadlimit_override");
+               BADCVAR("gameversion");
+               BADCVAR("g_allow_oldvortexbeam");
                BADCVAR("g_balance_kill_delay");
-               BADCVAR("g_ca_point_limit");
+               BADCVAR("g_campcheck_distance");
                BADCVAR("g_ca_point_leadlimit");
+               BADCVAR("g_ca_point_limit");
                BADCVAR("g_ctf_captimerecord_always");
                BADCVAR("g_ctf_flag_glowtrails");
                BADCVAR("g_ctf_flag_pickup_verbosename");
                BADCVAR("g_domination_point_leadlimit");
                BADCVAR("g_forced_respawn");
-               BADCVAR("g_freezetag_point_limit");
                BADCVAR("g_freezetag_point_leadlimit");
-               BADCVAR("g_keyhunt_point_leadlimit");
-               BADPREFIX("g_mod_");
+               BADCVAR("g_freezetag_point_limit");
+               BADCVAR("g_hats");
                BADCVAR("g_invasion_point_limit");
+               BADCVAR("g_keyhunt_point_leadlimit");
                BADCVAR("g_nexball_goalleadlimit");
-               BADCVAR("g_tdm_point_limit");
                BADCVAR("g_tdm_point_leadlimit");
+               BADCVAR("g_tdm_point_limit");
                BADCVAR("leadlimit_and_fraglimit");
                BADCVAR("leadlimit_override");
                BADCVAR("pausable");
                BADCVAR("sv_allow_fullbright");
                BADCVAR("sv_checkforpacketsduringsleep");
+               BADCVAR("sv_intermission_cdtrack");
+               BADCVAR("sv_minigames");
+               BADCVAR("sv_namechangetimer");
+               BADCVAR("sv_precacheplayermodels");
                BADCVAR("sv_timeout");
-               BADPREFIX("sv_timeout_");
                BADPREFIX("crypto_");
+               BADPREFIX("gameversion_");
                BADPREFIX("g_chat_");
                BADPREFIX("g_ctf_captimerecord_");
                BADPREFIX("g_maplist_votable_");
+               BADPREFIX("g_mod_");
+               BADPREFIX("g_respawn_");
                BADPREFIX("net_");
                BADPREFIX("prvm_");
                BADPREFIX("skill_");
                BADPREFIX("sv_cullentities_");
                BADPREFIX("sv_maxidle_");
+               BADPREFIX("sv_minigames_");
+               BADPREFIX("sv_timeout_");
                BADPREFIX("sv_vote_");
                BADPREFIX("timelimit_");
-               BADCVAR("gameversion");
-               BADPREFIX("gameversion_");
-               BADCVAR("sv_minigames");
-               BADPREFIX("sv_minigames_");
-               BADCVAR("sv_namechangetimer");
 
                // allowed changes to server admins (please sync this to server.cfg)
                // vi commands:
@@ -385,7 +392,9 @@ void cvar_changes_init()
                BADCVAR("g_mirrordamage");
                BADCVAR("g_nexball_goallimit");
                BADCVAR("g_powerups");
+               BADCVAR("g_spawnshieldtime");
                BADCVAR("g_start_delay");
+               BADCVAR("g_superspectate");
                BADCVAR("g_tdm_teams_override");
                BADCVAR("g_warmup");
                BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay");
@@ -404,6 +413,7 @@ void cvar_changes_init()
                BADCVAR("sv_autotaunt");
                BADCVAR("sv_curl_defaulturl");
                BADCVAR("sv_defaultcharacter");
+               BADCVAR("sv_defaultcharacterskin");
                BADCVAR("sv_defaultplayercolors");
                BADCVAR("sv_defaultplayermodel");
                BADCVAR("sv_defaultplayerskin");
@@ -423,7 +433,6 @@ void cvar_changes_init()
                BADCVAR("sv_vote_simple_majority_factor");
                BADCVAR("teamplay_mode");
                BADCVAR("timelimit_override");
-               BADCVAR("g_spawnshieldtime");
                BADPREFIX("g_warmup_");
                BADPREFIX("sv_ready_restart_");
 
@@ -503,24 +512,23 @@ entity randomseed;
 bool RandomSeed_Send(entity this, entity to, int sf)
 {
        WriteHeader(MSG_ENTITY, ENT_CLIENT_RANDOMSEED);
-       WriteShort(MSG_ENTITY, self.cnt);
+       WriteShort(MSG_ENTITY, this.cnt);
        return true;
 }
-void RandomSeed_Think()
-{SELFPARAM();
-       self.cnt = bound(0, floor(random() * 65536), 65535);
-       self.nextthink = time + 5;
+void RandomSeed_Think(entity this)
+{
+       this.cnt = bound(0, floor(random() * 65536), 65535);
+       this.nextthink = time + 5;
 
-       self.SendFlags |= 1;
+       this.SendFlags |= 1;
 }
 void RandomSeed_Spawn()
-{SELFPARAM();
-       randomseed = new(randomseed);
-       make_pure(randomseed);
-       randomseed.think = RandomSeed_Think;
+{
+       randomseed = new_pure(randomseed);
+       setthink(randomseed, RandomSeed_Think);
        Net_LinkEntity(randomseed, false, 0, RandomSeed_Send);
 
-       WITH(entity, self, randomseed, randomseed.think()); // sets random seed and nextthink
+       WITHSELF(randomseed, getthink(randomseed)(randomseed)); // sets random seed and nextthink
 }
 
 spawnfunc(__init_dedicated_server)
@@ -535,12 +543,12 @@ spawnfunc(__init_dedicated_server)
        remove = remove_unsafely;
 
        entity e = spawn();
-       e.think = GotoFirstMap;
+       setthink(e, GotoFirstMap);
        e.nextthink = time; // this is usually 1 at this point
 
        e = new(info_player_deathmatch);  // safeguard against player joining
 
-       self.classname = "worldspawn"; // safeguard against various stuff ;)
+       this.classname = "worldspawn"; // safeguard against various stuff ;)
 
        // needs to be done so early because of the constants they create
        static_init();
@@ -555,6 +563,18 @@ void __init_dedicated_server_shutdown() {
        MapInfo_Shutdown();
 }
 
+void SetLimits(int fraglimit_override, int leadlimit_override, float timelimit_override, float qualifying_override)
+{
+       if(!autocvar_g_campaign)
+       {
+               if(fraglimit_override >= 0) cvar_set("fraglimit", ftos(fraglimit_override));
+               if(timelimit_override >= 0) cvar_set("timelimit", ftos(timelimit_override));
+               if(leadlimit_override >= 0) cvar_set("leadlimit", ftos(leadlimit_override));
+               if(qualifying_override >= 0) cvar_set("g_race_qualifying_timelimit", ftos(qualifying_override));
+       }
+       limits_are_set = true;
+}
+
 void Map_MarkAsRecent(string m);
 float world_already_spawned;
 void Nagger_Init();
@@ -563,7 +583,70 @@ 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 +669,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();
 
@@ -649,10 +730,16 @@ spawnfunc(worldspawn)
        readlevelcvars();
        GrappleHookInit();
 
+       if(!limits_are_set)
+               SetLimits(autocvar_fraglimit_override, autocvar_leadlimit_override, autocvar_timelimit_override, -1);
+
+       if(warmup_limit == 0)
+               warmup_limit = (autocvar_timelimit > 0) ? autocvar_timelimit * 60 : autocvar_timelimit;
+
        player_count = 0;
        bot_waypoints_for_items = autocvar_g_waypoints_for_items;
        if(bot_waypoints_for_items == 1)
-               if(self.spawnflags & SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS)
+               if(this.spawnflags & SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS)
                        bot_waypoints_for_items = 0;
 
        precache();
@@ -673,7 +760,7 @@ spawnfunc(worldspawn)
                s = ":gameinfo:mutators:LIST";
 
                MUTATOR_CALLHOOK(BuildMutatorsString, s);
-               s = ret_string;
+               s = M_ARGV(0, string);
 
                // initialiation stuff, not good in the mutator system
                if(!autocvar_g_use_ammunition)
@@ -780,34 +867,37 @@ spawnfunc(worldspawn)
 
        CheatInit();
 
-       localcmd("\n_sv_hook_gamestart ", GetGametype(), "\n");
+       if (!wantrestart) 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
@@ -825,15 +915,15 @@ spawnfunc(worldspawn)
        // save it for later
        modname = strzone(modname);
 
-       WinningConditionHelper(); // set worldstatus
+       WinningConditionHelper(this); // set worldstatus
 
        world_initialized = 1;
 }
 
 spawnfunc(light)
 {
-       //makestatic (self); // Who the f___ did that?
-       remove(self);
+       //makestatic (this); // Who the f___ did that?
+       remove(this);
 }
 
 string GetGametype()
@@ -1206,18 +1296,18 @@ When the player presses attack or jump, change to the next level
 ============
 */
 .float autoscreenshot;
-void IntermissionThink()
-{SELFPARAM();
-       FixIntermissionClient(self);
+void IntermissionThink(entity this)
+{
+       FixIntermissionClient(this);
 
-       float server_screenshot = (autocvar_sv_autoscreenshot && self.cvar_cl_autoscreenshot);
-       float client_screenshot = (self.cvar_cl_autoscreenshot == 2);
+       float server_screenshot = (autocvar_sv_autoscreenshot && this.cvar_cl_autoscreenshot);
+       float client_screenshot = (this.cvar_cl_autoscreenshot == 2);
 
        if( (server_screenshot || client_screenshot)
-               && ((self.autoscreenshot > 0) && (time > self.autoscreenshot)) )
+               && ((this.autoscreenshot > 0) && (time > this.autoscreenshot)) )
        {
-               self.autoscreenshot = -1;
-               if(IS_REAL_CLIENT(self)) { stuffcmd(self, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), strftime(false, "%s"))); }
+               this.autoscreenshot = -1;
+               if(IS_REAL_CLIENT(this)) { stuffcmd(this, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), strftime(false, "%s"))); }
                return;
        }
 
@@ -1225,7 +1315,7 @@ void IntermissionThink()
                return;
 
        if(!mapvote_initialized)
-               if (time < intermission_exittime + 10 && !(self.BUTTON_ATCK || self.BUTTON_JUMP || self.BUTTON_ATCK2 || self.BUTTON_HOOK || self.BUTTON_USE))
+               if (time < intermission_exittime + 10 && !(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this)))
                        return;
 
        MapVote_Start();
@@ -1342,7 +1432,7 @@ void DumpStats(float final)
        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))
+               if(IS_PLAYER(it) || MUTATOR_CALLHOOK(GetPlayerStatus, it))
                        s = strcat(s, ftos(it.team), ":");
                else
                        s = strcat(s, "spectator:");
@@ -1459,7 +1549,7 @@ void NextLevel()
        PlayerStats_GameReport(true);
        WeaponStats_Shutdown();
 
-       Kill_Notification(NOTIF_ALL, world, MSG_CENTER, 0); // kill all centerprints now
+       Kill_Notification(NOTIF_ALL, world, MSG_CENTER, CPID_Null); // kill all centerprints now
 
        if(autocvar_sv_eventlog)
                GameLogEcho(":gameover");
@@ -1472,9 +1562,7 @@ void NextLevel()
                        bprint(it.netname, " ^7wins.\n");
        ));
 
-       entity oldself = self;
-       target_music_kill();
-       self = oldself;
+       WITHSELF(NULL, target_music_kill());
 
        if(autocvar_g_campaign)
                CampaignPreIntermission();
@@ -1491,13 +1579,13 @@ CheckRules_Player
 Exit deathmatch games upon conditions
 ============
 */
-void CheckRules_Player()
-{SELFPARAM();
+void CheckRules_Player(entity this)
+{
        if (gameover)   // someone else quit the game already
                return;
 
-       if(!IS_DEAD(self))
-               self.play_time += frametime;
+       if(!IS_DEAD(this))
+               this.play_time += frametime;
 
        // fixme: don't check players; instead check spawnfunc_dom_team and spawnfunc_ctf_team entities
        //   (div0: and that in CheckRules_World please)
@@ -1583,51 +1671,6 @@ void ClearWinners()
        FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.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;
-}
-
 void ShuffleMaplist()
 {
        cvar_set("g_maplist", shufflewords(autocvar_g_maplist));
@@ -1639,7 +1682,7 @@ float WinningCondition_Scores(float limit, float leadlimit)
        float limitreached;
 
        // TODO make everything use THIS winning condition (except LMS)
-       WinningConditionHelper();
+       WinningConditionHelper(NULL);
 
        if(teamplay)
        {
@@ -1885,7 +1928,7 @@ void CheckRules_World()
        if(checkrules_status == WINNING_YES)
                bprint("Hey! Someone ran out of spawns!\n");
        else if(MUTATOR_CALLHOOK(CheckRules_World, checkrules_status, timelimit, fraglimit))
-               checkrules_status = ret_float;
+               checkrules_status = M_ARGV(0, float);
        else
                checkrules_status = WinningCondition_Scores(fraglimit, leadlimit);
 
@@ -1942,40 +1985,36 @@ string GotoMap(string m)
 
 
 void EndFrame()
-{SELFPARAM();
+{
        anticheat_endframe();
 
-       float altime;
-       FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
+       FOREACH_CLIENT(IS_REAL_CLIENT(it), {
                entity e = IS_SPEC(it) ? it.enemy : it;
-               if(e.typehitsound)
+               if (e.typehitsound) {
                        it.typehit_time = time;
-               else if(e.damage_dealt)
-               {
+               } else if (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!
-       FOREACH_CLIENT(true, LAMBDA(
+       float altime = time + frametime * (1 + autocvar_g_antilag_nudge);
+       FOREACH_CLIENT(true, {
                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(
+               antilag_record(it, CS(it), altime);
+       });
+       FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, {
+               antilag_record(it, it, altime);
+       });
+       FOREACH_CLIENT(PS(it), {
                PlayerState s = PS(it);
                s.ps_push(s, it);
-       ));
+       });
 }
 
 
@@ -1986,7 +2025,7 @@ void EndFrame()
 float redirection_timeout;
 float redirection_nextthink;
 float RedirectionThink()
-{SELFPARAM();
+{
        float clients_found;
 
        if(redirection_target == "")
@@ -2009,13 +2048,12 @@ float RedirectionThink()
 
        clients_found = 0;
        FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
-               setself(it);
                // TODO add timer
-               LOG_INFO("Redirecting: sending connect command to ", self.netname, "\n");
+               LOG_INFO("Redirecting: sending connect command to ", it.netname, "\n");
                if(redirection_target == "self")
-                       stuffcmd(self, "\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " reconnect\n");
+                       stuffcmd(it, "\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " reconnect\n");
                else
-                       stuffcmd(self, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
+                       stuffcmd(it, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
                ++clients_found;
        ));