Python library for parsing Godot scene files

Overview

Godot Parser

Build Status Coverage Status Downloads

This is a python library for parsing Godot scene (.tscn) and resource (.tres) files. It's intended to make it easier to automate certain aspects of editing scene files or resources in Godot.

High-level API

godot_parser has roughly two levels of API. The low-level API has no Godot-specific logic and is just a dumb wrapper for the file format.

The high-level API has a bit of application logic on top to mirror Godot functionality and make it easier to perform certain tasks. Let's look at an example by creating a new scene file for a Player:

  from godot_parser import GDScene, Node

  scene = GDScene()
  res = scene.add_ext_resource("res://PlayerSprite.png", "PackedScene")
  with scene.use_tree() as tree:
      tree.root = Node("Player", type="KinematicBody2D")
      tree.root.add_child(
          Node(
              "Sprite",
              type="Sprite",
              properties={"texture": res.reference},
          )
      )
  scene.write("Player.tscn")

It's much easier to use the high-level API when it's available, but it doesn't cover everything. Note that use_tree() does support inherited scenes, and will generally function as expected (e.g. nodes on the parent scene will be available, and making edits will properly override fields in the child scene). There is no support yet for changing the inheritence of a scene.

Low-level API

Let's look at creating that same Player scene with the low-level API:

  from godot_parser import GDFile, ExtResource, GDSection, GDSectionHeader

  scene = GDFile(
      GDSection(GDSectionHeader("gd_scene", load_steps=2, format=2))
  )
  scene.add_section(
      GDSection(GDSectionHeader("ext_resource", path="res://PlayerSprite.png", type="PackedScene", id=1))
  )
  scene.add_section(
      GDSection(GDSectionHeader("node", name="Player", type="KinematicBody2D"))
  )
  scene.add_section(
      GDSection(
          GDSectionHeader("node", name="Sprite", type="Sprite", parent="."),
          texture=ExtResource(1)
      )
  )
  scene.write("Player.tscn")

You can see that this requires you to manage more of the application logic yourself, such as resource IDs and node structure, but it can be used to create any kind of TSCN file.

More Examples

Here are some more examples of how you can use this library.

Find all scenes in your project with a "Sensor" node and change the collision_layer:

  import os
  import sys
  from godot_parser import load

  def main(project):
      for root, _dirs, files in os.walk(project):
          for file in files:
              if os.path.splitext(file)[1] == '.tscn':
                  update_collision_layer(os.path.join(root, file))

  def update_collision_layer(filepath):
      scene = load(filepath)
      updated = False
      with scene.use_tree() as tree:
          sensor = tree.get_node('Sensor')
          if sensor is not None:
              sensor['collision_layer'] = 5
              updated = True

      if updated:
          scene.write(filepath)

  main(sys.argv[1])

Caveats

This was written with the help of the Godot TSCN docs, but it's still mostly based on visual inspection of the Godot files I'm working on. If you find a situation godot_parser doesn't handle or a feature it doesn't support, file an issue with the scene file and an explanation of the desired behavior. If you want to dig in and submit a pull request, so much the better!

If you want to run a quick sanity check for this tool, you can use the test_parse_files.py script. Pass in your root Godot directory and it will verify that it can correctly parse and re-serialize all scene and resource files in your project.

