]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Refactor map list code to use buffers
authorMario <mario.mario@y7mail.com>
Wed, 31 Jul 2024 05:01:59 +0000 (05:01 +0000)
committerDr. Jaska <drjaska83@gmail.com>
Wed, 31 Jul 2024 05:01:59 +0000 (05:01 +0000)
qcsrc/common/command/generic.qc
qcsrc/common/command/generic.qh
qcsrc/lib/string.qh
qcsrc/menu/xonotic/maplist.qc
qcsrc/server/command/getreplies.qc
qcsrc/server/intermission.qc
qcsrc/server/intermission.qh
qcsrc/server/mapvoting.qc
xonotic-server.cfg

index 3ce25e26d71272da71edde5d46f32ae482c7d879..b176cd8117a47b942794996a87272c341f44d9e0 100644 (file)
@@ -229,6 +229,25 @@ GENERIC_COMMAND(dumpcommands, "Dump all commands on the program to <program>_cmd
        }
 }
 
+string maplist_shuffle(string input)
+{
+       int buf = buf_create();
+
+       int _cnt = 0;
+       FOREACH_WORD(input, true,
+       {
+               int _j = floor(random() * (_cnt + 1));
+               if(_j != _cnt)
+                       bufstr_set(buf, _cnt, bufstr_get(buf, _j));
+               bufstr_set(buf, _j, it);
+               ++_cnt;
+       });
+
+       string output = buf_implode(buf, " ");
+       buf_del(buf);
+       return substring(output, 1, -1);
+}
+
 void GenericCommand_maplist(int request, int argc)
 {
        switch(request)
@@ -293,7 +312,7 @@ void GenericCommand_maplist(int request, int argc)
 
                                case "shuffle": // randomly shuffle the maplist
                                {
-                                       cvar_set("g_maplist", shufflewords(cvar_string("g_maplist")));
+                                       cvar_set("g_maplist", maplist_shuffle(cvar_string("g_maplist")));
                                        return;
                                }
 
index 68aa0ae88af0d1b0b1deafc9eee5f46735da808a..5117b2c417ea744ae99ced36f23cdd7db188e726 100644 (file)
@@ -38,3 +38,5 @@ void Curl_URI_Get_Callback(int id, float status, string data);
 int curl_uri_get_pos;
 float curl_uri_get_exec[URI_GET_CURL_END - URI_GET_CURL + 1];
 string curl_uri_get_cvar[URI_GET_CURL_END - URI_GET_CURL + 1];
+
+string maplist_shuffle(string input);
index f3fbc6bc229138108113bb4a9a6cb6198a3aea57..6c542c8fc107c097eb7fe46d38f683d7e83f2bb0 100644 (file)
@@ -299,7 +299,7 @@ string swapwords(string str, float i, float j)
 {
        string s1, s2, s3, s4, s5;
        float si, ei, sj, ej, s0, en;
-       int n = tokenizebyseparator(str, " ");  // must match g_maplist processing in ShuffleMaplist and "shuffle"
+       int n = tokenizebyseparator(str, " ");
        si = argv_start_index(i);
        sj = argv_start_index(j);
        ei = argv_end_index(i);
index 89748e85c3647dda4ac36afc3c06e08e83f3a166..84e408196674a1d4f2eb9307da91c5d721dc150e 100644 (file)
@@ -261,8 +261,8 @@ void MapList_Add_All(entity btn, entity me)
        _MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, MapInfo_ForbiddenFlags(), 0); // all
        s = "";
        for(i = 0; i < MapInfo_count; ++i)
-               s = strcat(s, " ", MapInfo_BSPName_ByID(i));
-       cvar_set("g_maplist", substring(s, 1, strlen(s) - 1));
+               s = cons(s, MapInfo_BSPName_ByID(i));
+       cvar_set("g_maplist", s);
        me.refilter(me);
 }
 
index 1746c107ba526f3170aa2d09e99b4cc0e0d80991..3eba391bc57ac2b3f0c8b6ea91af9ef6e5e521c5 100644 (file)
@@ -232,21 +232,20 @@ string getladder()
 
 string getmaplist()
 {
-       string maplist = "", col;
-       int i, argc;
+       if(autocvar_g_maplist == "")
+               return "^7Map list is empty";
 
-       argc = tokenize_console(autocvar_g_maplist);
-       for (i = 0; i < argc; ++i)
+       string maplist = "";
+       int mapcount = 0;
+       FOREACH_WORD(autocvar_g_maplist, MapInfo_CheckMap(it),
        {
-               if (MapInfo_CheckMap(argv(i)))
-               {
-                       if (i % 2) col = "^2"; else col = "^3";
-                       maplist = sprintf("%s%s%s ", maplist, col, argv(i));
-               }
-       }
+               string col = (i % 2) ? "^2" : "^3";
+               maplist = cons(maplist, strcat(col, it));
+               mapcount += 1;
+       });
 
        MapInfo_ClearTemps();
