A Weird Imagination

Metatables for Factorio reflection mod

The problem#

Using my Factorio reflection library discussed previously involves interacting with Lua values that are a combination of the actual value and some metadata, so you have to know about those values to use them. Worse, the interactions I defined are quite verbose. The main thing you're like to want to do on a value is lookup a property on it. Normally in Lua that looks like

table[key]

but if instead of table you have a wrapped value from the reflection library, you would look up key on it with

ReflectionLibraryMod.typed_object_lookup_property(
    wrapped, key).value

If you want to do multiple levels of property lookups, then this quickly gets quite unwieldy.

The solution#

Lua supports operator overloading through a mechanism it calls metatables (some additional examples).

Using that mechanism, the library defines a value ReflectionLibraryMod.typed_data_raw that can be indexed as wrapped[key] and assigned to like wrapped[key] = newValue.

The basic setup looks like

local prototype = {} -- table for methods
local mt = {}
mt.__index = function (table, key)
  local res = prototype[key]
    or ReflectionLibraryMod.wrap_typed_object(
      ReflectionLibraryMod.typed_object_lookup_property(
        table._private, key))
  if res == nil then
    if key == "_value" then
      res = table._private.value
    end
  end
  return res
end

mt.__newindex = function (table, key, newValue)
  -- If newValue is a wrapped typed value, then unwrap it.
  if getmetatable(newValue) == mt then
    newValue = newValue._private.value
  end
  table._private.value[key] = newValue
end

function ReflectionLibraryMod.wrap_typed_object(typedValue)
  if typedValue == nil then
    return nil
  end

  local res = {_private = typedValue}
  setmetatable(res, mt)

  return res
end

Any additional properties would be defined next to the definition of _value. And any methods would be defined on prototype.

The details#

Read more…

Displaying Factorio history

Posted in

The problem#

Last week, I got all of the Factorio saves I had been keeping around into a single directory in order by the time they were created. But what should we do with that data? We could load arbitrary saves to see what our base looked like in the past, but loading the saves individually isn't a great way to do that when there's a lot of them.

The solution#

Luckily, I'm not the only one to want screenshots of my Factorio bases, so there are existing mods to do so.

The FactorioMaps Timelapse mod will take a list of saves and generates a web page like this demo that lets you look around your base across time. As the documentation explains, this is not actually a mod you enable for your save, but a script that you place in your mods/ directory that will run Factorio repeatedly to generate screenshots.

To set it up, use a non-Steam install of Factorio and put the saves you want under its saves directory (in a subdirectory named to_screenshot/ in this example). If you downloaded the mod as ZIP file (e.g., by installing the mod from within Factorio), unzip it; alternatively you can clone the GitHub repo or my fork which includes a few minor improvements for when displaying a lot of saves, especially ones less than an hour apart.

