mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-30 07:27:57 +01:00
Ports TG lazy-loading of map templates using turf reservations (#28101)
* kills off heaps * turf reservations * lazy loading map templates * atmos + a comment * port LISTASSERTLEN and rename a proc * CI happy * yes * oops * removes blocks air and adds comments
This commit is contained in:
@@ -1,62 +0,0 @@
|
||||
// This represents a level we can carve up as we please, and hand out
|
||||
// chunks of to whatever requests it
|
||||
/datum/space_level/heap
|
||||
name = "Heap level #ERROR"
|
||||
var/datum/space_chunk/top
|
||||
linkage = UNAFFECTED
|
||||
|
||||
/datum/space_level/heap/New(z, name, transition_type, traits)
|
||||
..(z, "Heap level #[z]", UNAFFECTED, traits)
|
||||
top = new(1, 1, zpos, world.maxx, world.maxy)
|
||||
flags = traits
|
||||
|
||||
/datum/space_level/heap/proc/request(width, height)
|
||||
return top.can_fit_space(width, height)
|
||||
|
||||
// Returns a space chunk datum for some nerd to work with - tells them what's safe to write into, and such
|
||||
/datum/space_level/heap/proc/allocate(width, height)
|
||||
if(!request(width, height))
|
||||
return null
|
||||
|
||||
var/datum/space_chunk/C = top.get_optimal_chunk(width, height)
|
||||
if(!C)
|
||||
return null
|
||||
C.children.Cut()
|
||||
|
||||
if(C.width == width && C.height == height && C.is_empty)
|
||||
C.set_occupied(TRUE)
|
||||
return C
|
||||
|
||||
// Split the chunk into 4 pieces
|
||||
var/datum/space_chunk/return_chunk = new(C.x, C.y, C.zpos, width, height)
|
||||
C.children += return_chunk
|
||||
C.children += new /datum/space_chunk(C.x+width, C.y, C.zpos, C.width-width, height)
|
||||
C.children += new /datum/space_chunk(C.x, C.y+height, C.zpos, width, C.height-height)
|
||||
C.children += new /datum/space_chunk(C.x+width, C.y+height, C.zpos, C.width-width, C.height-height)
|
||||
|
||||
for(var/datum/space_chunk/C2 in C.children)
|
||||
C2.parent = C
|
||||
return_chunk.set_occupied(TRUE)
|
||||
return return_chunk
|
||||
|
||||
/datum/space_level/heap/proc/free(datum/space_chunk/C)
|
||||
if(!istype(C))
|
||||
return
|
||||
if(C.zpos != zpos)
|
||||
return
|
||||
C.set_occupied(FALSE)
|
||||
for(var/turf/T in block(C.x, C.y, C.zpos, C.x + C.width - 1, C.y + C.height - 1, C.zpos))
|
||||
for(var/atom/movable/M in T)
|
||||
if(isobserver(M))
|
||||
continue
|
||||
M.loc = null
|
||||
qdel(M, TRUE)
|
||||
T.ChangeTurf(T.baseturf)
|
||||
var/datum/space_chunk/last_empty_parent = C
|
||||
while(last_empty_parent.parent && last_empty_parent.parent.is_empty)
|
||||
last_empty_parent = last_empty_parent.parent
|
||||
// Prevent a self-qdel from killing this proc
|
||||
src = null
|
||||
for(var/datum/space_chunk/child in last_empty_parent.children)
|
||||
qdel(child)
|
||||
last_empty_parent.children.Cut()
|
||||
@@ -28,6 +28,8 @@
|
||||
/proc/level_boosts_signal(z)
|
||||
return check_level_trait(z, BOOSTS_SIGNAL)
|
||||
|
||||
#define is_reserved_level(z) check_level_trait(z, ZTRAIT_RESERVED)
|
||||
|
||||
// Used for the nuke disk, or for checking if players survived through xenos
|
||||
/proc/is_secure_level(z)
|
||||
var/secure = check_level_trait(z, STATION_LEVEL)
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Turf reservations are used to reserve specific areas of the reserved z-levels for various purposes, typically for late-loading maps.
|
||||
* It ensures that reserved turfs are properly managed and can be released when no longer needed.
|
||||
* Reservations are not automatically released, so they must be manually released when no longer needed.
|
||||
*
|
||||
* Usage:
|
||||
* - To create a new reservation, call the `request_turf_block_reservation(width, height)` method from the mapping subsystem.
|
||||
* - This will return a new instance of /datum/turf_reservation if the reservation is successful.
|
||||
*
|
||||
* Releasing:
|
||||
* - Call the `Release` method on the /datum/turf_reservation instance to release the reserved turfs and cordon turfs.
|
||||
* - This will return the used turfs to the mapping subsystem to allow for reuse of the turfs.
|
||||
*/
|
||||
|
||||
/datum/turf_reservation
|
||||
/// All turfs that we've reserved
|
||||
var/list/reserved_turfs = list()
|
||||
|
||||
/// Turfs around the reservation for cordoning
|
||||
var/list/cordon_turfs = list()
|
||||
|
||||
/// Area of turfs next to the cordon to fill with pre_cordon_area's
|
||||
var/list/pre_cordon_turfs = list()
|
||||
|
||||
/// The width of the reservation
|
||||
var/width = 0
|
||||
|
||||
/// The height of the reservation
|
||||
var/height = 0
|
||||
|
||||
/// Bottom left turf of the reservation
|
||||
var/turf/bottom_left_turf
|
||||
|
||||
/// Top right turf of the reservation
|
||||
var/turf/top_right_turf
|
||||
|
||||
/// The turf type the reservation is initially made with
|
||||
var/turf_type = /turf/space
|
||||
|
||||
/datum/turf_reservation/New()
|
||||
LAZYADD(SSmapping.turf_reservations, src)
|
||||
|
||||
/datum/turf_reservation/Destroy()
|
||||
Release()
|
||||
LAZYREMOVE(SSmapping.turf_reservations, src)
|
||||
return ..()
|
||||
|
||||
/datum/turf_reservation/proc/Release()
|
||||
bottom_left_turf = null
|
||||
top_right_turf = null
|
||||
|
||||
var/list/reserved_copy = reserved_turfs.Copy()
|
||||
SSmapping.used_turfs -= reserved_turfs
|
||||
reserved_turfs = list()
|
||||
|
||||
var/list/cordon_copy = cordon_turfs.Copy()
|
||||
SSmapping.used_turfs -= cordon_turfs
|
||||
cordon_turfs = list()
|
||||
|
||||
var/release_turfs = reserved_copy + cordon_copy
|
||||
|
||||
SSmapping.unreserve_turfs(release_turfs)
|
||||
|
||||
/// Attempts to calaculate and store a list of turfs around the reservation for cordoning. Returns whether a valid cordon was calculated
|
||||
/datum/turf_reservation/proc/calculate_cordon_turfs(turf/bottom_left, turf/top_right)
|
||||
if(bottom_left.x < 2 || bottom_left.y < 2 || top_right.x > (world.maxx - 2) || top_right.y > (world.maxy - 2))
|
||||
return FALSE // no space for a cordon here
|
||||
|
||||
var/list/possible_turfs = CORNER_OUTLINE(bottom_left, width, height)
|
||||
// if they're our cordon turfs, accept them
|
||||
possible_turfs -= cordon_turfs
|
||||
for(var/turf/cordon_turf as anything in possible_turfs)
|
||||
// if we changed this to check reservation turf instead of not unused, we could have overlapping cordons.
|
||||
// Unfortunately, that means adding logic for cordons not being removed if they have multiple edges and I'm lazy
|
||||
if(!(cordon_turf.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
return FALSE
|
||||
cordon_turfs |= possible_turfs
|
||||
|
||||
return TRUE
|
||||
|
||||
/// Actually generates the cordon around the reservation, and marking the cordon turfs as reserved
|
||||
/datum/turf_reservation/proc/generate_cordon()
|
||||
for(var/turf/cordon_turf as anything in cordon_turfs)
|
||||
var/area/cordon/cordon_area = GLOB.all_unique_areas[/area/cordon] || new /area/cordon
|
||||
|
||||
cordon_area.contents += cordon_turf
|
||||
|
||||
// Its no longer unused, but its also not "used"
|
||||
cordon_turf.turf_flags &= ~UNUSED_RESERVATION_TURF
|
||||
cordon_turf.empty(/turf/cordon)
|
||||
SSmapping.unused_turfs["[cordon_turf.z]"] -= cordon_turf
|
||||
// still gets linked to us though
|
||||
SSmapping.used_turfs[cordon_turf] = src
|
||||
|
||||
/// Internal proc which handles reserving the area for the reservation.
|
||||
/datum/turf_reservation/proc/_reserve_area(width, height, zlevel)
|
||||
src.width = width
|
||||
src.height = height
|
||||
if(width > world.maxx || height > world.maxy || width < 1 || height < 1)
|
||||
return FALSE
|
||||
var/list/avail = SSmapping.unused_turfs["[zlevel]"]
|
||||
var/turf/bottom_left
|
||||
var/turf/top_right
|
||||
var/list/turf/final_turfs = list()
|
||||
var/passing = FALSE
|
||||
for(var/i in avail)
|
||||
CHECK_TICK
|
||||
bottom_left = i
|
||||
if(!(bottom_left.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
continue
|
||||
if(bottom_left.x + width > world.maxx || bottom_left.y + height > world.maxy)
|
||||
continue
|
||||
top_right = locate(bottom_left.x + width - 1, bottom_left.y + height - 1, bottom_left.z)
|
||||
if(!(top_right.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
continue
|
||||
final_turfs = block(bottom_left, top_right)
|
||||
if(!final_turfs)
|
||||
continue
|
||||
passing = TRUE
|
||||
for(var/I in final_turfs)
|
||||
var/turf/checking = I
|
||||
if(!(checking.turf_flags & UNUSED_RESERVATION_TURF))
|
||||
passing = FALSE
|
||||
break
|
||||
if(passing) // found a potentially valid area, now try to calculate its cordon
|
||||
passing = calculate_cordon_turfs(bottom_left, top_right)
|
||||
if(!passing)
|
||||
continue
|
||||
break
|
||||
if(!passing || !istype(bottom_left) || !istype(top_right))
|
||||
return FALSE
|
||||
for(var/turf/T as anything in final_turfs)
|
||||
reserved_turfs |= T
|
||||
SSmapping.unused_turfs["[T.z]"] -= T
|
||||
SSmapping.used_turfs[T] = src
|
||||
T.turf_flags = (T.turf_flags | RESERVATION_TURF) & ~UNUSED_RESERVATION_TURF
|
||||
T.empty(turf_type)
|
||||
|
||||
bottom_left_turf = bottom_left
|
||||
top_right_turf = top_right
|
||||
return TRUE
|
||||
|
||||
/datum/turf_reservation/proc/reserve(width, height, z_reservation)
|
||||
|
||||
if(!_reserve_area(width, height, z_reservation))
|
||||
log_debug("Failed turf reservation: releasing")
|
||||
Release()
|
||||
return FALSE
|
||||
|
||||
log_debug("Turf reservation successful, generating cordon")
|
||||
generate_cordon()
|
||||
return TRUE
|
||||
|
||||
@@ -134,42 +134,3 @@ GLOBAL_DATUM_INIT(space_manager, /datum/zlev_manager, new())
|
||||
z_list.Remove(S)
|
||||
qdel(S)
|
||||
world.maxz--
|
||||
|
||||
|
||||
// An internally-used proc used for heap-zlevel management
|
||||
/datum/zlev_manager/proc/add_new_heap()
|
||||
world.maxz++
|
||||
var/our_z = world.maxz
|
||||
var/datum/space_level/yup = new /datum/space_level/heap(our_z, traits = list(BLOCK_TELEPORT, ADMIN_LEVEL))
|
||||
z_list["[our_z]"] = yup
|
||||
return yup
|
||||
|
||||
// This is what you can call to allocate a section of space
|
||||
// Later, I'll add an argument to let you define the flags on the region
|
||||
/datum/zlev_manager/proc/allocate_space(width, height)
|
||||
if(width > world.maxx || height > world.maxy)
|
||||
throw EXCEPTION("Too much space requested! \[[width],[height]\]")
|
||||
if(!length(heaps))
|
||||
heaps.len++
|
||||
heaps[length(heaps)] = add_new_heap()
|
||||
var/datum/space_level/heap/our_heap
|
||||
var/weve_got_vacancy = 0
|
||||
for(our_heap in heaps)
|
||||
weve_got_vacancy = our_heap.request(width, height)
|
||||
if(weve_got_vacancy)
|
||||
break // We're sticking with the present value of `our_heap` - it's got room
|
||||
// This loop will also run out if no vacancies are found
|
||||
|
||||
if(!weve_got_vacancy)
|
||||
heaps.len++
|
||||
our_heap = add_new_heap()
|
||||
heaps[length(heaps)] = our_heap
|
||||
return our_heap.allocate(width, height)
|
||||
|
||||
/datum/zlev_manager/proc/free_space(datum/space_chunk/C)
|
||||
if(!istype(C))
|
||||
return
|
||||
var/datum/space_level/heap/heap = z_list["[C.zpos]"]
|
||||
if(!istype(heap))
|
||||
throw EXCEPTION("Attempted to free chunk at invalid z-level ([C.x],[C.y],[C.zpos]) [C.width]x[C.height]")
|
||||
heap.free(C)
|
||||
|
||||
Reference in New Issue
Block a user