Skip to main content

ggraft

GLSL Graft is a cli tool allowing declarative injection patches for vanilla Minecraft shaders.

Overriding a core shader means copying Mojang's file and changing one line. Do that across twenty shaders and every Minecraft update becomes twenty manual re-copies, with no record of what you actually changed. ggraft keeps the edit under version control instead of the result: you declare which line to change, and it re-derives the overrides from any version on demand.

Terminology

Concept Name
The vanilla GLSL going in base
A file declaring edits patch
Which shader it's for target
Where in the source injection point (or anchor)
One edit inside a patch injection
What it does operation: insert, replace, wrap, declare, function
Anchor didn't match unresolved injection
Anchor matched N times ambiguous injection
GLSL coming out patched shader

Install

From PyPI

pip install ggraft

From source

Needs Python 3.11+ and Click.

pip install -e .

Quick start

ggraft init 26.3      # write ggraft.toml
ggraft pull           # fetch bases from misode/mcmeta
ggraft build          # apply patches -> patched shaders

Project configuration

ggraft.toml, found by walking up from the working directory. Paths resolve relative to it.

[base]
version = "26.3"                      # Minecraft version to pull
dir     = ".ggraft/base"              # where bases land (gitignore this)
source  = "assets/minecraft/shaders"  # subtree to pull, stripped from local paths
repo    = "misode/mcmeta"             # optional

[patches]
dir = "patches"

[output]
dir    = "assets/minecraft/shaders"   # required
header = true                         # stamp a provenance comment

Targets are relative to base.source: core/terrain.vsh reads from .ggraft/base/core/terrain.vsh and writes to <output.dir>/core/terrain.vsh.

Patches

name    = "iso-world"
targets = ["core/terrain.vsh", "core/rendertype_*.vsh"]
exclude = ["core/rendertype_lines.vsh"]

[[injection]]
op      = "declare"
include = "foo:iso.glsl"

[[injection]]
op    = "replace"
match = "gl_Position = ProjMat * ModelViewMat * vec4({expr}, 1.0);"
with  = "gl_Position = iso_position(ProjMat, ModelViewMat, vec4({expr}, 1.0));"

Injections apply in order, to every matching target. Patches stack in filename order.

Anchors

Key Meaning
match the anchor pattern
regex = true treat match as a regex, with named groups
multiline = true let one {name} (or a regex .) span lines (false by default)
occurrence = N take the Nth match (1-based)
every = true apply to all matches

occurrence and every cannot both be set. multiline is only about letting a single {name} (or a regex .) cross one. A capture is non-greedy and must match at least one character, so vec4({expr}) does not match vec4().

Under regex = true the pattern is used as written, with ^ and $ matching at every line boundary, and its named groups (?P<name>...) fill the same {name} placeholders. In the replacement, a {name} that captured nothing is left exactly as it is, so GLSL braces and unrelated placeholders survive untouched.

No match is an unresolved injection, and so is an occurrence past the number of matches. More than one match without occurrence or every is an ambiguous injection, reported with the line of every match. Both name the patch, the injection index and the target, and both fail the build.

Operations

Operation Keys Effect
replace match, with swap the anchored text
insert match, text, where (before/after, after by default) add text beside it
wrap match, prefix and/or suffix surround it, keeping it
declare include or text add a top-level declaration; no anchor
function function, text, at (HEAD/TAIL/RETURN/INVOKE/BEFORE/AFTER, HEAD by default), occurrence, and call plus where for INVOKE add code in or beside a function; no anchor

replace, insert and wrap are the anchored operations, so they also take regex, multiline, occurrence and every from Anchors. declare and function find their own place in the file and take no anchor.

declare writes the directive the version calls for, and inserts after the last import at conditional-nesting depth zero, falling back to #extension then #version, and is idempotent. Appending after the last #include in the file would strand it inside an #ifdef branch that some shader variants skip.

Function injections

function targets a function body rather than a piece of text, the way a regular mixin does. at names a structural point, not an anchor pattern.

