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…

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…