Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maploader Optimisations #2365

Merged
merged 6 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion check_regex.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ standards:

- exactly:
[
298,
295,
"non-bitwise << uses",
'(?<!\d)(?<!\d\s)(?<!<)<<(?!=|\s\d|\d|<|\/)',
]
Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/dcs/signals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
// start global signals with "!", this used to be necessary but now it's just a formatting choice
/// from base of datum/controller/subsystem/mapping/proc/add_new_zlevel(): (list/args)
#define COMSIG_GLOB_NEW_Z "!new_z"
/// sent after world.maxx and/or world.maxy are expanded: (has_exapnded_world_maxx, has_expanded_world_maxy)
#define COMSIG_GLOB_EXPANDED_WORLD_BOUNDS "!expanded_world_bounds"
/// called after a successful var edit somewhere in the world: (list/args)
#define COMSIG_GLOB_VAR_EDIT "!var_edit"
/// called after an explosion happened : (epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/is_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#define isatom(A) (isloc(A))

#define isdatum(thing) (istype(thing, /datum))

#define isweakref(D) (istype(D, /datum/weakref))

//Turfs
Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/maths.dm
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

#define CEILING(x, y) (-round(-(x) / (y)) * (y))

#define ROUND_UP(x) (-round(-(x)))

// round() acts like floor(x, 1) by default but can't handle other values
#define FLOOR(x, y) (round((x) / (y)) * (y))

Expand Down
7 changes: 7 additions & 0 deletions code/__DEFINES/misc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache)
//subtypesof(), typesof() without the parent path
#define subtypesof(typepath) (typesof(typepath) - typepath)

/// Takes a datum as input, returns its ref string, or a cached version of it
/// This allows us to cache \ref creation, which ensures it'll only ever happen once per datum, saving string tree time
/// It is slightly less optimal then a []'d datum, but the cost is massively outweighed by the potential savings
/// It will only work for datums mind, for datum reasons
/// : because of the embedded typecheck
#define text_ref(datum) (isdatum(datum) ? (datum:cached_ref ||= "\ref[datum]") : ("\ref[datum]"))

//Gets the turf this atom inhabits
#define get_turf(A) (get_step(A, 0))

Expand Down
2 changes: 1 addition & 1 deletion code/__DEFINES/typeids.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
#define TYPEID_NORMAL_LIST "f"
//helper macros
#define GET_TYPEID(ref) (((length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, -7)))
#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST)
#define IS_NORMAL_LIST(L) (GET_TYPEID(text_ref(L)) == TYPEID_NORMAL_LIST)


51 changes: 43 additions & 8 deletions code/__HELPERS/_lists.dm
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,49 @@

return null

/// Takes a weighted list (see above) and expands it into raw entries
/// This eats more memory, but saves time when actually picking from it
/proc/expand_weights(list/list_to_pick)
var/list/values = list()
for(var/item in list_to_pick)
var/value = list_to_pick[item]
if(!value)
continue
values += value

var/gcf = greatest_common_factor(values)

var/list/output = list()
for(var/item in list_to_pick)
var/value = list_to_pick[item]
if(!value)
continue
for(var/i in 1 to value / gcf)
output += item
return output

/// Takes a list of numbers as input, returns the highest value that is cleanly divides them all
/// Note: this implementation is expensive as heck for large numbers, I only use it because most of my usecase
/// Is < 10 ints
/proc/greatest_common_factor(list/values)
var/smallest = min(arglist(values))
for(var/i in smallest to 1 step -1)
var/safe = TRUE
for(var/entry in values)
if(entry % i != 0)
safe = FALSE
break
if(safe)
return i

/// Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/list_to_pick)
RETURN_TYPE(list_to_pick[_].type)
if(list_to_pick.len)
var/picked = rand(1,list_to_pick.len)
. = list_to_pick[picked]
list_to_pick.Cut(picked,picked+1) //Cut is far more efficient that Remove()

// Allows picks with non-integer weights and also 0
// precision 1000 means it works up to 3 decimal points
/proc/pickweight_float(list/L, default=1, precision=1000)
Expand All @@ -268,14 +311,6 @@

return null

//Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/L)
RETURN_TYPE(L[_].type)
if(L.len)
var/picked = rand(1,L.len)
. = L[picked]
L.Cut(picked,picked+1) //Cut is far more efficient that Remove()

