Documentation & Travis & Refactoring

This commit is contained in:
Ruslan Mustakov 2017-07-31 18:52:07 +07:00
commit 988b9e1f11
27 changed files with 772 additions and 123 deletions

6
.gitignore vendored
View file

@ -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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
--threads:on

View file

@ -1,4 +1,4 @@
version = "0.3.0"
version = "0.4.0"
author = "Xored Software, Inc."
description = "Godot Engine bindings"
license = "MIT"

View file

@ -1,3 +0,0 @@
{.warning: "'apigen.apigen' module is deprecated and will be removed in v0.4.0. Use 'godotapigen' instead.".}
include "../godotapigen.nim"

View file

@ -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
View file

@ -0,0 +1 @@
--path:"$projectdir/../"

View file

@ -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".}

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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 {.

View file

@ -2,7 +2,7 @@
type
GodotArray* {.byref.} = object
p: pointer
data: array[sizeof(int), byte]
import hashes
import godotobjects, godotpoolarrays, godotvariants, godotstrings

View file

@ -2,7 +2,7 @@
type
GodotDictionary* {.byref.} = object
p: pointer
data: array[sizeof(int), byte]
import godotarrays, godotvariants, godotstrings

View file

@ -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".}

View file

@ -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

View file

@ -2,7 +2,7 @@
type
GodotString* {.byref.} = object
p: pointer
data: array[sizeof(int), byte]
proc initGodotString(dest: var GodotString) {.
importc: "godot_string_new".}

View file

@ -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
View file

@ -0,0 +1 @@
--path:"$projectdir/../"

View file

@ -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.}

View file

@ -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
View file

@ -0,0 +1 @@
--path:"$projectdir/../"

5
godot/nimdoc.cfg Normal file
View file

@ -0,0 +1,5 @@
doc.item.seesrc = """&nbsp;&nbsp;<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>
"""