Modding Stonehearth's Graphics Test Program

@voxel_pirate @Xavion @Miturion @Froggy @Alesfatalis - You’re all awesome!

Seeing what you’re doing with just the graphics test has got me all excited for the possibilities.

2 Likes

@Xavion Do you have a working decompiled micro_world.luac ?

Sure, while luadec51 didn’t quite work the unluac code for it worked fine.

3 Likes

It’s just that the names for them aren’t stored in the bytecode so it replace them with it’s own names. It makes it a bit harder to figure what they’re meant to be but it normally doesn’t provide too much trouble.

The decompiler uses this “code” for variable names (as far as I can tell): x_y_z where:
x = is class count
y = method count
z = argument count

So if you had two classes in one file and three methods in the second class with four arguments in the third method the last argument of the last method of the last class would be:
2_2_3

Not that, that does much except help you trace an argument name a little better. It seems the class count starts at “1” while the method and argument counts start at “0”. Odd that the decompiler uses “zero based” and “one based” counting in the same file. But as a software engineer who has been writing code since I was 14 (33 years) I would do away with all zero based counting systems. The thousands of times I’ve seen that lead to off-by-one errors in code.

3 Likes

I’m really impressed with what you all have been able to tinker with so far :slight_smile: if only weddings, work, and holidays didn’t coincide with the graphics test for me :stuck_out_tongue:

I have a really basic question on all this: Is decompiling going to be how we approach modding in SH? On a scale from 1-10, where is decompiling in the finished project?

When I was doing all of my research for my Fallout Mod, it appeared to me that ‘decompiling’ or otherwise reversing the core code of a program was considered to be a hack (hacking, etc, whatever verb fits), not a mod. I’m not a programmer, so the thought of breaking apart the engine is less appealing to me for a few reasons. 1) it’s hard, 2) it’s a lot less “sharable,” especially via steam.

So is our current approach going to have a future for SH, even as a possibility? Like will our current approach be one of many, or not possible due to Radiant program restrictions? Maybe @sdee would be the best person to ask.

Or perhaps I am misinterpreting what you all are doing, and all that is happening is that the mod-able files are being changed. Sort of like changing an html file, not the browser.

I’m fairly certain that everyone here is simply tinkering with what we have available. Modding the game will be a whole other process altogether. However, it’s much easier to design some a new fuel injection system if you know how the car works :wink:

3 Likes

I am convinced that it is not. As we can see, uncompiled code is already readable. I could imagine that the devs have compiled the code for some obscurity-reasons :wink:. Why do I think so? The devs always highlight that modding will be easy in the “final” product. Decomiling does not fit into such statements.

As mentioned in the initial post… take all the posts in this thread very cautious. You need to be fully aware that we are doing things which the devs might not have planned for us to do. The Graphics Test is what it is called… a test for the graphics engine. It is no showcase for modding or anything in this direction.

However as @Salletan mentiones… we can take some information out of it, which is kind of cool.

1 Like

oh, absolutely not… these amazing programmers and tinkerers are simply taking apart the materials that were provided as part of the graphics test, to see how it all works, and tweak/test accordingly…

in the preview release and of course with the public release, there will be modding tools at our disposal… this is one of the core ideas behind SH, so making it accessible to the average user is (i’m sure) very high on radiant’s list of priorities… :+1:

2 Likes

Me votes for Steve’s new title… birds whisperer :wink:.

You know, that’s exactly how I felt when I started working here, after 10 years in the industry. Just try–I dare you!–to switch between C++, Lua, and JS four or five times all in one day and not make one mistake that involves a semi-colon and/or curly-brace. :slight_smile:

Why unlearn when you can apply? :wink: We’re all fans of design methodology over here.

Yes, good instincts. :slight_smile:

Yes, well said. :wink:

Basically, we compiled the code so it would be super clear that the stuff in the graphics test wasn’t final code/api yet, and so that you, looking at it in all of its obfuscated glory, would be unable to forget that it’s unofficial.

Which is to say, we’re really impressed with how far you guys have gotten into it already. :slight_smile: :slight_smile: :slight_smile: We can’t wait to see what happens over the next few months.

9 Likes

indeed… we already know there will be some big players in the modding arena…

but (i imagine) this will be one of those rare occasions where there will be huge numbers of the more “casual” players who make the leap into modding as well… some of which will start small (modifying stats, etc.), and others who will undoubtedly introduce massive changes to the core game…

i see this place being an absolute madhouse over the holiday break, and well into the new year… :smile:

3 Likes

I have been trying to add a stockpile to the GTP but with no success. Every time I call place_stockpile_cmd in micro_world.lua the app crashes. After reading a bunch of old out-of-date lua documentation (because the new stuff is just so dry I couldn’t read it), I decided to change the decompiler names for the arguments of the place_stockpile_cmd function.