//Returns the top(last) element from the list and removes it from the list (typical stack function)
/proc/pop(list/L)
if(L.len)
Expand Down
6 changes: 3 additions & 3 deletions code/__HELPERS/icons.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1198,7 +1198,7 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
if(isicon(icon) && isfile(icon))
//icons compiled in from 'icons/path/to/dmi_file.dmi' at compile time are weird and arent really /icon objects,
///but they pass both isicon() and isfile() checks. theyre the easiest case since stringifying them gives us the path we want
var/icon_ref = "\ref[icon]"
var/icon_ref = text_ref(icon)
var/locate_icon_string = "[locate(icon_ref)]"

icon_path = locate_icon_string
Expand All @@ -1209,7 +1209,7 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
// the rsc reference returned by fcopy_rsc() will be stringifiable to "icons/path/to/dmi_file.dmi"
var/rsc_ref = fcopy_rsc(icon)

var/icon_ref = "\ref[rsc_ref]"
var/icon_ref = text_ref(rsc_ref)

var/icon_path_string = "[locate(icon_ref)]"

Expand All @@ -1219,7 +1219,7 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
var/rsc_ref = fcopy_rsc(icon)
//if its the text path of an existing dmi file, the rsc reference returned by fcopy_rsc() will be stringifiable to a dmi path

var/rsc_ref_ref = "\ref[rsc_ref]"
var/rsc_ref_ref = text_ref(rsc_ref)
var/rsc_ref_string = "[locate(rsc_ref_ref)]"

icon_path = rsc_ref_string
Expand Down
23 changes: 22 additions & 1 deletion code/__HELPERS/text.dm
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,24 @@
return copytext(text, 1, i + 1)
return ""

//Returns a string with reserved characters and spaces after the first and last letters removed
//Like trim(), but very slightly faster. worth it for niche usecases
/proc/trim_reduced(text)
var/starting_coord = 1
var/text_len = length(text)
for (var/i in 1 to text_len)
if (text2ascii(text, i) > 32)
starting_coord = i
break

for (var/i = text_len, i >= starting_coord, i--)
if (text2ascii(text, i) > 32)
return copytext(text, starting_coord, i + 1)

if(starting_coord > 1)
return copytext(text, starting_coord)
return ""

/**
* Truncate a string to the given length
*
Expand All @@ -255,7 +273,7 @@
/proc/trim(text, max_length)
if(max_length)
text = copytext_char(text, 1, max_length)
return trim_left(trim_right(text))
return trim_reduced(text)

//Returns a string with the first element of the string capitalized.
/proc/capitalize(t)
Expand Down Expand Up @@ -735,6 +753,9 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
base = text("[]\herself", rest)
if("hers")
base = text("[]\hers", rest)
else // Someone fucked up, if you're not a macro just go home yeah?
// This does technically break parsing, but at least it's better then what it used to do
return base

. = base
if(rest)
Expand Down
12 changes: 7 additions & 5 deletions code/__HELPERS/unsorted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1364,19 +1364,21 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)

return "{[time_high]-[time_mid]-[GUID_VERSION][time_low]-[GUID_VARIANT][time_clock]-[node_id]}"

// \ref behaviour got changed in 512 so this is necesary to replicate old behaviour.
// If it ever becomes necesary to get a more performant REF(), this lies here in wait
// #define REF(thing) (thing && istype(thing, /datum) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]")
/**
* \ref behaviour got changed in 512 so this is necesary to replicate old behaviour.
* If it ever becomes necesary to get a more performant REF(), this lies here in wait
* #define REF(thing) (thing && isdatum(thing) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : text_ref(thing))
**/
/proc/REF(input)
if(istype(input, /datum))
if(isdatum(input))
var/datum/thing = input
if(thing.datum_flags & DF_USE_TAG)
if(!thing.tag)
stack_trace("A ref was requested of an object with DF_USE_TAG set but no tag: [thing]")
thing.datum_flags &= ~DF_USE_TAG
else
return "\[[url_encode(thing.tag)]\]"
return "\ref[input]"
return text_ref(input)

// Makes a call in the context of a different usr
// Use sparingly
Expand Down
8 changes: 4 additions & 4 deletions code/controllers/subsystem/garbage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ SUBSYSTEM_DEF(garbage)
var/type = D.type
var/datum/qdel_item/I = items[type]

