Detecting broken MP mods in 0.15.10

Place to post guides, observations, things related to modding that are not mods themselves.
Post Reply
Rseding91
Factorio Staff
Factorio Staff
Posts: 13248
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Detecting broken MP mods in 0.15.10

Post by Rseding91 »

I've added a new check in 0.15.10 that will detect when mods are doing event registration incorrectly as a result of saving/loading the game (which happens when anyone joins a multiplayer game). If your mod currently does anything like this then you're doing it wrong and your mod won't work in 0.15.10 (and already doesn't work now as it would cause desyncs when used in multiplayer):

Code: Select all

script.on_event(defines.events.on_tick, function(event)
  -- some code here
  script.on_event(defines.events.on_tick, nil)
end)
The on_load event specifically does not have access to "game" because you should never change the mod state or game state as a result of simply loading a save file and so I've seen people do the above to work around it. The above is incorrect code and will almost always result in a desync for anyone using that mod in multiplayer (see: viewtopic.php?f=7&t=46701)

The on_init event is the correct way to initialize variables your mod will be using for its lifetime. It's called when a new save file is created with your mod or when a save file is loaded with your mod when it didn't previously contain your mod.

The on_configuration_changed event is the correct way to detect mod additions, removals, or changes. It will be called any time any mods are changed (including the base game mods). Read: this includes any change to the mod protoype files - if the result crc from processing the mod is different it will be detected and the event will be fired. Even adding a single newline to any prototype file will trigger it.

The on_load event is ONLY meant for the 3 things listed on the docs page. If you do anything else in that event then you're almost certainly making an error.


If you have any questions please let me know and I'll do my best to answer them. Additionally the data lifecycle page may be helpful to understand in what order the game does startup and new game/load game processing
If you want to get ahold of me I'm almost always on Discord.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by Nexela »

on_load Examples. The Three things you can do

Code: Select all

local miners = {miners = {}}

script.on_load(
    function()
        --Make a local reference to global (or something in global)
        miners.miners = global.miners
        --Setup Metatables on miners (this example makes miners[unit_number] return miners.miners[unit_number]
        for _, miner in pairs(miners.miners) do
            setmetatable(miner, {__index = miners})
        end
        --Setup conditional event handlers, This example checks if the remote interface is available and if so register the event provided by the remote return
        if remote.interfaces["picker"] and remote.interfaces["picker"]["dolly_moved_entity_id"] then
            script.on_event(remote.call("picker", "dolly_moved_entity_id"), entity_moved)
        end
        --setup conditional event handlers, this example checks to see if a conditional event should be activated
        if global.actions then
            script.on_event("some_other_event", do_actions)
        end
    end
)

local function do_actions()
    if global.actions then
        --Do stuff here
    end
    global.actions = nil
    script.on_event("some_other_event", nil)
end

script.on_event("some_event",
function()
    global.actions = {}
    script.on_event("some_other_event", do_actions)
end
)

script.on_init(
    function()
        global.miners = {}
        if remote.interfaces["picker"] and remote.interfaces["picker"]["dolly_moved_entity_id"] then
            script.on_event(remote.call("picker", "dolly_moved_entity_id"), entity_moved)
        end
    end
)
[Edit] Updated to include more conditional event examples
Last edited by Nexela on Sun May 07, 2017 9:31 pm, edited 3 times in total.

Rseding91
Factorio Staff
Factorio Staff
Posts: 13248
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by Rseding91 »

Nexela wrote:on_load Examples. The Three things you can do/
One thing I didn't mention: if you're conditionally registering event handlers in on_load they should have been registered when the map was previously saved as well. So if you have some event you run only under some condition it would have been running when the save was saved and then in on_load you check the same condition that made it run before and if it's true you register the event to make it run now.

An example is the on_tick event handler I use in my Explosive Termites mod: https://github.com/Rseding91/Explosive- ... ontrol.lua
If you want to get ahold of me I'm almost always on Discord.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by Nexela »

Updated my previous examples to include checking for existing event. Rseding feel free to yell at me if I got it wrong :)

