Page 1 of 1

Detecting broken MP mods in 0.15.10

Posted: Sun May 07, 2017 12:43 am
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

Re: Detecting broken MP mods in 0.15.10

Posted: Sun May 07, 2017 2:02 am
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

Re: Detecting broken MP mods in 0.15.10

Posted: Sun May 07, 2017 7:07 am
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

Re: Detecting broken MP mods in 0.15.10

Posted: Sun May 07, 2017 9:30 pm
by Nexela
Updated my previous examples to include checking for existing event. Rseding feel free to yell at me if I got it wrong :)

Re: Detecting broken MP mods in 0.15.10

Posted: Thu May 11, 2017 4:34 pm
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?

Re: Detecting broken MP mods in 0.15.10

Posted: Thu May 11, 2017 4:42 pm
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.

Re: Detecting broken MP mods in 0.15.10

Posted: Thu May 11, 2017 7:20 pm
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.

Re: Detecting broken MP mods in 0.15.10

Posted: Fri May 12, 2017 2:50 am
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?

Re: Detecting broken MP mods in 0.15.10

Posted: Fri May 12, 2017 5:12 am
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.

Re: Detecting broken MP mods in 0.15.10

Posted: Fri May 12, 2017 6:57 am
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.

Re: Detecting broken MP mods in 0.15.10

Posted: Fri May 12, 2017 9:13 am
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.

Re: Detecting broken MP mods in 0.15.10

Posted: Fri May 12, 2017 5:45 pm
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.