MicroWorld.place_stockpile_cmd = function(unknown, inventory_id, x, y, length, width)
    if not length or not length then
        length = 3
    end
    if not width or not width then
        width = 3
    end
    local location = Point3(x, 1, y)
    local size = {length, width}
    local inventory_service = radiant.mods.load("stonehearth").inventory
    local inventory = inventory_service:get_inventory(inventory_id)
    inventory:create_stockpile(location, size)
end

This makes it easier to tell what the arguments for the function call are. You can also see that I am guessing on two of them and may have the last two pairs flipped. “unknown” is never used so I can’t tell what that should be. “inventory_id” could be an ID or some other way of identifying or typing a stockpile that I am unaware of. “x” and “y” are obvious, they place the stockpile relative to the origin 0, 0 (where I have a bunny sitting). “length” and “width” are also obvious, note the lua idiom to assign them a default value.

Which brings me to the point of this post. I decided I could not call all the arguments but needed to use the lua way of calling named arguments. My call went from this:
world:place_stockpile_cmd(0, 1, 6, 6, 2, 2)

to this:

world:place_stockpile_cmd(x=6, y=6)

You’ll also note that the variable “world” identifies whatever start_game.lua uses to identify what I think is the “world”. See code here:

MicroGame.__init = function(world)
    world[MicroWorld]:__init()
    world:create_special_world()
    world:place_tree(-6, -6)
    world:place_stockpile_cmd(x=6, y=6)
    world:place_tree(6, 14)
    world:place_citizen(0, -5)
    local worker = world:place_citizen(-6, 1)
    world:place_rabbit(0, 0)
    world:place_rabbit(4, 4)
    -- local brabbit = world:place_item("stonehearth:big_rabbit", 6, 6)
    -- brabbit:add_component("stonehearth:leash"):set_to_entity_location(brabbit)
    local rabbit = world:place_rabbit(1, 6)
    radiant.entities.turn_to_face(worker, rabbit)
end

The upshot of all this is that switching to named arguments has allowed me to call the function to place a stockpile without crashing the game!!! :slight_smile: The downside? Nothing but a black screen. Yup, nothing but the text in the UI renders, no bunnies, no trees, no nothing.

Progress? Maybe.

3 Likes

Hm… we know that we will be able to place more than one stockpile in the world, so couldn’t “inventory_id” be a unique identifier for each of them?

There are four things I think that could be passed to that argument to call forth a stockpile. A number, a string, a table, a function (Heaven forbid). Any of these could uniquely identify or define a stockpile. I have tried the first two, a function is beyond my current knowledge base, but a table is worth trying unless it is more than just an empty table (and why would it need an empty table?).

I am out of ideas.

Why unlearn when you can apply?

Unlearn was an iPhone autocorrect “to learn everything I can” is what I meant.

2 Likes

Before we shipped the graphics test, we yanked a lot of gameplay code out so we could isolate any errors that happened to a smaller set of files. The stockpile may indeed have been a casualty of the giant nuking. Still, good thinking!

1 Like

Stands for place_stockpile_command :slight_smile: I have tried this too but i haven’t a solution yet. There are different command listeners and event listener so i think you can’t execute the function with world:. Also there is somewhere a .luac which is something like a keyboard constructor i have to look into this but maybe we can assign a key to the function and get it working in that way.

LUA is such a different world for me. I am a PHP programmer and in school i am learning C#. C# is compareable to LUA but there is still the problem that we have to reverse engineer everything :frowning:

3 Likes

This reminds me of the blind men/elephant problem; Lua actually reminds ME of javascript. :wink: If you want to compare it to C#, though, just keep in mind that all objects are actually tables, and there are no types, and functions can be passed around like variables. There’s also no threads, just coroutines, but we try to keep those way out of sight in Stonehearth.

One reason our lua looks a bit more object oriented than it could is because we use unclasslib.lua to add a class-like structure to our code. However, if you really get into the guts of what we’re doing, there’s a very javascript-y, prototype-y thing going on. If you look at most of our lua files, they share a common stucture that makes them look like a class:

–Like declaring that this thing named MyClassName will be a class;
–ie, a collection of data and functions
local MyClassName = class()

–Like a constructor for that class
function MyClassName:__init(my_init_var_1, my_init_var_2)
end

–All your other functions
function MyClassName:my_other_functions()
end

–Return the class to people who want to use it
return MyClassName

If you think about this, you can use MyClassName like a C# class, but it is actually a variable object, in memory, that contains a template or prototype for the relevant functionality, not a class definition as we understand it in C/C++/C#/Java.

Note: I don’t claim to understand prototype inheritance and class well enough to say, teach it, but the point of using unclasslib and other constructs is to wrangle lua around to a place where it feels more like something we’re all familiar with.

4 Likes

@sdee Awesome thanks for that little lesson. That is going to help me understand more of that LUA thingy :wink:

2 Likes