[Help] Mod Authoring Tool Concepts

Hi everyone,

I am currently looking to get started working on a mod authoring tool for StoneHearth. The idea is to provide some basic implementation to cut down on repetitive tasks such as adding in craftable items, along with others such as adding new classes, ect. The current workflow for adding in a new item can be broken down to just copy and paste of an existing item, then a rename, but this can lead to issues, typos, frustration if you have a lot of items.

The idea here would be just

  • input your files (.png .qb .lua)
  • specify components you’d like to add
  • display all options for that component, ie “place_on_walls” - entity_forms boolean value, ect
  • specify what type of authoring to apply (craftable item, new class, ect)
  • export.

From here you’d have a complete .smod.

One of the biggest issues I’m having while brainstorming the tool is how StoneHearth will change between each update. I will need solid way to parse components and pull all the json key/value pairs available so I can display them from within the authoring tool so they can be appropriately set.

Now, here is the trouble entity_forms component

dining_table.json

"components": {
  "stonehearth:entity_forms" : {
     "iconic_form" : "file(dining_table_iconic.json)",
     "ghost_form" : "file(dining_table_ghost.json)",
     "placeable_on_ground" : true
  },

entity_forms.luac

// i've reduce the function down
function EntityFormsComponent:_post_create(json)
    self._sv._initialized=true
    self._sv.placeable_on_walls=json.placeable_on_walls
    self._sv.placeable_on_ground=json.placeable_on_ground
    self._sv.hide_placement_ui=json.hide_placement_ui
    if json.iconic_form then
        iconic_entity=radiant.entities.create_entity(json.iconic_form)
    end
    if json.ghost_form then
        ghost_entity=radiant.entities.create_entity(json.ghost_form)
    end

Json keys here are simple but displaying them from within the program not so much, options for entity_forms components

  • iconic_form - problem
  • ghost_form - problem
  • placeable_on_ground - easy checkbox
  • placeable_on_walls - easy checkbox
  • hide_placement_ui - easy checkbox

Iconic/ghost form are components, so the idea here would be that the program would display options for the iconic/ghost components in replacement of a textbox.

How would the program know to match iconic_form key to the iconic_form_component?
Further more ghost_form_component never accesses any of it’s json, instead iconic_form_component seems to interact with unit_info and material components even though ghost json specifies them???

what about in modded circumstances, where the component is modded and so is a reference in it’s json, who knows what might be going on :wink:

Another example crafter_component.
Please let me know if I’m missing something

Let use the carpenter for example - carpenter_description.json

// crafter_component.luac specified
"crafter" : {
  "work_effect": "saw",
  "recipe_list": "/stonehearth/jobs/carpenter/recipes/recipes.json"
},

// from crafter_component.luac - function CrafterComponent:initialize(entity,json)
 self._sv.work_effect=json.work_effect
    self._sv.fine_percentage=0
    if json.recipe_list then
        self:_build_craftable_recipe_list(json.recipe_list)

Again here we have our key/value pairs as expected

  • work_effect
  • recipe_list

But how do I display recipe_list without any knowledge on how it’s constructed?

I hoped self:_build_craftable_recipe_list(json.recipe_list) would expose all the json of each recipe, but it does not. Actually only a small amount of the json is referenced explicitly. We’d have to look in other areas of StoneHearth to find hardcoded references, here’s a few.

recipe example

stonehearth\ui\game\show_workshop\show_workshop.js 
recipe_name, requirement, and many others

stonehearth\services\server\town\orchestrators\work_at_workshop_orchestrator.luac
produces

I am really at a loss…

Main Solutions

  • hard code everything :sob: , parsing is more work but I’d hate to limit the program is this way

This seems just too reliant on everything being a certain way, and besides that, I’m imagining even if all the json key’s are access in a component, having to create some sort of call graph from the json init for my program to follow, and this could easily become very complicate with recursive function calls, nest arrays, other component references, ect. :confounded:

A really dynamic authoring tool might be too much for me. Something simpler is looking more realistic.

Any feedback or knowledge you’d like to share would be really greatly appreciated, I hope I’ve overlooked something, but I just don’t know…

Thanks for your time

1 Like

They aren’t components, components/stonehearth:entity_forms/icon_form and components/stonehearth:entity_forms/ghost_form are links to other files, or rather, entities. IIRC they’re used in radiant.entities.create_entity at various points.

The point about your whole concept here is this: There isn’t. The JSONs are just what they are: data. They have no rules (other than the JSON syntax of course), there’s no contracts on anything.

The best example of why this is very, very difficult is what I’ve encountered with Jofferson: There’s no distinction between a string and an entity reference. I’m just treating every single string as possible reference and use a heuristic to decide whether this is a valid link, a possible invalid link, or not a link for certain. The best example of why this is bad would be the “fake aliases” that Stonehearth has, such as stonhearth:customization_variants or stonehearth:combat. These look like aliases, but are actually names.

For Jofferson2, I have more intelligent entity parsing planned as well. As part of this, I’ll define most of the component logic in lua files. So whenever I’m encountering something that looks like an entity (which is also defined in lua), I’m firing several scripts on it that try to decode it. One would be, for example, to parse components/stonehearth:entity_forms. This script would then decide that iconic_form and ghost_form are of type entity_reference and create links to the specific files, as well as placeable_on_ground being a bool set to true. That would require that these scripts are specifically written for these components - which is a lot of work, but also the only way.

Because the data (or rather model) is completely independent of lua, and sometimes even used in JS instead of lua, it does not have a fixed form. There are no rules how to generate it, or how to parse it. Most components don’t deal well with ill-formatted data either.

In addition, there are things that happen at runtime - components are usually accessed by calling add_component, which returns the component if it already exists or creates a new one otherwise. You won’t be able to even figure them out with JSON alone.

Long story short, creating objects is rather simple with a halfway intelligent component system, but parsing objects completely is next to impossible without a tremendous amount of work.

2 Likes

Thanks @RepeatPan for your post,

I missed that, thanks.

This is very true. And as you’ve said the model is completely independent from the lua side (very very true). I guess I was trying to marry these 2 systems, but somethings were not meant to be ;).

I am just going to focus on repetitive tasks a modder will get into, ie adding craftable items, new classes, ect. These all would be hard coded into the program.

Thanks again @RepeatPan for you time, great work with Jofferson perhaps I’m too new here but I was unaware of the project, best luck with it.