Comments
  • Add groups to node section

    Add groups to node section

    Hi!

    First of all thank you so much for making this project! It's so far saved me quite a bit of time on my own Godot project.

    However, my project requires that the nodes that I'm generating have groups (this is normally done in Godot by manually assigning a node to a group in the inspector). It looks like groups was on your todo-list based on the code comments, so I tried my best to implement them. Implementing these changes locally has allowed me to export nodes with groups.

    I'm relatively new to Python development and I wasn't sure how to test my changes, so please let me know if this is an acceptable change, and if it's not I would appreciate some direction to make it so.

    opened by richard-gebbia 3
  • Add py.typed for typing

    Add py.typed for typing

    As is described in https://peps.python.org/pep-0561/#packaging-type-information

    It is possible some other change is needed to make sure it gets bundled properly

    opened by micimize 1
  • Missing: Does not handle escaped strings in .tscn files

    Missing: Does not handle escaped strings in .tscn files

    I have a pseudo-language that I use in some props of my scene objects, and godot-parser doesn't like escaped strings.

    Example from test_parse_files.py on my project: condition = "has_ability(\"djump\")" <x> condition = "has_ability("djump")"

    Note that I didn't add the escaping. I just typed has_ability("djump") in the editor, and apparently on save it escapes it.

    opened by ChainedLupine 1
  • Binary parent scenes cause exception

    Binary parent scenes cause exception

    This one is pretty obvious, but perhaps something to wrap in a friendly "I can't do that Dave" style exception.

    I have a .tscn file (text-based) which inherits from a .scn file (binary).

    When I open the .tscn file in tree mode, I get an exception.

    I suggest that until you start supporting .scn files you catch and re-raise this exception as something akin to "Binary files are not supported yet"

    Thanks

    c:\users\tom\appdata\local\programs\python\python39\lib\site-packages\godot_parser\tree.py in _load_parent_scene(root, file)
        327 
        328 def _load_parent_scene(root: Node, file: GDFile):
    --> 329     parent_file: GDFile = file.load_parent_scene()
        330     parent_tree = Tree.build(parent_file)
        331     # Transfer parent scene's children to this scene
    
    c:\users\tom\appdata\local\programs\python\python39\lib\site-packages\godot_parser\files.py in load_parent_scene(self)
        256                 "Could not find parent scene resource id(%d)" % root.instance
        257             )
    --> 258         return GDScene.load(gdpath_to_filepath(self.project_root, parent_res.path))
        259 
        260     @contextmanager
    
    c:\users\tom\appdata\local\programs\python\python39\lib\site-packages\godot_parser\files.py in load(cls, filepath)
        306     def load(cls: Type[GDFileType], filepath: str) -> GDFileType:
        307         with open(filepath, "r") as ifile:
    --> 308             file = cls.parse(ifile.read())
        309         file.project_root = find_project_root(filepath)
        310         return file
    
    c:\users\tom\appdata\local\programs\python\python39\lib\encodings\cp1252.py in decode(self, input, final)
         21 class IncrementalDecoder(codecs.IncrementalDecoder):
         22     def decode(self, input, final=False):
    ---> 23         return codecs.charmap_decode(input,self.errors,decoding_table)[0]
         24 
         25 class StreamWriter(Codec,codecs.StreamWriter):
    
    UnicodeDecodeError: 'charmap' codec can't decode byte 0x81 in position 1848: character maps to <undefined>
    
    
    opened by tom-leys 1
  • Supports trailing commas in list definitions.

    Supports trailing commas in list definitions.

    Apparently, lists can include a trailing comma (at least lists in the section header).

    This is an example, in the section header:

    [node name="Pit Water Trigger" type="Area2D" parent="Scene/Trial1/Acid Pit" groups=[
    "WaterBody",
    ]]
    visible = false
    position = Vector2( -8.50171, -130.333 )
    collision_layer = 256
    collision_mask = 2147483649
    script = ExtResource( 20 )
    __meta__ = {
    "_edit_group_": true
    }
    

    Note how the groups attribute is a list which contains newlines and a trailing separator (a comma). Newlines are not a problem for the parser but the comma was not supported.

    This pull request modifies the parser definition to add support for trailing commas in list values.

    opened by jjmontesl 1
  • Supports quoted keys in properties

    Supports quoted keys in properties

    Some properties can have spaces in their names. In this case Godot quotes them.

    This is an example of such situation:

    [node name="AnimationPlayer" type="AnimationPlayer" parent="Scene/TestDevel/Zone1/TestAnim"]
    autoplay = "Test"
    playback_process_mode = 0
    anims/Test = SubResource( 66 )
    "anims/Test 2" = SubResource( 67 )
    

    This pull request modifies the parser definition to add support quoted keys.

    opened by jjmontesl 1
  • Parsing large scene is slow.

    Parsing large scene is slow.

    Parsing small scenes works really well.

    And, I have this 50k lines, 67mb scene file converted from an .escn file imported from blender. I wanted to shrink its size from the source. I wrote some regex-based scripts and it was neither elegant nor convinent, then I found this parser.

    However, It took 11 minutes to parse this 67mb scene. RAM usage increased very slowly and the parsing was using only 6% CPU usage of a Ryzen 2700X.

    67mb lines is definitely large, but I think 11 minutes is a bit much. Is it a normal speed with pyparsing or does the script need deeper optimization idk.

    City.zip

    opened by rambda 1
Owner
Steven Arcangeli
Steven Arcangeli
Python library for creating and parsing HSReplay XML files

python-hsreplay A python module for HSReplay support. https://hearthsim.info/hsreplay/ Installation The library is available on PyPI. pip install hsre

HearthSim 45 Mar 28, 2022
App and Python library for parsing, writing, and validation of the STAND013 file format.

