mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-07-13 16:13:49 +01:00
Add bapi-dmm to reduce startup time by... a lot (#16148)
* Add bapi-dmm, a rust maploader that goes BRRRRRRR * A few more maps switched away from file paths * Update to include 48f4879d8704cb057a (better path error handling)
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
//THIS FILE IS AUTOMATICALLY GENERATED BY BAPI_DMM_READER, PLEASE DO NOT TOUCH IT
|
||||
//PROC DEFINITIONS MAY MOVE AROUND, THIS IS NORMAL
|
||||
|
||||
/* This comment bypasses grep checks */ /var/__bapi_dmm_reader
|
||||
|
||||
/proc/__detect_bapi_dmm_reader()
|
||||
if (world.system_type == UNIX)
|
||||
if(fexists("./libbapi_dmm_reader.so"))
|
||||
// No need for LD_LIBRARY_PATH badness.
|
||||
return __bapi_dmm_reader = "./libbapi_dmm_reader.so"
|
||||
// It's not in the current directory, so try others
|
||||
return __bapi_dmm_reader = "libbapi_dmm_reader"
|
||||
else
|
||||
return __bapi_dmm_reader = "bapi_dmm_reader"
|
||||
|
||||
#define BAPI_DMM_READER (__bapi_dmm_reader || __detect_bapi_dmm_reader())
|
||||
|
||||
/proc/_bapidmm_test_connection()
|
||||
return call_ext(BAPI_DMM_READER, "byond:_bapidmm_test_connection_ffi")()
|
||||
|
||||
/proc/_bapidmm_parse_map_blocking(dmm_file, map_datum)
|
||||
return call_ext(BAPI_DMM_READER, "byond:_bapidmm_parse_map_blocking_ffi")(dmm_file, map_datum)
|
||||
|
||||
/proc/_bapidmm_load_map_buffered(parsed_map, x_offset, y_offset, z_offset, crop_map, no_changeturf, x_lower,
|
||||
x_upper, y_lower, y_upper, z_lower, z_upper, place_on_top, new_z)
|
||||
return call_ext(BAPI_DMM_READER, "byond:_bapidmm_load_map_buffered_ffi")(parsed_map, x_offset, y_offset, z_offset, crop_map, no_changeturf, x_lower,
|
||||
x_upper, y_lower, y_upper, z_lower, z_upper, place_on_top, new_z)
|
||||
|
||||
/proc/_bapidmm_work_commandbuffer(parsed_map, resume_key)
|
||||
return call_ext(BAPI_DMM_READER, "byond:_bapidmm_work_commandbuffer_ffi")(parsed_map, resume_key)
|
||||
|
||||
/proc/_bapidmm_clear_map_data()
|
||||
return call_ext(BAPI_DMM_READER, "byond:_bapidmm_clear_map_data_ffi")()
|
||||
|
||||
/proc/bapidmm_generate_automata(limit_x, limit_y, iterations, initial_wall_cell)
|
||||
return call_ext(BAPI_DMM_READER, "byond:bapidmm_generate_automata_ffi")(limit_x, limit_y, iterations, initial_wall_cell)
|
||||
@@ -0,0 +1,274 @@
|
||||
// This file provides manually written utility types and such for the BAPI DMM Reader
|
||||
|
||||
GLOBAL_LIST_EMPTY(cached_maps)
|
||||
|
||||
GLOBAL_VAR_INIT(use_preloader, FALSE)
|
||||
GLOBAL_VAR_INIT(_preloader_path, null)
|
||||
GLOBAL_LIST_EMPTY(_preloader_attributes)
|
||||
|
||||
/world/proc/preloader_setup(list/the_attributes, path)
|
||||
if(LAZYLEN(the_attributes))
|
||||
GLOB.use_preloader = TRUE
|
||||
GLOB._preloader_attributes = the_attributes
|
||||
GLOB._preloader_path = path
|
||||
|
||||
/world/proc/preloader_load(atom/what)
|
||||
GLOB.use_preloader = FALSE
|
||||
var/list/attributes = GLOB._preloader_attributes
|
||||
for(var/attribute in attributes)
|
||||
var/value = attributes[attribute]
|
||||
if(islist(value))
|
||||
value = deepCopyList(value)
|
||||
#ifdef TESTING
|
||||
if(what.vars[attribute] == value)
|
||||
var/message = "<font color=green>[what.type]</font> at [AREACOORD(what)] - <b>VAR:</b> <font color=red>[attribute] = [isnull(value) ? "null" : (isnum(value) ? value : "\"[value]\"")]</font>"
|
||||
world.log << "DIRTY VAR: [message]"
|
||||
dirty_vars += message
|
||||
#endif
|
||||
what.vars[attribute] = value
|
||||
|
||||
#define MAP_DMM "dmm"
|
||||
/**
|
||||
* TGM SPEC:
|
||||
* TGM is a derevation of DMM, with restrictions placed on it
|
||||
* to make it easier to parse and to reduce merge conflicts/ease their resolution
|
||||
*
|
||||
* Requirements:
|
||||
* Each "statement" in a key's details ends with a new line, and wrapped in (...)
|
||||
* All paths end with either a comma or occasionally a {, then a new line
|
||||
* Excepting the area, who is listed last and ends with a ) to mark the end of the key
|
||||
*
|
||||
* {} denotes a list of variable edits applied to the path that came before the first {
|
||||
* the final } is followed by a comma, and then a new line
|
||||
* Variable edits have the form \tname = value;\n
|
||||
* Except the last edit, which has no final ;, and just ends in a newline
|
||||
* No extra padding is permitted
|
||||
* Many values are supported. See parse_constant()
|
||||
* Strings must be wrapped in "...", files in '...', and lists in list(...)
|
||||
* Files are kinda susy, and may not actually work. buyer beware
|
||||
* Lists support assoc values as expected
|
||||
* These constants can be further embedded into lists
|
||||
*
|
||||
* There can be no padding in front of, or behind a path
|
||||
*
|
||||
* Therefore:
|
||||
* "key" = (
|
||||
* /path,
|
||||
* /other/path{
|
||||
* var = list("name" = 'filepath');
|
||||
* other_var = /path
|
||||
* },
|
||||
* /turf,
|
||||
* /area)
|
||||
*
|
||||
*/
|
||||
#define MAP_TGM "tgm"
|
||||
#define MAP_UNKNOWN "unknown"
|
||||
|
||||
/// Returned from parse_map to give some metadata about the map
|
||||
/datum/bapi_parsed_map
|
||||
var/_internal_index = -1
|
||||
|
||||
var/original_path = ""
|
||||
var/map_format = MAP_UNKNOWN
|
||||
var/key_len = 0
|
||||
var/line_len = 0
|
||||
var/expanded_y = FALSE
|
||||
var/expanded_x = FALSE
|
||||
|
||||
/// Unoffset bounds. Null on parse failure.
|
||||
var/list/bounds = list()
|
||||
/// Offset bounds. Same as parsed_bounds until load().
|
||||
var/list/parsed_bounds = list()
|
||||
|
||||
///any turf in this list is skipped inside of build_coordinate. Lazy assoc list
|
||||
var/list/turf_blacklist
|
||||
|
||||
var/loading = FALSE
|
||||
var/loaded_warnings = list()
|
||||
|
||||
/**
|
||||
* Helper and recommened way to load a map file
|
||||
* - dmm_file: The path to the map file
|
||||
* - x_offset: The x offset to load the map at
|
||||
* - y_offset: The y offset to load the map at
|
||||
* - z_offset: The z offset to load the map at
|
||||
* - crop_map: If true, the map will be cropped to the world bounds
|
||||
* - measure_only: If true, the map will not be loaded, but the bounds will be calculated
|
||||
* - no_changeturf: If true, the map will not call /turf/AfterChange
|
||||
* - x_lower: The minimum x coordinate to load
|
||||
* - x_upper: The maximum x coordinate to load
|
||||
* - y_lower: The minimum y coordinate to load
|
||||
* - y_upper: The maximum y coordinate to load
|
||||
* - z_lower: The minimum z coordinate to load
|
||||
* - z_upper: The maximum z coordinate to load
|
||||
* - place_on_top: Whether to use /turf/proc/PlaceOnTop rather than /turf/proc/ChangeTurf
|
||||
* - new_z: If true, a new z level will be created for the map
|
||||
*/
|
||||
/proc/load_map_bapi(
|
||||
dmm_file,
|
||||
x_offset = 1,
|
||||
y_offset = 1,
|
||||
z_offset,
|
||||
crop_map = FALSE,
|
||||
measure_only = FALSE,
|
||||
no_changeturf = FALSE,
|
||||
x_lower = -INFINITY,
|
||||
x_upper = INFINITY,
|
||||
y_lower = -INFINITY,
|
||||
y_upper = INFINITY,
|
||||
z_lower = -INFINITY,
|
||||
z_upper = INFINITY,
|
||||
place_on_top = FALSE,
|
||||
new_z = FALSE,
|
||||
)
|
||||
if(isfile(dmm_file))
|
||||
log_debug("bapi-dmm was passed a file instead of a path string: [dmm_file]")
|
||||
var/name = sanitize_filename("[dmm_file]")
|
||||
var/path = "data/baked_dmm_files/[name]"
|
||||
if(!fexists(path))
|
||||
var/text = file2text(dmm_file)
|
||||
rustg_file_write(text, path)
|
||||
dmm_file = path
|
||||
|
||||
if(!fexists(dmm_file))
|
||||
stack_trace("Invalid map path: [dmm_file]")
|
||||
return
|
||||
|
||||
if(!(dmm_file in GLOB.cached_maps))
|
||||
GLOB.cached_maps[dmm_file] = new /datum/bapi_parsed_map(dmm_file)
|
||||
|
||||
// Bay assumption
|
||||
if(!z_offset)
|
||||
z_offset = world.maxz + 1
|
||||
|
||||
var/datum/bapi_parsed_map/parsed_map = GLOB.cached_maps[dmm_file]
|
||||
parsed_map = parsed_map.copy()
|
||||
if(!measure_only && !isnull(parsed_map.bounds))
|
||||
parsed_map.load(x_offset, y_offset, z_offset, crop_map, no_changeturf, x_lower, x_upper, y_lower, y_upper, z_lower, z_upper, place_on_top, new_z)
|
||||
return parsed_map
|
||||
|
||||
/datum/bapi_parsed_map/New(tfile)
|
||||
if(isnull(tfile))
|
||||
return // create a new datum without loading a map
|
||||
var/ret = _bapidmm_parse_map_blocking(tfile, src)
|
||||
if(!ret)
|
||||
CRASH("Failed to load map [tfile], check rust_log.txt")
|
||||
|
||||
/datum/bapi_parsed_map/Destroy()
|
||||
..()
|
||||
SSatoms.map_loader_stop(REF(src)) // Just in case, I don't want to double up here
|
||||
if(turf_blacklist)
|
||||
turf_blacklist.Cut()
|
||||
parsed_bounds.Cut()
|
||||
bounds.Cut()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/datum/bapi_parsed_map/proc/copy()
|
||||
// Avoids duped work just in case
|
||||
var/datum/bapi_parsed_map/newfriend = new()
|
||||
// use the same under-the-hood data
|
||||
newfriend._internal_index = _internal_index
|
||||
newfriend.original_path = original_path
|
||||
newfriend.map_format = map_format
|
||||
newfriend.key_len = key_len
|
||||
newfriend.line_len = line_len
|
||||
newfriend.parsed_bounds = parsed_bounds.Copy()
|
||||
// Copy parsed bounds to reset to initial values
|
||||
newfriend.bounds = parsed_bounds.Copy()
|
||||
newfriend.turf_blacklist = turf_blacklist?.Copy()
|
||||
// Explicitly do NOT copy `loaded` and `loaded_warnings`
|
||||
return newfriend
|
||||
|
||||
/datum/bapi_parsed_map/proc/load(
|
||||
x_offset = 1,
|
||||
y_offset = 1,
|
||||
z_offset = 1,
|
||||
crop_map = FALSE,
|
||||
no_changeturf = FALSE,
|
||||
x_lower = -INFINITY,
|
||||
x_upper = INFINITY,
|
||||
y_lower = -INFINITY,
|
||||
y_upper = INFINITY,
|
||||
z_lower = -INFINITY,
|
||||
z_upper = INFINITY,
|
||||
place_on_top = FALSE,
|
||||
new_z = FALSE,
|
||||
)
|
||||
Master.StartLoadingMap()
|
||||
. = _load_impl(x_offset, y_offset, z_offset, crop_map, no_changeturf, x_lower, x_upper, y_lower, y_upper, z_lower, z_upper, place_on_top, new_z)
|
||||
Master.StopLoadingMap()
|
||||
|
||||
/datum/bapi_parsed_map/proc/_load_impl(
|
||||
x_offset = 1,
|
||||
y_offset = 1,
|
||||
z_offset = 1,
|
||||
crop_map = FALSE,
|
||||
no_changeturf = FALSE,
|
||||
x_lower = -INFINITY,
|
||||
x_upper = INFINITY,
|
||||
y_lower = -INFINITY,
|
||||
y_upper = INFINITY,
|
||||
z_lower = -INFINITY,
|
||||
z_upper = INFINITY,
|
||||
place_on_top = FALSE,
|
||||
new_z = FALSE,
|
||||
)
|
||||
PRIVATE_PROC(TRUE)
|
||||
SSatoms.map_loader_begin(REF(src))
|
||||
|
||||
// `loading` var handled by bapidmm
|
||||
var/resume_key = _bapidmm_load_map_buffered(
|
||||
src,
|
||||
x_offset,
|
||||
y_offset,
|
||||
z_offset,
|
||||
crop_map,
|
||||
no_changeturf,
|
||||
x_lower,
|
||||
x_upper,
|
||||
y_lower,
|
||||
y_upper,
|
||||
z_lower,
|
||||
z_upper,
|
||||
place_on_top,
|
||||
new_z
|
||||
)
|
||||
|
||||
if(!resume_key)
|
||||
SSatoms.map_loader_stop(REF(src))
|
||||
CRASH("Failed to generate command buffer, check rust_log.txt and other runtimes")
|
||||
|
||||
var/work_remaining = FALSE
|
||||
do
|
||||
work_remaining = _bapidmm_work_commandbuffer(src, resume_key)
|
||||
stoplag()
|
||||
while(work_remaining)
|
||||
|
||||
SSatoms.map_loader_stop(REF(src))
|
||||
|
||||
// if(new_z)
|
||||
// for(var/z_index in bounds[MAP_MINZ] to bounds[MAP_MAXZ])
|
||||
// SSmapping.build_area_turfs(z_index)
|
||||
|
||||
// if(!no_changeturf)
|
||||
// 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/T as anything in turfs)
|
||||
// //we do this after we load everything in. if we don't, we'll have weird atmos bugs regarding atmos adjacent turfs
|
||||
// T.AfterChange(CHANGETURF_IGNORE_AIR)
|
||||
|
||||
// if(expanded_x || expanded_y)
|
||||
// SEND_GLOBAL_SIGNAL(COMSIG_GLOB_EXPANDED_WORLD_BOUNDS, expanded_x, expanded_y)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/bapi_parsed_map/proc/has_warnings()
|
||||
if(length(loaded_warnings))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// #undef MAP_DMM
|
||||
// #undef MAP_TGM
|
||||
// #undef MAP_UNKNOWN
|
||||
@@ -0,0 +1,110 @@
|
||||
// Internal bapi-dmm helpers
|
||||
/datum/bapi_parsed_map/proc/_bapi_add_warning(warning)
|
||||
loaded_warnings += list(warning)
|
||||
|
||||
/datum/bapi_parsed_map/proc/_bapi_expand_map(x, y, z, new_z, z_offset)
|
||||
if(x > world.maxx)
|
||||
expanded_x = TRUE
|
||||
world.maxx = x
|
||||
// if(new_z)
|
||||
// world.increase_max_x(x, map_load_z_cutoff = z_offset - 1)
|
||||
// else
|
||||
// world.increase_max_x(x)
|
||||
if(y > world.maxy)
|
||||
expanded_y = TRUE
|
||||
world.maxy = y
|
||||
// if(new_z)
|
||||
// world.increase_max_y(y, map_load_z_cutoff = z_offset - 1)
|
||||
// else
|
||||
// world.increase_max_y(y)
|
||||
if(z > world.maxz)
|
||||
while(world.maxz < z)
|
||||
world.increment_max_z()
|
||||
|
||||
/proc/_bapi_helper_get_world_bounds()
|
||||
. = list(world.maxx, world.maxy, world.maxz)
|
||||
|
||||
/proc/_bapi_helper_text2path(text)
|
||||
. = text2path(text)
|
||||
|
||||
/proc/_bapi_helper_text2file(text)
|
||||
. = file(text)
|
||||
|
||||
/proc/_bapi_create_atom(path, crds)
|
||||
set waitfor = FALSE
|
||||
. = new path (crds)
|
||||
|
||||
/proc/_bapi_setup_preloader(list/attributes, path)
|
||||
world.preloader_setup(attributes, path)
|
||||
|
||||
/proc/_bapi_apply_preloader(atom/A)
|
||||
if(GLOB.use_preloader)
|
||||
world.preloader_load(A)
|
||||
|
||||
/proc/_bapi_new_atom(text_path, turf/crds, list/attributes)
|
||||
var/path = text2path(text_path)
|
||||
if(!path)
|
||||
CRASH("Bad atom path [text_path]")
|
||||
|
||||
if(attributes != null)
|
||||
world.preloader_setup(attributes, path)
|
||||
|
||||
var/atom/instance = _bapi_create_atom(path, crds) // first preloader pass
|
||||
|
||||
if(GLOB.use_preloader && instance) // second preloader pass for atoms that don't ..() in New()
|
||||
world.preloader_load(instance)
|
||||
|
||||
/proc/_bapi_create_or_get_area(text_path)
|
||||
var/path = text2path(text_path)
|
||||
if(!path)
|
||||
CRASH("Bad area path [text_path]")
|
||||
|
||||
var/area/area_instance = GLOB.areas_by_type[path]
|
||||
if(!area_instance)
|
||||
area_instance = new path(null)
|
||||
if(!area_instance)
|
||||
CRASH("[path] failed to be new'd, what'd you do?")
|
||||
|
||||
return area_instance
|
||||
|
||||
/proc/_bapi_handle_area_contain(turf/T, area/A)
|
||||
// var/area/old_area = T.loc
|
||||
// if(old_area == A)
|
||||
// return // no changing areas that already contain this turf, it'll confuse them
|
||||
// LISTASSERTLEN(old_area.turfs_to_uncontain_by_zlevel, T.z, list())
|
||||
// LISTASSERTLEN(A.turfs_by_zlevel, T.z, list())
|
||||
// old_area.turfs_to_uncontain_by_zlevel[T.z] += T
|
||||
// A.turfs_by_zlevel[T.z] += T
|
||||
|
||||
/proc/_bapi_create_turf(turf/crds, text_path, list/attributes, place_on_top, no_changeturf)
|
||||
var/path = text2path(text_path)
|
||||
if(!path)
|
||||
CRASH("Bad turf path [text_path]")
|
||||
|
||||
if(attributes != null)
|
||||
world.preloader_setup(attributes, path)
|
||||
|
||||
var/atom/instance
|
||||
if(no_changeturf)
|
||||
instance = _bapi_create_atom(path, crds)
|
||||
else
|
||||
instance = crds.ChangeTurf(path, FALSE, TRUE)
|
||||
|
||||
if(GLOB.use_preloader && instance) // second preloader pass for atoms that don't ..() in New()
|
||||
world.preloader_load(instance)
|
||||
|
||||
/proc/_bapi_add_turf_to_area(area/A, turf/T)
|
||||
if(!A || !T)
|
||||
return
|
||||
A.contents.Add(T)
|
||||
|
||||
/proc/_bapi_helper_get_world_type_turf()
|
||||
return "[world.turf]"
|
||||
|
||||
/proc/_bapi_helper_get_world_type_area()
|
||||
return "[world.area]"
|
||||
|
||||
/// Implement this to have bapi-dmm sleep occasionally
|
||||
/// by returning true
|
||||
/proc/_bapi_helper_tick_check()
|
||||
return TICK_CHECK
|
||||
Reference in New Issue
Block a user