Hotloading mods

What is hotloading?

In the case of Stonehearth, it means activating something at run time, that is, after the game has already initialized everything.

When should I hotload mods?

Well, you might have created a custom kingdom or biome, and want only certain campaigns or other features to be enabled only when that kingdom / biome has been chosen. For instance, when we choose to play with the Rayya's children mod, we override some images so that the UI looks different, and when we choose to play in the Desert biome, we override some models of goblins and other animals, so that they look more desert-like.

In the past we called it "hotloading mods", but in the present it should actually be called "applying manifests", since we're not activating whole mods here.

iconHotloading manifests is only allowed before world generation completes -OR- for 'client_only' manifests from client Lua.

Not everything in applied manifests will work. It's a known issue that certain mixintos won't get applied correctly from a hotloaded manifest, because the game has already cached those values (e.g. adding new crops to the list of initial crops). In most cases you'll have to add those into your main manifest.

The applied/hotloaded manifests are usually inside some subfolder of our mod, and they have the

  "deferred_load": true

flag inside their "info" section.

How can I hotload mods?

In order to hotload the extra manifests we have two options:

  • If we made a custom biome, we can apply additional manifests in the "applied_manifests" key on the biome's JSON file (the one with the main info, not the generation data one), which will get applied when the player chooses to play in our biome:

    "applied_manifests": {
       "desert": "file(desert/manifest.json)"
    }
    
  • If we want to apply additional manifests only when a certain kingdom is chosen, or using other conditions, then we need to use a client_init_script.

    Create a Lua file inside your mod, and in your manifest, add:

    "client_init_script": "file(rayyas_children_client)"
    

    at the root level (e.g. below the "info" section).

    iconNotice how in this case we don't include the .lua extension inside "file()". For "server_init_script" we wouldn't include it either.

    The example above comes from the Rayya's Children mod. Now, inside your init script, you'd need to have something like this (continuing with the same example):

    rayyas_children = {
       constants = require 'constants'
    }
    local player_service_trace = nil
    
    local function check_override_ui(players, player_id)
       -- Load ui mod
       if not player_id then
          player_id = _radiant.client.get_player_id()
       end
    
       local client_player = players[player_id]
       if client_player then
          if client_player.kingdom == "rayyas_children:kingdoms:rayyas_children" then
             -- hot load rayyas children ui mod
             _radiant.res.apply_manifest("/rayyas_children/ui/manifest.json")
          end
       end
    end
    
    local function trace_player_service()
       _radiant.call('stonehearth:get_service', 'player')
          :done(function(r)
             local player_service = r.result
             check_override_ui(player_service:get_data().players)
             player_service_trace = player_service:trace('rayyas children ui change')
                   :on_changed(function(o)
                         check_override_ui(player_service:get_data().players)
                      end)
             end)
    end
    
    radiant.events.listen(rayyas_children, 'radiant:init', function()
       radiant.events.listen(radiant, 'radiant:client:server_ready', function()
             trace_player_service()
          end)
       end)
    
    return rayyas_children
    

    We have a couple local functions that we'll use to track which kingdom was chosen and to apply the additional manifest.

    At the bottom, we listen for this script being initialized, and when that's done then we listen for the 'radiant:client:server_ready' event. Once that's ready, then we can start checking (using the stonehearth services) which kingdom is chosen in the embarkation screens.

    If the kingdom URI is "rayyas_children:kingdoms:rayyas_children" then we call:

    _radiant.res.apply_manifest("/rayyas_children/ui/manifest.json")
    

    which effectively applies the extra manifest that changes the appearance of the UI. The deferred manifest has the client_only flag set to true (as noted above, it's one of the requirements to hotload manifests). You might also see a deferred_load flag in applied manifests, but it's no longer needed for them. Main manifests shouldn't have it, either.