Merge pull request #6675 from Crazylemon64/monster_allocator_port

Ports Monster's finishing of the space allocator
This commit is contained in:
Fox McCloud
2017-03-07 23:21:50 -05:00
committed by GitHub
3 changed files with 143 additions and 11 deletions
@@ -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()
+68 -1
View File
@@ -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)
@@ -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)