Changelog¶
Covers both published artifacts of this repo — the language binding
(scala-native-gdextension) and the sbt plugin (sbt-godot-scala-native);
they are always released together under one version. Versions are plain-semver
git tags built on demand by JitPack
under the group com.github.optical002.godot-scala-native. Versions are plain
semver — the topmost Unreleased section is the version being worked toward,
which sbt publishLocal produces locally and which appears on JitPack only once
its tag is pushed.
[0.1.9] — 2026-10-21¶
Memory and performance release. The Scala heap stays untouched on the engine
call path, RefCounted lifetimes are deterministic and leak-checked, and the
math builtins are a complete native port of Godot's core/math. Numbers are in
docs/guides/performance.md, measured by godot/memory_verify.gd.
Performance¶
- Engine method calls allocate nothing on the Scala heap and run 20-60x faster
(
setPosition: 832 B / 1982 ns → 0 B / 33 ns;getChild: 1072 B / 2467 ns → 0 B / 60 ns). - Each generated method caches its
MethodBindin a companion field instead of building a string map key per call. Ptrcalldispatchers areinlineand specialized per argument type at compile time (no typeclasses, no boxing).- Strings convert as UTF-16 through a stack buffer.
- Returned
StringNames are interned, so returning one allocates nothing. - GDScript → Scala
@funccalls: 2704 B / 5353 ns → 0 B / 100 ns._processdispatch: 130 B per node per frame → 0 B. The per-call virtuals and the method trampoline are@exportedfunctions (address viaresources/scala-native/gdext_trampolines.c) instead of lambdaCFuncPtrs, which box every argument. Primitive getters and setters are specialized at compile time. emitSignal,Input.singleton(and every singleton),getTreeallocate nothing. Singletons are cached.Log.tracebuilds its message only when tracing is on; registry lookups use token-indexed tables and primitive maps instead of boxedLongkeys; builtin constructor and method lookups are cached without per-call keys or closures.
Added¶
godotNativeModeplugin setting:releaseFast(default),releaseFull,debug. Projects previously always built indebug(no optimizer).Gd.scoped { ... }releases everyRefCountedreference adopted inside it;keep()takes one out of the block.gdext.api.Gc:collect(),disable()/enable(),paused { },stats(heap, total allocated bytes, collections, owned references).- Shutdown leak report: a warning naming the classes of
RefCountedreferences Scala still owns. - Full math API: all 367 methods, the operators, and the constants of
Vector2/3/4(i),Rect2(i),AABB,Plane,Quaternion,Basis,Transform2D/3D,Projection,Color(including HSV, OKHSL, HTML and the 146 named colors), plusGodotMathscalar helpers. - Static engine methods (71) are generated in the class companion, e.g.
FileAccess.fileExists,DirAccess.open. Arr,Dict,Packed*ArrayareAutoCloseable;Arr.free(),Dict.free().- igen
BuiltinGenerator: generatesBuiltinSizes,ColorNames, and the math parity harness fromextension_api.json. - Harness:
MemoryProbe+godot/memory_verify.gd(byte budgets per hot path, ownership rules);MathParityProbe+godot/math_parity_verify.gd(604 Scala-vs-engine checks over every math method, member and operator).
Changed¶
- Breaking: engine methods returning an object return the typed wrapper
(
getParent(): Node,getMesh(): Mesh,load(...): Resource) instead of a rawGodotObject. ReplaceGd.wrap[T](x.getFoo())withx.getFoo(), orx.getFoo().cast[T]to narrow. - Breaking: each engine object has exactly one wrapper (cached through
Godot instance bindings, like godot-cpp). Wrappers of objects the engine
freed throw
NullPointerExceptionon use instead of crashing. - Breaking:
Ref<T>returns andGd.createof aRefCountedare owned by the caller: release withunref()orGd.scoped.unref()destroys the object at the last reference (it previously never freed) and ignores a wrapper that owns no reference. Decoding an object from a Variant (ArrandDictelements,Object.get,Treselements) borrows instead of taking a reference. - Breaking:
Basis,Transform2D,Transform3D,Projectionare stored flat (one object instead of 4-6).Basis(row0, row1, row2)→Basis.fromRows(...);Basis(x, y, z)now takes columns (GDScript order);basis.row0→basis.row(0)/basis.xfor the X axis.Transform3D(basis, origin),Transform2D(x, y, origin),Projection(x, y, z, w)keep working as factories. - Breaking: math method signatures follow the engine API:
intarguments areLong, zero-argument methods take no parentheses.Vector3.rotatedY(a)is removed; userotated(Vector3.UP, a). Lowercasezero/identityconstants are deprecated aliases ofZERO/IDENTITY. - Exported resource, scene,
ArrandDictproperties release the value they replace and, when the instance is freed, what they still hold. BuiltinSizesis generated from the API dump.
Fixed¶
- Leaks: one Godot
Stringper engine call with aStringargument; aStringNamereference and 8 bytes of malloc perStringNamereturn; one reference perRef<T>return (load,duplicate,getMesh, ...); one reference per export setter call; the binding self-test'sDirAccess. The harness no longer ends withERROR: 3 resources still in use at exit. StringName.fromencoded with the platform default charset but measured UTF-8 bytes.GStringconversion used the platform default charset; it is now exact UTF-16.- Hand-written
FileAccess/DirAccessstatics in generated files were lost on regenerate; statics are now generated.
[0.1.8] — 2026-10-20¶
Removed¶
- Breaking: a
case classcan no longer be a Godot class. Godot objects have identity, so the structuralequals/hashCode/copya case class generates are wrong for them.RegistrationScannow fails the build, naming the file and line of every case class whose parent chain reaches an engine class, andRegister.autorejects one with a compile error. Declare these as a plainclass. Case classes that do not extend an engine class (ordinary game data) are unaffected.
Added¶
- Documentation site on GitHub Pages (MkDocs Material,
docs/): getting started, guides, sbt plugin reference, troubleshooting, and the binding's Scaladoc under/api/. Deployed by.github/workflows/docs.yml.
[0.1.7] — 2026-09-29¶
Added¶
PackedFloat32Array,PackedInt32Array,PackedVector3Array(gdext.builtin): heap-owned packed arrays withsize/resize/apply/update/freeandToVariant/FromVariant. Element access uses thepacked_*_operator_indexinterface functions; indices are bounds-checked (IndexOutOfBoundsException), use-after-free()throws, and decoding a Variant of the wrong type throwsClassCastExceptioninstead of reading garbage.GArray.set,variantTypeAt,isNilAt,GArray.allocate()/free();GArray.getis now bounds-checked.ToVariant[StringName].Basis/Transform3Dmath:xform,*,transposed,determinant,inverse,affineInverse,identity.EngineExtras:PhysicalBoneSimulator3D.physicalBonesStartSimulation()(and a bone-name subset overload),Mesh.surfaceGetArrays,ArrayMesh.addSurfaceFromArrays, plusMeshArrayslot/format constants. The hand-written wrappers throwNullPointerExceptionon a null-host wrapper instead of segfaulting.- Harness:
SliceRagdollProbe+godot/slice_ragdoll_verify.gdcovering the above (packed-array edge cases and stress, custom-channel mesh rebuilds that keep skinning, Jolt ragdoll start/stop/subset/impulse, capturing the live ragdoll pose viamodification_processedand freezing it). The harness project now runs on Jolt Physics.
Changed¶
Ptrcalldispatchers throwIllegalStateExceptionwhen a method bind is null (wrong hash / missing method) instead of handing it to the engine.
[0.1.6] — 2026-07-18¶
Changed¶
RegistrationScanno longer emits a repo-local documentation path into the generatedGeneratedRegistrations.scalaheader. The comment pointed at a file under this repo's.claude/directory, which does not exist in consumer projects that receive the generated code.- Version scheme is now plain semver, matching
godot-scala-native-utilities. The dev version was previously a-SNAPSHOTpinned at0.1.3-SNAPSHOT, which had drifted behind the released tags.
[0.1.5] — 2026-07-12¶
Re-tag of 0.1.4 with no code changes (same commit), cut to rebuild on JitPack.
[0.1.4] — 2026-07-12¶
Released without a changelog entry at the time; reconstructed from git history.
Added¶
- Static
FileAccess/DirAccessmethods for readingres://(PCK) files.
Fixed¶
- Guard
System.getenvinLoginit so Windowsgodot_scala_initsurvives. - Scala Native Windows init NPE; multithreading-off moved into the binding.
Changed¶
- Verbose tracing is gated behind a
.scala/tracefile (default off).
[0.1.3] — 2026-07-12¶
Diagnostic release for the Windows godot_scala_init crash, tagged without a
changelog entry at the time; reconstructed from git history.
Changed¶
.scala/logis verbose by default and records the init stack trace on failure (made opt-in again in0.1.4).- The trace prefix no longer calls the Linux-only
gettid, so logging works the same on Windows, macOS and Linux. - Unconditional log markers around each init call, to locate the crash.
install-toolchain.ps1:Ensure-BoehmGctolerates empty-stringPathentries; the Boehm GC step is documented as a known Windows limitation.
[0.1.2] — 2026-07-12¶
Added¶
- The sbt watch (
~godotBuild) now also fires on the prefab/id config inputs: the id-bearing asset dirs (godotIdConfs×godotConfigDir) andgodotPrefabsTresare declared asgenConfig / fileInputs, so adding or removing an asset.conf(or editingprefabs.tres) regeneratesconfig/generated/*without touching a Scala source. Content-only edits of existing asset confs are deliberately ignored (watchOnFileInputEvent) — the running game hot-reloads those, no relink/.so swap needed.
Changed¶
- Boehm GC is now discovered from the build host's standard toolchain search
paths instead of two hard-coded absolute paths. The plugin's
nativeConfigno longer injects-I/-L/-lgc/-rpathfor a specific install prefix; it only setsGC.boehm(Scala Native emits-lgcitself). Boehm must be installed on the build host and reachable by clang/ld — via system packages, or by exportingLIBRARY_PATH/C_INCLUDE_PATH(orpkg-config) to point at a non-standard prefix (e.g. a Nix store path). Fixes the build being pinned to one machine's Nix generation. - Dev-version scheme: the local fallback version is now the plain next release
(
0.1.2, no-SNAPSHOT) —publishLocaland the eventual JitPack release share the exact coordinates, and the version doesn't exist on JitPack until tagged.
[0.1.1] — 2026-07-06¶
First JitPack release.
Changed¶
- Publishing moved from Maven Central (Sonatype) to JitPack: group is now
com.github.optical002.godot-scala-native(wasio.github.optical002); releases are cut by pushing a plain semver tag (novprefix, no CI workflow, no signing) which JitPack builds via the repo-rootjitpack.yml. - The plugin injects the JitPack resolver into consumer projects alongside the
matching binding dependency, so a consumer's
project/plugins.sbtneeds only the resolver + oneaddSbtPluginline. - API surface rework vs
0.1.0: everyday helpers live ingdext.api(Gd.printreplacesGodotPrint,Tscn/Tresre-exported);Gd[T]smart pointer andGodotClass[T]typeclass removed — game classes extend engine classes directly.
Added¶
- Exported-property support for
varconstructor params (export-comp ctor-params), comp-enum registration, editor verify addons (comp_enum_verify,hot_reload_verify), Godot logo asset in the editor addon. Arr:iterator,foreach,isEmpty;Vector3: scalar*//, unary-,dot,cross,distance,rotatedY,moveToward.
[0.1.0]¶
Historical pre-JitPack development under the io.github.optical002 group
(Maven Central wiring via sbt-ci-release; superseded by the JitPack flow).