Taking Questions about Mixintos and Overrides

Oh me! I have a question! :smiley:

With mininto, does it overwrite the entire section it mixes? or does it add to it?
For example, it is possible to add tags to an item without deleting the tags already in place (original or other modder’s?)?
Same with the carpenter’s recipe list, is it possible to add to the list without re-writing the entire thing?

2 Likes

based on the blog, I would say no… but I’m curious as well:

Mixintos are used to modify or enhance the content in a mod. An override will completely overwrite that asset.

@chimeforest as far as I understand the mixintos are allowing you to “inject” code into existing files. You can use it to “overwrite” something which is already included in the file into which you are adding code. This is done simply by using the same names (e.g. for variables) and assign new values to them. But this is more a selective overwriting which comes in addition to adding code.

The overrides are really overwriting a whole file.

1 Like

@chimeforest, @SteveAdamo and @voxel_pirate are correct–Mixintos are purely additive.

A note of caution: mod loading is currently nondeterministic. That means that if 2 mods both override the same thing, or both specify mixintos that alter the same property, Stonehearth currently does not guarantee which one will take effect.

2 Likes

so, it’s basically n3rd Thunderdome… “two mods enter, one mod leaves!”

I’ve been rereading the blog post, getting geared up for (hopefully) some brief forays into mixinto programming this weekend!

@voxel_pirate asked on the blog about the namespaces and aliases we use in Stonehearth. Copy-pasting my reply in here:

The first goal of mixintos and overrides is to let people do qb-file swaps, and to manipulate the gross properties of the entities that define them, so that’s what is definitely supported (as per the example with the trees). This is all the team is currently officially ready to assure people is possible. :wink:

However, if you don’t mind playing in unstable territory, you CAN also use mixintos and overrides to do a wide range of things we’re only beginning to fully explore, up to and including replacing whole files of any type. (Given the currently fluid state of the Lua, this is like playing with gunpowder, from a stability POV. It is obviously not sustainable across releases.)

So what’s up with the mod:alias-syntax?

Every file in stonehearth has it’s hard-drive address. From the POV of loading resources referenced from json, this path is rooted in the mods folder. So the berry bush’s model’s full address is:

 "stonehearth/entities/plants/berry_bush/berry_bush.qb"

If you happen to be referring to an entity within your own mod (ie, the mod that the json file is a part of) you can use the file() syntax to automatically add the mod name. If the parent folder of a .json file is the same name as the .json file you want, you can also exclude the last part. So if you look at the manifest.json file for stonehearth, berry_bush.json can be addressed like this:

 "file(entities/plants/berry_bush)"

Usually, models are not aliased, because they are only used in the definition of the entity they belong to, which in this case, is lives in berry_bush.json.

Because we’re always moving files around, it’s very cumbersome to use the full address of a file when referring to it. So the manifest.json of any mod has an “alias” section into which you can just map names to files. stonehearth’s manifest.json describes berry bushes like this:

 "berry_bush" : "file(entities/plants/berry_bush)"

And if you ever need to refer to that berry bush, you can call it by using the name of its mod (stonehearth), plus the alias. So in lua, we do things like this all the time:

--In NewGameCallHandler, places a berry bush beside the camp standard"
self:place_item('stonehearth:berry_bush', camp_x, camp_z+3, 'civ')

So, how does this apply to mixintos? Most entities (things that appear in the game) are aliased, in stonehearth. So that’s why, if you want to mix into a berry bush, with a file you’ve specially prepared, your manifest will look like this:

 "mixintos" : {
      "stonehearth:berry_bush" : [
           "file(/entities/lantern/lit_bushes.json)"
      ]
  }

However, if the file you’re trying to mixinto IS NOT aliased by its mod (like the recipes file), you instead need to use its full path name (and then maybe let us know you’d really like it to be aliased). So if berry bushes weren’t aliased, the same line above would look like this:

  "mixintos" : {
        "stonehearth/entities/plants/berry_bush/berry_bush.json" : [
           "file(/entities/lantern/lit_bushes.json)"
        ]
  },

To figure out if you need to use an alias or the filepath to get to a file in Stonehearth, check the manifest.json file to see if it’s in there. If it’s not, then you’ll want to use the full path, and perhaps let Team Radiant know that there’s a desire for that file in the modding community.

4 Likes

What if we want to append something to a list? For example adding another model variant to a human without affecting what’s already there.

The file stuff is only there to load files from the mod it’s in as well right? That is it’s only function?

1 Like

So, the team is going to replace mixintos with extensions because mixintos sounds too cute.

The only proper way to do something like that would be to introduce magic keys à file() - for example, “append(models)” : [ “foo”, “bar” ] But that’s not really satisfying either. Or is it? Edit: Simply defining an array will merge them, although the order seems to be (mixinto, old values).

file() works relative to the mod if used absolute (i.e. file(/foo) is equal to /my_mod/foo) and relative to the current dictionary otherwise.

1 Like

Isn’t that the idea of Mixintos?

