diff --git a/docs/development/compiler-options.md b/docs/development/compiler-options.md new file mode 100644 index 0000000..08d10da --- /dev/null +++ b/docs/development/compiler-options.md @@ -0,0 +1,14 @@ +# Compiler Options + +| Name | Description | Default Value | +|---|---|---| +|JSB_MIN_LOG_LEVEL|minimum log level|Verbose| +|JSB_DEBUG| Debug mode.
It's overwritten in SCsub with the value of `DEV_ENABLED` by default. | 1 | +|JSB_LOG_WITH_SOURCE|log with [source filename, line number, function name]|0| +|JSB_WITH_VARIANT_POOL| use a pool allocator for creating variant instances | 1 | +|JSB_WITH_DEBUGGER| enable to debug with Chrome devtools | 1| +|JSB_WITH_SOURCEMAP|translate the js source stacktrace with source map (currently, the `.map` file must locate at the same filename & directory of the js source) | 1| +|JSB_WITH_STACKTRACE_ALWAYS| enable this to let all methods in `console` print with js stacktrace|0| +|JSB_WITH_LWS|JSB_WITH_LWS must be enabled if JSB_WITH_DEBUGGER is used.
Currently use `libwebsockets` to handle v8 debugger connection since `modules/websocket` fail to handshake with `devtools`.
`devtools` do not response the upgrade request with a `sec-websocket-protocol` header which does not apply the handshake requirements of `WSLPeer`
and the connection will break immediately by `devtools` if `selected_protocol` is assigned manually in `WSLPeer`|1| + + diff --git a/docs/development/dependencies.md b/docs/development/dependencies.md new file mode 100644 index 0000000..59bdb70 --- /dev/null +++ b/docs/development/dependencies.md @@ -0,0 +1,12 @@ +# Dependencies + +| Runtime | Version | | +| -------------- | --------------------- | -------------------------------------------------------- | +| v8 | refs/tags/12.4.254.21 | https://v8.dev/docs/build | +| JavaScriptCore | _System Bundled_ | https://developer.apple.com/documentation/javascriptcore | +| quickjs | 2024-01-13 | https://bellard.org/quickjs/ | +| quickjs-ng | 0.8.0 | https://github.com/quickjs-ng/quickjs/ | +| godot | 4.2.2-stable | https://github.com/godotengine/godot | +| libwebsockets | 4.3.3-13-g6901c32a | https://libwebsockets.org/ | + +> **NOTE:** `quickjs` is included with minor modifications for directly compiling with MSVC. diff --git a/docs/development/godot-js-debugging.md b/docs/development/godot-js-debugging.md new file mode 100644 index 0000000..9ec6416 --- /dev/null +++ b/docs/development/godot-js-debugging.md @@ -0,0 +1,32 @@ +# Logging + +All logging levels are defined in _jsb_log_severity.def.h_: + +- `VeryVerbose`: very trivial messages (omitted by default even if `JSB_DEBUG` is on) +- `Verbose`: trivial messges (will not output in the editor panel) +- `Debug`: not important, also used by _console.debug_ +- `Info`: general level, also used by _console.info_ +- `Log`: used by _console.log_ +- `Trace`: used by _console.trace_, with _stacktrace_ anyway +- `Warning`: used by _console.warn_ +- `Error`: used by _console.error_, unexpected but not critical errors +- `Assert`: used by _console.assert_, print only if assertion failed +- `Fatal`: critial errors + +By default, `VeryVerbose` is omitted at _compile-time_. You can change it in _jsb.config.h_ by setting `JSB_MIN_LOG_LEVEL` to `VeryVerbose`. +`Verbose` messages will be displayed only if the `--verbose` flag is set on the command line for the godot executable, and they will appear exclusively in the console (launch from IDE, or start godot from the command line). + +![logging_01](images/logging_01.png) + +All messages start with a category name that indicates where they are originated: + +- `JS`: messages from JS scripts (all `console.*` calls) +- `jsb`: all general messages in GodotJS +- `JSWorker`: messages from GodotJS worker implementation +- `JSDebugger`: messages from GodotJS debugger bridge +- `JSProcess`: sub-process spawned in GodotJS (`tsc` for instance) +- `JSExporter`: messages from the export plugin for packaging +- `jsc`: JavaScriptCore bridge implementation +- `quickjs`: QuickJS bridge implementation +- `web`: Web bridge implementation + diff --git a/docs/development/godot-unit-testing.md b/docs/development/godot-unit-testing.md new file mode 100644 index 0000000..2ba49df --- /dev/null +++ b/docs/development/godot-unit-testing.md @@ -0,0 +1,13 @@ +# Godot Unit Testing + +```sh +# An example on Windows: +scons tests=yes vsproj=yes dev_build=yes p=windows + +# build godot +# ... + +# After the godot is built +bin/your_godot_bin_file --test --test-case="[jsb]*" + +``` diff --git a/docs/development/images/logging_01.png b/docs/development/images/logging_01.png new file mode 100644 index 0000000..0bf5840 Binary files /dev/null and b/docs/development/images/logging_01.png differ diff --git a/docs/documentation/api.md b/docs/documentation/api.md deleted file mode 100644 index 8ed8da3..0000000 --- a/docs/documentation/api.md +++ /dev/null @@ -1,100 +0,0 @@ -All of Godot's APIs are defined within the `godot` namespace. - -No API names have been renamed or changed, so you shouldn't need to change your habits. - -| GDScript | JavaScript | -| ---------------------- | ---------------------------- | -| null | null | -| int | number | -| float | number | -| String | string | -| Array | Array | -| Dictionary | Object | -| NodePath | string | -| Object | godot.Object | -| Resource | godot.Resource | -| Vector2 | godot.Vector2 | -| Color | godot.Color | -| sin(v) | godot.sin(v) | -| print(v) | godot.print(v) | -| PI | godot.PI | -| Color.black | godot.Color.black | -| Control.CursorShape | godot.Control.CursorShape | -| Label.Align.ALIGN_LEFT | godot.Label.Align.ALIGN_LEFT | - -## API specification: - -- Keys of Dictionary are converted to String in JavaScript -- Signals are defined as constants to their classes - ``` - godot.Control.resized === 'resized' // true - ``` - -### Additional functions - -- `godot.register_signal(cls, signal_name)` to register signals -- `godot.register_property(cls, name, default_value)` to define and export properties -- `godot.register_class(cls, name)` to register named class manually -- `godot.set_script_tooled(cls, tooled)` to set `tooled` of the class -- `godot.set_script_icon(cls, path)` to set icon of the class -- `godot.get_type(val)` Returns the internal type of the given `Variant` object, using the `godot.TYPE_*` -- `godot.yield(target, signal)` Returns a Promise which will be resolved when the signal emitted -- `requestAnimationFrame(callback)` registers a callback function to be called every frame, returns a request ID. -- `cancelAnimationFrame(request_id)` to cancel a previously scheduled frame request -- `require(module_id)` to load a CommonJS module or load a resource file -- `$` is the alias of `Node.get_node` - -### Using signals - -Allow passing functions for `godot.Object.connect`, `godot.Object.disconnect`, and `godot.Object.is_connected`: - -```js -this.panel.connect(godot.Control.resized, (size) => { - console.log("The size of the panel changed to:", size); -}); -``` - -Using `await` to wait for signals - -```js -await godot.yield( - this.get_tree().create_timer(1), - godot.SceneTreeTimer.timeout -); -console.log("After one second to show"); -``` - -Preload resources with ECMAScript import statement - -```js -import ICON from "res://icon.png"; -``` - -### Multi-threading - -Multi-threading with minimal [Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Worker) (**This is an experimental feature**) - -Start a new thread with Worker: - -```js -const worker = new Worker("worker.js"); // Run worker.js in a new thread context -worker.postMessage({ type: "load_dlc", value: "dlc01.pck" }); -worker.onmessage = function (msg) { - console.log("[MainThread] received message from worker thread:", msg); -}; -``` - -Transfer value in different thread context with `godot.abandon_value` and `godot.adopt_value`: - -```js -// In worker thread -let id = godot.abandon_value(object); -postMessage({ type: "return_value", id: id }); - -// In the host thread -worker.onmessage = function (msg) { - if (typeof msg === "object" && msg.type === "return_value") { - let value_from_worker = godot.adopt_value(msg.id); - } -}; -``` diff --git a/docs/documentation/building-from-source/images/web_build.png b/docs/documentation/building-from-source/images/web_build.png new file mode 100644 index 0000000..7b7ea5f Binary files /dev/null and b/docs/documentation/building-from-source/images/web_build.png differ diff --git a/docs/documentation/building-from-source/index.md b/docs/documentation/building-from-source/index.md new file mode 100644 index 0000000..ea85d6e --- /dev/null +++ b/docs/documentation/building-from-source/index.md @@ -0,0 +1,30 @@ +# Build from Source + +You can build different runtimes from source. +The following runtimes are available: + +- [`v8`](v8.md) is proven to be one of the most powerful and high-performance JavaScript runtimes. +- [`QuickJS`](quick-js.md) is a remarkable and lightweight option. +- [`JavaScriptCore`](javascript-core.md) is the built-in JavaScript engine for WebKit and bundled with macOS/iOS. +- [`Web`](web.md) is only suitable when building for Web. All scripts run on the host browser JS VM rather than an additional interpreter. + +## Supported Platforms + +| | v8 | quickjs | quickjs-ng | Web Builtin JS | JavaScriptCore | +| -------------- | ---------------- | --------------- | --------------- | ------------------- | --------------- | +| Windows:x86_64 | ✅ | ✅ | ✅ | ❌ | ❌ | +| Windows:arm64 | ✅ | ✅ | ✅ | ❌ | ❌ | +| MacOS:x86_64 | ✅ (not tested) | ✅ (not tested) | ✅ (not tested) | ❌ | ✅ (not tested) | +| MacOS:arm64 | ✅ | ✅ | ✅ | ❌ | ✅ (debugging) | +| Linux:x86_64 | ✅ (not tested) | ✅ (not tested) | ✅ | ❌ | ❌ | +| Linux:arm64 | ✅ | ✅ | ✅ | ❌ | ❌ | +| Android:x86_64 | ✅ (not tested) | ✅ (not tested) | ✅ (not tested) | ❌ | ❌ | +| Android:arm64 | ✅ | ✅ (not tested) | ✅ (not tested) | ❌ | ❌ | +| iOS:x86_64 | ✅ (not tested) | ✅ (not tested) | ✅ (not tested) | ❌ | ✅ (not tested) | +| iOS:arm64 | ✅ (not tested) | ✅ (not tested) | ✅ (not tested) | ❌ | ✅ (not tested) | +| Web:wasm32 | ❌ | ✅ (not tested) | ✅ (not tested) | ✅ (debugging) | ❌ | +| Debugger | ✅ Chrome/VSCode | ❌ | ❌ | ✅ browser devtools | ✅ Safari | + +> Android: only tested on ndk_platform=android-24 +> Web: only tested on emsdk-3.1.64 +> JavaScriptCore: macOS 15, iOS 18 (support for lower versions may be implemented in future versions) diff --git a/docs/documentation/building-from-source/javascript-core.md b/docs/documentation/building-from-source/javascript-core.md new file mode 100644 index 0000000..5ace5c8 --- /dev/null +++ b/docs/documentation/building-from-source/javascript-core.md @@ -0,0 +1,11 @@ +# JavaScriptCore + +To enable `JavaScriptCore`, please run *scons* with the parameter `use_jsc=yes`. + +> **NOTE:** `JavaScriptCore` is only available on macOS/iOS since the system bundled `JavaScriptCore.framework` is used. + +```sh +# An example on macOS: +scons compiledb=yes dev_build=yes use_jsc=yes +``` + diff --git a/docs/documentation/building-from-source/quick-js.md b/docs/documentation/building-from-source/quick-js.md new file mode 100644 index 0000000..5dbd8db --- /dev/null +++ b/docs/documentation/building-from-source/quick-js.md @@ -0,0 +1,22 @@ +# QuickJS + +To enable `QuickJS`, please run *scons* with the parameter `use_quickjs=yes`, +or `use_quickjs_ng=yes` if [quickjs-ng](https://github.com/quickjs-ng/quickjs) is preferred. + +> **NOTE:** `QuickJS` is also available for WebBuild. + +If you choose *quickjs-ng*, please clone the source with submodules: +```sh +# If it's a fresh clone +git clone --recurse-submodules https://github.com/godotjs/GodotJS.git + +# If you've already cloned it prior +git submodule update --init --recursive +``` + +Then, build *Godot* from source: +```sh +# An example on Windows: +scons vsproj=yes dev_build=yes p=windows use_quickjs_ng=yes +``` + diff --git a/docs/documentation/building-from-source/v8.md b/docs/documentation/building-from-source/v8.md new file mode 100644 index 0000000..f1f65f2 --- /dev/null +++ b/docs/documentation/building-from-source/v8.md @@ -0,0 +1,176 @@ +# V8 + +`v8` is used as the default JavaScript runtime. + +## Build V8 from Source (Optional) + +> **NOTE:** See [Setup V8 in GodotJS](#setup-v8-in-godotjs) +> if a prebuilt version of v8 from [GodotJS-Dependencies](https://github.com/ialex32x/GodotJS-Dependencies/releases) is used. + +Building *V8* from source is needed if you do not want to use the prebuilt +version from [GodotJS-Dependencies](https://github.com/ialex32x/GodotJS-Dependencies/releases). Follow the steps below: + +**STEP 1** +Download `depot_tools` from https://storage.googleapis.com/chrome-infra/depot_tools.zip if using `Windows`, and extract it to a path you want. + +Otherwise, get `depot_tools` from the git repository: + +``` +git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git +``` + +**STEP 2** + +Add the `depot_tools` path into the environment variable `PATH`. + +On `windows`: + +```bat +set PATH=Your\Path\To\depot_tools;%PATH% +set DEPOT_TOOLS_WIN_TOOLCHAIN=0 +``` + +> **NOTE:** If `DEPOT_TOOLS_WIN_TOOLCHAIN` is not set, `depot_tools` will fail to build because it will try to use the google internal toolchain instead of the locally installed Visual Studio. + +On `linux`, `macos`: +```sh +export PATH=Your/Path/To/depot_tools:$PATH +``` + +**STEP 3** + +Sync and fetch v8: + +```sh +cd Your/Path/To/depot_tools +gclient + +mkdir -p Your/Path/To/v8 +cd Your/Path/To/v8 +fetch v8 +cd v8 +git checkout refs/tags/12.4.254.20 +gclient sync +``` + +**STEP 4** + +Generate build configurations: + +```sh +gn gen ./out.gn/x64.release +``` + +Modify the options in `out.gn/x64.release/args.gn`. See [Options](#options-currently-used-for-building-v8) + + +**STEP 5** + +Build: + +```sh +ninja -C ./out.gn/x64.release v8_monolith +``` + +### Options Currently Used for Building V8 + +Windows x64 +```toml +is_component_build = false +is_debug = false +target_cpu = "x64" +target_os = "win" +v8_enable_i18n_support = false +v8_monolithic = true +v8_use_external_startup_data = false +v8_enable_pointer_compression = true +v8_jitless = false # jit enabled + +use_custom_libcxx = false # false will produce warnings +treat_warnings_as_errors = false # +v8_symbol_level = 0 # smaller lib +v8_enable_sandbox = false +``` + +Macos arm64 +```toml +# v8_enable_backtrace = true +# v8_enable_disassembler = true +# v8_enable_object_print = true +# v8_enable_verify_heap = true + +dcheck_always_on = false + +is_component_build = false +is_debug = false +target_cpu = "arm64" +v8_target_cpu = "arm64" +target_os = "mac" +v8_enable_i18n_support = false +v8_monolithic = true +v8_use_external_startup_data = false +v8_enable_pointer_compression = true +v8_jitless = false # jit enabled +v8_enable_webassembly = false + +use_custom_libcxx = false # false will produce warnings +treat_warnings_as_errors = false # +v8_symbol_level = 0 # smaller lib +v8_enable_sandbox = false +use_rtti = true +``` + +## Setup V8 in GodotJS +Follow the instructions below to set it up: + +**STEP 1:** Download or clone the repo into the `modules` directory of your Godot engine source: + +```sh +cd YourGodotEngineSource/modules +git clone https://github.com/ialex32x/GodotJS.git +``` + +**STEP 2:** Put `v8` headers and libraries into `GodotJS`, or directly download the prebuilt `v8` from [GodotJS-Dependencies](https://github.com/ialex32x/GodotJS-Dependencies/releases): + +```sh +# download the archive of prebuilt v8 +curl https://github.com/ialex32x/GodotJS-Dependencies/releases/download/v8_12.4.254.21_r13/v8_12.4.254.21_r13.zip --output your/download/path/v8.zip + +# extract the zip file into your `GodotJS` directory, +# NOTE: no white space after the switch `-o` +7z x -o"YourGodotEngineSource/modules/GodotJS" your/download/path/v8.zip +``` + +> **NOTE:** Don't forget to put the headers/libraries of `v8` into the same directory structure used in prebuilt `v8` if you decide to compile it by yourself. + +The module directroy structure looks like this: +``` +┗━ godot + ┗━ modules + ┣━ ... + ┣━ gltf + ┣━ GodotJS + ┃ ┣━ bridge-quickjs + ┃ ┣━ bridge-v8 + ┃ ┣━ ... + ┃ ┣━ lws + ┃ ┗━ v8 + ┃ ┣━ include + ┃ ┣━ linux.x86_64.release + ┃ ┣━ macos.arm64.release + ┃ ┣━ windows_x86_64_release + ┃ ┗━ ... + ┣━ gridmap + ┣━ ... +``` + +The currently used version of `v8` is `12.4.254.21`. + +**STEP 3:** Compile and launch `Godot Editor`. Then, you can create a Godot project in TypeScript/JavaScript: +* [Create-a-TypeScript-Project-from-Scratch](Create-a-TypeScript-Project-from-Scratch) +* [Create-a-JavaScript-Project-from-Scratch](Create-a-JavaScript-Project-from-Scratch) + +> **NOTE:** Since the prebuilt `v8` library is built with the `windows-latest` github runner which uses VS2022, encountering `Unresolved external symbol` errors during linkage with `v8_monolith.lib` or `libucrt.lib` may be addressed by updating to the latest version of the `MSVC v143` toolchain, `Windows Universal CRT SDK` and `Visual Studio 2022` itself. See [GodotJS-Dependencies README](https://github.com/ialex32x/GodotJS-Dependencies) for the version of MSVC C++ Compiler used in different prebuilt library packages. + +A prebuilt version of `Godot Editor` can be downloaded from [GodotJS-Build](https://github.com/ialex32x/GodotJS-Build/releases). +**Because the GodotJS-Build workflow is currently run manually, it may not be built from the latest commit of `GodotJS`.** \ No newline at end of file diff --git a/docs/documentation/building-from-source/web.md b/docs/documentation/building-from-source/web.md new file mode 100644 index 0000000..67be7de --- /dev/null +++ b/docs/documentation/building-from-source/web.md @@ -0,0 +1,8 @@ +# Web + +It's enabled by default for WebBuild. + +> **NOTE:** Be cautious about the JS compatibility issues between different web browsers. + +![web_build.png](images/web_build.png) + diff --git a/docs/documentation/experimental/worker.md b/docs/documentation/experimental/worker.md new file mode 100644 index 0000000..c7702e4 --- /dev/null +++ b/docs/documentation/experimental/worker.md @@ -0,0 +1,52 @@ +# Worker + +_Worker_ is currently supported as an experimental feature with limitations: + +- Can not use script classes (GodotJSScript) in workers +- `transferable objects` are not supported +- `onerror` and `onready` events are not implemented yet + +> **NOTE:** The class name _Worker_ may be changed in future versions for compatibility in different JS engines. +> And, _Worker_ scripts may need to have a specific file name suffix or reside in a designated directory. + +# A Simple Example + +```ts +// tests/master.ts +// ... +import { Object, Node } from "godot"; +import { JSWorker } from "godot.worker"; + +let worker = new JSWorker("tests/worker"); +worker.onmessage = function (m: any) { + console.log("master: get message", m); +}; +worker.ontransfer = function (obj: Object) { + console.assert(obj instanceof Node); + this.add_child(obj); + worker.terminate(); +}; +worker.postMessage("hello"); +``` + +```ts +// tests/worker.ts +import { PackedScene, ResourceLoader } from "godot"; +import { JSWorkerParent } from "godot.worker"; + +if (typeof JSWorkerParent !== "undefined") { + JSWorkerParent.onmessage = function (m: any) { + console.log("worker: get message", m); + JSWorkerParent.postMessage("worker result"); + + // [EXPERIMENTAL] instantiate a PackedScene in worker thread and transfer it back to master thread + let asset = ResourceLoader.load("res://background.tscn"); + let obj = asset.instantiate(); + console.log("[worker] transfering object:", obj); + JSWorkerParent.transfer(obj); + + // worker can terminate itself by calling `close()` + // close(); + }; +} +``` diff --git a/docs/documentation/getting-started.md b/docs/documentation/getting-started.md index 003bb51..e9b69cd 100644 --- a/docs/documentation/getting-started.md +++ b/docs/documentation/getting-started.md @@ -1,75 +1,65 @@ -[Download the editor](https://github.com/godotjs/javascript/releases) and start the application +1. [Download the editor](https://github.com/ialex32x/GodotJS-Build/releases) and start the application +2. Rename the downloaded file to `godot` and [add Godot to your path](https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html#path) +3. Open a terminal +4. Test if you can use Godot via terminal and run: -Recommended for TypeScript: +```shell +godot --version +``` -- Rename the downloaded file to `godot` and [add Godot to your path](https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html#path) -- Open a terminal -- Test if you can use Godot via terminal and run `godot --version` +## Create a new project -## How to export script class to Godot +### Automatically with [godot-ts](https://github.com/godotjs/godot-ts) -1. Define your JavaScript class and inherit from a Godot class, then export it as the **default** entry: +1. Run `npx -y @godot-js/godot-ts init` (new project will be crated at your current terminal path) +2. Follow the prompts +3. Run `cd ` +4. Run `npm i` +5. Run `npm run dev` - this will enable typescript watch mode and opens the editor +6. Inside the editor [install preset files](#install-preset-files) via `Project > Tools > GodotJS > Install Preset files` +7. Click `OK` to confirm a list of files will be generated in the project. +8. Attach the `example.ts` script to a node and run the project -```javascript title="my-sprite.mjs" -// The default export entry is treated as an exported class to Godot -export default class MySprite extends godot.Sprite { - // this is _init() in GDScript - constructor() { - super(); - } +### Manually - _ready() {} +1. Run `godot -p` and create a new project +2. Inside the editor [install preset files](#install-preset-files) via `Project > Tools > GodotJS > Install Preset files` +3. Click `OK` to confirm a list of files will be generated in the project. +4. Run `cd ` +5. Run `npm i` +6. Run `npx tsc` to compile the typescript files - _process(delta) {} -} -``` +### Install Preset Files -2. Save the script with extension `.mjs` -3. Attach the script file to the node or resource object like you do with GDScript +![Install Presets](images/tsproj_install_presets.png) -## How to export signals +![Prompt](images/tsproj_install_presets_prompt.png) -```javascript title="my-sprite.mjs" -export default class MySprite extends godot.Sprite {} -// register game_over signal to MySprite class -godot.register_signal(MySprite, "game_over"); -``` +## Create Scripts -## How to export properties - -```javascript title="my-sprite.mjs" -export default class MySprite extends godot.Sprite { - _process(delta) { - // Yes! We can use operators in JavaScript like GDScript - this.position += this.direction * delta; - } -} -// export 'direction' properties to MySprite Godot inspector -godot.register_property(MySprite, "direction", new godot.Vector2(1, 0)); -``` +To create new scripts, press select GodotJSScript as language: -There are 2 ways of using the `godot.register_property`. The third parameter can either be a default value for the property you're trying to export or an object giving a more detailed description of how the editor should show it. +![Select Language](images/tsproj_select_godotjs.png) -```js -function register_property(target: GodotClass | godot.Object, name: string, value: any); -function register_property(target: GodotClass | godot.Object, name: string, info: PropertyInfo); -``` +Use the ``Node: Node.Ts`` template: -So calling the `register_property` like this: +![Create a Script](images/tsproj_create_script.png) -```js -godot.register_property(MyClass, "number_value", 3.14); -``` +Open the project folder in you IDE, you should see full TypeScript support! + +![Type Hint](images/tsproj_type_hint.png) + +## Compile TypeScript Sources without [godot-ts](https://github.com/godotjs/godot-ts) -Is the simplified version of: +Before your scripts runnable in _Godot_, run `tsc` to compile typescript sources into javascript. -```js -godot.register_property(MyClass, "number_value", { - type: godot.TYPE_REAL, - hint: godot.PropertyHint.PROPERTY_HINT_NONE, - hint_string: "", - default: 3.14, -}); +```sh +npx tsc + +# or watch if you want +npx tsc -w ``` -For more detail on how to use it, [click here](https://github.com/godotjs/javascript/issues/24#issuecomment-655584829). +Also, you can simply click the tool button on _GodotJS_ bottom panel in the godot editor. It'll do the same thing for you. + +![TSC Watch](images/tsproj_tsc_watch.png) diff --git a/docs/documentation/godot-js-scripts/bindings.md b/docs/documentation/godot-js-scripts/bindings.md new file mode 100644 index 0000000..dca0d52 --- /dev/null +++ b/docs/documentation/godot-js-scripts/bindings.md @@ -0,0 +1,139 @@ +# Bindings + +All godot types (Object & Primitive) are defined in the built-in module `godot` in javascript. Most of them are lazily loaded until actually used in scripts. + +## Godot Object Types + +### Enum + +Godot enums are exposed in javascript as `enum`, and the underlying constant values which already defined in enums are eliminated from bindings. + +> **NOTE:** The name of enum value can be retrieved by reading from enum type with the value as the key. + +For example: + +```ts +import { Image } from "godot"; + +// read enum value from the nested enum in class +let mode: Image.AlphaMode = Image.AlphaMode.ALPHA_NONE; + +// can not read enum constant value from class +// Image.ALPHA_NONE is not defined in GodotJS. + +// this statement prints '0' +console.log(mode); + +// this statement prints 'ALPHA_NONE' +console.log(Image.AlphaMode[mode]); +``` + +## Godot Primitive Types (Variant) + +Godot `Variant` values are automatically translated to the primitive types in javascript if available. + +| Variant Type in Godot | Counterpart in JS | Caveats | +| --------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| NIL | `any` | `NIL` used as a type stands for any variant type parameter in Godot | +| INT | `number` | `INT` is represented as `int64` in Godot, therefore only values less than the max value of int32 can be translated without loss. | +| FLOAT | `number` | | +| STRING | `string` | | +| STRING_NAME | `string` | | + +All variant types not mentioned above are exposed as `class` in javascript (such as `Vector2` `Vector3` `PackedStringArray` etc.). + +> **NOTE:** All primitive types which represented as `Variant` are pooled in `Environment` if `JSB_WITH_VARIANT_POOL` is enabled to reduce unnecessary cost on repeatedly memory allocation. + +### Pitfalls + +The javascript class variant values behave like value type when passing between `JS` and `C++`, they are **NOT** by-reference. But in pure javascript, they are still by-reference. + +```ts +let value = new Vector3(); + +// Input get a copy of the value since it's implemented in C++ +Input.set_gyroscope(value); + +function modify(v: Vector3) { + v.x = 1; +} + +// after this statement, `value.x` will be 1, since value itself is passed by-reference +modify(value); + +// node.position is NOT modified, since `node.position` is actually a getter implemented in C++ which returns a copy of it. +modify(node.position); +``` + +And, avoid coding property assignments like this: `node.position.x = 0;`, although, it works in GDScript. + +**It's not an error in javascript (which is more DANGEROUS)**, the actually modifed value is just a copy of `node.position`. + +### StringName + +`StringName` optimization is completely transparent in javascript. When passing `StringName` parameters, a mapping between `StringName` and `v8::String` will be automatically cached to avoid repeatedly allocation of new `v8::String` objects and Godot `Variant` objects. + +```ts +import { Input, Node2D } from "godot"; + +class MyActor extends Node2D { + _process() { + // so it's relatively lightweight to directly use a string here + if (Input.is_action_just_pressed("confirm")) { + // ... + } + } +} +``` + +### Packed Array (`Vector`) + +If `JSB_IMPLICIT_PACKED_ARRAY_CONVERSION` is defined as `true`, `Packed Array` will be implicitly converted from javascript `Array`. + +```ts +let a1 = new PackedStringArray(); +let a2 = new PackedStringArray(); +a2.append("test2"); + +a1.append_array(["hey", "there"]); // implicit, it will fail if `JSB_IMPLICIT_PACKED_ARRAY_CONVERSION` is `0` +a1.append_array(a2); // explicit + +// To get value from PackedArray use `get_indexed` +a1.get_indexed(2); + +// To set value in PackedArray use `set_indexed` +a1.set_indexed(2, "new value"); + +let b1 = new PackedByteArray(); +b1.append_array([20, 1, 6, 5]); + +// ArrayBuffer is implicitly treated as PackedByteBuffer even if `JSB_IMPLICIT_PACKED_ARRAY_CONVERSION` is `0` +// See `PackedByteArray and ArrayBuffer` +b1.append_array(new ArrayBuffer(16)); +``` + +#### PackedByteArray and ArrayBuffer + +A javascript `ArrayBuffer` can be used as a `PackedByteArray` implicitly. + +```ts +let file = FileAccess.open(filePath, FileAccess.ModeFlags.WRITE); +let buffer = new ArrayBuffer(16); +let view = new Uint8Array(buffer); + +view.fill(0); +file.store_buffer(buffer); +``` + +But reversely, `PackedByteArray.to_array_buffer` is required to get an `ArrayBuffer` from `PackedByteArray`. + +```ts +let packed = FileAccess.get_file_as_bytes("res://something.txt"); + +// 'packed' is a `PackedByteArray` +console.log(packed.size()); + +// 'buffer' is a `ArrayBuffer` +let buffer = packed.to_array_buffer(); +console.log(buffer.byteLength); +``` diff --git a/docs/documentation/godot-js-scripts/code-in-editor.md b/docs/documentation/godot-js-scripts/code-in-editor.md new file mode 100644 index 0000000..4c0a6cf --- /dev/null +++ b/docs/documentation/godot-js-scripts/code-in-editor.md @@ -0,0 +1,141 @@ + +# Running code in the editor + +> **NOTE:** Read [Godot Docs](https://docs.godotengine.org/en/stable/tutorials/plugins/running_code_in_the_editor.html#what-is-tool) for more details about `@tool`. + + +If a GodotJS class is annotated with `tool()`, it'll be instantiated in the editor. Call `Engine.is_editor_hint()` in the script to check if it's running in the editor. +It's also possible to show warnings on a `Node` on `Scene` panel with `_get_configuration_warnings` defined. Here is a simple example: + +```ts +import { Engine, PackedStringArray, Sprite2D, Variant } from "godot"; +import { export_, tool } from "godot.annotations"; + +@tool() +export default class MyEditorSprite extends Sprite2D { + + /** + * get/set property for `export` (both must be defined) + */ + @export_(Variant.Type.TYPE_FLOAT) + get speed(): number { return this._speed; } + set speed(value: number) { + if (this._speed != value) { + this._speed = value; + this.update_configuration_warnings(); + } + } + + /** + * plain field for `export` + */ + @export_(Variant.Type.TYPE_INT) + unused_int = 0; + + private _clockwise = false; + private _speed = 0; + + _ready() { + this._clockwise = Engine.is_editor_hint(); + } + + _process(delta: number) { + const step = Math.PI * delta * (this._clockwise ? this._speed : -this._speed); + this.rotation = this.rotation + step; + + // this version is available only if `JSB_EXCLUDE_GETSET_METHODS` is disabled on your side + // this.set_rotation(this.get_rotation() + step); + } + + _get_configuration_warnings() { + let warnings = new PackedStringArray(); + if (this._speed >= -0.01 && this._speed < 0.01) { + warnings.append("speed is too low"); + } + return warnings; + + // it's OK to directly use javascript Array as the return value (except the complains from ts compiler): + // return this._speed >= -0.01 && this._speed < 0.01 ? ["speed is too low"] : []; + + // So, you need to write it like this one which would be slightly ugly: + // return (this._speed >= -0.01 && this._speed < 0.01 ? ["speed is too low"] : []); + } +} +``` + +By attaching this script on a `Sprite2D` node and setting `_speed` as `0`, a warning message will be listed on `Scene` panel if `_speed` is too small. + +![show warnings on node](images/update_configuration_warnings.png) + + +# Running one-off scripts using EditorScript +Sometimes, you need to run code just one time to automate a certain task that is not available in the editor out of the box. Some examples might be: + +- Use as a playground for `GodotJS` scripting without having to run a project. print() output is displayed in the editor Output panel. + +- Scale all light nodes in the currently edited scene, as you noticed your level ends up looking too dark or too bright after placing lights where desired. + +- Replace nodes that were copy-pasted with scene instances to make them easier to modify later. + +This is available in Godot by extending `EditorScript` in a script. This provides a way to run individual scripts in the editor without having to create an editor plugin. + +```ts +import { EditorScript } from "godot"; +import { tool } from "godot.annotations"; + +@tool() +export default class MyEditorScript1 extends EditorScript { + _run() { + console.log("my editor script run"); + } +} +``` + +This `_run()` method is executed when you use `File > Run` or the keyboard shortcut `Ctrl + Shift + X` while the `EditorScript` is the currently open script in the script editor. This keyboard shortcut is only effective when currently focused on the script editor. + +![run_editor_script.png](images/run_editor_script.png) + +# Running batch scripts using editor parameters +Scripts can also be executed from command line arguments, making them very suitable for batch processing tasks, such as generating config files, converting formats, and so on. + +```sh +./bin/your_godot_binary_file --path "path\to\your_project" --script res://tests/read_xlsx.ts +``` + +It's an example script which leverages [xlsx.js](https://www.npmjs.com/package/xlsx) for directly reading data from Excel xlsx files, re-saving it as csv or anything you want. + +```ts +import * as jsb from "godot-jsb"; +import { FileAccess } from "godot"; + +console.log("please run 'npm install' in the directory './' at first if 'xlsx' module can not be resolved"); + +//NOTE xlsx requires 'stream' module if 'require' exists +// but, actually, this module is not utilized by xlsx in practice, +// pretending it exists helps avoid errors as a workaround. +jsb.internal.add_module("stream", {}); +import * as xlsx from "xlsx"; + +let filename = "res://test.xlsx"; +let wb = xlsx.read(FileAccess.get_file_as_bytes(filename).to_array_buffer(), { type: "buffer" }); + +console.log("read excel:", filename); +for (let sheetIndex in wb.SheetNames) { + let sheetName = wb.SheetNames[sheetIndex] + + console.log(`read sheet: ${sheetName}`); + let sheet = wb.Sheets[sheetName]; + let csv = xlsx.utils.sheet_to_csv(sheet); + console.log("to_csv:", csv); + let range = xlsx.utils.decode_range(sheet["!ref"]!); + for (let row = range.s.r; row <= range.e.r; row++) { + for (let col = range.s.c; col <= range.e.c; col++) { + let cell = sheet[xlsx.utils.encode_cell({ c: col, r: row })]; + if (cell) { + console.log(cell.v); + } + } + } +} + +``` diff --git a/docs/documentation/godot-js-scripts/cyclic-imports.md b/docs/documentation/godot-js-scripts/cyclic-imports.md new file mode 100644 index 0000000..890bc3d --- /dev/null +++ b/docs/documentation/godot-js-scripts/cyclic-imports.md @@ -0,0 +1,40 @@ +# Cyclic imports + +Cyclic imports are allowed in `GodotJS` with some limits. + +```ts +// file: cyclic_import_1.ts + +import { CyclicClass2 } from "./cyclic_import_2"; + +// NOT OK: The behaviour is undefined if anything from cyclic imported modules is referenced in the script compile-run scope +// let a_name = CyclicClass2.something; + +// NOT OK: extends a class from cyclic imported modules +// class BehaviorUndefined extends CyclicClass2 {} + +// OK: references at runtime +export class CyclicClass1 { + static call1() { + console.log("call1"); + CyclicClass2.call2(); + } + + static call3() { + console.log("call3"); + } +} +``` + +```ts +// file: cyclic_import_2.ts + +import { CyclicClass1 } from "./cyclic_import_1"; + +export class CyclicClass2 { + static call2() { + console.log("call2"); + CyclicClass1.call3(); + } +} +``` \ No newline at end of file diff --git a/docs/documentation/godot-js-scripts/decorators.md b/docs/documentation/godot-js-scripts/decorators.md new file mode 100644 index 0000000..6a9a960 --- /dev/null +++ b/docs/documentation/godot-js-scripts/decorators.md @@ -0,0 +1,114 @@ +# Decorators + +There are several decorators to help you define properties, signals, and other metadata for Godot objects. + +## Signal annotation + +You can define signals in your script using the `@signal` annotation: + +```ts +import { Node, Signal0, Callable } from "godot"; +import { signal } from "godot.annotations"; + +export default class MyJSNode extends Node { + @signal() + declare test!: Signal0; +} +``` + +For more information about signals, check this [link](signals.md). + +## Tool annotation + +If a GodotJS class is annotated with `tool()`, it'll be instantiated in the editor. + +```ts +import { Node } from "godot"; +import { tool } from "godot.annotations"; + +@tool() +export default class MyTool extends Node { + _ready() { + // This code will run in the editor + console.log("MyTool is running in the editor"); + } +} +``` + +For more information about running code in editor, check this [link](code-in-editor.md). + +## Icon annotation + +An icon can be used as node icon in the editor scene hierarchy with the annotation `@icon`. + +```ts +import { Sprite2D } from "godot"; +import { icon } from "godot.annotations"; + +@icon("res://icon/affiliate.svg") +export default class MySprite extends Sprite2D {} +``` + +![icon annotation](images/script_icon_annotation.png) + +## Export Annotation + +In `GodotJS`, class member properties/variables can be exported. +This means their value gets saved along with the resource +(such as the scene) they're attached to. +They will also be available for editing in the property editor. +Exporting is done by using the `@export_` annotation. + +```ts +import { export_ } from "godot.annotations"; + +export default class Shooter extends Sprite2D { + // type must be explicitly provided as the first parameter of @export_ + // cuz static type is actually a phantom in typescript + @export_(Variant.Type.TYPE_FLOAT) + speed: number = 0; + + // ... +} +``` + +In this example the value `0` will be saved and visible in the property editor. + +The retrieval of default value is implemented through `Class Default Object (CDO)`. +`GodotJS` will instantiate a pure javascript instance of the script class +(`Shooter` in this example) as `CDO`, then the property value is read from +`CDO` as `default value` in the property editor. + +> **NOTE:** Be cautious when coding within `constructor`, as it is probably called for initializing `CDO`. + +### Basic Use + +```ts +@export_(Variant.Type.TYPE_STRING) +address: string = "somewhere"; // `:string` can be omitted here + +@export_(Variant.Type.TYPE_INT) +age: number = 0; // `:number` can be omitted here +``` + +If there's no default value, `default value` of the give type will be used (`0` in this case). + +```ts +@export_(Variant.Type.TYPE_INT) +age: number; +``` + +### Exported Enum Properties + +Enum value properties can be exported with the built-in support in the property editor. + +> **NOTE:** So far, only `int` is supported as enum value. + +```ts +@export_enum(MyColor) +color: MyColor = MyColor.White; +``` + +The value can be easily chosen from a dropdown list in the editor. + +![enum_prop](images/export_enum_inspector.png) diff --git a/docs/documentation/godot-js-scripts/images/export_enum_inspector.png b/docs/documentation/godot-js-scripts/images/export_enum_inspector.png new file mode 100644 index 0000000..05c82a1 Binary files /dev/null and b/docs/documentation/godot-js-scripts/images/export_enum_inspector.png differ diff --git a/docs/documentation/godot-js-scripts/images/run_editor_script.png b/docs/documentation/godot-js-scripts/images/run_editor_script.png new file mode 100644 index 0000000..c6d104e Binary files /dev/null and b/docs/documentation/godot-js-scripts/images/run_editor_script.png differ diff --git a/docs/documentation/godot-js-scripts/images/script_icon_annotation.png b/docs/documentation/godot-js-scripts/images/script_icon_annotation.png new file mode 100644 index 0000000..940c939 Binary files /dev/null and b/docs/documentation/godot-js-scripts/images/script_icon_annotation.png differ diff --git a/docs/documentation/godot-js-scripts/images/update_configuration_warnings.png b/docs/documentation/godot-js-scripts/images/update_configuration_warnings.png new file mode 100644 index 0000000..c540be8 Binary files /dev/null and b/docs/documentation/godot-js-scripts/images/update_configuration_warnings.png differ diff --git a/docs/documentation/godot-js-scripts/intro.md b/docs/documentation/godot-js-scripts/intro.md new file mode 100644 index 0000000..525fd40 --- /dev/null +++ b/docs/documentation/godot-js-scripts/intro.md @@ -0,0 +1,68 @@ +# Scripting + +A GodotJS class can extend a Godot Object class: + +```ts +export default class MyNode extends Node { + _ready() { + console.log("MyNode _ready"); + } +} +``` + +> **Note:** A class must be exported as `default`, +> otherwise the script will not be recognized as a valid script class. + +## Constructor + +> **WARNING:** Explicitly defined `constructor` in script classes inherited from Godot Object are not recommended, because GodotJS constructs the script classes for special uses (such as CDO and cross-binding). +> If it can't be avoided, always define it with an explicit argument `identifier? any`, and don't forget to call `super(identifier)`. + +For instance: + +```ts +export default class MyExampleNode extends Node { + constructor(identifier?: any) { + super(identifier); + + // do other things you want + //... + } +} +``` + +You can instantiate a script class directly with `new` in scripts: + +```ts +// do not pass any arguments to the constructor +let node = new MyExampleNode(); +``` + +## Async/Await + +You can use `async/await` in your scripts to handle asynchronous operations. + +```ts +import { Node } from "godot"; + +function seconds(secs: number) { + return new Promise(function (resolve) { + setTimeout(function () { + resolve(undefined); + }, secs * 1000); + }); +} + +export default class MyJSNode extends Node { + // it's also possible to use async functions in scripts (even the `_ready` call) + async call_me() { + await seconds(1); + } +} +``` + +## Annotations + +Annotations are used to define properties, signals, and other metadata for Godot objects. +They are similar to decorators in TypeScript and can be used to enhance the functionality of your scripts. +Check out [decorators](decorators.md) for more information. diff --git a/docs/documentation/godot-js-scripts/npm-dependencies.md b/docs/documentation/godot-js-scripts/npm-dependencies.md new file mode 100644 index 0000000..2cab846 --- /dev/null +++ b/docs/documentation/godot-js-scripts/npm-dependencies.md @@ -0,0 +1,11 @@ +# NPM Dependencies + +Currently, `GodotJS` doesn't provide sufficient support for using code from npm packages. Because many factors are involved in it, such as: + +- Scripts depend on functionalities of node.js which is not supported +- How the typescript/javascript project is packaged (archive into a single script file or not) +- Javascript modular standard variants may be involved + +If a npm package just works, there is no guarantee that it does also work after packaging, `GodotJS` tries to export all dependant javascript sources into the targeting package. + +> **NOTE:** See [read_xlsx.ts](https://github.com/ialex32x/GodotJSExample/blob/main/tests/read_xlsx.ts) about using a npm package. diff --git a/docs/documentation/godot-js-scripts/signals.md b/docs/documentation/godot-js-scripts/signals.md new file mode 100644 index 0000000..803eedc --- /dev/null +++ b/docs/documentation/godot-js-scripts/signals.md @@ -0,0 +1,113 @@ +# Signals + +You can define signals based on the amount of arguments you want to pass: + +```ts +import { Node, Signal0, Signal1, Signal2, Callable } from "godot"; +import { signal } from "godot.annotations"; + +export default class MyJSNode extends Node { + @signal() + declare no_arg!: Signal0; + + @signal() + declare one_arg!: Signal1; + + @signal() + declare two_args!: Signal2; +} +``` + +> **NOTE:** You must `declare` signal properties. +> This creates an "ambient" declaration, which is a way of telling TypeScript +> that the property exists but not to generate any code to define it. +> Instead `@signal` automatically generates an accessor on your behalf. +> If you omit `declare`, the TypeScript compiler creates an "own property" +> on instances with an `undefined` value, which blocks access to the GodotJS' +> generated accessor method (which exists on the instance's prototype). + +## Passing Arguments via Signals + +When emitting signals, only Godot-native objects (GArray, GDictionary, and primitives) can be passed as valid arguments. Raw TypeScript/JavaScript objects cannot be used directly. + +Incorrect Example: + +```ts +this.some_signal.emit({ key: "value" }); // ❌ Raw JS object +``` + +Correct Example: + +```ts +import { GDictionary } from "godot"; + +const data = new GDictionary(); +data.set("key", "value"); +this.some_signal.emit(data); // ✅ Godot dictionary +``` + +If raw JavaScript objects must be passed, consider converting them into ``GDictionary`` or ``GArray`` before emitting them as arguments. +If you use [godot-ts](https://github.com/godotjs/godot-ts) you can use +the functions ``toGDictionary`` and `fromGDictionary` from ``generated/utils.ts``. + +## Connect and disconnect to a signal programmatically + +```ts +import { Node, Callable } from "godot"; + +export default class MyClass extends Node { + _ready() { + // subscribe + this.get_node("Button").pressed.connect( + Callable.create(this, this.handle_onclick), + 0, + ); + } + + handle_onclick() { + // handle the click event + } + + _exit_tree() { + // unsubscribe + this.get_node("Button").pressed.disconnect( + Callable.create(this, this.handle_onclick), + ); + } +} +``` + +> **NOTE:** Please be cautious that `Callable.create(this, this.xxx) === Callable.create(this, this.xxx)` returns `false`. +> But it's compared internally in C++ to check equality when using them for `connect` and `disconnect`. + +> **NOTE:** `Callable` does not hold a strong reference on `this` which given as the first parameter. It becomes invalid after the corresponding javascript object is garbage collected. +> **HOWEVER**, it may cause object leaks if `this` is captured in a lambda function. So avoid coding in this way `Callable.create(this, () => this.xxx())`. + +## Await a Signal + +A `Signal` can be awaitable in javascript by calling `as_promise()`: + +```ts +import { Node, Signal1 } from "godot"; +import { signal } from "godot.annotations"; + +class ExampleClass extends Node { + @signal() + declare test_signal!: Signal1; + + _ready() { + test(); + } + + async test() { + console.log("before signal emit"); + // result is 123 + const result = await this.test_signal.as_promise(); + console.log("after signal emit", result); + } + + emit_somehow() { + this.test_signal.emit(123); + } +} +``` diff --git a/docs/documentation/gotchas.md b/docs/documentation/gotchas.md deleted file mode 100644 index 9b75b9e..0000000 --- a/docs/documentation/gotchas.md +++ /dev/null @@ -1,34 +0,0 @@ -# Gotchas and limitations - -Some common mistakes and limitations. - -## Position.x is immutable - -You cannot change `this.position.x` try to change `this.position`: - -```javascript title="player.mjs" -export default class Player extends godot.KinematicBody2D { - constructor() { - super(); - this.direction = new godot.Vector2(1, 0); - } - _ready() {} - _process(delta) { - this.position.x += this.direction.x; // <- breaks - this.position += this.direction; // <- works - } -} -godot.register_property(Player, "direction", new godot.Vector2(1, 0)); -``` - -## `register_property` has to be a target - -You cannot change `this.position.x` try to change `this.position`: - -```javascript title="player.mjs" -export default class Player extends godot.KinematicBody2D {} -// This works -godot.register_property(Player, "directionWorks", new godot.Vector2(1, 0)); -// This breaks because `player` isn't a correct target -godot.register_property(player, "directionBreaks", new godot.Vector2(1, 0)); -``` diff --git a/docs/documentation/images/tsproj_create_script.png b/docs/documentation/images/tsproj_create_script.png new file mode 100644 index 0000000..7e0997b Binary files /dev/null and b/docs/documentation/images/tsproj_create_script.png differ diff --git a/docs/documentation/images/tsproj_install_presets.png b/docs/documentation/images/tsproj_install_presets.png new file mode 100644 index 0000000..4f4f191 Binary files /dev/null and b/docs/documentation/images/tsproj_install_presets.png differ diff --git a/docs/documentation/images/tsproj_install_presets_prompt.png b/docs/documentation/images/tsproj_install_presets_prompt.png new file mode 100644 index 0000000..5763842 Binary files /dev/null and b/docs/documentation/images/tsproj_install_presets_prompt.png differ diff --git a/docs/documentation/images/tsproj_select_godotjs.png b/docs/documentation/images/tsproj_select_godotjs.png new file mode 100644 index 0000000..e379a85 Binary files /dev/null and b/docs/documentation/images/tsproj_select_godotjs.png differ diff --git a/docs/documentation/images/tsproj_tsc_watch.png b/docs/documentation/images/tsproj_tsc_watch.png new file mode 100644 index 0000000..30a1b4e Binary files /dev/null and b/docs/documentation/images/tsproj_tsc_watch.png differ diff --git a/docs/documentation/images/tsproj_type_hint.png b/docs/documentation/images/tsproj_type_hint.png new file mode 100644 index 0000000..b6e4175 Binary files /dev/null and b/docs/documentation/images/tsproj_type_hint.png differ diff --git a/docs/documentation/images/use_external_editor.png b/docs/documentation/images/use_external_editor.png new file mode 100644 index 0000000..a804a34 Binary files /dev/null and b/docs/documentation/images/use_external_editor.png differ diff --git a/docs/documentation/typescript/decorators.md b/docs/documentation/typescript/decorators.md deleted file mode 100644 index a1510d3..0000000 --- a/docs/documentation/typescript/decorators.md +++ /dev/null @@ -1,74 +0,0 @@ -One feature of GodotTS is that your can use decorators for adding `register` functions to your class like [signals](../getting-started.md#how-to-export-signals). - -Most of the `register` functions are available as various decorators as seen below. Check the decorators in `src/decorators.bundle` generated in [intro](intro.md). - -```ts -import { signal, property, tool, onready, node } from "./decorators.bundle"; - -@tool // make the script runnable in godot editor -export default class InputLine extends godot.HBoxContainer { - // define a signal - @signal - static readonly OnTextChanged: string; - - // expose a node property - @node - icon: godot.Sprite; - - // register offset property with the godot inspector with default value of Vector2(0, 0) - @property({ default: godot.Vector2.ZERO }) - offset: godot.Vector2; - - // register properties for godot editor inspector - @property({ type: godot.VariantType.TYPE_STRING }) - get title() { - return this._title; - } - set title(v: string) { - this._title = v; - if (this._label) { - this._label.text = v; - } - } - private _title: string; - - @property({ default: "Input text here" }) - get hint() { - return this._hint; - } - set hint(v: string) { - this._hint = v; - if (this.edit) { - this.edit.hint_tooltip = v; - this.edit.placeholder_text = v; - } - } - private _hint: string; - - get label(): godot.Label { - return this._label; - } - protected _label: godot.Label; - - // call get_node('LineEdit') and assign the returned value to 'this.edit' automatically when the node is ready - @onready("LineEdit") - edit: godot.LineEdit; - - get text(): string { - return this.edit?.text; - } - - _ready() { - // get first child with the type of godot.Label - this._label = this.get_node(godot.Label); - - // Apply the inspector filled values with property setters - this.title = this.title; - this.hint = this.hint; - - this.edit.connect(godot.LineEdit.text_changed, (text: string) => { - this.emit_signal(InputLine.OnTextChanged, text); - }); - } -} -``` diff --git a/docs/documentation/typescript/godot-ts.md b/docs/documentation/typescript/godot-ts.md deleted file mode 100644 index f046d5a..0000000 --- a/docs/documentation/typescript/godot-ts.md +++ /dev/null @@ -1,16 +0,0 @@ -[`godot-ts`](https://github.com/godotjs/godot-ts) is a cli tool for using GodotJS with Typescript. - -See the [Intro](intro.md) how you initialize a new GodotTS project. - -Afterward, you can run ``godot-ts --help`` to see additional helping. - -## Further information - -The cli tool handles the compilation of `.ts` files into `.mjs` files. - -There are two commands `godot-ts build` & `godot-ts watch` which compile those files. Use `build` to compile your files once for production (with minification) and `watch` for developing. - -Both commands will compile every `*.ts` file as it is (without bundling), to remain the required class structures for GodotJS. - -Sometimes you require to bundle a `*.ts` file e.g. if you need something from `node_modules` ([example](npm-modules.md)). -In this case name your file `*.bundle.ts`, this won't generate a `.mjs` file, instead it generates a `.js` file as bundled code. diff --git a/docs/documentation/typescript/images/enable-external-editor.png b/docs/documentation/typescript/images/enable-external-editor.png deleted file mode 100644 index 4ec3b6f..0000000 Binary files a/docs/documentation/typescript/images/enable-external-editor.png and /dev/null differ diff --git a/docs/documentation/typescript/images/open-script.png b/docs/documentation/typescript/images/open-script.png deleted file mode 100644 index 56d03ef..0000000 Binary files a/docs/documentation/typescript/images/open-script.png and /dev/null differ diff --git a/docs/documentation/typescript/intro.md b/docs/documentation/typescript/intro.md deleted file mode 100644 index 32db476..0000000 --- a/docs/documentation/typescript/intro.md +++ /dev/null @@ -1,37 +0,0 @@ -1. [Download the editor](https://github.com/godotjs/javascript/releases) -2. Rename the downloaded file to `godot` and [add Godot to your path](https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html#path) -3. Open a terminal -4. Test if you can use Godot via CLI and run `godot --version` -5. Run `npx -y @godot-js/godot-ts init` (new project will be crated at your current terminal path) -6. Follow the prompts -7. Run `cd ` -8. Run `npm i` -9. Run `npm run dev` - this will enable typescript watch mode and opens the editor -10. Run the menu command inside the editor `Project > Tools > JavaScript > Generate TypeScript Declaration File` and overwrite file `godot.d.ts` - -## Features - -- By running `npm run dev` you compile your `.ts` files into `.mjs` automatically -- Use the generated `.mjs` files inside your editor -- To open the `.ts` origin file for a `.mjs` file you need to enable [External Editor](#open-scripts-from-the-editor) -- If you want to start the game without editor run `npm run start` -- For more information check out [`godot-ts`](godot-ts.md) - -## Open scripts from the editor - -Inside the editor you are normally able to open a script inside the build in text editor. - -![Open script](images/open-script.png) - -But we use a compiled `.mjs` file for our scripts. -`.ts` files can't be open in the editor. - -So we need to open the `.ts` files inside an external editor: - -1. Goto `Editor/Editor Settings/Text Editor/External` -2. Check the `Use External Editor` -3. Add your path your desired editor to `Exec Path` - Check [this](https://docs.godotengine.org/en/stable/tutorials/editor/external_editor.html) for more information -4. Paste `{project} --line {line} {file}` (JetBrains products) to `Exec Flags` -5. If you click on a `.mjs` script it should read the `banner` on top of the file like `//generatedPath=.ts` and opens this file - -![enable external editor](images/enable-external-editor.png) diff --git a/docs/documentation/typescript/npm-modules.md b/docs/documentation/typescript/npm-modules.md deleted file mode 100644 index 1a85575..0000000 --- a/docs/documentation/typescript/npm-modules.md +++ /dev/null @@ -1,31 +0,0 @@ -You can import ES5/ES6 modules installed from e.g. `npm`. But you need to bundle them first. - -We recommend that you create a `npm.bundle.ts` file where you locate all of your dependencies. - -## Example - -Install dependency: - -```shell title="terminal" -npm i dayjs -``` - -Export the dependency from your `npm.bundle.ts` file. - -```ts title="npm.bundle.ts" -export { default as dayjs } from "dayjs"; -``` - -Import the dependency in another class - -```ts title="my-class.ts" -import { dayjs } from "./npm-modules.bundle"; - -export default class MyClass extends Node { - _ready(): void { - console.log(dayjs().toString()); - } -} -``` - -> **Note:** If you use `godot-ts build/watch` the `npm.bundle.ts` file will be bundled with every dependency from `node_modules`, while regular `*.ts` files will preserve their imports. This is required for GodotJS to work. diff --git a/docs/documentation/use-external-editor.md b/docs/documentation/use-external-editor.md new file mode 100644 index 0000000..620285b --- /dev/null +++ b/docs/documentation/use-external-editor.md @@ -0,0 +1,12 @@ +# Use External Editor + +It's recommended to use external editor to write TypeScript. +Change the settings in `Editor > Edtior Settings > Text Editor > External`. + +> **NOTE:** You might need to use the switch to enable `Advanced Settings`. + +1. Check the `Use External Editor` +2. Add your path your desired editor to `Exec Path` - Check [this](https://docs.godotengine.org/en/stable/tutorials/editor/external_editor.html) for more information +3. For example write `{project} --line {line} {file}` for JetBrains products or `{project} --goto {file}:{line}:{col}` for VSCode to `Exec Flags` + +![use_external_editor](images/use_external_editor.png) diff --git a/docs/documentation/utilities/debugger.md b/docs/documentation/utilities/debugger.md new file mode 100644 index 0000000..1922883 --- /dev/null +++ b/docs/documentation/utilities/debugger.md @@ -0,0 +1,64 @@ +# Debugger + +A debugger bridge is implemented is _GodotJS_ which supports v8 inspector protocol. `Chrome devtools` and `VSCode` are both supported to debug your JS/TS code. + +> **NOTE:** The listening port can be changed in `Project Settings`, and a restart is required for it to take effect. +> `Project Settings > GodotJS > Debugger > Editor Port` for `Editor` environment +> `Project Settings > GodotJS > Debugger > Runtime Port` for `Runtime` environment. +> Check `Advanced Settings` if they are not listed in `Project Settings`. + +## VSCode + +Add the following configuration into `launch.json`: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "attach", + "name": "Attach (Inspector)", + "protocol": "inspector", + "address": "127.0.0.1", + "port": 9229 + } + ] +} +``` + +Then, press `Start debugging (F5)` on the `Run and Debug` panel after your app launched. + +![vscode_debug](images/vscode_debugger.png) + +## Chrome devtools + +Open `devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/1` in `Chrome` to start debugging the sources. + +![browser debugging](images/browser-debugger.png) + +## JetBrains IDEs + +1. Goto `Main Menu > Run > Edit Configurations` +2. Press ➕ (Add new configuration) +3. Select `Attach to Node.js/Chrome` +4. Add a name to the configuration and select port `9229` +5. Start the configuration + +![jetbrains ide debugger](images/jetbrains.png) + +## Safari + +Enable `Show features for web developers` in settings: +![safari_settings_enable_dev](images/safari_settings_enable_dev.png) + +Select the session from menu: +![safari_inspector_select](images/safari_inspector_select.png) + +A web inspector window will be open if successfully: +![safari_web_inspector](images/safari_web_inspector.png) + +## Troubleshooting + +Q: The debugger is connected successfully, but why no breakpoints can hit (or the devtools reports that a source can not be located)? +A: The debugger is resolving the source from a wrong place. Please update the value of `sourceRoot` in your `tsconfig.json` to an appropriate path (can be a relative path). diff --git a/docs/documentation/utilities/images/browser-debugger.png b/docs/documentation/utilities/images/browser-debugger.png new file mode 100644 index 0000000..9421291 Binary files /dev/null and b/docs/documentation/utilities/images/browser-debugger.png differ diff --git a/docs/documentation/utilities/images/jetbrains.png b/docs/documentation/utilities/images/jetbrains.png new file mode 100644 index 0000000..71c10c7 Binary files /dev/null and b/docs/documentation/utilities/images/jetbrains.png differ diff --git a/docs/documentation/utilities/images/monitor.png b/docs/documentation/utilities/images/monitor.png new file mode 100644 index 0000000..88f58fd Binary files /dev/null and b/docs/documentation/utilities/images/monitor.png differ diff --git a/docs/documentation/utilities/images/repl.gif b/docs/documentation/utilities/images/repl.gif new file mode 100644 index 0000000..1f6ad99 Binary files /dev/null and b/docs/documentation/utilities/images/repl.gif differ diff --git a/docs/documentation/utilities/images/safari_inspector_select.png b/docs/documentation/utilities/images/safari_inspector_select.png new file mode 100644 index 0000000..fd80b1b Binary files /dev/null and b/docs/documentation/utilities/images/safari_inspector_select.png differ diff --git a/docs/documentation/utilities/images/safari_settings_enable_dev.png b/docs/documentation/utilities/images/safari_settings_enable_dev.png new file mode 100644 index 0000000..ef60f5f Binary files /dev/null and b/docs/documentation/utilities/images/safari_settings_enable_dev.png differ diff --git a/docs/documentation/utilities/images/safari_web_inspector.png b/docs/documentation/utilities/images/safari_web_inspector.png new file mode 100644 index 0000000..05647cb Binary files /dev/null and b/docs/documentation/utilities/images/safari_web_inspector.png differ diff --git a/docs/documentation/utilities/images/sourcemap_example1.png b/docs/documentation/utilities/images/sourcemap_example1.png new file mode 100644 index 0000000..1c08806 Binary files /dev/null and b/docs/documentation/utilities/images/sourcemap_example1.png differ diff --git a/docs/documentation/utilities/images/statistics.png b/docs/documentation/utilities/images/statistics.png new file mode 100644 index 0000000..b80f885 Binary files /dev/null and b/docs/documentation/utilities/images/statistics.png differ diff --git a/docs/documentation/utilities/images/vscode_debugger.png b/docs/documentation/utilities/images/vscode_debugger.png new file mode 100644 index 0000000..0ca9db8 Binary files /dev/null and b/docs/documentation/utilities/images/vscode_debugger.png differ diff --git a/docs/documentation/utilities/repl.md b/docs/documentation/utilities/repl.md new file mode 100644 index 0000000..b3525b0 --- /dev/null +++ b/docs/documentation/utilities/repl.md @@ -0,0 +1,12 @@ +# REPL + +A simple `REPL`(read-eval-print loop) implementation is provided in the editor +which can be open from the bottom panel `GodotJS`. + +A dropdown list will be shown if `auto-complete` is available for the +input partial expression. Press `Up/Down` to select one, and `Tab` to confirm the selection. + +![read eval print loop](images/repl.gif) + +> **NOTE:** So far, `REPL` runs on the javascript runtime the `ScriptLanguage` using. +> And, the `auto-complete` feature may have side effects because the partial input is evaluated to get available candidates. diff --git a/docs/documentation/utilities/source-map.md b/docs/documentation/utilities/source-map.md new file mode 100644 index 0000000..e562d60 --- /dev/null +++ b/docs/documentation/utilities/source-map.md @@ -0,0 +1,8 @@ +# SourceMaps + +[A simple parser](https://github.com/godotjs/GodotJS/blob/main/internal/jsb_source_map.cpp) is implemented in `GodotJS` +to translate stacktrace info from javascript source position into typescript source position. + +![sourcemap example](images/sourcemap_example1.png) + +> **NOTE:** `inlineSourceMap` is not supported. diff --git a/docs/documentation/utilities/statistics.md b/docs/documentation/utilities/statistics.md new file mode 100644 index 0000000..43d5084 --- /dev/null +++ b/docs/documentation/utilities/statistics.md @@ -0,0 +1,15 @@ +# Statistics + +## Monitor + +Run the project, switch to the editor while the project is running and open `Debugger > Monitors` at the bottom of the editor window. +Scroll down to `GodotJS` section and check available statistics of the running project. You should see a graph appearing as follows: + +![monitor](images/monitor.png) + +## Statistics + +If you need to know the statistics of the JS environemnt running in the editor, open `GodotJS > Statistics` at the bottom of the editor window. +You will see a table as follows: + +![statistics](images/statistics.png) diff --git a/docs/examples/images/attached-properties.png b/docs/examples/images/attached-properties.png new file mode 100644 index 0000000..53c942a Binary files /dev/null and b/docs/examples/images/attached-properties.png differ diff --git a/docs/examples/images/change-health.png b/docs/examples/images/change-health.png new file mode 100644 index 0000000..d641ca5 Binary files /dev/null and b/docs/examples/images/change-health.png differ diff --git a/docs/examples/images/search-character.png b/docs/examples/images/search-character.png new file mode 100644 index 0000000..498e161 Binary files /dev/null and b/docs/examples/images/search-character.png differ diff --git a/docs/examples/load-json-in-singleton.md b/docs/examples/load-json-in-singleton.md index d2fffca..153fc3f 100644 --- a/docs/examples/load-json-in-singleton.md +++ b/docs/examples/load-json-in-singleton.md @@ -2,9 +2,7 @@ This example shows how to load a file like a `config` inside a singleton to access it everywhere. -For the `TypeScript` examples, all `.ts` files will be compiled as `.mjs` to the folder `scripts/generated/**`. - -> **Note:** If you use `TypeScript` you need to set `"resolveJsonModule": true` inside your `tsconfig.json`. +> **Note:** You need to set `"resolveJsonModule": true` inside your `tsconfig.json`. ## 1. Create file @@ -20,60 +18,25 @@ Next we write this to the `test.json`: ## 2. Create the singleton -We create a new file inside our `src` folder like `read-config.(mjs|ts)` and add this code to it: - -=== "JavaScript" - - ```javascript title="read-config.mjs" - import TestJson from "res://config/test.json"; - - export default class ReadConfig extends godot.Node { - static _singleton; - - static get singleton() { - return ReadConfig._singleton; - } - - constructor() { - super(); - if (!ReadConfig._singleton) { - ReadConfig._singleton = this; - } - } - - // This property is available for other classes - config = TestJson; - } - ``` - -=== "TypeScript" - - ```ts title="read-config.ts" - // @ts-ignore - import TestJson from "res://config/test.json"; - - type TestType = { - test: boolean; - }; - - export default class ReadConfig extends godot.Node { - static _singleton: ReadConfig; - - static get singleton() { - return ReadConfig._singleton; - } - - constructor() { - super(); - if (!ReadConfig._singleton) { - ReadConfig._singleton = this; - } - } - - // This property is available for other classes - config: TestType = TestJson as TestType; - } - ``` +We create a new file inside our `scripts` folder like `read-config.ts` and add this code to it: + +```ts title="read-config.ts" +// @ts-ignore +import { Node } from "godot"; +// @ts-ignore +import TestJson from "res://config/test.json"; + +type TestType = { + test: boolean; +}; +export default class ReadTest extends Node { + testType: TestType = TestJson as TestType; + logTestType() { + console.log(this.testType.test); + console.log(JSON.stringify(this.testType)); + } +} +``` ## 3. Autoload singleton in project @@ -83,35 +46,24 @@ We need to update the `[autoload]` inside `project.godot`: ... [autoload] -; Use the generated `.mjs` file instead of `.ts` -ReadConfig="*res://scripts/generated/read-config.mjs" +ReadTest="*res://examples/read-test/read-test.ts" ... ``` ## 4. Use the singleton in other class -In another class e.g. `main.(mjs|ts)` you need to import the `ReadConfig` then you can access every public property and method from `ReadConfig` via `ReadConfig.singleton`: - -=== "JavaScript" +In another class e.g. `main.ts` you need to import the `ReadConfig` then you +can access every public property and method from `ReadConfig` via +`this.get_node("/root/ReadTest")`: - ```ts title="main.mjs" - import ReadConfig from "./read-config"; +```ts title="main.ts" +import { Node } from "godot"; +import ReadTest from "./read-test"; - export default class Main extends godot.Node { - _ready() { - console.log(ReadConfig.singleton.config.test); // prints "true" - } - } - ``` - -=== "TypeScript" - - ```ts title="main.ts" - import ReadConfig from "./read-config"; - - export default class Main extends godot.Node { - _ready(): void { - console.log(ReadConfig.singleton.config.test); // prints "true" - } - } - ``` +export default class Main extends Node { + onButtonPressed() { + const readTest: ReadTest = this.get_node("/root/ReadTest") as ReadTest; + readTest.logTestType(); + } +} +``` diff --git a/docs/examples/read-file-local.md b/docs/examples/read-file-local.md index 8dbdf0a..f89f6d7 100644 --- a/docs/examples/read-file-local.md +++ b/docs/examples/read-file-local.md @@ -2,53 +2,37 @@ This example shows how to load any file as string from a local folder. -For the `TypeScript` examples, all `.ts` files will be compiled as `.mjs` to the folder `scripts/generated/**`. - ## 1. Create the file you want to read -In this example we try to read a ``.csv`` file, but it should work with any other file as well. +In this example we try to read a `.csv` file, but it should work with any other file as well. -We create a folder ``resources`` and add a new file `test.csv`: +We create a folder `resources` and add a new file `test.csv`: -````csv title="test.csv" +```csv title="test.csv" keys,en,de HELLO,hello,hallo -```` +``` ## 2. Read the file in class We use [FileAccess](https://docs.godotengine.org/en/stable/classes/class_fileaccess.html) to read the file. -We create a new file ``read-local-file.(mjs|ts)``: - -=== "JavaScript" - - ````ts title="read-local-file.mjs" - export default class ReadLocalFile extends godot.Node { - _ready() { - const file = new godot.FileAccess(); - file.open("res://resources/test.csv", godot.FileAccess.ModeFlags.READ); - let fileContent = ""; - while (!file.eof_reached()) { - fileContent += `${file.get_line()}\n`; - } - console.log(fileContent); - } - } - ```` -=== "TypeScript" - - ````ts title="read-local-file.ts" - export default class ReadLocalFile extends godot.Node { - _ready(): void { - const file = new godot.FileAccess(); - file.open("res://resources/test.csv", godot.FileAccess.ModeFlags.READ); - let fileContent: string = ""; - while (!file.eof_reached()) { - fileContent += `${file.get_line()}\n`; - } - console.log(fileContent); - } - } - ```` +We create a new file `read-local-file.ts`: +```ts title="read-local-file.ts" +import { FileAccess, Node } from "godot"; + +export default class ReadLocalFile extends Node { + _ready(): void { + const file = FileAccess.open( + "res://resources/test.csv", + FileAccess.ModeFlags.READ, + ); + let fileContent: string = ""; + while (!file.eof_reached()) { + fileContent += `${file.get_line()}\n`; + } + console.log(fileContent); + } +} +``` diff --git a/docs/examples/reuse-custom-resources.md b/docs/examples/reuse-custom-resources.md new file mode 100644 index 0000000..f729679 --- /dev/null +++ b/docs/examples/reuse-custom-resources.md @@ -0,0 +1,60 @@ +# Reuse custom resources + +This example shows how to create a custom resource and reuse it with different settings. + +## 1. Create custom resource + +We create a new file `character-attributes.ts` and add this code to it: + +```ts title="character-attributes.ts" +import { export_ } from "godot.annotations"; +import { Resource, Variant } from "godot"; + +export default class CharacterAttributes extends Resource { + @export_(Variant.Type.TYPE_INT) + health: number = 5; +} +``` + +## 2. Create new resources + +1. Left-click on the `res://` folder in the file system. +2. Select `➕ Create New > 📦 Resource` +3. Search for `CharacterAttributes` and select it. + ![character attributes searching](images/search-character.png) +4. Save the resource as `warrior.tres` +5. Repeat the process and save it as `mage.tres` +6. Change the `health` property to `8` for the `warrior.tres` resource + ![change health](images/change-health.png) + +## 3. Create a GodotJS class to consume the resources + +Create a new file `index.ts` and add this code to it: + +```ts title="index.ts" +import { Node, Variant } from "godot"; +import { export_ } from "godot.annotations"; +import CharacterAttributes from "./character-attributes"; + +export default class ResourceExample extends Node { + @export_(Variant.Type.TYPE_OBJECT) + warriorAttributes: CharacterAttributes | undefined = undefined; + + @export_(Variant.Type.TYPE_OBJECT) + mageAttributes: CharacterAttributes | undefined = undefined; + + _ready(): void { + console.log("warrior health", this.warriorAttributes?.health); + console.log("mage health", this.mageAttributes?.health); + } +} +``` + +## 4. Create a node and add ResourceExample + +1. Create a new scene and add a `Node` as root node. +2. Attach the `index.ts` script to the root node. +3. Select the root node and add the `warrior.tres` and `mage.tres` resources to the properties. + You can drag and drop the resources from the file system into the properties. + ![attached properties](images/attached-properties.png) +4. Run the project and check the output in the console. diff --git a/docs/examples/use-gdscript-in-js-or-ts.md b/docs/examples/use-gdscript-in-js-or-ts.md deleted file mode 100644 index b5ca70d..0000000 --- a/docs/examples/use-gdscript-in-js-or-ts.md +++ /dev/null @@ -1,68 +0,0 @@ -# Use GDScript in TypeScript - -This example shows how to use a class written in GDScript in another TS class. - -For the `TypeScript` examples, all `.ts` files will be compiled as `.mjs` to the folder `scripts/generated/**`. - -## 1. Create the GDScript file - -First we create a simple class with GDScript inside a new file `scripts/gd/GDTest.gd`: - -````gdscript title="GDTest.gd" -extends Node - -func _ready(): - pass - -func print_in_gd_test(): - print("gd_test") - -```` - -## 2. Create a declaration (*.d.ts) for GDScript (only TypeScript) - -For proper TypeScript support we need to add a ``gdtest.d.ts`` file: - -````ts title="gdtest.d.ts" -declare module "res://scripts/gd/GDTest.gd" { - class GDTest { - call(func: "print_in_gd_test"): void; - - static new() { - return this; - } - } - export = GDTest; -} -```` - -## 3. Use the class inside your TS file - -In the end we need to call the ``GDTest.gd`` from another `(.mjs|.ts)` file, like `main.(mjs|ts)`: - -=== "JavaScript" - ````ts title="main.mjs" - import GDTest from "res://scripts/gd/GDTest.gd"; - - export default class Main extends godot.Node { - _ready() { - const gdTest = GDTest.new(); - gdTest.call("print_in_gd_test"); - } - } - - ```` -=== "TypeScript" - ````ts title="main.ts" - import GDTest from "res://scripts/gd/GDTest.gd"; - - export default class Main extends godot.Node { - _ready(): void { - const gdTest: GDTest = GDTest.new(); - gdTest.call("print_in_gd_test"); - } - } - - ```` - -> **Note:** The important thing here is that you use `new()` to instantiate and `call` to execute the function diff --git a/docs/examples/use-gdscript-with-godot-js.md b/docs/examples/use-gdscript-with-godot-js.md new file mode 100644 index 0000000..8502639 --- /dev/null +++ b/docs/examples/use-gdscript-with-godot-js.md @@ -0,0 +1,64 @@ +# Use GDScript with GodotJS + +This example shows how to use a class written in GDScript in a GodotJS class. + +## 1. Create the GDScript file + +First we create a simple class with GDScript inside a new file `scripts/gd/GDTest.gd`: + +```gdscript title="GDTest.gd" +extends Node + +func _ready(): + pass + +func print_in_gd_test(): + print("gd_test") + +``` + +## 2. Create a declaration (\*.d.ts) for GDScript + +For proper TypeScript support we need to add a `gdscript.d.ts` file: + +```ts title="gdscript.d.ts" +declare module "gdscript" { + import { Node } from "godot"; + + declare class GDTest extends Node { + call(fn: "_ready"): void; + call(fn: "print_in_gd_test"): void; + } +} +``` + +## 3. Use the class inside your TS file + +In the end we need to call the `GDTest.gd` from a GodotJS class like `main.ts`: + +```ts title="main.ts" +import { ResourceLoader, Node } from "godot"; +import { GDTest } from "gdscript"; + +export default class Main extends Node { + _ready(): void { + const gdTest = ResourceLoader.load("res://examples/run-gd/runner.gd").call( + "new", + ) as GDTest; + + gdTest.call("print_in_gd_test"); + } +} +``` + +> **Note:** The important thing here is that you use `call("new")` to instantiate +> and `call("print_in_gd_test")` to execute the function + +## Use [godot-ts](https://github.com/godotjs/godot-ts) to generate the declaration files + +You can use [`godot-ts generate`](<(https://github.com/godotjs/godot-ts/blob/main/docs/API.md#generate)>) +to generate the declaration files for you. It will create a `gdscript.d.ts` +file with all custom GDScript classes inside your project. + +Furthermore, it will generate a ``generated/gdscript.ts`` file which contains a +function `instantiate_gdscript` which provides `ResourceLoader.load` with all available paths to GDScript files. diff --git a/docs/images/jumpybird.gif b/docs/images/jumpybird.gif new file mode 100644 index 0000000..06c546f Binary files /dev/null and b/docs/images/jumpybird.gif differ diff --git a/docs/images/snake_01.gif b/docs/images/snake_01.gif new file mode 100644 index 0000000..44412da Binary files /dev/null and b/docs/images/snake_01.gif differ diff --git a/docs/images/typescript_intellisence.png b/docs/images/typescript_intellisence.png new file mode 100644 index 0000000..f57b8fb Binary files /dev/null and b/docs/images/typescript_intellisence.png differ diff --git a/docs/index.md b/docs/index.md index 37818b1..aa7e795 100644 --- a/docs/index.md +++ b/docs/index.md @@ -6,54 +6,83 @@ hide: # **GodotJS** -***-- JavaScript and TypeScript language binding for Godot game engine --*** +**_-- JavaScript and TypeScript language binding for Godot game engine --_** -This module implements JavaScript/TypeScript language support for the Godot game engine using [QuickJS](https://bellard.org/quickjs/) as the JavaScript engine. +Supports JavaScript engines: +- V8 +- QuickJS +- JavaScriptCore +- Directly run scripts on the host browser JS VM when porting to web. + +![typescript_intellisence](images/typescript_intellisence.png) + +> **NOTE:** The core functionality is implemented and essentially usable but still under testing. ## Getting started -Read the [getting-started](https://godotjs.github.io/documentation/getting-started/). +Read the [getting-started](documentation/getting-started). ## Features -- Almost complete ES2020 support -- All Godot API available -- Operator overriding for built-in types (Vector3, Color, etc) -- TypeScript support -- [Using third-party libraries from npm](https://github.com/GodotExplorer/ECMAScriptDemos/tree/master/npm_module) -- Multi-thread support with Worker API -- Full code completion support for all Godot APIs including signals and enumerations -- Debug in Visual Studio Code with the [plugin](https://marketplace.visualstudio.com/items?itemName=geequlim.godot-javascript-debug) - currently not available for 4.x +- Godot ScriptLanguage integration +- Debug with Chrome/VSCode (with v8) and Safari (with JavaScriptCore) +- REPL in Editor +- Hot-reloading +- Support for multiple javascript engines ([v8](https://github.com/v8/v8), [quickjs](https://github.com/bellard/quickjs), [quickjs-ng](https://github.com/quickjs-ng/quickjs), [JavaScriptCore](https://developer.apple.com/documentation/javascriptcore), the host Browser JS) +- [Worker threads](documentation/experimental/worker.md) (limited support) (**experimental**) +- Asynchronously loaded modules (limited support) (_temporarily only available in v8.impl, quickjs.impl_) ## Getting the engine -No installation or setup necessary. The binaries for download are the complete, usable Godot editor and engine with JavaScript/TypeScript language support. +No installation or setup necessary. +The binaries for download are the complete, usable Godot editor +and engine with JavaScript/TypeScript language support. + +> **NOTE:** The GodotJS-Build workflow is currently run manually, it may not be built from the latest version of GodotJS. ### Binary downloads -Download the binaries from the [release page](https://github.com/GodotExplorer/ECMAScript/releases). +Download the binaries from the [GodotJS-Build](https://github.com/ialex32x/GodotJS-Build/releases). + +### Choose your engine + +Before initiating, make sure to select the JavaScript runtime you prefer between `v8`, `QuickJS` and `Web` (See [Supported Platforms](#supported-platforms)): + +- `v8` is proven to be one of the most powerful and high-performance JavaScript runtimes. +- `QuickJS` is a remarkable and lightweight option. +- `JavaScriptCore` is the built-in JavaScript engine for WebKit and bundled with macOS/iOS. +- `Web` is only suitable when building for Web. All scripts run on the host browser JS VM rather than an additional interpreter. + +### Building from source + +In some cases you want or need to build the engine from source. +Read the [Building from Source](documentation/building-from-source/) documentation in this case. -### Compiling from source +## Examples -- Clone the source code of [godot](https://github.com/godotengine/godot): - - `git clone git@github.com:godotengine/godot.git` or - - `git clone https://github.com/godotengine/godot.git` -- This branch uses version `4.1` so checkout the version with: `git checkout 4.1` -- Clone this module and put it into `godot/modules/javascript`: - - `git clone git@github.com:godotjs/javascript.git godot/modules/javascript` or - - `git clone https://github.com/godotjs/javascript.git godot/modules/javascript` -- [Recompile the godot engine](https://docs.godotengine.org/en/4.1/development/compiling/index.html) - - Use `scons` with those additional options `warnings=extra werror=yes module_text_server_fb_enabled=yes` to show all potential errors: - - Windows: `scons platform=windows warnings=extra werror=yes module_text_server_fb_enabled=yes` - - MacOS: `scons platform=macos arch=arm64 warnings=extra werror=yes module_text_server_fb_enabled=yes` +For more information on how to use `GodotJS` in a project, check out [GodotJSExample](https://github.com/ialex32x/GodotJSExample.git) for examples written in typescript. -## Documentation, Tutorials & Demos +![Example: Snake](images/snake_01.gif) +![Example: Jummpy Bird](images/jumpybird.gif) +## Supported Platforms -Read this [documentation](https://godotjs.github.io/documentation/getting-started/) or look at the tutorials or demos: +| | v8 | quickjs | quickjs-ng | Web Builtin JS | JavaScriptCore | +| -------------- | ---------------- | --------------- | --------------- | ------------------- | --------------- | +| Windows:x86_64 | ✅ | ✅ | ✅ | ❌ | ❌ | +| Windows:arm64 | ✅ | ✅ | ✅ | ❌ | ❌ | +| MacOS:x86_64 | ✅ (not tested) | ✅ (not tested) | ✅ (not tested) | ❌ | ✅ (not tested) | +| MacOS:arm64 | ✅ | ✅ | ✅ | ❌ | ✅ (debugging) | +| Linux:x86_64 | ✅ (not tested) | ✅ (not tested) | ✅ | ❌ | ❌ | +| Linux:arm64 | ✅ | ✅ | ✅ | ❌ | ❌ | +| Android:x86_64 | ✅ (not tested) | ✅ (not tested) | ✅ (not tested) | ❌ | ❌ | +| Android:arm64 | ✅ | ✅ (not tested) | ✅ (not tested) | ❌ | ❌ | +| iOS:x86_64 | ✅ (not tested) | ✅ (not tested) | ✅ (not tested) | ❌ | ✅ (not tested) | +| iOS:arm64 | ✅ (not tested) | ✅ (not tested) | ✅ (not tested) | ❌ | ✅ (not tested) | +| Web:wasm32 | ❌ | ✅ (not tested) | ✅ (not tested) | ✅ (debugging) | ❌ | +| Debugger | ✅ Chrome/VSCode | ❌ | ❌ | ✅ browser devtools | ✅ Safari | -- [ECMAScriptDemos](https://github.com/Geequlim/ECMAScriptDemos) - Demos -- [godot-ECMAScript-cookbook](https://github.com/why-try313/godot-ECMAScript-cookbook/wiki) - Tutorial -- [godot-typescript-starter](https://github.com/citizenll/godot-typescript-starter) - Template -- [godot-js-template](https://github.com/fukaraadam-workspace/godot-js-template) - Template +> Android: only tested on ndk_platform=android-24 +> Web: only tested on emsdk-3.1.64 +> JavaScriptCore: macOS 15, iOS 18 (support for lower versions may be implemented in future versions) diff --git a/docs/misc/architecture.md b/docs/misc/architecture.md new file mode 100644 index 0000000..785b200 --- /dev/null +++ b/docs/misc/architecture.md @@ -0,0 +1,27 @@ + +```mermaid +flowchart LR + +subgraph JS Environment + v8-->v8.impl + quickjs-->quickjs.impl + quickjs-ng-->quickjs.impl + web{emscripten}-->web.impl +end + +v8.impl-->bridge +quickjs.impl-->bridge +web.impl-->bridge + +subgraph Godot Integration + bridge-->weaver + bridge-->weaver-editor +end + +bridge{{bridge: JS Runtime Bridge}} +internal(internal: Native/Godot Helper Functions) +weaver(weaver: Godot Scripting Intgeration) +weaver-editor(weaver-editor: Godot Editor Intgeration) + +``` + diff --git a/docs/misc/breaking-changes.md b/docs/misc/breaking-changes.md new file mode 100644 index 0000000..aa82df3 --- /dev/null +++ b/docs/misc/breaking-changes.md @@ -0,0 +1,16 @@ +# Breaking Changes + +## v1.0.0 + +* `Worker` (in global context) is now moved into the module `godot.worker`, and renamed to `JSWorker` + +## v0.9.8 (1.0.0-pre) + +> **NOTE:** Regenerating `.d.ts` files in your project will help you a lot to fix most errors caused by the broken changes below. +> Old annotations still work temporarily in this version, please update your codes before the next version. + +* All annotations are moved into `godot.annotations` module (from `jsb.core`). +* `GLOBAL_GET` and `EDITOR_GET` are moved into `godot` module (from `jsb.core`). +* `callable()` is removed from `jsb.core`, use `Callable.create` in `godot` module instead. +* `to_array_buffer()` is removed from `jsb.core`, use `PackedByteArray.to_array_buffer()` instead. +* `$wait` is removed from `jsb.core`, use `Signal<..., R>.as_promise()` instead. diff --git a/mkdocs.yml b/mkdocs.yml index d0584bc..809e2ce 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,7 @@ # yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json site_name: GodotJS site_url: https://godotjs.github.io -repo_url: https://github.com/godotjs/javascript +repo_url: https://github.com/godotjs/GodotJS site_description: JS/TS language binding for Godot extra_css: - css/extra.css @@ -9,17 +9,41 @@ nav: - Home: index.md - Docs: - Getting Started: documentation/getting-started.md - - API: documentation/api.md - - TypeScript: - - Intro: documentation/typescript/intro.md - - GodotTS: documentation/typescript/godot-ts.md - - Decorators: documentation/typescript/decorators.md - - NPM Modules: documentation/typescript/npm-modules.md - - Gotchas: documentation/gotchas.md + - Use External Editor: documentation/use-external-editor.md + - Scripting: + - Intro: documentation/godot-js-scripts/intro.md + - Decorators: documentation/godot-js-scripts/decorators.md + - Signals: documentation/godot-js-scripts/signals.md + - NPM Dependencies: documentation/godot-js-scripts/npm-dependencies.md + - Cyclic Imports: documentation/godot-js-scripts/cyclic-imports.md + - Bindings: documentation/godot-js-scripts/bindings.md + - Running code in editor: documentation/godot-js-scripts/code-in-editor.md + - Utilities: + - REPL: documentation/utilities/repl.md + - SourceMaps: documentation/utilities/source-map.md + - Statistics: documentation/utilities/statistics.md + - Debugger: documentation/utilities/debugger.md + - Experimental: + - Worker: documentation/experimental/worker.md + - Building From Source: + - Intro: documentation/building-from-source/index.md + - V8: documentation/building-from-source/v8.md + - QuickJS: documentation/building-from-source/quick-js.md + - JavaScriptCore: documentation/building-from-source/javascript-core.md + - Web: documentation/building-from-source/web.md - Examples: - Load JSON in Singleton: examples/load-json-in-singleton.md - Read file local: examples/read-file-local.md - - Use GDScript in JS or TS: examples/use-gdscript-in-js-or-ts.md + - Use GDScript in JS or TS: examples/use-gdscript-with-godot-js.md + - Reuse custom resources: examples/reuse-custom-resources.md + - Development: + - Dependencies: development/dependencies.md + - Compiler Options: development/compiler-options.md + - GodotJS Debugging: development/godot-js-debugging.md + - Godot Unit Testing: development/godot-unit-testing.md + - Misc: + - Breaking Changes: misc/breaking-changes.md + - Architecture: misc/architecture.md theme: name: material @@ -33,6 +57,8 @@ theme: - navigation.instant.prefetch - navigation.path - navigation.top + - navigation.expand + - navigation.footer - search.suggest - search.highlight - content.code.copy @@ -71,3 +97,8 @@ markdown_extensions: - pymdownx.superfences - pymdownx.tabbed: alternate_style: true + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format