##Console
Ctrl+C opens the Stonehearth console. The user can interact with the game world to an almost unlimited degree through the console. I decided I wanted to add my own commands to it. In the end, I succeeded; it took me about half an hour of trial and error to do so. It took a lot longer to write this. I do hope, however, that my thought process in resolving this issue will help someone with their own coding challenges down the line. Skip to the bottom for the tl;dr.
Objective
Following from the last post, I want to add a method that gives me an entity’s location in the game world. I expect this location to be in the form of (x,y,z) Cartesian coordinates. Ideally, we’d like to click on an in-game element/entity, run the command, and receive the coordinates in the console.
Investigation
Generally speaking, plaintext is a great “code anchor”: if we see plaintext in an in-game area that interests us, there is a high chance that searching for it in the game files will lead us to relevant code. Let’s get some plaintext! We can run the help
command in the vanilla console. This gives us a number of options we can search for; I’ve picked dump_inaccessible_items
because of its nice and long name. A search reveals a JS file: /stonehearth/ui/debug/console/console_commands.js.
A cursory read-through of console_commands
offers the following tidbits. We have a selected
variable that appears to correspond to whatever entity is currently selected by the player (yay! That’s part of our objective solved!). There are two ways to call a Lua function from this JS file: via radiant.call
and radiant.call_obj
. The difference between these is not yet clear. Finally, these caller functions themselves accept multiple arguments. The first is the name of the function, the second (and everything after the second) are arguments we want to pass into the Lua function. Looks doable enough, let’s make one of our own!
Implementation
There’s no better way to learn how the code works than by breaking it. My first step was to register another console command called get_loc
, like so:
radiant.console.register('get_loc', {
call: function(cmdobj, fn, args) {
return radiant.call('radiant.entities.get_world_location', selected);
},
description: "Returns location of selected entity"
})
radiant.entities.get_world_location
is a valid function that can be checked out in /radiant/modules/entities.lua. It indeed accepts one parameter, the entity whose location we want to check, and it should return us a Point3
describing the current location of the entity.
Firing up stonehearth and opening the console, we can type help
again and we will now see our newly registered function. And yet, running it, we get an explosion: could not dispatch [function fn:474 route:radiant.entities.get_world_location]
. Hey: at least we got the function to show up in the list of console commands, right? That’s a start!
I’ve learned that when you cannot figure out how something works, generally speaking it’s a good idea to parrot somebody who does know. Radiant’s native console function kill
looks like a good candidate: it too accepts an argument in the form of an entity. The actual Lua function being called appears to be named stonehearth:kill_entity
. Let’s see where (and how) this function is defined.
I searched for what seemed to be the function’s name, but found nothing. This is fine; we dumb it down until we find something. I search for kill_entity
. I get a few results. A few of them are calling radiant.entities.kill_entity
. Considering that we already tried to run a method from radiant.entities
before, I am willing to bet that these are not the files we’re looking for.
There is one that calls stonehearth:kill_entity
, but because it’s calling it rather than defining it, I skip it and continue looking. Eventually, I happen upon /stonehearth/call_handlers/entity_call_handler.lua, which looks about right. And inside, we find our kill_entity
defined and calling another function, this time radiant.entities.kill_entity
. Well, that looks real close to what we tried to do, but couldn’t, with .get_world_location
. I don’t understand it yet, but it appears this entity_call_handler
is a necessary in-between to pass commands from JS to Lua. I define my function here this time:
function EntityCallHandler:get_loc(session, response, entity)
assert(entity)
return radiant.entities.get_world_location(entity)
end
Then I change the calling part of my console command registration code to the following: return radiant.call('stonehearth:get_loc', selected);
And yet, again… could not dispatch [function fn:474 route:stonehearth:get_loc]
.
Around here, I started to get a hunch. Isn’t it just peculiar that Stonehearth seems to magically add stonehearth:
in front of my function? I expand my search for kill_entity
from Lua files to include everything, and up comes my answer: we get a result for manifest.json
! Looks like the manifest is the ultimate glue between the different components of the program. We must register our function in the manifest before the console will know what we’re talking about:
"functions" : {
...
"get_loc": {
"controller": "file(call_handlers/entities_call_handler.lua)",
"endpoint": "server"
},
...
}
Now when we run Stonehearth, open the console, select a hearthling, and run get_loc
, we get a beautiful 3-dimensional output: {x: 100, y: 230, z: 4}
. Yay!
##tl;dr
To register a new console function, you must:
- Know what you want it to do, and find in-game Lua functions that will help you do it
- Using the above, create a method for your console command in the appropriate call handler
- Add the new command to the manifest using the name in the call handler
- Register the command by its manifest name inside console_commands.js
Happy scripting!