Backports several SSmapping improvements (#17208)

* Optimizes SSmapping

* Turfs inside area are stored

https://github.com/tgstation/tgstation/pull/70966

* Add async reserving of turfs

* Fix bug

* Fix shuttle init
This commit is contained in:
Ling
2022-12-31 16:41:47 +01:00
committed by GitHub
parent 5a5be9ef89
commit 17c5a68c33
48 changed files with 1390 additions and 644 deletions

View File

@@ -351,7 +351,6 @@ SUBSYSTEM_DEF(air)
queued_for_activation.Cut()
/datum/controller/subsystem/air/proc/setup_allturfs()
var/list/turfs_to_init = block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz))
var/list/active_turfs = src.active_turfs
var/times_fired = ++src.times_fired
@@ -359,7 +358,7 @@ SUBSYSTEM_DEF(air)
// one-by-one, and Initalize_Atmos only ever adds `src` back in.
active_turfs.Cut()
for(var/thing in turfs_to_init)
for(var/thing in ALL_TURFS())
var/turf/T = thing
if (T.blocks_air)
continue

View File

@@ -0,0 +1,55 @@
#define ALLOWED_LOOSE_TURFS 500
/**
* Responsible for managing the sizes of area.contained_turfs and area.turfs_to_uncontain
* These lists do not check for duplicates, which is fine, but it also means they can balloon in size over time
* as a consequence of repeated changes in area in a space
* They additionally may not always resolve often enough to avoid memory leaks
* This is annoying, so lets keep an eye on them and cut them down to size if needed
*/
SUBSYSTEM_DEF(area_contents)
name = "Area Contents"
flags = SS_NO_INIT
runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT
var/list/currentrun
var/list/area/marked_for_clearing = list()
/datum/controller/subsystem/area_contents/stat_entry(msg)
var/total_clearing_from = 0
var/total_to_clear = 0
for(var/area/to_clear as anything in marked_for_clearing)
total_to_clear += length(to_clear.turfs_to_uncontain)
total_clearing_from += length(to_clear.contained_turfs)
msg = "A:[length(currentrun)] MR:[length(marked_for_clearing)] TC:[total_to_clear] CF:[total_clearing_from]"
return ..()
/datum/controller/subsystem/area_contents/fire(resumed)
if(!resumed)
currentrun = GLOB.areas.Copy()
while(length(currentrun))
var/area/test = currentrun[length(currentrun)]
if(length(test.turfs_to_uncontain) > ALLOWED_LOOSE_TURFS)
marked_for_clearing |= test
currentrun.len--
if(MC_TICK_CHECK)
return
// Alright, if we've done a scan on all our areas, it's time to knock the existing ones down to size
while(length(marked_for_clearing))
var/area/clear = marked_for_clearing[length(marked_for_clearing)]
// The operation of cutting large lists can be expensive
// It scales almost directly with the size of the list we're cutting with
// Because of this, we're gonna stick to cutting 1 entry at a time
// There's no reason to batch it I promise, this is faster. No overtime too
var/amount_cut = 0
var/list/cut_from = clear.turfs_to_uncontain
for(amount_cut in 1 to length(cut_from))
clear.contained_turfs -= cut_from[amount_cut]
if(MC_TICK_CHECK)
cut_from.Cut(1, amount_cut + 1)
return
clear.turfs_to_uncontain = list()
marked_for_clearing.len--

View File

