# Detecting reason (success/fail) for cancelling tasks and my further scripting problems

**URL:** <https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248>\
**Category:** Scripting & Code\
**Created:** [September 29, 2017, 9:50pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248 "2017-09-29T21:50:27Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [September 29, 2017, 9:50pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/1 "2017-09-29T21:50:28Z")

</div>

So I wasted two evenings trying to code autoharvest properly but I found no way to complete the mechanism. The problem is the same command is used to cancel a task when it is complete and when it was cancelled without completion.

![Autoharvest Loop](https://us1.discourse-cdn.com/flex022/uploads/stonehearth/original/3X/1/6/16deca4036acc1bfb18310dcced416a819a749da.png)

The red part is what breaks the loop. For now I use modified task tracker component which allows looping a task until its done so harvest fired via autoharvest module simply cannot be cancelled, but it is not a proper way to solve the problem.

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [September 29, 2017, 9:53pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/2 "2017-09-29T21:53:57Z")

</div>

`Task` has `notify_completed()` and `notify_interrupted()` callbacks. Are those not sufficient for your needs?

Although now that I think of it, you are talking about a specific instance of harvesting, not the general harvesting task that lives forever. You can probably mixin an AI action to replace `HarvestRenewableResource` that does the same thing but also triggers an event afterwards. This wouldn’t be future proof, but should work for the current alpha.

---

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [September 29, 2017, 10:03pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/3 "2017-09-29T22:03:06Z")

</div>

> [@max99x](#):
>
> `Task` has `notify_completed()` and `notify_interrupted()` callbacks.

Ugh, searching for `notify_completed()` in stonehearth.smod and radiant.smod returned no matches. How can I learn how these work?

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [September 29, 2017, 10:06pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/4 "2017-09-29T22:06:20Z")

</div>

`stonehearth/components/trapping/trapping_grounds_component.lua` has an example of `notify_completed()`, but as per my edit above, I don’t think it’s gonna work for your case since the harvest task is global rather than per-instance. Modifying the task tracker as you are doing or the action to trigger an event when it finishes is probably the way to go.

---

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [September 29, 2017, 10:30pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/5 "2017-09-29T22:30:19Z")

</div>

My bad, I forgot to disable RegEx so the brackets made the search fail.

I haven’t worked with AI actions yet. I don’t understand how firing an event when AI action is finished works: does it fire whenever an action _ends_ or when it _reaches the end_, that is: when it’s completed successfully?

EDIT: Another way to do that: all successful harvests end in the node made unharvestable. An option to consider is to make TaskTracker fire an event whenever a task is cancelled (that would be a lot of events, wouldn’t that hit the performance?) with args = {entity, task\_name, category, player\_id} and listen to that event. If we’re still harvestable when it fired we should stop autoharvesting. Forcing `RenewableResourceNodeComponent:spawn_resource()` which cancels the task first and then marks the node unharvestable (why is that? in case task cancellation fails?) to do so in reverse order would do the trick.

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [September 29, 2017, 10:35pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/6 "2017-09-29T22:35:01Z")

</div>

You decide when it does. A simple way is to add something like this at the very end of `harvest_renewable_resource.lua`:

```auto
         :execute('stonehearth:trigger_event', {
            source = ai.ENTITY,
            event_name = 'mymod:harvesting:done'
         })

```

Then once all the harvesting logic is done (successfully), it’ll trigger that event.

The AI is probably the most complicated system we have, and the documentation isn’t great, so sorry about that, but perhaps we can release some more detailed docs with the AI refactor that’s going into A23.

---

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [September 29, 2017, 11:08pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/7 "2017-09-29T23:08:30Z")

</div>

I still don’t understand one very basic thing about compound actions: are all the commands merged into one action (so a trigger put at the end would fire when an AI starts the whole action) or fired in a cascade, one after another when the previous one ends (so a trigger put at the end would fire when an AI ends the last action before the trigger)?

I’m not sure how any of these would help me because there are easier ways to know when a harvest was requested (\*cough\* modifying `RenewableResourceNodeComponent:request_harvest()` \*cough\*) or ended successfully (`stonehearth:gather_renewable_resource` event is fired in harvest\_renewable\_resource\_adjacent.lua) and I need to know when it was cancelled. Even if I know when attempting to harvest the node started I won’t be able to find out when it is cancelled because there is no event to listen to.

The TaskTracker way is not optimal because there are some restock/task tracker shenanigans - try tasking undeploy, move and harvest on the same object few times in a short time (so none of the tasks is finished) and random order. Most of the time Hearthlings will undeploy the object, no matter which task was ordered last. I’ve already modified renewable resource node harvest to cancel all tasks (resource node does that while the renewable one doesn’t) via `stonehearth.town:get_town(player_id):remove_previous_task_on_item(self._entity)` so it should not be a problem. Changing tasks while the game is paused seems to intensify the problem.

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [September 29, 2017, 11:12pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/8 "2017-09-29T23:12:33Z")

</div>

> I still don’t understand one very basic thing about compound actions: are all the commands merged into one action (so a trigger put at the end would fire when an AI starts the whole action) or fired in a cascade, one after another when the previous one ends (so a trigger put at the end would fire when an AI ends the last action before the trigger)?

AI actions have a planning phase and a running phase. In a compound action, each step is planned (“thinked”) in order, then when the actions is chosen to run, each step is executed in order. The trigger only has a running phase, so it won’t happen until the previous step (`harvest_renewable_resource_adjacent`) successfully finishes.

---

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [September 29, 2017, 11:13pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/9 "2017-09-29T23:13:24Z")

</div>

The last action already fires `stonehearth:gather_renewable_resource` event.

EDIT: some Discourse bug replaced my post with my next reply.

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [September 29, 2017, 11:14pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/10 "2017-09-29T23:14:07Z")

</div>

In that case you can just listen to that event?

---

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [September 29, 2017, 11:19pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/11 "2017-09-29T23:19:52Z")

</div>

So I do. The problem is I need to know when harvest task is cancelled without completion. As I wrote:

> [@Pawel\_Malecki](#):
>
> Another way to do that: all successful harvests end in the node made unharvestable. An option to consider is to make TaskTracker fire an event whenever a task is cancelled (that would be a lot of events, wouldn’t that hit the performance?) with args = {entity, task\_name, category, player\_id} and listen to that event. If we’re still harvestable when it fired we should stop autoharvesting. Forcing RenewableResourceNodeComponent:spawn\_resource() which cancels the task first and then marks the node unharvestable (why is that? in case task cancellation fails?) to do so in reverse order would do the trick.

But what about performance here (tasks are cancelled very often and triggering it only if requested action is a harvest, while effective, is a very inelegant workaround as the function has to be altered every time a cancellation of a different task is required) and is there a reason for such order in `RenewableResourceNodeComponent:spawn_resource()`?

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [September 29, 2017, 11:25pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/12 "2017-09-29T23:25:09Z")

</div>

Firing an event (or a couple dozen) in response to a player action is insignificant performance-wise. Internal systems can fire hundreds of events every frame. What can be slow is listening to these events with expensive handlers in many places at once.

---

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [September 30, 2017, 12:09am UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/13 "2017-09-30T00:09:57Z")

</div>

So I’ll add `self._sv.task_successful` to TaskTracker and `self:mark_current_task_successful()` function setting it to `true`. Cancelling a task would fire an event with it listed in `args`.

I thought the challenge here would be to track all the functions cancelling tasks on completion and inject it into them. When I searched for `cancel_current_task()` I found only 6 files. How often is the task tracker used as of now? Are there any other tasks than harvests, placing an item and looting?

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [September 30, 2017, 12:16am UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/14 "2017-09-30T00:16:17Z")

</div>

> How often is the task tracker used as of now?

It’s mainly used for displaying the toasts above things that have some task issued on them. The name is confusing; it is not technically related to the task system.

> Are there any other tasks than harvests, placing an item and looting?

One off the top of my head is rescuing. There might be more.

---

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [September 30, 2017, 8:36am UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/15 "2017-09-30T08:36:51Z")

</div>

One side question: does `listen_once` completely destroy itself after a matching event is detected? In such case I wouldn’t need to specifically destroy listeners, what would simplify the code a bit.

After examining the code: renew detection loop doesn’t require a listener because as of now the only way to fire both events is via renew/spawn command and I have to mixin a bit of code into `RenewableResourceNodeComponent` anyway (as of now tasks are not cancelled by `request_harvest()` and depleted model/description is not applied if the node starts unharvestable, consider it a bug report). I wanted to use `stonehearth:gathered_renewable_resource` event but it is fired just after `spawn_resource()` which triggers `stonehearth:renewable_resource_spawned`. This means both renewal and depleting the node trigger an event inside `RenewableResourceNodeComponent` so it is not necessary to listen to these if I add one more saved variable to the node.

> [@max99x](#):
>
> It’s mainly used for displaying the toasts above things that have some task issued on them. The name is confusing; it is not technically related to the task system.

My last question is: is there any case of **purposefully** ordering the AI to perform/cancel an action which has a matching entry (harvest/placement/rescue) in TaskTracker without creating/deleting that entry? To put it another way: is it safe to assume that all harvest/placement/rescue actions and their cancellations are noticed by the TaskTracker? Monitoring the TaskTracker makes sense only if the tracker itself monitors all the corresponding actions, otherwise there would be information leaks leading to bugs.

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [September 30, 2017, 1:34pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/16 "2017-09-30T13:34:54Z")

</div>

> does listen\_once completely destroy itself after a matching event is detected?

Yes, but you still need to clean up the listener in case the event never happened.

> is it safe to assume that all harvest/placement/rescue actions and their cancellations are noticed by the TaskTracker?

TaskTracker itself is pretty much inert. Except for `cancel_task_if_entity_moves()`, it doesn’t change state unless someone calls methods on it. In the case of unmodded `RenewableResourceNode`, it doesn’t look like harvesting is ever enabled/disabled without notifying the task tracker.

---

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [September 30, 2017, 3:35pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/17 "2017-09-30T15:35:58Z")

</div>

So I have two major problems now.

1. Listeners are not created at all (Lua console returns nil when asked about them). I get no error log. In RenewableResourceNodeComponent I do it like this:

```auto
function PawelRenewableResourceNodeComponent:_create_task_cancelled_listener()
   local task_tracker_component = self._entity:get_component('stonehearth:task_tracker')
   if task_tracker_component == nil then
      return
   elseif task_tracker_component:is_activity_requested(HARVEST_ACTION) then
      self._task_cancelled_listener = radiant.events.listen(self._entity, 'pawel_API:task_tracker:task_cancelled', self, self._on_task_cancelled)
   end
end

function PawelRenewableResourceNodeComponent:_on_task_cancelled(args)
   if args.activity_name == HARVEST_ACTION and args.cancel_reason ~= 'completed' then
      self:set_autoharvested(false)
   end
end

```

1. Saved variables I added to initialize function of RRN component are gone after loading saved game.

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [September 30, 2017, 4:11pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/18 "2017-09-30T16:11:00Z")

</div>

That code looks reasonable at a glance. Perhaps it isn’t being called? You can add some logging statements there to make sure it is. For the saved variables, it might have to do with how the mixing is happening. Are you overriding the component via JSON or injecting methods at runtime?

---

<div class="post-metadata">

**Author:** ![Pawel\_Malecki](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/pawel_malecki/32/52928_2.png) [@Pawel\_Malecki](https://discourse.stonehearth.net/u/Pawel_Malecki)\
**Post date:** [October 1, 2017, 12:08pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/19 "2017-10-01T12:08:08Z")

</div>

Thanks to @max99x’s advice and explanations the autoharvest is working now.

However, my further ideas would involve a lot of listening to events. As all of them require components I could do it in a more memory-wise way by implementing a function which fires a specific command on all components of an entity instead of listening to an event inside each component:

```auto
function pawel_API:component_trigger(entity, function, args)
   if entity and entity:is_valid() and type(function) == 'string' then
      local component_list = --[[how do I get a list of all components an entity has?]]
      for i, component in ipairs(component_list) do
         if type(component[function]) == 'function' then
            component[function](self, args)
         end
      end
   end
end

```

For example, for detecting an entity going iconic I could do:

```auto
args = {}
pawel_API:component_trigger(self._entity, _on_removed_from_world_trigger, args)

```

instead of creating a listener on each component interested in the event:

```auto
self._on_removed_from_world_listener = radiant.events.listen(self._entity, 'stonehearth:on_removed_from_world', self, self._on_removed_from_world_trigger)

```

But, as I asked: how do I get a list of all components an entity has?

---

<div class="post-metadata">

**Author:** ![max99x](https://sea2.discourse-cdn.com/flex022/user_avatar/discourse.stonehearth.net/max99x/32/40094_2.png) [@max99x](https://discourse.stonehearth.net/u/max99x)\
**Post date:** [October 1, 2017, 7:21pm UTC](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248/20 "2017-10-01T19:21:03Z")

</div>

We don’t expose a function to get a component list, mainly because components are supposed to have unrelated APIs. You can hack around it using a trace, if you want:

```auto
local trace = entity:trace_components('a reason string that will appear in debug logs')
                   :on_added(function(name, component)
                         do_something_with_component(component)
                      end)
                   :push_object_state()
trace:destroy()

```

[Next page](https://discourse.stonehearth.net/t/detecting-reason-success-fail-for-cancelling-tasks-and-my-further-scripting-problems/32248.md?page=2)
