345 lines
15 KiB
Lua
345 lines
15 KiB
Lua
--[[ Copyright (c) 2017 Optera
|
|
* Part of Logistics Train Network
|
|
*
|
|
* See LICENSE.md in the project directory for license information.
|
|
--]]
|
|
|
|
---- INITIALIZATION ----
|
|
|
|
local function initialize(oldVersion, newVersion)
|
|
--log("oldVersion: "..tostring(oldVersion)..", newVersion: "..tostring(newVersion))
|
|
|
|
---- always start with stop updated after a config change, ensure consistent data and filled tables
|
|
global.tick_state = 0 -- index determining on_tick update mode 0: init, 1: stop update, 2: sort requests, 3: parse requests, 4: raise API update events
|
|
global.tick_stop_index = nil
|
|
global.tick_request_index = nil
|
|
global.tick_interval_start = nil -- stores tick of last state 0 for on_dispatcher_updated_event.update_interval
|
|
|
|
---- initialize logger
|
|
global.messageBuffer = {}
|
|
|
|
---- initialize Dispatcher
|
|
global.Dispatcher = global.Dispatcher or {}
|
|
|
|
-- set in UpdateAllTrains
|
|
global.Dispatcher.availableTrains = global.Dispatcher.availableTrains or {}
|
|
global.Dispatcher.availableTrains_total_capacity = global.Dispatcher.availableTrains_total_capacity or 0
|
|
global.Dispatcher.availableTrains_total_fluid_capacity = global.Dispatcher.availableTrains_total_fluid_capacity or 0
|
|
global.Dispatcher.Provided = global.Dispatcher.Provided or {} -- dictionary [type,name] used to quickly find available items
|
|
global.Dispatcher.Provided_by_Stop = global.Dispatcher.Provided_by_Stop or {} -- dictionary [stopID]; used only by interface
|
|
global.Dispatcher.Requests = global.Dispatcher.Requests or {} -- array of requests sorted by priority and age; used to loop over all requests
|
|
global.Dispatcher.Requests_by_Stop = global.Dispatcher.Requests_by_Stop or {} -- dictionary [stopID]; used to keep track of already handled requests
|
|
global.Dispatcher.RequestAge = global.Dispatcher.RequestAge or {}
|
|
global.Dispatcher.Deliveries = global.Dispatcher.Deliveries or {}
|
|
|
|
---- initialize stops
|
|
global.LogisticTrainStops = global.LogisticTrainStops or {}
|
|
|
|
-- table of connections per surface used to decide if providers from another surface are valid sources
|
|
-- { [surface1.index|surface2.index] = { [entity1.unit_number|entity2.unit_number] = { entity1, entity2, network_id } }
|
|
-- entity_key_pairs are automatically removed during delivery processing if at least one of the referenced entities becomes invalid
|
|
global.ConnectedSurfaces = global.ConnectedSurfaces or {}
|
|
|
|
-- clean obsolete global
|
|
global.Dispatcher.Requested = nil
|
|
global.Dispatcher.Orders = nil
|
|
global.Dispatcher.OrderAge = nil
|
|
global.Dispatcher.Storage = nil
|
|
global.useRailTanker = nil
|
|
global.tickCount = nil
|
|
global.stopIdStartIndex = nil
|
|
global.Dispatcher.UpdateInterval = nil
|
|
global.Dispatcher.UpdateStopsPerTick = nil
|
|
global.TrainStopNames = nil
|
|
|
|
-- update to 1.3.0
|
|
if oldVersion and oldVersion < "01.03.00" then
|
|
for stopID, stop in pairs(global.LogisticTrainStops) do
|
|
stop.minDelivery = nil
|
|
stop.ignoreMinDeliverySize = nil
|
|
end
|
|
end
|
|
|
|
-- update to 1.5.0 renamed priority to provider_priority
|
|
if oldVersion and oldVersion < "01.05.00" then
|
|
for stopID, stop in pairs (global.LogisticTrainStops) do
|
|
stop.provider_priority = stop.priority or 0
|
|
stop.priority = nil
|
|
end
|
|
global.Dispatcher.Requests = {}
|
|
global.Dispatcher.RequestAge = {}
|
|
end
|
|
|
|
-- update to 1.6.1 migrate locomotiveID to trainID
|
|
if oldVersion and oldVersion < "01.06.01" then
|
|
local locoID_to_trainID = {} -- id dictionary
|
|
local new_availableTrains = {}
|
|
local new_Deliveries = {}
|
|
for _,surface in pairs(game.surfaces) do
|
|
local trains = surface.get_trains()
|
|
for _, train in pairs(trains) do
|
|
-- build dictionary
|
|
local loco = Get_Main_Locomotive(train)
|
|
if loco then
|
|
locoID_to_trainID[loco.unit_number] = train.id
|
|
end
|
|
end
|
|
end
|
|
-- log("locoID_to_trainID: "..serpent.block(locoID_to_trainID))
|
|
|
|
for locoID, delivery in pairs(global.Dispatcher.Deliveries) do
|
|
local trainID = locoID_to_trainID[locoID]
|
|
if trainID then
|
|
log("Migrating global.Dispatcher.Deliveries from ["..tostring(locoID).."] to ["..tostring(trainID).."]")
|
|
new_Deliveries[trainID] = delivery
|
|
end
|
|
end
|
|
-- log("new_Deliveries: "..serpent.dump(new_Deliveries))
|
|
global.Dispatcher.Deliveries = new_Deliveries
|
|
end
|
|
|
|
-- update to 1.8.0
|
|
if oldVersion and oldVersion < "01.08.00" then
|
|
for stopID, stop in pairs(global.LogisticTrainStops) do
|
|
stop.entity.get_or_create_control_behavior().send_to_train = true
|
|
stop.entity.get_or_create_control_behavior().read_from_train = true
|
|
end
|
|
end
|
|
|
|
-- update to 1.12.3 migrate networkID to network_id
|
|
if oldVersion and oldVersion < "01.12.03" then
|
|
for train_id, delivery in pairs(global.Dispatcher.Deliveries) do
|
|
delivery.network_id = delivery.networkID
|
|
delivery.networkID = nil
|
|
end
|
|
end
|
|
|
|
-- update to 1.13.1 renamed almost all stop properties
|
|
if oldVersion and oldVersion < "01.13.01" and next(global.LogisticTrainStops) then
|
|
for stopID, stop in pairs(global.LogisticTrainStops) do
|
|
stop.lamp_control = stop.lamp_control or stop.lampControl
|
|
stop.lampControl = nil
|
|
stop.error_code = stop.error_code or stop.errorCode or -1
|
|
stop.errorCode = nil
|
|
stop.active_deliveries = stop.active_deliveries or stop.activeDeliveries or {}
|
|
stop.activeDeliveries = nil
|
|
-- control signals
|
|
stop.is_depot = stop.is_depot or stop.isDepot or false
|
|
stop.isDepot = nil
|
|
stop.depot_priority = stop.depot_priority or 0
|
|
stop.max_carriages = stop.max_carriages or stop.maxTraincars or 0
|
|
stop.maxTraincars = nil
|
|
stop.min_carriages = stop.min_carriages or stop.minTraincars or 0
|
|
stop.minTraincars = nil
|
|
stop.max_trains = stop.max_trains or stop.trainLimit or 0
|
|
stop.trainLimit = nil
|
|
stop.providing_threshold = stop.providing_threshold or stop.provideThreshold or min_provided
|
|
stop.provideThreshold = nil
|
|
stop.providing_threshold_stacks = stop.providing_threshold_stacks or stop.provideStackThreshold or 0
|
|
stop.provideStackThreshold = nil
|
|
stop.provider_priority = stop.provider_priority or stop.providePriority or 0
|
|
stop.providePriority = nil
|
|
stop.requesting_threshold = stop.requesting_threshold or stop.requestThreshold or min_requested
|
|
stop.requestThreshold = nil
|
|
stop.requesting_threshold_stacks = stop.requesting_threshold_stacks or stop.requestStackThreshold or 0
|
|
stop.requestStackThreshold = nil
|
|
stop.requester_priority = stop.requester_priority or stop.requestPriority or 0
|
|
stop.requestPriority = nil
|
|
stop.locked_slots = stop.locked_slots or stop.lockedSlots or 0
|
|
stop.lockedSlots = nil
|
|
stop.no_warnings = stop.no_warnings or stop.noWarnings or false
|
|
stop.noWarnings = nil
|
|
-- parked train data will be set during initializeTrainStops() and updateAllTrains()
|
|
stop.parkedTrain = nil
|
|
stop.parkedTrainID = nil
|
|
stop.parkedTrainFacesStop = nil
|
|
end
|
|
end
|
|
|
|
-- update to 1.9.4
|
|
if oldVersion and oldVersion < "01.09.04" then
|
|
for stopID, stop in pairs(global.LogisticTrainStops) do
|
|
stop.lamp_control.teleport({stop.input.position.x, stop.input.position.y}) -- move control under lamp
|
|
stop.input.disconnect_neighbour({target_entity=stop.lamp_control, wire=defines.wire_type.green}) -- reconnect wires
|
|
stop.input.disconnect_neighbour({target_entity=stop.lamp_control, wire=defines.wire_type.red})
|
|
stop.input.connect_neighbour({target_entity=stop.lamp_control, wire=defines.wire_type.green})
|
|
stop.input.connect_neighbour({target_entity=stop.lamp_control, wire=defines.wire_type.red})
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
-- run every time the mod configuration is changed to catch stops from other mods
|
|
-- ensures global.LogisticTrainStops contains valid entities
|
|
local function initializeTrainStops()
|
|
global.LogisticTrainStops = global.LogisticTrainStops or {}
|
|
-- remove invalidated stops
|
|
for stopID, stop in pairs (global.LogisticTrainStops) do
|
|
if not stop then
|
|
log("[LTN] removing empty stop entry "..tostring(stopID) )
|
|
global.LogisticTrainStops[stopID] = nil
|
|
elseif not(stop.entity and stop.entity.valid) then
|
|
-- stop entity is corrupt/missing remove I/O entities
|
|
log("[LTN] removing corrupt stop "..tostring(stopID) )
|
|
if stop.input and stop.input.valid then
|
|
stop.input.destroy()
|
|
end
|
|
if stop.output and stop.output.valid then
|
|
stop.output.destroy()
|
|
end
|
|
if stop.lamp_control and stop.lamp_control.valid then
|
|
stop.lamp_control.destroy()
|
|
end
|
|
global.LogisticTrainStops[stopID] = nil
|
|
end
|
|
end
|
|
|
|
-- add missing ltn stops
|
|
for _, surface in pairs(game.surfaces) do
|
|
local foundStops = surface.find_entities_filtered{type="train-stop"}
|
|
if foundStops then
|
|
for k, stop in pairs(foundStops) do
|
|
-- validate global.LogisticTrainStops
|
|
if ltn_stop_entity_names[stop.name] then
|
|
local ltn_stop = global.LogisticTrainStops[stop.unit_number]
|
|
if ltn_stop then
|
|
if not(ltn_stop.output and ltn_stop.output.valid and ltn_stop.input and ltn_stop.input.valid and ltn_stop.lamp_control and ltn_stop.lamp_control.valid) then
|
|
-- I/O entities are corrupted
|
|
log("[LTN] recreating corrupt stop "..tostring(stop.backer_name) )
|
|
global.LogisticTrainStops[stop.unit_number] = nil
|
|
CreateStop(stop) -- recreate to spawn missing I/O entities
|
|
|
|
end
|
|
else
|
|
log("[LTN] recreating stop missing from global.LogisticTrainStops "..tostring(stop.backer_name) )
|
|
CreateStop(stop) -- recreate LTN stops missing from global.LogisticTrainStops
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- run every time the mod configuration is changed to catch changes to wagon capacities by other mods
|
|
local function updateAllTrains()
|
|
-- reset global lookup tables
|
|
global.StoppedTrains = {} -- trains stopped at LTN stops
|
|
global.StopDistances = {} -- reset station distance lookup table
|
|
global.WagonCapacity = { --preoccupy table with wagons to ignore at 0 capacity
|
|
["rail-tanker"] = 0
|
|
}
|
|
global.Dispatcher.availableTrains_total_capacity = 0
|
|
global.Dispatcher.availableTrains_total_fluid_capacity = 0
|
|
global.Dispatcher.availableTrains = {}
|
|
|
|
-- remove all parked train from logistic stops
|
|
for stopID, stop in pairs (global.LogisticTrainStops) do
|
|
stop.parked_train = nil
|
|
stop.parked_train_id = nil
|
|
UpdateStopOutput(stop)
|
|
end
|
|
|
|
-- add still valid trains back to stops
|
|
for force_name, force in pairs(game.forces) do
|
|
local trains = force.get_trains()
|
|
if trains then
|
|
for _, train in pairs(trains) do
|
|
if train.station and ltn_stop_entity_names[train.station.name] then
|
|
TrainArrives(train)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- register events
|
|
local function registerEvents()
|
|
local filters_on_built = {{ filter="type", type="train-stop" }}
|
|
local filters_on_mined = {{ filter="type", type="train-stop" }, { filter="rolling-stock" }}
|
|
|
|
-- always track built/removed train stops for duplicate name list
|
|
script.on_event( defines.events.on_built_entity, OnEntityCreated, filters_on_built )
|
|
script.on_event( defines.events.on_robot_built_entity, OnEntityCreated, filters_on_built )
|
|
script.on_event( {defines.events.script_raised_built, defines.events.script_raised_revive, defines.events.on_entity_cloned}, OnEntityCreated )
|
|
|
|
script.on_event( defines.events.on_pre_player_mined_item, OnEntityRemoved, filters_on_mined )
|
|
script.on_event( defines.events.on_robot_pre_mined, OnEntityRemoved, filters_on_mined )
|
|
script.on_event( defines.events.on_entity_died, function(event) OnEntityRemoved(event, true) end, filters_on_mined )
|
|
script.on_event( defines.events.script_raised_destroy, OnEntityRemoved )
|
|
|
|
script.on_event( {defines.events.on_pre_surface_deleted, defines.events.on_pre_surface_cleared }, OnSurfaceRemoved )
|
|
|
|
if global.LogisticTrainStops and next(global.LogisticTrainStops) then
|
|
-- script.on_event(defines.events.on_tick, OnTick)
|
|
script.on_nth_tick(nil)
|
|
script.on_nth_tick(dispatcher_nth_tick, OnTick)
|
|
script.on_event(defines.events.on_train_changed_state, OnTrainStateChanged)
|
|
script.on_event(defines.events.on_train_created, OnTrainCreated)
|
|
end
|
|
|
|
-- disable instant blueprint in creative mode
|
|
if remote.interfaces["creative-mode"] and remote.interfaces["creative-mode"]["exclude_from_instant_blueprint"] then
|
|
remote.call("creative-mode", "exclude_from_instant_blueprint", ltn_stop_input)
|
|
remote.call("creative-mode", "exclude_from_instant_blueprint", ltn_stop_output)
|
|
remote.call("creative-mode", "exclude_from_instant_blueprint", ltn_stop_output_controller)
|
|
end
|
|
|
|
-- blacklist LTN entities from picker dollies
|
|
if remote.interfaces["PickerDollies"] and remote.interfaces["PickerDollies"]["add_blacklist_name"] then
|
|
for name, offset in pairs(ltn_stop_entity_names) do
|
|
remote.call("PickerDollies", "add_blacklist_name", name, true)
|
|
end
|
|
remote.call("PickerDollies", "add_blacklist_name", ltn_stop_input, true)
|
|
remote.call("PickerDollies", "add_blacklist_name", ltn_stop_output, true)
|
|
remote.call("PickerDollies", "add_blacklist_name", ltn_stop_output_controller, true)
|
|
end
|
|
end
|
|
|
|
script.on_load(function()
|
|
registerEvents()
|
|
end)
|
|
|
|
script.on_init(function()
|
|
-- format version string to "00.00.00"
|
|
local oldVersion, newVersion = nil
|
|
local newVersionString = game.active_mods[MOD_NAME]
|
|
if newVersionString then
|
|
newVersion = format("%02d.%02d.%02d", match(newVersionString, "(%d+).(%d+).(%d+)"))
|
|
end
|
|
initialize(oldVersion, newVersion)
|
|
initializeTrainStops()
|
|
updateAllTrains()
|
|
registerEvents()
|
|
|
|
log("[LTN] ".. MOD_NAME.." "..tostring(newVersionString).." initialized.")
|
|
end)
|
|
|
|
script.on_configuration_changed(function(data)
|
|
if data and data.mod_changes[MOD_NAME] then
|
|
-- format version string to "00.00.00"
|
|
local oldVersion, newVersion = nil
|
|
local oldVersionString = data.mod_changes[MOD_NAME].old_version
|
|
if oldVersionString then
|
|
oldVersion = format("%02d.%02d.%02d", match(oldVersionString, "(%d+).(%d+).(%d+)"))
|
|
end
|
|
local newVersionString = data.mod_changes[MOD_NAME].new_version
|
|
if newVersionString then
|
|
newVersion = format("%02d.%02d.%02d", match(newVersionString, "(%d+).(%d+).(%d+)"))
|
|
end
|
|
|
|
if oldVersion and oldVersion < "01.01.01" then
|
|
log("[LTN] Migration failed. Migrating from "..tostring(oldVersionString).." to "..tostring(newVersionString).."not supported.")
|
|
printmsg("[LTN] Error: Direct migration from "..tostring(oldVersionString).." to "..tostring(newVersionString).." is not supported. Oldest supported version: 1.1.1")
|
|
return
|
|
else
|
|
initialize(oldVersion, newVersion)
|
|
log("[LTN] Migrating from "..tostring(oldVersionString).." to "..tostring(newVersionString).." complete.")
|
|
printmsg("[LTN] Migration from "..tostring(oldVersionString).." to "..tostring(newVersionString).." complete.")
|
|
end
|
|
end
|
|
initializeTrainStops()
|
|
updateAllTrains()
|
|
registerEvents()
|
|
log("[LTN] ".. MOD_NAME.." "..tostring(game.active_mods[MOD_NAME]).." configuration updated.")
|
|
end)
|