[1.1.70] Crash when after mining other-force entity and then deleting other force

This subforum contains all the issues which we already resolved.
Post Reply
User avatar
Gerkiz
Long Handed Inserter
Long Handed Inserter
Posts: 50
Joined: Fri Nov 30, 2018 3:36 pm
Contact:

[1.1.70] Crash when after mining other-force entity and then deleting other force

Post by Gerkiz »

Hello,

We hosted our scenario Towny. Everything was all good and dandy - some players were playing and all of a sudden new players tried to join but couldn't.
Their client crashed to desktop.

I tried myself to join the server, but received the same error along with crash to desktop.

Attached is save and log.

Steps to reproduce hard crash:
1. Load save while holding CTRL
2. Crash

Steps to reproduce normal crash to menu:
1. Load save
2. Receive the following error:

Code: Select all

Entity [name=oil-refinery, type=assembling-machine, position={803.5000000000, -537.5000000000}, setup=false, owned-by-ghost=false, to-be-deconstructed=false, to-be-upgraded=false, simulation=true] belongs to invalid force index=6
/Gerkiz
Attachments
Towny.zip
(12.48 MiB) Downloaded 57 times
factorio-current20221031215547.log
(9.7 KiB) Downloaded 62 times

Hanakocz
Inserter
Inserter
Posts: 39
Joined: Sun Jun 24, 2018 7:06 pm
Contact:

Re: [1.1.70] Crash CraftingMachine::getRecipe()

Post by Hanakocz »

It is very possible that this is caused by force being removed, e.q. when one team loses their base, some entities get destroyed via entity.die(), then

Code: Select all

game.merge_forces(force_name, 'neutral')
is called. That removes the force from the game, and should also get rid of all remaining entities by converting them into neutral force.

Previously, it wasn't producing a crash, just error on load (one example):

Code: Select all

 21.711 Warning Map.cpp:347: Map loading failed: Entity [name=transport-belt, type=transport-belt, position={48.5000000000, 228.5000000000}, setup=false, owned-by-ghost=false, to-be-deconstructed=false, to-be-upgraded=false, simulation=true] belongs to invalid force index=7
  22.086 Error AppManagerStates.cpp:1566: Entity [name=transport-belt, type=transport-belt, position={48.5000000000, 228.5000000000}, setup=false, owned-by-ghost=false, to-be-deconstructed=false, to-be-upgraded=false, simulation=true] belongs to invalid force index=7
I attach savefile that produces this error (was played on 1.1.69, produces the same error still even if loaded in 1.1.70), and I think the scenario's code didn't changed since then, and server was even running continuously just with quick restart to update the game version in between those two examples.
Attachments
Towny.zip
(12.57 MiB) Downloaded 57 times

Loewchen
Global Moderator
Global Moderator
Posts: 8304
Joined: Wed Jan 07, 2015 5:53 pm
Contact:

Re: [1.1.70] Crash CraftingMachine::getRecipe()

Post by Loewchen »

Can you provide a set of commands that corrupts the save?

Hanakocz
Inserter
Inserter
Posts: 39
Joined: Sun Jun 24, 2018 7:06 pm
Contact:

Re: [1.1.70] Crash CraftingMachine::getRecipe()

Post by Hanakocz »

This is the function that is called when the team loses:
It is just our assumption that the cause of problem comes from that, as that's what is removing the forces (and it can go for days and plenty of teams being removed before it happens to get corrupted)
https://github.com/ComfyFactory/ComfyFa ... m.lua#L752

Code: Select all

