Documentation & Travis & Refactoring
This commit is contained in:
parent
ec707ea776
commit
988b9e1f11
27 changed files with 772 additions and 123 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -5,4 +5,10 @@ nimcache/
|
|||
/godot/godotapigen
|
||||
/godot/godotapigen.exe
|
||||
|
||||
/docgen
|
||||
/docs/docbuild
|
||||
/docs/docbuild.exe
|
||||
/docs/docpublish
|
||||
/docs/docpublish.exe
|
||||
|
||||
.DS_Store
|
||||
54
.travis.yml
Normal file
54
.travis.yml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
sudo: false
|
||||
language: c
|
||||
os:
|
||||
- osx
|
||||
compiler: clang
|
||||
env:
|
||||
global:
|
||||
- secure: o+IZkRS6J7bGYkiilm10KDeCTDIVHszZ9dPvb93cdUKX/9hFVRiUjDOvNXqBdyOv43JIAsRTppym4B2pfcp9cwLoVBJNW70GKhp6ChLZm3wFdabScWKemuG2YtAUyGiSmxbVmUZ4baltnADyGFHuYWgHLjx6AKO26SqrXRLJ0I1IzGNKKNIfoyZzm7qdFDGJRnSC7L+y1pOITK7dpy60D1vat4FhOLaz2K7L8T8tZYZFK7yyxKduFUlY3cy4z3TpjxMa9/ksCiPiuPDN5xOrVIw0AtgZ2gYodZJUEnfaRzFkgcZ4OJuTAMNMalu52mVAVAoDt4yc7BnbtfsYJzfIjTeLjjWKWz8mqqp/HpxcfVnmPKxV5zeN8UhbosT8vNjwretWbQcFndzt0ohK7E8uMsnDGER7sGPEhCZCttkId96gi7Ro1awPIKCqLaMqB43Bwo48nnW0oQgrfWe1ZKtREXWI4F1F2GMbU8Jsvv0N5GnPGd4RDieBH5KIkJXcttAwRk8AUQ4U5PVkQ/OK9xm9BH2tB0vrjZ4FZWnFPapKVcPUYw9deyyXOXwSMmDpuwkJIjqi3tRyBWJG9+tUlAJw8zyFkCPeN9kzyVH/PhbacwBMdSxew24VLAWWCUhQHCP+fcjpKio8uDUFQN3javIRV4N3mHJ9v0VAF7Ixzh8AviQ=
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-3.9
|
||||
packages:
|
||||
- build-essential
|
||||
- scons
|
||||
- pkg-config
|
||||
- libx11-dev
|
||||
- libxcursor-dev
|
||||
- libasound2-dev
|
||||
- libfreetype6-dev
|
||||
- libgl1-mesa-dev
|
||||
- libglu1-mesa-dev
|
||||
- libssl-dev
|
||||
- libxinerama-dev
|
||||
- libxrandr-dev
|
||||
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install scons ; fi
|
||||
|
||||
install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export GODOT_PLATFORM=osx; else export GODOT_PLATFORM=x11; fi
|
||||
- git clone --depth=1 https://github.com/nim-lang/Nim.git
|
||||
- cd Nim
|
||||
- git checkout devel
|
||||
- sh ci/build.sh
|
||||
- export PATH=$PATH:"`pwd`/bin"
|
||||
- ./koch tools -d:release
|
||||
- nimble install -y
|
||||
- cd ..
|
||||
- git clone --depth=1 https://github.com/godotengine/godot.git godotengine
|
||||
- cd godotengine
|
||||
- scons -j 3 platform=$GODOT_PLATFORM
|
||||
- export GODOT_BIN="`pwd`/bin/godot.$GODOT_PLATFORM.tools.64"
|
||||
- cd ..
|
||||
|
||||
script:
|
||||
- set -e
|
||||
- mkdir godotapi
|
||||
- nim c -d:release -r godot/godotapigen.nim "$GODOT_BIN" godotapi
|
||||
- nimble install -y
|
||||
- nim c -c godotapi/godotall.nim
|
||||
- nim c -d:release -r docs/docpublish.nim
|
||||
159
docs/docbuild.nim
Normal file
159
docs/docbuild.nim
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# Copyright 2017 Xored Software, Inc.
|
||||
|
||||
import threadpool, os, osproc, strutils, sequtils, pegs
|
||||
|
||||
import "../godot/godotapigen.nim"
|
||||
|
||||
const dirs = ["godot"/"core", "godot"/"internal", "godot"/"nim"]
|
||||
const files = ["godot"/"godotapigen.nim", "godot"/"godotinternal.nim",
|
||||
"godot"/"godot.nim"]
|
||||
const indexFile = "docs"/"index.rst"
|
||||
const gitHubUrl = "https://github.com/pragmagic/godot-nim"
|
||||
|
||||
proc outName(outDir, file: string): string =
|
||||
outDir / file.extractFilename().changeFileExt("html")
|
||||
|
||||
template quoted(s: string): string =
|
||||
('"' & s & '"')
|
||||
|
||||
proc execOrFail(cmd: string) =
|
||||
echo "[exec] " & cmd
|
||||
let ret = execShellCmd(cmd)
|
||||
if ret != 0:
|
||||
raise newException(
|
||||
Exception, "Command quit with exit code " & $ret & ": " & cmd)
|
||||
|
||||
template withDir(dir: string, body: typed) =
|
||||
let curDir = getCurrentDir()
|
||||
setCurrentDir(dir)
|
||||
try:
|
||||
body
|
||||
finally:
|
||||
setCurrentDir(curDir)
|
||||
|
||||
iterator walkDirRec(dir: string, filter: set[PathComponent],
|
||||
extensions: openarray[string]): string =
|
||||
for file in walkDirRec(dir, filter):
|
||||
if not extensions.anyIt(file.endsWith(it)): continue
|
||||
yield file
|
||||
|
||||
proc genApiFiles(targetDir, godotBin: string) =
|
||||
let jsonFile = targetDir / "api.json"
|
||||
try:
|
||||
execOrFail(quoted(godotBin) &
|
||||
" --gdnative-generate-json-api " & quoted(jsonFile))
|
||||
except:
|
||||
# this fails unstably even if api.json is created successfully
|
||||
discard
|
||||
if not fileExists(jsonFile):
|
||||
raise newException(Exception, "Failed to generate Godot API wrappers")
|
||||
|
||||
genApi(targetDir, jsonFile)
|
||||
|
||||
writeFile(targetDir / "nim.cfg", "path=\"$projectdir/../../godot\"")
|
||||
|
||||
proc extractVersion(nimbleFile: string): string =
|
||||
let contents = readFile(nimbleFile)
|
||||
var matches: array[1, string]
|
||||
doAssert match(contents, peg"""'version' \s* '=' \s* '"' {@} '"' """, matches)
|
||||
result = matches[0]
|
||||
|
||||
proc fixupHrefs(file: string) =
|
||||
var contents = readFile(file)
|
||||
contents = contents.replacef(
|
||||
peg"""'href="' ('internal'/'core'/'nim') '/' {@} '"' """,
|
||||
"href=\"$#\"")
|
||||
if file.contains("godotapi"):
|
||||
contents = contents.replacef(
|
||||
peg"""'href="' {('godotinternal' / 'godot') '.html"'} """,
|
||||
"href=\"../$#")
|
||||
writeFile(file, contents)
|
||||
|
||||
proc getGitHash(): string =
|
||||
result = execProcess("git rev-parse HEAD")
|
||||
if not result.isNil and result.len > 0:
|
||||
result = result.replace("\L", "").replace("\r", "")
|
||||
|
||||
proc buildDocs*(outDir, godotNimDir, godotBin: string) =
|
||||
removeDir(outDir)
|
||||
createDir(outDir)
|
||||
let outDirAbs = expandFilename(outDir)
|
||||
let godotBinAbs = expandFilename(godotBin)
|
||||
|
||||
setCurrentDir(godotNimDir)
|
||||
|
||||
let godotNimVersion = extractVersion("godot.nimble")
|
||||
putEnv("godotnimversion", godotNimVersion)
|
||||
putEnv("godotnimgithub", gitHubUrl)
|
||||
let gitCommit = getGitHash()
|
||||
|
||||
var allNimFiles = newSeq[string]()
|
||||
allNimFiles.add(files)
|
||||
for dir in dirs:
|
||||
for file in walkDirRec(dir, {pcFile, pcDir}, [".nim"]):
|
||||
allNimFiles.add(file)
|
||||
for file in allNimFiles:
|
||||
spawn execOrFail(
|
||||
"nim doc -o:" & quoted(outName(outDirAbs, file)) &
|
||||
" --git.url:" & quoted(gitHubUrl) &
|
||||
" --git.commit:" & quoted(gitCommit) &
|
||||
' ' & quoted(file))
|
||||
|
||||
let godotApiFolder = outDirAbs / "godotapi"
|
||||
createDir(godotApiFolder)
|
||||
genApiFiles(godotApiFolder, godotBinAbs)
|
||||
for file in walkDirRec(godotApiFolder, {pcFile, pcDir}, [".nim"]):
|
||||
spawn execOrFail("nim doc " & quoted(file))
|
||||
|
||||
sync()
|
||||
|
||||
var gitHash: string
|
||||
withDir(godotBinAbs.parentDir()):
|
||||
gitHash = getGitHash()
|
||||
if gitHash.isNil or gitHash.len == 0:
|
||||
raise newException(Exception,
|
||||
"Godot executable is not under Git repository")
|
||||
|
||||
var indexContent = readFile(indexFile)
|
||||
var apiList = newStringOfCap(4096)
|
||||
for file in walkDirRec(godotApiFolder, {pcFile, pcDir}):
|
||||
if not file.endsWith(".html"):
|
||||
removeFile(file)
|
||||
else:
|
||||
let moduleHtml = file.extractFileName()
|
||||
let moduleName = moduleHtml.changeFileExt("")
|
||||
apiList.add("* `" & moduleName & " <godotapi/" & moduleHtml & ">`_\L")
|
||||
indexContent = indexContent.replace("$GODOTAPI_CHANGESET_HASH", gitHash).
|
||||
replace("$AUTO_GENERATED_GODOTAPI_LIST", apiList).
|
||||
replace("$GODOTNIM_GITHUB_URL", gitHubUrl)
|
||||
|
||||
let tmpRst = outDirAbs/"index.rst"
|
||||
writeFile(tmpRst, indexContent)
|
||||
try:
|
||||
execOrFail("nim rst2html -o:" & quoted(outName(outDirAbs, tmpRst)) &
|
||||
' ' & quoted(tmpRst))
|
||||
finally:
|
||||
removeFile(tmpRst)
|
||||
|
||||
for file in walkDirRec(outDir, {pcFile, pcDir}, [".html"]):
|
||||
spawn fixupHrefs(file)
|
||||
|
||||
sync()
|
||||
|
||||
when isMainModule:
|
||||
const outDir = "docgen"
|
||||
|
||||
if not fileExists("godot.nimble"):
|
||||
echo "Must be executed from godot-nim root dir"
|
||||
quit(-1)
|
||||
|
||||
let godotBin = getEnv("GODOT_BIN")
|
||||
if godotBin.len == 0:
|
||||
echo "GODOT_BIN environment variable must point to Godot executable"
|
||||
quit(-1)
|
||||
|
||||
try:
|
||||
buildDocs(outDir, getCurrentDir(), godotBin)
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
quit(-1)
|
||||
94
docs/docpublish.nim
Normal file
94
docs/docpublish.nim
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Copyright 2017 Xored Software, Inc.
|
||||
|
||||
import os, pegs, strutils
|
||||
import docbuild
|
||||
|
||||
const indexTemplate = """<html>
|
||||
<head><title>godot-nim docs index</title></head>
|
||||
<body>
|
||||
<h1>Documentation of Nim bindings for Godot Engine (<a href="https://github.com/$REPO_SLUG">GitHub</a>)</h1><br/>
|
||||
$VERSION_LIST
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
proc execOrQuit(cmd: string) =
|
||||
let ret = execShellCmd(cmd)
|
||||
if ret != 0:
|
||||
quit(ret)
|
||||
|
||||
proc walkDirRecRelative(dir: string, cb: proc (file: string), start = "") =
|
||||
for kind, path in walkDir(dir, relative = true):
|
||||
if kind in {pcFile, pcLinkToFile}:
|
||||
cb(if start.len > 0: start / path else: path)
|
||||
elif kind in {pcDir, pcLinkToDir}:
|
||||
let fullPath = if start.len > 0: start / path else: path
|
||||
walkDirRecRelative(dir / fullPath, cb, fullPath)
|
||||
|
||||
proc publish(docDir, gitHubToken, repoSlug, branch, tag, changeset: string) =
|
||||
let release = if branch == "master": branch else: tag
|
||||
let commitComment = if release == "master":
|
||||
"Update master documentation for changeset " & changeset
|
||||
else:
|
||||
"Update documentation for " & tag
|
||||
|
||||
const repoDir = "gh-pages"
|
||||
execOrQuit(
|
||||
"git clone --depth=1 --branch=gh-pages https://github.com/$#.git $#" %
|
||||
[repoSlug, repoDir])
|
||||
try:
|
||||
removeDir(repoDir/release)
|
||||
createDir(repoDir/release)
|
||||
var versionList = newStringOfCap(4096)
|
||||
for kind, path in walkDir(repoDir, relative = true):
|
||||
if kind == pcDir and not path.startsWith("."):
|
||||
versionList.add("<a href='$1/index.html'>$1</a><br/>" % path)
|
||||
let index = indexTemplate.replace("$REPO_SLUG", repoSlug).
|
||||
replace("$VERSION_LIST", versionList)
|
||||
writeFile(repoDir/"index.html", index)
|
||||
|
||||
walkDirRecRelative(docDir) do (file: string):
|
||||
createDir(parentDir(repoDir/release/file))
|
||||
copyFile(docDir/file, repoDir/release/file)
|
||||
|
||||
setCurrentDir(repoDir)
|
||||
try:
|
||||
execOrQuit("git add --all")
|
||||
execOrQuit("git commit -m \"$#\"" % commitComment)
|
||||
execOrQuit("git push -fq \"https://$#@github.com/$#.git\" gh-pages" %
|
||||
[gitHubToken, repoSlug])
|
||||
finally:
|
||||
setCurrentDir("..")
|
||||
finally:
|
||||
removeDir(repoDir)
|
||||
|
||||
when isMainModule:
|
||||
proc getEnvOrQuit(key: string): string =
|
||||
result = getEnv(key)
|
||||
if result.len == 0:
|
||||
echo "Expected environment variable: " & key
|
||||
quit(1)
|
||||
|
||||
if getEnv("TRAVIS_PULL_REQUEST").len > 0:
|
||||
quit(0)
|
||||
|
||||
let repoSlug = getEnvOrQuit("TRAVIS_REPO_SLUG")
|
||||
let branch = getEnvOrQuit("TRAVIS_BRANCH")
|
||||
let changeset = getEnvOrQuit("TRAVIS_COMMIT")
|
||||
let tag = getEnv("TRAVIS_TAG")
|
||||
|
||||
# let repoSlug = "pragmagic/godot-nim"
|
||||
# let branch = "master"
|
||||
# let changeset = "0fd0101432c1fed1004f50b035fcf74f75f004a8"
|
||||
# let tag = ""
|
||||
|
||||
let gitHubToken = getEnvOrQuit("GITHUB_TOKEN")
|
||||
|
||||
if branch != "master" and not (tag =~ peg"^ 'v' \d+ '.' \d+ '.' \d+ $"):
|
||||
quit(0)
|
||||
|
||||
let godotBin = getEnvOrQuit("GODOT_BIN")
|
||||
|
||||
const docDir = "docgen"
|
||||
buildDocs(docDir, getCurrentDir(), godotBin)
|
||||
publish(docDir, gitHubToken, repoSlug, branch, tag, changeset)
|
||||
12
docs/godotapi.rst
Normal file
12
docs/godotapi.rst
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
===============
|
||||
Godot API (Nim)
|
||||
===============
|
||||
|
||||
:Godot Git Hash: |godothash|
|
||||
|
||||
.. contents::
|
||||
|
||||
This is an auto-generated index of Godot API. It's built from Git changeset
|
||||
$GODOTAPI_CHANGESET_HASH.
|
||||
|
||||
$AUTO_GENERATED_GODOTAPI_LIST
|
||||
198
docs/index.rst
Normal file
198
docs/index.rst
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
=============================
|
||||
Nim bindings for Godot Engine
|
||||
=============================
|
||||
|
||||
:Author: Ruslan Mustakov
|
||||
:Version: |godotnimversion|
|
||||
:GitHub: `$GODOTNIM_GITHUB_URL <$GODOTNIM_GITHUB_URL>`_
|
||||
|
||||
.. contents::
|
||||
|
||||
``godot-nim`` library allows to create games on
|
||||
`Godot Engine <https://godotengine.org/>`_ with
|
||||
`Nim programming language <https://nim-lang.org/>`_. Nim is a statically typed
|
||||
language with an elegant Python-like syntax that compiles to native code.
|
||||
It is garbage-collected, but its GC supports real-time mode which this library
|
||||
makes use of. It means the GC will never run during game frames and will use
|
||||
fixed amount of frame idle time to collect garbage. This leads to no stalls
|
||||
and close to zero compromise on performance comparing to native languages with
|
||||
manual memory management.
|
||||
|
||||
If you are not familiar with Nim yet, it is recommended to go through the
|
||||
`official tutorial <https://nim-lang.org/docs/tut1.html>`_.
|
||||
|
||||
`VSCode <https://code.visualstudio.com/>`_ is the recommended editor for
|
||||
working with Nim code. It is cross-platform and has the excellent
|
||||
`nim plugin <https://marketplace.visualstudio.com/items?itemName=kosz78.nim>`_
|
||||
that supports most of the features you would expect from an IDE.
|
||||
It also has `godot-tools plugin <https://marketplace.visualstudio.com/items?itemName=geequlim.godot-tools>`_
|
||||
which adds features for editing GDScript and Godot resource files.
|
||||
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
Building Godot
|
||||
--------------
|
||||
|
||||
The library requires a not yet released Godot version 3.0, which you can
|
||||
build yourself by running the commands below (requires
|
||||
`Git <https://git-scm.com/downloads>`_,
|
||||
`Python 2.7 <https://www.python.org/downloads/>`_,
|
||||
`SCons <http://www.scons.org/>`_):
|
||||
|
||||
.. code-block:: bash
|
||||
git clone https://github.com/godotengine/godot.git
|
||||
cd godot
|
||||
scons platform=<your_platform>
|
||||
|
||||
where ``<your_platform>`` can be ``windows``, ``osx``, ``x11``. After build
|
||||
is finished, Godot binaries will be under the ``bin`` folder. More details
|
||||
about compiling Godot can be found in `Godot documentation
|
||||
<https://godot.readthedocs.io/en/stable/development/compiling/index.html>`_.
|
||||
|
||||
|
||||
Building Nim
|
||||
-------------
|
||||
|
||||
The library requires a not yet released Nim version 0.17.1, which you can
|
||||
build yourself by following instructions in the
|
||||
`Nim repository <https://github.com/nim-lang/Nim>`_. Make sure to also run
|
||||
``./koch tools -d:release`` after the steps described there to build ``nimble``
|
||||
(package manager) and ``nimsuggest`` (IDE helper tool,
|
||||
used by VSCode Nim plugin)
|
||||
|
||||
|
||||
Creating Project
|
||||
----------------
|
||||
|
||||
The fastest way to set up a Godot-Nim project is to use an existing stub:
|
||||
|
||||
.. code-block:: bash
|
||||
git clone --depth=1 https://github.com/pragmagic/godot-nim-stub.git myproject
|
||||
|
||||
(you can then delete the .git directory within to untie the project from the
|
||||
stub repository)
|
||||
|
||||
The stub contains the necessary build configuration to compile your code for
|
||||
desktop and mobile platforms, as well as a couple of very simple scenes to
|
||||
help you get started. Consult the stub's `README
|
||||
<https://github.com/pragmagic/godot-nim-stub>`_ for information about
|
||||
compiling the project.
|
||||
|
||||
|
||||
Adding Nim to Existing Project
|
||||
------------------------------
|
||||
|
||||
If you would like to use Nim in an existing project:
|
||||
|
||||
1. Copy ``nakefile.nim`` file and ``src`` directory from the stub described
|
||||
in the previous section above your Godot project folder. Adjust paths in
|
||||
build scripts (``nakefile.nim``, ``src/stub.nimble``) according to your
|
||||
own project structure.
|
||||
|
||||
2. Copy ``project/nimlib.tres`` to your Godot project folder. It is a
|
||||
GDNative library resource that contains paths to dynamic libraries
|
||||
compiled by Nim.
|
||||
|
||||
3. Add ``NimRuntime`` as an `AutoLoad singleton <https://godot.readthedocs.io/en/stable/learning/step_by_step/singletons_autoload.html>`_
|
||||
to your project. To do this, copy ``project/scripts/NimRuntime.gdns`` into
|
||||
your project and add these lines to ``project.godot``, adjusting path to
|
||||
``NimRuntime.gdns`` as necessary:
|
||||
.. code-block:: cfg
|
||||
[autoload]
|
||||
NimRuntime="*res://scripts/NimRuntime.gdns"
|
||||
|
||||
|
||||
Next Steps
|
||||
----------
|
||||
|
||||
Once you are familiarized with the build process (it's as simple as running
|
||||
``nake build`` after you are set up), it is recommended to go through
|
||||
`godotmacros <godotmacros.html>`_ and `godotnim <godotnim.html>`_ module
|
||||
documentations. They describe special macros and procedures needed to define
|
||||
or instantiate Godot objects. After you learned that, the rest is similar to
|
||||
using any Nim library. These bindings do not limit any of Nim's capabilities,
|
||||
and you can use any Nim types as fields or parameters of Godot objects and
|
||||
their procedures (but, obviously, you may not be able to export some of them
|
||||
to Godot editor or GDScript, unless you define your own converters).
|
||||
|
||||
|
||||
Modules
|
||||
=======
|
||||
|
||||
The binding library consists of three major modules:
|
||||
|
||||
* `godot <#modules-godot-module>`_ - Contains core types and macro definitions.
|
||||
You need to import this in any module that defines or makes use of Godot
|
||||
types.
|
||||
|
||||
* `godotinternal <#modules-godotinternal-module>`_ - Contains raw wrappers over
|
||||
few core types, such as ``GodotVariant``, ``GodotString``, ``GodotNodePath``,
|
||||
``GodotDictionary``, pool arrays. These are used by ``godotapigen`` and macro
|
||||
implementations, and you don't have to use them at all in your code, unless
|
||||
you want to go into low-level details for some reason. Each of those types
|
||||
needs to be destructed manually with ``deinit`` procedure.
|
||||
|
||||
* `godotapigen <godotapigen.html>`_ - Wrapper generator based on data from
|
||||
Godot's ``ClassDB``. You only need to use it as a part of the build process.
|
||||
|
||||
|
||||
godot Module
|
||||
------------
|
||||
|
||||
Contains core types and macro definitions. You need to import this in any
|
||||
module that defines or makes use of Godot types. The sumbodules below are
|
||||
exported and you don't have to import any of them directly.
|
||||
|
||||
* `godotnim <godotnim.html>`_ Defines ``NimGodotObject`` and Varaint converters
|
||||
for standard Nim types.
|
||||
* `godotmacros <godotmacros.html>`_ Defines ``gdobj`` macro for defining
|
||||
Godot objects.
|
||||
* `variants <variants.html>`_ ``Variant`` type represents a "dynamic object"
|
||||
that many Godot procedures make use of.
|
||||
* `arrays <arrays.html>`_ Defines ``Array`` of Variants.
|
||||
* `basis <basis.html>`_ Defines 3D ``Basis``.
|
||||
* `colors <colors.html>`_ Defines ARGB ``Color``.
|
||||
* `dictionaries <dictionaries.html>`_ Defines ``Variant`` -> ``Variant``
|
||||
``Dictionary``.
|
||||
* `nodepaths <nodepaths.html>`_ Defines ``NodePath`` - a path to a ``Node``.
|
||||
* `planes <planes.html>`_ Defines 3D ``Plane``.
|
||||
* `poolarrays <poolarrays.html>`_ Defines pooled arrays: ``PoolByteArray``,
|
||||
``PoolIntArray``, ``PoolRealArray``, ``PoolVector2Array``,
|
||||
``PoolVector3Array``, ``PoolColorArray``, ``PoolStringArray``.
|
||||
* `quats <quats.html>`_ Defines ``Quat`` (quaternion) describing object
|
||||
rotation in 3D space.
|
||||
* `rect2 <rect2.html>`_ Defines ``Rect2`` - a 2D rectangle.
|
||||
* `rect3 <rect3.html>`_ Defines ``Rect3`` - a 3D box.
|
||||
* `rids <rids.html>`_ Defines ``RID`` - a resource identifier.
|
||||
* `transform2d <transform2d.html>`_ Defines ``Transform2D``.
|
||||
* `transforms <transforms.html>`_ Defines ``Transform``.
|
||||
* `vector2 <vector2.html>`_ Defines ``Vector2``.
|
||||
* `vector3 <vector3.html>`_ Defines ``Vector3``.
|
||||
* `godotbase <godotbase.html>`_ Defines ``Error`` type and few common math
|
||||
procedures missing in Nim's standard library.
|
||||
|
||||
|
||||
Godot API
|
||||
---------
|
||||
|
||||
This is an auto-generated list of Godot API modules. It's built from Godot
|
||||
changeset `$GODOTAPI_CHANGESET_HASH
|
||||
<https://github.com/godotengine/godot/commit/$GODOTAPI_CHANGESET_HASH>`_.
|
||||
|
||||
$AUTO_GENERATED_GODOTAPI_LIST
|
||||
|
||||
|
||||
godotinternal Module
|
||||
--------------------
|
||||
|
||||
Contains low-level wrappers over Godot types that require manual memory
|
||||
management. This module is used within ``godot-nim`` implementation and you
|
||||
don't need to import it unless you know what you are doing.
|
||||
|
||||
* `godotdictionaries <godotdictionaries.html>`_
|
||||
* `godotnodepaths <godotnodepaths.html>`_
|
||||
* `godotpoolarrays <godotpoolarrays.html>`_
|
||||
* `godotstrings <godotstrings.html>`_
|
||||
* `godotvariants <godotvariants.html>`_
|
||||
1
docs/nim.cfg
Normal file
1
docs/nim.cfg
Normal file
|
|
@ -0,0 +1 @@
|
|||
--threads:on
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
author = "Xored Software, Inc."
|
||||
description = "Godot Engine bindings"
|
||||
license = "MIT"
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
{.warning: "'apigen.apigen' module is deprecated and will be removed in v0.4.0. Use 'godotapigen' instead.".}
|
||||
|
||||
include "../godotapigen.nim"
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
# Copyright 2017 Xored Software, Inc.
|
||||
|
||||
import hashes
|
||||
import internal / [godotstrings, godotdictionaries, godotvariants]
|
||||
import internal.godotstrings, internal.godotdictionaries,
|
||||
internal.godotvariants
|
||||
|
||||
type
|
||||
Dictionary* = ref object
|
||||
|
|
|
|||
1
godot/core/nim.cfg
Normal file
1
godot/core/nim.cfg
Normal file
|
|
@ -0,0 +1 @@
|
|||
--path:"$projectdir/../"
|
||||
|
|
@ -2,7 +2,7 @@ import internal.godotobjects
|
|||
|
||||
type
|
||||
RID* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
|
||||
proc initRID(dest: var RID) {.
|
||||
importc: "godot_rid_new".}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import tables
|
||||
|
||||
import internal / [godotobjects, godotvariants, godotpoolarrays, godotstrings,
|
||||
godotnodepaths]
|
||||
import internal.godotobjects, internal.godotvariants, internal.godotpoolarrays,
|
||||
internal.godotstrings, internal.godotnodepaths
|
||||
|
||||
type
|
||||
Variant* = ref object
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
# Copyright 2017 Xored Software, Inc.
|
||||
|
||||
import core.godotbase, godotinternal
|
||||
import core / [vector2, rect2, vector3, transform2d, planes, quats]
|
||||
import core / [rect3, basis, transforms, colors, nodepaths, rids]
|
||||
import core / [dictionaries, arrays, poolarrays, variants]
|
||||
import core.vector2, core.rect2, core.vector3, core.transform2d, core.planes,
|
||||
core.quats
|
||||
import core.rect3, core.basis, core.transforms, core.colors, core.nodepaths,
|
||||
core.rids
|
||||
import core.dictionaries, core.arrays, core.poolarrays, core.variants
|
||||
|
||||
import nim / [godotmacros, godotnim]
|
||||
import nim.godotmacros, nim.godotnim
|
||||
|
||||
export godotbase, vector2, rect2, vector3, transform2d, planes, quats,
|
||||
rect3, basis, transforms, colors, nodepaths, rids, dictionaries, arrays,
|
||||
poolarrays, variants
|
||||
export godotmacros, godotnim
|
||||
|
||||
export GodotPropertyHint, GodotPropertyUsageFlags
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Copyright 2017 Xored Software, Inc.
|
||||
|
||||
import streams, json, ospaths, strutils, times, sets, tables
|
||||
import streams, json, os, strutils, times, sets, tables
|
||||
import sequtils, algorithm
|
||||
import compiler / [ast, renderer, idents, astalgo]
|
||||
|
||||
|
|
@ -261,8 +261,8 @@ const standardTypes = toSet(
|
|||
"PoolByteArray", "PoolIntArray", "PoolRealArray", "PoolStringArray",
|
||||
"PoolVector2Array", "PoolVector3Array", "PoolColorArray"])
|
||||
|
||||
const smallIntTypes = toSet(["uint8", "int8", "uint16", "int16", "uint32", "int32",
|
||||
"cint", "int", "Error"])
|
||||
const smallIntTypes = toSet(["uint8", "int8", "uint16", "int16", "uint32",
|
||||
"int32", "cint", "int", "Error"])
|
||||
const int64Types = toSet(["uint64", "int64"])
|
||||
const intTypes = union(smallIntTypes, int64Types)
|
||||
const float64Types = toSet(["float64", "cdouble"])
|
||||
|
|
@ -339,6 +339,11 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
|
|||
var body: PNode
|
||||
if not withImplementation:
|
||||
body = newNode(nkEmpty)
|
||||
elif godotMethodName == "_init" and typ.godotName == "Object":
|
||||
# It's not real (calling it crashes) and will be removed later,
|
||||
# but we use init() convention in Nim, so it needs to work.
|
||||
body = newNode(nkStmtList).addChain(
|
||||
newNode(nkDiscardStmt).addChain(newNode(nkEmpty)))
|
||||
else:
|
||||
var methodBindName = toNimStyle(
|
||||
typ.name & "_" & godotMethodName & "_method_bind")
|
||||
|
|
@ -410,7 +415,7 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
|
|||
let isStandardType = arg.typ in standardTypes
|
||||
let isWrappedType = arg.typ in wrapperTypes
|
||||
let convArg = if not varargsName.isNil:
|
||||
getInternalPtr(newCall("toGodot", argName), "Variant")
|
||||
getInternalPtr(newCall("toVariant", argName), "Variant")
|
||||
elif isWrappedType: getInternalPtr(argName, arg.typ)
|
||||
elif isStandardType: newCall("unsafeAddr", argName)
|
||||
elif arg.typ == "string": newCall("unsafeAddr",
|
||||
|
|
@ -440,7 +445,8 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
|
|||
)
|
||||
else:
|
||||
argConversions.add(newNode(nkVarSection).addChain(
|
||||
newIdentDefs(ident("idx"), newNode(nkEmpty), newIntLit(staticArgsLen))))
|
||||
newIdentDefs(ident("idx"), newNode(nkEmpty),
|
||||
newIntLit(staticArgsLen))))
|
||||
let argLoop = newNode(nkWhileStmt)
|
||||
argConversions.add(argLoop)
|
||||
argLoop.add(newInfix(ident("idx"), ident("<"), argLenNode)
|
||||
|
|
@ -544,7 +550,8 @@ proc doGenerateMethod(tree: PNode, methodBindRegistry: var HashSet[string],
|
|||
if isVariantRet:
|
||||
let convErrDef = newNode(nkLetSection).addChain(
|
||||
newIdentDefs(ident("convErr"), newNode(nkEmpty),
|
||||
newCall("fromGodot", ident("result"), newCall("newVariant", retValIdent))))
|
||||
newCall("fromVariant", ident("result"),
|
||||
newCall("newVariant", retValIdent))))
|
||||
let convCheck = newIfStmt(
|
||||
newInfix(ident("convErr"), ident("!="),
|
||||
newDotExpr(ident("ConversionResult"), ident("OK"))),
|
||||
|
|
@ -726,7 +733,7 @@ proc shouldExport(typ: GodotType, types: Table[string, GodotType]): bool =
|
|||
curTyp = types.getOrDefault(curTyp.baseName)
|
||||
result = curTyp.isNil
|
||||
|
||||
proc genTypeFile*(types: var Table[string, GodotType], targetDir: string) =
|
||||
proc genTypeFile(types: var Table[string, GodotType], targetDir: string) =
|
||||
for typ in types.values():
|
||||
types.incDerivedCount(typ.baseName)
|
||||
|
||||
|
|
@ -755,6 +762,9 @@ proc genTypeFile*(types: var Table[string, GodotType], targetDir: string) =
|
|||
renderModule(godotApiTypesTree, targetDir / "godottypes.nim")
|
||||
|
||||
proc genApi*(targetDir: string, apiJsonFile: string) =
|
||||
## Generates .nim files in the ``targetDir`` based on the ``apiJsonFile``.
|
||||
## The JSON file can be generated by executing Godot with
|
||||
## ``--gdnative-generate-json-api <target_filename>`` parameters.
|
||||
let apiJson = parseJson(newFileStream(apiJsonFile),
|
||||
apiJsonFile.extractFilename())
|
||||
var megaImport = newStringOfCap(64 * 1024)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Copyright 2017 Xored Software, Inc.
|
||||
|
||||
import internal / [
|
||||
godotobjects, godotarrays, godotnodepaths, godotpoolarrays,
|
||||
godotstrings, godotvariants, godotdictionaries]
|
||||
import internal.godotobjects, internal.godotarrays, internal.godotnodepaths,
|
||||
internal.godotpoolarrays, internal.godotstrings, internal.godotvariants,
|
||||
internal.godotdictionaries
|
||||
|
||||
export godotobjects, godotarrays, godotnodepaths, godotpoolarrays,
|
||||
godotstrings, godotvariants, godotdictionaries
|
||||
|
|
@ -206,7 +206,7 @@ type
|
|||
|
||||
proc godotScriptRegisterProperty*(libHandle: pointer;
|
||||
name: cstring; path: cstring;
|
||||
attr: var GodotPropertyAttributes;
|
||||
attr: ptr GodotPropertyAttributes;
|
||||
setFunc: GodotPropertySetFunc;
|
||||
getFunc: GodotPropertyGetFunc) {.
|
||||
importc: "godot_nativescript_register_property".}
|
||||
|
|
@ -270,6 +270,9 @@ proc getClassName*(o: ptr GodotObject): string =
|
|||
# There are physics type not known by ClassDB
|
||||
result = result[0..result.len-3]
|
||||
|
||||
proc getGodotSingleton*(name: cstring): ptr GodotObject {.
|
||||
importc: "godot_global_get_singleton".}
|
||||
|
||||
# System Functions
|
||||
|
||||
proc godotAlloc*(bytes: cint): pointer {.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
type
|
||||
GodotArray* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
|
||||
import hashes
|
||||
import godotobjects, godotpoolarrays, godotvariants, godotstrings
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
type
|
||||
GodotDictionary* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
|
||||
import godotarrays, godotvariants, godotstrings
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import godotstrings
|
|||
|
||||
type
|
||||
GodotNodePath* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
|
||||
proc initGodotNodePath*(dest: var GodotNodePath; s: GodotString) {.
|
||||
importc: "godot_node_path_new".}
|
||||
|
|
|
|||
|
|
@ -2,22 +2,22 @@
|
|||
|
||||
type
|
||||
GodotPoolByteArray* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
GodotPoolIntArray* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
GodotPoolRealArray* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
GodotPoolStringArray* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
GodotPoolVector2Array* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
GodotPoolVector3Array* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
GodotPoolColorArray* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
|
||||
import godotstrings, godotarrays
|
||||
import core / [godotbase, colors, vector2, vector3]
|
||||
import core.godotbase, core.colors, core.vector2, core.vector3
|
||||
|
||||
# byte
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
type
|
||||
GodotString* {.byref.} = object
|
||||
p: pointer
|
||||
data: array[sizeof(int), byte]
|
||||
|
||||
proc initGodotString(dest: var GodotString) {.
|
||||
importc: "godot_string_new".}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ type
|
|||
expected*: VariantType
|
||||
|
||||
GodotVariant* {.byref.} = object
|
||||
data: array[16 + sizeof(int), byte]
|
||||
data: array[4 + sizeof(int) div 4, float32]
|
||||
|
||||
import godotobjects, godotarrays, godotpoolarrays,
|
||||
godotstrings, godotnodepaths, godotdictionaries
|
||||
|
|
|
|||
1
godot/internal/nim.cfg
Normal file
1
godot/internal/nim.cfg
Normal file
|
|
@ -0,0 +1 @@
|
|||
--path:"$projectdir/../"
|
||||
|
|
@ -32,6 +32,19 @@ type
|
|||
|
||||
ParseError = object of Exception
|
||||
|
||||
proc godotToNim[T](val: Variant): (T, ConversionResult) =
|
||||
mixin fromVariant
|
||||
result[1] = fromVariant(result[0], val)
|
||||
|
||||
proc nimToGodot[T](val: T): Variant =
|
||||
mixin toVariant
|
||||
when compiles(toVariant(val)):
|
||||
result = toVariant(val)
|
||||
else:
|
||||
printError("Failed to convert Nim value of type " & T.name &
|
||||
" into Variant")
|
||||
initNilVariant(result)
|
||||
|
||||
template parseError(node: NimNode, msg: string) =
|
||||
raise newException(ParseError, lineinfo(node) & ": " & msg)
|
||||
|
||||
|
|
@ -249,7 +262,7 @@ macro invokeVarArgs(procIdent, objIdent;
|
|||
|
||||
if hasReturnValue:
|
||||
let theCall = newNimNode(nnkBracketExpr).add(newNimNode(nnkDotExpr).add(
|
||||
newCall("toGodot", invocation), ident("godotVariant")))
|
||||
newCall("toVariant", invocation), ident("godotVariant")))
|
||||
branchBody.add(getAst(initGodotVariantCall(ident("result"), theCall)))
|
||||
else:
|
||||
branchBody.add(invocation)
|
||||
|
|
@ -291,11 +304,18 @@ proc nimDestroyRefFunc(obj: ptr GodotObject, methData: pointer,
|
|||
# so nothing to do here.
|
||||
discard
|
||||
|
||||
proc refcountIncremented*(obj: NimGodotObject) =
|
||||
GC_ref(obj)
|
||||
proc refcountIncremented(obj: ptr GodotObject, methodData: pointer,
|
||||
userData: pointer, numArgs: cint,
|
||||
args: var array[MAX_ARG_COUNT, ptr GodotVariant]):
|
||||
GodotVariant {.noconv.} =
|
||||
GC_ref(cast[NimGodotObject](userData))
|
||||
|
||||
proc refcountDecremented*(obj: NimGodotObject): bool =
|
||||
GC_unref(obj)
|
||||
proc refcountDecremented(obj: ptr GodotObject, methodData: pointer,
|
||||
userData: pointer, numArgs: cint,
|
||||
args: var array[MAX_ARG_COUNT, ptr GodotVariant]):
|
||||
GodotVariant {.noconv.} =
|
||||
GC_unref(cast[NimGodotObject](userData))
|
||||
initGodotVariant(result, false) # destroy when Nim decides
|
||||
|
||||
template registerGodotClass(classNameIdent, classNameLit, isRef,
|
||||
baseNameLit, createFuncIdent; isTool: bool) =
|
||||
|
|
@ -306,6 +326,8 @@ template registerGodotClass(classNameIdent, classNameLit, isRef,
|
|||
nimObj.setGodotObject(obj)
|
||||
GC_ref(nimObj)
|
||||
result = cast[pointer](nimObj)
|
||||
when compiles(nimObj.init()):
|
||||
nimObj.init()
|
||||
|
||||
let createFuncObj = GodotInstanceCreateFunc(
|
||||
createFunc: createFuncIdent
|
||||
|
|
@ -356,19 +378,23 @@ template registerGodotField(classNameLit, classNameIdent, propNameLit,
|
|||
getFunc: getFuncIdent
|
||||
)
|
||||
{.push warning[ProveInit]: off.} # false warning, Nim bug
|
||||
var attr = GodotPropertyAttributes(
|
||||
typ: ord(godotVariantType(propTypeIdent)),
|
||||
hintString: hintTipLit.toGodotString(),
|
||||
var hintStr = hintTipLit.toGodotString()
|
||||
let attr = GodotPropertyAttributes(
|
||||
typ: ord(variantType(propTypeIdent)),
|
||||
hintString: hintStr,
|
||||
hint: GodotPropertyHint.hintIdent,
|
||||
usage: GodotPropertyUsageFlags.usageIdent
|
||||
)
|
||||
{.push warning[ProveInit]: on.}
|
||||
when hasDefaultValue:
|
||||
attr.defaultValue = (defaultValueNode).toGodot()
|
||||
attr.defaultValue = (defaultValueNode).toVariant().godotVariant[]
|
||||
godotScriptRegisterProperty(getNativeLibHandle(), classNameLit, propNameLit,
|
||||
attr, setFunc, getFunc)
|
||||
unsafeAddr attr, setFunc, getFunc)
|
||||
hintStr.deinit()
|
||||
|
||||
static:
|
||||
import strutils, sets
|
||||
import strutils, sets, sequtils
|
||||
|
||||
proc toGodotStyle(s: string): string {.compileTime.} =
|
||||
result = newStringOfCap(s.len + 10)
|
||||
for c in s:
|
||||
|
|
@ -469,7 +495,6 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
|
|||
GodotVariant {.noconv.} =
|
||||
let self = cast[classNameIdent](userData)
|
||||
const isStaticCall = methodNameLit == cstring"_ready" or
|
||||
methodNameLit == cstring"_init" or
|
||||
methodNameLit == cstring"_enter_tree" or
|
||||
methodNameLit == cstring"_exit_tree" or
|
||||
methodNameLit == cstring"_enter_world" or
|
||||
|
|
@ -520,25 +545,70 @@ proc genType(obj: ObjectDecl): NimNode {.compileTime.} =
|
|||
|
||||
if isRef:
|
||||
# add ref/unref for types inherited from Reference
|
||||
let noArgs = newSeq[NimNode]()
|
||||
result.add(getAst(
|
||||
registerGodotMethod(classNameLit, classNameIdent, ident("refcountIncremented"),
|
||||
cstring"_refcount_incremented", 0, 0,
|
||||
noArgs, genSym(nskProc, "refcount_incremented"),
|
||||
ident("false"))))
|
||||
result.add(getAst(
|
||||
registerGodotMethod(classNameLit, classNameIdent, ident("refcountDecremented"),
|
||||
cstring"_refcount_decremented", 0, 0,
|
||||
noArgs, genSym(nskProc, "refcount_decremented"),
|
||||
ident("true"))))
|
||||
template registerRefIncDec(classNameLit) =
|
||||
let refInc = GodotInstanceMethod(
|
||||
meth: refcountIncremented
|
||||
)
|
||||
let refDec = GodotInstanceMethod(
|
||||
meth: refcountDecremented
|
||||
)
|
||||
godotScriptRegisterMethod(getNativeLibHandle(), classNameLit,
|
||||
cstring"_refcount_incremented",
|
||||
GodotMethodAttributes(), refInc)
|
||||
godotScriptRegisterMethod(getNativeLibHandle(), classNameLit,
|
||||
cstring"_refcount_decremented",
|
||||
GodotMethodAttributes(), refDec)
|
||||
result.add(getAst(registerRefIncDec(classNameLit)))
|
||||
|
||||
{.push warning[Deprecated]: off.}
|
||||
# immediate macros are deprecated, but `untyped` doesnt make it immediate,
|
||||
# immediate macros are deprecated, but `untyped` doesn't make it immediate,
|
||||
# as the warning and the documentation claim.
|
||||
|
||||
macro gdobj*(definition: untyped, body: typed): typed {.immediate.} =
|
||||
## Generates Godot type. Self-documenting example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## import godot, node
|
||||
##
|
||||
## gdobj MyObj of Node:
|
||||
## var myField: int
|
||||
## ## Not exported to Godot (i.e. editor will not see this field).
|
||||
##
|
||||
## var myString* {.gdExport, hint: Length, tip: "20".}: string
|
||||
## ## Exported to Godot as ``my_string``.
|
||||
## ## Editor will limit this string to length 20.
|
||||
## ## ``hint` is a value of ``GodotPropertyHint`` enum.
|
||||
## ## ``tip`` depends on the value of ``hint``, its format is described
|
||||
## ## in ``GodotPropertyHint`` documentation.
|
||||
##
|
||||
## method ready*() =
|
||||
## ## Exported methods are exported to Godot by default,
|
||||
## ## and their Godot names are prefixed with ``_``
|
||||
## ## (in this case ``_ready``)
|
||||
## print("I am ready! myString is: " & myString)
|
||||
##
|
||||
## proc myProc*() {.gdExport.} =
|
||||
## ## Exported to Godot as ``my_proc``
|
||||
## print("myProc is called! Incrementing myField.")
|
||||
## inc myField
|
||||
##
|
||||
## If parent type is omitted, the type is inherited from ``Object``.
|
||||
##
|
||||
## ``tool`` specifier can be added to mark the type as an
|
||||
## `editor plugin <https://godot.readthedocs.io/en/stable/development/plugins/making_plugins.html>`_:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## import godot, editor_plugin
|
||||
##
|
||||
## gdobj(MyTool of EditorPlugin, tool):
|
||||
## method enterTree*() =
|
||||
## print("MyTool initialized!")
|
||||
##
|
||||
## Objects can be instantiated by invoking
|
||||
## `gdnew <godotnim.html#gdnew>`_ or by using
|
||||
## `load <godotapi/resource_loader.html#load,string,string,bool>`_ or any other way
|
||||
## that you can find in `Godot API <index.html#modules-godot-api>`_.
|
||||
let typeDef = parseType(definition, callsite())
|
||||
result = genType(typeDef)
|
||||
|
||||
{.push warning[Deprecated]: on.}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,15 +10,28 @@ import core.vector2, core.rect2,
|
|||
core.arrays, core.poolarrays, core.variants
|
||||
import godotinternal
|
||||
|
||||
## Definition of NimGodotObject and converters for built-in types.
|
||||
## This module defines ``NimGodotObject`` and ``toVariant``/``fromVariant``
|
||||
## converters for Nim types. The converters are used by
|
||||
## `gdobj <godotmacros.html#gdobj.m,untyped,typed>`_ macro to import/export
|
||||
## values from/to Godot (editor, GDScript).
|
||||
##
|
||||
## You can also allow conversion of any custom type ``MyType`` by implementing:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
## proc variantType*(T: typedesc[MyType]): VariantType
|
||||
## proc toVariant*(self: MyType): Variant
|
||||
## proc fromVariant*(self: var MyType, val: Variant): ConversionResult
|
||||
|
||||
type
|
||||
NimGodotObject* = ref object of RootObj
|
||||
## The base type all Godot types inherit from.
|
||||
## Manages lifecycle of the wrapped GodotObject.
|
||||
godotObject: ptr GodotObject
|
||||
isExternalRef: bool
|
||||
isOwn: bool
|
||||
|
||||
ConversionResult* {.pure.} = enum
|
||||
## Conversion result to return from ``fromVariant`` procedure.
|
||||
OK,
|
||||
TypeError,
|
||||
## Type mismatch
|
||||
|
|
@ -41,7 +54,11 @@ type
|
|||
SomeGodotOrNum = SomeGodot or SomeSignedInt or SomeUnsignedInt or SomeFloat
|
||||
|
||||
CallError* = object of Exception
|
||||
## Raised by wrappers in case of an incorrect dynamic invocation.
|
||||
## For example, if incorrect number of arguments were passed or they had
|
||||
## unexpected types.
|
||||
err*: VariantCallError
|
||||
## The error as returned by Godot
|
||||
|
||||
ObjectInfo = object
|
||||
constructor: proc(): NimGodotObject {.gcsafe, nimcall.}
|
||||
|
|
@ -49,20 +66,24 @@ type
|
|||
isNative: bool
|
||||
isRef: bool
|
||||
|
||||
template printWarning*(warn: typed) =
|
||||
template printWarning*(warning: typed) =
|
||||
## Prints ``warning`` to Godot log, adding filename and line information.
|
||||
let (filename, line) = instantiationInfo()
|
||||
godotPrintWarning(cstring($warn), nil, cstring(filename), line.cint)
|
||||
godotPrintWarning(cstring($warning), nil, cstring(filename), line.cint)
|
||||
|
||||
template printError*(err: typed) =
|
||||
template printError*(error: typed) =
|
||||
## Prints ``error`` to Godot log, adding filename and line information.
|
||||
let (filename, line) = instantiationInfo()
|
||||
godotPrintError(cstring($err), nil, cstring(filename), line.cint)
|
||||
godotPrintError(cstring($error), nil, cstring(filename), line.cint)
|
||||
|
||||
proc print*(message: string) {.inline.} =
|
||||
## Prints ``message`` to Godot log.
|
||||
var s = message.toGodotString()
|
||||
godotPrint(s)
|
||||
s.deinit()
|
||||
|
||||
proc print*(message: cstring) {.inline.} =
|
||||
## Prints ``message`` to Godot log.
|
||||
var s = message.toGodotString()
|
||||
godotPrint(s)
|
||||
s.deinit()
|
||||
|
|
@ -141,7 +162,9 @@ macro isReference(T: typedesc): bool =
|
|||
else: ident("false")
|
||||
|
||||
template registerClass*(T: typedesc; godotClassName: cstring,
|
||||
isNativeParam: bool) =
|
||||
native: bool) =
|
||||
## Registers the specified Godot type.
|
||||
## Used by ``gdobj`` macro and `godotapigen <godotapigen.html>`_.
|
||||
if classRegistry.isNil:
|
||||
classRegistry = newTable[cstring, ObjectInfo]()
|
||||
let constructor = proc(): NimGodotObject =
|
||||
|
|
@ -154,21 +177,21 @@ template registerClass*(T: typedesc; godotClassName: cstring,
|
|||
let objInfo = ObjectInfo(
|
||||
constructor: constructor,
|
||||
baseNativeClass: base,
|
||||
isNative: isNativeParam,
|
||||
isNative: native,
|
||||
isRef: isRef
|
||||
)
|
||||
classRegistry[godotClassName] = objInfo
|
||||
static:
|
||||
let objInfoStatic = ObjectInfo(
|
||||
baseNativeClass: base,
|
||||
isNative: isNativeParam,
|
||||
isRef: isRef
|
||||
isNative: native,
|
||||
isRef: isRef,
|
||||
)
|
||||
classRegistryStatic[godotClassName] = objInfoStatic
|
||||
when isRef:
|
||||
static:
|
||||
refClasses.add(T.name)
|
||||
when isNativeParam:
|
||||
when native:
|
||||
static:
|
||||
nativeClasses.add(T.name)
|
||||
|
||||
|
|
@ -187,6 +210,9 @@ proc newNimGodotObject[T: NimGodotObject](
|
|||
result.godotObject.reference()
|
||||
|
||||
proc asNimGodotObject*[T: NimGodotObject](godotObject: ptr GodotObject): T =
|
||||
## Wraps ``godotObject`` into Nim type ``T``.
|
||||
## This is used by `godotapigen <godotapigen.html>`_ and should rarely be
|
||||
## used by anything else.
|
||||
if godotObject.isNil: return nil
|
||||
let userDataPtr = godotObject.getUserData()
|
||||
if not userDataPtr.isNil:
|
||||
|
|
@ -202,15 +228,24 @@ proc newVariant*(obj: NimGodotObject): Variant {.inline.} =
|
|||
newVariant(obj.godotObject)
|
||||
|
||||
proc asObject*[T: NimGodotObject](v: Variant): T {.inline.} =
|
||||
## Converts ``v`` to object of type ``T``.
|
||||
## Returns ``nil`` if the conversion cannot be performed
|
||||
## (``v``'s type is not an Object or it's an object of an incompatible type)
|
||||
asNimGodotObject[T](v.asGodotObject())
|
||||
|
||||
proc asObject*(v: Variant, T: typedesc[NimGodotObject]): T {.inline.} =
|
||||
## Converts ``v`` to object of type ``T``.
|
||||
## Returns ``nil`` if the conversion cannot be performed
|
||||
## (``v``'s type is not an Object or it's an object of an incompatible type)
|
||||
asNimGodotObject[T](v.asGodotObject())
|
||||
|
||||
proc `as`*[T: NimGodotObject](obj: NimGodotObject, t: typedesc[T]): T =
|
||||
## Converts the ``obj`` into the specified type.
|
||||
## Returns ``nil`` if the conversion cannot be performed
|
||||
## (the ``obj`` is not of the type ``T``)
|
||||
##
|
||||
## This can be used either in dot notation (``node.as(Button)``) or
|
||||
## infix notation (``node as Button``).
|
||||
if obj.isNil or not (obj of T):
|
||||
when not defined(release):
|
||||
if not obj.isNil:
|
||||
|
|
@ -220,9 +255,6 @@ proc `as`*[T: NimGodotObject](obj: NimGodotObject, t: typedesc[T]): T =
|
|||
else:
|
||||
result = T(obj)
|
||||
|
||||
proc getSingletonGodot*(name: cstring): ptr GodotObject {.
|
||||
importc: "godot_global_get_singleton".}
|
||||
|
||||
proc newRStrLit(s: string): NimNode {.compileTime.} =
|
||||
result = newNimNode(nnkRStrLit)
|
||||
result.strVal = s
|
||||
|
|
@ -241,8 +273,11 @@ macro toGodotName(T: typedesc): cstring =
|
|||
ident("cstring"), newRStrLit(godotName))
|
||||
|
||||
proc getSingleton*[T: NimGodotObject](): T =
|
||||
## Returns singleton of type ``T``. Normally, this should not be used,
|
||||
## because `godotapigen <godotapigen.html>`_ wraps singleton methods so that
|
||||
## singleton objects don't have to be provided as parameters.
|
||||
const godotName = toGodotName(T)
|
||||
let singleton = getSingletonGodot(godotName)
|
||||
let singleton = getGodotSingleton(godotName)
|
||||
if singleton.isNil:
|
||||
printError("Tried to get non-existing singleton of type " & $godotName)
|
||||
else:
|
||||
|
|
@ -285,6 +320,7 @@ proc newOwnObj[T: NimGodotObject](name: cstring): T =
|
|||
ret.deinit()
|
||||
|
||||
proc gdnew*[T: NimGodotObject](): T =
|
||||
## Instantiates new object of type ``T``.
|
||||
const godotName = toGodotName(T)
|
||||
const objInfo = classRegistryStatic[godotName]
|
||||
result = when objInfo.isNative:
|
||||
|
|
@ -292,6 +328,7 @@ proc gdnew*[T: NimGodotObject](): T =
|
|||
else: newOwnObj[T](godotName)
|
||||
|
||||
proc newCallError*(err: VariantCallError): ref CallError =
|
||||
## Instantiates ``CallError`` from Godot ``err``.
|
||||
let msg = case err.error:
|
||||
of VariantCallErrorType.OK,
|
||||
VariantCallErrorType.InvalidMethod,
|
||||
|
|
@ -307,6 +344,8 @@ proc newCallError*(err: VariantCallError): ref CallError =
|
|||
result.err = err
|
||||
|
||||
proc newConversionError*(err: ConversionResult): ref ValueError =
|
||||
## Instantiates error raised by `godotapigen <godotapigen.html>`_
|
||||
## generated code in case of a conversion error.
|
||||
let msg = case err:
|
||||
of ConversionResult.TypeError:
|
||||
"Failed to convert the return value into Nim type"
|
||||
|
|
@ -317,28 +356,32 @@ proc newConversionError*(err: ConversionResult): ref ValueError =
|
|||
|
||||
result = newException(ValueError, msg)
|
||||
|
||||
proc removeGodotObject*(nimObj: NimGodotObject) {.inline.} =
|
||||
## Used from destructor
|
||||
nimObj.godotObject = nil
|
||||
|
||||
proc setGodotObject*(nimObj: NimGodotObject, obj: ptr GodotObject) {.inline.} =
|
||||
## Used from Godot constructor. Don't call this.
|
||||
assert(not obj.isNil)
|
||||
assert(nimObj.godotObject.isNil) # reassignment is not allowed
|
||||
nimObj.godotObject = obj
|
||||
|
||||
proc setOwn*(nimObj: NimGodotObject) {.inline.} =
|
||||
## Used from Godot constructor. Don't call this.
|
||||
nimObj.isOwn = true
|
||||
|
||||
proc removeGodotObject*(nimObj: NimGodotObject) {.inline.} =
|
||||
## Used from Godot destructor. Don't call this.
|
||||
nimObj.godotObject = nil
|
||||
|
||||
proc godotObject*(nimObj: NimGodotObject): ptr GodotObject {.inline.} =
|
||||
## Returns internal poitner to ``GodotObject``. Use only if you know what
|
||||
## you are doing.
|
||||
nimObj.godotObject
|
||||
|
||||
proc godotVariantType*(T: typedesc[NimGodotObject]): VariantType {.inline.} =
|
||||
proc variantType*(T: typedesc[NimGodotObject]): VariantType {.inline.} =
|
||||
VariantType.Object
|
||||
|
||||
proc toGodot*(self: NimGodotObject): Variant {.inline.} =
|
||||
proc toVariant*(self: NimGodotObject): Variant {.inline.} =
|
||||
newVariant(self.godotObject)
|
||||
|
||||
proc fromGodot*[T: NimGodotObject](self: var T,
|
||||
proc fromVariant*[T: NimGodotObject](self: var T,
|
||||
val: Variant): ConversionResult =
|
||||
if val.getType() == VariantType.Object:
|
||||
let objPtr = val.asGodotObject()
|
||||
|
|
@ -350,27 +393,27 @@ proc fromGodot*[T: NimGodotObject](self: var T,
|
|||
else:
|
||||
result = ConversionResult.TypeError
|
||||
|
||||
proc godotVariantType*(T: typedesc[enum]): VariantType {.inline.} =
|
||||
proc variantType*(T: typedesc[enum]): VariantType {.inline.} =
|
||||
VariantType.Int
|
||||
|
||||
proc toGodot*[T: enum](self: T): Variant {.inline.} =
|
||||
proc toVariant*[T: enum](self: T): Variant {.inline.} =
|
||||
newVariant(int64(ord(self)))
|
||||
|
||||
proc fromGodot*[T: enum](self: var T,
|
||||
proc fromVariant*[T: enum](self: var T,
|
||||
val: Variant): ConversionResult {.inline.} =
|
||||
if val.getType() == VariantType.Int:
|
||||
self = T(val.asInt())
|
||||
else:
|
||||
result = ConversionResult.TypeError
|
||||
|
||||
proc toGodot*(self: Variant): Variant {.inline.} =
|
||||
proc toVariant*(self: Variant): Variant {.inline.} =
|
||||
self
|
||||
|
||||
proc fromGodot*(self: var Variant,
|
||||
proc fromVariant*(self: var Variant,
|
||||
val: Variant): ConversionResult {.inline.} =
|
||||
self = newVariant(val)
|
||||
|
||||
proc godotVariantType*(T: typedesc[SomeGodotOrNum]): VariantType {.inline.} =
|
||||
proc variantType*(T: typedesc[SomeGodotOrNum]): VariantType {.inline.} =
|
||||
when T is SomeSignedInt or T is SomeUnsignedInt:
|
||||
VariantType.Int
|
||||
elif T is bool:
|
||||
|
|
@ -426,10 +469,10 @@ proc godotVariantType*(T: typedesc[SomeGodotOrNum]): VariantType {.inline.} =
|
|||
else:
|
||||
VariantType.Nil
|
||||
|
||||
proc toGodot*[T: SomeGodotOrNum](val: T): Variant {.inline.} =
|
||||
proc toVariant*[T: SomeGodotOrNum](val: T): Variant {.inline.} =
|
||||
newVariant(val)
|
||||
|
||||
proc fromGodot*[T: SomeSignedInt or SomeUnsignedInt](
|
||||
proc fromVariant*[T: SomeSignedInt or SomeUnsignedInt](
|
||||
self: var T, val: Variant): ConversionResult =
|
||||
if val.getType() != VariantType.Int:
|
||||
result = ConversionResult.TypeError
|
||||
|
|
@ -443,7 +486,7 @@ proc fromGodot*[T: SomeSignedInt or SomeUnsignedInt](
|
|||
else:
|
||||
self = T(intVal)
|
||||
|
||||
proc fromGodot*[T: SomeFloat](self: var T, val: Variant): ConversionResult =
|
||||
proc fromVariant*[T: SomeFloat](self: var T, val: Variant): ConversionResult =
|
||||
if val.getType() == VariantType.Real:
|
||||
self = T(val.asReal())
|
||||
elif val.getType() == VariantType.Int:
|
||||
|
|
@ -451,8 +494,8 @@ proc fromGodot*[T: SomeFloat](self: var T, val: Variant): ConversionResult =
|
|||
else:
|
||||
result = ConversionResult.TypeError
|
||||
|
||||
proc fromGodot*[T: SomeGodot](self: var T, val: Variant): ConversionResult =
|
||||
if godotVariantType(T) != val.getType():
|
||||
proc fromVariant*[T: SomeGodot](self: var T, val: Variant): ConversionResult =
|
||||
if variantType(T) != val.getType():
|
||||
return ConversionResult.TypeError
|
||||
when self is bool:
|
||||
self = val.asBool()
|
||||
|
|
@ -506,13 +549,13 @@ proc fromGodot*[T: SomeGodot](self: var T, val: Variant): ConversionResult =
|
|||
# mustn't reach this
|
||||
result = ConversionError.TypeError
|
||||
|
||||
proc godotVariantType*(T: typedesc[string]): VariantType {.inline.} =
|
||||
proc variantType*(T: typedesc[string]): VariantType {.inline.} =
|
||||
VariantType.String
|
||||
|
||||
proc toGodot*(s: string): Variant {.inline.} =
|
||||
proc toVariant*(s: string): Variant {.inline.} =
|
||||
newVariant(s)
|
||||
|
||||
proc fromGodot*(s: var string, val: Variant): ConversionResult =
|
||||
proc fromVariant*(s: var string, val: Variant): ConversionResult =
|
||||
if val.getType() == VariantType.String:
|
||||
s = val.asString()
|
||||
elif val.getType() == VariantType.Nil:
|
||||
|
|
@ -520,20 +563,20 @@ proc fromGodot*(s: var string, val: Variant): ConversionResult =
|
|||
else:
|
||||
result = ConversionResult.TypeError
|
||||
|
||||
proc godotVariantType*(T: typedesc[seq]): VariantType =
|
||||
proc variantType*(T: typedesc[seq]): VariantType =
|
||||
VariantType.Array
|
||||
|
||||
proc godotVariantType*(T: typedesc[array]): VariantType =
|
||||
proc variantType*(T: typedesc[array]): VariantType =
|
||||
VariantType.Array
|
||||
|
||||
proc toGodot*[T](s: openarray[T]): Variant =
|
||||
proc toVariant*[T](s: openarray[T]): Variant =
|
||||
var arr = newArray()
|
||||
mixin toGodot
|
||||
for item in s:
|
||||
arr.add(toGodot(item))
|
||||
arr.add(toVariant(item))
|
||||
result = newVariant(arr)
|
||||
|
||||
proc fromGodot*[T](s: var seq[T], val: Variant): ConversionResult =
|
||||
proc fromVariant*[T](s: var seq[T], val: Variant): ConversionResult =
|
||||
if val.getType() != VariantType.Array:
|
||||
result = ConversionResult.TypeError
|
||||
else:
|
||||
|
|
@ -541,12 +584,12 @@ proc fromGodot*[T](s: var seq[T], val: Variant): ConversionResult =
|
|||
s = newSeq[T](arr.len)
|
||||
for idx, item in arr:
|
||||
mixin fromGodot
|
||||
let convResult = fromGodot(s[idx], item)
|
||||
let convResult = fromVariant(s[idx], item)
|
||||
if convResult != ConversionResult.OK:
|
||||
s = nil
|
||||
return convResult
|
||||
|
||||
proc fromGodot*[T: array](s: var T, val: Variant): ConversionResult =
|
||||
proc fromVariant*[T: array](s: var T, val: Variant): ConversionResult =
|
||||
if val.getType() != VariantType.Array:
|
||||
result = ConversionResult.TypeError
|
||||
else:
|
||||
|
|
@ -555,21 +598,21 @@ proc fromGodot*[T: array](s: var T, val: Variant): ConversionResult =
|
|||
return ConversionResult.TypeError
|
||||
for idx, item in arr:
|
||||
mixin fromGodot
|
||||
let convResult = fromGodot(s[idx], item)
|
||||
let convResult = fromVariant(s[idx], item)
|
||||
if convResult != ConversionResult.OK:
|
||||
return convResult
|
||||
|
||||
proc godotVariantType*(T: typedesc[Table|TableRef]): VariantType {.inline.} =
|
||||
proc variantType*(T: typedesc[Table|TableRef]): VariantType {.inline.} =
|
||||
VariantType.Dictionary
|
||||
|
||||
proc toGodot*[T: Table or TableRef](t: T): Variant =
|
||||
proc toVariant*[T: Table or TableRef](t: T): Variant =
|
||||
var dict = newDictionary()
|
||||
mixin toGodot
|
||||
for k, v in t.pairs():
|
||||
dict[toGodot(k)] = toGodot(v)
|
||||
dict[toVariant(k)] = toVariant(v)
|
||||
result = newVariant(dict)
|
||||
|
||||
proc fromGodot*[T: Table or TableRef](t: var T, val: Variant): ConversionResult =
|
||||
proc fromVariant*[T: Table or TableRef](t: var T, val: Variant): ConversionResult =
|
||||
if val.getType() != VariantType.Dictionary:
|
||||
result = ConversionResult.TypeError
|
||||
else:
|
||||
|
|
@ -582,31 +625,18 @@ proc fromGodot*[T: Table or TableRef](t: var T, val: Variant): ConversionResult
|
|||
for k, v in dict:
|
||||
var nimKey: type(t.keys())
|
||||
var nimVal: type(t.values())
|
||||
let keyResult = fromGodot(nimKey, k)
|
||||
let keyResult = fromVariant(nimKey, k)
|
||||
if keyResult != ConversionResult.OK:
|
||||
when t is ref:
|
||||
t = nil
|
||||
return keyResult
|
||||
let valResult = fromGodot(nimVal, v)
|
||||
let valResult = fromVariant(nimVal, v)
|
||||
if valResult != ConversionResult.OK:
|
||||
when t is ref:
|
||||
t = nil
|
||||
return valResult
|
||||
t[nimKey] = nimVal
|
||||
|
||||
proc godotToNim*[T](val: Variant): (T, ConversionResult) =
|
||||
mixin fromGodot
|
||||
result[1] = fromGodot(result[0], val)
|
||||
|
||||
proc nimToGodot*[T](val: T): Variant =
|
||||
mixin toGodot
|
||||
when compiles(toGodot(val)):
|
||||
result = toGodot(val)
|
||||
else:
|
||||
printError("Failed to convert Nim value of type " & T.name &
|
||||
" into Variant")
|
||||
initNilVariant(result)
|
||||
|
||||
{.emit: """/*TYPESECTION*/
|
||||
void NimMain(void);
|
||||
N_NOINLINE(void, setStackBottom)(void* thestackbottom);
|
||||
|
|
@ -614,6 +644,8 @@ N_NOINLINE(void, setStackBottom)(void* thestackbottom);
|
|||
|
||||
var nativeLibHandle: pointer
|
||||
proc getNativeLibHandle*(): pointer =
|
||||
## Returns NativeScript library handle used to register type information
|
||||
## in Godot. Use only if you know what you are doing.
|
||||
return nativeLibHandle
|
||||
|
||||
proc godot_nativescript_init(handle: pointer) {.
|
||||
|
|
|
|||
1
godot/nim/nim.cfg
Normal file
1
godot/nim/nim.cfg
Normal file
|
|
@ -0,0 +1 @@
|
|||
--path:"$projectdir/../"
|
||||
5
godot/nimdoc.cfg
Normal file
5
godot/nimdoc.cfg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
doc.item.seesrc = """ <a
|
||||
href="${url}/tree/${commit}/${path}#L${line}"
|
||||
class="link-seesrc" target="_blank">Source</a>
|
||||
<a href="${url}/edit/master/${path}#L${line}" class="link-seesrc" target="_blank" >Edit</a>
|
||||
"""
|
||||
Loading…
Add table
Add a link
Reference in a new issue