Preserving and switching states for entities

I’ve been looking into coding recently, and while my experience in SH coding is minimal, I’m eager to try.
More precisely I was looking into decorations doing something on a button click. Things that seem similar to me are the locking iron door and archer switching arrow types. In both cases I saw entities (door and archer) having certain action buttons added, which trigger some action when clicked (trigger_lock for door and swap ammo for archer). I didn’t however found where those actions are coded.
A little help here?
@8BitCrab , any idea whom should I tag?

In the stonehearth manifest, you can see a section named functions{}. That is where most buttons get their code from.

3 Likes

Thanks!
From what I see, there are short code fragments called “call_handlers” that usually call LUA functions described in /components. Will dig there.

1 Like

More intel. While sifting through /docs in stonehearth.smod I’ve read docs on components. Seems like that is exactly what I need. It’s getting a little clearer now.

Some theory first:

Components are LUA classes which are integrated into our entity and represent different functionalities associated with it. Thus, every component has saved variables that persist between save / load and functions to do something with this component (and its saved variables).
Saved variables can be either initialized from a JSON describing the component or with LUA commands inside the component itself. That’s what bugged me - I thought JSON is used as a permanent storage while in fact it is only used for initialization.

Now if Radiant used incapsulation (and I suppose they did, because why not?), components can’t modify other components (or can do it only via some public “interface”). In general they are standalone code fragments, and changing / adding one should not influence (or break) the others.

Now off to some practice:

function LampComponent:initialize()
   local json = radiant.entities.get_json(self)
   self._light_policy = json.light_policy
   self._light_effect = json.light_effect  
   self._tracked_entities = {}
end

This is a constructor for lamp component. It reads light_policy from the JSON describing our lamp entity.

function LampComponent:activate(entity, json)
   self._render_info = self._entity:add_component('render_info')

   self:_check_light()

   if self._light_policy ~= "always_on" then
      self._sunrise_listener = stonehearth.calendar:set_alarm('6:00+5m', function()
            self:_light_off()
         end)
      self._sunset_listener = stonehearth.calendar:set_alarm('22:00+5m', function()
            self:_light_on()
         end)
   end
end

And this one is an… activator? Which creates a timer based on light_policy (or permanently switches a lamp on if the policy dictates it).

Of course, I ummediately have some “stipid” questions, like

  • When is the activator function triggered?
  • Is constructor called on entity crafting only or on entity load, too? (intuitively I suppose it should be called on crafting only but I may be wrong).

initialize() is called only once, at the exact moment the entity is created (starts existing in the game)

activate() is called every time the saved game is loaded, and after the initialize() if the entity just got created.

1 Like

Thanks!
To begin with something simple, I’m trying to add to my lamps a button that will toggle light_policy on and off.
As I see with your explanations, _light_policy (which should be a saved variable since it starts with _) is initialized only once - on item creation. To make it changeable I should

  1. Add a function to toggle it to the component
  2. Create a new command triggering that function (and add it to my mod manifest)
  3. Reference that command in the lamp’s JSON to get a nice button in my UI.

Time to try it :merry:

I’m really not sure about this. I didn’t checked further, but I’m under the impression that variables starting with “_” are simple private variables.
Those that will be saved are at the self:__sv.your_variable

Not sure though! Someone else could check this later.

offtop:

rescue_citizen_action.lua
--[[
   Task that represents a worker bringing a piece of wood to a firepit and setting it on fire.
]]
1 Like

They copy pasted from the light_firepit.lua to speed the coding :smile:

Ayep. I hope my hearthlings won’t set anyone on fire.

Things appear to be more complicated than I thought.
Commands can be of two types - those that have an action “fire_event” and those that “call” functions.
I can’t find anything on events, but I found where are those functions they call. Unexpectedly, not in the component. Or, more precisely, in the component, but they are not called from a command directly.

{
   "type": "command",
   "name": "call_trader",
   "display_name": "i18n(stonehearth:data.commands.call_trader.display_name)",
   "description": "i18n(stonehearth:data.commands.call_trader.description)",
   "disabled_description": "i18n(stonehearth:data.commands.call_trader.disabled_description)",
   "icon": "file(call_trader.png)",
   "action": "call",
   "function": "call_trader_command",
   "object": "stonehearth.shop",
   "args": [
   ]
}

The “call_trader_command” is not situated in the shop_component. Instead it is a proxy situated in /services/server/shop/shop_service.lua. This function is the one that finally addresses the component and calls its function.
Why is it done like that, I have no idea. I suppose Radiant had their reasons. Maybe it is something related to multithreading.

