PTree wrote: Mon Oct 02, 2023 12:54 pm
I'm trying to conceptualize the nature of having actually separated Lua states, which I assume Factorio uses, instead of coroutines or environments or whatever.
(I know of those concepts but have not tried them myself)
I assume that Factorio keeps one internal, C/C++ side Lua registry, or at least some sort of manager, that knows all the separate states (to iterate over them) and works with them...
The following is just how I suppose things may work, in the oversimplified way of somebody who's lacking the theoretical background and is not a professional programmer (I've studied humanities, not computer science).
The mystery to me is the data sharing, if there is indeed any.
I guess the game doesn't care about states, but entities, tiles, items etc. Mods don't do really much: Basically, a mod with a control script won't do anything on its own. The file control.lua will be read and will pull in any files loaded with "require()" when a new game is started or when an existing game is loaded. Depending on what the game signals during bootstrapping, the mod will then run the handlers for script.on_init, script.on_load, and/or script.on_configuration_changed. Usually, handlers for all events the mods needs to listen have been registered at this point. Apart from that, the mod will idle and do nothing until one of the events it's listening to is raised. At that point, the event handler will act like a remote function that has been called by the game with the event data as an argument.
In my last post, I've made a mistake, by the way: Apparently, the same event data will be passed on to all mods:
Code: Select all
====================================================================================================
Entered event script for on_entity_damaged
Event data: tick = 475379, entity = car "tank" (714), cause = character "character" (9), force = LuaForce 1 ("player"), damage_type = LuaDamagePrototype "explosion", original_damage_amount = 1160, final_damage_amount = 343.5, final_health = 1656.5
(@__autodrive__/scripts/event_handlers.lua: 1654)
====================================================================================================
294.162 Script @__autodrive__/libs/debugging.lua:161: event.entity: car "tank" (714)
294.162 Script @__autodrive__/libs/debugging.lua:161: event.original_damage_amount: 1160
294.162 Script @__autodrive__/libs/debugging.lua:161: event.final_damage_amount: 343.5
294.162 Script @__autodrive__/libs/debugging.lua:161: event.final_health: 1656.5
294.162 Script @__autodrive__/libs/debugging.lua:161: Healed entity!
294.162 Script @__autodrive__/libs/debugging.lua:161: event.original_damage_amount: 1160
294.162 Script @__autodrive__/libs/debugging.lua:161: event.final_damage_amount: 343.5
294.162 Script @__autodrive__/libs/debugging.lua:161: event.final_health: 1656.5
294.163 Script @__autodrive__/libs/debugging.lua:161:
====================================================================================================
Leaving event script for on_entity_damaged
Event data: tick = 475379, entity = car "tank" (714), cause = character "character" (9), force = LuaForce 1 ("player"), damage_type = LuaDamagePrototype "explosion", original_damage_amount = 1160, final_damage_amount = 343.5, final_health = 1656.5
(@__autodrive__/scripts/event_handlers.lua: 1668)
====================================================================================================
294.163 Script @___debug__/control.lua:36: Entered script for on_entity_damaged!
294.163 Script @___debug__/control.lua:40: event.entity: tank (714)
294.163 Script @___debug__/control.lua:41: event.original_damage_amount: 1160
294.163 Script @___debug__/control.lua:42: event.final_damage_amount: 343.5
294.163 Script @___debug__/control.lua:43: event.final_health: 1656.5
294.163 Script @___debug__/control.lua:44: event.entity.health: 2000
As you can see, both mods get the same event data, but event.entity has been changed (health: 1656.5-->2000). Now suppose the first mod wouldn't heal the damaged entity, but kill it for good:
Code: Select all
1801.866 Script @__autodrive__/libs/debugging.lua:161:
====================================================================================================
Entered event script for on_entity_damaged
Event data: tick = 474961, entity = car "tank" (714), cause = character "character" (9), force = LuaForce 1 ("player"), damage_type = LuaDamagePrototype "explosion", original_damage_amount = 1160, final_damage_amount = 343.5, final_health = 1656.5
(@__autodrive__/scripts/event_handlers.lua: 1654)
====================================================================================================
1801.866 Script @__autodrive__/libs/debugging.lua:161: event.entity: car "tank" (714)
1801.866 Script @__autodrive__/libs/debugging.lua:161: event.original_damage_amount: 1160
1801.866 Script @__autodrive__/libs/debugging.lua:161: event.final_damage_amount: 343.5
1801.866 Script @__autodrive__/libs/debugging.lua:161: event.final_health: 1656.5
1801.866 Script @__autodrive__/libs/debugging.lua:161: Destroyed entity!
1801.866 Script @__autodrive__/libs/debugging.lua:161: event.original_damage_amount: 1160
1801.866 Script @__autodrive__/libs/debugging.lua:161: event.final_damage_amount: 343.5
1801.866 Script @__autodrive__/libs/debugging.lua:161: event.final_health: 1656.5
1801.867 Script @__autodrive__/libs/debugging.lua:161:
====================================================================================================
Leaving event script for on_entity_damaged
Event data: tick = 474961, entity = LuaEntity (invalid), cause = character "character" (9), force = LuaForce 1 ("player"), damage_type = LuaDamagePrototype "explosion", original_damage_amount = 1160, final_damage_amount = 343.5, final_health = 1656.5
(@__autodrive__/scripts/event_handlers.lua: 1670)
====================================================================================================
As event.entity has become invalid, there is no point in raising on_entity_damaged for the mods that haven't seen the event yet, so the log file ends with the output of the first mod.
Without thinking deeply about it, my first impression was that it would just be a lot of duplication of functions, like, let's say the utility functions the game provides, table.deepcopy and whatnot.
I have not done enough research to actually know if such functions are actually available to the separate Lua states, but if they were, then, without any C Lua API shenanigans, every since state would have to have these functions as their own copy, which adds to memory usage.
Absolutely speculating: I imagine the game providing a kind of super-environment which makes libraries like util, gui etc. available to all other mods. Mods can "require()" a library (actually, a table of functions indexed by function name), and I'd really be surprised if that would place a copy of the library or its functions in their library -- I'd rather bet they all will get a pointer to the same function instead. There shouldn't be any issues with two different mods locking the function for each other because Factorio is an event-based game. Thus, whenever something happens to an entity, a GUI element, players, etc., the game will raise the appropriate event in the manner described: Call first mod that listens to the event, wait until it is done, check for stop condition (e.g. "event.entity is no longer valid) and proceed to the next mod if possible …
Not much... hell, probably quite insignificant amounts for even loosely contemporary machines, but I'm still curious how they actually implement this clean, yet highly customizable modding interface.
If you're lucky, perhaps one of the devs may give you a more technical explanation.
