Doctor_Willis wrote: Thu Apr 25, 2024 8:00 am
Thank you for the input!
I'm still new to modding and still finding my feet.
I wasn't aware of the multiplayer issue. Would you mind going over it with a more concrete example/how to? I don't fully understand it.
An example from
my latest mod:
Code: Select all
-- Map event names to event handlers
local event_handlers = {}
-- Map event names to event filters
local event_filters = {}
LCOE.optional_events = {
chunks = {
on_chunk_charted = LCOE_surfaces.on_chunk_charted,
},
chart_tags = {
on_chart_tag_modified = LCOE_charttags.on_chart_tag_changed,
on_chart_tag_removed = LCOE_charttags.on_chart_tag_changed,
},
portals = {
on_entity_damaged = LCOE_surfaces.on_entity_damaged,
on_entity_destroyed = LCOE_portals.restore_exit_portal,
on_gui_opened = LCOE_player.player_used_portal
},
}
-- Add filters for optional events
event_filters.on_entity_damaged = {
{filter = "damage-type", type = "explosion"},
}
event_handlers.on_player_joined_game = LCOE_player.on_player_joined_game
event_handlers.on_pre_player_left_game = LCOE_player.on_pre_player_left_game
event_handlers.on_pre_player_removed = LCOE_player.remove_player
event_handlers.on_player_died = LCOE_player.on_player_died
event_handlers.on_post_entity_died = LCOE_player.on_post_entity_died
event_filters.on_post_entity_died = {
{filter = "type", type = "character"}
}
event_handlers.on_character_corpse_expired = LCOE_corpse.remove_corpse_data
event_handlers.on_pre_player_mined_item = LCOE_corpse.remove_corpse_data
event_filters.on_pre_player_mined_item = {
{filter = "type", type = "character-corpse"}
}
…
------------------------------------------------------------------------------------
-- REGISTER EVENT HANDLERS! --
------------------------------------------------------------------------------------
LCOE_events.attach_events = function(event_list)
LCOE.assert(event_list, {"table", "nil"},
"table of event names and handlers or nil")
local id, filters
for event_name, event_handler in pairs(event_list or event_handlers) do
id = defines.events[event_name]
-- Register event
script.on_event(id, function(event)
event_handler(event)
end)
-- Set event filters?
filters = event_filters[event_name]
if filters and next(filters) then
script.set_event_filter(id, filters)
end
end
end
------------------------------------------------------------------------------------
-- REGISTER OPTIONAL EVENT HANDLERS! --
------------------------------------------------------------------------------------
LCOE_events.register_optional_events = function(group_name, event_name)
LCOE.assert(group_name, {"string", "nil"}, "name of table in LCOE.optional_events or nil")
LCOE.assert(event_name, {"string", "nil"}, "event_name or nil")
local event_list = LCOE.optional_events[group_name]
-- Attach single event
if event_name then
if event_list and event_list[event_name] then
LCOE_events.attach_events({[event_name] = event_list[event_name]})
global.optional_events[event_name] = group_name
end
-- Attach group of events
elseif event_list then
LCOE_events.register_optional_events(group)
for e_name, e_handler in pairs(event_list) do
global.optional_events[e_name] = group_name
end
-- Re-attach all events that are currently stored in global.optional_events
else
for e_name, group in pairs(global.optional_events) do
LCOE_events.register_optional_events(group, e_name)
end
end
end
------------------------------------------------------------------------------------
-- UNREGISTER EVENT HANDLERS! --
------------------------------------------------------------------------------------
LCOE_events.detach_events = function(event_list, keep_events)
LCOE.assert(event_list, {"table", "nil"},
"table of event names/handlers or nil")
LCOE.assert(keep_events, {"table", "nil"},
"table of event names to keep attached, or nil")
local keep_names = {}
for n, name in pairs(keep_events or {}) do
keep_names[name] = true
end
for event_name, event_handler in pairs(event_list or event_handlers) do
if keep_names[event_name] then
-- Ignore event
elseif script.get_event_handler(event_names[event_name]) then
-- Unregister event
script.on_event(event_names[event_name], nil)
-- Remove optional event from global.optional_events?
if event_list then
global.optional_events[event_name] = nil
end
end
end
end
-- Mod is running for the first time in this game, or
-- mods have been added/updated/removed
local function init()
…
LCOE_events.attach_events()
LCOE_events.register_optional_events()
…
end
script.on_init(init)
script.on_configuration_changed(init)
-- Saved game is loaded, we have read access to table "global", but
-- can't write to it!
script.on_load(function()
…
-- Attach events that we always need
LCOE_events.attach_events()
-- Attach all optional events that are stored in global.optional_events
LCOE_events.register_optional_events()
…
end)
-- Example: When a chart_tag has been added or removed by the mod,
-- this function is called to register/unregister handlers for the optional
-- events defined in LCOE.optional_events["chart_tags"].
LCOE_charttags.check_optional_events = function()
local need_event = false
-- We need events if at least one chart_tag has been stored for at least
-- one force
for f_name, f_data in pairs(global.force_data) do
for t_name, t_data in pairs(f_data.chart_tags or {}) do
if t_data.tag and t_data.tag.valid then
need_event = true
break
end
end
end
if need_event then
LCOE_events.register_optional_events("chart_tags")
else
LCOE_events.detach_events(LCOE.optional_events["chart_tags"])
end
end
This is perhaps a bit more complicated than what you may need because I've made the code flexible enough to handle single events as well as a group of events. The general idea is: If you turn on the handler for an optional event, store it in the global table, if you turn it off, remove the event from the global table. When a game is loaded (this will be triggered when a player is joining a running multiplayer game), register the basic events that are always needed, as well as the optional events that are stored in the global table.
And thanks for the heads up about the force. I'll make sure to change that to "player".
As for the other issues, they won't be a problem. This mod is never going public. I'm making it for a local tech-school, and using a very specific scenario where there will only be one train with the rails and train stops in specific coordinates.
OK, you'll probably get along with fixed values for force and surface in this case! I'm still doubtful about the position, though. Even if the position of the train stop will never change, your train will get longer, so after you've added a wagon, that position will be blocked. Shouldn't you try to determine the position for the new wagon based on the actual length of the train?
And yes, there are additional research levels, and I'm currently in the process of factoring out the repeated code into functions, so it will be able to adapt to new research.
Yes, I guess one function that is called with different arguments (e.g. the tech-level) would be enough …