godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.78k stars 21.13k forks source link

Integrate javascript support as scripting language #937

Closed yuri-karadzhov closed 9 years ago

yuri-karadzhov commented 9 years ago

As javascript is well known and really popular language it will be grate for godot to implement it as scripting language. As godot already supports C it can be done with help of this project http://duktape.org/

Geequlim commented 5 years ago

@rosshadden There are a lot of tools to compile TypeScript/ES6 to ES5. So don't worry about that.

Here is an example it works with my binding in typescript

@gdclass("MyNode", "res://icon.png")
class MyNode extends godot.Control {

    private time = 0;
    private label = null;

    constructor() {
      super();
      print("MyNode.constructor");
    }

    _ready() {
        print("MyNode._ready");
        this.label = this.get_node("Label");
    }

    _process(delta) {
        this.time += delta;
        this.label.text = `${this.time}`;
    }
}

Some feature like Promise or setTimeout won't be supported as we don't inruduce something like libuv. We may implement that in godot way in the future.

Geequlim commented 5 years ago

It starting work with my duktape binding. This is the pong demo implemented in TypeScript. 2019-02-22 22-54-26

arimus commented 5 years ago

Nice! Do you happen to have build instructions or that sample project available for testing? I can give it a whack on my side too.

On Fri, Feb 22, 2019, 7:02 AM Geequlim notifications@github.com wrote:

It starting work with my duktape binding. This is the pong demo implemented in TypeScript. [image: 2019-02-22 22-54-26] https://user-images.githubusercontent.com/6964556/53250631-bef31580-36f5-11e9-9d2b-8944d00964f4.png

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/godotengine/godot/issues/937#issuecomment-466426177, or mute the thread https://github.com/notifications/unsubscribe-auth/AAN8Kmsh-ozDLdb5JJOxxKhkkoyH2RkFks5vQAZvgaJpZM4DFSv6 .

Geequlim commented 5 years ago

Here is an example project to use TypeScript in godot

https://github.com/Geequlim/ECMAScriptDemos/tree/master/HelloWorld-TS

image

Geequlim commented 5 years ago

@jkb0o Tested with the duktape implement of JS binding. The BunnymarkV2 test case shows the duktape binding is about 2.1 times slower than GDScript.

arimus commented 5 years ago

@Geequlim do you have more information / build instructions for this module? Getting the following when dropping your folder into modules/

modules/ECMAScript//duktape/builtin_binding_generator.py
Traceback (most recent call last):
  File "modules/ECMAScript//duktape/builtin_binding_generator.py", line 218, in <module>
    generate_duktape_builtin_bindings()
  File "modules/ECMAScript//duktape/builtin_binding_generator.py", line 215, in generate_duktape_builtin_bindings
    file.write(generate_binding_code())
  File "modules/ECMAScript//duktape/builtin_binding_generator.py", line 197, in generate_binding_code
    open(API_FILE, 'r', encoding='utf8')
TypeError: 'encoding' is an invalid keyword argument for this function

Even after removing the encoding flag from the script, I get a number of additional errors:

modules/ECMAScript/tools/editor_tools.cpp:27:8: warning: use of enumeration in a nested name specifier is a C++11 extension [-Wc++11-extensions]
                case MenuItem::ITEM_RELOAD_LIBS:
                     ^
modules/ECMAScript/tools/editor_tools.cpp:30:8: warning: use of enumeration in a nested name specifier is a C++11 extension [-Wc++11-extensions]
                case MenuItem::ITEM_GEN_DECLAR_FILE:
                     ^
modules/ECMAScript/tools/editor_tools.cpp:61:9: error: conversion from 'long' to 'Variant' is ambiguous
        return NULL;
               ^~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.0/include/stddef.h:100:18: note: expanded from macro 'NULL'
#    define NULL __null
                 ^~~~~~
./core/variant.h:239:2: note: candidate constructor
        Variant(bool p_bool);
        ^

