Open ghost opened 9 years ago
I'm still out of dev process, but could it be similar to what happens to player when forcing time compression when landing using autopilot? The ship effectively seems to land on the pad, but the permission times out before that so it never actually completes docking.
That would be in keeping with my observations. I have only seen this when speeding up time. I will try another game where I don't speed up and just keep it running in the background. Let's see if the same happens.
I can also confirm that the ships that are "stuck" stay there for long. I have re-checked their status every minute and they just stay in that "non-docked" state for several minutes (I just aborted the script after that).
In a script modeling ~1000 ships in the Sol system I had 20 ships enter the "non-docked" state within 1.5 hours of game play. The problem is, over time, more and more ships get stuck in this state. Maybe it hasn't been noticed before because the traffic was kept low?
I just did another simulation where I did not speed up time. Within 5 minutes of (realtime) game play 3 ships got stuck. I was using the same simulation (~1000 ships in Sol) as before.
I am playing with the 20150120 64-bit linux release.
Some more information collected in the last few simulations:
All that leads me to believe that the ships are actually docked all right - they just need to change their flight status.
If you want me to test anything else - just let me know.
How are you detecting that they're getting stuck? I've been sticking breakpoints all over the place and they all seem to be docking but I might be missing something. Or I'm only getting the ones that are working and not the ones that fail.
I first noticed it when my script crashed pioneer when trying to undock ships that apparently were not docked in the first place. I now catch and collect ships that I have set to dock somewhere if the Ai claims to be done (onaicompleted) but the flight status did not change to DOCKED. Over time this bin fills up with more and more ships (hundreds over time). I then went and flew to an actual location where they were stuck. All 8 were sitting there. I can send you the script if you like. I do simulate 1000+ ships though.
It really does look like what happens to the player when trying to dock in accelerated time. Could it be something like permission timing out without AI being notified in any manner?
@clausimu yes if you could share your changes somehow that would be good as it sounds like it rarely happens, I'm sure that I can fix it.
@corundscale it does sound like that but when that happens to ships I still see them docking and changing status so there must be some other condition associated with it.
The following is a crude script that simulates local traffic. In the function "afterDocking" I check for the flight status (lines 180-192) and print all stuck ships together with their starport where they are stuck whenever a new ship is added to the list.
If there is a better way to share this, please let me know. Can I mark the following as code to have it formatted a certain way? I'm totally new to git and the game debugging process. Please also excuse my really hacky way of lua scripting. I'm an absolute beginner.
-- This script adds local traffic (between starports within the system). Currently, cargo traffic is random (= random items are
-- delivered from starport to starport). In the future it is planned to adapt this to the actual market.
--
-- Adapted from and copied in part from TradeShips.lua.
--
-- Claudius Mueller
--
-- 01-16-15
local Engine = import("Engine")
local Game = import("Game")
local Space = import("Space")
local Comms = import("Comms")
local Timer = import("Timer")
local Event = import("Event")
-- local Serializer = import("Serializer")
local ShipDef = import("ShipDef")
local Ship = import("Ship")
local utils = import("utils")
local e = import ("Equipment")
local starports = {} -- all starports in this system
local vacuum_starports = {} -- just starports in space in this system
local ships = {} -- all ships managed by this script
local stuck_ships = {} -- ships stuck in "docking" (bug!)
local addShipEquip = function (ship)
-- add standard equipment to ship
local ship_data = ships[ship]
local ship_def = ship_data['ship_def']
-- add atmospheric shielding if appropriate and flag
if ship_def.equipSlotCapacity.atmo_shield > 0 then
ship:AddEquip(e.misc.atmospheric_shielding)
ship_data.atmoshield = true
else
ship_data.atmoshield = false
end
-- add general equipment
ship:AddEquip(e.misc.scanner)
ship:AddEquip(e.misc.autopilot)
ship:AddEquip(e.misc.cargo_life_support)
-- add defensive equipment based on lawlessness, luck and size
local lawlessness = Game.system.lawlessness
local size_factor = ship.freeCapacity ^ 2 / 2000000
if Engine.rand:Number(1) - 0.1 < lawlessness then
local num = math.floor(math.sqrt(ship.freeCapacity / 50)) -
ship:CountEquip(e.misc.shield_generator)
if num > 0 then ship:AddEquip(e.misc.shield_generator, num) end
if ship_def.equipSlotCapacity.energy_booster > 0 and
Engine.rand:Number(1) + 0.5 - size_factor < lawlessness then
ship:AddEquip(e.misc.shield_energy_booster)
end
end
-- add auto hull repair (rare)
if ship_def.equipSlotCapacity.hull_autorepair > 0 and
Engine.rand:Number(1) + 0.75 - size_factor < lawlessness then
ship:AddEquip(e.misc.hull_autorepair)
end
end
local alterShipCargo = function (ship, starport)
-- load and unload ship (currently just randomly loads stuff)
-- debug
print(ship.label.." | status: "..ships[ship]['status'].." | flight state: "..ship.flightState.." - loading/unloading cargo")
--
local total = 0
local empty_space = math.min(ship.freeCapacity, ship:GetEquipFree("cargo"))
local cargo = {}
local ship_data = ships[ship]
-- remove old cargo and add to starbase stock if anything there
local num_removed = 0
if ship_data.cargo then
for equip, amount in pairs(ship_data.cargo) do
num_removed = num_removed + ship:RemoveEquip(equip, amount)
starport:AddEquipmentStock(equip, amount)
end
end
ship_data.cargo = nil
-- add new cargo (currently random type)
local cargo_types = {}
local i = 0
for cargo_type, cargo_data in pairs(e.cargo) do
i=i+1
cargo_types[i] = cargo_type
end
local equip = cargo_types[Engine.rand:Integer(1, #cargo_types)]
local num_added = ship:AddEquip(e.cargo[equip], empty_space)
cargo[e.cargo[equip]] = num_added
ship_data.cargo = cargo
return num_added + num_removed
end
local afterUndocking = function (ship, ship_data)
-- procedure after undocking (find target, fly there to dock)
-- debug
print(ship.label.." | status: "..ship_data['status'].." | flight state: "..ship.flightState.." - after undocking")
--
-- set ship status to 'transit'
ship_data['status'] = 'transit'
-- find appropriate new target (based on presence of atmoshield)
if ship_data.atmoshield == true then
ship_data['starport'] = starports[Engine.rand:Integer(1, #starports)]
else
ship_data['starport'] = vacuum_starports[Engine.rand:Integer(1, #vacuum_starports)]
end
-- fly to and dock with target
ship:AIDockWith(ship_data.starport)
end
local onShipUndocked = function (ship, starport)
-- triggered after a ship has undocked from a starport
-- filter to apply only to ships managed by this script
if ships[ship] == nil then return end
afterUndocking(ship, ships[ship])
end
Event.Register("onShipUndocked", onShipUndocked)
local doUndock
doUndock = function (ship, ship_data)
-- undock ship from starport
-- debug
print(ship.label.." | status: "..ship_data['status'].." | flight state: "..ship.flightState.." - undocking")
--
-- change ship status to 'undocking'
ship_data['status'] = 'undocking'
-- if can't undock try again in 1 minute
if not ship:Undock() then
ship_data['delay'] = Game.time + 60
Timer:CallAt(ship_data.delay, function () doUndock(ship, ship_data) end)
else
ship_data['delay'] = nil
end
end
-- debug
local checkStuckShip
checkStuckShip = function (ship)
-- check if a stuck ship is finally docked
if ship.flightState == "Docked" then
print("======================================> Ship now docked after "..stuck_ships[ship].. " minute(s).")
table.remove(stuck_ships, ship)
else
stuck_ships[ship] = stuck_ships[ship] + 1
local test_delay = Game.time + 60
Timer:CallAt(test_delay, function () checkStuckShip(ship) end)
for ship, stuckcheck in pairs(stuck_ships) do print(ship.label, stuckcheck) end
end
end
--
local afterDocking = function (ship, ship_data)
-- procedure after docking (transfer cargo, go to relaunch phase)
-- debug
print(ship.label.." | status: "..ship_data['status'].." | flight state: "..ship.flightState.." - after docking")
if ship.flightState ~= "DOCKED" then
local stuckcheck = 1
stuck_ships[ship] = stuckcheck
local stuckshipcount = 0
for _ in pairs(stuck_ships) do stuckshipcount = stuckshipcount + 1 end
local shipcount = 0
for _ in pairs(ships) do shipcount = shipcount + 1 end
print("==========================================> New ship stuck! A total of "..stuckshipcount.." ships are now stuck of a total of "..shipcount..".")
for ship,_ in pairs(stuck_ships) do print(ship.label, ships[ship].starport.label) end
local test_delay = Game.time + 60
-- Timer:CallAt(test_delay, function () checkStuckShip(ship) end)
else
--
-- change ship status to 'docked'
ship_data['status'] = 'docked'
-- unload/load ship with cargo
local delay = alterShipCargo(ship, ship_data.starport)
-- have ship wait 1-5 seconds per unit of cargo
if delay > 0 then
ship_data['delay'] = Game.time + (delay * Engine.rand:Number(1, 5))
end
-- undock from starport
Timer:CallAt(ship_data.delay, function () doUndock(ship, ship_data) end)
end
end
local getAcceptableShips = function ()
-- get ships without hyperdrives
local filter_function
-- if no more than 1 vacuum_starports get only ships with atmospheric shielding
if #vacuum_starports < 2 then
filter_function = function(k,def)
return def.tag == 'SHIP' and def.hyperdriveClass == 0 and def.equipSlotCapacity.atmo_shield > 0 and def.hullMass < 100
end
-- if both types of starports present get with and without atmospheric shielding
else
filter_function = function(k,def)
return def.tag == 'SHIP' and def.hyperdriveClass == 0 and def.hullMass < 100
end
end
-- pull appropriate ships and return as array
return utils.build_array(utils.map(function (k,def) return k,def end, utils.filter(filter_function, pairs(ShipDef))))
end
local chooseShipType = function (ship_defs, atmoshield)
-- get a ship type that fits specific criteria (i.e. has to fit atmoshield)
-- if atmospheric shield required
if atmoshield == true then
-- filter function for atmoshield
atmoshield_filter = function(k,def)
return def.equipSlotCapacity.atmo_shield > 0
end
-- pull appropriate ships and return as array
atmoshield_shipdefs = utils.build_array(utils.map(function (k,def) return k,def end, utils.filter(atmoshield_filter, pairs(ship_defs))))
-- get ship
return ship_defs[Engine.rand:Integer(1, #atmoshield_shipdefs)]
-- if not requirements get random ship
else
return ship_defs[Engine.rand:Integer(1, #ship_defs)]
end
end
local spawnInitialShips = function ()
--spawn initial ships and setup system variable (starports, trading tables, etc.)
--get starports (not more than 1 starport = no local traffic)
starports = Space.GetBodies(function (body) return body.superType == 'STARPORT' end)
if #starports < 2 then return nil end
vacuum_starports = Space.GetBodies(function (body)
return body.superType == 'STARPORT' and (body.type == 'STARPORT_ORBITAL' or (not body.path:GetSystemBody().parent.hasAtmosphere)) end)
-- get population (no population = no local traffic)
local population = Game.system.population
if population == 0 then return nil end
-- get appropriate ship types (no types = no local traffic)
local ship_defs = getAcceptableShips()
if #ship_defs == 0 then return nil end
-- determine how many trade ships to spawn
local lawlessness = Game.system.lawlessness
-- start with 100 ships per 1 billion population
local num_trade_ships = population * 100
-- reduce based on lawlessness
num_trade_ships = num_trade_ships * (1 - lawlessness)
-- compute average distance of spawned ships in space from core of system [AU]
local range = 3
-- spawn the initial trade ships
for i = 0, num_trade_ships do
-- get a ship_def
-- local ship_def = ship_defs[Engine.rand:Integer(1, #ship_defs)]
-- local ship = nil
-- spawn the first quarter in starport
if i < num_trade_ships / 4 then
local starport = starports[Engine.rand:Integer(1, #starports)]
-- if starport is on planet pull ship that can fit atmospheric shielding
local ship_def = chooseShipType(ship_defs, starport.isGroundStation)
ship = Space.SpawnShipDocked(ship_def.id, starport)
if ship ~= nil then
ship:SetLabel(Ship.MakeRandomLabel())
ships[ship] = {status = 'docked',
starport = starport,
ship_def = ship_def,
atmoshield = nil}
addShipEquip(ship)
-- if the starport is already full
else
ship = Space.SpawnShipNear(ship_def.id, starport, 5000, 10000000) -- 10 Mio km = 1AU
ship:SetLabel(Ship.MakeRandomLabel())
ships[ship] = {status = 'transit',
starport = starport,
ship_def = ship_def,
atmoshield = nil}
addShipEquip(ship)
end
-- spawn the rest inbound to some starport
else
local starport = starports[Engine.rand:Integer(1, #starports)]
-- if starport is on planet pull ship that can fit atmospheric shielding
local ship_def = chooseShipType(ship_defs, starport.isGroundStation)
ship = Space.SpawnShipNear(ship_def.id, starport, 5000, 10000000) -- 10 Mio km = 1AU
ship:SetLabel(Ship.MakeRandomLabel())
ships[ship] = {status = 'transit',
starport = starport,
ship_def = ship_def,
atmoshield = nil}
addShipEquip(ship)
end
local ship_data = ships[ship]
-- if ship is docked
if ship_data.status == 'docked' then
afterDocking(ship, ship_data)
-- if ship is in space
elseif ship_data.status == 'transit' then
-- dock with target starport
ship:AIDockWith(ship_data.starport)
end
end
end
local onEnterSystem = function (ship)
-- if the player has entered the systemafterdocking
-- spawn initial ships and get them going
if ship ~= Game.player then return end
spawnInitialShips()
end
Event.Register("onEnterSystem", onEnterSystem)
local onAICompleted = function (ship, ai_error)
-- if the AI is done with the task assigned find a new task
-- only applies if ship is managed by this script
if ships[ship] == nil then return end
local ship_data = ships[ship]
-- done with flying and docking
if ship_data.status == 'transit' then
afterDocking(ship, ship_data)
else
print("Ship done with AI but not in transit "..ship.label)
end
-- -- done with undocking
-- elseif ship_data.status == 'leaving' then
-- afterUndocking(ship, ship_data)
-- end
end
Event.Register("onAICompleted", onAICompleted)
local onGameStart = function ()
spawnInitialShips()
end
Event.Register("onGameStart", onGameStart)
@fluffyfreak Ok, so another question: What is supposed to happen after following sequence of events:
Could we ensure that things like permission timing out always happen as the last thing in a timestep and that stuff like permission duration is always rounded up (so regardless of timestep length it always takes at least as long as it's supposed to)? (I'm still out of the loop, alas.)
I think someone smart needs to take a look at my script again (see previous post). I just reduced the number of ships by 5 (20 * population) to make it easier to track each ship. Starting at Barnard's star I see 1 or two ships. Both get stuck upon landing each time. If that does happen every time a ship lands (using my script) - maybe there is something wrong with my script, rather than the game itself. I can't figure out what though. My steps are:
1) undock 2) find new target within the system that is suitable for ship (atmoshields) 3) set AI to dock with target 4) when AI is complete, trigger "afterdocking" stuff (unload cargo, etc.) 5) repeat from (1)
Everything works as planned, except that at (4) the flight status of the ship is not set to "DOCKED" but is still "DOCKING". Which leads to the ships being stuck, since I can't undock them anymore. I have actually followed a ship to its target destination and quickly landed before them. Then I watched the ship dock. Everything looks perfect. Except, it does not change its flight status.
I think I have it traced down to catching onAICompleted versus onShipDocked. If I proceed after onAICompleted is triggered (after having given the AIDockWith command), the ship never changes status to "Docked" but stays in "Docking" flight status (even though physically it has landed - and even after sitting for a long time). If I proceed after onShipDocked is triggered (also after having given the AIDockWith command), the ship docks fine and status is changed to "Docked".
Somebody more knowledgable than me will have to explain why it works like that though...
Does it work for all ships now, or do you still have a population that fails to dock properly, but doesn't cause visible problems because they no longer attempt to undock without being docked?
As of right now I have not seen another ship that got "stuck" in the docking process.
I think I have it traced down to catching
onAICompleted
versusonShipDocked
. If I proceed afteronAICompleted
is triggered (after having given theAIDockWith
command), the ship never changes status to "Docked
" but stays in "Docking
" flight status (even though physically it has landed - and even after sitting for a long time). If I proceed afteronShipDocked
is triggered (also after having given theAIDockWith
command), the ship docks fine and status is changed to "Docked
".
@clausimu Could you please clarify what you mean by your use of "proceed"? I'm afraid I don't quite follow.
I'm sorry for not being clear. With "proceed" I mean call the next function in the script (handling the loading/unloading of the ship and then undocking from the starport). If I wait for the onShipDocked event and then follow this with loading/unloading/undocking everything seems fine. If I wait for the onAICompleted (after having told the ship to dock via the AIDockWith command) event and then try to load/unload/undock the ship stays stuck in the "undocking" state (even though physically it has landed).
Years ago I observe the same as @clausimu and @corundscale The adjust of final sequence of docking is critical and no one has been able to solve yet.
@Gliese852 what's the status of this issue after manual docking changes? Is it still valid, or can it now be closed?
There is something strange going on with ships set to dock (ship:AIDockWith(starport)). The AI triggers the event "onAICompleted", but flight status of the ship is stuck in "DOCKING". Either the docking procedure is not complete (in which case the AI should not trigger "onAICompleted"), or the flight status should be changed to "DOCKED".
This does not happen for every docking event. I have not actually seen the ships stuck in docking (are they hanging in mid-air?). But I notice this because the game immediately crashes when I ask ships to undock that are stuck in this limbo. I can't figure out what exactly triggers this. Has anybody else seen it?
I'm currently using the 20150120 build for 64-bit linux.