-       return sprintf("^7Maps in list: %s\n", maplist);
+       return sprintf("^7Maps in list (%d): %s\n", mapcount, maplist);
 }
 
 const int LSMAPS_MAX = 250;
@@ -276,12 +275,12 @@ string getlsmaps()
                                if (i % 2) col = "^2"; else col = "^3";
                        }
 
-                       lsmaps = sprintf("%s%s%s ", lsmaps, col, MapInfo_Map_bspname);
+                       lsmaps = cons(lsmaps, strcat(col, MapInfo_Map_bspname));
                }
        }
 
        if(added > LSMAPS_MAX)
-               lsmaps = sprintf("%s^7(%d not listed)", lsmaps, added - LSMAPS_MAX);
+               lsmaps = sprintf("%s ^7(%d not listed)", lsmaps, added - LSMAPS_MAX);
 
        MapInfo_ClearTemps();
        return sprintf("^7Maps available (%d)%s: %s\n", added, (newmaps ? " (New maps have asterisks marked in blue)" : ""), lsmaps);
index 9749a95d2470b75912928a600162974a55b7afa0..7f11112c1133dd898123da259a89ecbecbef8e10 100644 (file)
@@ -32,7 +32,7 @@ int GetMaplistPosition()
        {
                if(idx < Map_Count)
                {
-                       if(map == argv(idx))
+                       if(map == bufstr_get(maplist_buffer, idx))
                        {
                                return idx;
                        }
@@ -41,7 +41,7 @@ int GetMaplistPosition()
 
        for(int pos = 0; pos < Map_Count; ++pos)
        {
-               if(map == argv(pos))
+               if(map == bufstr_get(maplist_buffer, pos))
                        return pos;
        }
 
@@ -99,9 +99,9 @@ bool MapHasRightSize(string map)
        return true;
 }
 
-string Map_Filename(int position)
+string Map_Filename(string m)
 {
-       return strcat("maps/", argv(position), ".bsp");
+       return strcat("maps/", m, ".bsp");
 }
 
 void Map_MarkAsRecent(string m)
@@ -111,31 +111,32 @@ void Map_MarkAsRecent(string m)
 
 bool Map_IsRecent(string m)
 {
+       if(autocvar_g_maplist_mostrecent == "")
+               return false;
        return strhasword(autocvar_g_maplist_mostrecent, m);
 }
 
-bool Map_Check(int position, float pass)
+bool Map_Check(string m, int pass)
 {
-       string map_next = argv(position);
        if(pass <= 1)
        {
-               if(Map_IsRecent(map_next))
+               if(Map_IsRecent(m))
                        return false;
        }
-       if(MapInfo_CheckMap(map_next))
+       if(MapInfo_CheckMap(m))
        {
                if(pass == 2)
                        return true;
                // MapInfo_Map_flags was set by MapInfo_CheckMap()
                if (MapInfo_Map_flags & MAPINFO_FLAG_DONOTWANT)
                        return false;
-               if(MapHasRightSize(map_next))
+               if(MapHasRightSize(m))
                        return true;
                return false;
        }
        else
        {
-               string filename = Map_Filename(position);
+               string filename = Map_Filename(m);
                LOG_DEBUG( "Couldn't select '", filename, "'..." );
        }
 
@@ -156,7 +157,7 @@ void Map_Goto_SetIndex(int position)
 {
        Map_Current = position;
        cvar_set("g_maplist_index", ftos(position));
-       Map_Goto_SetStr(argv(position));
+       Map_Goto_SetStr(bufstr_get(maplist_buffer, position));
 }
 
 void Map_Goto(float reinit)
@@ -169,28 +170,34 @@ void Map_Goto(float reinit)
 //   -2 = permanent failure
 int MaplistMethod_Iterate(void) // usual method
 {
-       int pass, i;
+       int attempts = 42; // skip advanced checks if this many maps fail
 
        LOG_TRACE("Trying MaplistMethod_Iterate");
 
-       for(pass = 1; pass <= 2; ++pass)
+       for(int i = 1; i < Map_Count; ++i)
        {
-               for(i = 1; i < Map_Count; ++i)
+               int mapindex = (i + Map_Current) % Map_Count;
+               if(Map_Check(bufstr_get(maplist_buffer, mapindex), 1))
+                       return mapindex;
+
+               attempts -= 1;
+               if(attempts <= 0)
                {
-                       int mapindex;
-                       mapindex = (i + Map_Current) % Map_Count;
-                       if(Map_Check(mapindex, pass))
-                               return mapindex;
+                       LOG_DEBUG("MaplistMethod_Iterate: Couldn't find a suitable map, falling back to next valid");
+                       break;
                }
        }
-       return -1;
+
+       // failing that, just accept the next map in the list
+       int mapindex = (1 + Map_Current) % Map_Count;
+       return mapindex;
 }
 
 int MaplistMethod_Repeat(void) // fallback method
 {
        LOG_TRACE("Trying MaplistMethod_Repeat");
 
-       if(Map_Check(Map_Current, 2))
+       if(Map_Check(bufstr_get(maplist_buffer, Map_Current), 2))
                return Map_Current;
        return -2;
 }
@@ -207,93 +214,94 @@ int MaplistMethod_Random(void) // random map selection
        {
                int mapindex;
                mapindex = (Map_Current + floor(random() * (Map_Count - 1) + 1)) % Map_Count; // any OTHER map
-               if(Map_Check(mapindex, 1))
+               if(Map_Check(bufstr_get(maplist_buffer, mapindex), 1))
                        return mapindex;
        }
        return -1;
 }
 
-// the exponent sets a bias on the map selection:
-// the higher the exponent, the less likely "shortly repeated" same maps are
-int MaplistMethod_Shuffle(float exponent) // more clever shuffling
+// NOTE: call Maplist_Close when you're done!
+int Maplist_Init(void)
 {
-       float i, j, imax, insertpos;
-
-       LOG_TRACE("Trying MaplistMethod_Shuffle");
-
-       imax = 42;
-
-       for(i = 0; i <= imax; ++i)
+       bool have_maps = false;
+       if(autocvar_g_maplist != "")
        {
-               string newlist;
-
-               // now reinsert this at another position
-               insertpos = (random() ** (1 / exponent));       // ]0, 1]
-               insertpos = insertpos * (Map_Count - 1);       // ]0, Map_Count - 1]
-               insertpos = ceil(insertpos) + 1;               // {2, 3, 4, ..., Map_Count}
-               LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos));
-
-               // insert the current map there
-               newlist = "";
-               for(j = 1; j < insertpos; ) // i == 1: no loop, will be inserted as first; however, i == 1 has been excluded above
+               // make sure there is at least one playable map in the list
+               bool needtrim = false;
+               FOREACH_WORD(autocvar_g_maplist, true,
                {
-                       if (j + 2 < insertpos)
-                               newlist = strcat(newlist, " ", argv(j++), " ", argv(j++), " ", argv(j++));
-                       else
-                               newlist = strcat(newlist, " ", argv(j++));
-               }
-               newlist = strcat(newlist, " ", argv(0));       // now insert the just selected map
-               for(j = insertpos; j < Map_Count; ) // i == Map_Count: no loop, has just been inserted as last
+                       if(!fexists(Map_Filename(it)))
+                       {
+                               needtrim = true;
+                               if(have_maps)
+                                       break;
+                               continue;
+                       }
+                       if(have_maps || !Map_Check(it, 2))
+                               continue;
+                       have_maps = true;
+                       if(needtrim)
+                               break;
+               });
+
+               // additionally trim any non-existent maps
+               if(needtrim)
                {
-                       if (j + 2 < Map_Count)
-                               newlist = strcat(newlist, " ", argv(j++), " ", argv(j++), " ", argv(j++));
-                       else
-                               newlist = strcat(newlist, " ", argv(j++));
+                       int trimmedmaps = 0;
+                       string newmaplist = "";
+                       FOREACH_WORD(autocvar_g_maplist, true,
+                       {
+                               if(!fexists(Map_Filename(it)))
+                               {
+                                       trimmedmaps += 1;
+                                       continue;
+                               }
+                               newmaplist = cons(newmaplist, it);
+                       });
+                       cvar_set("g_maplist", newmaplist);
+                       LOG_DEBUGF("Maplist_Init: trimmed %d missing maps from the list", trimmedmaps);
                }