Building on OSX atm and I can build godot without this module just fine. What are the build requirements? For instance, does the build require a version of Python > 2.7 (osx system default)?

Any assistance would be helpful. Thanks.

Geequlim commented 5 years ago

Not tested on osx and clang. Could you give me more information about compile error instead warnings ?

arimus notifications@github.com 于 2019年3月6日周三 10:31写道:

@Geequlim https://github.com/Geequlim do you have more information / build instructions for this module? Getting the following when dropping your folder into modules/

modules/ECMAScript//duktape/builtin_binding_generator.py Traceback (most recent call last): File "modules/ECMAScript//duktape/builtin_binding_generator.py", line 218, in generate_duktape_builtin_bindings() File "modules/ECMAScript//duktape/builtin_binding_generator.py", line 215, in generate_duktape_builtin_bindings file.write(generate_binding_code()) File "modules/ECMAScript//duktape/builtin_binding_generator.py", line 197, in generate_binding_code open(API_FILE, 'r', encoding='utf8') TypeError: 'encoding' is an invalid keyword argument for this function

Building on OSX atm. What are the build requirements? For instance, does the build require a version of Python > 2.7 (osx system default)?

Any assistance would be helpful. Thanks.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/godotengine/godot/issues/937#issuecomment-469942621, or mute the thread https://github.com/notifications/unsubscribe-auth/AGpFTFgxJI-OYGyLbAlj1FoHvlnMA1HQks5vTyiagaJpZM4DFSv6 .

arimus commented 5 years ago

That stack above has 2 warnings to start with, but that third entry is an error...

modules/ECMAScript/tools/editor_tools.cpp:61:9: error: conversion from 'long' to 'Variant' is ambiguous
        return NULL;
               ^~~~
Geequlim commented 5 years ago

@arimus I updated the code and push it to the module repo. Could you test it and feedback here https://github.com/Geequlim/ECMAScript/issues/2 ?

arimus commented 5 years ago

@Geequlim Seems better, although I still had a few errors like:

$ scons platform=osx
scons: Reading SConscript files ...
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Checking for C header file mntent.h... (cached) no
scons: done reading SConscript files.
scons: Building targets ...
[  7%] Compiling ==> modules/ECMAScript/duktape/duktape_binding_helper.cpp
modules/ECMAScript/duktape/duktape_binding_helper.cpp:432:13: error: conversion from 'long' to 'Variant' is ambiguous
                                Variant re = NULL;
                                        ^    ~~~~
./core/variant.h:239:2: note: candidate constructor
        Variant(bool p_bool);
        ^

Forgive me if I went down the wrong path here, but I made a couple changes to get things compiling. I'm not sure they are correct, given my inexperience with the internals of Godot.

$ git diff
diff --git a/duktape/duktape_binding_helper.cpp b/duktape/duktape_binding_helper.cpp
index 1eb665c..de08322 100644
--- a/duktape/duktape_binding_helper.cpp
+++ b/duktape/duktape_binding_helper.cpp
@@ -455,7 +455,7 @@ Variant DuktapeBindingHelper::duk_get_godot_variant(duk_context *ctx, duk_idx_t
                                                ret = *(static_cast<Transform2D*>(ptr));
                                                break;
                                        default:
-                                               ret = NULL;
+                                               ret = Variant::NIL;
                                                break;
                                }
                                return ret;
