geekrelief / gdnim

godot-nim based bootstrapping framework supporting hot reloading
Other
55 stars 4 forks source link
godot godot-nim nim

Gdnim

gdnim is a testbed for experimental features for godot-nim projects that implements hot reloading of dlls as well as features for ease of development.

NOTE A new macro gdnim was added to replace gdobj for defining classes. See the gdobj branch for the old samples.

Why?

The goal is to streamline and speed up the process of development for godot-nim by adding experimental features like:

Quick Setup Guide

For a new project (sample code removed), branching from master: ./build init [branch_name]

Quick Dev Guide

Prerequisites

Component Setup

Create a component by running ./build gencomp [name] [godot_class]. name is the name of your component, and godot_class is the class from which it derives. Both should be in snake_case.

For example ./build gencomp my_node node_2d generates a my_node.nim file in /components. And a matching tscn, gdns, and gdnlib file in the directories specified in build.ini.

To delete a component run: ./build delcomp [name].

New components are generated from template strings defined in tasks.nim. Customize it for your needs. Remember to recompile ./build with nim c build with any change to tasks.nim.

Hot Reloading Setup

The gdnim macro is a layer over godot-nim's gdobj macro that interacts with the Watcher to enable hot reloading. There are four sections to consider when setting up for hot reloading.

Hot Reloading Checks

The proc isNewInstance() can be used to check the reloading state of a component instance at runtime. This is useful for initialization or adding children dynamically.

You can do conditional compilation for reloading code with when defined(does_reload):, but this currently, only works inside methods. It would require a modification to gdobj to work in general.

Hot Reloading Switch

A top level gdnim macro is implemented to replace the use of gdobj. See below for implementation details

See components for samples on how to set things up for reloading.

Tips

Sample Projects

Project Structure

Gdnim uses a customized build script and godot engine 3.x which unloads gdnative libraries when their resource is no longer referenced. It removes the dependency on nake and nimscript, but requires you to recompile build.nim if you change build.nim or tasks.nim. Gdnim also uses a custom version of the godot-nim bindings in the deps/godot directory.

Files and Folders

Project Setup

Modify the build.ini, build.nim and tasks.nim script for your needs. build.ini expects some paths the godot engine repo and editor executables.

If you're not interested in building godot from source, you can use Godot 3.x and ignore the ./build gdengine command. If you want to use ./build gd or ./build play to launch the editor or app fill out the paths to your editor binaries.

The rest of the settings under build.ini's [Godot] section are for building godot from source. If you have all my mods from build.ini's merge_branches in your git repo you can, run ./build gdengine update. Otherwise stick to using godot engine 3.x.

The app folder contains the godot project. You can use ./build init branch_name to create a new gdnim project on a new branch.

You create "components" which are the classes that can reload by running the ./build gencomp your_module_name godot_base_class_name. A nim file will appear in the components folder. Generated files are stored in app/_dlls, app/_gdns, app/_tscn.

See the examples in the components folder.

See ./build help for available tasks like downloading the godot source, compiling the engine, generating godot-nim bindings api, compiling the watcher and components, etc.

./build gd Launches the godot editor. On Windows it spawns a terminal using Terminal. On Linux there isn't a general way to support launching the editor from a terminal for all distributions (as far as I know), so modify the task gd for your system.

Tasks

xxx The build system consists of build.nim, tasks.nim, build.nim.cfg and build.ini. build.nim includes tasks.nim and reads build.ini at runtime. To compile the build system run: nim c build.

Tasks are defined in tasks.nim. You can customize it for your needs, just make sure to recompile. Changes to build.ini are picked up when ./build executes. Find out what tasks are available by inspecting that file or running ./build help

By default running ./build will build any components that have changed. If you supply an argument with no task name: ./build my_comp the argument is assumed to be a component.

Implementation details

Watcher monitors the app/_dlls folder for updates and coordinates the reload process with the components. The components use the hot module save and load macros to persist data with Watcher.

NOTE Below details how to hot reloading works when using the gdobj macro. Using the gdnim macro reduces the boilerplate for the setup, and does all this stuff for you. See the gdobj branch components for samples.

To set up a component for reloading, the component needs to:

When a component is compiled it generates a library file (safe dll). If the godot editor is not in focus with the project opened the safe dll can be copied to the hot dll path. Otherwise, you'll get a warning that the dll can't be moved and reload will fail.

When the project application is running, update and build the components. Watcher will check if safe dll is newer than hot dll and start a reload if so.

Nim notes

The godot-nim library in deps has been customized to use the new gc:ORC and prep it for future versions of nim. This is why ORC is suitable for games: https://nim-lang.org/blog/2020/12/08/introducing-orc.html Use the build script to generate the godotapi into the deps folder. Gdnim, and the godot-nim bindings are tested against the nim devel branch 3b963a81.

Avoid the useMalloc option with ORC. It'll eventually cause a crash.

Compiler notes

License

This project is licensed under the MIT license. Read LICENSE file for details.

Copyright (c) 2018 Xored Software, Inc.

Copyright (c) 2020 Don-Duong Quach