Installing

Copy an addon folder, tick a plugin, and run the two-step check — plus the engine version, the local-development links, and why there is one repository per asset.

Every addon installs the same way, and the answer is deliberately boring: copy a folder.

Install an addon

# dot-core is required by everything. Copy it first.
cp -r dot-core/addons/dot_core       my-game/addons/
cp -r dot-timer/addons/dot_timer     my-game/addons/

Then enable each one in Project → Project Settings → Plugins.

The plugin toggle only registers inspector types

Every class is available through its class_name whether the plugin is enabled or not. Enabling it is what puts the resources in the Create Resource dialog and the nodes in Add Node.

An addon’s distributable is its addons/<name>/ folder and nothing else. Everything outside it in the repository — project.godot, examples/, fixtures/, icon.svg — exists so the addon can be opened and validated standalone, and is not copied into a consuming project.

Requires Godot 4.7 or newer

The collection was built on 4.4 and now targets 4.7.2. Every project.godot declares config/features=PackedStringArray("4.7"), dotserve and the server tool’s setup.sh both refuse anything older, and the container pins 4.7.2-stable with its checksum.

The move cost nothing in GDScript: every documented breaking change across 4.5, 4.6 and 4.7 — JSONRPC.set_scope, Node.get_rpc_config, the removed RenderingServer interpolation methods, AudioEffectSpectrumAnalyzer.tap_back_pos, the OpenXR and TextServerExtension signatures — occurs nowhere in these repositories.

One repository per asset

Each asset is its own Git repository and is cloned individually. There is no parent repository, so there is no way to clone the whole collection at once.

git clone https://github.com/modcommunity/dot-core
git clone https://github.com/modcommunity/dot-timer

That is a consequence of how they are consumed: somebody who wants a speedrun timer should be able to take dot-timer and dot-core and nothing else.

Working on an addon locally

Every project except dot-core needs dot-core present just to open, and the games need every addon they use. Those links are gitignored, because a consumer copies the folders instead — and a fresh clone therefore does not open until something has made them.

Do not make them by hand. dot-bootstrap clones every repository and links every addon each project needs:

git clone https://github.com/modcommunity/dot-bootstrap.git
cd dot-bootstrap
./bootstrap.sh              # clone what is missing, pull what is not, link everything
./bootstrap.sh --links      # only redo the links
./bootstrap.sh --status     # branch, dirty, unpushed, per repository
./bootstrap.sh --check      # does projects.tsv still agree with the disk?
./bootstrap.sh --content-keys   # a content signing key, so this machine can publish packs

It contains no list of projects and no list of what each one needs: the projects come from projects.tsv, and a project’s addons are read out of that project’s own .gitignore, from its /addons/<name> lines — the one place that cannot go stale, because the repository that gains a dependency is the repository that has to ignore the link. The version before it carried four manifests and they had drifted to 19 of the 33 repositories the collection had at the time; one run now links getting on for three hundred folders across fifty-eight of them.

--content-keys is the one mode that is not about repositories: dot-cloud refuses an unsigned manifest, so publishing a pack needs a private key, and a private key is the one thing a clone can never carry. It mints one and points dot-server-deploy/client/content.json at it — which leaves that committed file locally modified, on purpose, and is why it is opt-in.

Getting started is the whole walk-through, including what Windows does instead (it copies the folders, because Godot does not follow a junction).

dot-serve needs none of this — it is a shell script and finds a Godot binary at run time.

The two-step check

There is no CI. Every project has the same two-step check and both steps matter.

cd <project>

# 1. Every script parses.
godot --headless --path . --import
find . -name '*.gd' -not -path './.godot/*' | while read f; do
    godot --headless --path . --check-only --script "res://${f#./}"
done

# 2. It actually works.
godot --headless --path . res://examples/<the example>.tscn

Step 2 is the one people skip, and it is the one that finds things. Parse-clean GDScript can still be wrong in ways only execution shows: an interest cache that dropped what an observer owned, a render timeline that never moved between packets, a Dictionary handed out by reference so every “copy” shared one object. Each self-test example exits non-zero on failure, so they work as smoke tests unchanged.

Cap every headless run with `timeout`

A scene whose script fails to parse hangs rather than failing, and so does a scene with no script at all. Four example scenes are deliberately not self-tests and never terminate on their own.

Three targets, one codebase

Desktop (Windows, macOS, Linux), mobile (Android, iOS) and the browser. The browser is where the constraints live, and they are encoded in the code rather than left to the reader — see Conventions.

The quickest way to find out what a build can actually do:

godot --headless --path . res://examples/capability_report.tscn

Run it on every target you ship to. It is how you discover that your web export has no threads, or that a device reports no storage quota — both of which change how dot-cloud behaves.