I don’t quite think so… at least not in the sense that @Xavion is describing.
Since since in the male1.json “model” is the section, and if you write a mixinto to try and add to it, It’ll just overwrite the whole “model” section. In order to include the original models they will have to be in the mixinto file.

However, if it was the case of adding a new recipe to the carpenters list, since there are multiple categories, one could make a new category and add your items to it without writing over the other categories.

@sdee, Did I get that right?

I will try this one out, but my understanding is that mixintos only overwrite if you use the same variables. So it should be possible to add into files.

See Stephanie’s comment above…

… are correct–Mixintos are purely additive…

Let me know what you discover.
I hope I’m wrong, but I don’t think I am =[

I’m pretty sure “model” is the variable he would have to mess with…

It is not. They are additive, sure enough, but they work on an extending mechanism. If you want to have it in pseudo-code, it would be something like

function mixin(original_json, mixin_json)
	for key, value in mixin_json do
		if type(value) == 'object' and type(original_json[key]) == 'object' then
			mixin(original_json[key], value)
		elseif type(value) == 'array' and type(orginal_json[key]) == 'array' then
			original_json[key] = table.join(value, original_json[key])
		else
			original_json[key] = value
		end
	end
end

That means that keys are either added or overwritten. Keys that do not exist yet will be created whereas existing keys will be completely overwritten with whatever the mixinto (or, in the old version - not sure if this has changed, I really hope it did - the mixin) defines.

So if you have model_variants.models and you have a mixinto that defines model_variants.models, the mixinto will overwrite it - because it’s an array and therefore cannot be merged.

So as long as you are dealing with just a single mod, you could just copy the original over, add your stuff and define the whole thing as a mixinto?

As long as only one mod is dealing with it, yes. If two mods try to access the same array, however, you get into trouble.

Of course one of those mods maybe stonehearth and if gets updated everything can break so it’s still kind of limited. What we really need is an append to list feature, a non-overriding form might be useful as well, so it can create new attributes but not change existing ones. It could probably have uses when dealing with multiple mods covering the same subject or people who have multiple parts to their mod and only need one, it would need a stable load order though so it would be pretty much useless so far.

We don’t need a stable mod loading order, we need a customisable one - see RP. I think I’ve covered pretty much all possible cases in a quite solid system.

Appending to lists could become a bit weird though. There’s a few ways this issue could be tackled:

  • Introducing keywords for keys, such as append(key), which adds elements to the existing list, remove(key), which removes elements from the list - we could probably go on. Personally, I think sort(key, callback_handler_name) might be useful too, as would be insert(key, after_value) (or insertBefore/insertAfter/insertAt).
  • Using more magic. For example, recipe jsons could be identified by an ID instead of being an array. So [ { "category" : "Weapons" }, { "category" : "Tools" }] could become { "tools" : { "category" : "Tools" }, "weapons" : { "category" : "Weapons" } }. However, this comes at a price because (I believe) the order is not guaranteed with objects. So you would have to introduce a priority field there (which in this case would be the best approach I think) and then have lua deal with it (if possible by providing functions to deal with that).

If we modded radiant’s json loading function to allow us access to cache or even just to check against the append/sorted/removed/whatever than we could get that working. It probably wouldn’t even be too hard, load in the file, check for mixintos, run the mixintos on the cached code, voila! Actually when thinking about it how does lua handle mutability? If I make an object or array called x and then do y = x is y a copy of x or a reference to x? Because if it’s a reference than you wouldn’t even to mod radiant as we’d already have the cached object. I can’t really think of a use case for much of those either, what were you thinking?

I’m not really sure what you mean by your second point, it seems confusing and I’m not really sure how it works. It seems to be some way of turning arrays of objects into objects (of arrays? Your json is broken horribly), that just seems kind of odd to me for appending to a list.

EDIT:
I forgot that you’d have to cleanup the json from the objects caused by keys named things like “append(model_variants)” which would be created.

lua tables are always references. I really think that we can’t change it however. The whole loading business - which includes mixins and mixintos - is likely handled by the engine in C++.

insertBefore, insertAfter and companions might seem useless right now - but if we are already messing around with this stuff, we might as well provide some proper functionality that covers future cases too. The idea would be to cover all possible useful cases, not the ones we need right now. In the case of recipes, it’s useful to keep ordering intact - perhaps I want something in-between weapons and construction?

Give me a break, I just forgot three quotes.

The second point isn’t confusing at all. Instead of using an array you simply assign each category an unique ID (which happens to be “tools” and “weapons” here, but the other would be “construction_and_fences” or something like that). However, using an object isn’t exactly favorable, it bloats the json and is contra-productive in this case - because this might work for categories, but it won’t work for recipes themselves, unless you decide to give them all ids too.


It might be possible to add this functionality to single components of the game, but not everywhere. I believe it would be possible to patch Component:extend, which would allow us to search for insert and other key commands (however, it would need to be insert(key, my_mod_name) to avoid keys overwriting each other) and then perform this magic on the json before the original extend is called.

This excludes all C components, however, including model variants.