How can I mass delete a specific item from all BPs in a book? [solved]

Don't know how to use a machine? Looking for efficient setups? Stuck in a mission?
wild_dog
Inserter
Inserter
Posts: 30
Joined: Tue Apr 19, 2016 8:07 pm
Contact:

How can I mass delete a specific item from all BPs in a book? [solved]

Post by wild_dog »

Hey everyone, I'm looking for a little help with deleting specific items/buildings from every BP in a book.

I have a railbook with a lot of different blueprints for rail sections, and I want to make variations on it. (no laser defence, no roboports, no power poles).

I've basically completed the version with all the bells and whistles, and making the variants would just be a case of 'remove all of building X' compared to that, and i was hoping that there is a way to do that to all BPs simultaneously.

I hoped I could apply a filtered deconstruction planner to a book, like you can apply a filtered upgrade planner, but that doesn't seem to be possible.

Does anyone have a siggestion for how I could acomplish this?

I was thinking maybe export to string, uncompress from whatever encoding structure is used, remove all items that I want in an editor, and re-encode to a string, but I don't know what tools are needed/available to do this.
Last edited by wild_dog on Sat Nov 02, 2024 12:52 am, edited 1 time in total.
wild_dog
Inserter
Inserter
Posts: 30
Joined: Tue Apr 19, 2016 8:07 pm
Contact:

Re: How can I mass delete a specific item from all BPs in a book?

Post by wild_dog »

In the tradition of blacksmiths; if the tools don't exist, make them.

Included is python code to do exactly as I stated: import a BP string, remove the entities specefied (names are case sensitive), fix the entity indexing due to deleted entities, and convert back to a BP string.

Code: Select all

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Nov 02 16:13:48 2024

@author: wild_dog
"""

import json
import zlib
import base64

inputString="<BP string here>"

entitiesToRemove=['laser-turret','big-electric-pole','small-lamp','substation']

version_byte = None


def recursiveDeleteEntityFindBPsInBook(BPobject):
    #if the BP object is a blueprint, call remove entity.
    #if it is a blueprint book, scan all blueprint(book)s contained in it and call this function on it.
    if isinstance(BPobject, dict):
        if "blueprint" in BPobject:
            removeEntityFromBP(BPobject["blueprint"])
            fixIndices(BPobject["blueprint"])
        elif "blueprint_book" in BPobject:
            for newBPobject in BPobject["blueprint_book"]["blueprints"]:
                recursiveDeleteEntityFindBPsInBook(newBPobject)

def removeEntityFromBP(Blueprint):
    #create remove list on first scan, remove each item on separate pass. 
    #removing an entity mid for loop causes next entity to be skipped.
    detectedEntities=[]
    for entity in Blueprint["entities"]:
        if entity["name"] in entitiesToRemove:
            detectedEntities.append(entity)
    for entity in detectedEntities:
        Blueprint["entities"].remove(entity)
    return None

def bpSortFn(dict):
    return dict['name']

def fixIndices(Blueprint):
    renumberMap = {}
    #reorder entities to enhance compresability.
    Blueprint["entities"].sort(key=bpSortFn)
    
    #fix entity list numbering and remember what what changed
    for i in range(len(Blueprint["entities"])):
        origin = Blueprint["entities"][i]["entity_number"]
        target = i+1
        Blueprint["entities"][i]["entity_number"]=target
        renumberMap[origin]=target
    #detect which entities with a wireconnection have been removed, and remove the wires connecting to it.
    detectedObsoleteWires=[]
    for wire in Blueprint["wires"]:
        if not ((wire[0] in renumberMap) and wire[2] in renumberMap):
            detectedObsoleteWires.append(wire)
    
    for wire in detectedObsoleteWires:
        Blueprint["wires"].remove(wire)
    #fix wire connections based on remembered entity renumbering
    for wire in Blueprint["wires"]:
        wire[0]=renumberMap[wire[0]]
        wire[2]=renumberMap[wire[2]]
    return None

def fromExchangeString(exchange_str):
    global version_byte
    version_byte = exchange_str[0]
    decoded = base64.b64decode(exchange_str[1:])
    json_str = zlib.decompress(decoded)
    data = json.loads(json_str)
    return data

def toExchangeStringString(BPobject):
    global version_byte
    if version_byte is None:
        raise RuntimeError("Attempted to convert to exchange string with no version_byte")
    json_str = json.dumps(BPobject, 
                          indent = 4, 
                          separators=(",", ":"),
                          ensure_ascii=False
                          ).encode("utf8")
    compressed = zlib.compress(json_str, 9)
    encoded = base64.b64encode(compressed)
    return version_byte + encoded.decode()

BlueprintObject = fromExchangeString(inputString)
recursiveDeleteEntityFindBPsInBook(BlueprintObject)
newString = toExchangeStringString(BlueprintObject)
print(newString)
EDIT: tiny update, sorting the entities in the BP list results in BP strings that are ~3-5% shorter.
Post Reply

Return to “Gameplay Help”