# get to Factorio install /mods/ directory
cd factorio/mods
# clone the git repo with the mod's internal name
git clone https://github.com/dperelman/FactorioMaps.git L0laapk3_FactorioMaps
cd L0laapk3_FactorioMaps
# install dependencies
python -m venv .venv
. .venv/bin/activate
pip install --upgrade -r requirements.txt
# add saves in /saves/to_screenshot/ to timelapse "mybase"
python auto.py --standalone mybase to_screenshot/*

Then you can find the timelapse in the directory script-output/FactorioMaps/mybase/ of your Factorio install; just open index.html in any web browser. Especially if you have a lot of saves, consider adding the --dayonly option to not take twice as much time also generating screenshots of the night view.

Generating screenshots as you play#

Note that if you just want to take screenshots automatically as you play, you can use the Screenshot Toolkit mod, which is what I use for single player games. But due to the way Factorio multiplayer works, doing so impacts every player, so in a multiplayer game it may be better to just copy the saves as the game runs and generate the screenshots later.

The details#

Read more…

Devlog: Factorio reflection mod

The problem#

When developing the "garbage collector" for the Pacifist mod, I noted that I couldn't actually know which strings should be treated as references. As a workaround, I just assumed all strings were references, which worked well enough, but I wondered if there was a way to get more precise type information. Additionally, when I was trying to figure this out, the developer of exfret's randomizer expressed interest in getting access to such information for that mod.

The solution#

The Factorio documentation includes a machine-readable version of the prototype API documentation. My ReflectionLibrary mod provides access to that information from within a Factorio mod, effectively faking a reflection API for type information on data.raw during the Prototype Stage:

local data_raw = ReflectionLibraryMod.typed_data_raw

local bb = data_raw['blueprint-book']['blueprint-book']
log(bb.inventory_size._type.typeKind)  # prints "literal"
bb.inventory_size = 42
log(bb.inventory_size._type.typeKind)  # prints "alias"
log(bb.inventory_size._type.name)   # prints "ItemStackIndex"

The details#

Read more…

JSON to Lua

The problem#

The Factorio documentation includes a machine-readable version of the prototype API documentation. I wanted to be able to access that information from within a mod, which required somehow giving Lua access to a JSON object.

The solution#

Install my branch of json2lua and run it on your JSON file:

$ npm install git+https://github.com/dperelman/json2lua.git\#feature/string-escaping
$ npx json2lua "FILE.json" "FILE.lua"

Note the output is a Lua table literal, so you'll have to add code to actually assign it to a variable to be able to use it.

The details#

Read more…

Devlog: Pacifist Factorio mod PRs (3 of 3): garbage collection

The problem#

Continuing from the past two weeks, in my work on the Pacifist Factorio mod, I noticed there were rich text icons for military-related entities that I thought the mod had removed from the game. By "rich text icons", I'm referring to the dialog that some text boxes in the game (e.g. for naming a train stop) have a button to bring up which shows icons that can be inserted into the text box which include all of the items in the game along with a few other things. In that dialog, there were icons for things like biter corpses despite biters having been removed from the game.

The solution#

This PR, which is included in the latest version of Pacifist, hides all of the icons related only to items that have been removed or hidden by Pacifist.

The details#

Read more…

Devlog: Pacifist Factorio mod PRs (2 of 3): butter slots, not gun slots

The problem#

Continuing from last week, in my work on the Pacifist Factorio mod, I wanted to remove the character's gun slots. They are always visible in the bottom-left of the screen with gun and ammo icons, making it clear there's an expectation of weapons.

The solution#

This PR, which is included in the latest version of Pacifist, replaces the icon on the slots and rewords some of the text shown in the game about them.

The details#

Read more…

Devlog: Pacifist Factorio mod PRs (1 of 3): strings, strings, strings

The problem#

I wanted to introduce Factorio to some younger cousins but didn't think the military aspects of the game would be appropriate for them, both because their parents would rather they not be playing violent video games and it's simply an additional distraction and added complexity in a game that's already fairly involved, especially for a child.

Due to Factorio's active modding community, often if you can think of a mod you want, someone else has already thought of it and implemented it. And it turned out I'm not the first person to a less violent Factorio: Pacifist already existed and did most of what I wanted. Like many Factorio mods, it's open-source and has a GitHub page. The developer was very friendly and helpful, so I was able to contribute changes get them into the next release of the mod.

My contributions weren't fixing bugs in the mod as much as nudging its goals in a slightly different direction: it was already removing the military aspect from the gameplay, but I also wanted to remove hints of it from the UI as much as possible.

The solution#

Play Factorio with Pacifist, which now includes my changes. I recommend also including my mod BackpackRename. Additionally, I found the StartAlt and Attention Indicator mods good for playing with new players.

The details#

Read more…

A newbie's introduction to Factorio modding

Introduction#

Factorio is a sandbox automation and logistics game notable for, among other things, very good support for mods. The developers often go out of their way to support features and fix bugs that only affect mods.

I've only just started to dip my toes in the world of Factorio modding, so I'm definitely no authority on the topic. But this post will be about things that weren't obvious to me starting out.

Resources#

As I said, modding is very well supported, which includes comprehensive documentation and tutorials. One detail I'd call out is that I recommend installing FMTK, which provides IDE tooling for writing Factorio mods, including a VS Code extension (don't worry, there's a Vim mode for VS Code). Also you will probably spend a lot of time looking at the log file. Additionally, you can find lots of examples by looking at the many existing mods; a lot them have links to source code repositories from their mod pages, but even if they don't, you can just download them and unzip them.

If you have a question that can't be answered by those resources or a web search, you can ask for help on the modding forum or the #modding-help channel of the official Factorio Discord.

Other tips#

Read more…