events raised in on_init get skipped by mods already present on the save

Place to report issues and suggest improvements to the API documentation.
User avatar
Quezler
Fast Inserter
Fast Inserter
Posts: 207
Joined: Fri Mar 25, 2016 6:37 pm
Contact:

events raised in on_init get skipped by mods already present on the save

Post by Quezler »

I'll post it in documentation improvement requests since it seems just ever so slightly more suitable than calling it a bug due to the lifecycle diagram.

So lets start with probably the most common usecase, tracking data for any/all surfaces within your own mod, this is typically done by going over all surfaces (and possibly iterating entities on them), and then listening to the on_surface_created in order to detect new surfaces, but surfaces can slip through these cracks.

(note that it doesn't seem unique for just the surface creation events, raising build events from on_init also get missed by mod A)

"mod A"

Code: Select all

script.on_init(function()
  storage.surfacedata = {}

  for _, surface in pairs(game.surfaces) do
    storage.surfacedata[surface.index] = {some = "thing"}
  end
end)

script.on_event(defines.events.on_surface_created, function(event)
  log(string.format("surface '%s' created.", game.get_surface(event.surface_index).name))
  storage.surfacedata[event.surface_index] = {foo = "bar"}
end)

script.on_event(defines.events.script_raised_built, function(event)
  log(serpent.line(event.entity.position))
end)

commands.add_command("mod-a", nil, function(command)
  log(serpent.block(storage.surfacedata))
end)
"mod B"

Code: Select all

script.on_init(function()
  game.create_surface("on_init")
  game.surfaces["nauvis"].create_entity{
    name = "wooden-chest",
    force = "neutral",
    position = {0, 0},
    raise_built = true,
  }
end)

script.on_configuration_changed(function()
  game.create_surface("on_configuration_changed")
    game.surfaces["nauvis"].create_entity{
    name = "wooden-chest",
    force = "neutral",
    position = {1, 0},
    raise_built = true,
  }
end)

-- non save-load stable example
script.on_event(defines.events.on_tick, function(event)
  game.create_surface("on_tick")
    game.surfaces["nauvis"].create_entity{
    name = "wooden-chest",
    force = "neutral",
    position = {2, 0},
    raise_built = true,
  }
  script.on_event(defines.events.on_tick, nil)
end)
When you add these both to a world at the same time you'll see this in the log:
- Script @__mod-a__/control.lua:10: surface 'on_init' created.
- Script @__mod-a__/control.lua:15: {x = 0.5, y = 0.5}
- Script @__mod-a__/control.lua:10: surface 'on_tick' created.
- Script @__mod-a__/control.lua:15: {x = 2.5, y = 0.5}

But when you create the world with A installed, save, enable B, load you'll see this:
- Script @__mod-a__/control.lua:10: surface 'on_configuration_changed' created.
- Script @__mod-a__/control.lua:15: {x = 1.5, y = 0.5}
- Script @__mod-a__/control.lua:10: surface 'on_tick' created.
- Script @__mod-a__/control.lua:15: {x = 2.5, y = 0.5}

Or in other words, even though A is already initialized (and thus "ready" to receive events) the surface creation event triggered by the on_init of mod B doesn't get picked up by mod A at all. (surface "on_init" did get created, and surfaces created in on_configuration_changed do seem to get picked up)

So yea is this a bug, or something worth clearly documenting that events fired from on_init do not trigger in existing mods, so everyone in their "A" should recheck for new entities just like they would in their on_init?

I often get around it by writing code like this, the first time i ran into this issue long ago it felt really weird, it still does, but at least this does work:

Code: Select all

local function refresh_surfacedata()
  -- deleted old
  for surface_index, surfacedata in pairs(storage.surfacedata) do
    if surfacedata.surface.valid == false then
      storage.surfacedata[surface_index] = nil
    end
  end

  -- created new
  for _, surface in pairs(game.surfaces) do
    storage.surfacedata[surface.index] = storage.surfacedata[surface.index] or {
      surface = surface,
    }
  end
end

script.on_init(function()
  storage.surfacedata = {}
  refresh_surfacedata()
end)

script.on_configuration_changed(function()
  refresh_surfacedata()
end)

script.on_event(defines.events.on_surface_created, refresh_surfacedata)
script.on_event(defines.events.on_surface_deleted, refresh_surfacedata)
Attachments
mod-b_0.0.1.zip
(1.4 KiB) Downloaded 30 times
mod-a_0.0.1.zip
(1.58 KiB) Downloaded 29 times
User avatar
boskid
Factorio Staff
Factorio Staff
Posts: 3942
Joined: Thu Dec 14, 2017 6:56 pm
Contact:

Re: events raised in on_init get skipped by mods already present on the save

Post by boskid »

on_load:
Note that no other events will be raised for a mod until it has finished this step.
User avatar
Quezler
Fast Inserter
Fast Inserter
Posts: 207
Joined: Fri Mar 25, 2016 6:37 pm
Contact:

Re: events raised in on_init get skipped by mods already present on the save

Post by Quezler »

Wow that's quite out of sight near the bottom and in a normal font, not used to read the on_load documentation since i know what the event is for, it might be worth moving it to a more visible spot, like right below the lifecycle diagram in a noticable color 👀
curiosity
Filter Inserter
Filter Inserter
Posts: 656
Joined: Wed Sep 11, 2019 4:13 pm
Contact:

Re: events raised in on_init get skipped by mods already present on the save

Post by curiosity »

Same for on_init, but you don't seem to have a problem with that.
robot256
Smart Inserter
Smart Inserter
Posts: 1202
Joined: Sun Mar 17, 2019 1:52 am
Contact:

Re: events raised in on_init get skipped by mods already present on the save

Post by robot256 »

This is an interesting topic. Reading the diagram closely I can see how this occurs. It does feel unintuitive that "new" mods are potentially initialized before "old" mods are allowed to load. It's possible that this or similar behavior could explain some of the weirder bug reports I've received. And it will require more spam in in_configuration_changed to prevent.
Post Reply

Return to “Documentation Improvement Requests”