[LUA] Help with listeners (?)

Greetings :slight_smile:

I’m having some trouble with LUA scripting and I’m here to ask for directions or help, if possible. I wouldn’t usually get to this before searching and trying to fix things as much as I could, but after so much work on my current mod, this final hiccup is feeling a bit frustrating and I’ve been on it for some days already :frowning:

For my new mod, I wanted to learn a bit more of coding so I went for a more robust system, which is basically and adaptation from the Food_Decay service in Stonehearth. However I encountered a bug on Food_Decay itself that hinders a functionality that is particularly important to my mod. I reported the bug here, however I didn’t want to use that thread to request for help or talk about the issue since it is a bug report and I felt a new thread was needed for that :slight_smile:

Anyway, my system is a Cooling Down system, necessary for a new Job that crafts hot items that require cooling down before use. It works just like food decay/rotting, but with this simple difference:

  • An item - after some time - can turn into two different items, one if it was poorly cooled and another if it was properly cooled (inside storage)

I did manage to adapt the Food_Decay service to my needs however I encountered this bug on what I suppose is the “listener” part of the service: Basically, when an item goes straight from the crafter’s hands to a container/storage, it ‘dodges’ the listeners and decay is not applied to it.

That’s my diagnosis, but I’ve no idea how to fix it - the “listeners” part is still a bit unclear to me since I’m still learning and I have no idea on how to fix this issue technically, only theoretically, which is by making the listeners also listen for stuff in crafter’s inventories, I suppose? At least I think that is where the items being carried by them right after crafting are :slight_smile:

Anyway, if anyone would be willing to help me out with this issue, I’d be really grateful! Feel free to contact me here or through PMs about it.

And I guess I’ll probably stick to simpler mods until I learn a bit more, but right now I just want to be done with this one :stuck_out_tongue:

Listeners and events are a pretty simple concept - it’s just how any system notifies any other system. It’s hard to provide any specific advice without more context. Can you post which event specifically you are listening to and missing?

Well, that is exactly the problem, I don’t quite understand what is not being listened.

Here’s the Food_Decay system initialization function, as you probably know it :stuck_out_tongue:

function FoodDecayService:initialize()
   self.food_type_tags = {"raw_food", "prepared_food", "luxury_food"}
   self.enable_decay = true
   self._sv = self.__saved_variables:get_data()
   if not self._sv.initialized then
      self._sv.initialized = true
      self._sv._decaying_food = {}
      self._sv.decay_listener = stonehearth.calendar:set_persistent_interval("FoodDecayService on_decay", '1h', function()
            self:_on_decay()
         end)
      self._sv.food_type_counts = {}
      self._sv.version = self:get_version()
   else
      -- fix up food decay not having decay value
      self._sv.version = self._sv.version or VERSIONS.ZERO
      if self._sv.version ~= self:get_version() then
         self:fixup_post_load()
      end

      self._sv.decay_listener:bind(function()
         self:_on_decay()
      end)
   end

   local entity_container = radiant._root_entity:add_component('entity_container')
   self._entity_container_trace = entity_container:trace_children('FoodDecayService food in world')
      :on_added(function(id, entity)
            self:_on_entity_added_to_world(entity)
         end)

   radiant.events.listen(radiant, 'radiant:entity:post_destroy', function(e)
         local entity_id = e.entity_id
         self:_on_entity_destroyed(entity_id)
      end)

   if self._listen_for_rotten_food then
      self._listen_for_rotten_food = nil
      self._post_create_listener = radiant.events.listen(radiant, 'radiant:entity:post_create', function(e)
            local entity = e.entity
            self:_on_entity_added_to_world(entity)
         end)
   end
   self._game_loaded_listener = radiant.events.listen_once(radiant, 'radiant:game_loaded', function()
         if self._post_create_listener then
            self._post_create_listener:destroy()
            self._post_create_listener = nil
         end
         if not stonehearth.calendar:is_tracking_timer(self._sv.decay_listener) then
            radiant.log.write('food_decay', 0, 'food decay does not have a listener tracked by the calendar. Recreating a listener')
            if self._sv.decay_listener then
               self._sv.decay_listener:destroy()
            end
            -- omg there was a save file where this listener was lost too? I am le sad. -yshan
            self._sv.decay_listener = stonehearth.calendar:set_persistent_interval("FoodDecayService on_decay", '1h', function()
                  self:_on_decay()
               end)
         end
         self._game_loaded_listener = nil
      end)