log_world("## TESTING: GC: -- \ref[D] | [type] was unable to be GC'd --")
log_world("## TESTING: GC: -- [text_ref(D)] | [type] was unable to be GC'd --")
#ifdef TESTING
for(var/c in GLOB.admins) //Using testing() here would fill the logs with ADMIN_VV garbage
var/client/admin = c
Expand Down Expand Up @@ -258,7 +258,7 @@ SUBSYSTEM_DEF(garbage)
return
var/queue_time = world.time

var/refid = "\ref[D]"
var/refid = text_ref(D)
if (D.gc_destroyed <= 0)
D.gc_destroyed = queue_time

Expand All @@ -271,7 +271,7 @@ SUBSYSTEM_DEF(garbage)
++delslasttick
++totaldels
var/type = D.type
var/refID = "\ref[D]"
var/refID = text_ref(D)
var/datum/qdel_item/I = items[type]

if (!force && I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG)
Expand Down Expand Up @@ -389,7 +389,7 @@ SUBSYSTEM_DEF(garbage)
D.find_references()
if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object.
SSgarbage.Queue(D)
SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE
SSgarbage.reference_find_on_fail[text_ref(D)] = TRUE
#endif
else
#ifdef TESTING
Expand Down
15 changes: 7 additions & 8 deletions code/controllers/subsystem/statpanel.dm
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,16 @@ SUBSYSTEM_DEF(statpanels)
list("CPU:", world.cpu),
list("Instances:", "[num2text(world.contents.len, 10)]"),
list("World Time:", "[world.time]"),
list("Globals:", GLOB.stat_entry(), "\ref[GLOB]"),
list("[config]:", config.stat_entry(), "\ref[config]"),
list("Globals:", GLOB.stat_entry(), text_ref(GLOB)),
list("[config]:", config.stat_entry(), text_ref(config)),
list("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)"),
list("Master Controller:", Master.stat_entry(), "\ref[Master]"),
list("Failsafe Controller:", Failsafe.stat_entry(), "\ref[Failsafe]"),
list("Master Controller:", Master.stat_entry(), text_ref(Master)),
list("Failsafe Controller:", Failsafe.stat_entry(), text_ref(Failsafe)),
list("","")
)
for(var/ss in Master.subsystems)
var/datum/controller/subsystem/sub_system = ss
mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), "\ref[sub_system]")
mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", "\ref[GLOB.cameranet]")
for(var/datum/controller/subsystem/sub_system as anything in Master.subsystems)
mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), text_ref(sub_system))
mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", text_ref(GLOB.cameranet))
mc_data_encoded = url_encode(json_encode(mc_data))

/atom/proc/remove_from_cache()
Expand Down
4 changes: 2 additions & 2 deletions code/controllers/subsystem/timer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,8 @@ SUBSYSTEM_DEF(timer)
/datum/timedevent/proc/bucketJoin()
// Generate debug-friendly name for timer
var/static/list/bitfield_flags = list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT", "TIMER_LOOP")
name = "Timer: [id] (\ref[src]), TTR: [timeToRun], wait:[wait] Flags: [jointext(bitfield_to_list(flags, bitfield_flags), ", ")], \
callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), \
name = "Timer: [id] ([text_ref(src)]), TTR: [timeToRun], wait:[wait] Flags: [jointext(bitfield_to_list(flags, bitfield_flags), ", ")], \
callBack: [text_ref(callBack)], callBack.object: [callBack.object][text_ref(callBack.object)]([getcallingtype()]), \
callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""]), source: [source]"

if (bucket_joined)
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/subsystem/vis_overlays.dm
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ SUBSYSTEM_DEF(vis_overlays)
else
overlay = _create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
overlay.cache_expiration = -1
var/cache_id = "\ref[overlay]@{[world.time]}"
var/cache_id = "[text_ref(overlay)]@{[world.time]}"
vis_overlay_cache[cache_id] = overlay
. = overlay
if(overlay == null)
Expand Down
5 changes: 5 additions & 0 deletions code/datums/datum.dm
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
/// Datum level flags
var/datum_flags = NONE

/// A cached version of our \ref
/// The brunt of \ref costs are in creating entries in the string tree (a tree of immutable strings)
/// This avoids doing that more then once per datum by ensuring ref strings always have a reference to them after they're first pulled
var/cached_ref

/// A weak reference to another datum
var/datum/weakref/weak_reference

Expand Down
Loading