Mod event handlers

Place to post guides, observations, things related to modding that are not mods themselves.
Post Reply
Bilka
Factorio Staff
Factorio Staff
Posts: 3128
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Mod event handlers

Post by Bilka »

Rseding91: Topic split out from: viewtopic.php?f=28&t=62708
eradicator wrote: ↑
Mon Oct 01, 2018 8:27 pm
If the entity becomes invalid no further events for that entity will be raised. So all you need to worry is what you do in your own event after destroying the entity.
OP is using a library that doesn't handle this correctly (ahem stdlib ahem), so the event subscribers still get called.
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.

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

Re: Add the ability to influence item loot in lua

Post by Nexela »

Bilka wrote: ↑
Mon Oct 01, 2018 8:31 pm
eradicator wrote: ↑
Mon Oct 01, 2018 8:27 pm
If the entity becomes invalid no further events for that entity will be raised. So all you need to worry is what you do in your own event after destroying the entity.
OP is using a library that doesn't handle this correctly (ahem stdlib ahem), so the event subscribers still get called.
Ahem lies, stdlib handles it correctly

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

Re: Add the ability to influence item loot in lua

Post by Rseding91 »

Nexela wrote: ↑
Tue Oct 02, 2018 12:06 am
Bilka wrote: ↑
Mon Oct 01, 2018 8:31 pm
eradicator wrote: ↑
Mon Oct 01, 2018 8:27 pm
If the entity becomes invalid no further events for that entity will be raised. So all you need to worry is what you do in your own event after destroying the entity.
OP is using a library that doesn't handle this correctly (ahem stdlib ahem), so the event subscribers still get called.
Ahem lies, stdlib handles it correctly
No it doesn't. There are some events which have nested tables with LuaObjects which can be invalid and that isn't handled.

The entire event system in stdlib should be deleted and people should use the standard script.on_event. It adds unnecessary complexity, runtime overhead, and will generate more errors than not using it.

If I could, I would make the game fail to load if it detected you where using it.
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: Add the ability to influence item loot in lua

Post by Nexela »

Yes it adds more overhead, but so do most libraries. is it perfect no, but it does a nice job and catches most of the issues.

User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2903
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Add the ability to influence item loot in lua

Post by darkfrei »

Rseding91 wrote: ↑
Tue Oct 02, 2018 12:43 am
The entire event system in stdlib should be deleted and people should use the standard script.on_event. It adds unnecessary complexity, runtime overhead, and will generate more errors than not using it.
Can be "Multi-Event" been added to the vanilla api somehow?
If I have two events on_tick, why they are cannot be processed together? One internal Jack can be better than new solution n every single mod, except stdlib.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Add the ability to influence item loot in lua

Post by eradicator »

Rseding91 wrote: ↑
Tue Oct 02, 2018 12:43 am
The entire event system in stdlib should be deleted and people should use the standard script.on_event.
The main "problem" is that the standard system only allows one event handler per event. Being able to register more than one handler per event is a lot more comfortable for complex mods. Sure it could be done "manually", but trying to automate away something like handling multiple dynamic tick handlers seems only natural. With a simple addition like script.on_named_event(defines.events.on_tick, 'my_first_named_handler', function()end) you would catch many of the people who now use stdlib now.
Rseding91 wrote: ↑
Tue Oct 02, 2018 12:43 am
No it doesn't. There are some events which have nested tables with LuaObjects which can be invalid and that isn't handled.
Can you give examples? I'd be interested on a technical level. I'd really like to know how the base game handles this, is there a special test case for each event type?
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

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

Re: Add the ability to influence item loot in lua

Post by Rseding91 »

darkfrei wrote: ↑
Tue Oct 02, 2018 5:26 am
Rseding91 wrote: ↑
Tue Oct 02, 2018 12:43 am
The entire event system in stdlib should be deleted and people should use the standard script.on_event. It adds unnecessary complexity, runtime overhead, and will generate more errors than not using it.
Can be "Multi-Event" been added to the vanilla api somehow?
If I have two events on_tick, why they are cannot be processed together? One internal Jack can be better than new solution n every single mod, except stdlib.
You can already do that. You just call your individual functions from inside your event registration. It's as simple as it can get:

Code: Select all

