From 452c5ce360e4415ef6597f439046bfcf8e888e8b Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Tue, 1 Aug 2017 17:58:53 -0400 Subject: [PATCH] Makes GetComponent() faster using typecache magic (#29523) Added /proc/type2parent which takes a type (string, or type) and returns it's parent type (initial(x.parent_type) doesn't work) Instead of being a raw list of components, /datum/var/datum_components is now associated list keyed by type with some bells and whistles to make GetComponent() calls faster --- code/__HELPERS/type2type.dm | 15 +++++ code/datums/components/README.md | 2 +- code/datums/components/component.dm | 90 ++++++++++++++++++++++++----- 3 files changed, 92 insertions(+), 15 deletions(-) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index a35491212ca..95454835c08 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -537,3 +537,18 @@ if(!istype(the_matrix) || the_matrix.len != 20) return "#ffffffff" return rgb(the_matrix[1]*255, the_matrix[6]*255, the_matrix[11]*255, the_matrix[16]*255) + +/proc/type2parent(child) + var/string_type = "[child]" + var/last_slash = findlasttext(string_type, "/") + if(last_slash == 1) + switch(child) + if(/datum) + return null + if(/obj || /mob) + return /atom/movable + if(/area || /turf) + return /atom + else + return /datum + return text2path(copytext(string_type, 1, last_slash)) \ No newline at end of file diff --git a/code/datums/components/README.md b/code/datums/components/README.md index 310c300fb9a..84fa900764b 100644 --- a/code/datums/components/README.md +++ b/code/datums/components/README.md @@ -27,7 +27,7 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo ### Vars 1. `/datum/var/list/datum_components` (private) - * Lazy list of all components a datum has (TODO: Make this a typecache with longer paths overwriting shorter ones maybe? It'd be weird) + * Lazy associated list of type -> component/list of components. 1. `/datum/component/var/enabled` (protected, boolean) * If the component is enabled. If not, it will not react to signals * TRUE by default diff --git a/code/datums/components/component.dm b/code/datums/components/component.dm index a47a01d6d16..5e25a581f89 100644 --- a/code/datums/components/component.dm +++ b/code/datums/components/component.dm @@ -18,7 +18,38 @@ qdel(src) return P.SendSignal(COMSIG_COMPONENT_ADDED, src) - LAZYADD(P.datum_components, src) + + //lazy init the parent's dc list + var/list/dc = P.datum_components + if(!dc) + P.datum_components = dc = list() + + //set up the typecache + var/our_type = type + for(var/I in _GetInverseTypeListExceptRoot(our_type)) + var/test = dc[I] + if(test) //already another component of this type here + var/list/components_of_type + if(!islist(test)) + components_of_type = list(test) + dc[I] = components_of_type + else + components_of_type = test + if(I == our_type) //exact match, take priority + var/inserted = FALSE + for(var/J in 1 to components_of_type.len) + var/datum/component/C = components_of_type[J] + if(C.type != our_type) //but not over other exact matches + components_of_type.Insert(J, I) + inserted = TRUE + break + if(!inserted) + components_of_type += src + else //indirect match, back of the line with ya + components_of_type += src + else //only component of this type, no list + dc[I] = src + parent = P /datum/component/Destroy() @@ -33,7 +64,20 @@ /datum/component/proc/_RemoveNoSignal() var/datum/P = parent if(P) - LAZYREMOVE(P.datum_components, src) + var/list/dc = P.datum_components + var/our_type = type + for(var/I in _GetInverseTypeListExceptRoot(our_type)) + var/list/components_of_type = dc[I] + if(islist(components_of_type)) // + var/list/subtracted = components_of_type - src + if(subtracted.len == 1) //only 1 guy left + dc[I] = subtracted[1] //make him special + else + dc[I] = subtracted + else //just us + dc -= I + if(!dc.len) + P.datum_components = null parent = null /datum/component/proc/RegisterSignal(sig_type, proc_on_self, override = FALSE) @@ -66,7 +110,14 @@ /datum/component/proc/OnTransfer(datum/new_parent) return -/datum/var/list/datum_components //list of /datum/component +/datum/component/proc/_GetInverseTypeListExceptRoot(our_type_cached) + var/datum/component/current_type = our_type_cached + . = list() + while (current_type != /datum/component) + . += current_type + current_type = type2parent(current_type) + +/datum/var/list/datum_components //special typecache of /datum/component /datum/proc/SendSignal(sigtype, ...) var/list/comps = datum_components @@ -83,21 +134,32 @@ return /datum/proc/GetComponent(c_type) - for(var/I in datum_components) - if(istype(I, c_type)) - return I + var/list/dc = datum_components + if(!dc) + return null + . = dc[c_type] + if(islist(.)) + return .[1] /datum/proc/GetExactComponent(c_type) - for(var/I in datum_components) - var/datum/component/C = I + var/list/dc = datum_components + if(!dc) + return null + var/datum/component/C = dc[c_type] + if(C) + if(islist(C)) + C = C[1] if(C.type == c_type) - return I + return C + return null /datum/proc/GetComponents(c_type) - . = list() - for(var/I in datum_components) - if(istype(I, c_type)) - . += I + var/list/dc = datum_components + if(!dc) + return null + . = dc[c_type] + if(!islist(.)) + return list(.) /datum/proc/AddComponent(new_type, ...) var/nt = new_type @@ -116,4 +178,4 @@ helicopter.SendSignal(COMSIG_COMPONENT_REMOVING, C) C.OnTransfer(src) C.parent = src - SendSignal(COMSIG_COMPONENT_ADDED, C) \ No newline at end of file + SendSignal(COMSIG_COMPONENT_ADDED, C)