Another attempt at Python for Godot 4.x

The longer I think about it the more I’m convinced that for proper Python support a tight integration with Godot is required:

  1. as a module.
  2. Python version: ideally 3.14+ with GIL disabled
  3. Custom GC behaviour and tunning or even custom GC like one for GO (but 3.14 GC might be enough)
  4. Memory hooks connected to Godot’s memory allocators (profiling etc.)
  5. Unified refcounting so cycles across Godot-Python boundary can be cleared and Godot can use Python’s objects safely
    Cycles across boundary could also be prevented by manually in/decrementing every time it’s necessary but I think this is the reason why touilleMan/godot-python never got ready for 4.X
    GIL-less python extends standard refcounting to two counters - one for owner thread and second one for other threads.
  6. Pybind-11 for minimal binding and LibCST for boilerplate generation but also for scanning and transforming user code, with opt-out, to automagically replace known performance killers and/or give warnings
  7. buffer structures to limit costly copying

I want to do this with integrating StateCharts into the engine in mind:
I think that StateCharts are a must for working with complex UIs that* utilize a lot of microbehaviours.

  • I would like to also use them in Python and proper StateCharts will require hooking into Godot’s main loop. (timer event spawning after scene node process loop)
  • I think humans require graph/transition-centric definition to work with StateCharts efficiently but natural way of executing it requires node-centric definition.
  • I want to emulate how Xstate is doing this for JS.
  • Machine consists of:
    ** state/transition/action/guards definition
    ** action/guard calls mapping
    ** active state reference for each level (including parallel) and context
  • Machine reacts to events that are propagated through state layers until one of the states reacts to it or will get ignored by every state
  • It might be really beneficial to automagically replace simple guards in Python with native equivalents that will be hotswapped back to Python calls if original object will be replaced.
  1. (node-centric paradigm) Native runtime executing StateChart from it’s transition definition compiled into continuous memory block based on relative offsets (shared); another memory block of hot-swappable action, guard call mappings (per machine) and mutable current machine state definition - active states for each machine level and context.
    Blocks might be possibly created with flatbuffer.
  2. (graph/transition-centric paradigm) User layer with machine definition in Python.
  3. (graph/transition-centric paradigm) Graphical graph tool for visualization and editing that uses LibCST to read and write machine definition in Python so we keep SSOT.
    Maybe it is beneficial to always go via native flatbuffer form so other languages can benefit from this functionality.
  4. Parts of graph tool can be reused for RT visualization and debugging
  5. LibCST could be used to replace simple Python calls

There’s already a State Chart Plugin.

Have you looked at Godot 4.6 and the revamp of GDExtension? They’re pioneering it with C# but it’s intended for all languages to integrate more easily with the Godot Engine.

1 Like

I’ve seen that 4.4 or 4.5 removed requirement to call function after object constructor that was a biggest problem during my previous attempt.
Thanks for this suggestion I’ll take a look.

I’ve seen this one and other SM related plugins (as well as played with creating one using nodes myself) but I think defining schema with nodes is a suboptimal middle ground.
I also expect nodes to impact performance too much when I want to use SC everywhere and potentially for simple predictable AI

Perhaps. And if you’re doing this just for you that’s fine. Keep in mind that Godot has certain ways of doing things - and that’s through Nodes and Resources. I talk about it here in this post from yesterday when talking about componentization:

Point is, that your choices will impact anyone else who decides to use your python implementation. Ultimately, things not elevated to the node level are at least slightly obfuscated.

How come? Performance-wise, nodes are very small objects. Python is an interpreted language. Any performance issues you have are going to be related to that fact. And the solution is going to be to switch to a compiled language.

Performance issues that I’ve seen on these boards arise when there are tens of thousands of objects on the screen at a time. Most people hit video card issues before processor performance issues with Godot.

Again, whatever works for you, but it sounds to me like you’re trying to future proof a problem that you think might exist without doing any testing to see if it does exist.

Ultimately, it’s your project. Do what makes you happy.

  1. I want to get runtime general SC performance close to maximum so performance impact is not a consideration in any scenario when it is sensible to use them.
  2. Of course there will be “MachineInstance” node but anything related to defining schema should not relay on nodes as this is just typing keywords and setting properties with extra clicking that directly mimics code workflow and on the other side is too complex for non technicals that will rely on visual graph tool.
  3. Binary schema is a resource; code schema defined is also a resource (or even resource view);
  4. Considering binary schema the main format and supporting auto updating everything that is able to read and write that we can even get and amazing property of being able to modify schema in any language we would like, every change can be with different one, they will automatically sync from binary format and even graph will be in sync