How do I pass an object into my lua function via radiant.call(...)?

Since I can’t add an object_command to a default Stonehearth object (without directly editing the Stonehearth manifest), I have to operate on that object in a separate lua function, so I need to pass that object in. How do I do that?

In combat_server_commands_service, I see parties and target entities getting passed in as parameters, and the javascript calls for those commands pass them in as uri strings (e.g., “object://game/1234”), which then by some sort of magic get transformed from strings into userdata type objects when they arrive at the lua function. Because they’re userdata, I can’t see what’s in them, but apparently calls to “:get_id()” and “:get_component(…)” work just fine.

But when I pass in a “craft_order_list” object uri to my lua function and it gets transformed into a userdata type object, it only has the “:get_id()” function but not the “:get_component(…)” function or any class functions.

How do I pass a Stonehearth object from javascript so I can access the actual lua class properties/functions of that object, specifically a “craft_order_list” object?

Pseudo code since it’s ages since I’ve done this stuff. Maybe needs an underscore here or there. Hard to say without seeing any code, and I don’t think I quite grasp the problem per se, but let’s see.

JS: radiant.call('mymod:my_callback', my_entity_reference_that_I_got_somehow, 12, 'foobar');
lua: function CallbackHandler:my_callback(session, response, entity_reference, twelve, foobar)

It’s the same in lua, as the engine handles (de-)serialization of references. Stuff that can’t be serialized will either throw an error or crash the game violently if I remember correctly.

Because JS doesn’t know anything about Stonehearth, and runs completely sandboxed inside of CEF on top of it, it can’t have proper access to the entity (not that it should even if it could) - therefore, referencing to entities, services and game objects in general is done using URIs. The engine handles this accordingly by (de-)serializing entities from/to URIs. You have objects and tables in lua, and URIs, arrays and objects in JS.

Not entirely true. SH uses luabind for the C++ classes, and unclasslib for the lua ones. I think you can extract what kind of class it is by calling class_info(entity) on your userdata, which should return a table (if it’s a luabind type) or nil. If it’s a table, there’s a name property and a methods table, as well as an attributes table. Give it a try. If it isn’t a luabind entity, classname(entity) could work. See also the unclasslib docs., then you can try and see what classof(entity) yields - it should give you the definition of the entity’s class (a table).

How did you get that URI? It’s been a loong while, so I’m not sure if you can get a component from another component (I think you can’t?). It might be possible that you are passing a component, not an entity, and said component doesn’t have get_component - in that case, you would need to get the entity first (assuming the component/whatever you passed has it).

It could also be possible that you’re operating on the wrong realm (server vs. client) - some information might only be available on one of them. The client, for example, might know that it is an object that exists (for synchronisation purposes), but not what it does, or what data it carries.

1 Like

Thanks for the response!

I’m as sure as I can be that I was operating entirely in the server realm for dealing with the server object of craft_order_list (I specified the “endpoint” of my callback function as “server”), so that at least shouldn’t be an issue.

The same way (and in the same location as) the workshop UI gets it and calls CraftOrderList:add_order_command on it with radiant.call_obj(...): In show_workshop.js (which has its model URI specified as some sort of “workshop” object), the return value of this.get('model.order_list').

That’s a weird model. So you’re thinking if I did something like my_component:get_entity():get_component('my_component'):component_function() that would work when my_component:component_function() doesn’t? Why doesn’t the translation process by which the URI is transformed into a userdata object not turn it all the way into a usable object?

I’ll look into your suggestions at some point here (it’s just frustrating to do this without an API), but in the meantime I figured out a workaround. Instead of passing in the order_list URI, I pass in the crafter’s job alias (e.g., “stonehearth:jobs:carpenter”) and then get the job_info for that alias and the order_list from that:

function InsertCraftOrderCallHandler:insert_craft_order(session, response, crafter_job, recipe, condition)
	local job_info = stonehearth.job:get_job_info(session.player_id, crafter_job)
	local order_list = job_info._sv.order_list
	order_list:insert_order_command(session, response, recipe, condition)
end

It works for this issue, but isn’t a good strategy for dealing with other objects.

Nah, I was thinking you had a situation similar to component_a:get_entity():get_component('component_b'):call_b_method(). I haven’t familiarised myself with the entities/components/methods in question and am currently quite time-restricted so I tried to be as helpful as possible, which isn’t a lot (considering my usual standards).

It does turn it into an usable object, but it might not be the object you’re looking for.

What API are you looking for, exactly? class_info and classof can return you plenty of information that can help you identify the object. if you want to get fancy, throw debug.getinfo into the mix, for example:

classof(stonehearth.ai).methods to find a method, followed by debug.getinfo(classof(stonehearth.ai).initialize) to figure out where it’s been defined - and you’ll get a pretty source, including line number.

For C++ entities, I’ve written above how to query what they are and what they can do.

Thanks! By API I mean, in part, all of the stuff you listed here, but in general the whole object model: what all the objects are, what their properties/methods/etc. are, where the applicable files are located… all that stuff that I have to spend hours digging through similarly-named files in a bunch of different locations to try to figure out, only to later realize that something is happening at a higher level and I don’t know how or why or where to look for it.

Ages ago, I had written a lua script that dumped all available (or at least, easy-to-find) entities that were “hidden” in C++.

Since then, the APIs have been heavily restricted, especially io is completely gone - so you’d need to find another way to dump that stuff (send it to the CEF console, or a HTML widget, or whatever). Back then, it had a fair share of memory corruption around it too, for some reason. But basically, it created a file like this:

Although this is quite old (June 2017), most of it should still apply, I suppose.

It’s just part of the DIY-documentation that I had going back then.

For lua, debug.getinfo and CTRL-Shift-F are your best friends.

1 Like