From 36255fffad147396057e16f2a4f0a457b2a97e23 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Fri, 3 Mar 2017 17:15:59 -0800 Subject: [PATCH] Ports Monster's finishing of the space allocator --- .../space_management/heap_space_level.dm | 63 ++++++++++++++--- code/modules/space_management/space_chunk.dm | 69 ++++++++++++++++++- .../space_management/zlevel_manager.dm | 22 +++++- 3 files changed, 143 insertions(+), 11 deletions(-) diff --git a/code/modules/space_management/heap_space_level.dm b/code/modules/space_management/heap_space_level.dm index 8d5a3971ba1..640080095a3 100644 --- a/code/modules/space_management/heap_space_level.dm +++ b/code/modules/space_management/heap_space_level.dm @@ -1,15 +1,62 @@ // This represents a level we can carve up as we please, and hand out // chunks of to whatever requests it /datum/space_level/heap - var/datum/space_chunk/top - linkage = UNAFFECTED + name = "Heap level #ERROR" + var/datum/space_chunk/top + linkage = UNAFFECTED -/datum/space_level/heap/New() - ..() - top = new +/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 1 // All are welcome! At least until I add code for this + return top.can_fit_space(width, height) -/datum/zlevel/heap/proc/allocate(width, height) - return +// 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(locate(C.x, C.y, C.zpos), locate(C.x+C.width-1, C.y+C.height-1, C.zpos))) + for(var/atom/movable/M in T) + if(istype(M, /mob/dead/observer)) + continue + M.loc = null + qdel(M, TRUE) + T.ChangeTurf(/turf/space) + 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() diff --git a/code/modules/space_management/space_chunk.dm b/code/modules/space_management/space_chunk.dm index a150d25d2f3..8b6e5b6974e 100644 --- a/code/modules/space_management/space_chunk.dm +++ b/code/modules/space_management/space_chunk.dm @@ -10,14 +10,81 @@ var/width var/height var/zpos + // Whether dedicated for use or not + var/occupied = FALSE + // Whether the chunk contains used children or not + var/is_empty = TRUE + var/list/children = list() + var/datum/space_chunk/parent = null -/datum/space_chunk/New(w, h, z, new_x, new_y) +/datum/space_chunk/New(new_x, new_y, z, w, h) x = new_x y = new_y zpos = z width = w height = h +/datum/space_chunk/Destroy() + set_occupied(FALSE) + if(parent) + parent.children -= src + for(var/datum/space_chunk/child in children) + qdel(child) + children.Cut() + parent = null + . = ..() + +/datum/space_chunk/proc/can_fit_space(w, h) + if(w > width || h > height) + return FALSE + if(occupied) + return FALSE + if(is_empty) + return TRUE + for(var/datum/space_chunk/C in children) + if(C.can_fit_space(w, h)) + return TRUE + return FALSE + +// Recursively drops down the tree and finds the most efficient chunk to use +/datum/space_chunk/proc/get_optimal_chunk(w, h) + if(w > width || h > height) + return null + if(occupied) + return null + if(is_empty) + return src + var/datum/space_chunk/optimal_chunk + var/optimal_chunk_optimalness = 99999 + for(var/datum/space_chunk/C in children) + var/datum/space_chunk/C2 = C.get_optimal_chunk(w, h) + if(!C2) + continue + var/optimalness = C2.width + C2.height-w-h + if(optimalness < optimal_chunk_optimalness) + optimal_chunk_optimalness = optimalness + optimal_chunk = C2 + return optimal_chunk + +/datum/space_chunk/proc/set_occupied(new_occupied) + var/datum/space_chunk/C = parent + if(new_occupied) + occupied = TRUE + while(C) + C.is_empty = FALSE + C = C.parent + else + occupied = FALSE + while(C) + var/is_children_empty = TRUE + for(var/datum/space_chunk/C2 in C.children) + if(!C2.is_empty || C2.occupied) + is_children_empty = FALSE + if(!is_children_empty) + break + C.is_empty = TRUE + C = C.parent + /datum/space_chunk/proc/check_sanity() var/i_am_sane = 1 i_am_sane |= (x > 0) diff --git a/code/modules/space_management/zlevel_manager.dm b/code/modules/space_management/zlevel_manager.dm index d0bb9455eb8..087a70657ed 100644 --- a/code/modules/space_management/zlevel_manager.dm +++ b/code/modules/space_management/zlevel_manager.dm @@ -134,18 +134,36 @@ var/global/datum/zlev_manager/space_manager = new /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) + 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(!heaps.len) heaps.len++ heaps[heaps.len] = add_new_heap() var/datum/space_level/heap/our_heap + var/weve_got_vacancy = 0 for(our_heap in heaps) - var/weve_got_vacancy = our_heap.request(width, height) + 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[heaps.len] = 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)