diff --git a/README.md b/README.md index c32fb20..cdeadf9 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ - [Chunk](#chunk) - [Builder](#builder) - [Changelog](#changelog) + - [vX.Y.Z](#vxyz) - [v1.10.0](#v1100) - [v1.9.0](#v190) - [v1.8.0](#v180) @@ -1668,6 +1669,11 @@ builder_mt:destruction_policy :: id -> builder ## Changelog +### vX.Y.Z + +- Slightly improved performance of modifying operations for fragments with [`ON_INSERT`](#evolvedon_insert) and [`ON_REMOVE`](#evolvedon_remove) hooks +- Slightly improved performance of queries with [`EXPLICIT`](#evolvedexplicit) fragments + ### v1.10.0 - Added the new [`evolved.lookup`](#evolvedlookup) and [`evolved.multi_lookup`](#evolvedmulti_lookup) functions that allow finding ids by their names diff --git a/develop/all.lua b/develop/all.lua index f6c928b..0c8918d 100644 --- a/develop/all.lua +++ b/develop/all.lua @@ -1,4 +1,5 @@ require 'develop.testing.build_tests' +require 'develop.testing.cached_hooks_tests' require 'develop.testing.cancel_tests' require 'develop.testing.clone_tests' require 'develop.testing.depth_tests' diff --git a/develop/testing/cached_hooks_tests.lua b/develop/testing/cached_hooks_tests.lua new file mode 100644 index 0000000..3881111 --- /dev/null +++ b/develop/testing/cached_hooks_tests.lua @@ -0,0 +1,203 @@ +local evo = require 'evolved' + +evo.debug_mode(true) + +do + local f1, f2 = evo.id(2) + + local insert_hook_calls = 0 + + if f1 < f2 then + evo.set(f1, evo.ON_INSERT, function() + insert_hook_calls = insert_hook_calls + 1 + end) + else + evo.set(f2, evo.ON_INSERT, function() + insert_hook_calls = insert_hook_calls + 1 + end) + end + + do + insert_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + assert(insert_hook_calls == 1) + evo.destroy(e) + end + + evo.remove(f1, evo.ON_INSERT) + evo.remove(f2, evo.ON_INSERT) + + do + insert_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + assert(insert_hook_calls == 0) + evo.destroy(e) + end + + if f1 < f2 then + evo.set(f1, evo.ON_INSERT, function() + insert_hook_calls = insert_hook_calls + 2 + end) + else + evo.set(f2, evo.ON_INSERT, function() + insert_hook_calls = insert_hook_calls + 2 + end) + end + + do + insert_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + assert(insert_hook_calls == 2) + evo.destroy(e) + end +end + +do + local f1, f2 = evo.id(2) + + local insert_hook_calls = 0 + + if f1 > f2 then + evo.set(f1, evo.ON_INSERT, function() + insert_hook_calls = insert_hook_calls + 1 + end) + else + evo.set(f2, evo.ON_INSERT, function() + insert_hook_calls = insert_hook_calls + 1 + end) + end + + do + insert_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + assert(insert_hook_calls == 1) + evo.destroy(e) + end + + evo.remove(f1, evo.ON_INSERT) + evo.remove(f2, evo.ON_INSERT) + + do + insert_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + assert(insert_hook_calls == 0) + evo.destroy(e) + end + + if f1 > f2 then + evo.set(f1, evo.ON_INSERT, function() + insert_hook_calls = insert_hook_calls + 2 + end) + else + evo.set(f2, evo.ON_INSERT, function() + insert_hook_calls = insert_hook_calls + 2 + end) + end + + do + insert_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + assert(insert_hook_calls == 2) + evo.destroy(e) + end +end + +do + local f1, f2 = evo.id(2) + + local remove_hook_calls = 0 + + if f1 < f2 then + evo.set(f1, evo.ON_REMOVE, function() + remove_hook_calls = remove_hook_calls + 1 + end) + else + evo.set(f2, evo.ON_REMOVE, function() + remove_hook_calls = remove_hook_calls + 1 + end) + end + + do + remove_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + evo.destroy(e) + assert(remove_hook_calls == 1) + end + + evo.remove(f1, evo.ON_REMOVE) + evo.remove(f2, evo.ON_REMOVE) + + do + remove_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + evo.destroy(e) + assert(remove_hook_calls == 0) + end + + if f1 < f2 then + evo.set(f1, evo.ON_REMOVE, function() + remove_hook_calls = remove_hook_calls + 2 + end) + else + evo.set(f2, evo.ON_REMOVE, function() + remove_hook_calls = remove_hook_calls + 2 + end) + end + + do + remove_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + evo.destroy(e) + assert(remove_hook_calls == 2) + end +end + +do + local f1, f2 = evo.id(2) + + local remove_hook_calls = 0 + + if f1 > f2 then + evo.set(f1, evo.ON_REMOVE, function() + remove_hook_calls = remove_hook_calls + 1 + end) + else + evo.set(f2, evo.ON_REMOVE, function() + remove_hook_calls = remove_hook_calls + 1 + end) + end + + do + remove_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + evo.destroy(e) + assert(remove_hook_calls == 1) + end + + evo.remove(f1, evo.ON_REMOVE) + evo.remove(f2, evo.ON_REMOVE) + + do + remove_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + evo.destroy(e) + assert(remove_hook_calls == 0) + end + + if f1 > f2 then + evo.set(f1, evo.ON_REMOVE, function() + remove_hook_calls = remove_hook_calls + 2 + end) + else + evo.set(f2, evo.ON_REMOVE, function() + remove_hook_calls = remove_hook_calls + 2 + end) + end + + do + remove_hook_calls = 0 + local e = evo.spawn { [f1] = 42, [f2] = 'hello' } + evo.destroy(e) + assert(remove_hook_calls == 2) + end +end diff --git a/develop/testing/main_tests.lua b/develop/testing/main_tests.lua index 8f21ef0..ba33631 100644 --- a/develop/testing/main_tests.lua +++ b/develop/testing/main_tests.lua @@ -2928,8 +2928,8 @@ do last_insert_entity, last_insert_component = 0, 0 local e = evo.spawn({ [f2] = 21, [f1] = true }) assert(set_count == 2 and insert_count == 2) - assert(last_set_entity == e and last_set_component == 21) - assert(last_insert_entity == e and last_insert_component == 21) + assert(last_set_entity == e and (last_set_component == 21 or last_set_component == true)) + assert(last_insert_entity == e and (last_insert_component == 21 or last_insert_component == true)) end do @@ -2948,8 +2948,8 @@ do last_insert_entity, last_insert_component = 0, 0 local e = evo.spawn({ [f3] = 33, [f2] = 22 }) assert(set_count == 2 and insert_count == 2) - assert(last_set_entity == e and last_set_component == nil) - assert(last_insert_entity == e and last_insert_component == nil) + assert(last_set_entity == e and (last_set_component == nil or last_set_component == 22)) + assert(last_insert_entity == e and (last_insert_component == nil or last_insert_component == 22)) end end diff --git a/evolved.lua b/evolved.lua index fc87cf1..4b2350d 100644 --- a/evolved.lua +++ b/evolved.lua @@ -202,6 +202,12 @@ local __structural_changes = 0 ---@type integer ---@field package __has_internal_minors boolean ---@field package __has_internal_fragments boolean ---@field package __has_required_fragments boolean +---@field package __insert_fragment_list? evolved.fragment[] +---@field package __insert_fragment_count integer +---@field package __remove_fragment_list? evolved.fragment[] +---@field package __remove_fragment_count integer +---@field package __explicit_fragment_list? evolved.fragment[] +---@field package __explicit_fragment_count integer local __chunk_mt = {} __chunk_mt.__index = __chunk_mt @@ -819,12 +825,13 @@ end ---@param list V[] ---@param size? integer ---@return V[] +---@return integer dup_list_size ---@nodiscard function __list_fns.dup(list, size) local list_size = size or #list if list_size == 0 then - return {} + return {}, 0 end local dup_list = __list_fns.new(list_size) @@ -833,7 +840,7 @@ function __list_fns.dup(list, size) list, 1, list_size, 1, dup_list) - return dup_list + return dup_list, list_size end ---@generic V @@ -1408,6 +1415,12 @@ function __new_chunk(chunk_parent, chunk_fragment) __has_internal_minors = false, __has_internal_fragments = false, __has_required_fragments = false, + __insert_fragment_list = nil, + __insert_fragment_count = 0, + __remove_fragment_list = nil, + __remove_fragment_count = 0, + __explicit_fragment_list = nil, + __explicit_fragment_count = 0, }, __chunk_mt) if not chunk_parent then @@ -1590,17 +1603,22 @@ function __update_chunk_caches(chunk) local chunk_fragment_list = chunk.__fragment_list local chunk_fragment_count = chunk.__fragment_count - local has_setup_hooks = chunk_parent ~= nil and chunk_parent.__has_setup_hooks - or __evolved_has_any(chunk_fragment, __DEFAULT, __DUPLICATE) + local has_setup_major = __evolved_has_any(chunk_fragment, __DEFAULT, __DUPLICATE) + local has_setup_minors = chunk_parent ~= nil and chunk_parent.__has_setup_hooks - local has_assign_hooks = chunk_parent ~= nil and chunk_parent.__has_assign_hooks - or __evolved_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN) + local has_assign_major = __evolved_has_any(chunk_fragment, __ON_SET, __ON_ASSIGN) + local has_assign_minors = chunk_parent ~= nil and chunk_parent.__has_assign_hooks - local has_insert_hooks = chunk_parent ~= nil and chunk_parent.__has_insert_hooks - or __evolved_has_any(chunk_fragment, __ON_SET, __ON_INSERT) + local has_insert_major = __evolved_has_any(chunk_fragment, __ON_SET, __ON_INSERT) + local has_insert_minors = chunk_parent ~= nil and chunk_parent.__has_insert_hooks - local has_remove_hooks = chunk_parent ~= nil and chunk_parent.__has_remove_hooks - or __evolved_has(chunk_fragment, __ON_REMOVE) + local has_remove_major = __evolved_has(chunk_fragment, __ON_REMOVE) + local has_remove_minors = chunk_parent ~= nil and chunk_parent.__has_remove_hooks + + local has_setup_hooks = has_setup_major or has_setup_minors + local has_assign_hooks = has_assign_major or has_assign_minors + local has_insert_hooks = has_insert_major or has_insert_minors + local has_remove_hooks = has_remove_major or has_remove_minors local has_unique_major = __evolved_has(chunk_fragment, __UNIQUE) local has_unique_minors = chunk_parent ~= nil and chunk_parent.__has_unique_fragments @@ -1667,6 +1685,75 @@ function __update_chunk_caches(chunk) else chunk.__without_unique_fragments = chunk end + + if has_insert_hooks then + local parent_insert_fragment_list = chunk_parent and chunk_parent.__insert_fragment_list + local parent_insert_fragment_count = chunk_parent and chunk_parent.__insert_fragment_count or 0 + + if has_insert_major then + local insert_fragment_list, insert_fragment_count = parent_insert_fragment_list + and __list_fns.dup(parent_insert_fragment_list, parent_insert_fragment_count) + or __list_fns.new(1), 0 + + insert_fragment_count = parent_insert_fragment_count + 1 + insert_fragment_list[insert_fragment_count] = chunk_fragment + + chunk.__insert_fragment_list = insert_fragment_list + chunk.__insert_fragment_count = insert_fragment_count + else + chunk.__insert_fragment_list = parent_insert_fragment_list + chunk.__insert_fragment_count = parent_insert_fragment_count + end + else + chunk.__insert_fragment_list = nil + chunk.__insert_fragment_count = 0 + end + + if has_remove_hooks then + local parent_remove_fragment_list = chunk_parent and chunk_parent.__remove_fragment_list + local parent_remove_fragment_count = chunk_parent and chunk_parent.__remove_fragment_count or 0 + + if has_remove_major then + local remove_fragment_list, remove_fragment_count = parent_remove_fragment_list + and __list_fns.dup(parent_remove_fragment_list, parent_remove_fragment_count) + or __list_fns.new(1), 0 + + remove_fragment_count = parent_remove_fragment_count + 1 + remove_fragment_list[remove_fragment_count] = chunk_fragment + + chunk.__remove_fragment_list = remove_fragment_list + chunk.__remove_fragment_count = remove_fragment_count + else + chunk.__remove_fragment_list = parent_remove_fragment_list + chunk.__remove_fragment_count = parent_remove_fragment_count + end + else + chunk.__remove_fragment_list = nil + chunk.__remove_fragment_count = 0 + end + + if has_explicit_fragments then + local parent_explicit_fragment_list = chunk_parent and chunk_parent.__explicit_fragment_list + local parent_explicit_fragment_count = chunk_parent and chunk_parent.__explicit_fragment_count or 0 + + if has_explicit_major then + local explicit_fragment_list, explicit_fragment_count = parent_explicit_fragment_list + and __list_fns.dup(parent_explicit_fragment_list, parent_explicit_fragment_count) + or __list_fns.new(1), 0 + + explicit_fragment_count = parent_explicit_fragment_count + 1 + explicit_fragment_list[explicit_fragment_count] = chunk_fragment + + chunk.__explicit_fragment_list = explicit_fragment_list + chunk.__explicit_fragment_count = explicit_fragment_count + else + chunk.__explicit_fragment_list = parent_explicit_fragment_list + chunk.__explicit_fragment_count = parent_explicit_fragment_count + end + else + chunk.__explicit_fragment_list = nil + chunk.__explicit_fragment_count = 0 + end end ---@param chunk evolved.chunk @@ -2063,17 +2150,15 @@ function __query_minor_matches(chunk, query) end end - if chunk.__has_explicit_fragments then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count + local chunk_explicit_fragment_list = chunk.__explicit_fragment_list - for chunk_fragment_index = 1, chunk_fragment_count do - local chunk_fragment = chunk_fragment_list[chunk_fragment_index] + if chunk_explicit_fragment_list then + for chunk_explicit_fragment_index = 1, chunk.__explicit_fragment_count do + local fragment = chunk_explicit_fragment_list[chunk_explicit_fragment_index] local is_chunk_fragment_matched = - (not __evolved_has(chunk_fragment, __EXPLICIT)) or - (query_variant_count > 0 and query_variant_set[chunk_fragment]) or - (query_include_count > 0 and query_include_set[chunk_fragment]) + (query_variant_count > 0 and query_variant_set[fragment]) or + (query_include_count > 0 and query_include_set[fragment]) if not is_chunk_fragment_matched then return false @@ -2639,12 +2724,11 @@ function __spawn_entity(chunk, entity, component_table, component_mapper) component_mapper(chunk, place, place) end - if chunk.__has_insert_hooks then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count + local chunk_insert_fragment_list = chunk.__insert_fragment_list - for chunk_fragment_index = 1, chunk_fragment_count do - local fragment = chunk_fragment_list[chunk_fragment_index] + if chunk_insert_fragment_list then + for chunk_insert_fragment_index = 1, chunk.__insert_fragment_count do + local fragment = chunk_insert_fragment_list[chunk_insert_fragment_index] ---@type evolved.set_hook?, evolved.insert_hook? local fragment_on_set, fragment_on_insert = @@ -2778,12 +2862,11 @@ function __multi_spawn_entity(chunk, entity_list, entity_first, entity_count, co component_mapper(chunk, b_place, e_place) end - if chunk.__has_insert_hooks then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count + local chunk_insert_fragment_list = chunk.__insert_fragment_list - for chunk_fragment_index = 1, chunk_fragment_count do - local fragment = chunk_fragment_list[chunk_fragment_index] + if chunk_insert_fragment_list then + for chunk_insert_fragment_index = 1, chunk.__insert_fragment_count do + local fragment = chunk_insert_fragment_list[chunk_insert_fragment_index] ---@type evolved.set_hook?, evolved.insert_hook? local fragment_on_set, fragment_on_insert = @@ -2939,12 +3022,11 @@ function __clone_entity(prefab, entity, component_table, component_mapper) component_mapper(chunk, place, place) end - if chunk.__has_insert_hooks then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count + local chunk_insert_fragment_list = chunk.__insert_fragment_list - for chunk_fragment_index = 1, chunk_fragment_count do - local fragment = chunk_fragment_list[chunk_fragment_index] + if chunk_insert_fragment_list then + for chunk_insert_fragment_index = 1, chunk.__insert_fragment_count do + local fragment = chunk_insert_fragment_list[chunk_insert_fragment_index] ---@type evolved.set_hook?, evolved.insert_hook? local fragment_on_set, fragment_on_insert = @@ -3107,12 +3189,11 @@ function __multi_clone_entity(prefab, entity_list, entity_first, entity_count, c component_mapper(chunk, b_place, e_place) end - if chunk.__has_insert_hooks then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count + local chunk_insert_fragment_list = chunk.__insert_fragment_list - for chunk_fragment_index = 1, chunk_fragment_count do - local fragment = chunk_fragment_list[chunk_fragment_index] + if chunk_insert_fragment_list then + for chunk_insert_fragment_index = 1, chunk.__insert_fragment_count do + local fragment = chunk_insert_fragment_list[chunk_insert_fragment_index] ---@type evolved.set_hook?, evolved.insert_hook? local fragment_on_set, fragment_on_insert = @@ -3377,27 +3458,29 @@ function __clear_entity_one(entity) local chunk = entity_chunks[entity_primary] local place = entity_places[entity_primary] - if chunk and chunk.__has_remove_hooks then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count - local chunk_component_indices = chunk.__component_indices - local chunk_component_storages = chunk.__component_storages + if chunk then + local chunk_remove_fragment_list = chunk.__remove_fragment_list - for chunk_fragment_index = 1, chunk_fragment_count do - local fragment = chunk_fragment_list[chunk_fragment_index] + if chunk_remove_fragment_list then + local chunk_component_indices = chunk.__component_indices + local chunk_component_storages = chunk.__component_storages - ---@type evolved.remove_hook? - local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + for chunk_remove_fragment_index = 1, chunk.__remove_fragment_count do + local fragment = chunk_remove_fragment_list[chunk_remove_fragment_index] - if fragment_on_remove then - local component_index = chunk_component_indices[fragment] + ---@type evolved.remove_hook? + local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) - if component_index then - local component_storage = chunk_component_storages[component_index] - local old_component = component_storage[place] - fragment_on_remove(entity, fragment, old_component) - else - fragment_on_remove(entity, fragment) + if fragment_on_remove then + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + local old_component = component_storage[place] + fragment_on_remove(entity, fragment, old_component) + else + fragment_on_remove(entity, fragment) + end end end end @@ -3444,27 +3527,29 @@ function __destroy_entity_one(entity) local chunk = entity_chunks[entity_primary] local place = entity_places[entity_primary] - if chunk and chunk.__has_remove_hooks then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count - local chunk_component_indices = chunk.__component_indices - local chunk_component_storages = chunk.__component_storages + if chunk then + local chunk_remove_fragment_list = chunk.__remove_fragment_list - for chunk_fragment_index = 1, chunk_fragment_count do - local fragment = chunk_fragment_list[chunk_fragment_index] + if chunk_remove_fragment_list then + local chunk_component_indices = chunk.__component_indices + local chunk_component_storages = chunk.__component_storages - ---@type evolved.remove_hook? - local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + for chunk_remove_fragment_index = 1, chunk.__remove_fragment_count do + local fragment = chunk_remove_fragment_list[chunk_remove_fragment_index] - if fragment_on_remove then - local component_index = chunk_component_indices[fragment] + ---@type evolved.remove_hook? + local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) - if component_index then - local component_storage = chunk_component_storages[component_index] - local old_component = component_storage[place] - fragment_on_remove(entity, fragment, old_component) - else - fragment_on_remove(entity, fragment) + if fragment_on_remove then + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + local old_component = component_storage[place] + fragment_on_remove(entity, fragment, old_component) + else + fragment_on_remove(entity, fragment) + end end end end @@ -4060,8 +4145,6 @@ function __chunk_remove(old_chunk, ...) local old_entity_list = old_chunk.__entity_list local old_entity_count = old_chunk.__entity_count - local old_fragment_list = old_chunk.__fragment_list - local old_fragment_count = old_chunk.__fragment_count local old_component_indices = old_chunk.__component_indices local old_component_storages = old_chunk.__component_storages @@ -4075,12 +4158,14 @@ function __chunk_remove(old_chunk, ...) return end - if old_chunk.__has_remove_hooks then + local old_remove_fragment_list = old_chunk.__remove_fragment_list + + if old_remove_fragment_list then local new_fragment_set = new_chunk and new_chunk.__fragment_set or __safe_tbls.__EMPTY_FRAGMENT_SET - for old_fragment_index = 1, old_fragment_count do - local fragment = old_fragment_list[old_fragment_index] + for old_remove_fragment_index = 1, old_chunk.__remove_fragment_count do + local fragment = old_remove_fragment_list[old_remove_fragment_index] if not new_fragment_set[fragment] then ---@type evolved.remove_hook? @@ -4209,14 +4294,14 @@ function __chunk_clear(chunk) return end - if chunk.__has_remove_hooks then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count + local chunk_remove_fragment_list = chunk.__remove_fragment_list + + if chunk_remove_fragment_list then local chunk_component_indices = chunk.__component_indices local chunk_component_storages = chunk.__component_storages - for chunk_fragment_index = 1, chunk_fragment_count do - local fragment = chunk_fragment_list[chunk_fragment_index] + for chunk_remove_fragment_index = 1, chunk.__remove_fragment_count do + local fragment = chunk_remove_fragment_list[chunk_remove_fragment_index] ---@type evolved.remove_hook? local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) @@ -5610,17 +5695,17 @@ function __evolved_remove(entity, ...) __evolved_defer() if old_chunk and old_chunk ~= new_chunk then - local old_fragment_list = old_chunk.__fragment_list - local old_fragment_count = old_chunk.__fragment_count local old_component_indices = old_chunk.__component_indices local old_component_storages = old_chunk.__component_storages - if old_chunk.__has_remove_hooks then + local old_remove_fragment_list = old_chunk.__remove_fragment_list + + if old_remove_fragment_list then local new_fragment_set = new_chunk and new_chunk.__fragment_set or __safe_tbls.__EMPTY_FRAGMENT_SET - for old_fragment_index = 1, old_fragment_count do - local fragment = old_fragment_list[old_fragment_index] + for old_remove_fragment_index = 1, old_chunk.__remove_fragment_count do + local fragment = old_remove_fragment_list[old_remove_fragment_index] if not new_fragment_set[fragment] then ---@type evolved.remove_hook?