Page 1 of 1

[Lou][2.0.13] Inconsistent behaviour (belt vs others) when changing ghost entity force during super-forced builds

Posted: Fri Nov 01, 2024 7:31 pm
by azaghal
Description
When a player tries to do a super-forced build over existing ghost entities, there is a difference in behaviour, depending on ghost type, when the ghost entity's force is changed during the on_built_entity event.

Described scenario is a bit unusual, and probably not something that most mods deal with, but comes into play due to Construction Planner Continued's (mis)use of the modding API and force ownership. The mod changes the ghost force as part of the on_builty_entity event to prevent bots from the original (player) force constructing the ghost.

Since it is a bit hard for me to write this as reproduction steps, I will try to provide a little bit of code with some print statements to demonstrate the differences. I will focus on transport belt and underground pipes (as good examples).

Code: Select all

-- Set-up an allied force to the player's own - so we can easily see the placed ghosts when their force ownership is
-- changed.
local function create_not_player_force()
    local player_force = game.forces["player"]
    local not_player_force = game.create_force("not-player")

    player_force.set_friend(not_player_force, true)
    player_force.set_cease_fire(not_player_force, true)

    not_player_force.set_friend(player_force, true)
    not_player_force.set_cease_fire(player_force, true)
end

-- Switches force ownership for all placed entities (although primary interest is the ghost entities) to non-player
-- (allied) force as they are placed.
local function on_built_entity(event)
    local entity = event.entity

    local player_force = game.forces["player"]
    local not_player_force = game.forces["not-player"]
    
    print("Entity valid (pre-force change): " .. serpent.line(entity.valid) .. " @" .. game.tick)
    print("Entity unit number: " .. entity.unit_number .. " @" .. game.tick)
    print("Entity ghost name: " .. entity.ghost_name .. " @" .. game.tick)

    entity.force = not_player_force

    print("Entity valid (post-force change): " .. serpent.line(entity.valid) .. " @" .. game.tick)
end

script.on_init(create_not_player_force)

script.on_event(defines.events.on_built_entity, on_built_entity)
Underground pipe behaviour
With the above code, we can proceed to place the initial underground pipe ghost, which results in the following output:

Code: Select all

Entity valid (pre-force change): true @998
Entity unit number: 24 @998
Entity ghost name: pipe-to-ground @998
Entity valid (post-force change): true @998
After that, rotate the underground pipe so it is facing opposite direction of the initial one (game actually does this for us automatically, but just to emphasize that the underground pipe ghost placement attempt should not have the same orientation as the first one). Then try to super-force build on top of the existing pipe (which belongs to the other force at this point):

Code: Select all

Entity valid (pre-force change): true @13656
Entity unit number: 68 @13656
Entity ghost name: pipe-to-ground @13656
Entity valid (post-force change): true @13656
Couple of interesting things here:
  • It was possible to place down another underground pipe in the same spot.
  • The new underground pipe ghost entity remained valid after placement.
Transport belt behaviour
In the similar way to the above, place down a transport belt ghost:

Code: Select all

Entity valid (pre-force change): true @21088
Entity unit number: 82 @21088
Entity ghost name: transport-belt @21088
Entity valid (post-force change): true @21088
And then proceed to super-force place down the transport belt again on top of the existing one, but rotated by 180 degrees (similar to the underground pipe step):

Code: Select all

Entity valid (pre-force change): true @26422
Entity unit number: 106 @26422
Entity ghost name: transport-belt @26422
Entity valid (post-force change): false @26422
And here we can observe the following:
  • It was not possible to place two transport belt ghost entities in the same spot.
  • The newly placed entity seems to get immediately invalidated upon changing its force.
Conclusion?
So... It feels to me like only one of the two listed examples is the correct behaviour - or the very least that there is some kind of special logic that is handling transport belts etc in a bit of a different way. Would this be considered even as a bug in the game engine?

For me personally the behaviour of the underground pipe is maybe easier to handle (it kinda works out for my mod), but on the other hand... I'm pretty sure it is not normal to be able to have four ghost underground pipes (each facing its own direction) on a single tile...
Additional information
The reason I ended-up exploring this problem is because of a crash report for the Construction Planner Continued. While I have filed this under the bug reports, it might as well be intended behaviour or simply an implementation limitation/detail - but I wanted to at least double-check before I end-up trying to implement some kind of workaround in the mod.

I am also kinda curious what kind of oddities one could encounter

P.S.
Yours truly,
Doing-weird-things-with-your-game-engine

Re: [Lou][2.0.13] Inconsistent behaviour (belt vs others) when changing ghost entity force during super-forced builds

Posted: Mon Nov 04, 2024 4:47 pm
by azaghal
In the meantime I have figured out why the crash started to happen in the mod - there were some changes to event properties that I did not adjust the code for - so some of the code that was supposed to switch back the force for unapproved ghost just before a new ghost gets pasted in never kicked in.

The report is still valid on the inconsistent behaviour thing, and I have thanks to another bug testing reported realised that certain ghost entities can be stacked on top of each-other for the same force.

For example, you could run this in console to get multiple inserter ghosts placed in same position for the same force:

Code: Select all

/c game.player.surface.create_entity{name = "entity-ghost", inner_name = "inserter", position = {20,15}, force = game.player.force}
In fact, looks like you can even stack different entities this way (like pipe + inserter). If you bot-build one inserter, the other ghost will be gone. But... If you place multiple car/spidetron ghosts in similar manner, even when one of them gets built, the other ghost remains on top.

Hopefully this extra info helps a bit. :)

P.S.
I also fixed the URLs in the report (nothing else changed, it was just messing with my "OCD").