-               newlist = substring(newlist, 1, strlen(newlist) - 1);
-               cvar_set("g_maplist", newlist);
-               Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
-
-               // NOTE: the selected map has just been inserted at (insertpos-1)th position
-               if (Map_Check(insertpos - 1, 1))
-                       return insertpos - 1;
-       }
-       return -1;
-}
-
-int Maplist_Init(void)
-{
-       int i, available_maps = 0;
-       Map_Count = 0;
-       if(autocvar_g_maplist != "")
-       {
-               Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
-               for (i = 0; i < Map_Count; ++i)
-                       if (Map_Check(i, 2))
-                               ++available_maps;
        }
 
-       if (!available_maps)
+       if (!have_maps)
        {
                bprint( "Maplist contains no usable maps!  Resetting it to default map list.\n" );
                cvar_set("g_maplist", MapInfo_ListAllowedMaps(MapInfo_CurrentGametype(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
                if(!server_is_dedicated)
                        localcmd("\nmenu_cmd sync\n");
-               Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
-               for (i = 0; i < Map_Count; ++i)
-                       if (Map_Check(i, 2))
-                               ++available_maps;
        }
 
+       maplist_buffer = buf_create();
+
+       int _cnt = 0;
+       FOREACH_WORD(autocvar_g_maplist, Map_Check(it, 2),
+       {
+               // NOTE: inlined maplist_shuffle function to avoid a second buffer, keep both in sync
+               if(autocvar_g_maplist_shuffle)
+               {
+                       int _j = floor(random() * (_cnt + 1));
+                       if(_j != _cnt)
+                               bufstr_set(maplist_buffer, _cnt, bufstr_get(maplist_buffer, _j));
+                       bufstr_set(maplist_buffer, _j, it);
+                       ++_cnt;
+               }
+               else
+                       bufstr_set(maplist_buffer, i, it);
+       });
+
+       Map_Count = buf_getsize(maplist_buffer);
+
        if(Map_Count == 0)
                error("empty maplist, cannot select a new map");
 
        Map_Current = bound(0, GetMaplistPosition(), Map_Count - 1);
 
-       if(autocvar_g_maplist_shuffle)
-               cvar_set("g_maplist", shufflewords(autocvar_g_maplist));
+       return Map_Count;
+}
 
-       return available_maps;
+void Maplist_Close()
+{
+       buf_del(maplist_buffer);
 }
 
 // NOTE: call Maplist_Init() before making GetNextMap() call(s)
@@ -301,9 +309,6 @@ string GetNextMap(void)
 {
        int nextMap = -1;
 
-       if(nextMap == -1 && autocvar_g_maplist_shuffle > 0)
-               nextMap = MaplistMethod_Shuffle(autocvar_g_maplist_shuffle + 1);
-
        if(nextMap == -1 && autocvar_g_maplist_selectrandom)
                nextMap = MaplistMethod_Random();
 
@@ -394,6 +399,7 @@ void GotoNextMap(float reinit)
 
        Maplist_Init();
        string nextMap = GetNextMap();
+       Maplist_Close();
        if(nextMap == "")
                error("Everything is broken - cannot find a next map. Please report this to the developers.");
        Map_Goto(reinit);
index 67413147c18bc3e231e57619dced90864cc1521e..44a5a2da8384e09b58b0f34928afa2893299ea73 100644 (file)
@@ -10,6 +10,8 @@ bool intermission_running;
 float intermission_exittime;
 bool alreadychangedlevel;
 
+int maplist_buffer;
+
 string GetGametype();
 
 string GetMapname();
@@ -22,9 +24,10 @@ void GotoNextMap(float reinit);
 
 bool Map_IsRecent(string m);
 
-bool Map_Check(int position, float pass);
+bool Map_Check(string m, float pass);
 
 int Maplist_Init(void);
+void Maplist_Close();
 string GetNextMap(void);
 
 void Map_Goto_SetStr(string nextmapname);
index 8fa3c54f0548105c6153f92e9fee45d25aadb75c..b52f4518d978c3084504a19c9e12b55dccdf55df 100644 (file)
@@ -214,6 +214,8 @@ void MapVote_AddVotableMaps(int nmax, int smax)
 
        for (int i = 0; i < max_attempts && mapvote_count < nmax; ++i)
                MapVote_AddVotable(GetNextMap(), false);
+
+       Maplist_Close();
 }
 
 string voted_gametype_string;
index a4fa5d3b7422dcc945b6ba11897aa4c0dcd32149..d7304eca0ab8967ad22b79d8d54239552d376ef7 100644 (file)
@@ -227,7 +227,7 @@ set g_maplist_mostrecent "" "contains the name of the maps that were most recent
 set g_maplist_mostrecent_count 3 "number of most recent maps that are blocked from being played again"
 set g_maplist_index 0 "this is used internally for saving position in maplist cycle"
 set g_maplist_selectrandom 0 "if 1, a random map will be chosen as next map - DEPRECATED in favor of g_maplist_shuffle"
-set g_maplist_shuffle 1 "1: shuffling method which avoids playing the same maps in short succession by taking out the first element and inserting it into g_maplist with a bias to the end of the list. -1: a simpler shuffling method which should be adequate if g_maplist_mostrecent_count is large enough."
+set g_maplist_shuffle 1 "shuffles the order of maps during selection to ensure random maps are chosen"
 set g_maplist_check_waypoints 0 "when 1, maps are skipped if there currently are bots, but the map has no waypoints"
 set g_maplist_ignore_sizes 0 "when 1, all maps are shown in the map list regardless of player count"
 set g_maplist_sizes_count_maxplayers 1 "check the player limit when getting the player count so forced spectators don't affect the size restrictions"