python-stand013 python-stand013 is a Python app and library for parsing, writing, and validation of the STAND013 file format. Features The following i

Oda 3 Nov 9, 2022
Age of Empires II recorded game parsing and summarization in Python 3.

mgz Age of Empires II recorded game parsing and summarization in Python 3. Supported Versions Age of Kings (.mgl) The Conquerors (.mgx) Userpatch 1.4

null 148 Dec 11, 2022
A quick experiment to demonstrate Metamath formula parsing, where the grammar is embedded in a few additional 'syntax axioms'.

Warning: Hacked-up code ahead. (But it seems to work...) What it does This demonstrates an idea which I posted about several times on the Metamath mai

Marnix Klooster 1 Oct 21, 2021
Simple web application, which has a single endpoint, dedicated to annotation parsing and convertion.

Simple web application, which has a single endpoint, dedicated to annotation parsing and conversion.

Pavel Paranin 1 Nov 1, 2021
Automates the fixing of problems reported by yamllint by parsing its output

yamlfixer yamlfixer automates the fixing of problems reported by yamllint by parsing its output. Usage This software automatically fixes some errors a

OPT Nouvelle Caledonie 26 Dec 26, 2022
A visidata plugin for parsing f5 ltm/gtm/audit logs

F5 Log Visidata Plugin This plugin supports the default log format for: /var/log/ltm* /var/log/gtm* /var/log/apm* /var/log/audit* It extracts common l

James Deucker 1 Jan 6, 2022
PSP (Python Starter Package) is meant for those who want to start coding in python but are new to the coding scene.

Python Starter Package PSP (Python Starter Package) is meant for those who want to start coding in python, but are new to the coding scene. We include

Giter/ 1 Nov 20, 2021
Addon to give a keybind to automatically enable contact shadows on all lights in a scene

3-2-1 Contact(Shadow) An easy way to let you enable contact shadows on all your lights, because Blender doesn't enable it by default, and doesn't give

TDV Alinsa 3 Feb 2, 2022
Starscape is a Blender add-on for adding stars to the background of a scene.

Starscape Starscape is a Blender add-on for adding stars to the background of a scene. Features The add-on provides the following features: Procedural

Marco Rossini 5 Jun 24, 2022
Blender Add-on to Add Metal Materials to Your Scene

Blender QMM (Quick Metal Materials) Blender Addon to Add Metal Materials to Your Scene Installation Download the latest ZIP from Releases. Usage This

Don Schnitzius 27 Dec 26, 2022
Python library to natively send files to Trash (or Recycle bin) on all platforms.

Send2Trash -- Send files to trash on all platforms Send2Trash is a small package that sends files to the Trash (or Recycle Bin) natively and on all pl

Andrew Senetar 224 Jan 4, 2023
JLC2KICAD_lib is a python script that generate a component library for KiCad from the JLCPCB/easyEDA library.

JLC2KiCad_lib is a python script that generate a component library (schematic, footprint and 3D model) for KiCad from the JLCPCB/easyEDA library. This script requires Python 3.6 or higher.

Nicolas Toussaint 73 Dec 26, 2022
K2HASH Python library - NoSQL Key Value Store(KVS) library

k2hash_python Overview k2hash_python is an official python driver for k2hash. Install Firstly you must install the k2hash shared library: curl -o- htt

Yahoo! JAPAN 3 Oct 19, 2022
List of short Codeforces problems with a statement of 1000 characters or less. Python script and data files included.

Shortest problems on Codeforces List of Codeforces problems with a short problem statement of 1000 characters or less. Sorted for each rating level. B

null 32 Dec 24, 2022
These are After Effects and Python files that were made in the process of creating the video for the contest.

spirograph These are After Effects and Python files that were made in the process of creating the video for the contest. In the python file you can qu

null 91 Dec 7, 2022
python scripts and other files to generate induction encoder PCBs in Kicad

induction_encoder python scripts and other files to generate induction encoder PCBs in Kicad Targeting the Renesas IPS2200 encoder chips.

Taylor Alexander 8 Feb 16, 2022
This Python script can enumerate all URLs present in robots.txt files, and test whether they can be accessed or not.

Robots.txt tester With this script, you can enumerate all URLs present in robots.txt files, and test whether you can access them or not. Setup Clone t

Podalirius 32 Oct 10, 2022
Python plugin/extra to load data files from an external source (such as AWS S3) to a local directory

Data Loader Plugin - Python Table of Content (ToC) Data Loader Plugin - Python Table of Content (ToC) Overview References Python module Python virtual

Cloud Helpers 2 Jan 10, 2022