end

Now, like I said… I’m still learning :slight_smile: but as far as I understand what this function basically do, among a couple of fixes and other things, is:

  • define what items will have the “decay check” applied to them (the food_type_tags)
  • start up to run the on_decay function every hour
  • and here’s the part that it’s not yet so clear to me because it involves some functions from other scripts which I haven’t found or understood entirely (the radiant ones) but I think they refer to entities; so when entities are created, when entities are destroyed, etc… and as far as I can tell this part basically points out the circumstances where the “decay check” are applied to the entities that are listed at the start… so, when they are added to the world, when they are in storage, etc

I don’t think I’m quite accurate on that, however. It’s mostly an educated guess, I guess this is the part where something is missing but I can’t pinpoint what or how to fix it. What is missing, in my opinion:
There is one situation where the listener is failing to “find” items that have decay and need it to be tracked/turned on: which is when a crafter immediately moves a crafted item to a container/storage.

If the crafter drops the item and then a worker moves it to a crate (for example), the decay will be applied the moment the item is dropped and the entity is placed on that location on the ground. The item will rot inside the crate, it will work perfectly. However if the crafter never puts the item down - if it goes straight from the crafting bench to a container, the decay flag is never activated and that item will never rot - unless it is removed from the container.

This is what I wanted/needed to fix, I need the decay system to also “reach” items that were crafted but were never added to the world, they went straight from the crafter’s hands/inventory to a container. And my educated guess is that this part - the initialization of the Food_Decay service - is where something is missing. Something that would tell the listeners to also “look for” items in this existential limbo from the crafting to a container (but never as an entity on the ground)

I’m not entirely sure it is clear and I’m sorry for my layman’s terms :stuck_out_tongue:

But a clear example that could be reproduced is: build an output box to a cook, cook food and then somehow prevent that food from being taken or eaten (build a wall or dig around it) - you’ll notice that the food will never spoil. If you craft two units and when the cook is taking the second one you sound the town alarm (so the cook drops the food), and then you let them put it with the first one before isolating the output box, you’ll notice that only the second item will rot, which clearly shows this: when the item goes straight from crafting to container, Food_Decay fails to affect it.

Now, as I said… I don’t entirely understand the problem so there is a chance I’m completely wrong and what causes this issue is something else entirely, like a different part of the script or even a different script :slight_smile:
But like I said, I’m on the process of learning - I just really wanted to be able to wrap up my mod since pretty much everything else is done and I found this issue literally on my final testing :frowning:

I guess I shouldn’t have gone for something like that with so little practice, shame on me :stuck_out_tongue:
But thank you for the attention, of course! :slight_smile:

So the trace_children() part is where it’s listening to things being added directly to the world. If they aren’t added directly (e.g. they are instead placed in a crafter’s backpack), this won’t catch them as you found out. The existing bug aside, if you don’t care where the item is, you can ditch that and use the _post_create_listener (by removing the self._listen_for_rotten_food check), which will fire as soon as the entity is created, even when it’s not in the world.

2 Likes

Ohhh! So this is what is happening, hmmmm…

Alrigt, I’ll try that later tonight and see how it goes!
But considering the source, I’m quite optimistic it will work :wink:

Thank you so much! I’ll reply here again when I have news :slight_smile:

By the grace of Cid, that was… faster and more efficient than I expected. Woah!

THANK YOU very much, @max99x! :heart:

Now to finally wrap all the remaining details, polish the edges, let some friends playtest it a bit and release! :3

4 Likes