From f0bac4c1798b4dcb60ff427b62fb480b59dd9622 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Thu, 7 Jul 2016 00:17:21 -0700 Subject: [PATCH] Initialization deferral is now handled by the caller, but is now also seamless --- code/datums/helper_datums/map_template.dm | 12 +++++- code/game/atoms_movable.dm | 15 +++++-- .../admin/verbs/map_template_loadverb.dm | 6 +-- code/modules/awaymissions/map_rng.dm | 5 +-- code/modules/awaymissions/maploader/reader.dm | 25 ++++------- code/modules/awaymissions/zlevel.dm | 30 ++++++------- code/modules/spacial_allocator/zlevel.dm | 42 +++++++++++++++++++ .../spacial_allocator/zlevel_manager.dm | 28 ++++++++++--- 8 files changed, 110 insertions(+), 53 deletions(-) diff --git a/code/datums/helper_datums/map_template.dm b/code/datums/helper_datums/map_template.dm index c1b9312b734..03fe8602281 100644 --- a/code/datums/helper_datums/map_template.dm +++ b/code/datums/helper_datums/map_template.dm @@ -23,7 +23,7 @@ height = bounds[MAP_MAXY] return bounds -/datum/map_template/proc/load(turf/T, centered = 0, delay_init = 0) +/datum/map_template/proc/load(turf/T, centered = 0) var/turf/placement = T var/min_x = placement.x var/min_y = placement.y @@ -43,7 +43,13 @@ // 1 bigger, to update the turf smoothing var/turf/ST_bot_left = locate(max(1, min_x-1), max(1, min_y-1), placement.z) var/turf/ST_top_right = locate(min(world.maxx, max_x+1), min(world.maxy, max_y+1), placement.z) - var/list/bounds = maploader.load_map(get_file(), min_x, min_y, placement.z, cropMap = 1, delay_init = delay_init) + // This is to place a freeze on initialization until the map's done loading + // otherwise atmos and stuff will start running mid-load + // This system will metaphorically snap in half (not postpone init everywhere) + // if given a multi-z template + // it might need to be adapted for that when that time comes + zlevels.add_dirt(placement.z) + var/list/bounds = maploader.load_map(get_file(), min_x, min_y, placement.z, cropMap = 1) if(!bounds) return 0 if(bot_left == null || top_right == null) @@ -55,9 +61,11 @@ log_debug("One of the smoothing corners is bust") else log_debug("Tile smoothing from ([ST_bot_left.x],[ST_bot_left.y]) to ([ST_top_right.x],[ST_top_right.y])") + late_setup_level( block(bot_left, top_right), block(ST_bot_left, ST_top_right)) + zlevels.remove_dirt(placement.z) log_game("[name] loaded at [min_x],[min_y],[placement.z]") return 1 diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 4a422cf6d1b..f4cb6220cc4 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -23,12 +23,11 @@ /atom/movable/New() . = ..() areaMaster = get_area_master(src) - var/turf/T = get_turf(src) // If you're wondering what goofery this is, this is for things that need the environment // around them set up - like `air_update_turf` and the like - if(auto_init && (ticker && ticker.current_state == GAME_STATE_PLAYING) && !(T && zlevels.is_zlevel_dirty(T.z))) - initialize() + if(auto_init && (ticker && ticker.current_state == GAME_STATE_PLAYING)) + attempt_init() /atom/movable/Destroy() for(var/atom/movable/AM in contents) @@ -40,6 +39,16 @@ pulledby = null return ..() +// used to provide a good interface for the init delay system to step in +// and we don't need to call `get_turf` until the game's started +// at which point object creations are a fair toss more seldom +/atom/movable/proc/attempt_init() + var/turf/T = get_turf(src) + if (T && zlevels.is_zlevel_dirty(T.z)) + zlevels.postpone_init(T.z, src) + else + initialize() + /atom/movable/proc/initialize() return diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index f37ad73a1dc..f447810d860 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -19,10 +19,6 @@ to_chat(usr, "Map is too large to fit in bounds. Map's dimensions: ([template.width], [template.height])") return - var/delay_init = 0 - if(alert(usr,"Delay initialization? (Needed for large atmos-containing maps)","Delay","Yes","No") == "Yes") - delay_init = 1 - var/list/preview = list() for(var/S in template.get_affected_turfs(T,centered = TRUE)) preview += image('icons/turf/overlays.dmi',S,"greenOverlay") @@ -30,7 +26,7 @@ if(alert(usr,"Confirm location.","Template Confirm","Yes","No") == "Yes") var/timer = start_watch() message_admins("[key_name_admin(usr)] has started to place the map template ([template.name]) at (JMP)") - if(template.load(T, centered = TRUE, delay_init = delay_init)) + if(template.load(T, centered = TRUE)) message_admins("[key_name_admin(usr)] has placed a map template ([template.name]) at (JMP). Took [stop_watch(timer)]s.") else to_chat(usr, "Failed to place map") diff --git a/code/modules/awaymissions/map_rng.dm b/code/modules/awaymissions/map_rng.dm index 0f8ca13b7a6..00df28d2160 100644 --- a/code/modules/awaymissions/map_rng.dm +++ b/code/modules/awaymissions/map_rng.dm @@ -10,7 +10,6 @@ var/datum/map_template/template = null var/centered = 1 var/loaded = 0 - var/delay_init = 0 /obj/effect/landmark/map_loader/New(turf/loc, tname) ..() @@ -28,13 +27,13 @@ /obj/effect/landmark/map_loader/proc/load(datum/map_template/t) if(!t) return - if(loaded) + if(loaded) // I wanna be super sure this loads only once return loaded = 1 var/turf/pos = get_turf(src) // Hop to nullspace so we don't get re-initialized by the map we're loading loc = null - t.load(pos, centered = centered, delay_init = delay_init) + t.load(pos, centered = centered) t.loaded++ qdel(src) diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index 2ef9d79d994..d174822cdd0 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -31,12 +31,13 @@ var/global/dmm_suite/preloader/_preloader = new * If `measureOnly` is set, then no atoms will be created, and all this will do * is return the bounds after parsing the file * - * If delay_init is set, then all initializations while the map is loading will - * be deferred until later - * You'll need it for large initializations like loading the cyberiad, where - * glass and similar can cause atmos to initialize before it's ready + * If you need to freeze init while you're working, you can use the spacial allocator's + * "add_dirt" and "remove_dirt" which will put initializations on hold until you say + * the word. This is important for loading large maps such as the cyberiad, where + * atmos will attempt to start before it's ready, causing runtimes galore if init is + * allowed to romp unchecked. */ -/dmm_suite/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num, delay_init as num) +/dmm_suite/load_map(dmm_file as file, x_offset as num, y_offset as num, z_offset as num, cropMap as num, measureOnly as num) var/tfile = dmm_file//the map file we're creating var/fname = "Lambda" if(isfile(tfile)) @@ -57,8 +58,6 @@ var/global/dmm_suite/preloader/_preloader = new var/dmm_suite/loaded_map/LM = new - if(measureOnly) - delay_init = 0 // This try-catch is used as a budget "Finally" clause, as the dirt count // needs to be reset var/watch = start_watch() @@ -91,9 +90,6 @@ var/global/dmm_suite/preloader/_preloader = new var/xcrd var/ycrd = text2num(dmmRegex.group[4]) + y_offset - 1 var/zcrd = text2num(dmmRegex.group[5]) + z_offset - 1 - if(!measureOnly && delay_init) - zlevels.add_dirt(zcrd) - LM.touched_z_levels |= zcrd if(zcrd > world.maxz) if(cropMap) @@ -161,15 +157,9 @@ var/global/dmm_suite/preloader/_preloader = new CHECK_TICK catch(var/exception/e) _preloader.reset() - if(delay_init) - for(var/i in LM.touched_z_levels) - zlevels.remove_dirt(i) throw e - if(delay_init) - _preloader.reset() - for(var/i in LM.touched_z_levels) - zlevels.remove_dirt(i) + _preloader.reset() log_debug("Loaded map in [stop_watch(watch)]s.") qdel(LM) if(bounds[MAP_MINX] == 1.#INF) // Shouldn't need to check every item @@ -465,7 +455,6 @@ var/global/dmm_suite/preloader/_preloader = new // a new area type for each new ruin /dmm_suite/loaded_map parent_type = /datum - var/list/touched_z_levels = list() var/list/area_list = list() var/index = 1 // To store the state of the regex diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm index 1b1fd4d7b86..7c06aa07535 100644 --- a/code/modules/awaymissions/zlevel.dm +++ b/code/modules/awaymissions/zlevel.dm @@ -1,5 +1,7 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away_mission_config.txt") +// Call this before you remove the last dirt on a z level - that way, all objects +// will have proper atmos and other important enviro things /proc/late_setup_level(turfs, smoothTurfs) var/total_timer = start_watch() var/subtimer = start_watch() @@ -12,14 +14,10 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away log_debug("\tTook [stop_watch(subtimer)]s") subtimer = start_watch() - log_debug("Initializing objects and lighting") + log_debug("Initializing lighting") for(var/turf/T in turfs) if(T.dynamic_lighting) T.lighting_build_overlays() - for(var/obj/structure/cable/PC in T) - makepowernet_for(PC) - for(var/atom/movable/AM in T) - AM.initialize() log_debug("\tTook [stop_watch(subtimer)]s") subtimer = start_watch() @@ -35,14 +33,6 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away var/turf/simulated/mineral/MT = T MT.add_edges() log_debug("\tTook [stop_watch(subtimer)]s") - - log_debug("Initializing pipenets") - // We do this once we're finished initializing since otherwise the pipes - // won't have finished connecting - for(var/turf/T in turfs) - for(var/obj/machinery/atmospherics/machine in T) - machine.build_network() - log_debug("\tTook [stop_watch(subtimer)]s") log_debug("Late setup finished - took [stop_watch(total_timer)]s") /proc/createRandomZlevel() @@ -56,8 +46,11 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away var/map = pick(potentialRandomZlevels) var/file = file(map) if(isfile(file)) - maploader.load_map(file) - late_setup_level(block(locate(1, 1, world.maxz), locate(world.maxx, world.maxy, world.maxz))) + var/zlev = zlevels.add_new_zlevel() + zlevels.add_dirt(zlev) + maploader.load_map(file, z_offset = zlev) + late_setup_level(block(locate(1, 1, zlev), locate(world.maxx, world.maxy, zlev))) + zlevels.remove_dirt(zlev) log_to_dd(" Away mission loaded: [map]") //map_transition_config.Add(AWAY_MISSION_LIST) @@ -85,8 +78,11 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away var/file = file(map) if(isfile(file)) log_startup_progress("Loading away mission: [map]") - maploader.load_map(file) - late_setup_level(block(locate(1, 1, world.maxz), locate(world.maxx, world.maxy, world.maxz))) + var/zlev = zlevels.add_new_zlevel() + zlevels.add_dirt(zlev) + maploader.load_map(file, z_offset = zlev) + late_setup_level(block(locate(1, 1, zlev), locate(world.maxx, world.maxy, zlev))) + zlevels.remove_dirt(zlev) log_to_dd(" Away mission loaded: [map]") //map_transition_config.Add(AWAY_MISSION_LIST) diff --git a/code/modules/spacial_allocator/zlevel.dm b/code/modules/spacial_allocator/zlevel.dm index 45be707c15a..6fd3f75d53f 100644 --- a/code/modules/spacial_allocator/zlevel.dm +++ b/code/modules/spacial_allocator/zlevel.dm @@ -2,6 +2,48 @@ var/flags = 0 var/dirt_count = 0 var/zpos + var/list/init_list = list() + var/list/pipes = list() + var/list/cables = list() /datum/zlevel/New(z) zpos = z + +/datum/zlevel/proc/resume_init() + if(dirt_count > 0) + throw EXCEPTION("Init told to resume when z-level still dirty. Z level: '[zpos]'") + log_debug("Releasing freeze on z-level '[zpos]'!") + log_debug("Beginning initialization!") + var/watch = start_watch() + for(var/schmoo in init_list) + var/atom/movable/AM = schmoo + if(AM) // to catch stuff like the nuke disk that no longer exists + AM.initialize() + if(istype(AM, /obj/machinery/atmospherics)) + pipes.Add(AM) + else if(istype(AM, /obj/structure/cable)) + cables.Add(AM) + log_debug("Primary initialization finished in [stop_watch(watch)]s.") + init_list.Cut() + do_pipes() + do_cables() + +/datum/zlevel/proc/do_pipes() + var/watch = start_watch() + log_debug("Building pipenets on z-level '[zpos]'!") + for(var/schmoo in pipes) + var/obj/machinery/atmospherics/machine = schmoo + if(machine) + machine.build_network() + pipes.Cut() + log_debug("Took [stop_watch(watch)]s") + +/datum/zlevel/proc/do_cables() + var/watch = start_watch() + log_debug("Building powernets on z-level '[zpos]'!") + for(var/schmoo in cables) + var/obj/structure/cable/C = schmoo + if(C) + makepowernet_for(C) + cables.Cut() + log_debug("Took [stop_watch(watch)]s") diff --git a/code/modules/spacial_allocator/zlevel_manager.dm b/code/modules/spacial_allocator/zlevel_manager.dm index 7eb10d551e8..b2540f874e9 100644 --- a/code/modules/spacial_allocator/zlevel_manager.dm +++ b/code/modules/spacial_allocator/zlevel_manager.dm @@ -12,6 +12,15 @@ var/global/datum/zlev_manager/zlevels = new z_list[i] = new /datum/zlevel(i) + +/datum/zlev_manager/proc/get_zlev(z) + if(z < 1) + throw EXCEPTION("Non-positive z level given!") + else if (z > z_list.len) + throw EXCEPTION("Untracked z level: '[z]'") + else + return z_list[z] + // For when you need the z-level to be at a certain point /datum/zlev_manager/proc/increase_max_zlevel_to(new_maxz) if(world.maxz>=new_maxz) @@ -53,19 +62,28 @@ var/global/datum/zlev_manager/zlevels = new // Returns whether the given z level has a freeze on initialization /datum/zlev_manager/proc/is_zlevel_dirty(z) - if(z > z_list.len) - return 0 // It's not dirty if it's not being tracked - var/datum/zlevel/our_z = z_list[z] + var/datum/zlevel/our_z = get_zlev(z) return (our_z.dirt_count > 0) // Increases the dirt count on a z level /datum/zlev_manager/proc/add_dirt(z) - var/datum/zlevel/our_z = z_list[z] + var/datum/zlevel/our_z = get_zlev(z) + if(our_z.dirt_count == 0) + log_debug("Placing an init freeze on z-level '[our_z.zpos]'!") our_z.dirt_count++ // Decreases the dirt count on a z level /datum/zlev_manager/proc/remove_dirt(z) - var/datum/zlevel/our_z = z_list[z] + var/datum/zlevel/our_z = get_zlev(z) our_z.dirt_count-- + if(our_z.dirt_count == 0) + our_z.resume_init() + if(our_z.dirt_count < 0) + log_debug("WARNING: Imbalanced dirt removal") + our_z.dirt_count = 0 + +/datum/zlev_manager/proc/postpone_init(z, thing) + var/datum/zlevel/our_z = get_zlev(z) + our_z.init_list.Add(thing)