Page 1 of 1

Accessing "ghost" circuit connections

Posted: Sat Jun 01, 2019 7:38 am
by ThePlash
Hello!

I'm the developer of the BetterBots mod https://mods.factorio.com/mod/BetterBots

In my mod, I introduce some new technologies to empower roboports with added charge pads and electric energy flow for robots. All these new versions are actually new prototypes of type="roboport" because those values are defined during the prototype stage and can't be changed after that.

Once a new level of the main technology is researched, the mod "swaps" each roboport on the map with the new version automatically. From then on all new roboports placed by the player/robots are instantly swapped with the correct upgraded version. All the properties of the vanilla roboport are passed on to the newly created entity (inventory, health, internal charge, circuit connections etc.). After that, the old roboport entity is instantly destroyed.

Now, the issue I'm trying to work around is this: when I place a blueprint containing a roboport with a circuit connection to another entity, If the roboport is placed BEFORE the entity it is connected to, the connection is lost because (as i mentioned earlier) the roboport that is actually placed gets instantly destroyed in favour of the upgraded version, before the connected entity has a chance to be placed and the connection "physically" established. If the connected entity gets placed first instead, there's no problem because then the placed roboport gets connected before being destroyed and I can copy that connection over to the upgraded roboport.

Where's the information about those "ghost wires" stored? Of course if the entity that's supposed to be connected to the roboport has not yet been placed, there's no actual connection to fetch with the usual LuaEntity methods and properties...

I hope someone can shed some light on this matter...

Thanks

Re: Accessing "ghost" circuit connections

Posted: Sat Jun 01, 2019 8:04 am
by Bilka
Where's the information about those "ghost wires" stored? Of course if the entity that's supposed to be connected to the roboport has not yet been placed, there's no actual connection to fetch with the usual LuaEntity methods and properties...
The info is currently not accessible from Lua. You should be able to work around your problem by making your entities fast-replaceable of each other and then instead of destroying the placeholder, fastreplacing it with your real entity.

Re: Accessing "ghost" circuit connections

Posted: Sat Jun 01, 2019 9:56 am
by eradicator
Bilka wrote: Sat Jun 01, 2019 8:04 am The info is currently not accessible from Lua. You should be able to work around your problem by making your entities fast-replaceable of each other and then instead of destroying the placeholder, fastreplacing it with your real entity.
I had a similar situation the other day and the problem i had with surface.create_entity{fast_replace=true} is that it will drop the mining result of the thing being fast replaced as an item-on-ground stack into the world. Which i then have to surface.find_filtered_entity() to clean up the stack.
At least i didn't see any option to not create the stack when fast_replace'ing, nor an option to get a direct reference to the stack entity. Should i write a request for that maybe?

Re: Accessing "ghost" circuit connections

Posted: Sat Jun 01, 2019 3:34 pm
by Nexela
eradicator wrote: Sat Jun 01, 2019 9:56 am
Bilka wrote: Sat Jun 01, 2019 8:04 am The info is currently not accessible from Lua. You should be able to work around your problem by making your entities fast-replaceable of each other and then instead of destroying the placeholder, fastreplacing it with your real entity.
I had a similar situation the other day and the problem i had with surface.create_entity{fast_replace=true} is that it will drop the mining result of the thing being fast replaced as an item-on-ground stack into the world. Which i then have to surface.find_filtered_entity() to clean up the stack.
At least i didn't see any option to not create the stack when fast_replace'ing, nor an option to get a direct reference to the stack entity. Should i write a request for that maybe?
To have the item goto the player you also need to add `player = ` to your create_entity parameters

Re: Accessing "ghost" circuit connections

Posted: Sat Jun 01, 2019 5:40 pm
by eradicator
Nexela wrote: Sat Jun 01, 2019 3:34 pm To have the item goto the player you also need to add `player = ` to your create_entity parameters
You misunderstood. create_entity spawns an entity for *free*. Therefore the item created during fast_replace is also free and giving it to the player would be giving them an extra free item every time they build the thing ("build one get one free"). I want the item to be never created.

Re: Accessing "ghost" circuit connections

Posted: Sat Jun 01, 2019 5:57 pm
by Nexela
I did miss that part, in that case you need spill = false

spill :: boolean (optional): If false while fast_replace is true and player is nil any items from fast-replacing will be deleted instead of dropped on the ground.

Re: Accessing "ghost" circuit connections

Posted: Sat Jun 01, 2019 6:09 pm
by eradicator
Nexela wrote: Sat Jun 01, 2019 5:57 pm I did miss that part, in that case you need spill = false

spill :: boolean (optional): If false while fast_replace is true and player is nil any items from fast-replacing will be deleted instead of dropped on the ground.
Wtf...i swear that wasn't there yesterday! :oops:
Thank you.

Re: Accessing "ghost" circuit connections

Posted: Sun Jun 02, 2019 3:41 am
by ThePlash
Bilka wrote: Sat Jun 01, 2019 8:04 am The info is currently not accessible from Lua. You should be able to work around your problem by making your entities fast-replaceable of each other and then instead of destroying the placeholder, fastreplacing it with your real entity.
Thanks for the suggestion. I tried setting all roboport entities fast_replaceable_group to "roboports" and while it circumvented the original problem I had, it also introduced new issues that i could not work around (like, for example, being able to spam roboports over themselves or having each new custom roboport entity selectable in the upgrade planner)

