Shepherd_pasture_component's destroy() method fails

Summary: Whatever method actually handles the destroy process (which does properly run when the entity is destroyed), it isn’t the one defined as ShepherdPastureComponent:destroy().

Steps to reproduce:

  1. Start game with DebugTools mod enabled
  2. Create pasture
  3. Select pasture, open Lua console, and run e:get_component('stonehearth:shepherd_pasture'):destroy()
  4. Run e:get_component('stonehearth:shepherd_pasture')._sv

Expected Results: Timers like the stray_interval_timer should be gone, because those are destroyed and set to nil by the destroy() function.

Actual Results: Nothing changes.

Notes: Because destroy() is pointing to a function that does nothing, and the real destroy function is hidden somewhere and getting called by the game when the pasture gets destroyed, it’s impossible to override the destroy() function in a mod.

Version Number and Mods in use: Latest unstable, no mods that come anywhere near this file (disabled ACE for testing it).

To test the destroy of a component, try calling radiant.entities.destroy_entity(e) instead

But why doesn’t calling the destroy() function do anything? Other functions like activate() and restore() etc. can all be overridden and called just fine. How can I patch an existing component (or expand upon it like combat_job does with base_job) and do something when it gets destroyed unless it has some other function that only gets called during destroy() (which thankfully the shepherd_pasture component has) that I can override?

Edit: I ran into this problem in the first place because I was patching this component and my overridden destroy() function would only get called during the same game session as the entity/component was created. If I saved and reloaded, my patched function would no longer get called.

Hmm, mixins/mixintos are applied after the game engine caches the destroy fn during load, so it wouldn’t be pointed to your updated patched fn. We’ll work on this fix for a future patch.

1 Like

I know it’s too late now, but I just discovered that this is actually still a problem. ACE’s monkey-patches to the RRN and evolve components on entities that are loaded directly from a save (and aren’t created during the current session) don’t get their patched destroy() functions called and thus don’t properly dispose of additional listeners. This probably applies to a number of other components, also.

I think the best solution at this point for ACE is just to override all component files that we want to modify (and perhaps just override every single one). Then I’ll add _ace_initialize() and _ace_destroy() functions that get called from initialize() and destroy() so that other mods actually can monkey-patch them.

1 Like

That actually explains all destroy() problems I’ve been experiencing while modding Voxailles. It is very likely it also means modding in a new building block species is impossible because they need to be destroyed with the building. This probably extends to builder mode tools as they need to be destroyed as well.

Does that involve remove_component('component:uri') as well? I noticed that function is more reliable, maybe it points to correct destroy() somehow?

remove_component does not work. And unfortunately, my testing (with the buff controller) confirms that this happens with other controllers and not just components, so we should assume it happens with every controller. A better way to handle this might be to override radiant.lib.unclasslib, which is more complex, but is probably better than overriding close to 400 files.

2 Likes

Success! Steam Workshop :: Metaclass Override

1 Like

It’s worth checking whether this has a significant performance impact. Other than that, very neat!

1 Like

So obviously I didn’t test properly before, because I just found out that there was a more intractable problem which ruins a lot of this: saved controllers are loaded before any monkey-patches are applied. And it’s actually worse than that: saved controllers are loaded before even the server init script of a mod runs. So it’s not like I can listen to an earlier event to apply the monkey patches, or even apply them instantly when the server init script runs: there is simply no way with mods to monkey patch an initialize function.

The good news is that I can strip the mod back down to only worrying about the destroy function, and it’ll work for that, because it can still get around the caching of that. But any changes to a controller’s initialize function, if that controller may be saved and reloaded, needs to be done through a complete override.