PS.

function frostfeast:_three_headed_monkeys()
   local victim = radiant.mods.require('stonehearth.services.server.game_master.controllers.encounters.script_encounter')

The thing I love about programmers is their sense of humor.

Edit: found this comment by @sdee on firing events. It’s horribly outdated, but I now suspect events are handled not through server/client services but through call_handlers. The only thing that confuses me is that event name in command JSON and in manifest is different. For example, where JSON uses “radiant_place_item”, manifest lists only “place_item” function (that references the appropriate call handler).

Edit2: boop @RepeatPan. Because I can. Maybe he knows something.

Edit3: there are not 2 but 3 types of commands.

  • firing events
  • calling function on object (service)
  • calling function from manifest (stonehearth:function_name)

First of all, there’s multiple object creation callbacks. One of the files of frostfeast describes them in detail (heat_source_component.lua), but on top of my head, the class system with the component flavour attached:

  • initialize is always called when the class is created. Think of it as your classical constructor.
  • create is called the first time the component is created on an entity. I’m not sure if this is relevant outside of components. Called after initialize
  • restore is analogue to create, except when loading a save game. They’re mutually exclusive; restore is always called when loading from a save, create is always called when initially created. Called after initialize.
  • activate is called after create or restore
  • post_activate is called when all other components on this entity have been activated. This is only really relevant when you are loading the game, as this ensures the other components have been activated as well. When the component is being created at runtime, the other components (likely) already exist.
  • destroy might be called when the component is removed. This might also occur when the entity is deleted, as the components are removed then, but I’m not sure.

Normal SH components sometimes do something like self._restore = true to figure out whether they’ve been restored or newly created in activate/post_activate. It’s a handy trick if you need to do some special stuff.

Now, saved variables are explicitly within self._sv (for components, this is done for you; for everywhere else, you need to make your own saved variables stuff and wire it up - frostfeast does this for its services in frostfeast_server.lua, as does normal stonehearth in stonehearth_server.lua). It’s important that you need to access the variables within initialize, because the saved variables need a “schema” (at least on the first level), otherwise you’re going to get an error message.

Plus, remember that there’s a difference between self:foo() and self.foo, period vs colon. A colon is a function call (self:foo(bar) is analogue to self.foo(self, bar)) and may only be used for function calls on an object (basically, you’re passing the thing you’re calling it on as first parameter. For fun, try ("Hello %s!"):format("world")).

What you want to look at is likely the stonehearth_client.js somewhere around line 101 (doCommand). It’s the whole logic, I suppose, for the whole thing - including fire_event and call. fire_event seems like it’s calling an event in JavaScript, while call calls something either as a lua function (see resource_call_handler.lua as example, or the call handlers within Frostfeast), or a member of some “global service” if object was specified (for example, stonehearth.shop).

You are right that call_trader_command is a “proxy”, but also not quite. It’s not a real proxy, the call trader is instantiated as a service within stonehearth, given the name stonehearth.shop (on the global variable, which represents the mod stonehearth, called stonehearth, there’s a member/field that’s called shop - it’s basically just a table with mod relevant data, for Stonehearth, it contains the services, but every mod is kinda free to do what it wants with that). Instead of going through the whole call handler/function bit, it’ll just straight up call the shop instance (or rather singleton) and pass it the arguments (you can get at least the entity it was called on by default).

lua doesn’t do multi threading at all, but there’s two distinct lua states (server/client) that are independent of each other, hence why you have to define the side in the manifest.json (so the call gets routed properly).

What you want, probably, is:

  • A callback function (check out Frostfeast’s present_call_handler.lua for a very minimalistic approach)
  • A manifest.json entry so it’s loaded (again, Frostfeast manifest) - you probably want this on the server side, unless you’re doing some super advanced client rendering (which I heavily assume you’re not)
  • A command.json (again, Frostfeast, although I’m somewhat sure that {{self}} doesn’t do anything anymore)
  • An entry/mixinto in your object that adds said command to the entity.
4 Likes

That’s what I was going to try… more or less. But now when you’ve explained this “inner kitchen” a bit, I feel more confident. I usually have trouble using things when I don’t know how they work, at least on a general level.

Although I didn’t know about period and colon. I got used to colon representing referencing subelements (like variables in a class).

Also your insight about creation callbacks is very helpful, thanks!

Perhaps

print(("Hello %s!"):format("world"))

? :merry: