Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ports TG lazy-loading of map templates using turf reservations #28101

Merged
merged 9 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions _maps/__MAP_DEFINES.dm
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#define STATION_CONTACT "Station Contact"
// A level dedicated to admin use
#define ADMIN_LEVEL "Admin Level"
// For Z-levels dedicated to auto-spawning stuff in
#define Z_FLAG_RESERVED "Reserved"
// A level that can be navigated to by the crew without admin intervention or the emergency shuttle.
#define REACHABLE_BY_CREW "Reachable"
// For away missions - used by some consoles
Expand Down
18 changes: 18 additions & 0 deletions code/__DEFINES/_globals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,41 @@
#define GLOBAL_PROTECT(X)
#endif

/// Standard BYOND global, seriously do not use without an earthshakingly good reason
#define GLOBAL_REAL_VAR(X) var/global/##X

/// Standard typed BYOND global, seriously do not use without an earthshakingly good reason
#define GLOBAL_REAL(X, Typepath) var/global##Typepath/##X

/// Defines a global var on the controller, do not use
#define GLOBAL_RAW(X) /datum/controller/global_vars/var/global##X

/// Create an untyped global with an initializer expression
#define GLOBAL_VAR_INIT(X, InitValue) GLOBAL_RAW(/##X); GLOBAL_MANAGED(X, InitValue)

/// Create a global const var, do not use
#define GLOBAL_VAR_CONST(X, InitValue) GLOBAL_RAW(/const/##X) = InitValue; GLOBAL_UNMANAGED(X)

/// Create a list global with an initializer expression
#define GLOBAL_LIST_INIT(X, InitValue) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, InitValue)

/// Create a list global that is initialized as an empty list
#define GLOBAL_LIST_EMPTY(X) GLOBAL_LIST_INIT(X, list())

/// Create a typed list global with an initializer expression
#define GLOBAL_LIST_INIT_TYPED(X, Typepath, InitValue) GLOBAL_RAW(/list##Typepath/X); GLOBAL_MANAGED(X, InitValue)

/// Create a typed list global that is initialized as an empty list
#define GLOBAL_LIST_EMPTY_TYPED(X, Typepath) GLOBAL_LIST_INIT_TYPED(X, Typepath, list())

/// Create a typed global with an initializer expression
#define GLOBAL_DATUM_INIT(X, Typepath, InitValue) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, InitValue)

/// Create an untyped null global
#define GLOBAL_VAR(X) GLOBAL_RAW(/##X); GLOBAL_UNMANAGED(X)

/// Create a null global list
#define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_UNMANAGED(X)

/// Create a typed null global
#define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_UNMANAGED(X)
8 changes: 7 additions & 1 deletion code/__DEFINES/flags.dm
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,18 @@
#define PASSTAKE (1<<9)
#define PASSBARRICADE (1<<10)

//turf-only flags
//turf-only flags, under the flags variable
#define BLESSED_TILE (1<<0)
#define NO_LAVA_GEN (1<<1) //Blocks lava rivers being generated on the turf
#define NO_RUINS (1<<2)
#define LAVA_BRIDGE (1<<3) //! This turf has already been reserved for a lavaland bridge placement.

// turf flags, under the turf_flags variable
/// If a turf is an unused reservation turf awaiting assignment
#define UNUSED_RESERVATION_TURF (1<<4)
/// If a turf is a reserved turf
#define RESERVATION_TURF (1<<5)

//ORGAN TYPE FLAGS
#define AFFECT_ROBOTIC_ORGAN 1
#define AFFECT_ORGANIC_ORGAN 2
Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/shuttle_defines.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@
#define SHUTTLE_DOCKER_LANDING_CLEAR 1
#define SHUTTLE_DOCKER_BLOCKED_BY_HIDDEN_PORT 2
#define SHUTTLE_DOCKER_BLOCKED 3

#define SHUTTLE_TRANSIT_BORDER 16
7 changes: 7 additions & 0 deletions code/__DEFINES/turfs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@

#define MINERAL_PREVENT_DIG 0 //! A mineral turf should not be changed when mined.
#define MINERAL_ALLOW_DIG 1 //! A mineral turf should be dug out when mined.

/// Returns an outline (neighboring turfs) of the given block
#define CORNER_OUTLINE(corner, width, height) ( \
CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, -1) + \
CORNER_BLOCK_OFFSET(corner, width + 2, 1, -1, height) + \
CORNER_BLOCK_OFFSET(corner, 1, height, -1, 0) + \
CORNER_BLOCK_OFFSET(corner, 1, height, width, 0))
13 changes: 13 additions & 0 deletions code/__HELPERS/lists.dm
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,19 @@
LAZYINITLIST(lazy_list[key]); \
lazy_list[key] |= value;

