Nativescript 1.1

implemented instance binding data usage

This commit changes the way C++ wrapper classes work.
Previously, wrapper classes were merely wrapper *interfaces*.
They used the `this` pointer to store the actual foreign Godot
Object.

With the NativeScript 1.1 extension it is now possible to have
low-overhead language binding data attached to Objects.

The C++ bindings use that feature to implement *proper* wrappers
and enable regular C++ inheritance usage that way.

Some things might still be buggy and untested, but the C++
SimpleDemo works with those changes.

new and free change, custom free will crash engine, be wary

fix exporting of non-object types

fix free() crash with custom resources

added type tags and safe object casting

fix global type registration order

fix cast_to

changed build system to be more self contained

updated .gitignore

use typeid() for type tags now

fix indentation in bindings generator

remove accidentally added files

fix gitignore

Fixed up registering tool and updated godot_headers

Fix crash when calling String::split/split_floats

Was casting to the wrong object type.
Also adds parse_ints function to String with the same logic

Better warning/error macros

Change gitignore so we get our gen folders

New documentation based on nativescript 1.1

Fixed GODOT_SUBCLASS macro

Preventing crash when function returned null ptr

Adds needed include <typeinfo>

 Solves this issue #168 due to not having the include of typeinfo

Fix compile error of 'WARN_PRINT' and 'ERR_PRINT'.

cannot pass non-trivial object of type 'godot::String' to variadic function; expected type from format string was 'char *' [-Wnon-pod-varargs]

update vector3::distance_to

Remove godot_api.json as its now in the godot_headers submodule (api.json)
pull/194/head
karroffel 2018-02-11 15:50:01 +01:00 committed by Bastiaan Olij
parent 13f4f0e8f8
commit 200bf226bf
23 changed files with 547 additions and 140919 deletions

10
.gitignore vendored
View File

