How do I add an object command?

I can see where the Stonehearth manifest specifies “object_commands” and I tried adding one in my own manifest (after creating a lua class that I mixin to an existing Stonehearth class, using the same method as Smart Crafter), but my log reveals that my command is rejected by the server (“rpc rpc lua”).

I was originally trying to do a regular function call to which I would pass this object as a parameter, but passing the uri to it in the same way that uris get passed as parameters to other functions resulted in an object of UserData type which I couldn’t do anything with.

Anyone have a hint?

What exactly are you trying to do? What’s the desired outcome?

I’m trying to add an interaction to the craft_order_list object to, instead of adding the order to the end of the list like normal, add a new order to the top of the list. I can do this in a terribly hacky and inefficient way using existing object commands accessed through javascript (which I’m currently doing in my InsertCraftOrder mod), but it should be really simple to do the whole process in lua if I can only properly integrate the object command and call radiant.call_obj (or pass the order list object from javascript as a parameter with radiant.call).

You will need both a defined object and a call_handler for it, as well as all the relevant manifest registries.

Can you point me in the direction of an example?

As far as I can tell, I’m doing the same things for my function as the built-in craft_order_list functions have done for them. There is no separate call handler defined for craft_order_list:add_order_command aside from the “order_commands” section of the manifest.

When I was working on a similar issue, I used the “use consumable” button on e.g. strength potions as an example.

I just can’t find the part that I’m missing. This is my class code (which you can see includes a call handler):

local CraftOrderList = radiant.mods.require('stonehearth.components.workshop.craft_order_list')
local InsertCraftOrderList = class()
local log = radiant.log.create_logger('craft_order_list')

function InsertCraftOrderList:insert_order_command(session, response, recipe, condition)
	if #self._sv.orders >= MAX_ORDERS then
      -- do not add orders if we are above the cap
      return false
   end

   self:insert_order(session.player_id, recipe, condition)
   return true
end

function InsertCraftOrderList:insert_order(player_id, recipe, condition)
   local order = radiant.create_controller('stonehearth:craft_order', self._sv.next_order_id, recipe, condition, player_id, self)
   self._sv.next_order_id = self._sv.next_order_id + 1
   table.insert(self._sv.orders, 1, order)
   self:_on_order_list_changed()
   self.__saved_variables:mark_changed()
end

return InsertCraftOrderList

And in my initialization script I call this function:

local function mixin_craft_order_list()
   local insert_craft_order_list = require('insert_craft_order_list')
   local craft_order_list = radiant.mods.require('stonehearth.components.workshop.craft_order_list')
   radiant.mixin(craft_order_list, insert_craft_order_list)
end

And then my manifest includes:

"object_commands": {
    "craft_order_list": [
      "insert_order_command"
    ]
  }

And the mixing in seems to be working, because whether I specify “craft_order_list” or “insert_craft_order_list” as the object in the manifest, the log says the same thing:

[timestamp] | server | 0 | rpc rpc lua | rejecting: stonehearth:craft_order_list insert_order_command

(It knows I’m referring to the “stonehearth:craft_order_list” object even if I refer to it as “insert_craft_order_list”.) I think consumables are handled in a somewhat different way and aren’t a good model for what I’m trying to do.

By the way, that insert_order_command and insert_order code is basically verbatim lifted from craft_order_list.lua’s add_order_command and add_order, with just a “, 1” added to the “table.insert(...)” function call.

Have you specified your call handler in your manifest? I’m looking for something like this

   "functions":{
     "designate_firing_zone":{
       "controller":"file(call_handlers/cannon_call_handler.lua)",
       "endpoint":"client"
     }
   }

(example from an old unreleased mod, but that part nonetheless worked)

e: your endpoint would be server, obviously, rather than client

But isn’t that just for non-object function calls, called via radiant.call(...) rather than radiant.call_obj(...)?

In the Stonehearth manifest, the only places where “craft_order_list” is referenced are in the object_commands section and in the aliases section. Maybe I need to add an alias? But as I said, the code is being mixed in as evidenced by the log.