script.on_event(..., function(event)
call_handler_1(event);
call_handler_2(event);
...
)
If you want to get ahold of me I'm almost always on Discord.

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

Re: Add the ability to influence item loot in lua

Post by Rseding91 »

eradicator wrote: ↑
Tue Oct 02, 2018 9:26 am
Rseding91 wrote: ↑
Tue Oct 02, 2018 12:43 am
No it doesn't. There are some events which have nested tables with LuaObjects which can be invalid and that isn't handled.
... is there a special test case for each event type?
Yes. All of the error checking is already written on the C++ event side and is known to work correctly. Re-writing all of that on Lua side is slow, a waste of time, and error prone.
If you want to get ahold of me I'm almost always on Discord.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Add the ability to influence item loot in lua

Post by eradicator »

Rseding91 wrote: ↑
Tue Oct 02, 2018 3:36 pm
You can already do that. You just call your individual functions from inside your event registration. It's as simple as it can get:

Code: Select all

script.on_event(..., function(event)
call_handler_1(event);
call_handler_2(event);
...
)
But what is the simple solution if you have 10 different handlers, of which any or all of may be en-/disabled dynamically at runtime? If every tick handler has to check it's own run condition all the time you're back at "overhead and bugprone" again. (Conditions can be anything from mod-settings, to other-mod-exists, to entity-exists, equipment equipped, etcpp.)
Rseding91 wrote: ↑
Tue Oct 02, 2018 3:37 pm
Yes. All of the error checking is already written on the C++ event side and is known to work correctly. Re-writing all of that on Lua side is slow, a waste of time, and error prone.
I was more interested if the error-checking is a generic solution, or if there are special cases for every possible event type.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2903
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Add the ability to influence item loot in lua

Post by darkfrei »

Rseding91 wrote: ↑
Tue Oct 02, 2018 3:36 pm
darkfrei wrote: ↑
Tue Oct 02, 2018 5:26 am
Can be "Multi-Event" been added to the vanilla api somehow?
If I have two events on_tick, why they are cannot be processed together? One internal Jack can be better than new solution n every single mod, except stdlib.
You can already do that. You just call your individual functions from inside your event registration. It's as simple as it can get:

Code: Select all

script.on_event(..., function(event)
call_handler_1(event);
call_handler_2(event);
...
)
And if I have 10-15 difficult control.lua-only mods and want to set them into one?
I would be good ig I can just write:

Code: Select all

require ("another-big-part-of-control-mod-with-own-events")
and it works as is. Why the function script.on_event cannot just add the function, instead of rewrite it?

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

Re: Add the ability to influence item loot in lua

Post by Rseding91 »

darkfrei wrote: ↑
Tue Oct 02, 2018 4:55 pm
And if I have 10-15 difficult control.lua-only mods and want to set them into one?
If you're trying to do that: don't.
darkfrei wrote: ↑
Tue Oct 02, 2018 4:55 pm
and it works as is. Why the function script.on_event cannot just add the function, instead of rewrite it?
Because that's the wrong way to do it, would produce slower mods, and isn't needed 99% of the time.
If you want to get ahold of me I'm almost always on Discord.

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

Re: Add the ability to influence item loot in lua

Post by Rseding91 »

eradicator wrote: ↑
Tue Oct 02, 2018 4:07 pm
I was more interested if the error-checking is a generic solution, or if there are special cases for every possible event type.
It's special cases for every possible event type because near every event uses different data types.
If you want to get ahold of me I'm almost always on Discord.

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

Re: Add the ability to influence item loot in lua

Post by Rseding91 »

eradicator wrote: ↑
Tue Oct 02, 2018 4:07 pm
But what is the simple solution if you have 10 different handlers, of which any or all of may be en-/disabled dynamically at runtime? If every tick handler has to check it's own run condition all the time you're back at "overhead and bugprone" again. (Conditions can be anything from mod-settings, to other-mod-exists, to entity-exists, equipment equipped, etcpp.)
If that's your code then you should split that out into 10 different mods since that's how you're effectively running it anyway. Otherwise you end up with a bloated feature-creep mod like "Picker Extended" which does stuff like change the margins of GUIs - completely outside of what the mod name says it's doing.
If you want to get ahold of me I'm almost always on Discord.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Add the ability to influence item loot in lua

