Package: reticulator
ReticulatorReticulator is a library for parsing Minecraft Bedrock Add-ons, and interacting with them programmatically. First you read the project into a virtual filesystem, which is represented by a nested list of classes. You can read and write to these data structures. When you are finished, you can save your changes back to the filesystem.Reticulator is built as a wrapper around the 'dpath' library, which gives you a powerful interface for interacting with the raw json, if desired.Minimal ExampleThis small example shows you a minimal example of opening a project, making some changes, and then saving the results:importreticulator# Open up a new Reticulator project. There are various ways to do this.project=reticulator.Project('./path/to/bp','./path/to/rp')bp=project.behavior_pack# Step through the project in a comfortable manor, and adjust the 'health' component.forentityinbp.entities:forcomponentinentity.components:ifcomponent.id=="minecraft:health":print(f"Adjusted Health for{entity.identifier}.")component.set_jsonpath('value',10)# When you are finished, you can save your changes back to the filesystemproject.save()DocumentationProject StructureImporting and InstallingReticulator is available onpippackage manager. In most cases, you're going to want to install usingpip install reticulator. Once installed, you can import into your project however you find convenient. To keep the documentation concise, the documentation will assume you're imported the full namespace:fromreticulatorimport*Loading your ProjectReticulator is purpose built for reading and writing Bedrock add-ons. The bedrock wiki contains detailed information about the folderlayout of the folders. Reticulator can load projects matching this format.To load a project with both an RP and a BP, you can use theProjectclass. AProjectholds a reference to both packs, and allows classes in one side to be aware of the other. For example, an Entity has a definition file in both packs.project=Project('./path/to/bp','./path/to/rp')bp=project.behavior_packrp=project.resource_packIf you only have a single, stand-alone pack, you can open it directly usingBehaviorPackorResourcePack, which both inherit fromPack:bp=BehaviorPack('./path/to/bp')Saving your ProjectWhen you make a change in a reticulator class, it won't be saved back to the filesystem by default. Rather, it will be held in-memory, in a 'virtual filesystem'. To convert this virtual filesystem back into real files, you need tosaveyour project.Here is an example of opening a project, changing the identifier of an entity, and then saving that entity back into the filesystem:bp=BehaviorPack('./path/to/bp')entity=bp.get_entity('minecraft:snail')entity.identifier='minecraft:sirlich'bp.save()Thesave()here will put the files into the packsoutput_directory. By default, that will be the folder where the project files were read from. In other words reticulator is optimized for reading files, and saving the changes back into the same location. This makes it perfect forRegolith.If you want to change theoutput_directoryyou can do so from thePack:bp=BehaviorPack('./path/to/bp')entity=bp.get_entity('minecraft:snail')entity.identifier='minecraft:sirlich'bp.output_location='./path/to/save/to'bp.save()In the above example, you may notice that only the file containingminecraft:sirlichwill be exported. That's because regolith islazyand doesn't do more work than needed: only edited files are exported. Once again, regolith is optimized for destructive-writing into the same folder where the files were read.To force Regolith to write every file it knows about, you must call save like this:bp.save(force=True)Interacting with the Class-TreeRegolith is designed as a nested tree of classes, each class representing a 'part' of the add-on system which you may want to interact with. Here are a collection of small examples, showcasing the kind of read-calls that Reticulator supports:Print warnings for miss-matched entities:project=Project('./path/to/bp','./path/to/rp')rp=project.resource_packforentityinrp.entities:ifentity.counterpart==None:print(f"WARNING: Entity{entity.identifier}has a resource pack definition, but no behavior pack definition!")Ensure that models aren't too complex:rp=ResourcePack('./path/to/rp')formodelinentity.models:cube_count=0forboneinmodel.bones:cube_count+=len(bone.cubes)ifcube_count>200:print(f"Model{model.identifier}contains{cube_count}cubes, which is too many!")Raw JSON AccessDPATHThe class-tree is pretty cool, but sometimes it's just a bit too much boilerplate. Regolith provides a user-friendly wrapper arounddpath, which allows you to access and manipulate the json directly.In a nutshell, dpath allows you to interact with json using a string-path. For example the path:minecraft:entity/description/identifiercould be used to get the identifier of an entity. You can use*to stand in for a single 'layer' or**to stand in for an arbitrary number of layers. For example:**/identifierwould match ANY key called 'identifier' in the entity json.The following methods are available on any json based class, but I will use an 'entity' to illustrate:Get a path:data = entity.get_jsonpath('path/to/field')Set a path:entity.set_jsonpath('path/to/field', 'new value')Delete a path:entity.delete_jsonpath('path/to/field')Delete a path and return the result:data = entity.pop_jsonpath('path/to/field')Appends a value to a path (array expected):entity.append_jsonpath('path/to/array', 10)And then here is a feature complete example, which shows deleting a specific component from all entities.**is a wildcard for matching the component wherever it may be found -in components, or component groups:bp=BehaviorPack('bp')forentityinbp.entities:entity.delete_jsonpath('**/minecraft:minecraft:behavior.melee_attack')bp.save()True JSON AccessIf the DPATH methods don't please you, you're also welcome to interact with the json data directly, although this is discouraged. To do so, just access thedatafield on any json based class. For example:bp=BehaviorPack('./path/to/bp')entity=bp.get_entity('minecraft:snail')entity.data['minecraft:entity']['description']['identifier']=='minecraft:sirlich'I hope you can see why this isn't my favorite way to use Regolith.Language FilesRegolith offers strong support for language file editing. Here are a few examples:Printing out data:rp=ResourcePack('./path/to/rp')forlanguage_fileinrp.language_files:fortranslationinlanguage_files:print(f"Translation: key='{translation.key}', value='{translation.value}')Auto-translating entity names with place-holder:rp=ResourcePack('./path/to/rp')en_us=rp.get_language_file('texts/en_US')forentityinrp.entities:en_us.add_translation(Translation(f"item.spawn_egg.entity.{entity.identifier}.name",f"PLACEHOLDER NAME:{entity.identifier}","# TODO"))rp.save()FunctionsWe support functions, I just didn't document it yet