@ -1,8 +1,3 @@
# Generated bindings
src/*.cpp
src/*.hpp
include/*.hpp
# Misc
logs/*
@ -16,3 +11,8 @@ logs/*
*.pdb
*.lib
bin
*.config
*.creator
*.creator.user
*.files
*.includes

147
README.md
View File

@ -1,62 +1,91 @@
# godot-cpp
C++ bindings for the Godot script API
# Creating a GDNative library (Linux)
Create a directory named `SimpleLibrary` with subdirectories `lib, src`
Note that the master branch in this repository is for use with Godot build from its latest master.
If you need to support older versions of Godot use the relevant branch for that version in this repository.
The instructions below feature the new NativeScript 1.1 class structure and will only work for modules created for Godot 3.1 and later.
- [**Getting Started**](#getting-started)
- [**Creating a simple class**](#creating-a-simple-class)
## Getting Started
| **Build latest version of Godot** | [**GitHub**](https://github.com/godotengine/godot) | [**Docs**](https://godot.readthedocs.io/en/latest/development/compiling/index.html) |
| --- | --- | --- |
### Setting up a new project
We recommend using git for managing your project and the instructions below assume so. Alternatively you can download the source code directly from GitHub in which case you need to download both [godot-cpp](https://github.com/GodotNativeTools/godot-cpp) and [godot_headers](https://github.com/GodotNativeTools/godot_headers).
Getting latest `godot-cpp` and `godot_headers`
```
$ git clone https://github.com/GodotNativeTools/godot-cpp
$ git clone https://github.com/GodotNativeTools/godot_headers
```
right now our directory structure should look like this:
```
godot-cpp
godot_headers
SimpleLibrary
├── lib/
└── src/
$ mkdir SimpleLibrary
$ cd SimpleLibrary
$ mkdir bin
$ mkdir src
$ git clone --recursive https://github.com/GodotNativeTools/godot-cpp
```
Now to generate cpp bindings
Note that if you wish to use a specific branch, add the -b option to the clone command:
```
$ git clone --recursive https://github.com/GodotNativeTools/godot-cpp -b 3.0
```
Right now our directory structure should look like this:
```
SimpleLibrary/
├─godot-cpp/
| └─godot_headers/
├─bin/
└─src/
```
### Updating the api.json
Our api.json file contains meta data of all the classes that are part of the Godot core and are needed to generate the C++ binding classes for use in GDNative modules.
A file is supplied in our repository for your convinience but if you are running a custom build of Godot and need access to classes that have recent changes a new api.json file must be generated. You do this by starting your Godot executable with the following parameters:
```
$ godot --gdnative-generate-json-api api.json
```
Now copy the api.json file into your folder structure so its easy to access.
### Compiling the cpp bindings library
The final step is to compile our cpp bindings library:
```
$ cd godot-cpp
$ scons godotbinpath="../godot_fork/bin/godot_binary" p=linux
$ scons platform=<your platform> generate_bindings=yes
$ cd ..
```
resulting libraries will be placed under `bin/` and the generated headers will be placed under `include/*`
**Note:**
> `generate_bindings=yes` is used to force regenerating C++ bindings (`godot_api.json` - Godot API)
> Replace `<your platform>` with either `windows`, `linux` or `osx`.
> Include `use_llvm=yes` for using clang++
> You may need to specify `headers=../godot_headers` if you have compilation issues related to missing include files
> Include `target=runtime` to build a runtime build (windows only at the moment)
And our directory structure will be
```
godot-cpp
└── bin/libgodot-cpp.a
godot_headers
SimpleLibrary
├── lib/
└── src/
```
> The resulting library will be created in `godot-cpp/bin/`, take note of its name as it will be different depending on platform.
# Creating simple class
> If you want to use an alternative api.json file add `use_custom_api_file=yes custom_api_file=../api.json`, be sure to specify the correct location of where you placed your file.
## Creating a simple class
Create `init.cpp` under `SimpleLibrary/src/` and add the following code
```cpp
#include <core/Godot.hpp>
#include <Godot.hpp>
#include <Reference.hpp>
using namespace godot;
class SimpleClass : public GodotScript<Reference> {
GODOT_CLASS(SimpleClass);
class SimpleClass : public Reference {
GODOT_CLASS(SimpleClass, Reference);
public:
SimpleClass() { }
/* _init must exist as it is called by Godot */
void _init() { }
void test_void_method() {
Godot::print("This is test");
}
@ -75,7 +104,10 @@ public:
* How to register exports like gdscript
* export var _name = "SimpleClass"
**/
register_property((char *)"base/name", &SimpleClass::_name, String("SimpleClass"));
register_property<SimpleClass, String>("base/name", &SimpleClass::_name, String("SimpleClass"));
/* or alternatively with getter and setter methods */
register_property<SimpleClass, int>("base/value", &SimpleClass::set_value, &SimpleClass::get_value, 0);
/** For registering signal **/
// register_signal<SimpleClass>("signal_name");
@ -83,41 +115,66 @@ public:
}
String _name;
int _value;
void set_value(int p_value) {
_value = p_value;
}
int get_value() const {
return _value;
}
};
/** GDNative Initialize **/
extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o)
{
extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
godot::Godot::gdnative_init(o);
}
/** GDNative Terminate **/
extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o)
{
extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
godot::Godot::gdnative_terminate(o);
}
/** NativeScript Initialize **/
extern "C" void GDN_EXPORT godot_nativescript_init(void *handle)
{
extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) {
godot::Godot::nativescript_init(handle);
godot::register_class<SimpleClass>();
}
```
# Compiling
### Compiling
*Linux*
```
$ cd SimpleLibrary
$ clang -fPIC -o src/init.os -c src/init.cpp -g -O3 -std=c++14 -I../godot-cpp/include -Igodot_headers
$ clang -o lib/libtest.so -shared src/init.os -L../godot-cpp/lib -lgodot-cpp
$ clang -fPIC -o src/init.os -c src/init.cpp -g -O3 -std=c++14 -Igodot-cpp/include -Igodot-cpp/include/core -Igodot-cpp/include/gen -Igodot-cpp/godot_headers
$ clang -o bin/libtest.so -shared src/init.os -Lgodot-cpp/bin -l<name of the godot-cpp>
```
This creates the file `libtest.so` in your `SimpleLibrary/lib` directory. For windows you need to find out what compiler flags need to be used.
> This creates the file `libtest.so` in your `SimpleLibrary/bin` directory.
# Creating `.gdns` file
> You will need to replace `<name of the godot-cpp>` with the file that was created in [**Compiling the cpp bindings library**](#compiling-the-cpp-bindings-library)
*Windows*
```
$ cd SimpleLibrary
$ cl /Fosrc/init.obj /c src/init.cpp /nologo -EHsc -DNDEBUG /MDd /Igodot-cpp\include /Igodot-cpp\include\core /Igodot-cpp\include\gen /Igodot-cpp\godot_headers
$ link /nologo /dll /out:bin\libtest.dll /implib:bin\libsimple.lib src\init.obj godot-cpp\bin\<name of the godot-cpp>
```
> This creates the file `libtest.dll` in your `SimpleLibrary/bin` directory.
> You will need to replace `<name of the godot-cpp>` with the file that was created in [**Compiling the cpp bindings library**](#compiling-the-cpp-bindings-library)
> Finally replace `/MDd` with `/MD` if you're generated a runtime build.
*macOS*
For OSX you need to find out what compiler flags need to be used.
### Creating `.gdnlib` and `.gdns` files
follow [godot_header/README.md](https://github.com/GodotNativeTools/godot_headers/blob/master/README.md#how-do-i-use-native-scripts-from-the-editor) to create the `.gdns`
# Implementing with gdscript
### Implementing with gdscript
```gdscript
var simpleclass = load("res://simpleclass.gdns").new();
simpleclass.method("Test argument");

View File