Edit: and the only place where “add_order_command” is in the manifest is in the “craft_order_list” section of “object_commands”.

Also, I’m pretty sure I’ve gotten a different error when it can’t find the function. That actually pops up a lua error in the game to let me know. This just fizzles in-game and I have to check the log to see the rejection message.

I seem to remember having a bit of a struggle with the object_commands thing, too. I don’t think my aim was similar to yours, though, so I doubt it’d be helpful.

Have you tried hardcoding it right into craft_order_list.lua and seeing if that worked? To eliminate the possibility that you’re packaging things wrong, and there’s actually a logic error somewhere?

I can try that, though I’ve avoided modifying stonehearth.smod code directly up to this point.

It’s worth a try. I’d add your function in there, and have the existing callers of add_order_command call insert_order_command instead. See if it works the way it’s supposed to in a “perfect” world, where everything mixed in, and all the names match just right. If it still doesn’t work, then it’s gotta be your code. If it works after you do that, then the packaging must not have come together right.

e: I’m just having a closer look. Are you absolutely certain your initialization script mixin is running? From what I can see, if you mixed in the function name and location through the manifest, that’s how Stonehearth would know that your function should belong to the craft_order_list object command array. But if it tried to call this function, and this function didn’t exist, maybe it would give you that error. So, another two things I would try: 1) stick a log into the init and the insert_order_command functions to ensure they actually run, 2) try “constructively failing” like mixing in a non-existent command into craft_order_list through manifest and calling it; is the error you get the same as when you try to run your coded command? I’d try this in different combinations to feel out where my code is really failing.

Okay, putting the insert_order_command and insert_order functions directly into craft_order_list.lua and adding “insert_order_command” to the object_commands worked. So it is something with the mixing in that’s failing then.

Also, I realized as I was trying this that the rejection message isn’t proof that the object is getting mixed in because it’s not looking up the command and saying the call was rejected from the command’s object: it’s just checking the object type of what I’m passing in to radiant.call_obj(...) which is a stonehearth:craft_order_list object.

Thanks for the suggestions, I’ll continue trying things.

1 Like

So far, my greatest difficulties with SH haven’t been in programming for the game, but in telling it where to find and how to use my code. Once that works, programming the game mechanics has mostly been a lovely walk in the park; but getting there, making the connections work, is really trailblazing it through jungle.

1 Like

And that’s where having that part of the modding guide fleshed out would be invaluable, so I hope they get around to it soon, especially as we’re “engine complete” and the API shouldn’t be changing much anymore. I can easily look up what I don’t know about jQuery or lua syntax on the internet, but trying to figure out how things are calling each other or puzzle through the object model to find where a piece of code is hiding are quite challenging.

With logging, it appears the mixin is successful, which means it’s down to the object_command thing… and prefacing it with “stonehearth:” doesn’t seem to work either:

"object_commands": {
    "stonehearth:craft_order_list": [
      "insert_order_command"
    ]
  }

Edit: And guess what? If I keep all my mod code the way it is and just add “insert_order_command” to the actual Stonehearth manifest’s object_commands section, it works! So I just can’t add it via my own manifest, or there’s some trick to it that I don’t know.

Which brings me right back to my original question. How do I add an object command? Or, failing that, how do I pass an object to a function via radiant.call(...)?

@ayazar or @morgan10e should be able to help you with the exact syntax for declaring object commands, but I would recommend using regular commands as they are more straightforward. Passing an object through should work just fine by passing an object address like you mentioned above. It’s quite reasonable for you to get a userdata object out. Many objects including entities are userdatas. What sort of object are you trying to pass through?

I was trying to pass in a CraftOrderList object, acquired via App.StonehearthTeamCrafterView.getOrderList(), which returns this.get('model.order_list').__self. This object works for calling radiant.call_obj(self.getOrderList(), 'add_order_command', recipe, condition);, but when I pass it into my own function with radiant.call(...), it doesn’t seem to have any of the properties or functions associated with the CraftOrderList class.