mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-30 03:22:41 +00:00
* 'optimizes' space transitions by like 0.06 seconds, makes them easier to read tho, so that's an upside * ''''optimizes'''' parsed map loading I'm honestly not sure how big a difference this makes, looked like small percentage points if anything It's a bit more internally concistent at least, which is nice. Also I understand the system now. I'd like to think it helped but I think this is kinda a "do you think it's easier to read" sort of situation. if it did help it was by the skin of its teeth * Saves 0.6 seconds off loading meta and lavaland's map files This is just a lot of micro stuff. 1: Bound checks don't need to be inside for loops, we can instead bound the iteration counts 2: TGM and DMM are parsed differently. in dmm a grid_set is one z level, in tgm it's one collumn. Realizing this allows you to skip copytexts and other such silly in the tgm implemenentation, saving a good bit of time 3: Min/max bounds do not need to be checked inside for loops, and can instead be handled outside of them, because we know the order of x and y iteration. This saves 0.2 seconds I may or may not have made the code harder to read, if so let me know and I'll check it over. * Micro ops key caching significantly. Fixes macros bug inserting \ into a dmm with no valid target would just less then loop the string. Dumb Anyway, optimizations. I save a LOT of time by not needing to call find_next_delimiter_position for every entry and var set. (like maybe 0.5 seconds, not totally sure) I save this by using splittext, which is significantly faster. this would cause parsing issues if you could embed \n into dmms, but you can't, so I'm safe. Lemme see uh, lots of little things, stuff that's suboptimal or could be done cheaper. Some "hey you and I both know a \" is 2 chars long sort of stuff I removed trim_text because the quote trimming was never actually used, and the space trimming was slower then using the code in trim. I also micro'd trim to save a bit of time. this saves another maybe 0.5. Few other things, I think that's the main of it. Gives me the fuzzy feelings * Saves 50% of build_coordinate's time Micro optimizing go brrrrr I made turf_blacklist an assoc list rather then just a normal one, so lookups are O(log n) instead of O(n). Also it's faster for the base case of loading mostly space. Instead of toggling the map loader right before and right after New() calls, we toggle at the start of mapload, and disable then reenable if we check tick. This saves like 0.3 seconds Rather then tracking an area cache ourselves, and needing to pass it around, we use a locally static list to reference the global list of area -> type. This is much faster, if slightly fragile. Rather then checking for a null turf at every line, we do it at the start of the proc and not after. Faster this way, tho it can in theory drop area vvs. Avoids calling world.preloader_setup unless we actually have a unique set of attributes. We use another static list to make this comparison cheap. This saves another 0.3 Rather then checking for area paths in the turf logic, or vis versa, we assume we are creating the type implied by the index we're reading off. So only the last type entry will be loaded like a turf, etc. This is slightly unsafe but saves a good bit of time, and will properly error on fucked maps. Also, rather then using a datum to hold preloader vars, we use 2 global variables. This is faster. This marks the end of my optimizations for direct maploading. I've reduced the cost of loading a map by more then 50% now. Get owned. * Adds a define for maploading tick check * makes shuttles load again, removes some of the hard limits I had on the reader for profiling * Macro ops cave generation Cave generation was insanely more expensive then it had any right to be. Maybe 0.5 seconds was saved off not doing a range(12) for EVERY SPAWNED MOB. 0.14 was saved off using expanded weighted lists (A new idea of mine) This is useful because I can take a weighted list, and condense it into weight * path count. This is more memory heavy, and costs more to create, but is so much faster then the proc. I also added a naive implementation of gcd to make this a bit less bad. It's not great, but it'll do for this usecase. Oh and I changed some ChangeTurfs into New()s. I'm still not entirely sure what the core difference between the two is, but it seems to work fine. I believe it's safe because the turf below us hasn't init'd yet, there's nothing to take from them. It's like 3 seconds faster too so I'll be sad when it turns out I'm being dumb * Micros river spawning This uses the same sort of concepts as the last change, mostly New being preferable to ChangeTurf at this level of code. This bit isn't nearly as detailed as the last few, I honestly got a bit tired. It's still like 0.4 seconds saved tho * Micros ruin loading Turns out it saves time if you don't check area type for every tile on a ruin. Not a whole ton faster, like 0.03, but faster. Saves even more time (0.1) to not iterate all your ruin's turfs 3 times to clear away lavaland mobs, when you're IN SPACE who wrote this. Oh it also saves time to only pull your turf list once, rather then 3 times
241 lines
7.9 KiB
Plaintext
241 lines
7.9 KiB
Plaintext
/datum/map_template
|
|
var/name = "Default Template Name"
|
|
var/width = 0
|
|
var/height = 0
|
|
var/mappath = null
|
|
var/loaded = 0 // Times loaded this round
|
|
var/datum/parsed_map/cached_map
|
|
var/keep_cached_map = FALSE
|
|
var/station_id = null // used to override the root id when generating
|
|
|
|
///Default area associated with the map template
|
|
var/default_area
|
|
|
|
///if true, turfs loaded from this template are placed on top of the turfs already there, defaults to TRUE
|
|
var/should_place_on_top = TRUE
|
|
|
|
///if true, creates a list of all atoms created by this template loading, defaults to FALSE
|
|
var/returns_created_atoms = FALSE
|
|
|
|
///the list of atoms created by this template being loaded, only populated if returns_created_atoms is TRUE
|
|
var/list/created_atoms = list()
|
|
//make sure this list is accounted for/cleared if you request it from ssatoms!
|
|
|
|
// vars for automatic ceiling generation
|
|
var/has_ceiling = FALSE
|
|
var/turf/ceiling_turf = /turf/open/floor/plating
|
|
var/list/ceiling_baseturfs = list()
|
|
|
|
/datum/map_template/New(path = null, rename = null, cache = FALSE)
|
|
if(path)
|
|
mappath = path
|
|
if(mappath)
|
|
preload_size(mappath, cache)
|
|
if(rename)
|
|
name = rename
|
|
ceiling_baseturfs.Insert(1, /turf/baseturf_bottom)
|
|
|
|
/datum/map_template/proc/preload_size(path, cache = FALSE)
|
|
var/datum/parsed_map/parsed = new(file(path))
|
|
var/bounds = parsed?.bounds
|
|
if(bounds)
|
|
width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1
|
|
height = bounds[MAP_MAXY]
|
|
if(cache)
|
|
cached_map = parsed
|
|
return bounds
|
|
|
|
/datum/map_template/proc/initTemplateBounds(list/bounds)
|
|
if (!bounds) //something went wrong
|
|
stack_trace("[name] template failed to initialize correctly!")
|
|
return
|
|
|
|
var/list/obj/machinery/atmospherics/atmos_machines = list()
|
|
var/list/obj/structure/cable/cables = list()
|
|
var/list/atom/movable/movables = list()
|
|
var/list/obj/docking_port/stationary/ports = list()
|
|
var/list/area/areas = list()
|
|
|
|
var/list/turfs = block(
|
|
locate(
|
|
bounds[MAP_MINX],
|
|
bounds[MAP_MINY],
|
|
bounds[MAP_MINZ]
|
|
),
|
|
locate(
|
|
bounds[MAP_MAXX],
|
|
bounds[MAP_MAXY],
|
|
bounds[MAP_MAXZ]
|
|
)
|
|
)
|
|
for(var/turf/current_turf as anything in turfs)
|
|
var/area/current_turfs_area = current_turf.loc
|
|
areas |= current_turfs_area
|
|
if(!SSatoms.initialized)
|
|
continue
|
|
|
|
for(var/movable_in_turf in current_turf)
|
|
movables += movable_in_turf
|
|
if(istype(movable_in_turf, /obj/structure/cable))
|
|
cables += movable_in_turf
|
|
continue
|
|
if(istype(movable_in_turf, /obj/machinery/atmospherics))
|
|
atmos_machines += movable_in_turf
|
|
if(istype(movable_in_turf, /obj/docking_port/stationary))
|
|
ports += movable_in_turf
|
|
|
|
// Not sure if there is some importance here to make sure the area is in z
|
|
// first or not. Its defined In Initialize yet its run first in templates
|
|
// BEFORE so... hummm
|
|
SSmapping.reg_in_areas_in_z(areas)
|
|
SSnetworks.assign_areas_root_ids(areas, src)
|
|
if(!SSatoms.initialized)
|
|
return
|
|
|
|
SSatoms.InitializeAtoms(areas + turfs + movables, returns_created_atoms ? created_atoms : null)
|
|
|
|
for(var/turf/unlit as anything in turfs)
|
|
if(unlit.always_lit)
|
|
continue
|
|
var/area/loc_area = unlit.loc
|
|
if(!loc_area.static_lighting)
|
|
continue
|
|
unlit.lighting_build_overlay()
|
|
|
|
// NOTE, now that Initialize and LateInitialize run correctly, do we really
|
|
// need these two below?
|
|
SSmachines.setup_template_powernets(cables)
|
|
SSair.setup_template_machinery(atmos_machines)
|
|
SSshuttle.setup_shuttles(ports)
|
|
|
|
//calculate all turfs inside the border
|
|
var/list/template_and_bordering_turfs = block(
|
|
locate(
|
|
max(bounds[MAP_MINX]-1, 1),
|
|
max(bounds[MAP_MINY]-1, 1),
|
|
bounds[MAP_MINZ]
|
|
),
|
|
locate(
|
|
min(bounds[MAP_MAXX]+1, world.maxx),
|
|
min(bounds[MAP_MAXY]+1, world.maxy),
|
|
bounds[MAP_MAXZ]
|
|
)
|
|
)
|
|
for(var/turf/affected_turf as anything in template_and_bordering_turfs)
|
|
affected_turf.air_update_turf(TRUE, TRUE)
|
|
affected_turf.levelupdate()
|
|
|
|
/datum/map_template/proc/load_new_z(secret = FALSE)
|
|
var/x = round((world.maxx - width) * 0.5) + 1
|
|
var/y = round((world.maxy - height) * 0.5) + 1
|
|
|
|
var/datum/space_level/level = SSmapping.add_new_zlevel(name, secret ? ZTRAITS_AWAY_SECRET : ZTRAITS_AWAY)
|
|
var/datum/parsed_map/parsed = load_map(file(mappath), x, y, level.z_value, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=should_place_on_top)
|
|
var/list/bounds = parsed.bounds
|
|
if(!bounds)
|
|
return FALSE
|
|
|
|
repopulate_sorted_areas()
|
|
|
|
//initialize things that are normally initialized after map load
|
|
initTemplateBounds(bounds)
|
|
smooth_zlevel(world.maxz)
|
|
log_game("Z-level [name] loaded at [x],[y],[world.maxz]")
|
|
|
|
return level
|
|
|
|
/datum/map_template/proc/load(turf/T, centered = FALSE)
|
|
if(centered)
|
|
T = locate(T.x - round(width/2) , T.y - round(height/2) , T.z)
|
|
if(!T)
|
|
return
|
|
if(T.x+width > world.maxx)
|
|
return
|
|
if(T.y+height > world.maxy)
|
|
return
|
|
|
|
var/list/border = block(locate(max(T.x-1, 1), max(T.y-1, 1), T.z),
|
|
locate(min(T.x+width+1, world.maxx), min(T.y+height+1, world.maxy), T.z))
|
|
for(var/L in border)
|
|
var/turf/turf_to_disable = L
|
|
SSair.remove_from_active(turf_to_disable) //stop processing turfs along the border to prevent runtimes, we return it in initTemplateBounds()
|
|
turf_to_disable.atmos_adjacent_turfs?.Cut()
|
|
|
|
// Accept cached maps, but don't save them automatically - we don't want
|
|
// ruins clogging up memory for the whole round.
|
|
var/datum/parsed_map/parsed = cached_map || new(file(mappath))
|
|
cached_map = keep_cached_map ? parsed : null
|
|
|
|
var/list/turf_blacklist = list()
|
|
update_blacklist(T, turf_blacklist)
|
|
|
|
UNSETEMPTY(turf_blacklist)
|
|
parsed.turf_blacklist = turf_blacklist
|
|
if(!parsed.load(T.x, T.y, T.z, cropMap=TRUE, no_changeturf=(SSatoms.initialized == INITIALIZATION_INSSATOMS), placeOnTop=should_place_on_top))
|
|
return
|
|
var/list/bounds = parsed.bounds
|
|
if(!bounds)
|
|
return
|
|
|
|
if(!SSmapping.loading_ruins) //Will be done manually during mapping ss init
|
|
repopulate_sorted_areas()
|
|
|
|
//initialize things that are normally initialized after map load
|
|
initTemplateBounds(bounds)
|
|
|
|
if(has_ceiling)
|
|
var/affected_turfs = get_affected_turfs(T, FALSE)
|
|
generate_ceiling(affected_turfs)
|
|
|
|
log_game("[name] loaded at [T.x],[T.y],[T.z]")
|
|
return bounds
|
|
|
|
/datum/map_template/proc/generate_ceiling(affected_turfs)
|
|
for (var/turf/turf in affected_turfs)
|
|
var/turf/ceiling = get_step_multiz(turf, UP)
|
|
if (ceiling)
|
|
if (istype(ceiling, /turf/open/openspace) || istype(ceiling, /turf/open/space/openspace))
|
|
ceiling.ChangeTurf(ceiling_turf, ceiling_baseturfs, CHANGETURF_INHERIT_AIR)
|
|
|
|
/datum/map_template/proc/post_load()
|
|
return
|
|
|
|
/datum/map_template/proc/update_blacklist(turf/T, list/input_blacklist)
|
|
return
|
|
|
|
/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
|
|
var/turf/placement = T
|
|
if(centered)
|
|
var/turf/corner = locate(placement.x - round(width/2), placement.y - round(height/2), placement.z)
|
|
if(corner)
|
|
placement = corner
|
|
return block(placement, locate(placement.x+width-1, placement.y+height-1, placement.z))
|
|
|
|
/// Takes in a type path, locates an instance of that type in the cached map, and calculates its offset from the origin of the map, returns this offset in the form list(x, y).
|
|
/datum/map_template/proc/discover_offset(obj/marker)
|
|
var/key
|
|
var/list/models = cached_map.grid_models
|
|
for(key in models)
|
|
if(findtext(models[key], "[marker]")) // Yay compile time checks
|
|
break // This works by assuming there will ever only be one mobile dock in a template at most
|
|
|
|
for(var/datum/grid_set/gset as anything in cached_map.gridSets)
|
|
var/ycrd = gset.ycrd
|
|
for(var/line in gset.gridLines)
|
|
var/xcrd = gset.xcrd
|
|
for(var/j in 1 to length(line) step cached_map.key_len)
|
|
if(key == copytext(line, j, j + cached_map.key_len))
|
|
return list(xcrd, ycrd)
|
|
++xcrd
|
|
--ycrd
|
|
|
|
|
|
//for your ever biggening badminnery kevinz000
|
|
//❤ - Cyberboss
|
|
/proc/load_new_z_level(file, name, secret)
|
|
var/datum/map_template/template = new(file, name, TRUE)
|
|
if(!template.cached_map || template.cached_map.check_for_errors())
|
|
return FALSE
|
|
template.load_new_z(secret)
|
|
return TRUE
|