@ -96,7 +96,10 @@ elif env['platform'] == 'windows':
env.Append(LINKFLAGS=['--static', '-Wl,--no-undefined', '-static-libgcc', '-static-libstdc++'])
env.Append(CPPPATH=['.', env['headers_dir'], 'include', 'include/core'])
env.Append(CPPPATH=['.', env['headers_dir'], 'include', 'include/gen', 'include/core'])
# Generate bindings?
json_api_file = ''
# Generate bindings?
json_api_file = ''
@ -104,7 +107,7 @@ json_api_file = ''
if ARGUMENTS.get('use_custom_api_file', 'no') == 'yes':
json_api_file = ARGUMENTS.get('custom_api_file', '')
else:
json_api_file = os.path.join(os.getcwd(), 'godot_api.json')
json_api_file = os.path.join(os.getcwd(), 'godot_headers', 'api.json')
if ARGUMENTS.get('generate_bindings', 'no') == 'yes':
# Actually create the bindings here
@ -113,9 +116,11 @@ if ARGUMENTS.get('generate_bindings', 'no') == 'yes':
binding_generator.generate_bindings(json_api_file)
# source to compile
sources = []
add_sources(sources, 'src/core', 'cpp')
add_sources(sources, 'src', 'cpp')
add_sources(sources, 'src/gen', 'cpp')
library = env.StaticLibrary(
target='bin/' + 'libgodot-cpp.{}.{}.{}'.format(env['platform'], env['target'], env['bits']), source=sources

View File

@ -21,19 +21,22 @@ def generate_bindings(path):
impl = generate_class_implementation(icalls, used_classes, c)
header_file = open("include/" + strip_name(c["name"]) + ".hpp", "w+")
header_file = open("include/gen/" + strip_name(c["name"]) + ".hpp", "w+")
header_file.write(header)
source_file = open("src/" + strip_name(c["name"]) + ".cpp", "w+")
source_file = open("src/gen/" + strip_name(c["name"]) + ".cpp", "w+")
source_file.write(impl)
icall_header_file = open("src/__icalls.hpp", "w+")
icall_header_file = open("src/gen/__icalls.hpp", "w+")
icall_header_file.write(generate_icall_header(icalls))
icall_source_file = open("src/__icalls.cpp", "w+")
icall_source_file = open("src/gen/__icalls.cpp", "w+")
icall_source_file.write(generate_icall_implementation(icalls))
register_types_file = open("src/gen/__register_types.cpp", "w+")
register_types_file.write(generate_type_registry(classes))
def is_reference_type(t):
for c in classes:
@ -79,6 +82,8 @@ def generate_class_header(used_classes, c):
# so don't include it here because it's not needed
if class_name != "Object" and class_name != "Reference":
source.append("#include <core/Ref.hpp>")
else:
source.append("#include <core/TagDB.hpp>")
included = []
@ -101,7 +106,6 @@ def generate_class_header(used_classes, c):
source.append("#include \"" + strip_name(c["base_class"]) + ".hpp\"")
source.append("namespace godot {")
source.append("")
@ -118,15 +122,40 @@ def generate_class_header(used_classes, c):
vararg_templates = ""
# generate the class definition here
source.append("class " + class_name + ("" if c["base_class"] == "" else (" : public " + strip_name(c["base_class"])) ) + " {")
source.append("class " + class_name + (" : public _Wrapped" if c["base_class"] == "" else (" : public " + strip_name(c["base_class"])) ) + " {")
if c["base_class"] == "":
source.append("public: enum { ___CLASS_IS_SCRIPT = 0, };")
source.append("private:")
source.append("")
if c["singleton"]:
source.append("\tstatic " + class_name + " *_singleton;")
source.append("")
source.append("\t" + class_name + "();")
source.append("")
source.append("public:")
source.append("")
# ___get_class_name
source.append("\tstatic inline char *___get_class_name() { return (char *) \"" + strip_name(c["name"]) + "\"; }")
source.append("\tstatic inline Object *___get_from_variant(Variant a) { return (Object *) a; }")
if c["singleton"]:
source.append("\tstatic inline " + class_name + " *get_singleton()")
source.append("\t{")
source.append("\t\tif (!" + class_name + "::_singleton) {")
source.append("\t\t\t" + class_name + "::_singleton = new " + class_name + ";")
source.append("\t\t}")
source.append("\t\treturn " + class_name + "::_singleton;")
source.append("\t}")
source.append("")
# godot::api->godot_global_get_singleton((char *) \"" + strip_name(c["name"]) + "\");"
# ___get_class_name
source.append("\tstatic inline const char *___get_class_name() { return (const char *) \"" + strip_name(c["name"]) + "\"; }")
source.append("\tstatic inline Object *___get_from_variant(Variant a) { godot_object *o = (godot_object*) a; return (Object *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, o); }")
enum_values = []
@ -146,17 +175,26 @@ def generate_class_header(used_classes, c):
if c["instanciable"]:
source.append("\tstatic void *operator new(size_t);")
source.append("\tstatic void operator delete(void *);")
source.append("")
source.append("")
source.append("\tstatic " + class_name + " *_new();")
source.append("\n\t// methods")
if class_name == "Object":
source.append("#ifndef GODOT_CPP_NO_OBJECT_CAST")
source.append("\ttemplate<class T>")
source.append("\tstatic T *cast_to(const Object *obj);")
source.append("#endif")
source.append("")
for method in c["methods"]:
method_signature = ""
method_signature += "static " if c["singleton"] else ""
# TODO decide what to do about virtual methods
# method_signature += "virtual " if method["is_virtual"] else ""
method_signature += make_gdnative_type(method["return_type"])
method_name = escape_cpp(method["name"])
method_signature += method_name + "("
@ -224,7 +262,7 @@ def generate_class_header(used_classes, c):
vararg_templates += "\ttemplate <class... Args> " + method_signature + "Args... args){\n\t\treturn " + method_name + "(" + method_arguments + "Array::make(args...));\n\t}\n"""
method_signature += "const Array& __var_args = Array()"
method_signature += ")" + (" const" if method["is_const"] and not c["singleton"] else "")
method_signature += ")" + (" const" if method["is_const"] else "")
source.append("\t" + method_signature + ";")
@ -234,11 +272,10 @@ def generate_class_header(used_classes, c):
source.append("")
source.append("}")
source.append("")
source.append("#endif")
@ -279,23 +316,20 @@ def generate_class_implementation(icalls, used_classes, c):
source.append("namespace godot {")
core_object_name = ("___static_object_" + strip_name(c["name"])) if c["singleton"] else "this"
core_object_name = "this"
source.append("")
source.append("")
if c["singleton"]:
source.append("static godot_object *" + core_object_name + ";")
source.append("" + class_name + " *" + class_name + "::_singleton = NULL;")
source.append("")
source.append("")
# FIXME Test if inlining has a huge impact on binary size
source.append("static inline void ___singleton_init()")
source.append("{")
source.append("\tif (" + core_object_name + " == nullptr) {")
source.append("\t\t" + core_object_name + " = godot::api->godot_global_get_singleton((char *) \"" + strip_name(c["name"]) + "\");")
source.append("\t}")
source.append(class_name + "::" + class_name + "() {")
source.append("\t_owner = godot::api->godot_global_get_singleton((char *) \"" + strip_name(c["name"]) + "\");")
source.append("}")
source.append("")
@ -304,19 +338,15 @@ def generate_class_implementation(icalls, used_classes, c):
if c["instanciable"]:
source.append("void *" + strip_name(c["name"]) + "::operator new(size_t)")
source.append(class_name + " *" + strip_name(c["name"]) + "::_new()")
source.append("{")
source.append("\treturn godot::api->godot_get_class_constructor((char *)\"" + c["name"] + "\")();")
source.append("}")
source.append("void " + strip_name(c["name"]) + "::operator delete(void *ptr)")
source.append("{")
source.append("\tgodot::api->godot_object_destroy((godot_object *)ptr);")
source.append("\treturn (" + class_name + " *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, godot::api->godot_get_class_constructor((char *)\"" + c["name"] + "\")());")
source.append("}")
for method in c["methods"]:
method_signature = ""
method_signature += make_gdnative_type(method["return_type"])
method_signature += strip_name(c["name"]) + "::" + escape_cpp(method["name"]) + "("
@ -332,15 +362,18 @@ def generate_class_implementation(icalls, used_classes, c):
method_signature += ", "
method_signature += "const Array& __var_args"
method_signature += ")" + (" const" if method["is_const"] and not c["singleton"] else "")
method_signature += ")" + (" const" if method["is_const"] else "")
source.append(method_signature + " {")
if c["singleton"]:
source.append("\t___singleton_init();")
if method["name"] == "free":
# dirty hack because Object::free is marked virtual but doesn't actually exist...
source.append("\tgodot::api->godot_object_destroy(_owner);")
source.append("}")
source.append("")
continue
else:
source.append("\tstatic godot_method_bind *mb = nullptr;")
source.append("\tif (mb == nullptr) {")
@ -408,7 +441,14 @@ def generate_class_implementation(icalls, used_classes, c):
source.append("")
source.append("\tVariant __result;")
source.append("\t*(godot_variant *) &__result = godot::api->godot_method_bind_call(mb, (godot_object *) " + core_object_name + ", (const godot_variant **) __args, " + size + ", nullptr);")
source.append("\t*(godot_variant *) &__result = godot::api->godot_method_bind_call(mb, ((const Object *) " + core_object_name + ")->_owner, (const godot_variant **) __args, " + size + ", nullptr);")
source.append("")
if is_class_type(method["return_type"]):
source.append("\tObject *obj = Object::___get_from_variant(__result);")
source.append("\tif (obj->has_method(\"reference\"))")
source.append("\t\tobj->callv(\"reference\", Array());")
source.append("")
@ -424,7 +464,7 @@ def generate_class_implementation(icalls, used_classes, c):
if is_reference_type(method["return_type"]):
cast += "Ref<" + strip_name(method["return_type"]) + ">::__internal_constructor(__result);"
else:
cast += "(" + strip_name(method["return_type"]) + " *) (Object *) __result;"
cast += "(" + strip_name(method["return_type"]) + " *) " + strip_name(method["return_type"] + "::___get_from_variant(") + "__result);"
else:
cast += "__result;"
source.append("\treturn " + cast)
@ -445,7 +485,7 @@ def generate_class_implementation(icalls, used_classes, c):
icall_name = get_icall_name(icall_sig)
return_statement += icall_name + "(mb, (godot_object *) " + core_object_name
return_statement += icall_name + "(mb, (const Object *) " + core_object_name
for arg in method["arguments"]:
return_statement += ", " + escape_cpp(arg["name"]) + (".ptr()" if is_reference_type(arg["type"]) else "")
@ -494,7 +534,7 @@ def generate_icall_header(icalls):
method_signature = ""
method_signature += return_type(ret_type) + get_icall_name(icall) + "(godot_method_bind *mb, godot_object *inst"
method_signature += return_type(ret_type) + get_icall_name(icall) + "(godot_method_bind *mb, const Object *inst"
for arg in args:
method_signature += ", const "
@ -547,7 +587,7 @@ def generate_icall_implementation(icalls):
method_signature = ""
method_signature += return_type(ret_type) + get_icall_name(icall) + "(godot_method_bind *mb, godot_object *inst"
method_signature += return_type(ret_type) + get_icall_name(icall) + "(godot_method_bind *mb, const Object *inst"
for i, arg in enumerate(args):
method_signature += ", const "
@ -568,7 +608,7 @@ def generate_icall_implementation(icalls):
source.append(method_signature + " {")
if ret_type != "void":
source.append("\t" + return_type(ret_type) + "ret;")
source.append("\t" + ("godot_object *" if is_class_type(ret_type) else return_type(ret_type)) + "ret;")
if is_class_type(ret_type):
source.append("\tret = nullptr;")
@ -581,7 +621,7 @@ def generate_icall_implementation(icalls):
if is_primitive(arg) or is_core_type(arg):
wrapped_argument += "(void *) &arg" + str(i)
else:
wrapped_argument += "(void *) arg" + str(i)
wrapped_argument += "(void *) arg" + str(i) + "->_owner"
wrapped_argument += ","
source.append(wrapped_argument)
@ -589,9 +629,16 @@ def generate_icall_implementation(icalls):
source.append("\t};")
source.append("")
source.append("\tgodot::api->godot_method_bind_ptrcall(mb, inst, args, " + ("nullptr" if ret_type == "void" else "&ret") + ");")
source.append("\tgodot::api->godot_method_bind_ptrcall(mb, inst->_owner, args, " + ("nullptr" if ret_type == "void" else "&ret") + ");")
if ret_type != "void":
if is_class_type(ret_type):
source.append("\tif (ret) {")
source.append("\t\treturn (Object *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, ret);")
source.append("\t}")
source.append("")
source.append("\treturn (Object *) ret;")
else:
source.append("\treturn ret;")
source.append("}")
@ -604,8 +651,44 @@ def generate_icall_implementation(icalls):
def generate_type_registry(classes):
source = []
source.append("#include \"TagDB.hpp\"")
source.append("#include <typeinfo>")
source.append("\n")
for c in classes:
source.append("#include <" + strip_name(c["name"]) + ".hpp>")
source.append("")
source.append("")
source.append("namespace godot {")
source.append("void ___register_types()")
source.append("{")
for c in classes:
class_name = strip_name(c["name"])
base_class_name = strip_name(c["base_class"])
class_type_hash = "typeid(" + class_name + ").hash_code()"
base_class_type_hash = "typeid(" + base_class_name + ").hash_code()"
if base_class_name == "":
base_class_type_hash = "0"
source.append("\tgodot::_TagDB::register_global_type(\"" + class_name + "\", " + class_type_hash + ", " + base_class_type_hash + ");")
source.append("}")
source.append("")
source.append("}")
return "\n".join(source)
@ -664,6 +747,8 @@ def get_used_classes(c):
def strip_name(name):
if len(name) == 0:
return name
if name[0] == '_':
return name[1:]
return name

File diff suppressed because it is too large Load Diff

@ -1 +1 @@
Subproject commit 2d221de20c7ddc297b8bb14f0dbd55c053eb3024
Subproject commit a51d905c5a34a441c1d4701b57c9de6548efbea3

View File

@ -21,5 +21,7 @@
#include "Vector2.hpp"
#include "Vector3.hpp"
#include "Wrapped.hpp"
#endif // CORETYPES_H

View File

@ -107,32 +107,45 @@ typedef float real_t;
#define _PLANE_EQ_DOT_EPSILON 0.999
#define _PLANE_EQ_D_EPSILON 0.0001
#ifndef ERR_FAIL_COND_V
#define ERR_FAIL_COND_V(cond, ret) do { if (cond) { return ret; } } while(0)
// ERR/WARN macros
#ifndef WARN_PRINT
#define WARN_PRINT(msg) fprintf(stdout, "ERROR: %s\n", msg); fflush(stdout)
#endif
#ifndef WARN_PRINTS
#define WARN_PRINTS(msg) WARN_PRINT((msg).utf8().get_data())
#endif
#ifndef ERR_PRINT
#define ERR_PRINT(x) fprintf(stderr, "ERROR: %s\n", x)
#endif
#ifndef ERR_PRINTS
#define ERR_PRINTS(msg) ERR_PRINT((msg).utf8().get_data())
#endif
#ifndef ERR_FAIL
#define ERR_FAIL() ERR_PRINT("Failed")
#endif
#ifndef ERR_FAIL_V
#define ERR_FAIL_V(a) return a
#define ERR_FAIL_V(a) { ERR_FAIL(); return a; }
#endif
#ifndef ERR_FAIL_COND
#define ERR_FAIL_COND(a) do { if (a) { ERR_PRINT(#a); return; } } while(0)
#endif
#ifndef ERR_FAIL_COND_V
#define ERR_FAIL_COND_V(cond, ret) do { if (cond) { ERR_PRINT(#cond); return ret; } } while(0)
#endif
#ifndef ERR_FAIL_INDEX
#define ERR_FAIL_INDEX(a, b)
#endif
#ifndef ERR_PRINT
#define ERR_PRINT(msg) fprintf(stderr, "ERROR: %S\n", (msg).unicode_str())
#define ERR_FAIL_INDEX(a, b) do { if (a < 0 || a >= b) { ERR_FAIL(); return; } } while(0)
#endif
#ifndef ERR_FAIL_INDEX_V
#define ERR_FAIL_INDEX_V(a, b, c)
#endif
#ifndef ERR_FAIL_COND
#define ERR_FAIL_COND(a) do { if (a) { fprintf(stderr, #a); return; } } while(0)
#define ERR_FAIL_INDEX_V(a, b, c) do { if (a < 0 || a >= b) { ERR_FAIL(); return c; } } while(0)
#endif

View File

@ -6,60 +6,52 @@
#include <gdnative_api_struct.gen.h>
#include <nativescript/godot_nativescript.h>
#include <typeinfo>
#include "CoreTypes.hpp"
#include "Variant.hpp"
#include "Ref.hpp"
#include "TagDB.hpp"
#include "Object.hpp"
#include "GodotGlobal.hpp"
#include <NativeScript.hpp>
#include <GDNativeLibrary.hpp>
namespace godot {
template<class T>
T *as(Object *obj)
T *as(const Object *obj)
{
return (T *) godot::nativescript_api->godot_nativescript_get_userdata(obj);
return (T *) godot::nativescript_api->godot_nativescript_get_userdata(obj->_owner);
}
template<class T>
class GodotScript {
public:
T *owner;
// GodotScript() {}
void _init() {}
static const char *___get_base_type_name()
T *get_wrapper(godot_object *obj)
{
return T::___get_class_name();
return (T *) godot::nativescript_1_1_api->godot_nativescript_get_instance_binding_data(godot::_RegisterState::language_index, obj);
}
static GodotScript<T> *___get_from_variant(Variant a)
{
return as<GodotScript<T> >((Object *) a);
}
static void _register_methods() {}
};
#define GODOT_CLASS(Name) \
#define GODOT_CLASS(Name, Base) \
public: inline static const char *___get_type_name() { return static_cast<const char *>(#Name); } \
enum { ___CLASS_IS_SCRIPT = 1, }; \
inline static Name *_new() { godot::NativeScript *script = godot::NativeScript::_new(); script->set_library(godot::get_wrapper<godot::GDNativeLibrary>((godot_object *) godot::gdnlib)); script->set_class_name(#Name); Name *instance = godot::as<Name>(script->new_()); return instance; } \
inline static const char *___get_base_type_name() { return Base::___get_class_name(); } \
inline static Object *___get_from_variant(godot::Variant a) { return (godot::Object *) godot::as<Name>(godot::Object::___get_from_variant(a)); } \
private:
#define GODOT_SUBCLASS(Name, Base) \
public: inline static const char *___get_type_name() { return static_cast<const char *>(#Name); } \
inline static const char *___get_base_type_name() { return static_cast<const char *>(#Base); } \
enum { ___CLASS_IS_SCRIPT = 1, }; \
inline static Name *_new() { godot::NativeScript *script = godot::NativeScript::_new(); script->set_library(godot::get_wrapper<godot::GDNativeLibrary>((godot_object *) godot::gdnlib)); script->set_class_name(#Name); Name *instance = godot::as<Name>(script->new_()); return instance; } \
inline static const char *___get_base_type_name() { return #Base; } \
inline static Object *___get_from_variant(godot::Variant a) { return (godot::Object *) godot::as<Name>(godot::Object::___get_from_variant(a)); } \
private:
template<class T>
struct _ArgCast {
static T _arg_cast(Variant a)
@ -94,7 +86,8 @@ template<class T>
void *_godot_class_instance_func(godot_object *p, void *method_data)
{
T *d = new T();
*(godot_object **) &d->owner = p;
d->_owner = p;
d->_type_tag = typeid(T).hash_code();
d->_init();
return d;
}
@ -116,8 +109,10 @@ void register_class()
godot_instance_destroy_func destroy = {};
destroy.destroy_func = _godot_class_destroy_func<T>;
_TagDB::register_type(typeid(T).hash_code(), typeid(T).hash_code());
godot::nativescript_api->godot_nativescript_register_class(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_base_type_name(), create, destroy);
godot::nativescript_1_1_api->godot_nativescript_set_type_tag(godot::_RegisterState::nativescript_handle, T::___get_type_name(), (const void *) typeid(T).hash_code());
T::_register_methods();
}
@ -130,8 +125,10 @@ void register_tool_class()
godot_instance_destroy_func destroy = {};
destroy.destroy_func = _godot_class_destroy_func<T>;
_TagDB::register_type(typeid(T).hash_code(), typeid(T).hash_code());
godot::nativescript_api->godot_nativescript_register_tool_class(godot::_RegisterState::nativescript_handle, T::___get_type_name(), T::___get_base_type_name(), create, destroy);
godot::nativescript_1_1_api->godot_nativescript_set_type_tag(godot::_RegisterState::nativescript_handle, T::___get_type_name(), (const void *) typeid(T).hash_code());
T::_register_methods();
}
@ -369,7 +366,7 @@ void register_property(const char *name, P (T::*var), P default_value, godot_met
usage = (godot_property_usage_flags) ((int) usage | GODOT_PROPERTY_USAGE_SCRIPT_VARIABLE);
if (def_val.get_type() == Variant::OBJECT) {
Object *o = def_val;
Object *o = get_wrapper<Object>(def_val.operator godot_object*());
if (o && o->is_class("Resource")) {
hint = (godot_property_hint) ((int) hint | GODOT_PROPERTY_HINT_RESOURCE_TYPE);
hint_string = o->get_class();
@ -490,6 +487,35 @@ void register_signal(String name, Args... varargs)
register_signal<T>(name, Dictionary::make(varargs...));
}
#ifndef GODOT_CPP_NO_OBJECT_CAST
template<class T>
T *Object::cast_to(const Object *obj)
{
size_t have_tag = (size_t) godot::nativescript_1_1_api->godot_nativescript_get_type_tag(obj->_owner);
if (have_tag) {
if (!godot::_TagDB::is_type_known((size_t) have_tag)) {
have_tag = 0;
}
}
if (!have_tag) {
have_tag = obj->_type_tag;
}
if (godot::_TagDB::is_type_compatible(typeid(T).hash_code(), have_tag)) {
return (T::___CLASS_IS_SCRIPT) ? godot::as<T>(obj) : (T *) obj;
} else {
return nullptr;
}
}
#endif
}
#endif // GODOT_H

View File

@ -5,11 +5,13 @@
#include "String.hpp"
#include "Array.hpp"
namespace godot {
extern "C" const godot_gdnative_core_api_struct *api;
extern "C" const godot_gdnative_ext_nativescript_api_struct *nativescript_api;
extern "C" const godot_gdnative_ext_nativescript_1_1_api_struct *nativescript_1_1_api;
extern "C" const void *gdnlib;
class Godot {
@ -21,6 +23,7 @@ public:
static void gdnative_init(godot_gdnative_init_options *o);
static void gdnative_terminate(godot_gdnative_terminate_options *o);
static void nativescript_init(void *handle);
static void nativescript_terminate(void *handle);
template <class... Args>
static void print(const String& fmt, Args... values) {
@ -32,6 +35,7 @@ public:
struct _RegisterState {
static void *nativescript_handle;
static int language_index;
};
}

View File

@ -3,7 +3,7 @@
#include "Variant.hpp"
#include "GodotGlobal.hpp"
#include "../Reference.hpp"
#include "Reference.hpp"
namespace godot {
@ -107,7 +107,7 @@ public:
void operator=(const Variant &p_variant) {
// TODO We need a safe cast
Reference *refb = (Reference *) (Object *) p_variant;
Reference *refb = (Reference *) T::___get_from_variant(p_variant);
if (!refb) {
unref();
return;
@ -156,7 +156,7 @@ public:
reference = nullptr;
// TODO We need a safe cast
Reference *refb = (Reference *) (Object *) p_variant;
Reference *refb = (Reference *) T::___get_from_variant(p_variant);
if (!refb) {
unref();
return;
@ -180,14 +180,14 @@ public:
if (reference && reference->unreference()) {
//memdelete(reference);
delete reference;
reference->free();
}
reference = nullptr;
}
void instance() {
//ref(memnew(T));
ref(new T);
ref(T::_new());
}
Ref() {

View File

@ -8,6 +8,7 @@ namespace godot {
class NodePath;
class Variant;
class PoolByteArray;
class PoolIntArray;
class PoolRealArray;
class PoolStringArray;
class String;
@ -120,6 +121,7 @@ public:
String sha256_text() const;
float similarity(String text) const;
PoolStringArray split(String divisor, bool allow_empty = true) const;
PoolIntArray split_ints(String divisor, bool allow_empty = true) const;
PoolRealArray split_floats(String divisor, bool allow_empty = true) const;
String strip_edges(bool left = true, bool right = true) const;
String substr(int from, int len) const;

19
include/core/TagDB.hpp Normal file
View File

@ -0,0 +1,19 @@
#ifndef TAGDB_HPP
#define TAGDB_HPP
#include <stddef.h>
namespace godot {
namespace _TagDB {
void register_type(size_t type_tag, size_t base_type_tag);
bool is_type_known(size_t type_tag);
void register_global_type(const char *name, size_t type_tag, size_t base_type_tag);
bool is_type_compatible(size_t type_tag, size_t base_type_tag);
}
}
#endif // TAGDB_HPP

View File

@ -225,7 +225,7 @@ public:
operator NodePath() const;
operator RID() const;
operator Object*() const;
operator godot_object*() const;
operator Dictionary() const;
operator Array() const;

16
include/core/Wrapped.hpp Normal file
View File

@ -0,0 +1,16 @@
#ifndef WRAPPED_HPP
#define WRAPPED_HPP
#include <gdnative/gdnative.h>
namespace godot {
class _Wrapped {
public:
godot_object *_owner;
size_t _type_tag;
};
}
#endif // WRAPPED_HPP

2
include/gen/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -269,7 +269,7 @@ Color Color::html(const String& p_color)
} else if (color.length()==6) {
alpha=false;
} else {
ERR_PRINT(String("Invalid Color Code: ") + p_color);
ERR_PRINTS(String("Invalid Color Code: ") + p_color);
ERR_FAIL_V(Color());
}
@ -277,7 +277,7 @@ Color Color::html(const String& p_color)
if (alpha) {
a=_parse_col(color,0);
if (a<0) {
ERR_PRINT("Invalid Color Code: "+p_color);
ERR_PRINTS(String("Invalid Color Code: ") + p_color);
ERR_FAIL_V(Color());
}
}
@ -286,17 +286,17 @@ Color Color::html(const String& p_color)
int r=_parse_col(color,from+0);
if (r<0) {
ERR_PRINT("Invalid Color Code: "+p_color);
ERR_PRINTS(String("Invalid Color Code: ") + p_color);
ERR_FAIL_V(Color());
}
int g=_parse_col(color,from+2);
if (g<0) {
ERR_PRINT("Invalid Color Code: "+p_color);
ERR_PRINTS(String("Invalid Color Code: ") + p_color);
ERR_FAIL_V(Color());
}
int b=_parse_col(color,from+4);
if (b<0) {
ERR_PRINT("Invalid Color Code: "+p_color);
ERR_PRINTS(String("Invalid Color Code: ") + p_color);
ERR_FAIL_V(Color());
}

View File

@ -2,11 +2,35 @@
#include "String.hpp"
#include "Wrapped.hpp"
static GDCALLINGCONV void *wrapper_create(void *data, const void *type_tag, godot_object *instance)
{
godot::_Wrapped *wrapper_memory = (godot::_Wrapped *) godot::api->godot_alloc(sizeof(godot::_Wrapped));
if (!wrapper_memory)
return NULL;
wrapper_memory->_owner = instance;
wrapper_memory->_type_tag = (size_t) type_tag;
return (void *) wrapper_memory;
}
static GDCALLINGCONV void wrapper_destroy(void *data, void *wrapper)
{
if (wrapper)
godot::api->godot_free(wrapper);
}
namespace godot {
void *_RegisterState::nativescript_handle;
int _RegisterState::language_index;
const godot_gdnative_core_api_struct *api = nullptr;
const godot_gdnative_ext_nativescript_api_struct *nativescript_api = nullptr;
const godot_gdnative_ext_nativescript_1_1_api_struct *nativescript_1_1_api = nullptr;
const void *gdnlib = NULL;
void Godot::print(const String& message)
{
@ -47,19 +71,33 @@ void Godot::print_error(const String& description, const String& function, const
if (c_file != nullptr) godot::api->godot_free(c_file);
}
void ___register_types();
void Godot::gdnative_init(godot_gdnative_init_options *options)
{
godot::api = options->api_struct;
godot::gdnlib = options->gd_native_library;
// now find our extensions
for (int i = 0; i < godot::api->num_extensions; i++) {
switch (godot::api->extensions[i]->type) {
case GDNATIVE_EXT_NATIVESCRIPT: {
godot::nativescript_api = (godot_gdnative_ext_nativescript_api_struct *)godot::api->extensions[i];
}; break;
godot::nativescript_api = (const godot_gdnative_ext_nativescript_api_struct *)godot::api->extensions[i];
const godot_gdnative_api_struct *extension = godot::nativescript_api->next;
while (extension) {
if (extension->version.major == 1 && extension->version.minor == 1) {
godot::nativescript_1_1_api = (const godot_gdnative_ext_nativescript_1_1_api_struct *) extension;
}
extension = extension->next;
}
} break;
default: break;
};
};
}
}
}
void Godot::gdnative_terminate(godot_gdnative_terminate_options *options)
@ -70,6 +108,19 @@ void Godot::gdnative_terminate(godot_gdnative_terminate_options *options)
void Godot::nativescript_init(void *handle)
{
godot::_RegisterState::nativescript_handle = handle;
godot_instance_binding_functions binding_funcs = {};
binding_funcs.alloc_instance_binding_data = wrapper_create;
binding_funcs.free_instance_binding_data = wrapper_destroy;
godot::_RegisterState::language_index = godot::nativescript_1_1_api->godot_nativescript_register_instance_binding_data_functions(binding_funcs);
___register_types();
}
};
void Godot::nativescript_terminate(void *handle)
{
godot::nativescript_1_1_api->godot_nativescript_unregister_instance_binding_data_functions(godot::_RegisterState::language_index);
}
}

View File

@ -219,7 +219,7 @@ bool String::begins_with_char_array(const char *p_char_array) const {
PoolStringArray String::bigrams() const {
godot_array arr = godot::api->godot_string_bigrams(&_godot_string);
return *(PoolStringArray *)&arr;
return *(Array *)&arr;
}
String String::c_escape() const {
@ -479,13 +479,19 @@ float String::similarity(String text) const {
PoolStringArray String::split(String divisor, bool allow_empty) const {
godot_array arr = godot::api->godot_string_split(&_godot_string, &divisor._godot_string);
return *(PoolStringArray *)&arr;
return *(Array *)&arr;
}
PoolIntArray String::split_ints(String divisor, bool allow_empty) const {
godot_array arr = godot::api->godot_string_split_floats(&_godot_string, &divisor._godot_string);
return *(Array *)&arr;
}
PoolRealArray String::split_floats(String divisor, bool allow_empty) const {
godot_array arr = godot::api->godot_string_split_floats(&_godot_string, &divisor._godot_string);
return *(PoolRealArray *)&arr;
return *(Array *)&arr;
}
String String::strip_edges(bool left, bool right) const {

55
src/core/TagDB.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "TagDB.hpp"
#include <unordered_map>
#include <GodotGlobal.hpp>
namespace godot {
namespace _TagDB {
std::unordered_map<size_t, size_t> parent_to;
void register_type(size_t type_tag, size_t base_type_tag)
{
if (type_tag == base_type_tag) {
return;
}
parent_to[type_tag] = base_type_tag;
}
bool is_type_known(size_t type_tag)
{
return parent_to.find(type_tag) != parent_to.end();
}
void register_global_type(const char *name, size_t type_tag, size_t base_type_tag)
{
godot::nativescript_1_1_api->godot_nativescript_set_global_type_tag(godot::_RegisterState::language_index, name, (const void *) type_tag);
register_type(type_tag, base_type_tag);
}
bool is_type_compatible(size_t ask_tag, size_t have_tag)
{
if (have_tag == 0)
return false;
size_t tag = have_tag;
while (tag != 0) {
if (tag == ask_tag)
return true;
tag = parent_to[tag];
}
return false;
}
}
}

View File

@ -5,6 +5,7 @@
#include "Defs.hpp"
#include "CoreTypes.hpp"
#include "GodotGlobal.hpp"
#include "Object.hpp"
#include <iostream>
@ -141,7 +142,7 @@ Variant::Variant(const RID& p_rid)
Variant::Variant(const Object* p_object)
{
godot::api->godot_variant_new_object(&_godot_variant, (godot_object *) p_object);
godot::api->godot_variant_new_object(&_godot_variant, p_object->_owner);
}
Variant::Variant(const Dictionary& p_dictionary)
@ -363,9 +364,8 @@ Variant::operator PoolColorArray() const
godot_pool_color_array s = godot::api->godot_variant_as_pool_color_array(&_godot_variant);
return *(PoolColorArray *) &s;
}
Variant::operator Object*() const {
godot_object *o = godot::api->godot_variant_as_object(&_godot_variant);
return (Object *) o;
Variant::operator godot_object*() const {
return godot::api->godot_variant_as_object(&_godot_variant);
}
Variant::Type Variant::get_type() const

View File

@ -229,12 +229,12 @@ real_t Vector3::length_squared() const
real_t Vector3::distance_squared_to(const Vector3& b) const
{
return (b-*this).length();
return (b-*this).length_squared();
}
real_t Vector3::distance_to(const Vector3& b) const
{
return (b-*this).length_squared();
return (b-*this).length();
}
real_t Vector3::dot(const Vector3& b) const

2
src/gen/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore