Files
4ab234f10c maploader / overmaps update - struct system, overmaps location bindings, misc backend improvements (#6403)
tl;dr level collections that can easily be logically restructured/moved
as necessary

will eventually be used for radio and overmaps location resolution.

this is the last part of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP/pull/4841
that has not yet been integrated.
this will be a draft for a while.

---------

Co-authored-by: LetterN <24603524+LetterN@users.noreply.github.com>
2025-11-06 17:49:23 -08:00

172 lines
6.4 KiB
Plaintext

//* This file is explicitly licensed under the MIT license. *//
//* Copyright (c) 2025 Citadel Station Developers *//
/**
* initializes the key-value store of map datums.
*/
/datum/controller/subsystem/mapping/proc/init_maps()
keyed_maps = list()
var/list/skipping = list()
// if this runtimes, go vv repair it; this is fucked.
for(var/datum/map/map as anything in loaded_maps)
keyed_maps[map.id] = map
skipping[map.id] = TRUE
for(var/datum/map/path as anything in subtypesof(/datum/map))
if(initial(path.abstract_type) == path)
continue
var/id = initial(path.id)
if(isnull(id))
continue
if(skipping[id])
continue
var/datum/map/created = new path
if(keyed_maps[created.id])
STACK_TRACE("collision between [path] and [keyed_maps[created.id].type]")
continue
keyed_maps[created.id] = created
/**
* resolves a map by ID, type, or no-op if it's an instance
*/
/datum/controller/subsystem/mapping/proc/resolve_map(datum/map/id_type_instance)
if(istext(id_type_instance))
return keyed_maps[id_type_instance]
else if(ispath(id_type_instance))
return keyed_maps[initial(id_type_instance.id)]
return id_type_instance
/datum/controller/subsystem/mapping/proc/load_map(datum/map/instance)
UNTIL(!map_system_mutex)
map_system_mutex = TRUE
. = load_map_impl(arglist(args))
map_system_mutex = FALSE
/datum/controller/subsystem/mapping/proc/load_map_impl(datum/map/instance, from_world_load)
emit_info_log("load: beginning load sequence of map id '[instance.id]")
// unroll & ready maps
var/list/datum/map/maps_to_load = list()
var/list/datum/map/maps_to_iterate = list(instance)
var/maps_to_iterate_idx = 1
while(maps_to_iterate_idx <= length(maps_to_iterate))
var/datum/map/iterating = maps_to_iterate[maps_to_iterate_idx]
maps_to_iterate_idx++
// TODO: if this returns FALSE, yell about it
iterating.ready()
maps_to_load += iterating
var/list/chainload_ids = list()
if(length(iterating.dependencies))
chainload_ids += iterating.dependencies
if(!from_world_load || !global.world_init_options.load_only_station)
if(length(iterating.lateload))
chainload_ids += iterating.lateload
emit_info_log("load - '[instance.id] - resolved chainload ids '[json_encode(chainload_ids)]'")
for(var/id in chainload_ids)
var/datum/map/resolved = resolve_map(id)
if(resolved)
maps_to_iterate |= resolved
else
if(from_world_load)
emit_init_fatal("load - could not resolve map id '[id]' while resolving chainload dependencies for '[instance.id]")
else
emit_fatal_log("load - could not resolve map id '[id]' while resolving chainload dependencies for '[instance.id]")
stack_trace("missing map id '[id]' during map chainload")
// load maps as needed
var/list/datum/map/loaded_maps = list()
var/list/datum/map_level/loaded_lockstep_levels = list()
var/list/datum/dmm_context/loaded_lockstep_contexts = list()
var/list/datum/callback/deferred_generation_callbacks = list()
for(var/datum/map/loading_map as anything in maps_to_load)
emit_info_log("load - loading '[loading_map.id]' with [length(loading_map.levels)] levels...")
var/list/validation_errors = list()
if(!loading_map.validate(TRUE, validation_errors))
var/validation_fail_msg = "Map [loading_map.id] ([loading_map.type]) failed validation with errors [json_encode(validation_errors)]. \
The map will be loaded anyways due to this being a subsystem-level mapload call, \
but this round will probably break in some spectacular manner."
STACK_TRACE(validation_fail_msg)
to_chat(world, SPAN_BOLDANNOUNCE("SSmapping: [validation_fail_msg]"))
if(!loading_map.construct())
emit_init_fatal("load - could not construct map id '[loading_map.id]'; skipping")
stack_trace("map id '[loading_map.id]' failed construct()ion")
continue
var/list/map_use_area_cache = loading_map.load_shared_area_cache ? list() : null
for(var/datum/map_level/level as anything in loading_map.get_sorted_levels())
var/datum/dmm_context/level_use_dmm_context = create_dmm_context()
level_use_dmm_context.mangling_id = loading_map.mangling_id || loading_map.id
var/list/datum/callback/level_generation_callbacks = list()
var/datum/dmm_context/level_context = load_level_impl(
level,
map_use_area_cache,
level_use_dmm_context,
TRUE,
level_generation_callbacks,
)
if(isnull(level_context))
emit_fatal_log("load - failed to load level '[level.id]' in map '[instance.id]")
stack_trace("unable to load level [level] ([level.id])")
to_chat(world, SPAN_DANGER("PANIC: Unable to load level [level] ([level.id])"))
continue
loaded_lockstep_levels += level
loaded_lockstep_contexts += level_context
deferred_generation_callbacks += level_generation_callbacks
emit_info_log("load - finished loading '[instance.id]' with [length(loading_map.levels)] levels")
loading_map.on_loaded_immediate()
loaded_maps += loading_map
//! LEGACY
for(var/path in loading_map.legacy_assert_shuttle_datums)
SSshuttle.legacy_shuttle_assert(path)
//! END
emit_info_log("load - initializing [length(loaded_lockstep_levels)] levels...")
// fire generation and atom init after, now that everything has had a chance to load
for(var/datum/callback/cb as anything in deferred_generation_callbacks)
cb.Invoke()
for(var/datum/dmm_context/ctx as anything in loaded_lockstep_contexts)
if(SSatoms.initialized)
SSatoms.init_map_bounds(ctx.loaded_bounds)
// fire finalize hooks
for(var/datum/map_level/level as anything in loaded_lockstep_levels)
level.on_loaded_finalize(level.z_index)
for(var/datum/map/map as anything in loaded_maps)
map.on_loaded_finalize()
for(var/datum/map_level/loaded_level as anything in loaded_lockstep_levels)
rebuild_multiz(loaded_level.z_index)
emit_info_log("load - initialized [length(loaded_lockstep_levels)] levels")
return TRUE
/**
* destroys a loaded map and frees it for later usage
*
* @return TRUE / FALSE based on success / fail
*/
/datum/controller/subsystem/mapping/proc/unload_map(datum/map/instance)
CRASH("unimplemented")
/**
* immediately de-allocates a loaded map and frees its z-index.
*
* **Do not use this directly unless you absolutely know what you are doing.**
* This does not perform any cleanup, and calling this on a loaded zmap can have
* severe consequences.
*
* @return TRUE / FALSE based on success / fail
*/
/datum/controller/subsystem/mapping/proc/unallocate_map(datum/map/instance)
CRASH("unimplemented")