Post by eradicator »

Rseding91 wrote: ↑
Tue Oct 02, 2018 3:36 pm
You can already do that. You just call your individual functions from inside your event registration. It's as simple as it can get:

Code: Select all

script.on_event(..., function(event)
call_handler_1(event);
call_handler_2(event);
...
)
Also upon further inspection this suffers from the exact same problem: If call_handler_1() invalidates any of the event objects the validity checking has to be re-implemented on the lua side again.
Rseding91 wrote: ↑
Tue Oct 02, 2018 5:19 pm
eradicator wrote: ↑
Tue Oct 02, 2018 4:07 pm
But what is the simple solution if you have 10 different handlers, of which any or all of may be en-/disabled dynamically at runtime? If every tick handler has to check it's own run condition all the time you're back at "overhead and bugprone" again. (Conditions can be anything from mod-settings, to other-mod-exists, to entity-exists, equipment equipped, etcpp.)
If that's your code then you should split that out into 10 different mods since that's how you're effectively running it anyway. Otherwise you end up with a bloated feature-creep mod like "Picker Extended" which does stuff like change the margins of GUIs - completely outside of what the mod name says it's doing.
Well, most of it is buildings and equipment that require special scripting. But as they're part of the "economy" it doesn't make sense to use them seperately. So i'd have to make them all required dependencies. This is neither a nice experience for the user (no modpack support) nor for me (no support for automatic mod publishing).
Rseding91 wrote: ↑
Tue Oct 02, 2018 5:17 pm
eradicator wrote: ↑
Tue Oct 02, 2018 4:07 pm
I was more interested if the error-checking is a generic solution, or if there are special cases for every possible event type.
It's special cases for every possible event type because near every event uses different data types.
Would you be willing to share that bit of code? Or at least one of the problematic cases? Because at a glance i can't see any events that have nested results.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2903
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Add the ability to influence item loot in lua

Post by darkfrei »

Rseding91 wrote: ↑
Tue Oct 02, 2018 5:16 pm
darkfrei wrote: ↑
Tue Oct 02, 2018 4:55 pm
And if I have 10-15 difficult control.lua-only mods and want to set them into one?
If you're trying to do that: don't.
darkfrei wrote: ↑
Tue Oct 02, 2018 4:55 pm
and it works as is. Why the function script.on_event cannot just add the function, instead of rewrite it?
Because that's the wrong way to do it, would produce slower mods, and isn't needed 99% of the time.
There is a lot of map scenarios, much of them are very complicated, for example viewtopic.php?t=36987 or a lot another you can find in server list. They are very often very big and need events for every part of code.

User avatar
Klonan
Factorio Staff
Factorio Staff
Posts: 5150
Joined: Sun Jan 11, 2015 2:09 pm
Contact:

Re: Mod event handlers

Post by Klonan »

So, I have been writing Factorio scripts for nearly 3 years now, and I have decided upon late on the following solution for myself, I tried a few methods over the years:
First method: The standard Factorio way.

Code: Select all

script.on_event(defines.events.on_whatever, function(event)
	if event.this and event.that then go on...
	if not this or that then other_function() end
end)
Basically, I just dump all the script onto the event registration. It works in the simple cases, but not for more complex things.



Second method: Some conditional checking to point towards functions

Code: Select all

script.on_event(defines.event.on_entity_created, function(event)
	if event.entity.name == "big-turret" then
		big_turret_built(event.entity)
	elseif event.entity.name == "small-turret" then
		small_turret_build(event.entity)
	end	
end)
This method is nice in some ways, I can check for entity.valid in the event registration, and skip checking in the function call, and can keep things clean. But it can get messy and bloated past the more simple cases.


Third method: Throw the event to all the functions, and they do their own checking

Code: Select all

script.on_event(defines.events.on_entity_created, function(event)
	big_turret_built(event)
	small_turret_built(event)
	other_something_built(event)
end)
This is cleanly, I can easily throw another function on the bottom of the stack, and not worry about anything else, or fitting it in with the rest of the script registration. The downside is, you have to check event entity validity in each function, over and over.
It can be inefficient, but I think you should always check anyway.