[[injection]]
op       = "function"
function = "main"
at       = "HEAD"           # HEAD by default
text     = "iso_setup();"
Point Where the text lands
HEAD first statement of the body
TAIL last statement, before the closing brace
RETURN before every return, nested ones included
INVOKE around every statement that calls call
BEFORE above the definition, at top level
AFTER below the definition, at top level

Text lands on a line of its own, indented like its neighbours. Multi-line text is indented line by line and keeps its blank lines; a one-line body stays on one line. Nothing is substituted, so {x} stays {x}.

INVOKE needs call, the function being called, and takes where (before by default, or after):

[[injection]]
op       = "function"
function = "main"
at       = "INVOKE"
call     = "fog_distance"
where    = "after"
text     = "iso_note_fog();"

It wraps the whole statement holding the call, never the expression inside it, and hits every call in the body. for one call in particular, use insert with a match anchor. A call that opens a block, like if (helper(x)) {, has no statement to follow: where = "after" is unresolved there, before always works.

Prototypes, calls and commented-out code are not definitions. An overloaded name is an ambiguous injection until occurrence = N picks one, and RETURN on a function that never returns is an unresolved injection, use TAIL insteaad.

Commands

Command Purpose
ggraft pull [version] fetch bases; --force refetches, -j sets parallelism. Pulling another version replaces the base
ggraft build apply patches; --check dry run, -t filters by glob
ggraft targets show which patches hit which targets
ggraft init [version] write a starter config; --force overwrites

-h/--help works on the group and every command, -V/--version prints the version, and -c/--config points at a specific ggraft.toml instead of searching upwards. pull --token also reads GITHUB_TOKEN.

Build guarantees

  • --check dry run without output writes.
  • A target pattern matching no base file fails the build. What a patch orphaned by a Minecraft update looks like.
  • A patch applying to nothing fails the build, so a mistake in targets or exclude cannot disable a patch silently.
  • Stale output is pruned. Shaders ggraft wrote are recorded in .ggraft/manifest.json, and a target dropped from a patch has its override deleted on the next build. Only manifest entries are ever deleted; -t disables pruning, since a partial build cannot tell what is stale.

Bases

Uses misode/mcmeta. Tree walk might be rate limited. Set GITHUB_TOKEN or pass --token to bypass the limit.

Tests

python -m unittest discover -s tests   # everything
python -m unittest tests.test_cli      # one module

One module per layer: test_structure, test_glsl, test_anchor, test_operations, test_engine, test_sources, test_cli. Shared shaders, project files and a network-free mcmeta client live in tests/fixtures.py.

MOJ_IMPORT support

Versions before 26.3 were using the #moj_import directive instead of #include. Setting in ggraft.toml the target version strictly below 26.3 will automatically convert behavior of declare operator to output the #moj_import directive.

Planned Features

  • python module API

Metadata

Release files for ggraft 1.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ggraft 1.1.1
File Size Uploaded Provenance Actions
ggraft-1.1.1.tar.gz 40.8 kB With provenance Details for ggraft-1.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for ggraft 1.1.1
File Interpreter ABI Platform Actions
ggraft-1.1.1-py3-none-any.whl Python 3 none any Details for ggraft-1.1.1-py3-none-any.whl

Total release size: 68.3 kB

Release files / ggraft-1.1.1.tar.gz

Download URL ggraft-1.1.1.tar.gz
Size 40.8 kB
Tags Source
SHA-256 checksum
How to use checksums
1369f850dd43643b8498e68dacc860169cdae5ac80cea286a589073b528fb46a
BLAKE2b-256 checksum
How to use checksums
23f9269b8e06e65073b156e6af4258ca2e0b07854dfd8dc6b6e4b12cecc55a0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Release files / ggraft-1.1.1-py3-none-any.whl

Download URL ggraft-1.1.1-py3-none-any.whl
Size 27.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fe4f528ed4868272a0d0a38958b60bf80997f18bf1659f0613fdbccf52a87d0f
BLAKE2b-256 checksum
How to use checksums
be26521d17226ae75626b0d982defda221c04252d41397fb8fcbc2a78a44fde8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

Security for ggraft 1.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ggraft-1.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page