Optimized vine spawning (#93648)

## About The Pull Request

Tin, the current algorithm is very greedy and can call Enter() on
thousands of turfs potentially depending on how your map is set up. This
really isn't necessary and leads to spess lag and sometimes spess
crashes.

Instead of doing that we can test a smaller random subset of tiles with
Enter() and achieve basically the same results.

This basically just puts a safe bound on how many Enter() calls we can
do.

<img width="1307" height="739" alt="dreamseeker_6nE6Hjad7B"
src="https://github.com/user-attachments/assets/3405ee8f-1af8-4dce-b253-1fcbf47412ea"
/>

## Why It's Good For The Game

Less spess lag

## Changelog

🆑
code: optimizes space vine spawning to create less lag.
/🆑

---------

Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
This commit is contained in:
Bloop
2025-10-30 05:06:37 -04:00
committed by GitHub
parent 761f830342
commit e1eacad24e
2 changed files with 38 additions and 9 deletions

View File

@@ -541,6 +541,27 @@
. = pick_weight(list_to_pick)
list_to_pick[.]--
/**
* Picks n items from a list. The same index will not be chosen more than once.
* e.g. pick_n(list_of_stuff, 10) would return a list of 10 items from the list, chosen randomly.
*/
/proc/pick_n(list/list_to_pick, n)
var/list_to_pick_length = length(list_to_pick)
if(!islist(list_to_pick) || !list_to_pick_length || n <= 0)
return list()
/// The final list that gets returned
var/list/result
/// length of our list_to_pick
n = min(n, list_to_pick_length)
// Shuffle and slice the first n indices
var/list/copy_to_shuffle = list_to_pick.Copy()
shuffle(copy_to_shuffle)
result = copy_to_shuffle.Copy(1, n + 1)
return result
/**
* Given a list, return a copy where values without defined weights are given weight 1.
* For example, fill_with_ones(list(A, B=2, C)) = list(A=1, B=2, C=1)

View File

@@ -29,23 +29,31 @@
var/production
/datum/round_event/spacevine/start()
var/list/turfs = list() //list of all the empty floor turfs in the hallway areas
var/list/final_turf_candidates = list() // final list of eligible empty floor turfs in the hallway areas that can be chosen
if(override_turf)
turfs += override_turf
final_turf_candidates += override_turf
else
var/obj/structure/spacevine/vine = new()
for(var/area/station/hallway/area in GLOB.areas)
var/list/floor_candidates = list()
for(var/area/station/hallway/area in shuffle(GLOB.areas.Copy()))
for(var/turf/open/floor in area.get_turfs_from_all_zlevels())
if(!isopenspaceturf(floor) && floor.Enter(vine))
turfs += floor
if(isopenspaceturf(floor))
continue
floor_candidates += floor
// Enter() is expensive to call on potentially hundreds to thousands of turfs at once and can even lead to server crashes.
// We can pick() a subset instead and get close enough results at a fraction of the cost.
var/turfs_to_test = 100
var/list/sampled_floor_candidates = pick_n(floor_candidates, min(turfs_to_test, length(floor_candidates))) // results in at most 100 calls of Enter(), a reasonable amount while still feeling random.
for(var/turf/open/floor as anything in sampled_floor_candidates)
if(floor.Enter(vine))
final_turf_candidates += floor
qdel(vine)
if(length(turfs)) //Pick a turf to spawn at if we can
var/turf/floor = pick(turfs)
if(length(final_turf_candidates)) //Pick a turf to spawn at if we can
var/turf/floor = pick(final_turf_candidates)
var/list/selected_mutations = list()
if(mutations_overridden == FALSE)