User avatar
Mooncat
Smart Inserter
Smart Inserter
Posts: 1194
Joined: Wed May 18, 2016 4:55 pm
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by Mooncat »

May I know why deregistering on_tick inside the on_tick handler is wrong?
I think I have done something like this in the handler:

Code: Select all

for index, entity in ipairs(global.entities) do
    if not entity.valid then
        table.remove(global.entities, index)
    end
end
if #global.entities <= 0 then
    script.on_event(defines.events.on_tick, nil)
end
Edit: oh, is it because of the anonymous function?

User avatar
prg
Filter Inserter
Filter Inserter
Posts: 947
Joined: Mon Jan 19, 2015 12:39 am
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by prg »

I think it's only bad if you do it unconditionally. This would break multiplayer as the on_tick handler would run once for the player who just joined but not anyone else.
Automatic Belt (and pipe) Planner—Automate yet another aspect of constructing your factory!

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by Nexela »

As prg said You can unregister it in on_tick you just need to let on_load know if it is registered or not. This is accomplished by using a global variable

on_load(if #global.entities > 0 then register on_tick) or something like that.

User avatar
Mooncat
Smart Inserter
Smart Inserter
Posts: 1194
Joined: Wed May 18, 2016 4:55 pm
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by Mooncat »

Oh, so the problem is not how it is deregistered, but when it is registered?

Say, if I register on_tick only inside on_built_entity, the game will desync when another player join the game because on_tick is not registered for him?

User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2919
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by Optera »

Rseding91 wrote: One thing I didn't mention: if you're conditionally registering event handlers in on_load they should have been registered when the map was previously saved as well. So if you have some event you run only under some condition it would have been running when the save was saved and then in on_load you check the same condition that made it run before and if it's true you register the event to make it run now.

An example is the on_tick event handler I use in my Explosive Termites mod: https://github.com/Rseding91/Explosive- ... ontrol.lua
I thought my English was fine but i really don't understand this.
First you basically explain how registering on_tick in on_load is unnecessary?
Yet the example shows you doing just that.

User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3700
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by DaveMcW »

It is never necessary to register on_tick in on_load. In fact you will probably get it wrong.

Just do this:

Code: Select all

script.on_event(defines.events.on_tick, function()
    if global.tick_enabled then
        do_stuff()
    end
end)
If for some reason you really, really want to register and unregister events during gameplay, Explosive-Termites is an example of how to do it correctly in Factorio 0.15.10.

User avatar
Optera
Smart Inserter
Smart Inserter
Posts: 2919
Joined: Sat Jun 11, 2016 6:41 am
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by Optera »

DaveMcW wrote:It is never necessary to register on_tick in on_load. In fact you will probably get it wrong.

Just do this:

Code: Select all

script.on_event(defines.events.on_tick, function()
    if global.tick_enabled then
        do_stuff()
    end
end)
If for some reason you really, really want to register and unregister events during gameplay, Explosive-Termites is an example of how to do it correctly in Factorio 0.15.10.
That's terrible.
You constantly drain resources in on_tick even if you have nothing to do.

Optimized code registers to an event only when there's something to do e.g. an entity has been created that needs on_tick to update something.
ofc it also deregisters when there no longer is something to do.

User avatar
prg
Filter Inserter
Filter Inserter
Posts: 947
Joined: Mon Jan 19, 2015 12:39 am
Contact:

Re: Detecting broken MP mods in 0.15.10

Post by prg »

Mooncat wrote:Oh, so the problem is not how it is deregistered, but when it is registered?

Say, if I register on_tick only inside on_built_entity, the game will desync when another player join the game because on_tick is not registered for him?
That's why you also check in on_load whether there's stuff to do for on_tick, then conditionally register an event handler for it.
Automatic Belt (and pipe) Planner—Automate yet another aspect of constructing your factory!

Post Reply

Return to “Modding discussion”