Skip to content
Afforess edited this page May 9, 2016 · 12 revisions

Welcome to the Factorio Standard Library!

What is the Factorio Standard Library?

The Factorio Standard Library is a project to bring Factorio modders high-quality, commonly-required utilities and tools that developers have been often copying around, remixing, and rewriting poor quality copies of. Basic tools like logging, math calculations, and position or area manipulation, which involve repeated drudgery and are commonly reproduced in high-quality, documented form in the Factorio Standard Library.

How do I use the Factorio Standard Library in my mod?

  1. Download the latest release of the stdlib
  2. Unpack the archive, and copy the stdlib/ directory into your base mod directory
  3. In your control.lua or other lua classes, you can import the stdlib classes you need.

Logger example

For example, using the logger:

require 'stdlib/log/logger'
require 'defines'

LOGGER = Logger.new('example-mod-name')

script.on_event(defines.events.on_tick, function(event)
    if event.tick % 600 == 0 then
        LOGGER.log("An example log message!")
    end
end)

In factorio, mod log output will be in %appdata%\factorio\script-output\logs (on windows), or ~/.factorio/script-output/logs (on OSX or Linux).

Avoiding iteration, and general stdlib tools

Often in factorio modding, you have an array of entities and need to apply some action or data to them. For example, if you want to damage or kill biters nearby players when they log in, you might write this code:

require 'defines'

script.on_event(defines.events.on_player_created, function(event)
    local player = game.players[event.player_index].character
    local position = player.position
    local force = player.force
    local entities = player.surface.find_entities_filtered(area = {{position.x - 25, position.y - 25}, {position.x + 25, position.y + 25}, type = 'unit', force = 'enemy'})
    for _, entity in pairs(entities) do
        if entity.valid then
            entity.damage(100, force)
        end
    end
end)

That code is a bit verbose. Particularly creating the area around the player and damaging the entities is very long. It's easy to miss an - or + and make a mistake in the math, especially on long lines.

Improved code

Using the standard library, we can simplify this a great deal. I'm going to use the position and table modules only, and can short the code greatly.

require 'defines'
require 'stdlib/table'
require 'stdlib/area/position'

script.on_event(defines.events.on_player_created, function(event)
    local player = game.players[event.player_index].character
    local force = player.force
    local valid_enemies = table.filter(player.surface.find_entities_filtered(area = Position.expand_to_area(player.position, 25), type = 'unit', force = 'enemy'}), function(e) return e.valid end)
    table.each(valid_enemies, function(entity)
         entity.damage(100, force)
    end)
end)

The second example has a few new require statements at the top, to give access to the extra table methods (table.filter and table.each), and requires the Position class to give access to Position.expand_to_area along with all other position methods.

So, what is the second code doing? Position.expand_to_area creates an area (square) with the player position at the center, and the edges of the area 25 blocks away from each side, just like the previous code did. table.filter takes an array, and is filtering out any that are not true (in this case, are not valid). table.each applies the function to each of the valid enemies, and damages them by 100 HP.

It is easier to understand and read what the code is doing in the second example, and much less error prone. There is no math to get wrong, no verbose iteration.

Improved improved code

However, we aren't done yet. There are a few final improvements that can be done. The function we wrote for table.filter is so common, it is also provided in the stdlib, in Game.VALID_FILTER. We can use that instead of rewriting it ourselves.

Also, we can use the Factorio stdlib event handlers. It allows mods to register multiple handlers for a single event (encouraging smaller, easier-to-read handlers) and has built in error handling. Errors in events will trigger a message in game instead of a hard-exit-to-menu.

Here's the final code!

require 'defines'
require 'stdlib/table'
require 'stdlib/game'
require 'stdlib/event/event'
require 'stdlib/area/position'

Event.register(defines.events.on_player_created, function(event)
    local player = game.players[event.player_index].character
    local force = player.force
    local valid_enemies = table.filter(player.surface.find_entities_filtered(area = Position.expand_to_area(player.position, 25), type = 'unit', force = 'enemy'}), Game.VALID_FILTER)
    table.each(valid_enemies, function(entity)
         entity.damage(100, force)
    end)
end)
Clone this wiki locally