The existing controllers are distributed among many of the folders from the stonehearth mod, so to find them take a look at their paths in the manifest.

About controllers

A controller is a utility class that stores data/performs a function (and isn't a component / service / call handler / etc). Components themselves are also controllers, except they get loaded at a specific time at game start up, and are specifically tied to an entity. Controllers have some behaviors that make initializing / loading / destroying easier.

Example controllers:

  • All scenarios
  • Inventory trackers (and the util class that distinguishes each inventory tracker's functions)
  • Trait / buff scripts
  • Class-specific functions/logic

Anatomy of a controller

Controllers are classes with self._sv for saving variables and with initialize/restore/etc functions that guarantee some saved state flow control between saves and loads.

  local MyController = class()

  -- Initialize is called when the controller is first created
  function MyController:initialize()
     -- Note: all controllers are automatically equipped with a
     -- self._sv variable into which you can save things
     self._sv._entity = nil
     self._sv._uri = nil

  function MyController:create(entity, uri)
     self._sv._entity = entity
     self._sv._uri = uri

  -- Called on load, after all datastores saved in self._sv have been restored
  -- Note, if you want to be sure something has been restored, by the time we
  -- initialize ourselves, save it in self._sv
  function MyController:restore()

  function MyController:activate()
     local json = radiant.resources.load_json(self._sv._uri)
     local data =

     local some_component = self._sv._entity:get_component('my_mod:custom_comp')

  function MyController:destroy()
     local some_component = self._sv._entity:get_component('my_mod:custom_comp')

  -- Return the controller so it can be used as a class. 
  return MyController

Remember that you might not need to add all the special functions to your controllers, only the ones that you need for its correct behavior.

Using a Controller

Assuming you have a controller.lua file as described above, you can create an instance of it anywhere in Lua code. You can even pass controllers to each other!

First, declare the controller in the "controllers" section of your mod's manifest (at the same level than "aliases", "mixintos", etc). This maps a stonehearth-visible name to the controller file:

  "controllers" : {
     "my_controller" : "file(services/server/my_controller/my_controller.lua)"

The folder in which you save your controller Lua file will vary. Usually in the same folder than the main script or JSON file that will be using it, so that all the related files are together.

Then in some other script you can create an instance of the controller by calling:

  local controller = radiant.create_controller('my_mod_namespace:my_controller')

Anything you pass after the name of the controller is passed into the create method of the controller as arguments, e.g.:

  local tracker = radiant.create_controller('stonehearth:inventory_tracker', controller)

And finally you can call functions from that controller within your script:

  local tr_data = tracker:get_tracking_data()

iconMind that sometimes there's already code from the stonehearth mod that will create and use your controller. Some JSON files reference a controller alias/file that will be created for you, so you just need to declare your controller in your manifest, write its Lua file and reference it in the JSON (for example: components, trait/buff scripts, etc).

Client controllers

The "controllers" section of the manifest is for server controllers. Most controllers will fall under this category. You can access server-side services like the population service from them. Controller state is saved.

Client controllers are generally for things like the camera, rendering, sound, and other client-side behavior. Unlike regular controllers, client controllers can access client services like the seasons_render service or the camera service. They can also access client C++ commands like _radiant.client.get_render_entity.

Client controllers do not have saved state. Their _sv variables are used for remoting to the UI.

You should add any controllers you plan to create through radiant.create_controller into the manifest. Not adding them can cause issues with the controller lifecycle methods. For client controllers, you should add them under the "client_controllers" section.