local function kill_force(force_name, cause)
    local this = ScenarioTable.get_table()
    local force = game.forces[force_name]
    local town_center = this.town_centers[force_name]
    local market = town_center.market
    local position = market.position
    local surface = market.surface
    local balance = town_center.coin_balance
    local town_name = town_center.town_name
    surface.create_entity({name = 'big-artillery-explosion', position = position})

    local is_suicide = cause and force_name == cause.force.name

    for _, player in pairs(force.players) do
        this.spawn_point[player.index] = nil
        this.cooldowns_town_placement[player.index] = game.tick + 3600 * 5
        this.buffs[player.index] = {}
        if player.character then
            player.character.die()
        else
            this.requests[player.index] = 'kill-character'
        end
        player.force = game.forces.player
        Map.disable_world_map(player)
        Public.set_player_color(player)
        Public.give_key(player.index)
    end
    for _, e in pairs(surface.find_entities_filtered({force = force_name})) do
        if e.valid then
            if destroy_military_types[e.type] == true then
                surface.create_entity({name = 'big-artillery-explosion', position = position})
                e.die()
            elseif destroy_robot_types[e.type] == true then
                surface.create_entity({name = 'explosion', position = position})
                e.die()
            elseif destroy_wall_types[e.type] == true then
                e.die()
            elseif storage_types[e.type] ~= true then -- spare chests
                local random = math_random()
                if random > 0.5 or e.health == nil then
                    e.die()
                elseif random < 0.25 then
                    e.health = e.health * math_random()
                end
            end
        end
    end
    local r = 27
    for _, e in pairs(surface.find_entities_filtered({area = {{position.x - r, position.y - r}, {position.x + r, position.y + r}}, force = 'neutral', type = 'resource'})) do
        if e.name ~= 'crude-oil' then
            e.destroy()
        end
    end

    game.merge_forces(force_name, 'neutral')
    this.town_centers[force_name] = nil
    delete_chart_tag_for_all_forces(market)

    -- reward the killer
    local message
    if is_suicide then
        message = town_name .. ' has given up'
    elseif cause == nil or not cause.valid or cause.force == nil then
        message = town_name .. ' has fallen to an unknown entity (DEBUG ID 0)!' -- TODO: remove after some testing
    elseif cause.force.name == 'player' or cause.force.name == 'rogue' then
        local items = {name = 'coin', count = balance}
        town_center.coin_balance = 0
        if balance > 0 then
            if cause.can_insert(items) then
                cause.insert(items)
            else
                local chest = surface.create_entity({name = 'steel-chest', position = position, force = 'neutral'})
                chest.insert(items)
            end
        end
        if cause.name == 'character' then
            message = town_name .. ' has fallen to ' .. cause.player.name .. '!'
        elseif cause.force.name == 'player' then
            message = town_name .. ' has fallen to outlanders!'
        elseif cause.force.name == 'rogue' then
            message = town_name .. ' has fallen to rogues!'
        else
            message = town_name .. ' has fallen to an unknown entity (DEBUG ID 1)!' -- TODO: remove after some testing
        end
    elseif cause.force.name ~= 'enemy' then
        if this.town_centers[cause.force.name] ~= nil then
            local killer_town_center = this.town_centers[cause.force.name]
            if balance > 0 then
                killer_town_center.coin_balance = killer_town_center.coin_balance + balance
                cause.force.print(balance .. ' coins have been transferred to your town')
            end
            if cause.name == 'character' then
                message = town_name .. ' has fallen to ' .. cause.player.name .. ' from ' .. killer_town_center.town_name .. '!'
            else
                message = town_name .. ' has fallen to ' .. killer_town_center.town_name .. '!'
            end
        else
            message = town_name .. ' has fallen to an unknown entity (DEBUG ID 2)!' -- TODO: remove after some testing
            log('cause.force.name=' .. cause.force.name)
        end
    else
        message = town_name .. ' has fallen to the biters!'
    end

    Server.to_discord_embed(message)
    game.print('>> ' .. message, {255, 255, 0})
end

local function on_forces_merged()
    -- Remove any ghosts that have been moved into neutral after a town is destroyed. This caused desyncs before.
    for _, e in pairs(game.surfaces.nauvis.find_entities_filtered({force = 'neutral', type = 'entity-ghost'})) do
        if e.valid then
            e.destroy()
        end
    end
end

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

Re: [1.1.70] Crash when after mining other-force entity and then deleting other force

Post by Rseding91 »

OK I figured out what went wrong:

* Player on force A mines friendly entity on force B
* Force B is deleted
* Entity from force B exists in payer A's undo queue

When deleing forces entities on that force in other players undo queues are not properly transferred.
If you want to get ahold of me I'm almost always on Discord.

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

Re: [1.1.70] Crash when after mining other-force entity and then deleting other force

Post by Rseding91 »

Thanks for the report and the additional information. The underlying issue is now fixed for the next release. The save file is in a broken state so it won't be usable.
If you want to get ahold of me I'm almost always on Discord.

blubFisch
Manual Inserter
Manual Inserter
Posts: 2
Joined: Sat Jan 16, 2016 11:21 pm
Contact:

Re: [1.1.70] Crash when after mining other-force entity and then deleting other force

Post by blubFisch »

Great, thanks!

I don't know if it's relevant, but for me, this bug was triggered only sometimes. If I load the same save 5 times, 3 times it fails while 2 times it loads fine.

Post Reply

Return to “Resolved Problems and Bugs”