///Ensures the length of a list is at least I, prefilling it with V if needed. if V is a proc call, it is repeated for each new index so that list() can just make a new list for each item.
#define LISTASSERTLEN(L, I, V...) \
if(length(L) < I) { \
var/_OLD_LENGTH = length(L); \
L.len = I; \
/* Convert the optional argument to a if check */ \
for(var/_USELESS_VAR in list(V)) { \
for(var/_INDEX_TO_ASSIGN_TO in _OLD_LENGTH+1 to I) { \
L[_INDEX_TO_ASSIGN_TO] = V; \
} \
} \
}

//same, but returns nothing and acts on list in place
/proc/shuffle_inplace(list/L)
if(!L)
Expand Down
5 changes: 5 additions & 0 deletions code/_globalvars/bitfields.dm
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ DEFINE_BITFIELD(flags, list(
"NO_SCREENTIPS" = NO_SCREENTIPS,
))

DEFINE_BITFIELD(turf_flags, list(
"UNUSED_RESERVATION_TURF" = UNUSED_RESERVATION_TURF,
"RESERVATION_TURF" = RESERVATION_TURF
))

DEFINE_BITFIELD(flags_2, list(
"SLOWS_WHILE_IN_HAND_2" = SLOWS_WHILE_IN_HAND_2,
"NO_EMP_WIRES_2" = NO_EMP_WIRES_2,
Expand Down
2 changes: 1 addition & 1 deletion code/_globalvars/lists/objects.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ GLOBAL_LIST_EMPTY(hierophant_walls)
GLOBAL_LIST_EMPTY(pandemics)

GLOBAL_LIST_EMPTY(all_areas)
GLOBAL_LIST_EMPTY(all_unique_areas) // List of all unique areas. AKA areas with there_can_be_many = FALSE
GLOBAL_LIST_EMPTY_TYPED(all_unique_areas, /area) // List of all unique areas. AKA areas with there_can_be_many = FALSE
GLOBAL_LIST_EMPTY(machines)
GLOBAL_LIST_EMPTY(rcd_list) //list of Rapid Construction Devices.

Expand Down
131 changes: 129 additions & 2 deletions code/controllers/subsystem/non_firing/SSmapping.dm
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING // 9
flags = SS_NO_FIRE

/// What map datum are we using
var/datum/map/map_datum
/// What map will be used next round
Expand Down Expand Up @@ -31,6 +31,15 @@ SUBSYSTEM_DEF(mapping)
/// Ruin placement manager for lavaland levels.
var/datum/ruin_placer/lavaland/lavaland_ruins_placer

var/num_of_res_levels = 0
var/clearing_reserved_turfs = FALSE
var/list/datum/turf_reservations //list of turf reservations

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/used_turfs = list() //list of turf = datum/turf_reservation
/// List of lists of turfs to reserve
var/list/lists_to_reserve = list()

// This has to be here because world/New() uses [station_name()], which looks this datum up
/datum/controller/subsystem/mapping/PreInit()
. = ..()
Expand Down Expand Up @@ -109,6 +118,8 @@ SUBSYSTEM_DEF(mapping)

// Makes a blank space level for the sake of randomness
GLOB.space_manager.add_new_zlevel("Empty Area", linkage = CROSSLINKED, traits = empty_z_traits)
// Add a reserved z-level
// add_reservation_zlevel() // CURRENTLY DISABLED, AS NOTHING USES IT. IF YOU WANT TO ADD LAZYLOADING TO ANYTHING, MAKE SURE TO REIMPLEMENT THIS

// Setup the Z-level linkage
GLOB.space_manager.do_transition_setup()
Expand Down Expand Up @@ -346,4 +357,120 @@ SUBSYSTEM_DEF(mapping)
SSblackbox.record_feedback("nested tally", "keycard_auths", 1, list("emergency station access", "disabled"))

/datum/controller/subsystem/mapping/Recover()
flags |= SS_NO_INIT
num_of_res_levels = SSmapping.num_of_res_levels
clearing_reserved_turfs = SSmapping.clearing_reserved_turfs
turf_reservations = SSmapping.turf_reservations
unused_turfs = SSmapping.unused_turfs
used_turfs = SSmapping.used_turfs
lists_to_reserve = SSmapping.lists_to_reserve

/datum/controller/subsystem/mapping/proc/get_reservation_from_turf(turf/T)
RETURN_TYPE(/datum/turf_reservation)
return used_turfs[T]

/// Requests a /datum/turf_reservation based on the given width, height.
/datum/controller/subsystem/mapping/proc/request_turf_block_reservation(width, height)
UNTIL(!clearing_reserved_turfs)
log_debug("Reserving [width]x[height] turf reservation")
var/datum/turf_reservation/reserve = new /datum/turf_reservation
for(var/i in levels_by_trait(Z_FLAG_RESERVED))
if(reserve.reserve(width, height, i))
return reserve
//If we didn't return at this point, theres a good chance we ran out of room on the exisiting reserved z levels, so lets try a new one
var/z_level_num = add_reservation_zlevel()
if(reserve.reserve(width, height, z_level_num))
return reserve
qdel(reserve)

/datum/controller/subsystem/mapping/proc/add_reservation_zlevel()
num_of_res_levels++
// . here is the z of the just added z-level
. = GLOB.space_manager.add_new_zlevel("Transit/Reserved #[num_of_res_levels]", traits = list(Z_FLAG_RESERVED, BLOCK_TELEPORT, IMPEDES_MAGIC))
initialize_reserved_level(.)
if(!initialized)
return
if(length(SSidlenpcpool.idle_mobs_by_zlevel) == . || !islist(SSidlenpcpool.idle_mobs_by_zlevel)) // arbitrary chosen from these lists that require the length of the z-levels
return
LISTASSERTLEN(SSidlenpcpool.idle_mobs_by_zlevel, ., list())
LISTASSERTLEN(SSmobs.clients_by_zlevel, ., list())
LISTASSERTLEN(SSmobs.dead_players_by_zlevel, ., list())

///Sets up a z level as reserved
///This is not for wiping reserved levels, use wipe_reservations() for that.
///If this is called after SSatom init, it will call Initialize on all turfs on the passed z, as its name promises
/datum/controller/subsystem/mapping/proc/initialize_reserved_level(z)
UNTIL(!clearing_reserved_turfs) //regardless, lets add a check just in case.
log_debug("Initializing new reserved Z-level")
clearing_reserved_turfs = TRUE //This operation will likely clear any existing reservations, so lets make sure nothing tries to make one while we're doing it.
if(!check_level_trait(z, Z_FLAG_RESERVED))
clearing_reserved_turfs = FALSE
CRASH("Invalid z level prepared for reservations.")
var/block = block(SHUTTLE_TRANSIT_BORDER, SHUTTLE_TRANSIT_BORDER, z, world.maxx - SHUTTLE_TRANSIT_BORDER, world.maxy - SHUTTLE_TRANSIT_BORDER)
for(var/turf/T as anything in block)
// No need to empty() these, because they just got created and are already /turf/space.
T.turf_flags |= UNUSED_RESERVATION_TURF
CHECK_TICK

// Gotta create these suckers if we've not done so already
if(SSatoms.initialized)
SSatoms.InitializeAtoms(block(1, 1, world.maxx, world.maxy, z), FALSE)

unused_turfs["[z]"] = block
clearing_reserved_turfs = FALSE

/datum/controller/subsystem/mapping/fire(resumed)
// Cache for sonic speed
var/list/list/turf/unused_turfs = src.unused_turfs
var/list/world_contents = GLOB.all_unique_areas[world.area].contents
// var/list/world_turf_contents_by_z = GLOB.all_unique_areas[world.area].turfs_by_zlevel
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)
if(index)
lists_to_reserve.Cut(1, index)
return
var/turf/reserving_turf = packet[packetlen]
reserving_turf.empty(/turf/space)
LAZYINITLIST(unused_turfs["[reserving_turf.z]"])
if(!(reserving_turf in unused_turfs["[reserving_turf.z]"]))
unused_turfs["[reserving_turf.z]"].Insert(1, reserving_turf)
reserving_turf.turf_flags = UNUSED_RESERVATION_TURF

world_contents += reserving_turf
packet.len--
packetlen = length(packet)

index++
lists_to_reserve.Cut(1, index)

/**
* Lazy loads a template on a lazy-loaded z-level
* If you want to use this as non-debug, make sure to uncomment add_reservation_zlevel in /datum/controller/subsystem/mapping/Initialize()
*/
/datum/controller/subsystem/mapping/proc/lazy_load_template(datum/map_template/template)
RETURN_TYPE(/datum/turf_reservation)

UNTIL(initialized)
var/static/lazy_loading = FALSE
UNTIL(!lazy_loading)

lazy_loading = TRUE
. = _lazy_load_template(template)
lazy_loading = FALSE

/datum/controller/subsystem/mapping/proc/_lazy_load_template(datum/map_template/template)
PRIVATE_PROC(TRUE)
var/datum/turf_reservation/reservation = request_turf_block_reservation(template.width, template.height)
if(!istype(reservation))
return

template.load(reservation.bottom_left_turf)
return reservation

/// Schedules a group of turfs to be handed back to the reservation system's control
/datum/controller/subsystem/mapping/proc/unreserve_turfs(list/turfs)
lists_to_reserve += list(turfs)
8 changes: 8 additions & 0 deletions code/game/area/misc_areas.dm
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,11 @@

/area/syndicate_mothership/jail
name = "\improper Syndicate Jail"

/area/cordon
name = "CORDON"
icon_state = "cordon"
requires_power = FALSE
always_unpowered = TRUE
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
valid_territory = FALSE
7 changes: 5 additions & 2 deletions code/game/turfs/turf.dm
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@

var/blocks_air = FALSE

flags = 0
flags = 0 // TODO, someday move all off the flags here to turf_flags

var/turf_flags = NONE

var/image/obscured //camerachunks

Expand Down Expand Up @@ -282,8 +284,9 @@
qdel(src) //Just get the side effects and call Destroy
var/list/old_comp_lookup = comp_lookup?.Copy()
var/list/old_signal_procs = signal_procs?.Copy()

var/carryover_turf_flags = turf_flags & (RESERVATION_TURF|UNUSED_RESERVATION_TURF)
var/turf/W = new path(src)
W.turf_flags |= carryover_turf_flags
if(old_comp_lookup)
LAZYOR(W.comp_lookup, old_comp_lookup)
if(old_signal_procs)
Expand Down
1 change: 1 addition & 0 deletions code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
/proc/machine_upgrade,
/client/proc/map_template_load,
/client/proc/map_template_upload,
/client/proc/map_template_load_lazy,
/client/proc/view_runtimes,
/client/proc/admin_serialize,
/client/proc/uid_log,
Expand Down
20 changes: 20 additions & 0 deletions code/modules/admin/verbs/map_template_loadverb.dm
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,23 @@
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has uploaded a map template ([map]). Took [stop_watch(timer)]s.</span>")
else
to_chat(usr, "Map template '[map]' failed to load properly")

/client/proc/map_template_load_lazy()
set category = "Debug"
set name = "Map template - Lazy Load"

if(!check_rights(R_DEBUG))
return

var/map = input(usr, "Choose a Map Template to place on the lazy load map level.","Place Map Template") as null|anything in GLOB.map_templates
if(!map)
return
var/datum/map_template/template = GLOB.map_templates[map]

message_admins("<span class='adminnotice'>[key_name_admin(usr)] is lazyloading the map template ([template.name]).</span>")
var/datum/turf_reservation/reserve = SSmapping.lazy_load_template(template)
if(!istype(reserve))
message_admins("<span class='danger'>Lazyloading [template.name] failed! You should report this as a bug.</span>")
return
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has lazyloaded the map template ([template.name]) at [ADMIN_JMP(reserve.bottom_left_turf)]</span>")

32 changes: 32 additions & 0 deletions code/modules/awaymissions/cordon.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/// Turf type that appears to be a world border, completely impassable and non-interactable to all physical (alive) entities.
/turf/cordon
name = "cordon"
icon = 'icons/turf/walls.dmi'
icon_state = "cordon"
invisibility = INVISIBILITY_ABSTRACT
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
explosion_block = 50
rad_insulation = RAD_FULL_INSULATION
opacity = TRUE
density = TRUE
blocks_air = TRUE
baseturf = /turf/cordon

// /turf/cordon/rust_heretic_act()
// return FALSE

/turf/cordon/acid_act(acidpwr, acid_volume, acid_id)
return FALSE

/turf/cordon/singularity_act()
return FALSE

/turf/cordon/TerraformTurf(path, list/new_baseturfs, flags)
return

/turf/cordon/bullet_act(obj/item/projectile/hitting_projectile, def_zone, piercing_hit)
SHOULD_CALL_PARENT(FALSE) // Fuck you
return

/turf/cordon/Adjacent(atom/neighbor, atom/target, atom/movable/mover)
return FALSE
Loading