Files
Bubberstation/code/modules/mapping/map_template.dm
Kylerace c7a399ec4a Fixes bug with atoms (including new players!!!!) not spawned from the holodeck being deleted by it if they happened to be initialized at the same time (#57510)
About The Pull Request

Fixes #57446
yeah not my best moment, holodeck currently sets SSatoms to add every call to InitAtom() to a list and then give it back to the holodeck console (it actually goes through map_template/holodeck to do it but whatever). However it turns out atom/New() calls InitAtom too, so if an atom is created while SSatoms is still creating the list to give to the holodeck then that atom is added to the list regardless of whether or not its actually from the holodeck template.

Now theres an extra argument to InitAtom that tells it whether its spawned directly from a map template (ie, its part of the input list of uninitialized atoms that InitializeAtoms was given) or otherwise the output list that the holodeck uses is populated by calling GetAllContents on all atom/movables spawned directly from the template.

also renamed some vars in initTemplateBounds because it was hard to reason what it was doing and made it use as anything

also note that loading a map template with returns_created_atoms = TRUE will no longer track atoms that arent in the map file but are spawned directly onto a turf, currently nothing does this with the holodeck (which is the only map template that has this feature)

by the way this bug is my fault
Why It's Good For The Game

incredibly incredibly unlucky new players dont deserve to be deleted just because they didnt spawn in the holodeck
Changelog

🆑
fix: the holodeck is no longer so powerful that it can destroy anything and everything that dares to start existing while it's busy loading programs
/🆑
2021-03-20 11:56:35 +13:00

187 lines
5.9 KiB
Plaintext

/datum/map_template
var/name = "Default Template Name"
var/width = 0
var/height = 0
var/mappath = null
var/loaded = 0 // Times loaded this round
var/datum/parsed_map/cached_map
var/keep_cached_map = FALSE
var/station_id = null // used to override the root id when generating
///if true, turfs loaded from this template are placed on top of the turfs already there, defaults to TRUE
var/should_place_on_top = TRUE
///if true, creates a list of all atoms created by this template loading, defaults to FALSE
var/returns_created_atoms = FALSE
///the list of atoms created by this template being loaded, only populated if returns_created_atoms is TRUE
var/list/created_atoms = list()
//make sure this list is accounted for/cleared if you request it from ssatoms!
/datum/map_template/New(path = null, rename = null, cache = FALSE)
if(path)
mappath = path
if(mappath)
preload_size(mappath, cache)
if(rename)
name = rename
/datum/map_template/proc/preload_size(path, cache = FALSE)
var/datum/parsed_map/parsed = new(file(path))
var/bounds = parsed?.bounds
if(bounds)
width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1
height = bounds[MAP_MAXY]
if(cache)
cached_map = parsed
return bounds
/datum/map_template/proc/initTemplateBounds(list/bounds)
if (!bounds) //something went wrong
stack_trace("[name] template failed to initialize correctly!")
return
var/list/obj/machinery/atmospherics/atmos_machines = list()
var/list/obj/structure/cable/cables = list()
var/list/atom/movable/movables = list()
var/list/area/areas = list()
var/list/turfs = block(
locate(
bounds[MAP_MINX],
bounds[MAP_MINY],
bounds[MAP_MINZ]
),
locate(
bounds[MAP_MAXX],
bounds[MAP_MAXY],
bounds[MAP_MAXZ]
)
)
for(var/turf/current_turf as anything in turfs)
var/area/current_turfs_area = current_turf.loc
areas |= current_turfs_area
if(!SSatoms.initialized)
continue
for(var/movable_in_turf in current_turf)
movables += movable_in_turf
if(istype(movable_in_turf, /obj/structure/cable))
cables += movable_in_turf
continue
if(istype(movable_in_turf, /obj/machinery/atmospherics))
atmos_machines += movable_in_turf
// Not sure if there is some importance here to make sure the area is in z
// first or not. Its defined In Initialize yet its run first in templates
// BEFORE so... hummm
SSmapping.reg_in_areas_in_z(areas)
SSnetworks.assign_areas_root_ids(areas, src)
if(!SSatoms.initialized)
return
SSatoms.InitializeAtoms(areas + turfs + movables, returns_created_atoms ? created_atoms : null)
// NOTE, now that Initialize and LateInitialize run correctly, do we really
// need these two below?
SSmachines.setup_template_powernets(cables)
SSair.setup_template_machinery(atmos_machines)
//calculate all turfs inside the border
var/list/template_and_bordering_turfs = block(
locate(
max(bounds[MAP_MINX]-1, 1),
max(bounds[MAP_MINY]-1, 1),
bounds[MAP_MINZ]
),
locate(
min(bounds[MAP_MAXX]+1, world.maxx),
min(bounds[MAP_MAXY]+1, world.maxy),
bounds[MAP_MAXZ]
)
)
for(var/turf/affected_turf as anything in template_and_bordering_turfs)
affected_turf.air_update_turf(TRUE, TRUE)
affected_turf.levelupdate()
/datum/map_template/proc/load_new_z()
var/x = round((world.maxx - width) * 0.5) + 1
var/y = round((world.maxy - height) * 0.5) + 1
var/datum/space_level/level = SSmapping.add_new_zlevel(name, list(ZTRAIT_AWAY = TRUE))
var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=should_place_on_top)
var/list/bounds = parsed.bounds
if(!bounds)
return FALSE
repopulate_sorted_areas()
//initialize things that are normally initialized after map load
initTemplateBounds(bounds)
smooth_zlevel(world.maxz)
log_game("Z-level [name] loaded at [x],[y],[world.maxz]")
return level
/datum/map_template/proc/load(turf/T, centered = FALSE)
if(centered)
T = locate(T.x - round(width/2) , T.y - round(height/2) , T.z)
if(!T)
return
if(T.x+width > world.maxx)
return
if(T.y+height > world.maxy)
return
var/list/border = block(locate(max(T.x-1, 1), max(T.y-1, 1), T.z),
locate(min(T.x+width+1, world.maxx), min(T.y+height+1, world.maxy), T.z))
for(var/L in border)
var/turf/turf_to_disable = L
SSair.remove_from_active(turf_to_disable) //stop processing turfs along the border to prevent runtimes, we return it in initTemplateBounds()
turf_to_disable.atmos_adjacent_turfs?.Cut()
// Accept cached maps, but don't save them automatically - we don't want
// ruins clogging up memory for the whole round.
var/datum/parsed_map/parsed = cached_map || new(file(mappath))
cached_map = keep_cached_map ? parsed : null
var/list/turf_blacklist = list()
update_blacklist(T, turf_blacklist)
parsed.turf_blacklist = turf_blacklist
if(!parsed.load(T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=should_place_on_top))
return
var/list/bounds = parsed.bounds
if(!bounds)
return
if(!SSmapping.loading_ruins) //Will be done manually during mapping ss init
repopulate_sorted_areas()
//initialize things that are normally initialized after map load
initTemplateBounds(bounds)
log_game("[name] loaded at [T.x],[T.y],[T.z]")
return bounds
/datum/map_template/proc/post_load()
return
/datum/map_template/proc/update_blacklist(turf/T, list/input_blacklist)
return
/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
var/turf/placement = T
if(centered)
var/turf/corner = locate(placement.x - round(width/2), placement.y - round(height/2), placement.z)
if(corner)
placement = corner
return block(placement, locate(placement.x+width-1, placement.y+height-1, placement.z))
//for your ever biggening badminnery kevinz000
//❤ - Cyberboss
/proc/load_new_z_level(file, name)
var/datum/map_template/template = new(file, name)
template.load_new_z()