Adds Lua-programmable circuit network combinator. Based on Sandboxed LuaCombinator and LuaCombinator2 mods. Probably won't work in multiplayer games.
Mods introducing new content into the game.
Entities which interact with the circuit network.
When I blueprint a ML-combinator then progam text of the editor is not copied. Numbered save items seem to be copied.
When I blueprint a ML-combinator then progam text of the editor is not copied.
That is expected, as I did remove all complicated blueprint code.
Might restore it later, but don't really think it's a good idea to plop these things around easily, as pretty sure running lua code potentially on every tick in every combinator can get pretty expensive fast.
Copying settings via Shift+RMB + Shift+LMB should work though (it works via map too, I think, i.e. can be done remotely), and preset buttons on top kinda allow for such save/restore.
So don't really feel like blueprinting is that important, but would be nice to have ofc, just need to check on how to do proper serialization there.
Original mod had "blueprint_custom_data" lib bundled for that, it seems, maybe will use something like that, or just base64 the code without any extra magic.
But again, maybe sometime later.
Numbered save items seem to be copied.
They are global, so not really copied, just shown the same everywhere.
When I blueprint a ML-combinator then progam text of the editor is not copied.
That is expected, as I did remove all complicated blueprint code.
I really hope you will change your mind.
Numbered save items seem to be copied.
They are global, so not really copied, just shown the same everywhere.
Didn't now that. But actually this is really helpful.
Didn't now that. But actually this is really helpful.
Ah, guess another thing that's unclear from that mediocre help text.
I really hope you will change your mind.
Not really against the feature, just don't think it's very important, and yeah, will probably add it back before long.
This would probably be a ways down the road but it would be cool if MLCs could be "linked" to a script save slot. So if I have two MLCs linked to slot 0, and I edit the script on the first MLC, the second MLC begins to run the new code. Similarly, if I blueprint a linked MLC, change the linked code, then paste the MLC blueprint, the MLC is already running the new code.
Ha, that'd be like software auto-update mechanism (think windows updates).
Thinking along these lines, more sadistic method to do it would then be to allow combinator to send/receive/reload its code from the network, encoded into whatever special "code update" signal there, with all the gory side-effects that we see with OS updates breaking everything all over the real world!
That would be a really cruel lession for someone who e.g. keeps criticizing MS for issues with their windows update mechanism - try running those yourself, see how easy it is :)
Added limited blueprinting support in 0.0.35.
Limitation is that code is only copied while original combinator from which blueprint was made exists, as code itself is not stored in the blueprint string.
Also added a simple Over-The-Air updates in the same vein via "ota_update_from_uid" value - set that to any MLC uid (number at the top of the window, or that "uid" in the lua env), and on next run, code will be copied from specified combinator, if it exists.
Only code is copied, so any kind of local state (var, out, globals) can be used by the new code immediately, or can be easily cleaned-up by e.g. checking some "version" var there and cleaning-up if it doesn't match the code.
Doing proper to-blueprint-string serialization is somewhat difficult (done via +1 invisible combinator copied along with the main one, where data is dumped into 32-bit signal settings), and I don't really want to bother.
One way to work around this limitation is probably to keep "source" combinators somewhere, and bootstrap new ones from them, either via signal or whatever other logic in their code, after they get placed by the robots, if it's not done for simple copy-pasting.
Moving stuff containing MLCs with Ctrl+X without such tricks probably won't work, which seem to be the most unfortunate problem with this approach for me, but oh well.
Regarding the problem of no code in the blueprint,
I see this api
https://lua-api.factorio.com/latest/LuaItemStack.html#LuaItemStack.set_blueprint_entity_tag
I don't know if this is useful, hope it can help you
I think that's a method to set tags for blueprint item in the inventory, unrelated to creating those.
Sorry to raise this thread, but I think that you can blueprint the lua code as text by using https://mods.factorio.com/mod/example-entity-with-tags example mod. Should be able to save the lua script to blueprint that way.
Oh yeah, liumailong was right above, and such tags are stored in blueprints, as that example seem to demonstrate.
Didn't realize blueprint item always exists via player.blueprint_to_setup.
Thanks for pointing that out.
I think that example is also unnecessary complicated for modern factorio versions too, since on_player_setup_blueprint passes direct mapping of all entities used in there (since https://forums.factorio.com/viewtopic.php?p=457054#p457054 ), so about 2/3rds of the code in that example should by now be obsoleted by that.
EDIT: ah nope, looks like that was done deliberately to find correct entity if that mapping was broken due to mods, as mrvn (example author) pointed out in the same forum thread.
Yea, though because it's just at blueprint setup, it's just run at that time and doesn't exactly need to be the fastest code. But it looks like you can almost just drop the "code" right into it, as the tag expects a table of basic types (including tables).
Yeah, there's even an example of using it as such library in the neighbor thread - https://forums.factorio.com/viewtopic.php?p=466734#p466734
My main concern here is not performance but maintaining extra 120 lines of math/geometry code, once bugs start inevitably popping-up there :)
But it's not a big concern of course, and I think it should be fine to only fallback to that code if list of entity names in the blueprint mapping changes (due to mod/entity removals), otherwise just pick them by index from that mapping directly without the complexity.
Since you are just looking to add tags to your ghosts: each BlueprintEntity in the list contains the position
( https://lua-api.factorio.com/latest/Concepts.html#BlueprintEntity )
Table with the following fields:
- entity_number :: uint: The entity's unique identifier in the blueprint.
- name :: string: The prototype name of the entity. Check for your device
- position :: Position: The position of the entity. To get the entity to copy data from the player.surface.find_entities(position)
- ...
- tags :: Tags (optional): The entity tags of the entity, if there are any. Only relevant for entity ghosts.
This should keep it simple, as this is all you need for what we're looking to do here.
Edit: Well, I think you'd have to subtract the event.area
center position to the position given here to get the actual position...
This get's complicated :P
Maybe overly complicated for no good reason.
Not sure if there are any mods that add/remove/modify entities in on_player_setup_blueprint hook, so only added a consistency check for that mapping and a warning if it looks invalidated in 0.0.69, which hopefully never happens in practice, though then again Murphy Law ensures that these corner-case somehow always come up...
Can always add more complicated hacks there later, if someone needs them.
Same as with earlier copy-paste trick, only code gets stored in blueprints, not globals and outputs.
Not sure if other state is worth preserving, as it can be specific to a source location, and this would allow combinator code to easily know that it's a new place, maybe run any necessary init code or whatever.
(though latter can also be done by checking "uid" vs some global stored on the thing)
Woohoo! noice!
I can work with the current constraints, but I do use other mods that swap ground entities for placement marker entities. Bulk Rail Loaders is one of them. They swap for the placement entity in-place (different name), so it'd fail if I Bp a mlc if I had a bulk rail loader in the bp.
I doubt I'd have a rail (un)loader in the same bp as my main circuit logic would be in a secure compound (trying to go for self-building factory with recursive blueprints) :)
As far as other state info, I don't think there is a need for any other state than the code, as it should be "fresh install" when the bp is placed down. Though if the mlc gets placed before other circuit controls, the code should be written to account for that and wait.
Hm, yeah, it seem to do set_blueprint_entities(), and tweaks entity names, so definitely will trigger that invalidation-check.
Ah well, guess should copy that strange offset-comparing code after all, given that there's an immediate and concrete example that it is needed.
Though if the mlc gets placed before other circuit controls, the code should be written to account for that and wait.
Unless it's the self-building factory use-case, can also place a switchbutton to turn whatever machinery on manually after checking that everything is in place and connected.
Was just messing with the code last night. No need to translate from BP space to surface space, as the position values given to the blueprint entities are the actual surface positions. You can use them directly to find the entities you want out of the BP.
local function get_blueprint_to_setup(player_index)
local player = game.players[player_index]
if not (player and player.valid) return end
local blueprint_to_setup = player.blueprint_to_setup
if blueprint_to_setup
and blueprint_to_setup.valid_for_read then
return blueprint_to_setup, player
end
local cursor_stack = player.cursor_stack
if cursor_stack
and cursor_stack.valid_for_read
and cursor_stack.is_blueprint
and cursor_stack.is_blueprint_setup() then
return cursor_stack, player
end
end
function on_setup_blueprint(ev)
local bp, p = get_blueprint_to_setup(ev.player_index)
if not (bp and p and p.valid) then return end
-- bp_es = the entities gathered in the blueprint
local bp_es = {}
if bp and bp.valid_for_read then bp_es = bp.get_blueprint_entities() end
if not (bp_es) then return console_warn( p, 'BUG: Failed to detect'..
' blueprint item/info, Moon Logic Combinator code (if any) WILL NOT be stored there' ) end
-- Blueprint mapping seems to be broken, though blueprint_entities have <real> surface position coordinates.
-- Blueprint ev.mapping can be invalidated by other mods acting on this event, so checked first
-- See https://forums.factorio.com/viewtopic.php?p=457054#p457054 for more details
local bp_mlc_uids = {}
for _,e in ipairs(bp_es) do
if e.name == "mlc" then
--log("testing for mlc at position:(" .. e.position.x .. "," .. e.position.y .. ")")
local map_en = ev.surface.find_entity("mlc",e.position)
if map_en ~= nil then
-- log("mlc found at (" .. e.position.x .. "," .. e.position.y .. ") Unit number: " .. map_en.unit_number )
bp_mlc_uids[e.entity_number] = map_en.unit_number
else
console_warn(p,'BUG: blueprint map does not point to an actual mlc\n' ..
'blueprint entity id : ' .. e.entity_number ..
'\nblueprint entity name : ' .. e.name)
end
end
end
for bp_idx, uid in pairs(bp_mlc_uids)
do bp.set_blueprint_entity_tag(bp_idx, 'mlc-code', global.combinators[uid].code) end
end
Thanks. Indeed these positions seem to match, guess it's a more recent factorio change.
Added it as a fallback for using event.mapping in 0.0.70, also matching ghost entities, in case blueprint was copied from these, but without doing anything with them - should have code-tags already, so can be copied without any modifications.
One other corner-case highlighted in the example-entity-with-tags is that blueprint can actually have no entities in it and still be valid - can be a tile pattern, so these can be skipped without any unnecessary warnings.
Oh, one more thing: did change tag from "mlc-code" to "mlc_code" so that lua's "tags.mlc_code" syntax sugar can be used on it.
That might discard code stored in blueprints made in 0.0.69 (i.e. within last day), but hopefully no one but you knows about this stuff yet anyway, so won't notice, and there's an ota-update-like fallback if original combinator is still on the map.
That might discard code stored in blueprints made in 0.0.69 (i.e. within last day), but hopefully no one but you knows about this stuff yet anyway, so won't notice,
lol
Also noticed that, if you reuse a blueprint (ie: use the blue new contents button) the on_player_setup_blueprint
event isn't fired so, for now, you have to use a new blueprint each time.
There is an older "Pending" thread talking about this that matched the non-firing of this event, so I dropped a post about it... Hopefully they will see it.
[kovarex] [1.0.0] New contents for blueprint broken vs. new blueprint
Also noticed that, if you reuse a blueprint (ie: use the blue new contents button) the on_player_setup_blueprint event isn't fired
Oh, that's probably a good thing to add to "Known Issues" section of the description.
on_player_setup_blueprint event isn't fired
Just tried it here, and it works for me, but produces "BUG: Failed to detect blueprint ..." message with 0.0.70, because it looks like:
Fixed it in 0.0.71 by removing that extra check - doesn't seem to be necessary.
So I think if you're using code pasted above, if not (bp and p and p.valid) then return end
line might cause silent return due to that check for you.
Or also possible that event is indeed not fired due to some mod combination that you have.
Might be worth checking which is it (e.g. add unconditional debug-print there and disable mods until it fires), and maybe providing extra information in that forum post either way with more precise info on when/how issue can be reproduced.
To be clear on how I'm testing this:
Let me know if you're seeing something else happen there, or maybe doing something differently above, which doesn't work the same way.
One small fix:
control.lua:477 change
if not bp_mlc_uids then
to
if not next(bp_mlc_uids) then
if not bp_mlc_uids then
toif not next(bp_mlc_uids) then
No, that's deliberate - validation calls just above it both have cases like:
- if not e.valid or bp_check[bp_idx] ~= e.name then return end -- abort on mismatch
- if next(bp_mlcs) then return end -- blueprint entities left unmapped
Which deliberately return nil if it looks like there's a mismatch between map/blueprint to produce that warning and not mess-up blueprint with invalid data.
Empty table return from these is perfectly valid though, e.g. if there're no MLCs in the blueprint.
The problem is that the abort never goes through as :
for bp_idx, e in pairs(bp_map) do
will skip the inner code when the bp_map (valid table) is empty.
maybe have if not next(bp_map) then return end
right before the loop through the mapping?
edit: BTW this is a great mod and it's been fascinating in it's simplicity in having the lua sandbox work through just one code file.
The failure of the game to call on_player_setup_blueprint
is wonky, as it gets called if the bp is in a book ?!, but not as a single page.
Oh, right, I think I see what you mean - that mapping can end up being empty if invalidated, and blueprint_map_validate only looks for mismatch by iterating over it, so if it is empty, won't really check anything and incorrectly consider it to be valid. Makes sense, my bad.
maybe have if not next(bp_map) then return end right before the loop through the mapping?
Yeah, or might be better to check for mismatches in both directions - set bp_check[bp_idx] = nil
within that loop and abort later on next(bp_check), as anything leftover there indicates that kind of superset mismatch.
lua sandbox work through just one code file.
To be fair, it's a 1000-lines code file.
I still find it easy enough to navigate myself, but I'm sure at least some people less familiar with the code in there might find it difficult already, and would rather have it split into easier-to-navigate parts.
might be better to check for mismatches in both directions
Added that check in 0.0.72, should fix the issue. Thanks again.
Just played around with the Bulk Rail Loader code. Added a flag to check whether to do set_blueprint_entities()
depending on if there are BRL to replace.
Guess what! the mappings come back!... So any setting of entities (replacement/adding) causes the game to delete the mapping, resulting in a empty table of entities. So the game already "marks" the mapping as invalid if the table used by get_blueprint_entities()
changes.
This could simplify the code a bit.
Edit: spelling, formatting
Might be why Factorio API has that strange LazyLoadedValue there instead of a normal table, but was apparently added to 0.17.69 that way, i.e. not a more recent change.
Given that interation over that stuff is needed to map blueprint indexes to unit numbers anyway, and that API description still clearly states that such mapping "will be incorrect" and that forum advice from back when that was created, feel like it's probably better to keep these extra 4 lines of validation in case it does get broken without ev.mapping.valid flipping to false, as that might otherwise silently create incomplete/broken blueprint.
I.e. might just be an accident that it works cleanly like that with changes from this particular mod.
Also found out this while doing testing:
If you reuse a blueprint (ie. use the blue button to select new contents), the player.blueprint_to_setup
holds an invalid blueprint, and when you get it from player.cursor_stack
the get_blueprint_entities()
function returns a literal nil
.
Made a post about it here [1.1.36] Blueprints missing entity list when reused
Might be why Factorio API has that strange LazyLoadedValue there instead of a normal table, but was apparently added to 0.17.69 that way, i.e. not a more recent change.
Maybe, but I think that may have more to do with large blueprints.
Given that interation over that stuff is needed to map blueprint indexes to unit numbers anyway, and that API description still clearly states that such mapping "will be incorrect" and that forum advice from back when that was created, feel like it's probably better to keep these extra 4 lines of validation in case it does get broken without ev.mapping.valid flipping to false, as that might otherwise silently create incomplete/broken blueprint.
That validation is a good safeguard, in case they have to change the internals around again. Also, the mapping.valid
is in reference to the LazyLoadedValue
, specifying that it's a valid, usable object. That's probably why I've always seen that boolean to be true
.
I.e. might just be an accident that it works cleanly like that with changes from this particular mod.
By this particular mod
you mean the Bulk Rail Loaders? If so, it's not from that one, as it'll abort and not change bp contents if there are no BRL_containers in the blueprint.
By this particular mod you mean the Bulk Rail Loaders? If so, it's not from that one, as it'll abort and not change bp contents if there are no BRL_containers in the blueprint.
Yeah, I meant "works cleanly" as in "ev.mapping.valid = false when stuff changes" as opposed to potential "entities get shuffled around randomly" mess, and as you mentioned it was the former case when you tested it.
If you reuse a blueprint (ie. use the blue button to select new contents), the player.blueprint_to_setup holds an invalid blueprint, and when you get it from player.cursor_stack the get_blueprint_entities() function returns a literal nil.
Oh, guess I missed that during testing that button earlier, as I only looked at whether there's any blueprint in these two places, but didn't check what that method returns, and always-nil from there indeed sounds bogus.
I think in this unfortunate case there'll probably be no tiles in that blueprint either, and if blueprint gets "setup" with neither entities nor tiles in it, then it's probably safe to assume such bogus call and issue a warning about stuff not being stored in it properly.
Good catch!
Maybe, but I think that may have more to do with large blueprints.
Might be too, though given that factorio APIs usually return custom C++ objects instead of tables, I'd assume most if not all of them are actually lazy-loaded or lazy-queried too, i.e. just a bunch of pointers to some C++ lookup methods, running "under the hood" from a Lua perspective.
(hence querying them in a wrong way often raises something like "__index argument is expected to be uint and not string")
and if blueprint gets "setup" with neither entities nor tiles in it, then it's probably safe to assume such bogus call
It also occured to me that maybe that'd be same as checking bp.is_blueprint_setup()
from your earlier example, which I removed assuming that cursor_stack was fine and it's that check that was bogus, while it seem to be the other way around.
Looking into what "is_blueprint_setup()" means, found this older bug report - https://forums.factorio.com/88100
It seem to be same as your https://forums.factorio.com/viewtopic.php?f=7&t=99323 but reported earlier, complaining about same issue and use-case, though sadly with no resolution either, but with good exploration of what happens there. Might be worth linking in the new post too.
probably safe to assume such bogus call and issue a warning about stuff not being stored in it properly.
Did look into this, and decided to not add this check, as it's probably very rare that "new contents" button is used on MLCs (as most blueprints won't have one), but with no valid blueprint, there's no way to check whether it contains an MLC, so such warning will have to be issued on every use of that button, and almost certainly be useless due to that (afaik people are very good at ignoring false-positives/noise).
There is also on_player_configured_blueprint event which can potentially be used to check if every MLC in blueprint has mlc_code tag (i.e. have been processed), but unfortunately it also doesn't have any way to refer to an actual blueprint that was configured when that "new contents" button is used - blueprint_to_setup/cursor_stack don't have anything valid in them, so can't be used for such sanity-check either.
Will file it under Known Issues, and hopefully this shortcoming might be addressed in the main game sometime - using that button should magically "just work" when that happens (as presumably there'll be valid blueprint_to_setup then).
Thanks for that thread! I wonder why I couldn't find that one when I did a search It had all the search terms .. nvm I was using "reuse" and not "new contents".
But still, that's got a lot of info it seems
Also, like you mention, it seems this is as far as we go for now with this feature of Moon Logic.
Thanks for your work and help finding those other threads in the forums!
This new blueprint feature, seems to only work in new games, at least for me.
When I try to blueprint -> delete original -> paste blueprint, I get: -- No code was stored in blueprint and Moon Logic [268959] is unavailable for OTA code update.
This works fine in a new game though.
Don't think there should be anything different wrt how it works between diff games or entities placed by different mod versions.
You do create new blueprint via e.g. Ctrl-C/Ctrl-V or a Blueprint button, not using existing blueprint in any way, right?
(as also mentioned above, there's a known issue in that regard)
It seem to work as expected when trying to reproduce it like you described above in any of my old saves too - that removing old combinator is a factor suggests that code isn't copied into blueprint, and that's supposed to either produce some kind of error message or - if there's none - probably either another strange "empty blueprint is being setup" case or that hook code doesn't run at all somehow.
Maybe you can rule-out the latter (doubly-strange) option, by going into "mods" directory under factorio dir (at least on linux, but probably same on windows too), unpack Moon_Logic_x.y.z.zip there into same dir, remove the zip file, so that you have directory instead of .zip there, and edit control.lua there (in something like Notepad++ - it's a text file) so that on_setup_blueprint on line 466 starts with:
local function on_setup_blueprint(ev)
local p = game.players[ev.player_index]
console_warn(p, 'TEST: on_setup_blueprint hook from Moon Logic')
if not (p and p.valid) then return end
I.e. add that one extra "console_warn" line that'd produce a message to player unconditionally to make sure that hook runs in your game.
Restart factorio, reload save, and try creating new blueprint there - game should print that "TEST: ..." line at the bottom of the screen, it'd be interesting to know if it doesn't.
You can just remove that console_warn() line afterwards, and I think factorio will replace the dir there back with new .zip file when updating the mod anytime later, so no need to worry about it.
One thing that can be different about new game btw is "Map" options, which are loaded with the save, and maybe you can also test-reset these to all-defaults after loading the game (not sure if it might reset global Map options to defaults too?) and see if that changes anything - it can potentially indicate that some mod changes what you're holding when setting-up blueprint based on such options, or at least that's something else that I can think of that can mess it up silently like that.
I use Ctrl-C, Ctrl-V and have also used the Blueprint button to test this, same result. I'm not using the "Select new content for blueprint"
on_setup_blueprint executes every time I copy or blueprint an MLC, at least I get the added warning.
I have reset the map settings, saved, closed down factorio, and reloaded the save. still same error.
I'll share my save if you want to test yourself. https://mega.nz/file/655ilQ7T#cgM9V-wl8xGXPWo_FXmY9ol2HB6jo69m-UdRyrqrwGM
I've created a clean "mods" dir, copied mlcTest2.zip into "saves", and synced the mods when loading the game, which downloaded them all from scratch to match your setup, and loaded the game afterwards, but couldn't reproduce the issue.
Specific things I tried should be easy to see in the video here: https://e.var.nz/factorio.2021-08-08.s8MICeG9.mp4
Tried to Ctrl-X/Ctrl-V the thing and create/apply blueprint with and without source combinator on the map, both seem to work as expected to me.
Am I doing something different from what you tried there?
If you load same exact save and do something similar to that video, does everything works the same as in the video?
If it does not, I'd think that maybe you have some local mod tweaks, which weren't downloaded when I clicked "sync mods", and which you can probably easily find that way if your internet bandwidth permits - copy "mods" dir to e.g. "mods.old", create new dir there, sync mods to that save from scratch, load it and make sure that blueprinting stuff works, then use any kind of directory-compare app (ideally checking files by content, on linux cli just diff -r mods mods.old
should work) to check if it's maybe upstream-replaced same-version mod stuck in there or something.
(jic, here's output from sha256sum -b *
in the mods dir that I got after syncing - https://e.var.nz/factorio.2021-08-08.s8MICeG9.mods.sha256.txt )
And if mods are same and you're doing exact same things, starting from exact same save file, yet still get diff result - probably safe to assume that it's the factorio game itself or its settings that are different somehow, as that's the only other relevant component left there (which presumably should work exactly same wrt game rules/logic regardless of underlying OS and hardware), but that looks like a bit wild and unlikely bug tbh, gotta be something simplier.
(my factorio version here, as reported to console on game start - Factorio 1.1.37 (build 58871, linux64, full)
)
I think I've found the problem. The mod "The Blueprint Designer Lab" seems to be causing the problem. At least when you are in "The Lab". It works outside the lab in the normal world, but for some reason not inside the lab. (click the "TheLab" button in the top right to enter).
I noticed because you did your tests outside the lab in the video. And I've been doing my testing inside, but it did not occur to me that could in any way be the reason.
Occurs in new games as well, so should not be a problem specific to the save.
Yeah, how Blueprint Designer Lab creates the blueprinted entities seem to be the incompatibility here.
ReviveEntity() there in scripts/common.lua runs _, revived, request = entity.revive({true})
, which is entity.revive() API call, but it does not pass raise_revive=true
parameter to it, so tags with the code attached to the blueprinted entity-ghost get lost there, as it gets replaced with the entity without firing the event that'd copy code from these tags.
I'm not sure why that mod does it like this, and suspect it's might be simply that Factorio API added script_raised_revive event after that mod was initially created and an option to raise it - raised "optionally" to avoid breaking compatibility with mods that already send script_raised_built after entity.revive(), like this one actually does.
It's not really a bug there, but it does create incompatibility like that with any mods storing their custom data in blueprint tags, I believe.
So probably worth at least mentioning it to current mod maintainer(s), in case they might not be aware of it.
It might be done this way intentionally though, or folks there simply don't have time to update/fix and test that stuff properly.
I didn't try it, but I think if you change ReviveEntity() to use that raise_revive=true
option instead of separate script_raised_built event, it might fix the issue without breaking anything, but did not test it myself, and again ideally it's best to check with maintainer(s) whether things might be implemented like that intentionally.
(if you'll be testing such changes - runtime code in unpacked mods reloads when loading a save btw, so kinda easy to tweak and test stuff quickly without restarting whole game or anything like that)
Oh, one simple implication of the problem above, is that stuff that you copy into blueprints from the lab is actually all stored correctly, it's only placing blueprints into the lab that is done in a way that's guaranteed to loose this kind of data.
On top of that, Moon Logic combinators have that fallback hack to also restore code from original combinator if it's still present on the map.
So now that you know this, it might not actually be much of an issue, if you mostly place blueprints outside the thing and know not to remove original entities when you copy "real" stuff into the lab.
When wanting to place some old blueprint into the lab, you can also work around the issue by placing it onto the "real" map first, letting bots build MLCs, and then copy THAT into the lab instead (e.g. via ctrl-c/ctrl-v), as that'd copy the code from "real" mlc's via unit_number value stored in the base factorio combinator settings.
Obviously it'd be nicer to not need such hacks or remember such limitations by fixing the lab-mod, but still wanted to mention in case blueprint-placing there is rarely needed, if main concern was not copying stuff out correctly, or if fixing it upstream is simply not an option (e.g. no maintainer and hard to do locally).
Thank you, I'll try those workarounds/hacks for now.
I have made a thread regarding this on "The Blueprint Designer Lab" page: https://mods.factorio.com/mod/BlueprintLab_design/discussion/61102a9b1b73abdc29639a74
I had a random idea to catch tags from on_built events for entity-ghost when ghosts are blueprinted and before lab mod revives them, and buffer them somewhere, so that they can be restored when mlc entity gets "built" in exactly same place as the ghost.
But sadly it looks like these ghosts always have nil tags, and game only passes these when building entity from a ghost, not to the ghost entities themselves, so alas such hack won't work :(
More intrusive hack along same lines can be to have this mod immediately revive the ghost "properly", store/cache tags from it, then destroy it and create new ghost in the same place, but I think this should be racy (only one mod can revive "original" ghost, and it's undefined which one it'll be), and potentially wreak havoc on other mods' behavior which don't expect such weird shenanigans, somewhat like what happens to this mod in the "lab environment", so probably best not to do that...