@@ -1078,12 +1078,12 @@ ECMAScriptGCHandler DuktapeBindingHelper::create_ecma_instance_for_godot_object(
 Variant DuktapeBindingHelper::call_method(const ECMAScriptGCHandler &p_object, const ECMAMethodInfo &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
        if (p_object.is_null()) {
                r_error.error = Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL;
-               return NULL;
+               return Variant::NIL;
        }

        if (p_method.is_null()) {
                r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
-               return NULL;
+               return Variant::NIL;
        }
        duk_push_heapptr(ctx, p_method.ecma_object);
        duk_push_heapptr(ctx, p_object.ecma_object);
diff --git a/ecmascript_instance.cpp b/ecmascript_instance.cpp
index 78cf76c..345471a 100644
--- a/ecmascript_instance.cpp
+++ b/ecmascript_instance.cpp
@@ -58,7 +58,7 @@ Variant::Type ECMAScriptInstance::get_property_type(const StringName &p_name, bo

 Variant ECMAScriptInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {

-       ERR_FAIL_COND_V(script.is_null() || ecma_object.is_null(), NULL);
+       ERR_FAIL_COND_V(script.is_null() || ecma_object.is_null(), Variant::NIL);

        ECMAClassInfo *cls = script->get_ecma_class();
        if (cls == NULL) {

I was able to build, run and even run the Hello World demo! But something along the way broke when trying to run Pong and now it seems I can't even run Hello World any more. Godot dies with the following:

0: ./bin/godot.osx.tools.64
Current path: /Users/arimus/workspace/godot
OpenGL ES 3.0 Renderer: NVIDIA GeForce GT 650M OpenGL Engine
Editing project: /Users/arimus/workspace/ECMAScriptDemos/hello_world (::Users::arimus::workspace::ECMAScriptDemos::hello_world)
arguments
0: /Users/arimus/workspace/godot/bin/godot.osx.tools.64
1: --path
2: /Users/arimus/workspace/ECMAScriptDemos/hello_world
3: --editor
Current path: /Users/arimus/workspace/godot
handle_crash: Program crashed with signal 4
Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues
[1] 1   libsystem_platform.dylib            0x00007fff75909f5a _sigtramp + 26
OpenGL ES 3.0 Renderer: NVIDIA GeForce GT 650M OpenGL Engine
[2] 2   ???                                 0x0000000000000006 0x0 + 6
ERROR: grab_focus: Condition ' !is_inside_tree() ' is true.
   At: scene/gui/control.cpp:2074.
ERROR: safe_eval_text: Condition ' Thread::get_caller_id() != Thread::get_main_id() ' is true. returned: ERR_UNAVAILABLE
   At: modules/ECMAScript/duktape/duktape_binding_helper.cpp:153.
ERROR: set_path: Another resource is loaded from path: res://ECMAClass/Main.es (possible cyclic resource inclusion)
   At: core/resource.cpp:79.
Geequlim commented 5 years ago

@arimus Your diff content seems contains error. But I'm not sure the crash of yours is because of that. I updated the code again it should compiles with clang. Make sure your typescript is compiled to JS before you run the code without the editor mode.

arimus commented 5 years ago

To compile, I still had one error with this line of code:

$ git diff -u
diff --git a/duktape/duktape_binding_helper.cpp b/duktape/duktape_binding_helper.cpp
index c6ee235..f9f7d67 100644
--- a/duktape/duktape_binding_helper.cpp
+++ b/duktape/duktape_binding_helper.cpp
@@ -462,7 +462,7 @@ Variant DuktapeBindingHelper::duk_get_godot_variant(duk_context *ctx, duk_idx_t
                                                ret = *(static_cast<Basis*>(ptr));
                                                break;
                                        default:
-                                               ret = NULL;
+                                               ret = Variant::NIL;
                                                break;
                                }
                                return ret;

Compiled fine, but still having issues loading up the project in the editor:

$ ./bin/godot.osx.tools.64
arguments
0: ./bin/godot.osx.tools.64
Current path: /Users/arimus/workspace/godot
OpenGL ES 3.0 Renderer: NVIDIA GeForce GT 650M OpenGL Engine
Editing project: /Users/arimus/workspace/ECMAScriptDemos/pong (::Users::arimus::workspace::ECMAScriptDemos::pong)
arguments
0: /Users/arimus/workspace/godot/bin/godot.osx.tools.64
1: --path
2: /Users/arimus/workspace/ECMAScriptDemos/pong
3: --editor
Current path: /Users/arimus/workspace/godot
handle_crash: Program crashed with signal 4
Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues
OpenGL ES 3.0 Renderer: NVIDIA GeForce GT 650M OpenGL Engine
[1] 1   libsystem_platform.dylib            0x00007fff75909f5a _sigtramp + 26
[2] 2   ???                                 0x0000000000000000 0x0 + 0
ERROR: grab_focus: Condition ' !is_inside_tree() ' is true.
   At: scene/gui/control.cpp:2074.
ERROR: safe_eval_text: Condition ' Thread::get_caller_id() != Thread::get_main_id() ' is true. returned: ERR_UNAVAILABLE
   At: modules/ECMAScript/duktape/duktape_binding_helper.cpp:153.
ERROR: safe_eval_text: Condition ' Thread::get_caller_id() != Thread::get_main_id() ' is true. returned: ERR_UNAVAILABLE
   At: modules/ECMAScript/duktape/duktape_binding_helper.cpp:153.
ERROR: safe_eval_text: Condition ' Thread::get_caller_id() != Thread::get_main_id() ' is true. returned: ERR_UNAVAILABLE
   At: modules/ECMAScript/duktape/duktape_binding_helper.cpp:153.
ERROR: safe_eval_text: Condition ' Thread::get_caller_id() != Thread::get_main_id() ' is true. returned: ERR_UNAVAILABLE
   At: modules/ECMAScript/duktape/duktape_binding_helper.cpp:153.
Geequlim commented 5 years ago

@arimus Can you provide the demo project which crashes your editor?

This error won't crash your editor

ERROR: safe_eval_text: Condition ' Thread::get_caller_id() != Thread::get_main_id() ' is true. returned: ERR_UNAVAILABLE
   At: modules/ECMAScript/duktape/duktape_binding_helper.cpp:153.

BTW we should move to a repo to avoid disturbing other people who are watching Godot.

Geequlim commented 4 years ago

The implementation of duktape binding proves that the way of abstracting the JavaScript binding process works. It is also confirmed that the performance of duktape binding is difficult to optimize to a satisfactory level.

After a few months I gave up to optimize the duktape binding. The QuickJS was born. It supports the latest ECMAScript specification while being as lightweight as duktape, and has slightly more performance than Lua. I think it is the most suitable JS engine for me to binding to godot.

Now the duktape has now been replaced by QuickJS. At the same time, it supports more complete functions such as builtin operator overloading, multi-threading, etc. The performance test results are also acceptabl.

For everyone who interested in JavaScript binding of godot. You can get the code in this repository. And you can also get compiled binary with godot 3.2beta for Windows and Linux platform.

https://github.com/Geequlim/ECMAScript

claytongulick commented 4 years ago

@Geequlim this is amazing work, thanks!!

One question: have you thought about trying deno?

It's a NodeJS replacement, with one of the stated project goals being simple v8 embedding for external languages (v8 is a beast to build).

Geequlim commented 4 years ago

@clayjohn Deno is an application but not an ECMAScript engine. So it is not something meaningful for godot engine.

claytongulick commented 4 years ago

@Geequlim afaik, deno was also designed to be easily embedded in rust, and c/++ via libdeno

https://denolib.gitbook.io/guide/codebase-basics/infrastructure

https://github.com/denolib/guide/blob/master/advanced/interaction-with-v8.md

It ships with a rust crate for this purpose

https://crates.io/crates/deno_core

I'm a big fan of quickjs, but if you really wanted to use v8, maybe borrowing from the hard work the deno folks have done on the v8 build system and exposing isolated via c and rs api is worth checking out?

Geequlim commented 4 years ago

@claytongulick Thnaks for your advice. If I really want V8 I'd like to integrate it in C++ to avoid itroduce more dependencies.