However when working with more scripts/modules, I ran into a problem, that you will have to register the events, but individually for each event.
For instance PvP will have a `on_gui_click` handler, and Silo-script will also have an `on_gui_click` handler, so I would need to 'know' that each of these scripts needs me to register the handler in the control script:

Code: Select all

script.on_event(defines.events.on_gui_click, function(event)
	silo_script.on_gui_click(event)
	pvp.on_gui_click(event)
end)
This is really laborious to do. Adding a new script/module means I have to open it and see which event it wants to be listened to, if it has a `on_entity_created`, I need to make sure I add that in my control.lua...



Current solution I am using: Each library handling all events

Code: Select all

script.on_event(defines.events, function(event)
	for name, library in pairs (libraries) do
		library.on_event(event)
	end
end)
Then each library checks if it has a function for that event:

Code: Select all

events =
{
	[defines.events.on_tick] = on_tick,
	[defines.events.on_gui_click] = on_gui_click
}

lib.on_event = function(event)
	action = events[event.name]
	if action then
		action(event)	
	end
end
This could be further optimized down the line, but for now it is really working out for me. The downside is the same as before, I need to check all event data validity before accessing it.
The benefit though of completely not caring is really great. I have a WIP mod now with 10+ scripts/modules, each with separate global data and script event handlers, and I have not run in to any problem for now.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Mod event handlers

Post by eradicator »

Klonan wrote: ↑
Tue Oct 02, 2018 7:35 pm
Current solution I am using: Each library handling all events

Code: Select all

script.on_event(defines.events, function(event)
	for name, library in pairs (libraries) do
		library.on_event(event)
	end
end)
Then each library checks if it has a function for that event:

Code: Select all

events =
{
	[defines.events.on_tick] = on_tick,
	[defines.events.on_gui_click] = on_gui_click
}

lib.on_event = function(event)
	action = events[event.name]
	if action then
		action(event)	
	end
end
This could be further optimized down the line, but for now it is really working out for me. The downside is the same as before, I need to check all event data validity before accessing it.
The benefit though of completely not caring is really great. I have a WIP mod now with 10+ scripts/modules, each with separate global data and script event handlers, and I have not run in to any problem for now.
That's similar to what i do for my system (and i'm guessing what stdlib does too). But it's the same thing again. You're doing all the validity checking on the lua side because you can't utilize the built-in checking. Also this doesn't work for custom_input, because custom input uses event.input_name instead of event.name, and there's no method to get the numeric event.name for a given custom_input in advance. So i have to do [event.input_name or event.name].

Currently i'm using this generic loop to try and guess if an event is still valid after running a handler, before handing it to the next handler:

Code: Select all

local function on_event(event)

  --[snip]

  local userdatas = {} --try to find everything that could possibly become invalid
  for _,value in pairs(event) do
    if ((type(value) == 'table')
      and value.__self
      and (type(value.__self) == 'userdata'))
      or
      (type(value) == 'userdata') --ducktape
      then
      userdatas[#userdatas+1] = value
      if type(value.valid) ~= 'boolean' then
        error('Userdata object without ".valid" method detected!')
        end
      end
    end

  for i=1,#handlers do
    handlers[i](event)
    for i=1,#userdatas do if userdatas[i].valid ~= true then
      return --the event has invalidated some LuaObjects! Stop processing!
      end end
    end

  end

PS: It's nice to know that i'm not the only one struggling for a solution here :).
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: ζ—₯本θͺž, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
Klonan
Factorio Staff
Factorio Staff
Posts: 5150
Joined: Sun Jan 11, 2015 2:09 pm
Contact:

Re: Mod event handlers

Post by Klonan »

eradicator wrote: ↑
Tue Oct 02, 2018 8:17 pm
Also this doesn't work for custom_input, because custom input uses event.input_name instead of event.name, and there's no method to get the numeric event.name for a given custom_input in advance. So i have to do [event.input_name or event.name].
I use a hack to solve this:

Code: Select all

local hotkeys = names.hotkeys
for k, name in pairs (hotkeys) do
  local event_name = script.generate_event_name()
  defines.events[name] = event_name
  script.on_event(name, function(event) script.raise_event(event_name, event) end)
end

local events =
{
    [defines.events[names.hotkeys.open_gui]] = open_gui
}

Post Reply

Return to β€œModding discussion”