@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
flags = SS_NO_FIRE
runlevels = ALL
loading_points = 11 SECONDS // Yogs -- loading times
@@ -32,6 +32,8 @@ SUBSYSTEM_DEF(mapping)
var/list/turf/unused_turfs = list() //Not actually unused turfs they're unused but reserved for use for whatever requests them. "[zlevel_of_turf]" = list(turfs)
var/list/datum/turf_reservations //list of turf reservations
var/list/used_turfs = list() //list of turf = datum/turf_reservation
/// List of lists of turfs to reserve
var/list/lists_to_reserve = list()
var/clearing_reserved_turfs = FALSE
@@ -63,7 +65,7 @@ SUBSYSTEM_DEF(mapping)
to_chat(world, span_boldannounce("Unable to load next or default map config, defaulting to Box Station"))
config = old_config
loadWorld()
repopulate_sorted_areas()
require_area_resort()
process_teleport_locs() //Sets up the wizard teleport locations
preloadTemplates()
run_map_generation()
@@ -91,20 +93,20 @@ SUBSYSTEM_DEF(mapping)
loading_ruins = TRUE
var/list/lava_ruins = levels_by_trait(ZTRAIT_LAVA_RUINS)
if (lava_ruins.len)
seedRuins(lava_ruins, CONFIG_GET(number/lavaland_budget), list(/area/lavaland/surface/outdoors/unexplored), lava_ruins_templates)
seedRuins(lava_ruins, CONFIG_GET(number/lavaland_budget), list(/area/lavaland/surface/outdoors/unexplored), lava_ruins_templates, clear_below = TRUE)
for (var/lava_z in lava_ruins)
spawn_rivers(lava_z)
var/list/ice_ruins = levels_by_trait(ZTRAIT_ICE_RUINS)
if (ice_ruins.len)
// needs to be whitelisted for underground too so place_below ruins work
seedRuins(ice_ruins, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/surface/outdoors/unexplored, /area/icemoon/underground/unexplored), ice_ruins_templates)
seedRuins(ice_ruins, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/surface/outdoors/unexplored, /area/icemoon/underground/unexplored), ice_ruins_templates, clear_below = TRUE)
for(var/ice_z in ice_ruins)
spawn_rivers(ice_z, 4, /turf/open/openspace/icemoon, /area/icemoon/surface/outdoors/unexplored)
var/list/ice_ruins_underground = levels_by_trait(ZTRAIT_ICE_RUINS_UNDERGROUND)
if (ice_ruins_underground.len)
seedRuins(ice_ruins_underground, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/underground/unexplored), ice_ruins_underground_templates)
seedRuins(ice_ruins_underground, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/underground/unexplored), ice_ruins_underground_templates, clear_below = TRUE)
for(var/ice_z in ice_ruins_underground)
spawn_rivers(ice_z, 4, /turf/open/lava/plasma/ice_moon, /area/icemoon/underground/unexplored)
@@ -134,7 +136,7 @@ SUBSYSTEM_DEF(mapping)
PM.initTemplateBounds()
// Add the transit level
transit = add_new_zlevel("Transit/Reserved", list(ZTRAIT_RESERVED = TRUE))
repopulate_sorted_areas()
require_area_resort()
// Set up Z-level transitions.
setup_map_transitions()
generate_station_area_list()
@@ -143,6 +145,35 @@ SUBSYSTEM_DEF(mapping)
build_minimaps()
return SS_INIT_SUCCESS
/datum/controller/subsystem/mapping/fire(resumed)
// Cache for sonic speed
var/list/unused_turfs = src.unused_turfs
var/list/world_contents = GLOB.areas_by_type[world.area].contents
var/list/world_turf_contents = GLOB.areas_by_type[world.area].contained_turfs
var/list/lists_to_reserve = src.lists_to_reserve
var/index = 0
while(index < length(lists_to_reserve))
var/list/packet = lists_to_reserve[index + 1]
var/packetlen = length(packet)
while(packetlen)
if(MC_TICK_CHECK)
lists_to_reserve.Cut(1, index)
return
var/turf/T = packet[packetlen]
T.empty(RESERVED_TURF_TYPE, RESERVED_TURF_TYPE, null, TRUE)
LAZYINITLIST(unused_turfs["[T.z]"])
unused_turfs["[T.z]"] |= T
var/area/old_area = T.loc
old_area.turfs_to_uncontain += T
T.flags_1 |= UNUSED_RESERVATION_TURF_1
world_contents += T
world_turf_contents += T
packet.len--
packetlen = length(packet)
index++
lists_to_reserve.Cut(1, index)
/datum/controller/subsystem/mapping/proc/wipe_reservations(wipe_safety_delay = 100)
if(clearing_reserved_turfs || !initialized) //in either case this is just not needed.
return
@@ -250,13 +281,13 @@ SUBSYSTEM_DEF(mapping)
var/start_z = world.maxz + 1
var/i = 0
for (var/level in traits)
add_new_zlevel("[name][i ? " [i + 1]" : ""]", level)
add_new_zlevel("[name][i ? " [i + 1]" : ""]", level, contain_turfs = FALSE)
++i
// load the maps
for (var/P in parsed_maps)
var/datum/parsed_map/pm = P
if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE))
if (!pm.load(1, 1, start_z + parsed_maps[P], no_changeturf = TRUE, new_z = TRUE))
errorList |= pm.original_path
if(!silent)
INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!")
@@ -310,7 +341,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
/datum/controller/subsystem/mapping/proc/generate_station_area_list()
var/list/station_areas_blacklist = typecacheof(list(/area/space, /area/mine, /area/ruin, /area/asteroid/nearstation))
for(var/area/A in world)
for(var/area/A in GLOB.areas)
if (is_type_in_typecache(A, station_areas_blacklist))
continue
if (!A.contents.len || !A.unique)
@@ -323,7 +354,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
log_world("ERROR: Station areas list failed to generate!")
/datum/controller/subsystem/mapping/proc/run_map_generation()
for(var/area/A in world)
for(var/area/A as anything in GLOB.areas)
A.RunGeneration()
/datum/controller/subsystem/mapping/proc/maprotate()
@@ -549,15 +580,12 @@ GLOBAL_LIST_EMPTY(the_station_areas)
unused_turfs["[i]"] = block
clearing_reserved_turfs = FALSE
/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs)
for(var/i in turfs)
var/turf/T = i
T.empty(RESERVED_TURF_TYPE, RESERVED_TURF_TYPE, null, TRUE)
LAZYINITLIST(unused_turfs["[T.z]"])
unused_turfs["[T.z]"] |= T
T.flags_1 |= UNUSED_RESERVATION_TURF_1
GLOB.areas_by_type[world.area].contents += T
CHECK_TICK
/// Schedules a group of turfs to be handed back to the reservation system's control
/// If await is true, will sleep until the turfs are finished work
/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs, await = FALSE)
lists_to_reserve += list(turfs)
if(await)
UNTIL(!length(turfs))
//DO NOT CALL THIS PROC DIRECTLY, CALL wipe_reservations().
/datum/controller/subsystem/mapping/proc/do_wipe_turf_reservations()
@@ -575,7 +603,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
clearing |= used_turfs //used turfs is an associative list, BUT, reserve_turfs() can still handle it. If the code above works properly, this won't even be needed as the turfs would be freed already.
unused_turfs.Cut()
used_turfs.Cut()
reserve_turfs(clearing)
reserve_turfs(clearing, await = TRUE)
/datum/controller/subsystem/mapping/proc/build_minimaps()
to_chat(world, span_boldannounce("Building minimaps..."))
@@ -586,3 +614,15 @@ GLOBAL_LIST_EMPTY(the_station_areas)
for(var/B in areas)
var/area/A = B
A.reg_in_areas_in_z()
/datum/controller/subsystem/mapping/proc/build_area_turfs(z_level, space_guaranteed)
// If we know this is filled with default tiles, we can use the default area
// Faster
if(space_guaranteed)
var/area/global_area = GLOB.areas_by_type[world.area]
global_area.contained_turfs += Z_TURFS(z_level)
return
for(var/turf/to_contain as anything in Z_TURFS(z_level))
var/area/our_area = to_contain.loc
our_area.contained_turfs += to_contain

View File

@@ -516,9 +516,12 @@ SUBSYSTEM_DEF(shuttle)
var/turf/midpoint = locate(transit_x, transit_y, bottomleft.z)
if(!midpoint)
return FALSE
var/area/old_area = midpoint.loc
old_area.turfs_to_uncontain += proposal.reserved_turfs
var/area/shuttle/transit/A = new()
A.parallax_movedir = travel_dir
A.contents = proposal.reserved_turfs
A.contained_turfs = proposal.reserved_turfs
var/obj/docking_port/stationary/transit/new_transit_dock = new(midpoint)
new_transit_dock.reserved_area = proposal
new_transit_dock.name = "Transit for [M.id]/[M.name]"