mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 04:30:44 +01:00
## About The Pull Request I normally really don't like doing this but because this is so in the weeds this is an alt pr. Alt of #96366, I really don't want to add overhead to process_cell(). The issue here is when we request a reservation the turfs are empty()'d which clears them out and places them in SSair's adjacent_turf recalc queue (which will eventually cause an activation to clear out excited groups). We then immediately start maploading, which creates new, uninitialized turfs (that remain in the queue because turf refs are based off position instead of the actual datum). These unintialized turfs are able to interact with the other, yet to be new()'d over turfs in the recalc queue. This causes runtimes. Later, while we initialize the turfs we loaded, we also have interactions between initialized and uninitialized turfs, which also causes runtimes. The solution here is to prevent atmos processing in a loading template. I do that here by first clearing out all relevant turfs from atmos_adjacent_turf lists and deactivating them. We also have to clear them from the recalc queue to prevent "fixing" this problem. Then, after initialize is complete, we requeue them for recalcing, to restore them to a workable state. The underlying problem here is the logic of maploading is kind of designed to run before everything else has initialized. It's something we can dodge, but it will also show up in things that start processing on initialize and expect their turf to immediately be ready for them. S a bit of a mess. Of note, it's technically possible for a turf to be initialized, activate, and then attempt to process with an unitialized neighbor with this solution. My gut fix for this would be using blocks_air to prevent adjacent turfs from being recalculated until after InitializeAtoms is finished, but that breaks turf/open/Initialize where we setup gasmixtures, and I don't want to duplicate that code. So we'll just bite the small risk of runtimes, they won't actually break anything after all just make a bit of noise. ## Why It's Good For The Game Closes #89649 This adds about 110ms of cost to loading the nukie station on my machine (I used line by line macros around the two for loops), which is significant but not huge, and it's cost that can be safely CHECK_TICK'd. I think the cost of actually loading outweighs that significantly enough (6.6s on my machine) that it's fine. I prefer this solution to the alternative of adding guard checks to process_cell because I do not want to add overhead to atmos code, and also I don't like the idea that we cannot reliably prevent this (shuttle code handles it effectively using blocks_air, as an example). Also, it doesn't really resolve the uninitialized bit of this problem, which isn't... good. Technically changes the math on share() too)
157 lines
5.4 KiB
Plaintext
157 lines
5.4 KiB
Plaintext
|
|
/**
|
|
* Datum used to designate certain areas that do not need to exist nor be loaded at world start
|
|
* but do want to be loaded under certain circumstances. Use this for stuff like the nukie base or wizden, aka stuff that only matters when their antag is rolled.
|
|
*/
|
|
/datum/lazy_template
|
|
/// If this is true each load will increment an index keyed to the type and it will load [map_name]_[index]
|
|
var/list/datum/turf_reservation/reservations = list()
|
|
var/uses_multiple_allocations = FALSE
|
|
/// Key to identify this template - used in caching
|
|
var/key
|
|
/// Directory of maps to prefix to the filename
|
|
var/map_dir = "_maps/templates/lazy_templates"
|
|
/// The filename (without extension) of the map to load
|
|
var/map_name
|
|
/// place_on_top: Whether to use /turf/proc/PlaceOnTop rather than /turf/proc/ChangeTurf
|
|
var/place_on_top = FALSE
|
|
/// type of turf reservation
|
|
var/turf_reservation_type = /datum/turf_reservation
|
|
|
|
/datum/lazy_template/New()
|
|
reservations = list()
|
|
..()
|
|
|
|
/datum/lazy_template/Destroy(force)
|
|
if(!force)
|
|
stack_trace("Something is trying to delete [type]")
|
|
return QDEL_HINT_LETMELIVE
|
|
|
|
QDEL_LIST(reservations)
|
|
GLOB.lazy_templates -= key
|
|
return ..()
|
|
|
|
/**
|
|
* Does the grunt work of loading the template.
|
|
*/
|
|
/datum/lazy_template/proc/lazy_load()
|
|
RETURN_TYPE(/turf)
|
|
// This is a static assosciative list that is used to ensure maps that have variations are correctly varied when spawned
|
|
// I want to make it to where you can make a range and it'll randomly pick'n'take from the available versions at random
|
|
// But that can be done later when I have the time
|
|
var/static/list/multiple_allocation_hash = list()
|
|
|
|
var/load_path = "[map_dir]/[map_name].dmm"
|
|
if(uses_multiple_allocations)
|
|
var/times = multiple_allocation_hash[key] || 0
|
|
times += 1
|
|
multiple_allocation_hash[key] = times
|
|
load_path = "[map_dir]/[map_name]_[times].dmm"
|
|
|
|
if(!load_path || !fexists(load_path))
|
|
CRASH("lazy template [type] has an invalid load_path: '[load_path]', check directory and map name!")
|
|
|
|
var/datum/parsed_map/parsed_template = load_map(
|
|
file(load_path),
|
|
measure_only = TRUE,
|
|
)
|
|
if(isnull(parsed_template.parsed_bounds))
|
|
CRASH("Failed to cache lazy template for loading: '[key]'")
|
|
|
|
var/width = parsed_template.parsed_bounds[MAP_MAXX] - parsed_template.parsed_bounds[MAP_MINX] + 1
|
|
var/height = parsed_template.parsed_bounds[MAP_MAXY] - parsed_template.parsed_bounds[MAP_MINY] + 1
|
|
var/datum/turf_reservation/reservation = SSmapping.request_turf_block_reservation(
|
|
width,
|
|
height,
|
|
parsed_template.parsed_bounds[MAP_MAXZ],
|
|
reservation_type = turf_reservation_type,
|
|
)
|
|
if(!reservation)
|
|
CRASH("Failed to reserve a block for lazy template: '[key]'")
|
|
|
|
// lists kept for overall loading
|
|
var/list/loaded_atom_movables = list()
|
|
var/list/loaded_turfs = list()
|
|
var/list/loaded_areas = list()
|
|
|
|
var/list/obj/structure/cable/loaded_cables = list()
|
|
var/list/obj/machinery/atmospherics/loaded_atmospherics = list()
|
|
|
|
for(var/z_idx in parsed_template.parsed_bounds[MAP_MAXZ] to 1 step -1)
|
|
var/turf/bottom_left = reservation.bottom_left_turfs[z_idx]
|
|
var/turf/top_right = reservation.top_right_turfs[z_idx]
|
|
|
|
// Make our turfs dead to atmos
|
|
// Cache for sonic speed
|
|
var/list/to_rebuild = SSair.adjacent_rebuild
|
|
for(var/turf/contained_turf as anything in block(bottom_left, top_right))
|
|
SSair.remove_from_active(contained_turf)
|
|
to_rebuild -= contained_turf
|
|
for(var/turf/sub_turf as anything in contained_turf.atmos_adjacent_turfs)
|
|
sub_turf.atmos_adjacent_turfs?.Remove(contained_turf)
|
|
contained_turf.atmos_adjacent_turfs?.Cut()
|
|
CHECK_TICK
|
|
|
|
load_map(
|
|
file(load_path),
|
|
bottom_left.x,
|
|
bottom_left.y,
|
|
bottom_left.z,
|
|
z_upper = z_idx,
|
|
z_lower = z_idx,
|
|
place_on_top = place_on_top,
|
|
)
|
|
for(var/turf/turf as anything in block(bottom_left, top_right))
|
|
loaded_turfs += turf
|
|
loaded_areas |= get_area(turf)
|
|
|
|
// atoms can actually be in the contents of two or more turfs based on its icon/bound size
|
|
// see https://www.byond.com/docs/ref/index.html#/atom/var/contents
|
|
for(var/thing in (turf.get_all_contents() - turf))
|
|
if(istype(thing, /obj/structure/cable))
|
|
loaded_cables += thing
|
|
else if(istype(thing, /obj/machinery/atmospherics))
|
|
loaded_atmospherics += thing
|
|
loaded_atom_movables |= thing
|
|
|
|
SSatoms.InitializeAtoms(loaded_areas + loaded_atom_movables + loaded_turfs)
|
|
for(var/turf/turf as anything in loaded_turfs)
|
|
CALCULATE_ADJACENT_TURFS(turf, NORMAL_TURF)
|
|
CHECK_TICK
|
|
|
|
SSlighting.setup_static_lighting_if_needed(loaded_turfs)
|
|
SSmachines.setup_template_powernets(loaded_cables)
|
|
SSair.setup_template_machinery(loaded_atmospherics)
|
|
|
|
SEND_SIGNAL(src, COMSIG_LAZY_TEMPLATE_LOADED, loaded_atom_movables, loaded_turfs, loaded_areas)
|
|
reservations += reservation
|
|
return reservation
|
|
|
|
/datum/lazy_template/nukie_elevator
|
|
key = LAZY_TEMPLATE_KEY_NUKIEELEVATOR
|
|
map_name = "nukie_elevator"
|
|
|
|
/datum/lazy_template/nukie_base
|
|
key = LAZY_TEMPLATE_KEY_NUKIEBASE
|
|
map_name = "nukie_base"
|
|
|
|
/datum/lazy_template/wizard_dem
|
|
key = LAZY_TEMPLATE_KEY_WIZARDDEN
|
|
map_name = "wizard_den"
|
|
|
|
/datum/lazy_template/ninja_holding_facility
|
|
key = LAZY_TEMPLATE_KEY_NINJA_HOLDING_FACILITY
|
|
map_name = "ninja_den"
|
|
|
|
/datum/lazy_template/abductor_ship
|
|
key = LAZY_TEMPLATE_KEY_ABDUCTOR_SHIPS
|
|
map_name = "abductor_ships"
|
|
|
|
/datum/lazy_template/heretic_sacrifice_room
|
|
key = LAZY_TEMPLATE_KEY_HERETIC_SACRIFICE
|
|
map_name = "heretic_sacrifice"
|
|
|
|
/datum/lazy_template/voidwalker_void
|
|
key = LAZY_TEMPLATE_KEY_VOIDWALKER_VOID
|
|
map_name = "voidwalker_void"
|