Actually, I'm trying another approach: I'm using a "on_put_item" event listener that checks if the player.cursor_stack is a blueprint. If it is, it searches the stack for any roboport entity and sets its name property to the correct upgraded one based on the tech level researched.
The blueprint is then placed with upgraded roboport ghosts instead of the base ones. Bots are then able to build them because they have placeable_by = { item = "roboport", count = 1 } in their prototype.

After that (during the on_built_entity event), if event.stack is a blueprint i search for all upgraded roboport entities in it and change them back to vanilla roboports so the blueprint is preserved if the player chooses to uninstall the mod (or if they want to export them).

I still have to figure out a way to avoid some problems like for example:
1) this doesn't work if the player builds the roboports by hand instead of using bots (ghost circuit connections are severed)
2) if the user puts the blueprint exactly over already placed entities the game triggers an on_put event with no subsequent on_built_entity one, so the second half of the procedure is never called and the blueprint retains the custom entities
3) if the user places a roboport ghost and then, before it gets physically placed, a new tier of roboport technology is researched, I still have to figure out a way to change the entity ghost to be the new tier of roboports instead of the one that has been placed. I can't simply swap them like i do with physical roboports because doing that cuts the ghost circuit connections that I want to preserve...

Thanks for the help everyone!

EDIT: I managed to solve number 2) by storing a reference to the blueprint in a global variable and then moving the second half of the procedure to the on_tick event. I don't like this solution but it should encompass any loose end i hope...

Re: Accessing "ghost" circuit connections

Posted: Sun Jun 02, 2019 11:32 am
by eradicator
Hm...let's see. What if you have an itemA that places a dummyA entity. dummyA does not have a fast replaceable group at all. If dummyA or a ghost of it is built you replace it with entityTierK (K being any number). That way all tiered entities K to N (N being the maximum tier) can retain a fast replaceable group without the player being able to fast replace them manually. If enityTier1 is the vanilla roboport you can retain vanilla compatible blueprints.

Hm..the only problem with that is players trying to "revive" ghosts manually. If you're able to intercept it in time you can retain the circuits but get a "building failed" sound. If interception is too slow the connections are severed.

Re: Accessing "ghost" circuit connections

Posted: Mon Jun 03, 2019 1:48 am
by ThePlash
eradicator wrote: Sun Jun 02, 2019 11:32 am Hm..the only problem with that is players trying to "revive" ghosts manually. If you're able to intercept it in time you can retain the circuits but get a "building failed" sound. If interception is too slow the connections are severed.
That's actually what i've been working on today. The on_put_item event is called after the player clicks to build an entity but before the entity is actually built, so I could intercept the player trying to place a roboport exactly over a roboport ghost: if the ghost is one of my new entities, I call .revive() on it so it gets placed retaining all its properties (ghost connections too). Then I lower the cursor_stack.count by 1 to simulate the use of a roboport from inventory.

This solves number 1) from my previous post yay :D

The only issue I have is that to prevent a "building failed" notification (which would make no sense from the player perspective since "something has actually been built") i need to call player.clean_cursor() to empty the player cursor so the building procedure "stops" (it tries to build nothing because the cursor is now empty).

It's a minor nuisance so I could live with that tbh.... but I wonder is there a way to prevent the building of an entity during the on_put_item event other than using player.clean_cursor()?

Thanks

EDIT: I fixed it with a similar approach to the blueprint problem: I saved a reference to the player in a global variable and, during the next on_tick event, if I find that reference I call player.pipette_entity("roboport"). I tried using the on_entity_built event instead of on_tick but it seems that they are not raised one after another like i thought... something weird is going on because I wasn't able to find that global reference during the on_entity_built event that was supposed to be following the on_put_item event that set it.

Anyway, It seems to work fine now!

Re: Accessing "ghost" circuit connections

Posted: Mon Jun 03, 2019 6:18 am
by eradicator
ThePlash wrote: Mon Jun 03, 2019 1:48 am It's a minor nuisance so I could live with that tbh.... but I wonder is there a way to prevent the building of an entity during the on_put_item event other than using player.clean_cursor()?
Generally speaking events are just beurocratic notifications that something has already been done. None of the events can be canceled. The clean_cursor() is a nice hack to prevent the next event though, nice find! I bet i can use that myself somewhere.

As all events happen immediately, the on_build event would be called as part of revive(), so in your code one line after revive() it's already over. Edit: Revive only raises script_raised_revive and only if explicitly told to do so.

Re: Accessing "ghost" circuit connections

Posted: Tue Jun 04, 2019 4:40 am
by ThePlash
eradicator wrote: Mon Jun 03, 2019 6:18 am Generally speaking events are just beurocratic notifications that something has already been done. None of the events can be canceled. The clean_cursor() is a nice hack to prevent the next event though, nice find! I bet i can use that myself somewhere.

As all events happen immediately, the on_build event would be called as part of revive(), so in your code one line after revive() it's already over.
Hmm I see, I didn't consider .revive() to be raising an on_built_entity event. Maybe that's why i thought it acted weirdly.

Anyway thanks again for your help, I managed to have everything working as I wanted and pushed the next version of the mod. The only thing that's missing is the ability to change an entity-ghost inner entity without destroying the ghost, but I'm pretty sure that's an impossible feat.

Bye!

Re: Accessing "ghost" circuit connections

Posted: Tue Jun 04, 2019 7:49 am
by eradicator
ThePlash wrote: Tue Jun 04, 2019 4:40 am Hmm I see, I didn't consider .revive() to be raising an on_built_entity event. Maybe that's why i thought it acted weirdly.
Well, meh, it doesn't. What was i thinking. :oops: (Above post corrected for future readers.)
Though concerning the order of calling *if* it raises script_raised_revived that would happen during the call.