Compare commits

..

No commits in common. "master" and "v1.0-dev1" have entirely different histories.

597 changed files with 4685 additions and 32372 deletions

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
ko_fi: limbonaut

View File

@ -1,64 +0,0 @@
name: Bug report
description: Report a bug in LimboAI
labels:
- bug
body:
- type: markdown
attributes:
value: |
- Write a descriptive title above.
- Search [open](https://github.com/limbonaut/limboai/issues) and [closed](https://github.com/limbonaut/limboai/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported.
- type: input
attributes:
label: Godot version
description: |
- Specify the Godot version and hardware information if relevant.
- You can copy the version info by clicking on it in the Godot status bar.
- Alternatively, you can copy the version and hardware info in Godot using the main menu command "Help -> Copy System Info".
placeholder: v4.2.2.limboai+v1.1.0.gha [15073afe3]
validations:
required: true
- type: input
attributes:
label: LimboAI version
description: |
- Specify the LimboAI version.
- You can copy the version info by clicking on it in the toolbar of the LimboAI editor (top-right corner).
placeholder: v1.1.0 [8fa609e]
validations:
required: true
- type: dropdown
id: variant
attributes:
label: LimboAI variant
description: Which variant of our plugin are you running?
options:
- GDExtension / AssetLib
- Module (custom editor or template build)
default: 0
validations:
required: true
- type: textarea
attributes:
label: Issue description
description: |
- Describe your issue in detail. What doesn't work and how do you expect it to work instead?
- Provide screenshots and/or a console output if it helps to convey the problem.
validations:
required: true
- type: textarea
attributes:
label: How to reproduce
description: |
- Provide a list of steps or sample code that reproduces the issue.
- You can provide a small Godot project which reproduces the issue, with no unnecessary files included.
- Drag and drop a ZIP archive to upload it (10Mb limit).
- Don't include the `.godot` folder in the archive.
- Redroduction project helps to find the bug more quickly!
validations:
required: true

View File

@ -1,14 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Documentation
url: https://limboai.readthedocs.io/en/stable/
about: Please see our documentation for more information.
- name: Discussions
url: https://github.com/limbonaut/limboai/discussions
about: Need help? Ask questions in Discussions or on our Discord server.
- name: Discord Server
url: https://discord.gg/N5MGC95GpP
about: Share your experience or get help on our Discord server.

View File

@ -1,38 +0,0 @@
name: Feature or improvement proposal
description: Propose a new feature to be added or improved in LimboAI
labels:
- enhancement
body:
- type: markdown
attributes:
value: |
- Write a descriptive title above.
- Search [open](https://github.com/limbonaut/limboai/issues) and [closed](https://github.com/limbonaut/limboai/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported.
- type: textarea
attributes:
label: Problem statement
description: |
- Describe the problem or limitation you're currently facing with the LimboAI plugin.
- If it helps, describe the project you're working on and how it relates to the problem or limitation.
validations:
required: true
- type: textarea
attributes:
label: Proposed solution
description: |
- Describe your proposed solution and how it helps to overcome the problem or limitation.
- If it helps, show how it will work with code, pseudo-code, mock-ups, and/or diagrams.
- You can include any images or videos with drag'n'drop, and sample code blocks with <code>```</code> tags.
validations:
required: true
- type: textarea
attributes:
label: Alternatives
description: |
- Describe alternative solutions and features you've considered.
- If this enhancement will not be used often, can it be worked around with a few clicks or lines of code?
validations:
required: true

View File

@ -1,33 +0,0 @@
name: Build .NET assemblies
inputs:
platform:
required: true
type: choice
options:
- linuxbsd
- windows
- macos
bin:
required: true
type: string
runs:
using: "composite"
steps:
- name: Override GODOT_VERSION_STATUS for NuGet packages
shell: bash
run: |
GODOT_VERSION_STATUS=$(echo "${GODOT_VERSION_STATUS}" | sed "s|+|-|").gha
echo "GODOT_VERSION_STATUS=${GODOT_VERSION_STATUS}" >> "$GITHUB_ENV"
echo "GODOT_VERSION_STATUS: ${GODOT_VERSION_STATUS}"
- name: Generate C# glue
shell: bash
run: |
./bin/${{inputs.bin}} --headless --generate-mono-glue ./modules/mono/glue || true
- name: Build .NET assemblies
shell: bash
run: |
python ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=${{ inputs.platform }}

View File

@ -15,10 +15,10 @@ runs:
echo "GDEXTENSION_VERSION=${GDEXTENSION_VERSION}" >> "$GITHUB_ENV" echo "GDEXTENSION_VERSION=${GDEXTENSION_VERSION}" >> "$GITHUB_ENV"
cd .. cd ../limboai
echo "LIMBOAI_VERSION=$( (git describe --tags --exact-match HEAD || git rev-parse --short HEAD) | sed 's/\(.*\)-\(.*\)/\1.\2/g' )" >> "$GITHUB_ENV" echo "LIMBOAI_VERSION=$( (git describe --tags --exact-match HEAD || git rev-parse --short HEAD) | sed 's/\(.*\)-\(.*\)/\1.\2/g' )" >> "$GITHUB_ENV"
- name: Set NAME_PREFIX - name: Set NAME_PREFIX
shell: bash shell: bash
run: | run: echo "NAME_PREFIX=gdextension-${GDEXTENSION_VERSION}.limboai+${LIMBOAI_VERSION}" >> "$GITHUB_ENV"
echo "NAME_PREFIX=limboai+${LIMBOAI_VERSION}.gdextension-${GDEXTENSION_VERSION}" >> "$GITHUB_ENV"

View File

@ -12,7 +12,7 @@ runs:
- name: Set NAME_PREFIX - name: Set NAME_PREFIX
shell: bash shell: bash
run: echo "NAME_PREFIX=limboai+${LIMBOAI_VERSION}.godot-${GODOT_VERSION}" >> "$GITHUB_ENV" run: echo "NAME_PREFIX=godot-${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}" >> "$GITHUB_ENV"
- name: Set GODOT_VERSION_STATUS & BUILD_NAME - name: Set GODOT_VERSION_STATUS & BUILD_NAME
shell: bash shell: bash

View File

@ -1,58 +0,0 @@
name: Setup Linux toolchain
inputs:
arch:
required: true
runs:
using: "composite"
steps:
- name: Set up environment
shell: bash
run: |
# ! Settings:
TOOLCHAIN_64_URL=https://github.com/godotengine/buildroot/releases/download/godot-2020.11.x-2/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
TOOLCHAIN_64_SHA=16c8302fcb676c1f0fb9df73d6cff250ba1f4286
TOOLCHAIN_32_URL=https://github.com/godotengine/buildroot/releases/download/godot-2020.11.x-2/i686-godot-linux-gnu_sdk-buildroot.tar.bz2
TOOLCHAIN_32_SHA=6171652abc54ef219e5187bc53660ee4e2f796f4
TOOLCHAIN_ARM64_URL=https://github.com/godotengine/buildroot/releases/download/godot-2023.08.x-3/aarch64-godot-linux-gnu_sdk-buildroot.tar.bz2
TOOLCHAIN_ARM64_SHA=73bed3d26b92c8b9a93c0ceb6bcce8fe567d1764
TOOLCHAIN_ARM32_URL=https://github.com/godotengine/buildroot/releases/download/godot-2023.08.x-3/arm-godot-linux-gnueabihf_sdk-buildroot.tar.bz2
TOOLCHAIN_ARM32_SHA=aa5853fd73faec3837c6127649471275d7e61cb2
# ! Export variables:
if [[ "${{ inputs.arch }}" == "x86_64" ]]; then
echo "TOOLCHAIN_URL=${TOOLCHAIN_64_URL}" >> "$GITHUB_ENV"
echo "TOOLCHAIN_SHA=${TOOLCHAIN_64_SHA}" >> "$GITHUB_ENV"
elif [[ "${{ inputs.arch }}" == "x86_32" ]]; then
echo "TOOLCHAIN_URL=${TOOLCHAIN_32_URL}" >> "$GITHUB_ENV"
echo "TOOLCHAIN_SHA=${TOOLCHAIN_32_SHA}" >> "$GITHUB_ENV"
elif [[ "${{ inputs.arch }}" == "arm64" ]]; then
echo "TOOLCHAIN_URL=${TOOLCHAIN_ARM64_URL}" >> "$GITHUB_ENV"
echo "TOOLCHAIN_SHA=${TOOLCHAIN_ARM64_SHA}" >> "$GITHUB_ENV"
elif [[ "${{ inputs.arch }}" == "arm32" ]]; then
echo "TOOLCHAIN_URL=${TOOLCHAIN_ARM32_URL}" >> "$GITHUB_ENV"
echo "TOOLCHAIN_SHA=${TOOLCHAIN_ARM32_SHA}" >> "$GITHUB_ENV"
fi
- name: Cache buildroot
id: cache-buildroot
uses: actions/cache@v4
with:
path: buildroot
key: ${{env.TOOLCHAIN_SHA}}
- name: Set up buildroot
if: steps.cache-buildroot.outputs.cache-hit != 'true'
shell: bash
run: |
mkdir buildroot
wget ${TOOLCHAIN_URL} -O buildroot/buildroot.tar.bz2
cd buildroot
echo "${TOOLCHAIN_SHA} buildroot.tar.bz2"
echo "${TOOLCHAIN_SHA} buildroot.tar.bz2" | shasum --check
tar -xjf buildroot.tar.bz2 --strip-components=1
ls -l
rm buildroot.tar.bz2
./relocate-sdk.sh
cd ..

View File

@ -2,22 +2,22 @@ name: 🔗 All builds
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: 4.2.1-stable
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
godot-cpp-ref: godot-cpp-treeish:
description: A tag, branch or commit hash in the godot-cpp repository. description: A tag, branch or commit hash in the godot-cpp repository.
type: string type: string
default: master default: 4.2
jobs: jobs:
cache-sha: cache-sha:
name: Cache SHA name: Cache treeish SHA
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
godot-sha: ${{ steps.cache-sha.outputs.godot-sha }} godot-sha: ${{ steps.cache-sha.outputs.godot-sha }}
@ -27,13 +27,13 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
- name: Cache SHA - name: Cache SHA
id: cache-sha id: cache-sha
@ -47,24 +47,24 @@ jobs:
needs: cache-sha needs: cache-sha
uses: ./.github/workflows/android.yml uses: ./.github/workflows/android.yml
with: with:
godot-ref: ${{ needs.cache-sha.outputs.godot-sha }} godot-treeish: ${{ needs.cache-sha.outputs.godot-sha }}
limboai-ref: ${{ needs.cache-sha.outputs.limboai-sha }} limboai-treeish: ${{ needs.cache-sha.outputs.limboai-sha }}
ios-build: ios-build:
name: 🍏 iOS name: 🍏 iOS
needs: cache-sha needs: cache-sha
uses: ./.github/workflows/ios.yml uses: ./.github/workflows/ios.yml
with: with:
godot-ref: ${{ needs.cache-sha.outputs.godot-sha }} godot-treeish: ${{ needs.cache-sha.outputs.godot-sha }}
limboai-ref: ${{ needs.cache-sha.outputs.limboai-sha }} limboai-treeish: ${{ needs.cache-sha.outputs.limboai-sha }}
linux-build: linux-build:
name: 🐧 Linux name: 🐧 Linux
needs: cache-sha needs: cache-sha
uses: ./.github/workflows/linux.yml uses: ./.github/workflows/linux.yml
with: with:
godot-ref: ${{ needs.cache-sha.outputs.godot-sha }} godot-treeish: ${{ needs.cache-sha.outputs.godot-sha }}
limboai-ref: ${{ needs.cache-sha.outputs.limboai-sha }} limboai-treeish: ${{ needs.cache-sha.outputs.limboai-sha }}
test-build: false test-build: false
macos-build: macos-build:
@ -72,16 +72,16 @@ jobs:
needs: cache-sha needs: cache-sha
uses: ./.github/workflows/macos.yml uses: ./.github/workflows/macos.yml
with: with:
godot-ref: ${{ needs.cache-sha.outputs.godot-sha }} godot-treeish: ${{ needs.cache-sha.outputs.godot-sha }}
limboai-ref: ${{ needs.cache-sha.outputs.limboai-sha }} limboai-treeish: ${{ needs.cache-sha.outputs.limboai-sha }}
windows-build: windows-build:
name: 🪟 Windows name: 🪟 Windows
needs: cache-sha needs: cache-sha
uses: ./.github/workflows/windows.yml uses: ./.github/workflows/windows.yml
with: with:
godot-ref: ${{ needs.cache-sha.outputs.godot-sha }} godot-treeish: ${{ needs.cache-sha.outputs.godot-sha }}
limboai-ref: ${{ needs.cache-sha.outputs.limboai-sha }} limboai-treeish: ${{ needs.cache-sha.outputs.limboai-sha }}
test-build: false test-build: false
web-build: web-build:
@ -89,39 +89,14 @@ jobs:
needs: cache-sha needs: cache-sha
uses: ./.github/workflows/web.yml uses: ./.github/workflows/web.yml
with: with:
godot-ref: ${{ needs.cache-sha.outputs.godot-sha }} godot-treeish: ${{ needs.cache-sha.outputs.godot-sha }}
limboai-ref: ${{ needs.cache-sha.outputs.limboai-sha }} limboai-treeish: ${{ needs.cache-sha.outputs.limboai-sha }}
gdextension: gdextension:
name: 🔌 GDExtension name: 🔌 GDExtension
needs: cache-sha needs: cache-sha
uses: ./.github/workflows/gdextension.yml uses: ./.github/workflows/gdextension.yml
with: with:
godot-cpp-ref: ${{ inputs.godot-cpp-ref }} godot-cpp-treeish: ${{ inputs.godot-cpp-treeish }}
limboai-ref: ${{ needs.cache-sha.outputs.limboai-sha }} limboai-treeish: ${{ needs.cache-sha.outputs.limboai-sha }}
test-build: false test-build: false
demo:
name: 🎮️ Demo project
needs: cache-sha
uses: ./.github/workflows/demo.yml
with:
limboai-ref: ${{ needs.cache-sha.outputs.limboai-sha }}
merge_templates:
name: 📦 Merge templates
if: ${{ always() }}
needs:
[
cache-sha,
android-build,
ios-build,
linux-build,
macos-build,
windows-build,
web-build,
]
uses: ./.github/workflows/merge_templates.yml
with:
godot-ref: ${{ needs.cache-sha.outputs.godot-sha }}
limboai-ref: ${{ needs.cache-sha.outputs.limboai-sha }}

View File

@ -2,22 +2,22 @@ name: 🤖 Android builds
on: on:
workflow_call: workflow_call:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
workflow_dispatch: workflow_dispatch:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
@ -37,123 +37,59 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
# * Standard arm64
- name: Template (arm64, debug) - name: Template (arm64, debug)
arch: arm64 arch: arm64
target: template_debug target: template_debug
dotnet: false
- name: Template (arm64, release) - name: Template (arm64, release)
arch: arm64 arch: arm64
target: template_release target: template_release
dotnet: false
# * Standard arm32
- name: Template (arm32, debug) - name: Template (arm32, debug)
arch: arm32 arch: arm32
target: template_debug target: template_debug
dotnet: false
- name: Template (arm32, release) - name: Template (arm32, release)
arch: arm32 arch: arm32
target: template_release target: template_release
dotnet: false
# * Standard x86_64
- name: Template (x86_64, debug) - name: Template (x86_64, debug)
arch: x86_64 arch: x86_64
target: template_debug target: template_debug
dotnet: false
- name: Template (x86_64, release) - name: Template (x86_64, release)
arch: x86_64 arch: x86_64
target: template_release target: template_release
dotnet: false
# * Standard x86_32
- name: Template (x86_32, debug) - name: Template (x86_32, debug)
arch: x86_32 arch: x86_32
target: template_debug target: template_debug
dotnet: false
- name: Template (x86_32, release) - name: Template (x86_32, release)
arch: x86_32 arch: x86_32
target: template_release target: template_release
dotnet: false
# * .NET arm64
- name: Template .NET (arm64, debug)
arch: arm64
target: template_debug
dotnet: true
- name: Template .NET (arm64, release)
arch: arm64
target: template_release
dotnet: true
# * .NET arm32
- name: Template .NET (arm32, debug)
arch: arm32
target: template_debug
dotnet: true
- name: Template .NET (arm32, release)
arch: arm32
target: template_release
dotnet: true
# * .NET x86_64
- name: Template .NET (x86_64, debug)
arch: x86_64
target: template_debug
dotnet: true
- name: Template .NET (x86_64, release)
arch: x86_64
target: template_release
dotnet: true
# * .NET x86_32
- name: Template .NET (x86_32, debug)
arch: x86_32
target: template_debug
dotnet: true
- name: Template .NET (x86_32, release)
arch: x86_32
target: template_release
dotnet: true
env: env:
BIN: godot.linuxbsd.${{matrix.target}}.${{matrix.arch}}${{ matrix.dotnet == true && '.mono' || '' }} BIN: godot.linuxbsd.${{matrix.target}}.${{matrix.arch}} #${{ matrix.build-mono == true && '.mono' || '' }}
steps: steps:
- name: Clone Godot - name: Clone Godot
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables. # Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version - uses: ./modules/limboai/.github/actions/init-version
- name: Set up Java 17 - name: Set up Java 17
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 17
@ -165,75 +101,65 @@ jobs:
python --version python --version
scons --version scons --version
# ! Note: we stopped using the scons cache in release builds. - name: Set up scons cache
# - name: Set up scons cache uses: actions/cache@v3
# uses: actions/cache@v4 with:
# with: path: ${{github.workspace}}/.scons_cache/
# path: ${{github.workspace}}/.scons_cache/ key: ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
# key: ${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} restore-keys: |
# restore-keys: | ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
# ${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}
# ${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}} ${{env.BIN}}-${{inputs.godot-treeish}}
# ${{env.BIN}}-${{inputs.godot-ref}}
- name: Compilation - name: Compilation
env: env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/ SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: | run: |
scons platform=android target=${{matrix.target}} arch=${{matrix.arch}} module_mono_enabled=${{matrix.dotnet}} ${{env.SCONSFLAGS}} scons platform=android target=${{matrix.target}} arch=${{matrix.arch}} ${{env.SCONSFLAGS}}
ls platform/android/java/lib/libs/* ls platform/android/java/lib/libs/*
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: tmp-android${{matrix.dotnet == true && '-dotnet' || ''}}-templates-${{strategy.job-index}} name: android-templates
path: platform/android/java/lib/libs/* path: platform/android/java/lib/libs/*
make-android-package: make-android-package:
runs-on: "ubuntu-20.04" runs-on: "ubuntu-20.04"
name: Make Android package
needs: android-builds needs: android-builds
name: ${{ matrix.name }}
strategy:
fail-fast: false
matrix:
include:
- name: Package Android templates
dotnet: false
- name: Package Android .NET templates
dotnet: true
steps: steps:
- name: Clone Godot - name: Clone Godot
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables. # Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version - uses: ./modules/limboai/.github/actions/init-version
- name: Download Android template builds - name: Download Android template builds
uses: actions/download-artifact@v4 uses: actions/download-artifact@v3
with: with:
pattern: tmp-android${{matrix.dotnet == true && '-dotnet' || ''}}-templates-* name: android-templates
merge-multiple: true
path: platform/android/java/lib/libs/ path: platform/android/java/lib/libs/
- name: Set up Java 17 - name: Set up Java 17
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 17
- name: Set up Python 3.x - name: Set up Python 3.x
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: 3.x python-version: 3.x
architecture: x64 architecture: x64
@ -254,24 +180,23 @@ jobs:
mkdir -p out/templates/ mkdir -p out/templates/
mv bin/android_* out/templates/ mv bin/android_* out/templates/
echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}${{matrix.dotnet == true && '.mono' || ''}}" > out/templates/version.txt echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}" > out/templates/version.txt
ls -l out/* ls -l out/*
- name: Delete Android template builds - name: Delete Android template builds
uses: geekyeggo/delete-artifact@v5 uses: geekyeggo/delete-artifact@v2
with: with:
name: tmp-android${{matrix.dotnet == true && '-dotnet' || ''}}-templates-* name: android-templates
useGlob: true
failOnError: false failOnError: false
- name: Upload Android libs - name: Upload Android libs
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: ${{env.NAME_PREFIX}}${{matrix.dotnet == true && '.dotnet' || ''}}.android-lib name: ${{env.NAME_PREFIX}}.android-lib
path: bin/godot-lib.* path: bin/godot-lib.*
- name: Upload Android templates - name: Upload Android templates
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: ${{env.NAME_PREFIX}}${{matrix.dotnet == true && '.dotnet' || ''}}.export-templates.android name: ${{env.NAME_PREFIX}}.export-templates
path: out/* path: out/*

View File

@ -1,46 +0,0 @@
name: 🎮️ Demo project
on:
workflow_call:
inputs:
limboai-ref:
description: A tag, branch or commit hash in the LimboAI repository.
type: string
default: master
workflow_dispatch:
inputs:
limboai-ref:
description: A tag, branch or commit hash in the LimboAI repository.
type: string
default: master
jobs:
package-demo:
runs-on: ubuntu-latest
name: Package demo
steps:
- name: Clone LimboAI module
uses: actions/checkout@v4
with:
fetch-tags: true
ref: ${{ inputs.limboai-ref }}
- name: Init version
run: echo "LIMBOAI_VERSION=$( (git describe --tags --exact-match HEAD || git rev-parse --short HEAD) | sed 's/\(.*\)-\(.*\)/\1.\2/g' )" >> "$GITHUB_ENV"
- name: Prepare artifact
shell: bash
run: |
cp {README,LICENSE}.md demo/
mkdir -p demo/addons/limboai/
cp -R icons/ demo/addons/limboai/icons
echo "${LIMBOAI_VERSION}" > demo/version.txt
- name: Upload artifact
uses: actions/upload-artifact@v4
env:
NAME: limboai+${{env.LIMBOAI_VERSION}}.demo-project
with:
name: ${{ env.NAME }}
path: demo/*

View File

@ -2,57 +2,43 @@ name: 🔌 GDExtension
on: on:
workflow_call: workflow_call:
inputs: inputs:
godot-cpp-ref: godot-cpp-treeish:
description: A tag, branch or commit hash in the godot-cpp repository. description: A tag, branch or commit hash in the godot-cpp repository.
type: string type: string
default: godot-4.3-stable default: 4.2
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
test-build: test-build:
description: Limit to pre-defined test builds description: Should we perform only a limited number of test builds?
type: boolean
default: false
debug-symbols:
description: Build with debug symbols
type: boolean type: boolean
default: false default: false
workflow_dispatch: workflow_dispatch:
inputs: inputs:
godot-cpp-ref: godot-cpp-treeish:
description: A tag, branch or commit hash in the godot-cpp repository. description: A tag, branch or commit hash in the godot-cpp repository.
type: string type: string
default: godot-4.3-stable default: 4.2
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
test-build: test-build:
description: Limit to pre-defined test builds description: Should we perform only a limited number of test builds?
type: boolean
default: false
debug-symbols:
description: Build with debug symbols
type: boolean type: boolean
default: false default: false
# Global Settings # Global Settings
env: env:
SCONS_CACHE_LIMIT: 4096 SCONS_CACHE_LIMIT: 4096
SCONSFLAGS: use_mingw=yes dev_build=no SCONSFLAGS: dev_build=no debug_symbols=no
EM_VERSION: 3.1.45
EM_CACHE_FOLDER: "emsdk-cache"
GODOT_VERSION: 4.4-beta3
GODOT_REPO: godotengine/godot-builds
jobs: jobs:
gdextension: gdextension:
runs-on: ${{ matrix.opts.runner }} runs-on: ${{ matrix.opts.runner }}
name: ${{ matrix.opts.name }} name: ${{ matrix.opts.name }}
outputs:
name-prefix: ${{ steps.output-name-prefix.outputs.name-prefix }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -99,288 +85,125 @@ jobs:
arch: universal arch: universal
should-build: true should-build: true
- name: 🌐 Web (wasm32, release)
runner: ubuntu-20.04
platform: web
target: template_release
arch: wasm32
should-build: ${{ !inputs.test-build }}
- name: 🌐 Web (wasm32, debug)
runner: ubuntu-20.04
platform: web
target: editor
arch: wasm32
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (arm64, release)
runner: ubuntu-20.04
platform: android
target: template_release
arch: arm64
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (arm64, debug)
runner: ubuntu-20.04
platform: android
target: editor
arch: arm64
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (arm32, release)
runner: ubuntu-20.04
platform: android
target: template_release
arch: arm32
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (arm32, debug)
runner: ubuntu-20.04
platform: android
target: editor
arch: arm32
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (x86_64, release)
runner: ubuntu-20.04
platform: android
target: template_release
arch: x86_64
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (x86_64, debug)
runner: ubuntu-20.04
platform: android
target: editor
arch: x86_64
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (x86_32, release)
runner: ubuntu-20.04
platform: android
target: template_release
arch: x86_32
should-build: ${{ !inputs.test-build }}
- name: 🤖 Android (x86_32, debug)
runner: ubuntu-20.04
platform: android
target: editor
arch: x86_32
should-build: ${{ !inputs.test-build }}
- name: 🍏 iOS (arm64, release)
runner: macos-latest
platform: ios
target: template_release
arch: arm64
should-build: ${{ !inputs.test-build }}
- name: 🍏 iOS (arm64, debug)
runner: macos-latest
platform: ios
target: editor
arch: arm64
should-build: ${{ !inputs.test-build }}
- name: 🍏 iOS (simulator, release)
runner: macos-latest
platform: ios
target: template_release
arch: universal
scons-flags: ios_simulator=yes
should-build: ${{ !inputs.test-build }}
- name: 🍏 iOS (simulator, debug)
runner: macos-latest
platform: ios
target: editor
arch: universal
scons-flags: ios_simulator=yes
should-build: ${{ !inputs.test-build }}
exclude: exclude:
- { opts: { should-build: false } } - { opts: {should-build: false }}
env: env:
BIN: liblimboai.${{matrix.opts.platform}}.${{matrix.opts.target}}.${{matrix.opts.arch}} BIN: liblimboai.${{matrix.opts.platform}}.${{matrix.opts.target}}.${{matrix.opts.arch}}
steps: steps:
- name: Clone LimboAI module
uses: actions/checkout@v4
with:
fetch-tags: true
ref: ${{ inputs.limboai-ref }}
- name: Clone godot-cpp - name: Clone godot-cpp
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot-cpp repository: godotengine/godot-cpp
fetch-tags: true fetch-tags: true
path: godot-cpp path: godot-cpp
ref: ${{ inputs.godot-cpp-ref }} ref: ${{ inputs.godot-cpp-treeish }}
- name: Clone LimboAI module
uses: actions/checkout@v4
with:
path: limboai
fetch-tags: true
ref: ${{ inputs.limboai-treeish }}
# Inits GDEXTENSION_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables. # Inits GDEXTENSION_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./.github/actions/init-version-gdext - uses: ./limboai/.github/actions/init-version-gdext
- name: Output NAME_PREFIX - name: Set up Linux buildroot x86_64
id: output-name-prefix if: matrix.opts.platform == 'linux' && matrix.opts.arch == 'x86_64'
run: echo "name-prefix=${NAME_PREFIX}" >> $GITHUB_OUTPUT run: |
wget https://download.tuxfamily.org/godotengine/toolchains/linux/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
tar -xjf x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
mv x86_64-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
./relocate-sdk.sh
cd ..
- name: Setup Linux toolchain - name: Set up Linux buildroot x86_32
if: matrix.opts.platform == 'linux' if: matrix.opts.platform == 'linux' && matrix.opts.arch == 'x86_32'
uses: ./.github/actions/setup-linux-toolchain run: |
with: wget https://download.tuxfamily.org/godotengine/toolchains/linux/i686-godot-linux-gnu_sdk-buildroot.tar.bz2
arch: ${{matrix.opts.arch}} tar -xjf i686-godot-linux-gnu_sdk-buildroot.tar.bz2
mv i686-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
./relocate-sdk.sh
cd ..
- name: Set up Python 3.x - name: Set up Python 3.x
if: matrix.opts.platform == 'windows' || matrix.opts.platform == 'macos' if: matrix.opts.platform == 'windows' || matrix.opts.platform == 'macos'
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: "3.x" python-version: '3.x'
architecture: "x64" architecture: 'x64'
- name: Set up Emscripten cache
if: matrix.opts.platform == 'web'
uses: actions/cache@v4
with:
path: ${{env.EM_CACHE_FOLDER}}
key: ${{env.EM_VERSION}}-${{runner.os}}-libs
- name: Set up Emscripten
if: matrix.opts.platform == 'web'
uses: mymindstorm/setup-emsdk@v14
with:
version: ${{env.EM_VERSION}}
actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
- name: Verify Emscripten setup
if: matrix.opts.platform == 'web'
run: |
emcc -v
- name: Set up scons - name: Set up scons
if: matrix.opts.platform != 'linux' if: matrix.opts.platform == 'windows' || matrix.opts.platform == 'macos'
run: | run: |
python -c "import sys; print(sys.version)" python -c "import sys; print(sys.version)"
python -m pip install scons==4.4.0 python -m pip install scons==4.4.0
python --version python --version
scons --version scons --version
- name: Set up Java 17
if: matrix.opts.platform == 'android'
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- uses: nttld/setup-ndk@v1
if: matrix.opts.platform == 'android'
id: setup-ndk
with:
ndk-version: r23c
link-to-sdk: true
- name: Set up MSVC problem matcher on Windows - name: Set up MSVC problem matcher on Windows
if: matrix.opts.platform == 'windows' if: matrix.opts.platform == 'windows'
uses: ammaraskar/msvc-problem-matcher@master uses: ammaraskar/msvc-problem-matcher@master
- name: Set up scons cache - name: Set up scons cache
if: inputs.test-build # ! Only cache test/PR builds uses: actions/cache@v3
uses: actions/cache@v4
with: with:
path: ${{github.workspace}}/.scons_cache/ path: ${{github.workspace}}/.scons_cache/
key: ${{env.BIN}}-${{inputs.debug-symbols}}-${{inputs.godot-cpp-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} key: ${{env.BIN}}-${{inputs.godot-cpp-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
restore-keys: | restore-keys: |
${{env.BIN}}-${{inputs.debug-symbols}}-${{inputs.godot-cpp-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} ${{env.BIN}}-${{inputs.godot-cpp-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
${{env.BIN}}-${{inputs.debug-symbols}}-${{inputs.godot-cpp-ref}}-${{inputs.limboai-ref}} ${{env.BIN}}-${{inputs.godot-cpp-treeish}}-${{inputs.limboai-treeish}}
${{env.BIN}}-${{inputs.debug-symbols}}-${{inputs.godot-cpp-ref}} ${{env.BIN}}-${{inputs.godot-cpp-treeish}}
- name: Setup project structure for GDExtension
shell: bash
run: |
python ./limboai/gdextension/update_icons.py
mkdir -p demo/addons/limboai/bin
mv limboai/icons demo/addons/limboai/
cp limboai/gdextension/SConstruct SConstruct
cp limboai/gdextension/limboai.gdextension demo/addons/limboai/bin
echo "---"
ls -l
ls -l -R ./demo/
- name: Compilation - name: Compilation
shell: bash shell: bash
env: env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/ SCONS_CACHE: ${{github.workspace}}/.scons_cache/
DEBUG_FLAGS: ${{ inputs.debug-symbols && 'debug_symbols=yes symbols_visibility=visible' || 'debug_symbols=no' }}
run: | run: |
PATH=${GITHUB_WORKSPACE}/buildroot/bin:$PATH PATH=${GITHUB_WORKSPACE}/buildroot/bin:$PATH
scons platform=${{matrix.opts.platform}} target=${{matrix.opts.target}} arch=${{matrix.opts.arch}} ${{env.DEBUG_FLAGS}} ${{matrix.opts.scons-flags}} ${{env.SCONSFLAGS}} scons platform=${{matrix.opts.platform}} target=${{matrix.opts.target}} arch=${{matrix.opts.arch}} ${{env.SCONSFLAGS}}
- name: Prepare artifact - name: Prepare artifact
shell: bash shell: bash
run: | run: |
ls -R demo/addons/limboai/
mkdir out mkdir out
mv demo/addons/ out/ mv demo/addons/ out/
cp {README,LICENSE,LOGO_LICENSE}.md out/addons/limboai/
cp -R demo/demo/ out/demo/
cp demo/LICENSE_ASSETS.md out/demo/
rm -f out/addons/limboai/bin/*.{exp,lib,pdb} rm -f out/addons/limboai/bin/*.{exp,lib,pdb}
echo "${LIMBOAI_VERSION}" > out/addons/limboai/version.txt
echo "---"
ls -R out/ ls -R out/
- name: Strip lib
if: matrix.opts.platform != 'windows'
run: |
ls -l -R out/addons/limboai/bin/
echo "---"
if [ "$RUNNER_OS" == "macOS" ]; then
strip -u out/addons/limboai/bin/liblimboai*/liblimboai*
else
strip out/addons/limboai/bin/liblimboai*
fi
echo "---"
ls -l -R out/addons/limboai/bin/
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
env: env:
NAME: tmp-gdextension.${{matrix.opts.platform}}.${{matrix.opts.target}}.${{matrix.opts.arch}} NAME: ${{env.NAME_PREFIX}}
with: with:
name: ${{ env.NAME }} name: ${{ env.NAME }}
path: out/* path: out/*
package-extension:
name: 📦 Package extension
runs-on: ubuntu-latest
needs: gdextension
steps:
- name: Merge artifacts
uses: actions/upload-artifact/merge@v4
with:
name: ${{needs.gdextension.outputs.name-prefix}}
pattern: tmp-gdextension.*
delete-merged: true
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: ${{needs.gdextension.outputs.name-prefix}}
path: out/
- name: Setup Godot
shell: bash
run: |
echo "Downloading Godot ${GODOT_VERSION}"
mkdir bin
cd bin
wget "https://github.com/${GODOT_REPO}/releases/download/${GODOT_VERSION}/Godot_v${GODOT_VERSION}_linux.x86_64.zip" -O godot.zip
unzip godot.zip
rm godot.zip
mv Godot_* godot
chmod u+x godot
ls -l
cd ..
./bin/godot --version
- name: Generate icon .import files
shell: bash
run: |
touch out/project.godot
timeout 20s ./bin/godot --headless --editor --path ./out/ || /bin/true
rm out/project.godot
rm -rf out/.godot/
- name: Change editor icon import settings
shell: bash
run: |
echo "--- Listing icons dir:"
ls out/addons/limboai/icons/
echo "--- (end of listing)"
sed -i 's|editor/scale_with_editor_scale=false|editor/scale_with_editor_scale=true|' out/addons/limboai/icons/*.import
sed -i 's|editor/convert_colors_with_editor_theme=false|editor/convert_colors_with_editor_theme=true|' out/addons/limboai/icons/*.import
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{needs.gdextension.outputs.name-prefix}}
path: out/*
overwrite: true

View File

@ -2,34 +2,36 @@ name: 🍏 iOS builds
on: on:
workflow_call: workflow_call:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
workflow_dispatch: workflow_dispatch:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
# Global Settings # Global Settings
env: env:
SCONS_CACHE_LIMIT: 4096 SCONS_CACHE_LIMIT: 4096
SCONSFLAGS: production=yes tests=no verbose=yes warnings=extra SCONSFLAGS: production=yes tests=no verbose=yes warnings=extra werror=yes
DOTNET_NOLOGO: true DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs: jobs:
ios-builds: ios-builds:
runs-on: "macos-latest" runs-on: "macos-latest"
name: ${{ matrix.name }} name: ${{ matrix.name }}
@ -42,25 +44,21 @@ jobs:
target: template_release target: template_release
arch: arm64 arch: arm64
ios_simulator: false ios_simulator: false
dotnet: false
- name: Template (arm64, debug) - name: Template (arm64, debug)
target: template_debug target: template_debug
arch: arm64 arch: arm64
ios_simulator: false ios_simulator: false
dotnet: false
- name: Simulator (x86_64, release) - name: Simulator Lib (x86_64, release)
target: template_release target: template_release
arch: x86_64 arch: x86_64
ios_simulator: true ios_simulator: true
dotnet: false
- name: Simulator (x86_64, debug) - name: Simulator Lib (x86_64, debug)
target: template_debug target: template_debug
arch: x86_64 arch: x86_64
ios_simulator: true ios_simulator: true
dotnet: false
# ! Disabled for now as it doesn't work with cctools-port and current LLVM. # ! Disabled for now as it doesn't work with cctools-port and current LLVM.
# * See https://github.com/godotengine/build-containers/pull/85. # * See https://github.com/godotengine/build-containers/pull/85.
@ -74,54 +72,31 @@ jobs:
# arch: arm64 # arch: arm64
# ios_simulator: true # ios_simulator: true
- name: Template .NET (arm64, release)
target: template_release
arch: arm64
ios_simulator: false
dotnet: true
- name: Template .NET (arm64, debug)
target: template_debug
arch: arm64
ios_simulator: false
dotnet: true
- name: Simulator .NET (x86_64, release)
target: template_release
arch: x86_64
ios_simulator: true
dotnet: true
- name: Simulator .NET (x86_64, debug)
target: template_debug
arch: x86_64
ios_simulator: true
dotnet: true
env: env:
BIN: godot.ios.${{matrix.target}}.${{matrix.arch}}${{ matrix.dotnet == true && '.mono' || '' }} BIN: godot.ios.${{matrix.target}}.${{matrix.arch}}
steps: steps:
- name: Clone Godot - name: Clone Godot
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables. # Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version - uses: ./modules/limboai/.github/actions/init-version
- name: Set up Python 3.x - name: Set up Python 3.x
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: "3.x" python-version: '3.x'
architecture: "x64" architecture: 'x64'
- name: Set up scons - name: Set up scons
run: | run: |
@ -136,84 +111,60 @@ jobs:
- name: Set up Vulkan SDK - name: Set up Vulkan SDK
run: | run: |
# ! Note: Vulkan SDK changed packaging, so we need to inline these steps for the time being. sh misc/scripts/install_vulkan_sdk_macos.sh
#sh misc/scripts/install_vulkan_sdk_macos.sh
curl -L "https://sdk.lunarg.com/sdk/download/latest/mac/vulkan-sdk.zip" -o /tmp/vulkan-sdk.zip - name: Set up scons cache
unzip /tmp/vulkan-sdk.zip -d /tmp uses: actions/cache@v3
/tmp/InstallVulkan.app/Contents/MacOS/InstallVulkan --accept-licenses --default-answer --confirm-command install with:
rm -Rf /tmp/InstallVulkan.app path: ${{github.workspace}}/.scons_cache/
rm -f /tmp/vulkan-sdk.zip key: ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
restore-keys: |
# ! Note: we stopped using the scons cache in release builds. ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
# - name: Set up scons cache ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}
# uses: actions/cache@v4 ${{env.BIN}}-${{inputs.godot-treeish}}
# with:
# path: ${{github.workspace}}/.scons_cache/
# key: ${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}}
# restore-keys: |
# ${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}}
# ${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}
# ${{env.BIN}}-${{inputs.godot-ref}}
- name: Compilation - name: Compilation
env: env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/ SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: | run: |
scons platform=ios target=${{matrix.target}} arch=${{matrix.arch}} ios_simulator=${{matrix.ios_simulator}} module_mono_enabled=${{matrix.dotnet}} ${{env.SCONSFLAGS}} scons platform=ios target=${{matrix.target}} arch=${{matrix.arch}} ios_simulator=${{matrix.ios_simulator}} ${{env.SCONSFLAGS}}
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: tmp-ios${{matrix.dotnet == true && '-dotnet' || ''}}-templates-${{strategy.job-index}} name: ios-templates
path: bin/* path: bin/*
package-ios-templates: package-ios-templates:
runs-on: "macos-latest" runs-on: "macos-latest"
name: ${{ matrix.name }} name: Package iOS templates
needs: ios-builds needs: ios-builds
strategy:
fail-fast: false
matrix:
include:
- name: Package iOS templates
dotnet: false
- name: Package iOS .NET templates
dotnet: true
steps: steps:
- name: Clone Godot - name: Clone Godot
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables. # Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version - uses: ./modules/limboai/.github/actions/init-version
- name: Set up Vulkan SDK - name: Set up Vulkan SDK
run: | run: |
# ! Note: Vulkan SDK changed packaging, so we need to inline these steps for the time being. sh misc/scripts/install_vulkan_sdk_macos.sh
#sh misc/scripts/install_vulkan_sdk_macos.sh
curl -L "https://sdk.lunarg.com/sdk/download/latest/mac/vulkan-sdk.zip" -o /tmp/vulkan-sdk.zip
unzip /tmp/vulkan-sdk.zip -d /tmp
/tmp/InstallVulkan.app/Contents/MacOS/InstallVulkan --accept-licenses --default-answer --confirm-command install
rm -Rf /tmp/InstallVulkan.app
rm -f /tmp/vulkan-sdk.zip
- name: Download templates artifact - name: Download templates artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v3
with: with:
pattern: tmp-ios${{matrix.dotnet == true && '-dotnet' || ''}}-templates-* name: ios-templates
merge-multiple: true
path: bin/ path: bin/
- name: Make template bundle - name: Make template bundle
@ -222,6 +173,8 @@ jobs:
cp -r misc/dist/ios_xcode bin/ cp -r misc/dist/ios_xcode bin/
cd bin/ cd bin/
strip *.a
mv libgodot.ios.template_debug.arm64.a ios_xcode/libgodot.ios.debug.xcframework/ios-arm64/libgodot.a mv libgodot.ios.template_debug.arm64.a ios_xcode/libgodot.ios.debug.xcframework/ios-arm64/libgodot.a
# ! lipo -create libgodot.ios.template_debug.arm64.simulator.a libgodot.ios.template_debug.x86_64.simulator.a -output ios_xcode/libgodot.ios.debug.xcframework/ios-arm64_x86_64-simulator/libgodot.a # ! lipo -create libgodot.ios.template_debug.arm64.simulator.a libgodot.ios.template_debug.x86_64.simulator.a -output ios_xcode/libgodot.ios.debug.xcframework/ios-arm64_x86_64-simulator/libgodot.a
mv libgodot.ios.template_debug.x86_64.simulator.a ios_xcode/libgodot.ios.debug.xcframework/ios-arm64_x86_64-simulator/libgodot.a mv libgodot.ios.template_debug.x86_64.simulator.a ios_xcode/libgodot.ios.debug.xcframework/ios-arm64_x86_64-simulator/libgodot.a
@ -230,26 +183,25 @@ jobs:
# ! lipo -create libgodot.ios.template_release.arm64.simulator.a libgodot.ios.template_release.x86_64.simulator.a -output ios_xcode/libgodot.ios.release.xcframework/ios-arm64_x86_64-simulator/libgodot.a # ! lipo -create libgodot.ios.template_release.arm64.simulator.a libgodot.ios.template_release.x86_64.simulator.a -output ios_xcode/libgodot.ios.release.xcframework/ios-arm64_x86_64-simulator/libgodot.a
mv libgodot.ios.template_release.x86_64.simulator.a ios_xcode/libgodot.ios.release.xcframework/ios-arm64_x86_64-simulator/libgodot.a mv libgodot.ios.template_release.x86_64.simulator.a ios_xcode/libgodot.ios.release.xcframework/ios-arm64_x86_64-simulator/libgodot.a
cp -r ~/VulkanSDK/*/macOS/lib/MoltenVK.xcframework ios_xcode cp -r ~/VulkanSDK/*/MoltenVK/MoltenVK.xcframework ios_xcode
rm -rf ios_xcode/MoltenVK.xcframework/{macos,tvos}* rm -rf ios_xcode/MoltenVK.xcframework/{macos,tvos}*
mkdir -p ${{github.workspace}}/out/templates/ mkdir -p ${{github.workspace}}/out/templates/
cd ios_xcode cd ios_xcode
zip -q -9 -r ${{github.workspace}}/out/templates/ios.zip * zip -q -9 -r ${{github.workspace}}/out/templates/ios.zip *
echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}${{matrix.dotnet == true && '.mono' || ''}}" > ${{github.workspace}}/out/templates/version.txt echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}" > ${{github.workspace}}/out/templates/version.txt
ls -l ${{github.workspace}}/out/* ls -l ${{github.workspace}}/out/*
- name: Upload template bundle - name: Upload template bundle
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: ${{env.NAME_PREFIX}}${{matrix.dotnet == true && '.dotnet' || ''}}.export-templates.ios name: ${{env.NAME_PREFIX}}.export-templates
path: out/* path: out/*
- name: Delete templates artifact - name: Delete templates artifact
uses: geekyeggo/delete-artifact@v5 uses: geekyeggo/delete-artifact@v2
with: with:
name: tmp-ios${{matrix.dotnet == true && '-dotnet' || ''}}-templates-* name: ios-templates
useGlob: true
failOnError: false failOnError: false

View File

@ -2,31 +2,31 @@ name: 🐧 Linux builds
on: on:
workflow_call: workflow_call:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
test-build: test-build:
description: Limit to pre-defined test builds description: Should we perform only a limited number of test builds?
type: boolean type: boolean
default: false default: false
workflow_dispatch: workflow_dispatch:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
test-build: test-build:
description: Limit to pre-defined test builds description: Should we perform only a limited number of test builds?
type: boolean type: boolean
default: false default: false
@ -45,216 +45,158 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
opts: opts:
# * Standard x86_64
- name: Editor (x86_64, release) - name: Editor (x86_64, release)
target: editor target: editor
arch: x86_64 arch: x86_64
dotnet: false build-mono: false
should-build: true should-build: true
- name: Template (x86_64, release) - name: Template (x86_64, release)
target: template_release target: template_release
arch: x86_64 arch: x86_64
dotnet: false build-mono: false
should-build: true should-build: true
- name: Template (x86_64, debug) - name: Template (x86_64, debug)
target: template_debug target: template_debug
arch: x86_64 arch: x86_64
dotnet: false build-mono: false
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
# * Standard x86_32
# - name: Editor (x86_32, release) # - name: Editor (x86_32, release)
# target: editor # target: editor
# arch: x86_32 # arch: x86_32
# dotnet: false # build-mono: false
# should-build: ${{ !inputs.test-build }} # should-build: ${{ !inputs.test-build }}
- name: Template (x86_32, release) - name: Template (x86_32, release)
target: template_release target: template_release
arch: x86_32 arch: x86_32
dotnet: false build-mono: false
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Template (x86_32, debug) - name: Template (x86_32, debug)
target: template_debug target: template_debug
arch: x86_32 arch: x86_32
dotnet: false build-mono: false
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
# * Standard arm64
- name: Editor (arm64, release)
target: editor
arch: arm64
dotnet: false
should-build: ${{ !inputs.test-build }}
- name: Template (arm64, release)
target: template_release
arch: arm64
dotnet: false
should-build: ${{ !inputs.test-build }}
- name: Template (arm64, debug)
target: template_debug
arch: arm64
dotnet: false
should-build: ${{ !inputs.test-build }}
# * Standard arm32
- name: Template (arm32, release)
target: template_release
arch: arm32
dotnet: false
should-build: ${{ !inputs.test-build }}
- name: Template (arm32, debug)
target: template_debug
arch: arm32
dotnet: false
should-build: ${{ !inputs.test-build }}
# * .NET x86_64
- name: Editor .NET (x86_64, release) - name: Editor .NET (x86_64, release)
target: editor target: editor
arch: x86_64 arch: x86_64
dotnet: true build-mono: true
should-build: true should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_64, release) - name: Template .NET (x86_64, release)
target: template_release target: template_release
arch: x86_64 arch: x86_64
dotnet: true build-mono: true
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_64, debug) - name: Template .NET (x86_64, debug)
target: template_debug target: template_debug
arch: x86_64 arch: x86_64
dotnet: true build-mono: true
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
# * .NET x86_32
# - name: Editor .NET (x86_32, release) # - name: Editor .NET (x86_32, release)
# target: editor # target: editor
# arch: x86_32 # arch: x86_32
# dotnet: true # build-mono: true
# should-build: ${{ !inputs.test-build }} # should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_32, release) - name: Template .NET (x86_32, release)
target: template_release target: template_release
arch: x86_32 arch: x86_32
dotnet: true build-mono: true
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_32, debug) - name: Template .NET (x86_32, debug)
target: template_debug target: template_debug
arch: x86_32 arch: x86_32
dotnet: true build-mono: true
should-build: ${{ !inputs.test-build }}
# * .NET arm64
# ! FIXME: Needs a separate job for .NET glue generation since we can't execute arm64 binaries on x86_64.
# ! Alternatively, solution generation can be done as post-process job, taking the glue from x86_64, after all builds complete.
# - name: Editor .NET (arm64, release)
# target: editor
# arch: arm64
# dotnet: true
# should-build: ${{ !inputs.test-build }}
- name: Template .NET (arm64, release)
target: template_release
arch: arm64
dotnet: true
should-build: ${{ !inputs.test-build }}
- name: Template .NET (arm64, debug)
target: template_debug
arch: arm64
dotnet: true
should-build: ${{ !inputs.test-build }}
# * .NET arm32
- name: Template .NET (arm32, release)
target: template_release
arch: arm32
dotnet: true
should-build: ${{ !inputs.test-build }}
- name: Template .NET (arm32, debug)
target: template_debug
arch: arm32
dotnet: true
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
exclude: exclude:
- { opts: { should-build: false } } - { opts: {should-build: false }}
env: env:
BIN: godot.linuxbsd.${{matrix.opts.target}}.${{matrix.opts.arch}}${{ matrix.opts.dotnet == true && '.mono' || '' }} BIN: godot.linuxbsd.${{matrix.opts.target}}.${{matrix.opts.arch}}${{ matrix.opts.build-mono == true && '.mono' || '' }}
steps: steps:
- name: Clone Godot - name: Clone Godot
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables. # Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version - uses: ./modules/limboai/.github/actions/init-version
- name: Set up Linux toolchain # About sed see: https://github.com/godotengine/buildroot/issues/6
uses: ./modules/limboai/.github/actions/setup-linux-toolchain - name: Set up buildroot x86_64
with: if: matrix.opts.arch == 'x86_64'
arch: ${{matrix.opts.arch}}
- name: Set up Wayland deps
run: | run: |
sudo apt-get install libwayland-dev wget https://download.tuxfamily.org/godotengine/toolchains/linux/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
tar -xjf x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
mv x86_64-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
sed -i x86_64-godot-linux-gnu/sysroot/usr/lib/pkgconfig/dbus-1.pc -e "s@/lib@/lib64@g"
./relocate-sdk.sh
cd ..
- name: Set up buildroot x86_32
if: matrix.opts.arch == 'x86_32'
run: |
wget https://download.tuxfamily.org/godotengine/toolchains/linux/i686-godot-linux-gnu_sdk-buildroot.tar.bz2
tar -xjf i686-godot-linux-gnu_sdk-buildroot.tar.bz2
mv i686-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
./relocate-sdk.sh
cd ..
- name: Set up scons cache - name: Set up scons cache
if: inputs.test-build # ! Only cache test/PR builds uses: actions/cache@v3
uses: actions/cache@v4
with: with:
path: ${{github.workspace}}/.scons_cache/ path: ${{github.workspace}}/.scons_cache/
key: ${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} key: ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
restore-keys: | restore-keys: |
${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}} ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}
${{env.BIN}}-${{inputs.godot-ref}} ${{env.BIN}}-${{inputs.godot-treeish}}
- name: Compilation - name: Compilation
env: env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/ SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: | run: |
PATH=${GITHUB_WORKSPACE}/buildroot/bin:$PATH PATH=${GITHUB_WORKSPACE}/buildroot/bin:$PATH
scons platform=linuxbsd target=${{matrix.opts.target}} arch=${{matrix.opts.arch}} module_mono_enabled=${{matrix.opts.dotnet}} ${{env.SCONSFLAGS}} scons platform=linuxbsd target=${{matrix.opts.target}} arch=${{matrix.opts.arch}} module_mono_enabled=${{matrix.opts.build-mono}} ${{env.SCONSFLAGS}}
- name: Build .NET assemblies - name: Generate C# glue
if: matrix.opts.dotnet && matrix.opts.target == 'editor' if: matrix.opts.build-mono && matrix.opts.target == 'editor'
uses: ./modules/limboai/.github/actions/build-dotnet-assemblies env:
with: GODOT_VERSION_STATUS: limboai
platform: linuxbsd run: |
bin: ${{env.BIN}} ./bin/$BIN --headless --generate-mono-glue ./modules/mono/glue || true
- name: Build .NET solutions
if: matrix.opts.build-mono && matrix.opts.target == 'editor'
env:
GODOT_VERSION_STATUS: limboai
run: |
./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=linuxbsd
- name: Prepare artifact - name: Prepare artifact
env: env:
OUTDIR: ${{ startsWith(matrix.opts.target, 'template') && 'out/templates' || 'out/' }} OUTDIR: ${{ startsWith(matrix.opts.target, 'template') && 'out/templates' || 'out/' }}
run: | run: |
strip ./bin/godot.*
chmod +x ./bin/godot.* chmod +x ./bin/godot.*
mkdir -p ${{env.OUTDIR}} mkdir -p ${{env.OUTDIR}}
mv bin/* ${{env.OUTDIR}} mv bin/* ${{env.OUTDIR}}
@ -269,7 +211,6 @@ jobs:
pushd out/ pushd out/
zip -r godot-limboai.editor.linux.zip * zip -r godot-limboai.editor.linux.zip *
rm godot.* rm godot.*
rm -rf GodotSharp/
echo -e "## Why another ZIP inside?\n\nWorkaround for: https://github.com/actions/upload-artifact/issues/38\n" > README.md echo -e "## Why another ZIP inside?\n\nWorkaround for: https://github.com/actions/upload-artifact/issues/38\n" > README.md
popd popd
@ -279,11 +220,14 @@ jobs:
BUILD_TYPE: ${{ endsWith(matrix.opts.target, 'release') && 'release' || 'debug' }} BUILD_TYPE: ${{ endsWith(matrix.opts.target, 'release') && 'release' || 'debug' }}
run: | run: |
mv out/templates/${BIN} out/templates/linux_${BUILD_TYPE}.${{matrix.opts.arch}} mv out/templates/${BIN} out/templates/linux_${BUILD_TYPE}.${{matrix.opts.arch}}
echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}${{matrix.opts.dotnet == true && '.mono' || ''}}" > out/templates/version.txt echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}" > out/templates/version.txt
ls -R out/ ls -R out/
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
env:
NAME_EDITOR: ${{env.NAME_PREFIX}}.${{matrix.opts.target}}.linux.${{matrix.opts.arch}}${{ matrix.opts.build-mono == true && '.mono' || '' }}
NAME_TEMPLATES: ${{env.NAME_PREFIX}}.export-templates${{ matrix.opts.build-mono == true && '.mono' || '' }}
with: with:
name: ${{env.NAME_PREFIX}}${{matrix.opts.dotnet == true && '.dotnet' || ''}}.${{matrix.opts.target}}.linux.${{matrix.opts.arch}} name: ${{ startsWith(matrix.opts.target, 'template') && env.NAME_TEMPLATES || env.NAME_EDITOR }}
path: out/* path: out/*

View File

@ -2,38 +2,39 @@ name: 🍎 macOS builds
on: on:
workflow_call: workflow_call:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
test-build: test-build:
description: Limit to pre-defined test builds description: Should we perform only a limited number of test builds?
type: boolean type: boolean
default: false default: false
workflow_dispatch: workflow_dispatch:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
test-build: test-build:
description: Limit to pre-defined test builds description: Should we perform only a limited number of test builds?
type: boolean type: boolean
default: false default: false
# Global Settings # Global Settings
env: env:
SCONS_CACHE_LIMIT: 4096 SCONS_CACHE_LIMIT: 4096
SCONSFLAGS: production=yes tests=no verbose=yes warnings=extra SCONSFLAGS: production=yes tests=no verbose=yes warnings=extra werror=yes
DOTNET_NOLOGO: true DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true DOTNET_CLI_TELEMETRY_OPTOUT: true
@ -49,114 +50,125 @@ jobs:
- name: Editor (x86_64, release) - name: Editor (x86_64, release)
target: editor target: editor
arch: x86_64 arch: x86_64
dotnet: false build-mono: false
artifact-name: macos-editor
should-build: true should-build: true
- name: Template (x86_64, release) - name: Template (x86_64, release)
target: template_release target: template_release
arch: x86_64 arch: x86_64
dotnet: false build-mono: false
artifact-name: macos-templates
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Template (x86_64, debug) - name: Template (x86_64, debug)
target: template_debug target: template_debug
arch: x86_64 arch: x86_64
dotnet: false build-mono: false
artifact-name: macos-templates
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Editor (arm64, release) - name: Editor (arm64, release)
target: editor target: editor
arch: arm64 arch: arm64
dotnet: false build-mono: false
artifact-name: macos-editor
should-build: true should-build: true
- name: Template (arm64, release) - name: Template (arm64, release)
target: template_release target: template_release
arch: arm64 arch: arm64
dotnet: false build-mono: false
artifact-name: macos-templates
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Template (arm64, debug) - name: Template (arm64, debug)
target: template_debug target: template_debug
arch: arm64 arch: arm64
dotnet: false build-mono: false
artifact-name: macos-templates
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: .NET Editor (x86_64, release) # ! Disabled for now: .NET version fails to build
target: editor
arch: x86_64
dotnet: true
should-build: ${{ !inputs.test-build }}
- name: .NET Template (x86_64, release) # - name: .NET Editor (x86_64, release)
target: template_release # target: editor
arch: x86_64 # arch: x86_64
dotnet: true # build-mono: true
should-build: ${{ !inputs.test-build }} # artifact-name: macos-mono-editor
# cache-name: macos-editor
# should-build: ${{ !inputs.test-build }}
- name: .NET Template (x86_64, debug) # - name: .NET Template (x86_64, release)
target: template_debug # target: template_release
arch: x86_64 # arch: x86_64
dotnet: true # build-mono: true
should-build: ${{ !inputs.test-build }} # artifact-name: macos-mono-templates
# should-build: ${{ !inputs.test-build }}
- name: .NET Editor (arm64, release) # - name: .NET Template (x86_64, debug)
target: editor # target: template_debug
arch: arm64 # arch: x86_64
dotnet: true # build-mono: true
should-build: ${{ !inputs.test-build }} # artifact-name: macos-mono-templates
# should-build: ${{ !inputs.test-build }}
- name: .NET Template (arm64, release) # - name: .NET Editor (arm64, release)
target: template_release # target: editor
arch: arm64 # arch: arm64
dotnet: true # build-mono: true
should-build: ${{ !inputs.test-build }} # artifact-name: macos-mono-editor
# cache-name: macos-editor
# should-build: ${{ !inputs.test-build }}
- name: .NET Template (arm64, debug) # - name: .NET Template (arm64, release)
target: template_debug # target: template_release
arch: arm64 # arch: arm64
dotnet: true # build-mono: true
should-build: ${{ !inputs.test-build }} # artifact-name: macos-mono-templates
# should-build: ${{ !inputs.test-build }}
# - name: .NET Template (arm64, debug)
# target: template_debug
# arch: arm64
# build-mono: true
# artifact-name: macos-mono-templates
# should-build: ${{ !inputs.test-build }}
exclude: exclude:
- { opts: { should-build: false } } - { opts: {should-build: false }}
env: env:
BIN: godot.macos.${{matrix.opts.target}}.${{matrix.opts.arch}}${{ matrix.opts.dotnet == true && '.mono' || '' }} BIN: godot.macos.${{matrix.opts.target}}.${{matrix.opts.arch}}${{ matrix.opts.build-mono == true && '.mono' || '' }}
steps: steps:
- name: Clone Godot - name: Clone Godot
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables. # Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version - uses: ./modules/limboai/.github/actions/init-version
- name: Set up Python 3.x - name: Set up Python 3.x
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: "3.x" python-version: '3.x'
architecture: "x64" architecture: 'x64'
- name: Set up scons - name: Set up scons
run: | run: |
python -c "import sys; print(sys.version)" python -c "import sys; print(sys.version)"
python -m pip install scons==4.4.0 python -m pip install scons==4.4.0
- name: Set up .NET SDK 6.0
if: matrix.opts.dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: "6.0.x"
- name: Diagnostics - name: Diagnostics
run: | run: |
python --version python --version
@ -165,49 +177,45 @@ jobs:
- name: Set up Vulkan SDK - name: Set up Vulkan SDK
run: | run: |
# ! Note: Vulkan SDK changed packaging, so we need to inline these steps for the time being. sh misc/scripts/install_vulkan_sdk_macos.sh
#sh misc/scripts/install_vulkan_sdk_macos.sh
curl -L "https://sdk.lunarg.com/sdk/download/latest/mac/config.json" -o /tmp/vulkan-sdk.json
sdk_version=`jq -r '.version' /tmp/vulkan-sdk.json`
curl -L "https://sdk.lunarg.com/sdk/download/latest/mac/vulkan-sdk.zip" -o /tmp/vulkan-sdk.zip
unzip /tmp/vulkan-sdk.zip -d /tmp
/tmp/InstallVulkan-${sdk_version}.app/Contents/MacOS/InstallVulkan-${sdk_version} --accept-licenses --default-answer --confirm-command install
rm -Rf /tmp/InstallVulkan-${sdk_version}.app
rm -f /tmp/vulkan-sdk.zip
- name: Set up scons cache - name: Set up scons cache
if: inputs.test-build # ! Only cache test/PR builds uses: actions/cache@v3
uses: actions/cache@v4
with: with:
path: ${{github.workspace}}/.scons_cache/ path: ${{github.workspace}}/.scons_cache/
key: ${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} key: ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
restore-keys: | restore-keys: |
${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}} ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}
${{env.BIN}}-${{inputs.godot-ref}} ${{env.BIN}}-${{inputs.godot-treeish}}
- name: Compilation - name: Compilation
env: env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/ SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: | run: |
scons -j2 platform=macos target=${{matrix.opts.target}} arch=${{matrix.opts.arch}} module_mono_enabled=${{matrix.opts.dotnet}} ${{env.SCONSFLAGS}} scons -j2 platform=macos target=${{matrix.opts.target}} arch=${{matrix.opts.arch}} module_mono_enabled=${{matrix.opts.build-mono}} ${{env.SCONSFLAGS}}
- name: Build .NET assemblies # ! Disabled for now: .NET version fail to build
if: matrix.opts.dotnet && matrix.opts.target == 'editor'
uses: ./modules/limboai/.github/actions/build-dotnet-assemblies # - name: Generate C# glue
with: # if: matrix.opts.build-mono && matrix.opts.target == 'editor'
platform: macos # run: |
bin: ${{env.BIN}} # ./bin/$BIN --headless --generate-mono-glue ./modules/mono/glue || true
# - name: Build .NET solutions
# if: matrix.opts.build-mono && matrix.opts.target == 'editor'
# run: |
# ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=macos
- name: Prepare artifact - name: Prepare artifact
run: | run: |
strip bin/godot.*
chmod +x bin/godot.* chmod +x bin/godot.*
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: tmp-${{matrix.opts.dotnet == true && 'dotnet-' || ''}}macos-${{matrix.opts.target}}-${{matrix.opts.arch}} name: ${{ matrix.opts.artifact-name }}
path: bin/* path: bin/*
make-macos-bundle: make-macos-bundle:
@ -215,76 +223,60 @@ jobs:
name: Make macOS Bundles name: Make macOS Bundles
needs: macos-builds needs: macos-builds
strategy:
fail-fast: false
matrix:
opts:
- download-prefix: tmp-macos
dotnet: false
should-build: true
- download-prefix: tmp-dotnet-macos
dotnet: true
should-build: ${{ !inputs.test-build }}
exclude:
- { opts: { should-build: false } }
steps: steps:
- name: Clone Godot - name: Clone Godot
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables. # Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version - uses: ./modules/limboai/.github/actions/init-version
- name: Download editor artifact - name: Download editor artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v3
with: with:
pattern: ${{matrix.opts.download-prefix}}-editor-* name: macos-editor
merge-multiple: true
path: bin/ path: bin/
# Zipping the editor bundle to retain executable bit; # Zipping the editor bundle to retain executable bit;
# workaround for: https://github.com/actions/upload-artifact/issues/38 # workaround for: https://github.com/actions/upload-artifact/issues/38
- name: Make editor bundle - name: Make editor bundle
env:
APP_NAME: Godot${{matrix.opts.dotnet == true && '_mono' || ''}}.app
run: | run: |
ls bin/ ls bin/
lipo -create bin/godot.macos.editor.x86_64* bin/godot.macos.editor.arm64* -output bin/godot.macos.editor.universal lipo -create bin/godot.macos.editor.x86_64 bin/godot.macos.editor.arm64 -output bin/godot.macos.editor.universal
strip bin/godot.macos.editor.universal
rm bin/godot.macos.editor.{x86_64,arm64}
mkdir -p out/editor/ mkdir -p out/editor/
cp -r misc/dist/macos_tools.app out/editor/${APP_NAME} cp -r misc/dist/macos_tools.app out/editor/Godot.app
mkdir -p out/editor/${APP_NAME}/Contents/{MacOS,Resources} mkdir -p out/editor/Godot.app/Contents/MacOS
cp bin/godot.macos.editor.universal out/editor/${APP_NAME}/Contents/MacOS/Godot cp bin/godot.macos.editor.universal out/editor/Godot.app/Contents/MacOS/Godot
chmod +x out/editor/${APP_NAME}/Contents/MacOS/Godot chmod +x out/editor/Godot.app/Contents/MacOS/Godot
cp -r bin/GodotSharp out/editor/${APP_NAME}/Contents/Resources/GodotSharp || true
pushd out/editor pushd out/editor
zip -q -9 -r ${APP_NAME}.zip ${APP_NAME} zip -r Godot.app.zip Godot.app
rm -rf ${APP_NAME} rm -rf Godot.app
echo -e "## Why another ZIP inside?\n\nWorkaround for: https://github.com/actions/upload-artifact/issues/38\n" > README.md echo -e "## Why another ZIP inside?\n\nWorkaround for: https://github.com/actions/upload-artifact/issues/38\n" > README.md
popd popd
rm -rf bin/*
ls out/editor/ ls out/editor/
- name: Upload editor bundle - name: Upload editor bundle
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: ${{env.NAME_PREFIX}}${{matrix.opts.dotnet == true && '.dotnet' || ''}}.editor.macos.universal name: ${{env.NAME_PREFIX}}.editor.macos.universal
path: out/editor/* path: out/editor/*
- name: Download templates artifact - name: Download templates artifact
if: ${{ !inputs.test-build }} if: ${{ !inputs.test-build }}
uses: actions/download-artifact@v4 uses: actions/download-artifact@v3
with: with:
pattern: ${{matrix.opts.download-prefix}}-template_* name: macos-templates
merge-multiple: true
path: bin/ path: bin/
- name: Make templates bundle - name: Make templates bundle
@ -292,29 +284,31 @@ jobs:
run: | run: |
rm -rf out/ rm -rf out/
ls bin/ ls bin/
lipo -create bin/godot.macos.template_release.x86_64* bin/godot.macos.template_release.arm64* -output bin/godot.macos.template_release.universal lipo -create bin/godot.macos.template_release.x86_64 bin/godot.macos.template_release.arm64 -output bin/godot.macos.template_release.universal
lipo -create bin/godot.macos.template_debug.x86_64* bin/godot.macos.template_debug.arm64* -output bin/godot.macos.template_debug.universal lipo -create bin/godot.macos.template_debug.x86_64 bin/godot.macos.template_debug.arm64 -output bin/godot.macos.template_debug.universal
rm bin/godot.macos.template_{debug,release}.{x86_64,arm64}
strip bin/godot.*
cp -r misc/dist/macos_template.app macos_template.app cp -r misc/dist/macos_template.app macos_template.app
mkdir -p macos_template.app/Contents/MacOS mkdir -p macos_template.app/Contents/MacOS
cp bin/godot.macos.template_debug.universal macos_template.app/Contents/MacOS/godot_macos_debug.universal cp bin/godot.macos.template_debug.universal macos_template.app/Contents/MacOS/godot_macos_debug.universal
cp bin/godot.macos.template_release.universal macos_template.app/Contents/MacOS/godot_macos_release.universal cp bin/godot.macos.template_release.universal macos_template.app/Contents/MacOS/godot_macos_release.universal
chmod +x macos_template.app/Contents/MacOS/godot_macos_{release,debug}.universal chmod +x macos_template.app/Contents/MacOS/godot_macos_{release,debug}.universal
zip -q -9 -r macos.zip macos_template.app zip -r macos.zip macos_template.app
rm bin/*
mkdir -p out/templates/ mkdir -p out/templates/
mv macos.zip out/templates/macos.zip mv macos.zip out/templates/macos.zip
echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}${{matrix.opts.dotnet == true && '.mono' || ''}}" > out/templates/version.txt echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}" > out/templates/version.txt
rm -rf bin/*
ls out/templates/ ls out/templates/
- uses: geekyeggo/delete-artifact@v5 - uses: geekyeggo/delete-artifact@v2
with: with:
name: ${{matrix.opts.download-prefix}}-* name: macos-*
useGlob: true useGlob: true
failOnError: false failOnError: false
- name: Upload templates bundle - name: Upload templates bundle
if: ${{ !inputs.test-build }} if: ${{ !inputs.test-build }}
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: ${{env.NAME_PREFIX}}${{matrix.opts.dotnet == true && '.dotnet' || ''}}.export-templates.macos name: ${{env.NAME_PREFIX}}.export-templates
path: out/* path: out/*

View File

@ -1,52 +0,0 @@
name: 📦️ Merge templates
on:
workflow_call:
inputs:
godot-ref:
description: A tag, branch or commit hash in the Godot repository.
type: string
limboai-ref:
description: A tag, branch or commit hash in the LimboAI repository.
type: string
workflow_dispatch:
inputs:
godot-ref:
description: A tag, branch or commit hash in the Godot repository.
type: string
limboai-ref:
description: A tag, branch or commit hash in the LimboAI repository.
type: string
jobs:
merge-templates:
runs-on: ubuntu-latest
steps:
- name: Clone Godot
uses: actions/checkout@v4
with:
repository: godotengine/godot
ref: ${{ inputs.godot-ref }}
- name: Clone LimboAI module
uses: actions/checkout@v4
with:
path: modules/limboai
ref: ${{ inputs.limboai-ref }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version
- name: Merge classical templates
uses: actions/upload-artifact/merge@v4
with:
name: ${{env.NAME_PREFIX}}.export-templates
pattern: ${{env.NAME_PREFIX}}.{export-templates,template_}*
delete-merged: true
- name: Merge .NET templates
uses: actions/upload-artifact/merge@v4
with:
name: ${{env.NAME_PREFIX}}.dotnet.export-templates
pattern: ${{env.NAME_PREFIX}}.dotnet.{export-templates,template_}*
delete-merged: true

View File

@ -3,22 +3,22 @@ on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: [master] branches: [ master ]
paths-ignore: paths-ignore:
- "README.md" - "README.md"
- "LICENSE.md" - "LICENSE"
- "**/*.png" - "**/*.png"
- "demo/**" - "demo/*"
- "doc/**" - "doc/*"
pull_request: pull_request:
branches: [master] branches: [ master ]
paths-ignore: paths-ignore:
- "README.md" - "README.md"
- "LICENSE.md" - "LICENSE"
- "**/*.png" - "**/*.png"
- "demo/**" - "demo/*"
- "doc/**" - "doc/*"
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@ -26,8 +26,8 @@ concurrency:
# Global Settings. # Global Settings.
env: env:
GODOT_REF: "master" GODOT_REF: "4.2.1-stable"
GODOT_CPP_REF: "master" GODOT_CPP_REF: "4.2"
jobs: jobs:
unit-tests: unit-tests:
@ -36,7 +36,7 @@ jobs:
# Settings # Settings
env: env:
SCONSFLAGS: platform=linuxbsd target=editor arch=x86_64 production=false dev_build=true tests=true verbose=yes warnings=extra werror=yes strict_checks=yes SCONSFLAGS: platform=linuxbsd target=editor arch=x86_64 production=false dev_build=true tests=true verbose=yes warnings=extra werror=yes
SCONS_CACHE_LIMIT: 7168 SCONS_CACHE_LIMIT: 7168
BIN: godot.linuxbsd.editor.dev.x86_64 BIN: godot.linuxbsd.editor.dev.x86_64
@ -52,13 +52,19 @@ jobs:
with: with:
path: modules/limboai path: modules/limboai
- name: Set up Linux toolchain # About sed see: https://github.com/godotengine/buildroot/issues/6
uses: ./modules/limboai/.github/actions/setup-linux-toolchain - name: Set up buildroot x86_64
with: run: |
arch: x86_64 wget https://download.tuxfamily.org/godotengine/toolchains/linux/x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
tar -xjf x86_64-godot-linux-gnu_sdk-buildroot.tar.bz2
mv x86_64-godot-linux-gnu_sdk-buildroot buildroot
cd buildroot
sed -i x86_64-godot-linux-gnu/sysroot/usr/lib/pkgconfig/dbus-1.pc -e "s@/lib@/lib64@g"
./relocate-sdk.sh
cd ..
- name: Set up scons cache - name: Set up scons cache
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: ${{github.workspace}}/.scons_cache/ path: ${{github.workspace}}/.scons_cache/
key: ${{env.BIN}}-${{env.GODOT_REF}}-${{github.ref}}-${{github.sha}} key: ${{env.BIN}}-${{env.GODOT_REF}}-${{github.ref}}-${{github.sha}}
@ -69,10 +75,10 @@ jobs:
continue-on-error: true continue-on-error: true
- name: Set up Python 3.x - name: Set up Python 3.x
uses: actions/setup-python@v5 uses: actions/setup-python@v4
with: with:
python-version: "3.x" python-version: '3.x'
architecture: "x64" architecture: 'x64'
- name: Set up scons - name: Set up scons
run: | run: |
@ -101,18 +107,6 @@ jobs:
run: | run: |
bin/${{ env.BIN }} --test --headless bin/${{ env.BIN }} --test --headless
static-checks:
name: ⚙️ Static checks
runs-on: ubuntu-20.04
steps:
- name: Clone LimboAI module
uses: actions/checkout@v4
- name: Code style checks
uses: pre-commit/action@v3.0.1
with:
extra_args: --all-files
cache-env: cache-env:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
@ -130,8 +124,8 @@ jobs:
needs: cache-env needs: cache-env
uses: ./.github/workflows/linux.yml uses: ./.github/workflows/linux.yml
with: with:
godot-ref: ${{ needs.cache-env.outputs.godot-ref }} godot-treeish: ${{ needs.cache-env.outputs.godot-ref }}
limboai-ref: ${{ github.sha }} limboai-treeish: ${{ github.sha }}
test-build: true test-build: true
windows-test-build: windows-test-build:
@ -139,8 +133,8 @@ jobs:
needs: cache-env needs: cache-env
uses: ./.github/workflows/windows.yml uses: ./.github/workflows/windows.yml
with: with:
godot-ref: ${{ needs.cache-env.outputs.godot-ref }} godot-treeish: ${{ needs.cache-env.outputs.godot-ref }}
limboai-ref: ${{ github.sha }} limboai-treeish: ${{ github.sha }}
test-build: true test-build: true
macos-test-build: macos-test-build:
@ -148,8 +142,8 @@ jobs:
needs: cache-env needs: cache-env
uses: ./.github/workflows/macos.yml uses: ./.github/workflows/macos.yml
with: with:
godot-ref: ${{ needs.cache-env.outputs.godot-ref }} godot-treeish: ${{ needs.cache-env.outputs.godot-ref }}
limboai-ref: ${{ github.sha }} limboai-treeish: ${{ github.sha }}
test-build: true test-build: true
gdextension: gdextension:
@ -157,6 +151,6 @@ jobs:
needs: cache-env needs: cache-env
uses: ./.github/workflows/gdextension.yml uses: ./.github/workflows/gdextension.yml
with: with:
godot-cpp-ref: ${{ needs.cache-env.outputs.godot-cpp-ref }} godot-cpp-treeish: ${{ needs.cache-env.outputs.godot-cpp-ref }}
limboai-ref: ${{ github.sha }} limboai-treeish: ${{ github.sha }}
test-build: true test-build: true

View File

@ -2,22 +2,22 @@ name: 🌐 Web builds
on: on:
workflow_call: workflow_call:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
workflow_dispatch: workflow_dispatch:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
@ -31,80 +31,68 @@ env:
EM_CACHE_FOLDER: "emsdk-cache" EM_CACHE_FOLDER: "emsdk-cache"
jobs: jobs:
# Workaround for issue: https://github.com/mymindstorm/setup-emsdk/issues/20
init-emscripten:
name: Init Emscripten
runs-on: "ubuntu-20.04"
steps:
- name: Set up Emscripten
uses: mymindstorm/setup-emsdk@v12
with:
version: ${{env.EM_VERSION}}
actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
web-builds: web-builds:
runs-on: "ubuntu-20.04" runs-on: "ubuntu-20.04"
name: ${{ matrix.name }} name: ${{ matrix.name }}
needs: init-emscripten
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- name: Template (release) - name: Template (release)
target: template_release target: template_release
threads: true dlink_enabled: false
dlink: false
- name: Template (release, dlink) - name: Template (release, dlink_enabled=true)
target: template_release target: template_release
threads: true dlink_enabled: true
dlink: true
- name: Template (debug) - name: Template (debug)
target: template_debug target: template_debug
threads: true dlink_enabled: false
dlink: false
- name: Template (debug, dlink) - name: Template (debug, dlink_enabled=true)
target: template_debug target: template_debug
threads: true dlink_enabled: true
dlink: true
- name: Template (release, nothreads)
target: template_release
threads: false
dlink: false
- name: Template (release, nothreads, dlink)
target: template_release
threads: false
dlink: true
- name: Template (debug, nothreads)
target: template_debug
threads: false
dlink: false
- name: Template (debug, nothreads, dlink)
target: template_debug
threads: false
dlink: true
env: env:
CACHE_NAME: godot.web.${{matrix.target}}${{ matrix.dlink == true && '.dlink' || '' }} CACHE_NAME: godot.web.${{matrix.target}}${{ matrix.dlink_enabled == true && '.dlink' || '' }}
steps: steps:
- name: Clone Godot - name: Clone Godot
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables. # Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version - uses: ./modules/limboai/.github/actions/init-version
- name: Set up Emscripten cache # - name: Set up Emscripten cache
uses: actions/cache@v4 # uses: actions/cache@v3
with: # with:
path: ${{env.EM_CACHE_FOLDER}} # path: ${{env.EM_CACHE_FOLDER}}
key: ${{env.EM_VERSION}}-${{runner.os}}-libs # key: ${{env.EM_VERSION}}-${{runner.os}}-libs
- name: Set up Emscripten - name: Set up Emscripten
uses: mymindstorm/setup-emsdk@v14 uses: mymindstorm/setup-emsdk@v12
with: with:
version: ${{env.EM_VERSION}} version: ${{env.EM_VERSION}}
actions-cache-folder: ${{env.EM_CACHE_FOLDER}} actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
@ -120,39 +108,34 @@ jobs:
python --version python --version
scons --version scons --version
# ! Note: we stopped using the scons cache in release builds. - name: Set up scons cache
# - name: Set up scons cache uses: actions/cache@v3
# uses: actions/cache@v4 with:
# with: path: ${{github.workspace}}/.scons_cache/
# path: ${{github.workspace}}/.scons_cache/ key: ${{env.CACHE_NAME}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
# key: ${{env.CACHE_NAME}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} restore-keys: |
# restore-keys: | ${{env.CACHE_NAME}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
# ${{env.CACHE_NAME}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} ${{env.CACHE_NAME}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}
# ${{env.CACHE_NAME}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}} ${{env.CACHE_NAME}}-${{inputs.godot-treeish}}
# ${{env.CACHE_NAME}}-${{inputs.godot-ref}}
- name: Compilation - name: Compilation
env: env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/ SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: | run: |
scons platform=web target=${{matrix.target}} threads=${{matrix.threads}} dlink_enabled=${{matrix.dlink}} ${{env.SCONSFLAGS}} scons platform=web target=${{matrix.target}} dlink_enabled=${{matrix.dlink_enabled}} ${{env.SCONSFLAGS}}
- name: Prepare artifacts - name: Prepare artifacts
run: | run: |
mkdir -p out/templates/ mkdir -p out/templates/
mv bin/godot.web.template_debug.wasm32.zip out/templates/web_debug.zip || true
mv bin/godot.web.template_debug.wasm32.nothreads.zip out/templates/web_nothreads_debug.zip || true
mv bin/godot.web.template_debug.wasm32.dlink.zip out/templates/web_dlink_debug.zip || true
mv bin/godot.web.template_debug.wasm32.nothreads.dlink.zip out/templates/web_dlink_nothreads_debug.zip || true
mv bin/godot.web.template_release.wasm32.zip out/templates/web_release.zip || true mv bin/godot.web.template_release.wasm32.zip out/templates/web_release.zip || true
mv bin/godot.web.template_release.wasm32.nothreads.zip out/templates/web_nothreads_release.zip || true
mv bin/godot.web.template_release.wasm32.dlink.zip out/templates/web_dlink_release.zip || true mv bin/godot.web.template_release.wasm32.dlink.zip out/templates/web_dlink_release.zip || true
mv bin/godot.web.template_release.wasm32.nothreads.dlink.zip out/templates/web_dlink_nothreads_release.zip || true mv bin/godot.web.template_debug.wasm32.zip out/templates/web_debug.zip || true
mv bin/godot.web.template_debug.wasm32.dlink.zip out/templates/web_dlink_debug.zip || true
rm -rf bin/ rm -rf bin/
echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}" > out/templates/version.txt echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}" > out/templates/version.txt
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: ${{env.NAME_PREFIX}}.${{matrix.target}}.web${{matrix.threads == false && '.nothreads' || ''}}${{matrix.dlink == true && '.dlink' || ''}} name: ${{env.NAME_PREFIX}}.export-templates
path: out/* path: out/*

View File

@ -2,274 +2,189 @@ name: 🪟 Windows builds
on: on:
workflow_call: workflow_call:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
test-build: test-build:
description: Limit to pre-defined test builds description: Should we perform only a limited number of test builds?
type: boolean type: boolean
default: false default: false
workflow_dispatch: workflow_dispatch:
inputs: inputs:
godot-ref: godot-treeish:
description: A tag, branch or commit hash in the Godot repository. description: A tag, branch or commit hash in the Godot repository.
type: string type: string
default: master default: master
limboai-ref: limboai-treeish:
description: A tag, branch or commit hash in the LimboAI repository. description: A tag, branch or commit hash in the LimboAI repository.
type: string type: string
default: master default: master
test-build: test-build:
description: Limit to pre-defined test builds description: Should we perform only a limited number of test builds?
type: boolean type: boolean
default: false default: false
# Global Settings # Global Settings
env: env:
BUILD_IMAGE_VERSION: 4.3-f40 SCONS_CACHE_MSVC_CONFIG: true
MESA_VERSION: 23.1.9-1
SCONS_CACHE_LIMIT: 4096 SCONS_CACHE_LIMIT: 4096
SCONSFLAGS: production=yes use_mingw=yes verbose=yes warnings=no progress=no d3d12=yes SCONSFLAGS: production=yes tests=no verbose=yes warnings=extra werror=yes
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs: jobs:
windows-builds: windows-builds:
runs-on: "ubuntu-24.04" runs-on: "windows-latest"
name: ${{ matrix.opts.name }} name: ${{ matrix.opts.name }}
outputs:
built-dotnet-editor: ${{ steps.mark-dotnet-editor.outputs.built-dotnet-editor }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
opts: opts:
# * Standard x86_64
- name: Editor (x86_64, release) - name: Editor (x86_64, release)
target: editor target: editor
arch: x86_64 arch: x86_64
llvm: false build-mono: false
dotnet: false
should-build: true should-build: true
- name: Template (x86_64, release) - name: Template (x86_64, release)
target: template_release target: template_release
arch: x86_64 arch: x86_64
llvm: false build-mono: false
dotnet: false
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Template (x86_64, debug) - name: Template (x86_64, debug)
target: template_debug target: template_debug
arch: x86_64 arch: x86_64
llvm: false build-mono: false
dotnet: false
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
# * Standard x86_32
# - name: Editor (x86_32, release) # - name: Editor (x86_32, release)
# target: editor # target: editor
# arch: x86_32 # arch: x86_32
# llvm: false # build-mono: false
# dotnet: false
# should-build: ${{ !inputs.test-build }} # should-build: ${{ !inputs.test-build }}
- name: Template (x86_32, release) - name: Template (x86_32, release)
target: template_release target: template_release
arch: x86_32 arch: x86_32
llvm: false build-mono: false
dotnet: false
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Template (x86_32, debug) - name: Template (x86_32, debug)
target: template_debug target: template_debug
arch: x86_32 arch: x86_32
llvm: false build-mono: false
dotnet: false
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
# * Standard arm64
# - name: Editor (arm64, release)
# target: editor
# arch: arm64
# llvm: true
# dotnet: false
# should-build: ${{ !inputs.test-build }}
- name: Template (arm64, release)
target: template_release
arch: arm64
llvm: true
dotnet: false
scons-flags: mingw_prefix=/root/llvm-mingw
should-build: ${{ !inputs.test-build }}
- name: Template (arm64, debug)
target: template_debug
arch: arm64
llvm: true
dotnet: false
scons-flags: mingw_prefix=/root/llvm-mingw
should-build: ${{ !inputs.test-build }}
# * .NET x86_64
- name: Editor .NET (x86_64, release) - name: Editor .NET (x86_64, release)
target: editor target: editor
arch: x86_64 arch: x86_64
llvm: false build-mono: true
dotnet: true should-build: true
should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_64, release) - name: Template .NET (x86_64, release)
target: template_release target: template_release
arch: x86_64 arch: x86_64
llvm: false build-mono: true
dotnet: true
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_64, debug) - name: Template .NET (x86_64, debug)
target: template_debug target: template_debug
arch: x86_64 arch: x86_64
llvm: false build-mono: true
dotnet: true
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
# * .NET x86_32
# - name: Editor .NET (x86_32, release) # - name: Editor .NET (x86_32, release)
# target: editor # target: editor
# arch: x86_32 # arch: x86_32
# llvm: false # build-mono: true
# dotnet: true
# should-build: ${{ !inputs.test-build }} # should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_32, release) - name: Template .NET (x86_32, release)
target: template_release target: template_release
arch: x86_32 arch: x86_32
llvm: false build-mono: true
dotnet: true
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_32, debug) - name: Template .NET (x86_32, debug)
target: template_debug target: template_debug
arch: x86_32 arch: x86_32
llvm: false build-mono: true
dotnet: true
should-build: ${{ !inputs.test-build }}
# * .NET arm64
# - name: Editor .NET (arm64, release)
# target: editor
# arch: arm64
# llvm: true
# dotnet: true
# should-build: ${{ !inputs.test-build }}
- name: Template .NET (arm64, release)
target: template_release
arch: arm64
llvm: true
dotnet: true
scons-flags: mingw_prefix=/root/llvm-mingw
should-build: ${{ !inputs.test-build }}
- name: Template .NET (arm64, debug)
target: template_debug
arch: arm64
llvm: true
dotnet: true
scons-flags: mingw_prefix=/root/llvm-mingw
should-build: ${{ !inputs.test-build }} should-build: ${{ !inputs.test-build }}
exclude: exclude:
- { opts: { should-build: false } } - { opts: {should-build: false }}
env: env:
BIN: godot.windows.${{matrix.opts.target}}.${{matrix.opts.arch}}${{matrix.opts.llvm && '.llvm' || ''}}${{matrix.opts.dotnet == true && '.mono' || ''}} BIN: godot.windows.${{matrix.opts.target}}.${{matrix.opts.arch}}${{ matrix.opts.build-mono == true && '.mono' || '' }}
steps: steps:
- name: Clone Godot - name: Clone Godot
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
repository: godotengine/godot repository: godotengine/godot
ref: ${{ inputs.godot-ref }} ref: ${{ inputs.godot-treeish }}
- name: Clone LimboAI module - name: Clone LimboAI module
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
path: modules/limboai path: modules/limboai
ref: ${{ inputs.limboai-ref }} ref: ${{ inputs.limboai-treeish }}
# Inits GODOT_VERSION, LIMBOAI_VERSION, NAME_PREFIX, GODOT_VERSION_STATUS, BUILD_NAME environment variables. # Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version - uses: ./modules/limboai/.github/actions/init-version
- name: Set up Python 3.x
uses: actions/setup-python@v4
with:
python-version: '3.x'
architecture: 'x64'
- name: Set up scons
run: |
python -c "import sys; print(sys.version)"
python -m pip install scons==4.4.0
python --version
scons --version
- name: Set up MSVC problem matcher
uses: ammaraskar/msvc-problem-matcher@master
- name: Set up scons cache - name: Set up scons cache
if: inputs.test-build # ! Only cache test/PR builds uses: actions/cache@v3
uses: actions/cache@v4
with: with:
path: ${{github.workspace}}/.scons_cache/ path: ${{github.workspace}}/.scons_cache/
key: ${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} key: ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
restore-keys: | restore-keys: |
${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}} ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}-${{env.LIMBOAI_VERSION}}
${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}} ${{env.BIN}}-${{inputs.godot-treeish}}-${{inputs.limboai-treeish}}
${{env.BIN}}-${{inputs.godot-ref}} ${{env.BIN}}-${{inputs.godot-treeish}}
- name: Static ANGLE libs - name: Compilation
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: | run: |
mkdir -p deps/angle scons -j2 platform=windows target=${{matrix.opts.target}} arch=${{matrix.opts.arch}} module_mono_enabled=${{matrix.opts.build-mono}} ${{env.SCONSFLAGS}}
cd deps/angle
url=https://github.com/godotengine/godot-angle-static/releases/download/chromium%2F6601.2/godot-angle-static
curl -L -o windows_${{matrix.opts.arch}}.zip $url-${{matrix.opts.arch}}-${{matrix.opts.llvm && 'llvm' || 'gcc'}}-release.zip
unzip windows_${{matrix.opts.arch}}.zip
rm windows_${{matrix.opts.arch}}.zip
- name: Mesa libs - name: Generate C# glue
if: matrix.opts.build-mono && matrix.opts.target == 'editor'
env:
GODOT_VERSION_STATUS: limboai
run: | run: |
mkdir -p deps/mesa ./bin/${{ env.BIN }} --headless --generate-mono-glue ./modules/mono/glue || true
cd deps/mesa
curl -L -o mesa_${{matrix.opts.arch}}.zip https://github.com/godotengine/godot-nir-static/releases/download/${{env.MESA_VERSION}}/godot-nir-static-${{matrix.opts.arch}}-${{matrix.opts.llvm && 'llvm' || 'gcc'}}-release.zip
unzip -o mesa_${{matrix.opts.arch}}.zip
rm -f mesa_${{matrix.opts.arch}}.zip
- name: Pull build container - name: Build .NET solutions
if: matrix.opts.build-mono && matrix.opts.target == 'editor'
env:
GODOT_VERSION_STATUS: limboai
run: | run: |
podman pull ghcr.io/limbonaut/godot-windows:${{env.BUILD_IMAGE_VERSION}} python ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=windows
- name: Build using container
shell: bash
run: |
scons_command="scons \
platform=windows \
target=${{matrix.opts.target}} \
arch=${{matrix.opts.arch}} \
use_llvm=${{matrix.opts.llvm}} \
module_mono_enabled=${{matrix.opts.dotnet}} \
${{env.SCONSFLAGS}} \
${{matrix.opts.scons-flags}} \
angle_libs=/build/deps/angle \
mesa_libs=/build/deps/mesa \
"
podman_run="podman run -it --rm \
--env GODOT_VERSION_STATUS=${GODOT_VERSION_STATUS} \
--env BUILD_NAME=${BUILD_NAME} \
--env NUM_CORES=$(nproc --all) \
--env DOTNET_NOLOGO=true \
--env DOTNET_CLI_TELEMETRY_OPTOUT=true \
--env SCONS_CACHE=/build/.scons_cache/ \
--env SCONS_CACHE_LIMIT=${SCONS_CACHE_LIMIT} \
-v ${GITHUB_WORKSPACE}/:/build/ \
-w /build godot-windows:${{env.BUILD_IMAGE_VERSION}} bash -c \
"
echo "Running ${podman_run} \"${scons_command}\""
${podman_run} "${scons_command}"
- name: Prepare artifact - name: Prepare artifact
shell: bash shell: bash
@ -289,70 +204,14 @@ jobs:
run: | run: |
mv out/templates/${BIN}.exe out/templates/windows_${BUILD_TYPE}_${{matrix.opts.arch}}.exe mv out/templates/${BIN}.exe out/templates/windows_${BUILD_TYPE}_${{matrix.opts.arch}}.exe
mv out/templates/${BIN}.console.exe out/templates/windows_${BUILD_TYPE}_${{matrix.opts.arch}}_console.exe mv out/templates/${BIN}.console.exe out/templates/windows_${BUILD_TYPE}_${{matrix.opts.arch}}_console.exe
echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}${{matrix.opts.dotnet == true && '.mono' || ''}}" > out/templates/version.txt echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}" > out/templates/version.txt
ls -R out/ ls -R out/
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with:
name: ${{env.NAME_PREFIX}}${{matrix.opts.dotnet == true && '.dotnet' || ''}}.${{matrix.opts.target}}.windows.${{matrix.opts.arch}}
path: out/*
- name: Mark .NET editor as built
if: matrix.opts.dotnet && matrix.opts.target == 'editor'
id: mark-dotnet-editor
run: echo "built-dotnet-editor=true" >> $GITHUB_OUTPUT
dotnet-assemblies:
name: .NET assembly for ${{matrix.opts.arch}}
needs: windows-builds
if: always() && needs.windows-builds.outputs.built-dotnet-editor == 'true'
runs-on: "windows-latest"
strategy:
fail-fast: false
matrix:
opts:
- arch: x86_64
llvm: false
env: env:
BIN: godot.windows.editor.${{matrix.opts.arch}}${{matrix.opts.llvm && '.llvm' || ''}}.mono NAME_EDITOR: ${{env.NAME_PREFIX}}.${{matrix.opts.target}}.windows.${{matrix.opts.arch}}${{ matrix.opts.build-mono == true && '.mono' || '' }}
steps: NAME_TEMPLATES: ${{env.NAME_PREFIX}}.export-templates${{ matrix.opts.build-mono == true && '.mono' || '' }}
- name: Clone Godot
uses: actions/checkout@v4
with: with:
repository: godotengine/godot name: ${{ startsWith(matrix.opts.target, 'template') && env.NAME_TEMPLATES || env.NAME_EDITOR }}
ref: ${{ inputs.godot-ref }} path: out/*
- name: Clone LimboAI module
uses: actions/checkout@v4
with:
path: modules/limboai
ref: ${{ inputs.limboai-ref }}
# Inits GODOT_VERSION, LIMBOAI_VERSION and NAME_PREFIX environment variables.
- uses: ./modules/limboai/.github/actions/init-version
- name: Reconstruct artifact name
shell: bash
run: |
echo "ARTIFACT=${{env.NAME_PREFIX}}.dotnet.editor.windows.${{matrix.opts.arch}}" >> $GITHUB_ENV
- name: Download editor artifact
uses: actions/download-artifact@v4
with:
name: ${{env.ARTIFACT}}
path: bin/
- name: Build .NET assemblies
uses: ./modules/limboai/.github/actions/build-dotnet-assemblies
with:
platform: windows
bin: ${{env.BIN}}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{env.ARTIFACT}}
overwrite: true
path: bin/*

5
.gitignore vendored
View File

@ -1,8 +1,6 @@
# LimboAI-specific # LimboAI-specific
demo/addons/ demo/addons/
demo/script_templates/
icons/*.import icons/*.import
godot-cpp
# Godot auto generated files # Godot auto generated files
*.gen.* *.gen.*
@ -187,6 +185,3 @@ godot.creator.*
# compile commands (https://clang.llvm.org/docs/JSONCompilationDatabase.html) # compile commands (https://clang.llvm.org/docs/JSONCompilationDatabase.html)
compile_commands.json compile_commands.json
# clang cache
.cache

View File

@ -1,24 +0,0 @@
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v17.0.6
hooks:
- id: clang-format
files: \.(c|h|cpp|hpp|cc|cxx|m|mm|inc|java|glsl)$
types_or: [text]
exclude: |
(?x)^(
tests/python_build.*|
.*thirdparty.*|
.*platform/android/java/lib/src/com.*|
.*-so_wrap.*
)
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.2.0
hooks:
- id: black
files: (\.py$|SConstruct|SCsub)
types_or: [text]
exclude: .*thirdparty.*
args:
- --line-length=120

View File

@ -1,4 +1,4 @@
Copyright (c) 2023-2025 Serhii Snitsaruk and the LimboAI contributors. Copyright 2021-2023 Serhii Snitsaruk
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

119
README.md
View File

@ -1,48 +1,29 @@
# LimboAI - Behavior Trees and State Machines for Godot 4
<p align="center"> <p align="center">
<img src="doc/images/logo.svg" width="400" alt="LimboAI logo"> <img src="doc/images/logo.svg" width="400" alt="LimboAI logo">
</p> </p>
# LimboAI - Behavior Trees & State Machines for Godot 4
[![🔗 All builds](https://github.com/limbonaut/limboai/actions/workflows/all_builds.yml/badge.svg)](https://github.com/limbonaut/limboai/actions/workflows/all_builds.yml) [![🔗 All builds](https://github.com/limbonaut/limboai/actions/workflows/all_builds.yml/badge.svg)](https://github.com/limbonaut/limboai/actions/workflows/all_builds.yml)
[![🔎 Unit Tests](https://github.com/limbonaut/limboai/actions/workflows/test_builds.yml/badge.svg)](https://github.com/limbonaut/limboai/actions/workflows/test_builds.yml) [![🔎 Unit Tests](https://github.com/limbonaut/limboai/actions/workflows/test_builds.yml/badge.svg)](https://github.com/limbonaut/limboai/actions/workflows/test_builds.yml)
[![Documentation Status](https://readthedocs.org/projects/limboai/badge/?version=latest)](https://limboai.readthedocs.io/en/latest/?badge=latest) [![Documentation Status](https://readthedocs.org/projects/limboai/badge/?version=latest)](https://limboai.readthedocs.io/en/latest/?badge=latest)
[![GitHub License](https://img.shields.io/github/license/limbonaut/limboai)](https://github.com/limbonaut/limboai/blob/master/LICENSE.md) ![GitHub License](https://img.shields.io/github/license/limbonaut/limboai)
[![Discord](https://img.shields.io/discord/1185664967379267774?logo=discord&link=https%3A%2F%2Fdiscord.gg%2FN5MGC95GpP)](https://discord.gg/N5MGC95GpP) ![Discord](https://img.shields.io/discord/1185664967379267774?logo=discord&link=https%3A%2F%2Fdiscord.gg%2FN5MGC95GpP)
[![Mastodon Follow](https://img.shields.io/mastodon/follow/109346796150895359?domain=https%3A%2F%2Fmastodon.gamedev.place)](https://mastodon.gamedev.place/@limbo) ![Mastodon Follow](https://img.shields.io/mastodon/follow/109346796150895359?domain=https%3A%2F%2Fmastodon.gamedev.place)
>**🛈 Supported Godot Engine:** **4.3** (v1.2.0+) | **4.2** (v1.1.x releases) **LimboAI** is an open-source C++ module for **Godot Engine 4** providing a combination of
**LimboAI** is an open-source C++ plugin for **Godot Engine 4** providing a combination of
**Behavior Trees** and **State Machines**, which can be used together to create complex AI behaviors. **Behavior Trees** and **State Machines**, which can be used together to create complex AI behaviors.
It comes with a behavior tree editor, built-in documentation, visual debugger, extensive demo project with a tutorial, and more! It comes with a behavior tree editor, built-in documentation, visual debugger, and more!
While it is implemented in C++, it fully supports GDScript for [creating your own tasks](https://limboai.readthedocs.io/en/stable/behavior-trees/custom-tasks.html) and [states](https://limboai.readthedocs.io/en/stable/hierarchical-state-machines/create-hsm.html). While it is implemented in C++, it fully supports GDScript for [creating your own tasks](https://limboai.readthedocs.io/en/latest/getting-started/custom-tasks.html) and states.
If you enjoy using LimboAI, please **consider supporting** my efforts with a donation on Ko-fi 😊 Your contribution will help me continue developing and improving it. >**🛈 Supported Godot Engine: 4.2**
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Y8Y2TCNH0) >**🛈 License**: Use of this source code is governed by an MIT-style license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
Behavior Trees are powerful hierarchical structures used to model and control the behavior of agents in a game (e.g., characters, enemies, entities). They are designed to make it easier to create complex and highly modular behaviors for your games. To learn more about behavior trees, check out [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/latest/getting-started/introduction.html).
![Textured screenshot](doc/images/behavior-tree-editor-debugger.png) ![Textured screenshot](doc/images/behavior-tree-editor-debugger.png)
Behavior Trees are powerful hierarchical structures used to model and control the behavior of agents in a game (e.g., characters, enemies). They are designed to make it easier to create rich and highly modular behaviors for your games. To learn more about behavior trees, check out [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/stable/behavior-trees/introduction.html) and our demo project, which includes a tutorial.
## Demonstration
![Charger from Demo](doc/images/demo_charger.gif)
>**🛈 Demo project** lives in the `demo` folder and is available separately in [**Releases**](https://github.com/limbonaut/limboai/releases).
> Run `demo/scenes/showcase.tscn` to get started.
> It also includes a tutorial that introduces behavior trees through illustrative examples.
### Videos
> **🛈** YouTube videos produced by various creators
<a href="https://www.youtube.com/watch?v=NWaMArUg7mY"><img src="https://img.youtube.com/vi/NWaMArUg7mY/0.jpg" width=410></a>
<a href="https://www.youtube.com/watch?v=aP0Aacdxmno"><img src="https://img.youtube.com/vi/aP0Aacdxmno/0.jpg" width=410></a>
<a href="https://www.youtube.com/watch?v=vZHzMO90IwQ"><img src="https://img.youtube.com/vi/vZHzMO90IwQ/0.jpg" width=410></a>
<a href="https://www.youtube.com/watch?v=gAk3xl5fBsM"><img src="https://img.youtube.com/vi/gAk3xl5fBsM/0.jpg" width=410></a>
## Features ## Features
- **Behavior Trees (BT):** - **Behavior Trees (BT):**
@ -50,41 +31,30 @@ Behavior Trees are powerful hierarchical structures used to model and control th
- Execute `BehaviorTree` resources using the `BTPlayer` node. - Execute `BehaviorTree` resources using the `BTPlayer` node.
- Create complex behaviors by combining and nesting tasks in a hierarchy. - Create complex behaviors by combining and nesting tasks in a hierarchy.
- Control execution flow using composite, decorator, and condition tasks. - Control execution flow using composite, decorator, and condition tasks.
- [Create custom tasks](https://limboai.readthedocs.io/en/stable/behavior-trees/custom-tasks.html) by extending core classes: `BTAction`, `BTCondition`, `BTDecorator`, and `BTComposite`. - [Create custom tasks](https://limboai.readthedocs.io/en/latest/getting-started/custom-tasks.html) by extending core classes: `BTAction`, `BTCondition`, `BTDecorator`, and `BTComposite`.
- Built-in class documentation. - Built-in class documentation. Check out the `BehaviorTree` class documentation to get started.
- Blackboard system: Share data seamlessly between tasks using the `Blackboard`. - Share data seamlessly between tasks using the `Blackboard`.
- Blackboard plans: Define variables in the BehaviorTree resource and override their values in the BTPlayer node. - Blackboard scopes isolate variable namespaces and enable advanced techniques like sharing data between agents in a group.
- Plan editor: Manage variables, their data types and property hints.
- Blackboard scopes: Prevent name conflicts and enable advanced techniques like [sharing data between several agents](https://limboai.readthedocs.io/en/stable/behavior-trees/using-blackboard.html#sharing-data-between-several-agents).
- Blackboard parameters: [Export a BB parameter](https://limboai.readthedocs.io/en/stable/behavior-trees/using-blackboard.html#task-parameters), for which user can provide a value or bind it to a blackboard variable (can be used in custom tasks).
- Inspector support for specifying blackboard variables (custom editor for exported `StringName` properties ending with "_var").
- Use the `BTSubtree` task to execute a tree from a different resource file, promoting organization and reusability. - Use the `BTSubtree` task to execute a tree from a different resource file, promoting organization and reusability.
- Visual Debugger: Inspect the execution of any BT in a running scene to identify and troubleshoot issues. - Visual Debugger: Inspect the execution of any BT in a running scene to identify and troubleshoot issues.
- Visualize BT in-game using `BehaviorTreeView` node (for custom in-game tools).
- Monitor tree performance with custom performance monitors. - Monitor tree performance with custom performance monitors.
- **Hierarchical State Machines (HSM):** - **Hierarchical State Machines (HSM):**
- Extend the `LimboState` class to implement state logic. - Extend the `LimboState` class to implement state logic.
- `LimboHSM` node serves as a state machine that manages `LimboState` instances and transitions. - The `LimboHSM` node serves as a state machine that manages `LimboState` instances and transitions.
- `LimboHSM` is a state itself and can be nested within other `LimboHSM` instances. - `LimboHSM` is a state itself and can be nested within other `LimboHSM` instances.
- [Event-based](https://limboai.readthedocs.io/en/stable/hierarchical-state-machines/create-hsm.html#events-and-transitions): Transitions are associated with events and are triggered by the state machine when the relevant event is dispatched, allowing for better decoupling of transitions from state logic. - Event-based: Transitions are associated with events and are triggered by the state machine when the relevant event is dispatched, allowing for better decoupling of transitions from state logic.
- Combine state machines with behavior trees using `BTState` for advanced reactive AI. - Combine state machines with behavior trees using `BTState` for advanced reactive AI.
- Delegation Option: Using the vanilla `LimboState`, [delegate the implementation](https://limboai.readthedocs.io/en/stable/hierarchical-state-machines/create-hsm.html#single-file-state-machine-setup) to your callback functions, making it perfect for rapid prototyping and game jams. - Delegation Option: Using the vanilla `LimboState`, delegate the implementation to your callback functions, making it perfect for rapid prototyping and game jams.
- 🛈 Note: State machine setup and initialization require code; there is no GUI editor. - 🛈 Note: State machine setup and initialization require code; there is no GUI editor.
- **Tested:** Behavior tree tasks and HSM are covered by unit tests. - **Tested:** Behavior tree tasks and HSM are covered by unit tests.
- **GDExtension:** LimboAI can be [used as extension](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html#get-gdextension-version). Custom engine builds are not necessary. - **GDExtension:** LimboAI can be [used as extension](https://limboai.readthedocs.io/en/latest/getting-started/gdextension.html). Custom engine builds are not necessary.
- **Demo + Tutorial:** Check out our extensive demo project, which includes an introduction to behavior trees using examples.
## First steps
Follow the [Getting started](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html) guide to learn how to get started with LimboAI and the demo project.
## Getting LimboAI ## Getting LimboAI
LimboAI can be used as either a C++ module or as a GDExtension shared library. GDExtension version is more convenient to use but somewhat limited in features. Whichever you choose to use, your project will stay compatible with both and you can switch from one to the other any time. See [Using GDExtension](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html#get-gdextension-version). LimboAI can be used as either a C++ module or as a GDExtension shared library. GDExtension version is more convenient to use but somewhat limited in features. Whichever you choose to use, your project will stay compatible with both and you can switch from one to the other any time. See [Using GDExtension](https://limboai.readthedocs.io/en/latest/getting-started/gdextension.html).
### Precompiled builds ### Precompiled builds
@ -98,42 +68,35 @@ LimboAI can be used as either a C++ module or as a GDExtension shared library. G
- If you plan to export a game utilizing the LimboAI module, you'll also need to build export templates. - If you plan to export a game utilizing the LimboAI module, you'll also need to build export templates.
- To execute unit tests, compile the engine with `tests=yes` and run it with `--test --tc="*[LimboAI]*"`. - To execute unit tests, compile the engine with `tests=yes` and run it with `--test --tc="*[LimboAI]*"`.
#### For GDExtension
- You'll need SCons build tool and a C++ compiler. See also [Compiling](https://docs.godotengine.org/en/stable/contributing/development/compiling/index.html).
- Run `scons target=editor` to build the plugin library for your current platform.
- SCons will automatically clone the godot-cpp/ repository if it doesn't already exist in the `limboai/godot-cpp` directory.
- By default, built targets are placed in the demo project: `demo/addons/limboai/bin/`
- Check `scons -h` for other options and targets.
## Using the plugin ## Using the plugin
- Online Documentation: [stable](https://limboai.readthedocs.io/en/stable/index.html), [latest](https://limboai.readthedocs.io/en/latest/index.html) - [Online Documentation](https://limboai.readthedocs.io/en/latest/index.html)
- [Getting started](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html) - [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/latest/getting-started/introduction.html)
- [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/stable/behavior-trees/introduction.html) - [Creating custom tasks in GDScript](https://limboai.readthedocs.io/en/latest/getting-started/custom-tasks.html)
- [Creating custom tasks in GDScript](https://limboai.readthedocs.io/en/stable/behavior-trees/custom-tasks.html) - [Using GDExtension](https://limboai.readthedocs.io/en/latest/getting-started/gdextension.html)
- [Sharing data using Blackboard](https://limboai.readthedocs.io/en/stable/behavior-trees/using-blackboard.html) - [Class reference](https://limboai.readthedocs.io/en/latest/getting-started/featured-classes.html)
- [Accessing nodes in the scene tree](https://limboai.readthedocs.io/en/stable/behavior-trees/accessing-nodes.html)
- [State machines](https://limboai.readthedocs.io/en/stable/hierarchical-state-machines/create-hsm.html)
- [Using GDExtension](https://limboai.readthedocs.io/en/stable/getting-started/getting-limboai.html#get-gdextension-version)
- [Using LimboAI with C#](https://limboai.readthedocs.io/en/stable/getting-started/c-sharp.html)
- [Class reference](https://limboai.readthedocs.io/en/stable/classes/featured-classes.html)
## Contributing ## Contributing
Contributions are welcome! Please open issues for bug reports, feature requests, or code changes. All contributions are welcome! Feel free to open issues with bug reports and feature requests, and submit PRs.
For detailed guidelines on contributing to code or documentation, check out our [Contributing](https://limboai.readthedocs.io/en/latest/getting-started/contributing.html) page.
If you have an idea for a behavior tree task or a feature that could be useful in a variety of projects, open an issue to discuss it. Got an idea for a behavior tree task that you think would be useful on a variety of projects?
Feel free to open an issue and describe your concept.
## Social ## Social
Need help? We have a Discord server: https://discord.gg/N5MGC95GpP We have a fresh new Discord server: https://discord.gg/N5MGC95GpP
I write about LimboAI development on Mastodon: https://mastodon.gamedev.place/@limbo. I also write about LimboAI development on the Mastodon: https://mastodon.gamedev.place/@limbo.
## License ## Roadmap
Use of this source code is governed by an MIT-style license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT Features and improvements that may be implemented in the future:
- ~~Providing precompiled builds for download.~~ 🗸
LimboAI logo and demo project art assets are licensed under the Creative Commons Attribution 4.0 International license that can be found at https://creativecommons.org/licenses/by/4.0/ - ~~Tests and CI.~~ 🗸
- Expanding the library of commonly useful tasks.
- Creating a non-trivial demo project to showcase the capabilities of LimboAI.
- Exploring the execution history of behavior trees in the visual debugger.
- Per-project ignore list for tasks that users may want to hide in the task palette.
- GUI editor for state machines.
~~- Supporting GDExtension in the future, once it matures.~~ 🗸

View File

@ -1,160 +0,0 @@
#!/usr/bin/env python
"""
This is SConstruct file for building GDExtension variant using SCons build system.
For module variant, see SCsub file.
Use --project=DIR to customize output path for built targets.
- Built targets are placed into "DIR/addons/limboai/bin".
- For example: scons --project="../my_project"
- built targets will be placed into "../my_project/addons/limboai/bin".
- If not specified, built targets are put into the demo/ project.
"""
import os
import sys
import subprocess
from limboai_version import generate_module_version_header, godot_cpp_ref
sys.path.append("gdextension")
from update_icon_entries import update_icon_entries
from fix_icon_imports import fix_icon_imports
# Check if godot-cpp/ exists
if not os.path.exists("godot-cpp"):
print("Directory godot-cpp/ not found. Cloning repository...")
result = subprocess.run(
["git", "clone", "-b", godot_cpp_ref, "https://github.com/godotengine/godot-cpp.git"],
check=True,
# capture_output=True
)
if result.returncode != 0:
print("Error: Cloning godot-cpp repository failed.")
Exit(1)
print("Finished cloning godot-cpp repository.")
AddOption(
"--project",
dest="project",
type="string",
nargs=1,
action="store",
metavar="DIR",
default="demo",
help="Specify project directory",
)
help_text = """
Options:
--project=DIR Specify project directory (default: "demo");
built targets will be placed in DIR/addons/limboai/bin
"""
Help(help_text)
project_dir = GetOption("project")
if not os.path.isdir(project_dir):
print("Project directory not found: " + project_dir)
Exit(2)
# Parse LimboAI-specific variables.
vars = Variables()
vars.AddVariables(
BoolVariable("deploy_manifest", help="Deploy limboai.gdextension into PROJECT/addons/limboai/bin", default=True),
BoolVariable("deploy_icons", help="Deploy icons into PROJECT/addons/limboai/icons", default=True),
)
env = Environment(tools=["default"], PLATFORM="", variables=vars)
Help(vars.GenerateHelpText(env))
# Read LimboAI-specific variables.
deploy_manifest = env["deploy_manifest"]
deploy_icons = env["deploy_icons"]
# Remove processed variables from ARGUMENTS to avoid godot-cpp warnings.
for o in vars.options:
if o.key in ARGUMENTS:
del ARGUMENTS[o.key]
# For reference:
# - CCFLAGS are compilation flags shared between C and C++
# - CFLAGS are for C-specific compilation flags
# - CXXFLAGS are for C++-specific compilation flags
# - CPPFLAGS are for pre-processor flags
# - CPPDEFINES are for pre-processor defines
# - LINKFLAGS are for linking flags
env = SConscript("godot-cpp/SConstruct")
# Generate version header.
print("Generating LimboAI version header...")
generate_module_version_header()
# Update icon entries in limboai.gdextension file.
# Note: This will remove everything after [icons] section, and rebuild it with generated icon entries.
print("Updating LimboAI icon entries...")
update_icon_entries(silent=True)
# Fix icon imports in the PROJECT/addons/limboai/icons/.
# Enables scaling and color conversion in the editor for imported SVG icons.
try:
fix_icon_imports(project_dir)
except FileNotFoundError as e:
print(e)
except Exception as e:
print("Unknown error: " + str(e))
# Tweak this if you want to use different folders, or more folders, to store your source code in.
env.Append(CPPDEFINES=["LIMBOAI_GDEXTENSION"])
sources = Glob("*.cpp")
sources += Glob("blackboard/*.cpp")
sources += Glob("blackboard/bb_param/*.cpp")
sources += Glob("bt/*.cpp")
sources += Glob("bt/tasks/*.cpp")
sources += Glob("bt/tasks/blackboard/*.cpp")
sources += Glob("bt/tasks/composites/*.cpp")
sources += Glob("bt/tasks/decorators/*.cpp")
sources += Glob("bt/tasks/scene/*.cpp")
sources += Glob("bt/tasks/utility/*.cpp")
sources += Glob("gdextension/*.cpp")
sources += Glob("editor/debugger/*.cpp")
sources += Glob("editor/*.cpp")
sources += Glob("hsm/*.cpp")
sources += Glob("util/*.cpp")
# Generate documentation header.
if env["target"] in ["editor", "template_debug"]:
doc_data = env.GodotCPPDocData("gen/doc_data.gen.cpp", source=Glob("doc_classes/*.xml"))
sources.append(doc_data)
# Build library.
if env["platform"] == "macos":
library = env.SharedLibrary(
project_dir
+ "/addons/limboai/bin/liblimboai.{}.{}.framework/liblimboai.{}.{}".format(
env["platform"], env["target"], env["platform"], env["target"]
),
source=sources,
)
else:
library = env.SharedLibrary(
project_dir + "/addons/limboai/bin/liblimboai{}{}".format(env["suffix"], env["SHLIBSUFFIX"]),
source=sources,
)
Default(library)
# Deploy icons into PROJECT/addons/limboai/icons.
if deploy_icons:
cmd_deploy_icons = env.Command(
project_dir + "/addons/limboai/icons/",
"icons/",
Copy("$TARGET", "$SOURCE"),
)
Default(cmd_deploy_icons)
# Deploy limboai.gdextension into PROJECT/addons/limboai/bin.
if deploy_manifest:
cmd_deploy_manifest = env.Command(
project_dir + "/addons/limboai/bin/limboai.gdextension",
"gdextension/limboai.gdextension",
Copy("$TARGET", "$SOURCE"),
)
Default(cmd_deploy_manifest)

6
SCsub
View File

@ -5,11 +5,7 @@ Import("env_modules")
module_env = env.Clone() module_env = env.Clone()
module_env.Append(CPPDEFINES=["LIMBOAI_MODULE"]) module_env.Append(CPPDEFINES = ['LIMBOAI_MODULE'])
import limboai_version
limboai_version.generate_module_version_header()
module_env.add_source_files(env.modules_sources, "*.cpp") module_env.add_source_files(env.modules_sources, "*.cpp")
module_env.add_source_files(env.modules_sources, "blackboard/*.cpp") module_env.add_source_files(env.modules_sources, "blackboard/*.cpp")

View File

@ -1,7 +1,7 @@
/** /**
* bb_aabb.h * bb_aabb.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBAabb : public BBParam {
GDCLASS(BBAabb, BBParam); GDCLASS(BBAabb, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::AABB; } virtual Variant::Type get_type() const override { return Variant::AABB; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_array.h * bb_array.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBArray : public BBParam {
GDCLASS(BBArray, BBParam); GDCLASS(BBArray, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::ARRAY; } virtual Variant::Type get_type() const override { return Variant::ARRAY; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_basis.h * bb_basis.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBBasis : public BBParam {
GDCLASS(BBBasis, BBParam); GDCLASS(BBBasis, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::BASIS; } virtual Variant::Type get_type() const override { return Variant::BASIS; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_bool.h * bb_bool.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBBool : public BBParam {
GDCLASS(BBBool, BBParam); GDCLASS(BBBool, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::BOOL; } virtual Variant::Type get_type() const override { return Variant::BOOL; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_byte_array.h * bb_byte_array.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBByteArray : public BBParam {
GDCLASS(BBByteArray, BBParam); GDCLASS(BBByteArray, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_BYTE_ARRAY; } virtual Variant::Type get_type() const override { return Variant::PACKED_BYTE_ARRAY; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_color.h * bb_color.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBColor : public BBParam {
GDCLASS(BBColor, BBParam); GDCLASS(BBColor, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::COLOR; } virtual Variant::Type get_type() const override { return Variant::COLOR; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_color_array.h * bb_color_array.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBColorArray : public BBParam {
GDCLASS(BBColorArray, BBParam); GDCLASS(BBColorArray, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_COLOR_ARRAY; } virtual Variant::Type get_type() const override { return Variant::PACKED_COLOR_ARRAY; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_dictionary.h * bb_dictionary.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBDictionary : public BBParam {
GDCLASS(BBDictionary, BBParam); GDCLASS(BBDictionary, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::DICTIONARY; } virtual Variant::Type get_type() const override { return Variant::DICTIONARY; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_float.h * bb_float.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBFloat : public BBParam {
GDCLASS(BBFloat, BBParam); GDCLASS(BBFloat, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::FLOAT; } virtual Variant::Type get_type() const override { return Variant::FLOAT; }
}; };

View File

@ -1,26 +0,0 @@
/**
* bb_float32_array.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#ifndef BB_FLOAT32_ARRAY_H
#define BB_FLOAT32_ARRAY_H
#include "bb_param.h"
class BBFloat32Array : public BBParam {
GDCLASS(BBFloat32Array, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_FLOAT32_ARRAY; }
};
#endif // BB_FLOAT32_ARRAY_H

View File

@ -1,7 +1,7 @@
/** /**
* bb_float64_array.h * bb_float_array.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -9,18 +9,16 @@
* ============================================================================= * =============================================================================
*/ */
#ifndef BB_FLOAT64_ARRAY_H #ifndef BB_FLOAT_ARRAY_H
#define BB_FLOAT64_ARRAY_H #define BB_FLOAT_ARRAY_H
#include "bb_param.h" #include "bb_param.h"
class BBFloat64Array : public BBParam { class BBFloatArray : public BBParam {
GDCLASS(BBFloat64Array, BBParam); GDCLASS(BBFloatArray, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_FLOAT64_ARRAY; } virtual Variant::Type get_type() const override { return Variant::PACKED_FLOAT64_ARRAY; }
}; };
#endif // BB_FLOAT64_ARRAY_H #endif // BB_FLOAT_ARRAY_H

View File

@ -1,7 +1,7 @@
/** /**
* bb_int.h * bb_int.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBInt : public BBParam {
GDCLASS(BBInt, BBParam); GDCLASS(BBInt, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::INT; } virtual Variant::Type get_type() const override { return Variant::INT; }
}; };

View File

@ -1,26 +0,0 @@
/**
* bb_int32_array.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#ifndef BB_INT32_ARRAY_H
#define BB_INT32_ARRAY_H
#include "bb_param.h"
class BBInt32Array : public BBParam {
GDCLASS(BBInt32Array, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_INT32_ARRAY; }
};
#endif // BB_INT32_ARRAY_H

View File

@ -1,7 +1,7 @@
/** /**
* bb_int64_array.h * bb_int_array.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -9,18 +9,16 @@
* ============================================================================= * =============================================================================
*/ */
#ifndef BB_INT64_ARRAY_H #ifndef BB_INT_ARRAY_H
#define BB_INT64_ARRAY_H #define BB_INT_ARRAY_H
#include "bb_param.h" #include "bb_param.h"
class BBInt64Array : public BBParam { class BBIntArray : public BBParam {
GDCLASS(BBInt64Array, BBParam); GDCLASS(BBIntArray, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_INT64_ARRAY; } virtual Variant::Type get_type() const override { return Variant::PACKED_INT64_ARRAY; }
}; };
#endif // BB_INT64_ARRAY_H #endif // BB_INT_ARRAY_H

View File

@ -1,7 +1,7 @@
/** /**
* bb_node.cpp * bb_node.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -11,9 +11,18 @@
#include "bb_node.h" #include "bb_node.h"
Variant BBNode::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default) { #ifdef LIMBOAI_MODULE
ERR_FAIL_NULL_V_MSG(p_scene_root, Variant(), "BBNode: get_value() failed - scene_root is null."); #include "core/error/error_macros.h"
ERR_FAIL_COND_V_MSG(p_blackboard.is_null(), Variant(), "BBNode: get_value() failed - blackboard is null."); #include "scene/main/node.h"
#endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/node.hpp>
#endif // LIMBOAI_GDEXTENSION
Variant BBNode::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_COND_V(p_agent == nullptr, Variant());
ERR_FAIL_COND_V(!p_blackboard.is_valid(), Variant());
Variant val; Variant val;
if (get_value_source() == SAVED_VALUE) { if (get_value_source() == SAVED_VALUE) {
@ -23,11 +32,16 @@ Variant BBNode::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboar
} }
if (val.get_type() == Variant::NODE_PATH) { if (val.get_type() == Variant::NODE_PATH) {
return p_scene_root->get_node_or_null(val); Node *agent = Object::cast_to<Node>(p_agent);
} else if (val.get_type() == Variant::OBJECT || val.get_type() == Variant::NIL) { ERR_FAIL_COND_V_MSG(agent == nullptr, Variant(), "BBNode: p_agent must be a Node.");
return val; return agent->get_node_or_null(val);
} else { } else {
WARN_PRINT("BBNode: Unexpected variant type: " + Variant::get_type_name(val.get_type()) + ". Returning default value."); Object *obj = val;
if (unlikely(obj == nullptr && val.get_type() != Variant::NIL)) {
WARN_PRINT("BBNode: Unexpected variant type of a blackboard variable.");
return p_default; return p_default;
} else {
return obj;
}
} }
} }

View File

@ -1,7 +1,7 @@
/** /**
* bb_node.h * bb_node.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,11 +18,10 @@ class BBNode : public BBParam {
GDCLASS(BBNode, BBParam); GDCLASS(BBNode, BBParam);
protected: protected:
static void _bind_methods() {} virtual Variant::Type get_type() const override { return Variant::NODE_PATH; }
public: public:
virtual Variant::Type get_type() const override { return Variant::NODE_PATH; } virtual Variant get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant()) override;
virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant()) override;
}; };
#endif // BB_NODE_H #endif // BB_NODE_H

View File

@ -1,7 +1,7 @@
/** /**
* bb_param.cpp * bb_param.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -14,6 +14,12 @@
#include "../../util/limbo_utility.h" #include "../../util/limbo_utility.h"
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
#include "core/core_bind.h"
#include "core/error/error_macros.h"
#include "core/object/class_db.h"
#include "core/object/object.h"
#include "core/variant/variant.h"
#include "core/variant/variant_utility.h"
#endif // LIMBOAI_MODULE #endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION #ifdef LIMBOAI_GDEXTENSION
@ -43,8 +49,8 @@ void BBParam::set_saved_value(Variant p_value) {
emit_changed(); emit_changed();
} }
void BBParam::set_variable(const StringName &p_variable) { void BBParam::set_variable(const String &p_value) {
variable = p_variable; variable = p_value;
_update_name(); _update_name();
emit_changed(); emit_changed();
} }
@ -75,13 +81,10 @@ String BBParam::_to_string() {
} }
} }
Variant BBParam::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default) { Variant BBParam::get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_COND_V(!p_blackboard.is_valid(), p_default); ERR_FAIL_COND_V(!p_blackboard.is_valid(), p_default);
if (value_source == SAVED_VALUE) { if (value_source == SAVED_VALUE) {
if (saved_value == Variant()) {
_assign_default_value();
}
return saved_value; return saved_value;
} else { } else {
ERR_FAIL_COND_V_MSG(!p_blackboard->has_var(variable), p_default, vformat("BBParam: Blackboard variable \"%s\" doesn't exist.", variable)); ERR_FAIL_COND_V_MSG(!p_blackboard->has_var(variable), p_default, vformat("BBParam: Blackboard variable \"%s\" doesn't exist.", variable));
@ -93,22 +96,22 @@ void BBParam::_get_property_list(List<PropertyInfo> *p_list) const {
if (value_source == ValueSource::SAVED_VALUE) { if (value_source == ValueSource::SAVED_VALUE) {
p_list->push_back(PropertyInfo(get_type(), "saved_value")); p_list->push_back(PropertyInfo(get_type(), "saved_value"));
} else { } else {
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "variable")); p_list->push_back(PropertyInfo(Variant::STRING, "variable"));
} }
} }
void BBParam::_bind_methods() { void BBParam::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_value_source", "value_source"), &BBParam::set_value_source); ClassDB::bind_method(D_METHOD("set_value_source", "p_value_source"), &BBParam::set_value_source);
ClassDB::bind_method(D_METHOD("get_value_source"), &BBParam::get_value_source); ClassDB::bind_method(D_METHOD("get_value_source"), &BBParam::get_value_source);
ClassDB::bind_method(D_METHOD("set_saved_value", "value"), &BBParam::set_saved_value); ClassDB::bind_method(D_METHOD("set_saved_value", "p_value"), &BBParam::set_saved_value);
ClassDB::bind_method(D_METHOD("get_saved_value"), &BBParam::get_saved_value); ClassDB::bind_method(D_METHOD("get_saved_value"), &BBParam::get_saved_value);
ClassDB::bind_method(D_METHOD("set_variable", "variable_name"), &BBParam::set_variable); ClassDB::bind_method(D_METHOD("set_variable", "p_variable_name"), &BBParam::set_variable);
ClassDB::bind_method(D_METHOD("get_variable"), &BBParam::get_variable); ClassDB::bind_method(D_METHOD("get_variable"), &BBParam::get_variable);
ClassDB::bind_method(D_METHOD("get_type"), &BBParam::get_type); ClassDB::bind_method(D_METHOD("get_type"), &BBParam::get_type);
ClassDB::bind_method(D_METHOD("get_value", "scene_root", "blackboard", "default"), &BBParam::get_value, Variant()); ClassDB::bind_method(D_METHOD("get_value", "p_agent", "p_blackboard", "p_default"), &BBParam::get_value, Variant());
ADD_PROPERTY(PropertyInfo(Variant::INT, "value_source", PROPERTY_HINT_ENUM, "Saved Value,Blackboard Var"), "set_value_source", "get_value_source"); ADD_PROPERTY(PropertyInfo(Variant::INT, "value_source", PROPERTY_HINT_ENUM, "Saved Value,Blackboard Var"), "set_value_source", "get_value_source");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "variable", PROPERTY_HINT_NONE, "", 0), "set_variable", "get_variable"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "variable", PROPERTY_HINT_NONE, "", 0), "set_variable", "get_variable");
ADD_PROPERTY(PropertyInfo(Variant::NIL, "saved_value", PROPERTY_HINT_NONE, "", 0), "set_saved_value", "get_saved_value"); ADD_PROPERTY(PropertyInfo(Variant::NIL, "saved_value", PROPERTY_HINT_NONE, "", 0), "set_saved_value", "get_saved_value");
BIND_ENUM_CONSTANT(SAVED_VALUE); BIND_ENUM_CONSTANT(SAVED_VALUE);
@ -117,4 +120,7 @@ void BBParam::_bind_methods() {
BBParam::BBParam() { BBParam::BBParam() {
value_source = SAVED_VALUE; value_source = SAVED_VALUE;
variable = "";
_assign_default_value();
} }

View File

@ -1,7 +1,7 @@
/** /**
* bb_param.h * bb_param.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -17,10 +17,16 @@
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
#include "core/io/resource.h" #include "core/io/resource.h"
#include "core/object/object.h"
#include "core/typedefs.h"
#include "core/variant/variant.h"
#endif // LIMBOAI_MODULE #endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION #ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/object.hpp>
#include <godot_cpp/classes/resource.hpp> #include <godot_cpp/classes/resource.hpp>
#include <godot_cpp/core/type_info.hpp>
#include <godot_cpp/variant/variant.hpp>
#endif // LIMBOAI_GDEXTENSION #endif // LIMBOAI_GDEXTENSION
class BBParam : public Resource { class BBParam : public Resource {
@ -35,7 +41,7 @@ public:
private: private:
ValueSource value_source; ValueSource value_source;
Variant saved_value; Variant saved_value;
StringName variable; String variable;
_FORCE_INLINE_ void _update_name() { _FORCE_INLINE_ void _update_name() {
set_name((value_source == SAVED_VALUE) ? String(saved_value) : LimboUtility::get_singleton()->decorate_var(variable)); set_name((value_source == SAVED_VALUE) ? String(saved_value) : LimboUtility::get_singleton()->decorate_var(variable));
@ -44,19 +50,28 @@ private:
protected: protected:
static void _bind_methods(); static void _bind_methods();
_FORCE_INLINE_ void _assign_default_value() { saved_value = VARIANT_DEFAULT(get_type()); } _FORCE_INLINE_ void _assign_default_value() {
#ifdef LIMBOAI_MODULE
Callable::CallError err;
Variant::construct(get_type(), saved_value, nullptr, 0, err);
#elif LIMBOAI_GDEXTENSION
saved_value.clear();
#endif
}
void _get_property_list(List<PropertyInfo> *p_list) const; void _get_property_list(List<PropertyInfo> *p_list) const;
public: public:
virtual Variant::Type get_type() const { return Variant::NIL; }
void set_value_source(ValueSource p_value); void set_value_source(ValueSource p_value);
ValueSource get_value_source() const { return value_source; } ValueSource get_value_source() const { return value_source; }
void set_saved_value(Variant p_value); void set_saved_value(Variant p_value);
Variant get_saved_value(); Variant get_saved_value();
void set_variable(const StringName &p_variable); void set_variable(const String &p_value);
StringName get_variable() const { return variable; } String get_variable() const { return variable; }
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
virtual String to_string() override; virtual String to_string() override;
@ -64,9 +79,7 @@ public:
virtual String _to_string(); virtual String _to_string();
#endif #endif
virtual Variant::Type get_type() const { return Variant::NIL; } virtual Variant get_value(Object *p_agent, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant());
virtual Variant::Type get_variable_expected_type() const { return get_type(); }
virtual Variant get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default = Variant());
BBParam(); BBParam();
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_plane.h * bb_plane.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBPlane : public BBParam {
GDCLASS(BBPlane, BBParam); GDCLASS(BBPlane, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PLANE; } virtual Variant::Type get_type() const override { return Variant::PLANE; }
}; };

View File

@ -1,26 +0,0 @@
/**
* bb_projection.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#ifndef BB_PROJECTION_H
#define BB_PROJECTION_H
#include "bb_param.h"
class BBProjection : public BBParam {
GDCLASS(BBProjection, BBParam);
protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PROJECTION; }
};
#endif // BB_PROJECTION_H

View File

@ -1,7 +1,7 @@
/** /**
* bb_quaternion.h * bb_quaternion.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBQuaternion : public BBParam {
GDCLASS(BBQuaternion, BBParam); GDCLASS(BBQuaternion, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::QUATERNION; } virtual Variant::Type get_type() const override { return Variant::QUATERNION; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_rect2.h * bb_rect2.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBRect2 : public BBParam {
GDCLASS(BBRect2, BBParam); GDCLASS(BBRect2, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::RECT2; } virtual Variant::Type get_type() const override { return Variant::RECT2; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_rect2i.h * bb_rect2i.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBRect2i : public BBParam {
GDCLASS(BBRect2i, BBParam); GDCLASS(BBRect2i, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::RECT2I; } virtual Variant::Type get_type() const override { return Variant::RECT2I; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_string.h * bb_string.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBString : public BBParam {
GDCLASS(BBString, BBParam); GDCLASS(BBString, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::STRING; } virtual Variant::Type get_type() const override { return Variant::STRING; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_string_array.h * bb_string_array.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBStringArray : public BBParam {
GDCLASS(BBStringArray, BBParam); GDCLASS(BBStringArray, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_STRING_ARRAY; } virtual Variant::Type get_type() const override { return Variant::PACKED_STRING_ARRAY; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_string_name.h * bb_string_name.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBStringName : public BBParam {
GDCLASS(BBStringName, BBParam); GDCLASS(BBStringName, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::STRING_NAME; } virtual Variant::Type get_type() const override { return Variant::STRING_NAME; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_transform2d.h * bb_transform2d.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBTransform2D : public BBParam {
GDCLASS(BBTransform2D, BBParam); GDCLASS(BBTransform2D, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::TRANSFORM2D; } virtual Variant::Type get_type() const override { return Variant::TRANSFORM2D; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_transform3d.h * bb_transform3d.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBTransform3D : public BBParam {
GDCLASS(BBTransform3D, BBParam); GDCLASS(BBTransform3D, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::TRANSFORM3D; } virtual Variant::Type get_type() const override { return Variant::TRANSFORM3D; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_variant.cpp * bb_variant.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -27,7 +27,7 @@ Variant::Type BBVariant::get_type() const {
} }
void BBVariant::_bind_methods() { void BBVariant::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_type", "type"), &BBVariant::set_type); ClassDB::bind_method(D_METHOD("set_type", "p_type"), &BBVariant::set_type);
String vtypes; String vtypes;
for (int i = 0; i < Variant::VARIANT_MAX; i++) { for (int i = 0; i < Variant::VARIANT_MAX; i++) {

View File

@ -1,7 +1,7 @@
/** /**
* bb_variant.h * bb_variant.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -27,8 +27,6 @@ public:
virtual Variant::Type get_type() const override; virtual Variant::Type get_type() const override;
void set_type(Variant::Type p_type); void set_type(Variant::Type p_type);
virtual Variant::Type get_variable_expected_type() const override { return Variant::NIL; }
BBVariant(const Variant &p_value); BBVariant(const Variant &p_value);
BBVariant(); BBVariant();
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_vector2.h * bb_vector2.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBVector2 : public BBParam {
GDCLASS(BBVector2, BBParam); GDCLASS(BBVector2, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR2; } virtual Variant::Type get_type() const override { return Variant::VECTOR2; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_vector2_array.h * bb_vector2_array.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBVector2Array : public BBParam {
GDCLASS(BBVector2Array, BBParam); GDCLASS(BBVector2Array, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_VECTOR2_ARRAY; } virtual Variant::Type get_type() const override { return Variant::PACKED_VECTOR2_ARRAY; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_vector2i.h * bb_vector2i.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBVector2i : public BBParam {
GDCLASS(BBVector2i, BBParam); GDCLASS(BBVector2i, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR2I; } virtual Variant::Type get_type() const override { return Variant::VECTOR2I; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_vector3.h * bb_vector3.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBVector3 : public BBParam {
GDCLASS(BBVector3, BBParam); GDCLASS(BBVector3, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR3; } virtual Variant::Type get_type() const override { return Variant::VECTOR3; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_vector3_array.h * bb_vector3_array.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBVector3Array : public BBParam {
GDCLASS(BBVector3Array, BBParam); GDCLASS(BBVector3Array, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::PACKED_VECTOR3_ARRAY; } virtual Variant::Type get_type() const override { return Variant::PACKED_VECTOR3_ARRAY; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_vector3i.h * bb_vector3i.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBVector3i : public BBParam {
GDCLASS(BBVector3i, BBParam); GDCLASS(BBVector3i, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR3I; } virtual Variant::Type get_type() const override { return Variant::VECTOR3I; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_vector4.h * bb_vector4.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBVector4 : public BBParam {
GDCLASS(BBVector4, BBParam); GDCLASS(BBVector4, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR4; } virtual Variant::Type get_type() const override { return Variant::VECTOR4; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bb_vector4i.h * bb_vector4i.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,6 @@ class BBVector4i : public BBParam {
GDCLASS(BBVector4i, BBParam); GDCLASS(BBVector4i, BBParam);
protected: protected:
static void _bind_methods() {}
virtual Variant::Type get_type() const override { return Variant::VECTOR4I; } virtual Variant::Type get_type() const override { return Variant::VECTOR4I; }
}; };

View File

@ -1,190 +0,0 @@
/**
* bb_variable.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#include "bb_variable.h"
#include "../util/limbo_compat.h"
void BBVariable::unref() {
if (data && data->refcount.unref()) {
memdelete(data);
}
data = nullptr;
}
void BBVariable::set_value(const Variant &p_value) {
data->value = p_value; // Setting value even when bound as a fallback in case the binding fails.
data->value_changed = true;
if (is_bound()) {
Object *obj = OBJECT_DB_GET_INSTANCE(data->bound_object);
ERR_FAIL_COND_MSG(!obj, "Blackboard: Failed to get bound object.");
#ifdef LIMBOAI_MODULE
bool r_valid;
obj->set(data->bound_property, p_value, &r_valid);
ERR_FAIL_COND_MSG(!r_valid, vformat("Blackboard: Failed to set bound property `%s` on %s", data->bound_property, obj));
#elif LIMBOAI_GDEXTENSION
obj->set(data->bound_property, p_value);
#endif
}
}
Variant BBVariable::get_value() const {
if (is_bound()) {
Object *obj = OBJECT_DB_GET_INSTANCE(data->bound_object);
ERR_FAIL_COND_V_MSG(!obj, data->value, "Blackboard: Failed to get bound object.");
#ifdef LIMBOAI_MODULE
bool r_valid;
Variant ret = obj->get(data->bound_property, &r_valid);
ERR_FAIL_COND_V_MSG(!r_valid, data->value, vformat("Blackboard: Failed to get bound property `%s` on %s", data->bound_property, obj));
#elif LIMBOAI_GDEXTENSION
Variant ret = obj->get(data->bound_property);
#endif
return ret;
}
return data->value;
}
void BBVariable::set_type(Variant::Type p_type) {
data->type = p_type;
data->value = VARIANT_DEFAULT(p_type);
}
Variant::Type BBVariable::get_type() const {
return data->type;
}
void BBVariable::set_hint(PropertyHint p_hint) {
data->hint = p_hint;
}
PropertyHint BBVariable::get_hint() const {
return data->hint;
}
void BBVariable::set_hint_string(const String &p_hint_string) {
data->hint_string = p_hint_string;
}
String BBVariable::get_hint_string() const {
return data->hint_string;
}
BBVariable BBVariable::duplicate(bool p_deep) const {
BBVariable var;
var.data->hint = data->hint;
var.data->hint_string = data->hint_string;
var.data->type = data->type;
if (p_deep) {
var.data->value = data->value.duplicate(p_deep);
} else {
var.data->value = data->value;
}
var.data->binding_path = data->binding_path;
var.data->bound_object = data->bound_object;
var.data->bound_property = data->bound_property;
return var;
}
bool BBVariable::is_same_prop_info(const BBVariable &p_other) const {
if (data->type != p_other.data->type) {
return false;
}
if (data->hint != p_other.data->hint) {
return false;
}
if (data->hint_string != p_other.data->hint_string) {
return false;
}
return true;
}
void BBVariable::copy_prop_info(const BBVariable &p_other) {
data->type = p_other.data->type;
data->hint = p_other.data->hint;
data->hint_string = p_other.data->hint_string;
}
void BBVariable::bind(Object *p_object, const StringName &p_property) {
ERR_FAIL_NULL_MSG(p_object, "Blackboard: Binding failed - object is null.");
ERR_FAIL_COND_MSG(p_property == StringName(), "Blackboard: Binding failed - property name is empty.");
ERR_FAIL_COND_MSG(!OBJECT_HAS_PROPERTY(p_object, p_property), vformat("Blackboard: Binding failed - %s has no property `%s`.", p_object, p_property));
data->bound_object = p_object->get_instance_id();
data->bound_property = p_property;
}
void BBVariable::unbind() {
data->bound_object = 0;
data->bound_property = StringName();
}
bool BBVariable::operator==(const BBVariable &p_var) const {
if (data == p_var.data) {
return true;
}
if (!data || !p_var.data) {
return false;
}
if (data->type != p_var.data->type) {
return false;
}
if (data->hint != p_var.data->hint) {
return false;
}
if (data->hint_string != p_var.data->hint_string) {
return false;
}
if (get_value() != p_var.get_value()) {
return false;
}
return true;
}
bool BBVariable::operator!=(const BBVariable &p_var) const {
return !(*this == p_var);
}
void BBVariable::operator=(const BBVariable &p_var) {
if (this == &p_var) {
return;
}
unref();
if (p_var.data && p_var.data->refcount.ref()) {
data = p_var.data;
}
}
BBVariable::BBVariable(const BBVariable &p_var) {
if (p_var.data && p_var.data->refcount.ref()) {
data = p_var.data;
}
}
BBVariable::BBVariable(Variant::Type p_type, PropertyHint p_hint, const String &p_hint_string) {
data = memnew(Data);
data->refcount.init();
set_type(p_type);
data->hint = p_hint;
data->hint_string = p_hint_string;
}
BBVariable::~BBVariable() {
unref();
}

View File

@ -1,84 +0,0 @@
/**
* bb_variable.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#ifndef BB_VARIABLE_H
#define BB_VARIABLE_H
#ifdef LIMBOAI_MODULE
#include "core/object/object.h"
#endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION
#include "godot_cpp/core/object.hpp"
using namespace godot;
#endif // LIMBOAI_GDEXTENSION
class BBVariable {
private:
struct Data {
// Is used to decide if the value needs to be synced in a derived plan.
bool value_changed = false;
SafeRefCount refcount;
Variant value;
Variant::Type type = Variant::NIL;
PropertyHint hint = PropertyHint::PROPERTY_HINT_NONE;
String hint_string;
NodePath binding_path;
uint64_t bound_object = 0;
StringName bound_property;
};
Data *data = nullptr;
void unref();
public:
void set_value(const Variant &p_value);
Variant get_value() const;
void set_type(Variant::Type p_type);
Variant::Type get_type() const;
void set_hint(PropertyHint p_hint);
PropertyHint get_hint() const;
void set_hint_string(const String &p_hint_string);
String get_hint_string() const;
BBVariable duplicate(bool p_deep = false) const;
_FORCE_INLINE_ bool is_value_changed() const { return data->value_changed; }
_FORCE_INLINE_ void reset_value_changed() { data->value_changed = false; }
bool is_same_prop_info(const BBVariable &p_other) const;
void copy_prop_info(const BBVariable &p_other);
// * Editor binding methods
NodePath get_binding_path() const { return data->binding_path; }
void set_binding_path(const NodePath &p_binding_path) { data->binding_path = p_binding_path; }
bool has_binding() { return data->binding_path.is_empty(); }
// * Runtime binding methods
_FORCE_INLINE_ bool is_bound() const { return data->bound_object != 0; }
void bind(Object *p_object, const StringName &p_property);
void unbind();
bool operator==(const BBVariable &p_var) const;
bool operator!=(const BBVariable &p_var) const;
void operator=(const BBVariable &p_var);
BBVariable(const BBVariable &p_var);
BBVariable(Variant::Type p_type = Variant::Type::NIL, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "");
~BBVariable();
};
#endif // BB_VARIABLE_H

View File

@ -1,7 +1,7 @@
/** /**
* blackboard.cpp * blackboard.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -10,9 +10,9 @@
*/ */
#include "blackboard.h" #include "blackboard.h"
#include "../util/limbo_compat.h"
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
#include "core/error/error_macros.h"
#include "core/variant/variant.h" #include "core/variant/variant.h"
#include "scene/main/node.h" #include "scene/main/node.h"
#endif // LIMBOAI_MODULE #endif // LIMBOAI_MODULE
@ -22,146 +22,63 @@
#include <godot_cpp/classes/ref.hpp> #include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/classes/ref_counted.hpp> #include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/core/object.hpp> #include <godot_cpp/core/object.hpp>
#include <godot_cpp/variant/dictionary.hpp>
using namespace godot; using namespace godot;
#endif #endif
Ref<Blackboard> Blackboard::top() const { Ref<Blackboard> Blackboard::top() const {
Ref<Blackboard> bb(this); Ref<Blackboard> bb(this);
while (bb->get_parent().is_valid()) { while (bb->get_parent_scope().is_valid()) {
bb = bb->get_parent(); bb = bb->get_parent_scope();
} }
return bb; return bb;
} }
Variant Blackboard::get_var(const StringName &p_name, const Variant &p_default, bool p_complain) const { Variant Blackboard::get_var(const Variant &p_key, const Variant &p_default) const {
if (data.has(p_name)) { if (data.has(p_key)) {
return data.get(p_name).get_value(); return data.get(p_key, Variant());
} else if (parent.is_valid()) { } else if (parent.is_valid()) {
return parent->get_var(p_name, p_default, p_complain); return parent->get_var(p_key, p_default);
} else { } else {
if (p_complain) {
ERR_PRINT(vformat("Blackboard: Variable \"%s\" not found.", p_name));
}
return p_default; return p_default;
} }
} }
void Blackboard::set_var(const StringName &p_name, const Variant &p_value) { void Blackboard::set_var(const Variant &p_key, const Variant &p_value) {
if (data.has(p_name)) { data[p_key] = p_value;
// Not checking type - allowing duck-typing.
data[p_name].set_value(p_value);
} else {
BBVariable var(p_value.get_type());
var.set_value(p_value);
data.insert(p_name, var);
}
} }
bool Blackboard::has_var(const StringName &p_name) const { bool Blackboard::has_var(const Variant &p_key) const {
return data.has(p_name) || (parent.is_valid() && parent->has_var(p_name)); return data.has(p_key) || (parent.is_valid() && parent->has_var(p_key));
} }
void Blackboard::erase_var(const StringName &p_name) { void Blackboard::erase_var(const Variant &p_key) {
data.erase(p_name); data.erase(p_key);
} }
TypedArray<StringName> Blackboard::list_vars() const { void Blackboard::prefetch_nodepath_vars(Node *p_node) {
TypedArray<StringName> var_names; ERR_FAIL_COND(p_node == nullptr);
var_names.resize(data.size()); Array keys = data.keys();
int idx = 0; Array values = data.values();
for (const KeyValue<StringName, BBVariable> &kv : data) {
var_names[idx] = kv.key;
idx += 1;
}
return var_names;
}
void Blackboard::print_state() const {
Ref<Blackboard> bb{ this };
int scope_idx = 0;
while (bb.is_valid()) {
int i = 0;
String line = "Scope " + itos(scope_idx) + ": { ";
for (const KeyValue<StringName, BBVariable> &kv : bb->data) {
if (i > 0) {
line += ", ";
}
line += String(kv.key) + ": " + String(kv.value.get_value());
i++;
}
line += " }";
PRINT_LINE(line);
bb = bb->get_parent();
scope_idx++;
}
}
Dictionary Blackboard::get_vars_as_dict() const {
Dictionary dict;
for (const KeyValue<StringName, BBVariable> &kv : data) {
dict[kv.key] = kv.value.get_value();
}
return dict;
}
void Blackboard::populate_from_dict(const Dictionary &p_dictionary) {
Array keys = p_dictionary.keys();
for (int i = 0; i < keys.size(); i++) { for (int i = 0; i < keys.size(); i++) {
if (keys[i].get_type() == Variant::STRING_NAME || keys[i].get_type() == Variant::STRING) { if (values[i].get_type() == Variant::NODE_PATH) {
set_var(keys[i], p_dictionary[keys[i]]); Node *fetched_node = p_node->get_node_or_null(values[i]);
} else { if (fetched_node != nullptr) {
ERR_PRINT("Blackboard: Invalid key type in dictionary to populate blackboard. Must be StringName or String."); data[keys[i]] = fetched_node;
} }
} }
}
void Blackboard::bind_var_to_property(const StringName &p_name, Object *p_object, const StringName &p_property, bool p_create) {
if (!data.has(p_name)) {
if (p_create) {
data.insert(p_name, BBVariable());
} else {
ERR_FAIL_MSG("Blackboard: Can't bind variable that doesn't exist (var: " + p_name + ").");
} }
}
data[p_name].bind(p_object, p_property);
}
void Blackboard::unbind_var(const StringName &p_name) {
ERR_FAIL_COND_MSG(!data.has(p_name), "Blackboard: Can't unbind variable that doesn't exist (var: " + p_name + ").");
data[p_name].unbind();
}
void Blackboard::assign_var(const StringName &p_name, const BBVariable &p_var) {
data.insert(p_name, p_var);
}
void Blackboard::link_var(const StringName &p_name, const Ref<Blackboard> &p_target_blackboard, const StringName &p_target_var, bool p_create) {
if (!data.has(p_name)) {
if (p_create) {
data.insert(p_name, BBVariable());
} else {
ERR_FAIL_MSG("Blackboard: Can't link variable that doesn't exist (var: " + p_name + ").");
}
}
ERR_FAIL_COND_MSG(p_target_blackboard.is_null(), "Blackboard: Can't link variable to target blackboard that is null (var: " + p_name + ").");
ERR_FAIL_COND_MSG(!p_target_blackboard->data.has(p_target_var), "Blackboard: Can't link variable to non-existent target (var: " + p_name + ", target: " + p_target_var + ").");
data[p_name] = p_target_blackboard->data[p_target_var];
} }
void Blackboard::_bind_methods() { void Blackboard::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_var", "var_name", "default", "complain"), &Blackboard::get_var, DEFVAL(Variant()), DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_data"), &Blackboard::get_data);
ClassDB::bind_method(D_METHOD("set_var", "var_name", "value"), &Blackboard::set_var); ClassDB::bind_method(D_METHOD("set_data", "p_data"), &Blackboard::set_data);
ClassDB::bind_method(D_METHOD("has_var", "var_name"), &Blackboard::has_var); ClassDB::bind_method(D_METHOD("get_var", "p_key", "p_default"), &Blackboard::get_var, Variant());
ClassDB::bind_method(D_METHOD("set_parent", "blackboard"), &Blackboard::set_parent); ClassDB::bind_method(D_METHOD("set_var", "p_key", "p_value"), &Blackboard::set_var);
ClassDB::bind_method(D_METHOD("get_parent"), &Blackboard::get_parent); ClassDB::bind_method(D_METHOD("has_var", "p_key"), &Blackboard::has_var);
ClassDB::bind_method(D_METHOD("erase_var", "var_name"), &Blackboard::erase_var); ClassDB::bind_method(D_METHOD("set_parent_scope", "p_blackboard"), &Blackboard::set_parent_scope);
ClassDB::bind_method(D_METHOD("clear"), &Blackboard::clear); ClassDB::bind_method(D_METHOD("get_parent_scope"), &Blackboard::get_parent_scope);
ClassDB::bind_method(D_METHOD("list_vars"), &Blackboard::list_vars); ClassDB::bind_method(D_METHOD("erase_var", "p_key"), &Blackboard::erase_var);
ClassDB::bind_method(D_METHOD("print_state"), &Blackboard::print_state); ClassDB::bind_method(D_METHOD("prefetch_nodepath_vars", "p_node"), &Blackboard::prefetch_nodepath_vars);
ClassDB::bind_method(D_METHOD("get_vars_as_dict"), &Blackboard::get_vars_as_dict);
ClassDB::bind_method(D_METHOD("populate_from_dict", "dictionary"), &Blackboard::populate_from_dict);
ClassDB::bind_method(D_METHOD("top"), &Blackboard::top); ClassDB::bind_method(D_METHOD("top"), &Blackboard::top);
ClassDB::bind_method(D_METHOD("bind_var_to_property", "var_name", "object", "property", "create"), &Blackboard::bind_var_to_property, DEFVAL(false));
ClassDB::bind_method(D_METHOD("unbind_var", "var_name"), &Blackboard::unbind_var);
ClassDB::bind_method(D_METHOD("link_var", "var_name", "target_blackboard", "target_var", "create"), &Blackboard::link_var, DEFVAL(false));
} }

View File

@ -1,7 +1,7 @@
/** /**
* blackboard.h * blackboard.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -12,11 +12,10 @@
#ifndef BLACKBOARD_H #ifndef BLACKBOARD_H
#define BLACKBOARD_H #define BLACKBOARD_H
#include "bb_variable.h"
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
#include "core/object/object.h" #include "core/object/object.h"
#include "core/object/ref_counted.h" #include "core/object/ref_counted.h"
#include "core/variant/dictionary.h"
#include "core/variant/variant.h" #include "core/variant/variant.h"
#include "scene/main/node.h" #include "scene/main/node.h"
#endif // LIMBOAI_MODULE #endif // LIMBOAI_MODULE
@ -26,7 +25,7 @@
#include <godot_cpp/classes/ref.hpp> #include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/classes/ref_counted.hpp> #include <godot_cpp/classes/ref_counted.hpp>
#include <godot_cpp/core/object.hpp> #include <godot_cpp/core/object.hpp>
#include <godot_cpp/templates/hash_map.hpp> #include <godot_cpp/variant/dictionary.hpp>
using namespace godot; using namespace godot;
#endif // LIMBOAI_GDEXTENSION #endif // LIMBOAI_GDEXTENSION
@ -34,40 +33,27 @@ class Blackboard : public RefCounted {
GDCLASS(Blackboard, RefCounted); GDCLASS(Blackboard, RefCounted);
private: private:
HashMap<StringName, BBVariable> data; Dictionary data;
Ref<Blackboard> parent; Ref<Blackboard> parent;
protected: protected:
static void _bind_methods(); static void _bind_methods();
#ifdef LIMBOAI_GDEXTENSION
String _to_string() const { return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
public: public:
void set_parent(const Ref<Blackboard> &p_blackboard) { parent = p_blackboard; } void set_data(const Dictionary &p_value) { data = p_value; }
Ref<Blackboard> get_parent() const { return parent; } Dictionary get_data() const { return data; }
void set_parent_scope(const Ref<Blackboard> &p_blackboard) { parent = p_blackboard; }
Ref<Blackboard> get_parent_scope() const { return parent; }
Ref<Blackboard> top() const; Ref<Blackboard> top() const;
Variant get_var(const StringName &p_name, const Variant &p_default = Variant(), bool p_complain = true) const; Variant get_var(const Variant &p_key, const Variant &p_default) const;
void set_var(const StringName &p_name, const Variant &p_value); void set_var(const Variant &p_key, const Variant &p_value);
bool has_var(const StringName &p_name) const; bool has_var(const Variant &p_key) const;
_FORCE_INLINE_ bool has_local_var(const StringName &p_name) const { return data.has(p_name); } void erase_var(const Variant &p_key);
void erase_var(const StringName &p_name);
void clear() { data.clear(); }
TypedArray<StringName> list_vars() const;
void print_state() const;
Dictionary get_vars_as_dict() const; void prefetch_nodepath_vars(Node *p_node);
void populate_from_dict(const Dictionary &p_dictionary);
void bind_var_to_property(const StringName &p_name, Object *p_object, const StringName &p_property, bool p_create = false);
void unbind_var(const StringName &p_name);
void assign_var(const StringName &p_name, const BBVariable &p_var);
void link_var(const StringName &p_name, const Ref<Blackboard> &p_target_blackboard, const StringName &p_target_var, bool p_create = false);
}; };
#endif // BLACKBOARD_H #endif // BLACKBOARD_H

View File

@ -1,578 +0,0 @@
/**
* blackboard_plan.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#include "blackboard_plan.h"
#include "../util/limbo_utility.h"
#ifdef LIMBOAI_MODULE
#include "editor/editor_inspector.h"
#include "editor/editor_interface.h"
#elif LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/editor_inspector.hpp>
#include <godot_cpp/classes/editor_interface.hpp>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/scene_tree.hpp>
#endif
bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
String name_str = p_name;
#ifdef TOOLS_ENABLED
// * Editor
if (var_map.has(p_name)) {
BBVariable &var = var_map[p_name];
var.set_value(p_value);
if (base.is_valid() && p_value == base->get_var(p_name).get_value()) {
// When user pressed reset property button in inspector...
var.reset_value_changed();
}
return true;
}
#endif // TOOLS_ENABLED
// * Mapping
if (name_str.begins_with("mapping/")) {
StringName mapped_var_name = name_str.get_slicec('/', 1);
StringName value = p_value;
bool prop_list_changed = false;
if (value == StringName()) {
if (parent_scope_mapping.has(mapped_var_name)) {
prop_list_changed = true;
parent_scope_mapping.erase(mapped_var_name);
}
} else {
if (!parent_scope_mapping.has(mapped_var_name)) {
prop_list_changed = true;
}
parent_scope_mapping[mapped_var_name] = value;
}
if (prop_list_changed) {
notify_property_list_changed();
}
return true;
}
// * Binding
if (name_str.begins_with("binding/")) {
StringName bound_var = name_str.get_slicec('/', 1);
NodePath value = p_value;
bool prop_list_changed = false;
if (value.is_empty()) {
if (property_bindings.has(bound_var)) {
prop_list_changed = true;
property_bindings.erase(bound_var);
}
} else {
if (!property_bindings.has(bound_var)) {
prop_list_changed = true;
}
property_bindings[bound_var] = value;
}
if (prop_list_changed) {
notify_property_list_changed();
}
}
// * Storage
if (name_str.begins_with("var/")) {
StringName var_name = name_str.get_slicec('/', 1);
String what = name_str.get_slicec('/', 2);
if (!var_map.has(var_name) && what == "name") {
add_var(var_name, BBVariable());
}
if (what == "name") {
// We don't store variable name with the variable.
} else if (what == "type") {
var_map[var_name].set_type((Variant::Type)(int)p_value);
} else if (what == "value") {
var_map[var_name].set_value(p_value);
} else if (what == "hint") {
var_map[var_name].set_hint((PropertyHint)(int)p_value);
} else if (what == "hint_string") {
var_map[var_name].set_hint_string(p_value);
} else if (what == "property_binding") {
property_bindings[var_name] = NodePath(p_value);
} else {
return false;
}
return true;
}
return false;
}
bool BlackboardPlan::_get(const StringName &p_name, Variant &r_ret) const {
String name_str = p_name;
#ifdef TOOLS_ENABLED
// * Editor
if (var_map.has(p_name)) {
if (has_mapping(p_name)) {
r_ret = "Mapped to " + LimboUtility::get_singleton()->decorate_var(parent_scope_mapping[p_name]);
} else if (has_property_binding(p_name)) {
const NodePath &binding = property_bindings[p_name];
Node *edited_node = Object::cast_to<Node>(EditorInterface::get_singleton()->get_inspector()->get_edited_object());
if (!edited_node) {
edited_node = SCENE_TREE()->get_edited_scene_root();
}
Node *bound_node = edited_node ? edited_node->get_node_or_null(binding) : nullptr;
String shortened_path;
if (bound_node) {
shortened_path = (String)bound_node->get_name() +
":" + (String)binding.get_concatenated_subnames();
} else {
shortened_path = (String)binding.get_name(binding.get_name_count() - 1) +
":" + (String)binding.get_concatenated_subnames();
}
r_ret = String::utf8("🔗 ") + shortened_path;
} else {
r_ret = var_map[p_name].get_value();
}
return true;
}
#endif // TOOLS_ENABLED
// * Mapping
if (name_str.begins_with("mapping/")) {
StringName mapped_var_name = name_str.get_slicec('/', 1);
ERR_FAIL_COND_V(mapped_var_name == StringName(), false);
if (has_mapping(mapped_var_name)) {
r_ret = parent_scope_mapping[mapped_var_name];
} else if (has_property_binding(mapped_var_name)) {
r_ret = RTR("Already bound to property.");
} else {
r_ret = StringName();
}
return true;
}
// * Binding
if (name_str.begins_with("binding/")) {
StringName bound_var = name_str.get_slicec('/', 1);
ERR_FAIL_COND_V(bound_var == StringName(), false);
if (has_property_binding(bound_var)) {
r_ret = property_bindings[bound_var];
} else if (has_mapping(bound_var)) {
r_ret = RTR("Already mapped to variable.");
} else {
r_ret = NodePath();
}
return true;
}
// * Storage
if (!name_str.begins_with("var/")) {
return false;
}
StringName var_name = name_str.get_slicec('/', 1);
String what = name_str.get_slicec('/', 2);
ERR_FAIL_COND_V(!var_map.has(var_name), false);
if (what == "name") {
r_ret = var_name;
} else if (what == "type") {
r_ret = var_map[var_name].get_type();
} else if (what == "value") {
r_ret = var_map[var_name].get_value();
} else if (what == "hint") {
r_ret = var_map[var_name].get_hint();
} else if (what == "hint_string") {
r_ret = var_map[var_name].get_hint_string();
}
return true;
}
void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Pair<StringName, BBVariable> &p : var_list) {
String var_name = p.first;
BBVariable var = p.second;
#ifdef TOOLS_ENABLED
// * Editor
if (!_is_var_nil(var) && !_is_var_private(var_name, var)) {
if (has_mapping(var_name) || has_property_binding(var_name)) {
p_list->push_back(PropertyInfo(Variant::STRING, var_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
} else {
p_list->push_back(PropertyInfo(var.get_type(), var_name, var.get_hint(), var.get_hint_string(), PROPERTY_USAGE_EDITOR));
}
}
#endif // TOOLS_ENABLED
// * Storage
if (is_derived() && (!var.is_value_changed() || var.get_value() == base->var_map[var_name].get_value())) {
// Don't store variable if it's not modified in a derived plan.
// Variable is considered modified when it's marked as changed and its value is different from the base plan.
continue;
}
p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::INT, "var/" + var_name + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(var.get_type(), "var/" + var_name + "/value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::INT, "var/" + var_name + "/hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::STRING, "var/" + var_name + "/hint_string", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
// * Mapping
if (is_mapping_enabled()) {
p_list->push_back(PropertyInfo(Variant::NIL, "Mapping", PROPERTY_HINT_NONE, "mapping/", PROPERTY_USAGE_GROUP));
for (const Pair<StringName, BBVariable> &p : var_list) {
if (_is_var_private(p.first, p.second)) {
continue;
}
if (unlikely(has_property_binding(p.first))) {
p_list->push_back(PropertyInfo(Variant::STRING, "mapping/" + p.first, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
} else {
// Serialize only non-empty mappings.
PropertyUsageFlags usage = has_mapping(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR;
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "mapping/" + p.first, PROPERTY_HINT_NONE, "", usage));
}
}
}
// * Binding
p_list->push_back(PropertyInfo(Variant::NIL, "Binding", PROPERTY_HINT_NONE, "binding/", PROPERTY_USAGE_GROUP));
for (const Pair<StringName, BBVariable> &p : var_list) {
if (_is_var_nil(p.second) || _is_var_private(p.first, p.second)) {
continue;
}
if (unlikely(has_mapping(p.first))) {
p_list->push_back(PropertyInfo(Variant::STRING, "binding/" + p.first, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY));
} else {
PropertyUsageFlags usage = has_property_binding(p.first) ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_EDITOR;
// PROPERTY_HINT_LINK is used to signal that NodePath should point to a property.
// Our inspector plugin will know how to handle it.
p_list->push_back(PropertyInfo(Variant::NODE_PATH, "binding/" + p.first, PROPERTY_HINT_LINK, itos(p.second.get_type()), usage));
}
}
}
bool BlackboardPlan::_property_can_revert(const StringName &p_name) const {
if (String(p_name).begins_with("mapping/")) {
return true;
}
return base.is_valid() && base->var_map.has(p_name);
}
bool BlackboardPlan::_property_get_revert(const StringName &p_name, Variant &r_property) const {
if (String(p_name).begins_with("mapping/")) {
r_property = StringName();
return true;
}
if (base->var_map.has(p_name)) {
r_property = base->var_map[p_name].get_value();
return true;
}
return false;
}
void BlackboardPlan::set_base_plan(const Ref<BlackboardPlan> &p_base) {
if (p_base == this) {
WARN_PRINT_ED("BlackboardPlan: Using same resource for derived blackboard plan is not supported.");
base.unref();
} else {
base = p_base;
}
sync_with_base_plan();
notify_property_list_changed();
}
void BlackboardPlan::set_parent_scope_plan_provider(const Callable &p_parent_scope_plan_provider) {
parent_scope_plan_provider = p_parent_scope_plan_provider;
notify_property_list_changed();
}
bool BlackboardPlan::has_mapping(const StringName &p_name) const {
return is_mapping_enabled() && parent_scope_mapping.has(p_name) && parent_scope_mapping[p_name] != StringName();
}
void BlackboardPlan::set_property_binding(const StringName &p_name, const NodePath &p_path) {
property_bindings[p_name] = p_path;
emit_changed();
}
void BlackboardPlan::set_prefetch_nodepath_vars(bool p_enable) {
prefetch_nodepath_vars = p_enable;
emit_changed();
}
bool BlackboardPlan::is_prefetching_nodepath_vars() const {
if (is_derived()) {
return base->is_prefetching_nodepath_vars();
} else {
return prefetch_nodepath_vars;
}
}
void BlackboardPlan::add_var(const StringName &p_name, const BBVariable &p_var) {
ERR_FAIL_COND(p_name == StringName());
ERR_FAIL_COND(var_map.has(p_name));
var_map.insert(p_name, p_var);
var_list.push_back(Pair<StringName, BBVariable>(p_name, p_var));
notify_property_list_changed();
emit_changed();
}
void BlackboardPlan::remove_var(const StringName &p_name) {
ERR_FAIL_COND(!var_map.has(p_name));
var_list.erase(Pair<StringName, BBVariable>(p_name, var_map[p_name]));
var_map.erase(p_name);
notify_property_list_changed();
emit_changed();
}
BBVariable BlackboardPlan::get_var(const StringName &p_name) {
ERR_FAIL_COND_V(!var_map.has(p_name), BBVariable());
return var_map.get(p_name);
}
Pair<StringName, BBVariable> BlackboardPlan::get_var_by_index(int p_index) {
Pair<StringName, BBVariable> ret;
ERR_FAIL_INDEX_V(p_index, (int)var_map.size(), ret);
return var_list.get(p_index);
}
TypedArray<StringName> BlackboardPlan::list_vars() const {
TypedArray<StringName> ret;
for (const Pair<StringName, BBVariable> &p : var_list) {
ret.append(p.first);
}
return ret;
}
StringName BlackboardPlan::get_var_name(const BBVariable &p_var) const {
for (const Pair<StringName, BBVariable> &p : var_list) {
if (p.second == p_var) {
return p.first;
}
}
return StringName();
}
bool BlackboardPlan::is_valid_var_name(const StringName &p_name) const {
String name_str = p_name;
if (name_str.begins_with("resource_")) {
return false;
}
return name_str.is_valid_identifier() && !var_map.has(p_name);
}
void BlackboardPlan::rename_var(const StringName &p_name, const StringName &p_new_name) {
if (p_name == p_new_name) {
return;
}
ERR_FAIL_COND(!is_valid_var_name(p_new_name));
ERR_FAIL_COND(!var_map.has(p_name));
ERR_FAIL_COND(var_map.has(p_new_name));
BBVariable var = var_map[p_name];
Pair<StringName, BBVariable> new_entry(p_new_name, var);
Pair<StringName, BBVariable> old_entry(p_name, var);
var_list.find(old_entry)->set(new_entry);
var_map.erase(p_name);
var_map.insert(p_new_name, var);
if (parent_scope_mapping.has(p_name)) {
parent_scope_mapping[p_new_name] = parent_scope_mapping[p_name];
parent_scope_mapping.erase(p_name);
}
notify_property_list_changed();
emit_changed();
}
void BlackboardPlan::move_var(int p_index, int p_new_index) {
ERR_FAIL_INDEX(p_index, (int)var_map.size());
ERR_FAIL_INDEX(p_new_index, (int)var_map.size());
if (p_index == p_new_index) {
return;
}
List<Pair<StringName, BBVariable>>::Element *E = var_list.front();
for (int i = 0; i < p_index; i++) {
E = E->next();
}
List<Pair<StringName, BBVariable>>::Element *E2 = var_list.front();
for (int i = 0; i < p_new_index; i++) {
E2 = E2->next();
}
var_list.move_before(E, E2);
if (p_new_index > p_index) {
var_list.move_before(E2, E);
}
notify_property_list_changed();
emit_changed();
}
void BlackboardPlan::sync_with_base_plan() {
if (base.is_null()) {
return;
}
bool changed = false;
// Sync variables with the base plan.
for (const Pair<StringName, BBVariable> &p : base->var_list) {
const StringName &base_name = p.first;
const BBVariable &base_var = p.second;
if (!var_map.has(base_name)) {
add_var(base_name, base_var.duplicate());
changed = true;
continue;
}
BBVariable var = var_map[base_name];
if (!var.is_same_prop_info(base_var)) {
var.copy_prop_info(base_var);
changed = true;
}
if ((!var.is_value_changed() && var.get_value() != base_var.get_value()) ||
(var.get_value().get_type() != base_var.get_type())) {
// Reset value according to base plan.
var.set_value(base_var.get_value());
var.reset_value_changed();
changed = true;
}
}
// Erase variables that do not exist in the base plan.
List<StringName> erase_list;
for (const Pair<StringName, BBVariable> &p : var_list) {
if (!base->has_var(p.first)) {
erase_list.push_back(p.first);
changed = true;
}
}
while (erase_list.size()) {
remove_var(erase_list.front()->get());
erase_list.pop_front();
}
// Sync order of variables.
// Glossary: E - element of current plan, B - element of base plan, F - element of current plan (used for forward search).
ERR_FAIL_COND(base->var_list.size() != var_list.size());
List<Pair<StringName, BBVariable>>::Element *B = base->var_list.front();
for (List<Pair<StringName, BBVariable>>::Element *E = var_list.front(); E; E = E->next()) {
if (E->get().first != B->get().first) {
List<Pair<StringName, BBVariable>>::Element *F = E->next();
while (F) {
if (F->get().first == B->get().first) {
var_list.move_before(F, E);
E = F;
break;
}
F = F->next();
}
}
B = B->next();
}
if (changed) {
notify_property_list_changed();
emit_changed();
}
}
Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_prefetch_root, const Ref<Blackboard> &p_parent_scope, Node *p_prefetch_root_for_base_plan) {
ERR_FAIL_COND_V(p_prefetch_root == nullptr && prefetch_nodepath_vars, memnew(Blackboard));
Ref<Blackboard> bb = memnew(Blackboard);
bb->set_parent(p_parent_scope);
populate_blackboard(bb, true, p_prefetch_root, p_prefetch_root_for_base_plan);
return bb;
}
void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_prefetch_root, Node *p_prefetch_root_for_base_plan) {
ERR_FAIL_COND(p_prefetch_root == nullptr && prefetch_nodepath_vars);
ERR_FAIL_COND(p_blackboard.is_null());
for (const Pair<StringName, BBVariable> &p : var_list) {
if (p_blackboard->has_local_var(p.first) && !overwrite) {
#ifdef DEBUG_ENABLED
Variant::Type existing_type = p_blackboard->get_var(p.first).get_type();
Variant::Type planned_type = p.second.get_type();
if (existing_type != planned_type && existing_type != Variant::NIL && planned_type != Variant::NIL && !(existing_type == Variant::OBJECT && planned_type == Variant::NODE_PATH)) {
WARN_PRINT(vformat("BlackboardPlan: Not overwriting %s as it already exists in the blackboard, but it has a different type than planned (%s vs %s). File: %s",
LimboUtility::get_singleton()->decorate_var(p.first), Variant::get_type_name(existing_type), Variant::get_type_name(planned_type), get_path()));
}
#endif
continue;
}
bool is_bound = has_property_binding(p.first) || (is_derived() && get_base_plan()->has_property_binding(p.first));
bool has_mapping = parent_scope_mapping.has(p.first);
bool do_prefetch = !is_bound && !has_mapping && prefetch_nodepath_vars;
// Add a variable duplicate to the blackboard, optionally with NodePath prefetch.
BBVariable var = p.second.duplicate(true);
if (unlikely(do_prefetch && p.second.get_type() == Variant::NODE_PATH)) {
Node *prefetch_root = !p_prefetch_root_for_base_plan || !is_derived() || is_derived_var_changed(p.first) ? p_prefetch_root : p_prefetch_root_for_base_plan;
Node *n = prefetch_root->get_node_or_null(p.second.get_value());
if (n != nullptr) {
var.set_value(n);
} else {
ERR_PRINT(vformat("BlackboardPlan: Prefetch failed for variable $%s with value: %s", p.first, p.second.get_value()));
var.set_value(Variant());
}
}
p_blackboard->assign_var(p.first, var);
if (has_mapping) {
StringName target_var = parent_scope_mapping[p.first];
if (target_var != StringName()) {
ERR_CONTINUE_MSG(p_blackboard->get_parent().is_null(), vformat("BlackboardPlan: Cannot link variable %s to parent scope because the parent scope is not set.", LimboUtility::get_singleton()->decorate_var(p.first)));
p_blackboard->link_var(p.first, p_blackboard->get_parent(), target_var);
}
} else if (is_bound) {
// Bind variable to a property of a scene node.
NodePath binding_path;
Node *binding_root;
if (has_property_binding(p.first)) {
binding_path = property_bindings[p.first];
binding_root = p_prefetch_root;
} else {
binding_path = get_base_plan()->property_bindings[p.first];
binding_root = p_prefetch_root_for_base_plan;
}
ERR_CONTINUE_MSG(binding_path.get_subname_count() != 1, vformat("BlackboardPlan: Can't bind variable %s using property path that contains multiple sub-names: %s", LimboUtility::get_singleton()->decorate_var(p.first), binding_path));
NodePath node_path{ binding_path.get_concatenated_names() };
StringName prop_name = binding_path.get_subname(0);
// TODO: Implement binding for base plan as well.
Node *n = binding_root->get_node_or_null(node_path);
ERR_CONTINUE_MSG(n == nullptr, vformat("BlackboardPlan: Binding failed for variable %s using property path: %s", LimboUtility::get_singleton()->decorate_var(p.first), binding_path));
var.bind(n, prop_name);
}
}
}
void BlackboardPlan::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_prefetch_nodepath_vars", "enable"), &BlackboardPlan::set_prefetch_nodepath_vars);
ClassDB::bind_method(D_METHOD("is_prefetching_nodepath_vars"), &BlackboardPlan::is_prefetching_nodepath_vars);
ClassDB::bind_method(D_METHOD("set_base_plan", "blackboard_plan"), &BlackboardPlan::set_base_plan);
ClassDB::bind_method(D_METHOD("get_base_plan"), &BlackboardPlan::get_base_plan);
ClassDB::bind_method(D_METHOD("is_derived"), &BlackboardPlan::is_derived);
ClassDB::bind_method(D_METHOD("sync_with_base_plan"), &BlackboardPlan::sync_with_base_plan);
ClassDB::bind_method(D_METHOD("set_parent_scope_plan_provider", "callable"), &BlackboardPlan::set_parent_scope_plan_provider);
ClassDB::bind_method(D_METHOD("get_parent_scope_plan_provider"), &BlackboardPlan::get_parent_scope_plan_provider);
ClassDB::bind_method(D_METHOD("create_blackboard", "prefetch_root", "parent_scope", "prefetch_root_for_base_plan"), &BlackboardPlan::create_blackboard, DEFVAL(Ref<Blackboard>()), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "prefetch_root", "prefetch_root_for_base_plan"), &BlackboardPlan::populate_blackboard, DEFVAL(Variant()));
// To avoid cluttering the member namespace, we do not export unnecessary properties in this class.
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefetch_nodepath_vars", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_prefetch_nodepath_vars", "is_prefetching_nodepath_vars");
}
BlackboardPlan::BlackboardPlan() {
}

View File

@ -1,111 +0,0 @@
/**
* blackboard_plan.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#ifndef BLACKBOARD_PLAN_H
#define BLACKBOARD_PLAN_H
#include "bb_variable.h"
#include "blackboard.h"
#ifdef LIMBOAI_MODULE
#include "core/io/resource.h"
#endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/resource.hpp>
using namespace godot;
#endif // LIMBOAI_GDEXTENSION
class BlackboardPlan : public Resource {
GDCLASS(BlackboardPlan, Resource);
private:
List<Pair<StringName, BBVariable>> var_list;
HashMap<StringName, BBVariable> var_map;
// When base is not null, the plan is considered to be derived from the base plan.
// A derived plan can only have variables that exist in the base plan,
// and only the values can be different in those variables.
// The derived plan is synced with the base plan to maintain consistency.
Ref<BlackboardPlan> base;
// Mapping between variables in this plan and their parent scope names.
// Used for linking variables to their parent scope counterparts upon Blackboard creation/population.
HashMap<StringName, StringName> parent_scope_mapping;
// Fetcher function for the parent scope plan. Function should return a Ref<BlackboardPlan>.
// Used in the inspector: enables mapping feature when set.
Callable parent_scope_plan_provider;
// Bindings to properties in the scene to which this plan belongs.
HashMap<StringName, NodePath> property_bindings;
bool property_binding_enabled = false;
// If true, NodePath variables will be prefetched, so that the vars will contain node pointers instead (upon BB creation/population).
bool prefetch_nodepath_vars = true;
_FORCE_INLINE_ bool _is_var_nil(const BBVariable &p_var) const { return p_var.get_type() == Variant::NIL; }
_FORCE_INLINE_ bool _is_var_private(const String &p_name, const BBVariable &p_var) const { return is_derived() && p_name.begins_with("_"); }
protected:
static void _bind_methods();
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
bool _property_can_revert(const StringName &p_name) const;
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
#ifdef LIMBOAI_GDEXTENSION
String _to_string() const { return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
public:
void set_base_plan(const Ref<BlackboardPlan> &p_base);
Ref<BlackboardPlan> get_base_plan() const { return base; }
void set_parent_scope_plan_provider(const Callable &p_parent_scope_plan_provider);
Callable get_parent_scope_plan_provider() const { return parent_scope_plan_provider; }
bool is_mapping_enabled() const { return parent_scope_plan_provider.is_valid() && (parent_scope_plan_provider.call() != Ref<BlackboardPlan>()); }
bool has_mapping(const StringName &p_name) const;
bool has_property_binding(const StringName &p_name) const { return property_bindings.has(p_name); }
void set_property_binding(const StringName &p_name, const NodePath &p_path);
NodePath get_property_binding(const StringName &p_name) const { return property_bindings.has(p_name) ? property_bindings[p_name] : NodePath(); }
void set_prefetch_nodepath_vars(bool p_enable);
bool is_prefetching_nodepath_vars() const;
void add_var(const StringName &p_name, const BBVariable &p_var);
void remove_var(const StringName &p_name);
BBVariable get_var(const StringName &p_name);
Pair<StringName, BBVariable> get_var_by_index(int p_index);
_FORCE_INLINE_ bool has_var(const StringName &p_name) { return var_map.has(p_name); }
_FORCE_INLINE_ bool is_empty() const { return var_map.is_empty(); }
int get_var_count() const { return var_map.size(); }
TypedArray<StringName> list_vars() const;
StringName get_var_name(const BBVariable &p_var) const;
bool is_valid_var_name(const StringName &p_name) const;
void rename_var(const StringName &p_name, const StringName &p_new_name);
void move_var(int p_index, int p_new_index);
void sync_with_base_plan();
_FORCE_INLINE_ bool is_derived() const { return base.is_valid(); }
_FORCE_INLINE_ bool is_derived_var_changed(const StringName &p_name) const { return base.is_valid() && var_map.has(p_name) && var_map[p_name].is_value_changed(); }
Ref<Blackboard> create_blackboard(Node *p_prefetch_root, const Ref<Blackboard> &p_parent_scope = Ref<Blackboard>(), Node *p_prefetch_root_for_base_plan = nullptr);
void populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_prefetch_root, Node *p_prefetch_root_for_base_plan = nullptr);
BlackboardPlan();
};
#endif // BLACKBOARD_PLAN_H

View File

@ -1,7 +1,7 @@
/** /**
* behavior_tree.cpp * behavior_tree.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -11,8 +11,6 @@
#include "behavior_tree.h" #include "behavior_tree.h"
#include "../util/limbo_string_names.h"
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
#include "core/error/error_macros.h" #include "core/error/error_macros.h"
#include "core/object/class_db.h" #include "core/object/class_db.h"
@ -24,44 +22,6 @@
#include "godot_cpp/core/error_macros.hpp" #include "godot_cpp/core/error_macros.hpp"
#endif // ! LIMBOAI_GDEXTENSION #endif // ! LIMBOAI_GDEXTENSION
void BehaviorTree::set_description(const String &p_value) {
description = p_value;
emit_changed();
}
void BehaviorTree::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
if (blackboard_plan == p_plan) {
return;
}
if (Engine::get_singleton()->is_editor_hint() && blackboard_plan.is_valid() &&
blackboard_plan->is_connected(LW_NAME(changed), callable_mp(this, &BehaviorTree::_plan_changed))) {
blackboard_plan->disconnect(LW_NAME(changed), callable_mp(this, &BehaviorTree::_plan_changed));
}
blackboard_plan = p_plan;
if (blackboard_plan.is_null()) {
blackboard_plan = Ref<BlackboardPlan>(memnew(BlackboardPlan));
}
if (Engine::get_singleton()->is_editor_hint()) {
blackboard_plan->connect(LW_NAME(changed), callable_mp(this, &BehaviorTree::_plan_changed));
}
_plan_changed();
}
void BehaviorTree::set_root_task(const Ref<BTTask> &p_value) {
#ifdef TOOLS_ENABLED
_unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
root_task = p_value;
#ifdef TOOLS_ENABLED
_set_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
emit_changed();
}
Ref<BehaviorTree> BehaviorTree::clone() const { Ref<BehaviorTree> BehaviorTree::clone() const {
Ref<BehaviorTree> copy = duplicate(false); Ref<BehaviorTree> copy = duplicate(false);
copy->set_path(""); copy->set_path("");
@ -77,63 +37,22 @@ void BehaviorTree::copy_other(const Ref<BehaviorTree> &p_other) {
root_task = p_other->get_root_task(); root_task = p_other->get_root_task();
} }
Ref<BTInstance> BehaviorTree::instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_instance_owner, Node *p_custom_scene_root) const { Ref<BTTask> BehaviorTree::instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard) const {
ERR_FAIL_COND_V_MSG(root_task.is_null(), nullptr, "BehaviorTree: Instantiation failed - BT has no valid root task."); ERR_FAIL_COND_V_MSG(root_task == nullptr, memnew(BTTask), "Trying to instance a behavior tree with no valid root task.");
ERR_FAIL_NULL_V_MSG(p_agent, nullptr, "BehaviorTree: Instantiation failed - agent can't be null."); Ref<BTTask> inst = root_task->clone();
ERR_FAIL_NULL_V_MSG(p_instance_owner, nullptr, "BehaviorTree: Instantiation failed -- instance owner can't be null."); inst->initialize(p_agent, p_blackboard);
ERR_FAIL_COND_V_MSG(p_blackboard.is_null(), nullptr, "BehaviorTree: Instantiation failed - blackboard can't be null."); return inst;
Node *scene_root = p_custom_scene_root ? p_custom_scene_root : p_instance_owner->get_owner();
ERR_FAIL_NULL_V_MSG(scene_root, nullptr, "BehaviorTree: Instantiation failed - unable to establish scene root. This is likely due to the instance owner not being owned by a scene node and custom_scene_root being null.");
Ref<BTTask> root_copy = root_task->clone();
root_copy->initialize(p_agent, p_blackboard, scene_root);
return BTInstance::create(root_copy, get_path(), p_instance_owner);
} }
void BehaviorTree::_plan_changed() {
emit_signal(LW_NAME(plan_changed));
emit_changed();
}
#ifdef TOOLS_ENABLED
void BehaviorTree::_set_editor_behavior_tree_hint() {
if (root_task.is_valid()) {
root_task->data.behavior_tree_id = this->get_instance_id();
}
}
void BehaviorTree::_unset_editor_behavior_tree_hint() {
if (root_task.is_valid()) {
root_task->data.behavior_tree_id = ObjectID();
}
}
#endif // TOOLS_ENABLED
void BehaviorTree::_bind_methods() { void BehaviorTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_description", "description"), &BehaviorTree::set_description); ClassDB::bind_method(D_METHOD("set_description", "p_value"), &BehaviorTree::set_description);
ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description); ClassDB::bind_method(D_METHOD("get_description"), &BehaviorTree::get_description);
ClassDB::bind_method(D_METHOD("set_blackboard_plan", "plan"), &BehaviorTree::set_blackboard_plan); ClassDB::bind_method(D_METHOD("set_root_task", "p_value"), &BehaviorTree::set_root_task);
ClassDB::bind_method(D_METHOD("get_blackboard_plan"), &BehaviorTree::get_blackboard_plan);
ClassDB::bind_method(D_METHOD("set_root_task", "task"), &BehaviorTree::set_root_task);
ClassDB::bind_method(D_METHOD("get_root_task"), &BehaviorTree::get_root_task); ClassDB::bind_method(D_METHOD("get_root_task"), &BehaviorTree::get_root_task);
ClassDB::bind_method(D_METHOD("clone"), &BehaviorTree::clone); ClassDB::bind_method(D_METHOD("clone"), &BehaviorTree::clone);
ClassDB::bind_method(D_METHOD("copy_other", "other"), &BehaviorTree::copy_other); ClassDB::bind_method(D_METHOD("copy_other", "p_other"), &BehaviorTree::copy_other);
ClassDB::bind_method(D_METHOD("instantiate", "agent", "blackboard", "instance_owner", "custom_scene_root"), &BehaviorTree::instantiate, DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("instantiate", "p_agent", "p_blackboard"), &BehaviorTree::instantiate);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "description", PROPERTY_HINT_MULTILINE_TEXT), "set_description", "get_description");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT), "set_blackboard_plan", "get_blackboard_plan");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_root_task", "get_root_task"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_task", PROPERTY_HINT_RESOURCE_TYPE, "BTTask", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_root_task", "get_root_task");
ADD_SIGNAL(MethodInfo("plan_changed"));
}
BehaviorTree::BehaviorTree() {
}
BehaviorTree::~BehaviorTree() {
if (Engine::get_singleton()->is_editor_hint() && blackboard_plan.is_valid() &&
blackboard_plan->is_connected(LW_NAME(changed), callable_mp(this, &BehaviorTree::_plan_changed))) {
blackboard_plan->disconnect(LW_NAME(changed), callable_mp(this, &BehaviorTree::_plan_changed));
}
} }

View File

@ -1,7 +1,7 @@
/** /**
* behavior_tree.h * behavior_tree.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -12,12 +12,11 @@
#ifndef BEHAVIOR_TREE_H #ifndef BEHAVIOR_TREE_H
#define BEHAVIOR_TREE_H #define BEHAVIOR_TREE_H
#include "../blackboard/blackboard_plan.h"
#include "bt_instance.h"
#include "tasks/bt_task.h" #include "tasks/bt_task.h"
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
#include "core/io/resource.h" #include "core/io/resource.h"
#include "modules/limboai/blackboard/blackboard.h"
#endif // LIMBOAI_MODULE #endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION #ifdef LIMBOAI_GDEXTENSION
@ -30,43 +29,31 @@ class BehaviorTree : public Resource {
private: private:
String description; String description;
Ref<BlackboardPlan> blackboard_plan;
Ref<BTTask> root_task; Ref<BTTask> root_task;
void _plan_changed();
#ifdef TOOLS_ENABLED
void _set_editor_behavior_tree_hint();
void _unset_editor_behavior_tree_hint();
#endif // TOOLS_ENABLED
protected: protected:
static void _bind_methods(); static void _bind_methods();
#ifdef LIMBOAI_GDEXTENSION
String _to_string() const { return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
public: public:
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
virtual bool editor_can_reload_from_file() override { return false; } virtual bool editor_can_reload_from_file() override { return false; }
#endif #endif
void set_description(const String &p_value); void set_description(String p_value) {
description = p_value;
emit_changed();
}
String get_description() const { return description; } String get_description() const { return description; }
void set_blackboard_plan(const Ref<BlackboardPlan> &p_plan); void set_root_task(const Ref<BTTask> &p_value) {
Ref<BlackboardPlan> get_blackboard_plan() const { return blackboard_plan; } root_task = p_value;
emit_changed();
void set_root_task(const Ref<BTTask> &p_value); }
Ref<BTTask> get_root_task() const { return root_task; } Ref<BTTask> get_root_task() const { return root_task; }
Ref<BehaviorTree> clone() const; Ref<BehaviorTree> clone() const;
void copy_other(const Ref<BehaviorTree> &p_other); void copy_other(const Ref<BehaviorTree> &p_other);
Ref<BTInstance> instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_instance_owner, Node *p_custom_scene_root = nullptr) const; Ref<BTTask> instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard) const;
BehaviorTree();
~BehaviorTree();
}; };
#endif // BEHAVIOR_TREE_H #endif // BEHAVIOR_TREE_H

View File

@ -1,156 +0,0 @@
/**
* bt_instance.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#include "bt_instance.h"
#include "../editor/debugger/limbo_debugger.h"
#include "behavior_tree.h"
#ifdef LIMBOAI_MODULE
#include "core/os/time.h"
#include "main/performance.h"
#endif
#ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/performance.hpp>
#include <godot_cpp/classes/time.hpp>
#endif
Ref<BTInstance> BTInstance::create(Ref<BTTask> p_root_task, String p_source_bt_path, Node *p_owner_node) {
ERR_FAIL_COND_V(p_root_task.is_null(), nullptr);
ERR_FAIL_NULL_V(p_owner_node, nullptr);
Ref<BTInstance> inst;
inst.instantiate();
inst->root_task = p_root_task;
inst->owner_node_id = p_owner_node->get_instance_id();
inst->source_bt_path = p_source_bt_path;
return inst;
}
BT::Status BTInstance::update(double p_delta) {
ERR_FAIL_COND_V(!root_task.is_valid(), BT::FRESH);
#ifdef DEBUG_ENABLED
double start = Time::get_singleton()->get_ticks_usec();
#endif
const Ref<BTInstance> keep_alive{ this }; // keep instance alive until update is finished
last_status = root_task->execute(p_delta);
emit_signal(LW_NAME(updated), last_status);
#ifdef DEBUG_ENABLED
double end = Time::get_singleton()->get_ticks_usec();
update_time_acc += (end - start);
update_time_n += 1.0;
#endif
return last_status;
}
void BTInstance::set_monitor_performance(bool p_monitor) {
#ifdef DEBUG_ENABLED
monitor_performance = p_monitor;
if (monitor_performance) {
_add_custom_monitor();
} else {
_remove_custom_monitor();
}
#endif
}
bool BTInstance::get_monitor_performance() const {
#ifdef DEBUG_ENABLED
return monitor_performance;
#else
return false;
#endif
}
void BTInstance::register_with_debugger() {
#ifdef DEBUG_ENABLED
if (LimboDebugger::get_singleton()->is_active()) {
LimboDebugger::get_singleton()->register_bt_instance(get_instance_id());
}
#endif
}
void BTInstance::unregister_with_debugger() {
#ifdef DEBUG_ENABLED
if (LimboDebugger::get_singleton() && LimboDebugger::get_singleton()->is_active()) {
LimboDebugger::get_singleton()->unregister_bt_instance(get_instance_id());
}
#endif
}
#ifdef DEBUG_ENABLED
double BTInstance::_get_mean_update_time_msec_and_reset() {
if (update_time_n) {
double mean_time_msec = (update_time_acc * 0.001) / update_time_n;
update_time_acc = 0.0;
update_time_n = 0.0;
return mean_time_msec;
}
return 0.0;
}
void BTInstance::_add_custom_monitor() {
ERR_FAIL_NULL(get_owner_node());
ERR_FAIL_COND(root_task.is_null());
ERR_FAIL_NULL(root_task->get_agent());
if (monitor_id == StringName()) {
monitor_id = vformat("LimboAI/update_ms|%s_%s_%s", root_task->get_agent()->get_name(), get_owner_node()->get_name(),
String(itos(get_instance_id())).md5_text().substr(0, 4));
}
if (!Performance::get_singleton()->has_custom_monitor(monitor_id)) {
PERFORMANCE_ADD_CUSTOM_MONITOR(monitor_id, callable_mp(this, &BTInstance::_get_mean_update_time_msec_and_reset));
}
}
void BTInstance::_remove_custom_monitor() {
if (monitor_id != StringName() && Performance::get_singleton()->has_custom_monitor(monitor_id)) {
Performance::get_singleton()->remove_custom_monitor(monitor_id);
}
}
#endif // * DEBUG_ENABLED
void BTInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_root_task"), &BTInstance::get_root_task);
ClassDB::bind_method(D_METHOD("get_owner_node"), &BTInstance::get_owner_node);
ClassDB::bind_method(D_METHOD("get_last_status"), &BTInstance::get_last_status);
ClassDB::bind_method(D_METHOD("get_source_bt_path"), &BTInstance::get_source_bt_path);
ClassDB::bind_method(D_METHOD("get_agent"), &BTInstance::get_agent);
ClassDB::bind_method(D_METHOD("get_blackboard"), &BTInstance::get_blackboard);
ClassDB::bind_method(D_METHOD("is_instance_valid"), &BTInstance::is_instance_valid);
ClassDB::bind_method(D_METHOD("set_monitor_performance", "monitor"), &BTInstance::set_monitor_performance);
ClassDB::bind_method(D_METHOD("get_monitor_performance"), &BTInstance::get_monitor_performance);
ClassDB::bind_method(D_METHOD("update", "delta"), &BTInstance::update);
ClassDB::bind_method(D_METHOD("register_with_debugger"), &BTInstance::register_with_debugger);
ClassDB::bind_method(D_METHOD("unregister_with_debugger"), &BTInstance::unregister_with_debugger);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitor_performance"), "set_monitor_performance", "get_monitor_performance");
ADD_SIGNAL(MethodInfo("updated", PropertyInfo(Variant::INT, "status")));
ADD_SIGNAL(MethodInfo("freed"));
}
BTInstance::~BTInstance() {
emit_signal(LW_NAME(freed));
#ifdef DEBUG_ENABLED
_remove_custom_monitor();
unregister_with_debugger();
#endif
}

View File

@ -1,68 +0,0 @@
/**
* bt_instance.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* =============================================================================
*/
#ifndef BT_INSTANCE_H
#define BT_INSTANCE_H
#include "tasks/bt_task.h"
class BTInstance : public RefCounted {
GDCLASS(BTInstance, RefCounted);
private:
Ref<BTTask> root_task;
uint64_t owner_node_id = 0;
String source_bt_path;
BT::Status last_status = BT::FRESH;
#ifdef DEBUG_ENABLED
bool monitor_performance = false;
StringName monitor_id;
double update_time_acc = 0.0;
double update_time_n = 0.0;
double _get_mean_update_time_msec_and_reset();
void _add_custom_monitor();
void _remove_custom_monitor();
#endif // * DEBUG_ENABLED
protected:
static void _bind_methods();
#ifdef LIMBOAI_GDEXTENSION
String _to_string() const { return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
public:
_FORCE_INLINE_ Ref<BTTask> get_root_task() const { return root_task; }
_FORCE_INLINE_ Node *get_owner_node() const { return owner_node_id ? Object::cast_to<Node>(OBJECT_DB_GET_INSTANCE(owner_node_id)) : nullptr; }
_FORCE_INLINE_ BT::Status get_last_status() const { return last_status; }
_FORCE_INLINE_ String get_source_bt_path() const { return source_bt_path; }
_FORCE_INLINE_ Node *get_agent() const { return root_task.is_valid() ? root_task->get_agent() : nullptr; }
_FORCE_INLINE_ Ref<Blackboard> get_blackboard() const { return root_task.is_valid() ? root_task->get_blackboard() : Ref<Blackboard>(); }
_FORCE_INLINE_ bool is_instance_valid() const { return root_task.is_valid(); }
BT::Status update(double p_delta);
void set_monitor_performance(bool p_monitor);
bool get_monitor_performance() const;
void register_with_debugger();
void unregister_with_debugger();
static Ref<BTInstance> create(Ref<BTTask> p_root_task, String p_source_bt_path, Node *p_owner_node);
BTInstance() = default;
~BTInstance();
};
#endif // BT_INSTANCE_H

View File

@ -1,7 +1,7 @@
/** /**
* bt_player.cpp * bt_player.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -11,6 +11,7 @@
#include "bt_player.h" #include "bt_player.h"
#include "../editor/debugger/limbo_debugger.h"
#include "../util/limbo_compat.h" #include "../util/limbo_compat.h"
#include "../util/limbo_string_names.h" #include "../util/limbo_string_names.h"
@ -42,102 +43,31 @@
VARIANT_ENUM_CAST(BTPlayer::UpdateMode); VARIANT_ENUM_CAST(BTPlayer::UpdateMode);
void BTPlayer::_instantiate_bt() { void BTPlayer::_load_tree() {
bt_instance.unref();
ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer: Initialization failed - needs a valid behavior tree.");
ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Initialization failed - behavior tree has no valid root task.");
Node *agent = GET_NODE(this, agent_node);
ERR_FAIL_NULL_MSG(agent, vformat("BTPlayer: Initialization failed - can't get agent with path '%s'.", agent_node));
Node *scene_root = _get_scene_root();
ERR_FAIL_COND_MSG(scene_root == nullptr,
"BTPlayer: Initialization failed - unable to establish scene root. This is likely due to BTPlayer not being owned by a scene node. Check BTPlayer.set_scene_root_hint().");
bt_instance = behavior_tree->instantiate(agent, blackboard, this, scene_root);
ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTPlayer: Failed to instantiate behavior tree.");
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
bt_instance->set_monitor_performance(monitor_performance); if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
bt_instance->register_with_debugger(); LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path());
#endif // DEBUG_ENABLED
}
void BTPlayer::_update_blackboard_plan() {
if (blackboard_plan.is_null()) {
blackboard_plan = Ref<BlackboardPlan>(memnew(BlackboardPlan));
} else if (!RESOURCE_IS_BUILT_IN(blackboard_plan)) {
WARN_PRINT_ED("BTPlayer: Using external resource for derived blackboard plan is not supported. Converted to built-in resource.");
blackboard_plan = blackboard_plan->duplicate();
} }
#endif
blackboard_plan->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr); tree_instance.unref();
} ERR_FAIL_COND_MSG(!behavior_tree.is_valid(), "BTPlayer: Needs a valid behavior tree.");
ERR_FAIL_COND_MSG(!behavior_tree->get_root_task().is_valid(), "BTPlayer: Behavior tree has no valid root task.");
void BTPlayer::_initialize() { if (prefetch_nodepath_vars == true) {
if (blackboard.is_null()) { blackboard->prefetch_nodepath_vars(this);
blackboard = Ref<Blackboard>(memnew(Blackboard));
} }
if (blackboard_plan.is_valid()) { tree_instance = behavior_tree->instantiate(get_owner(), blackboard);
// Don't overwrite existing blackboard values as they may be initialized from code.
blackboard_plan->populate_blackboard(blackboard, false, this, _get_scene_root());
}
if (behavior_tree.is_valid()) {
_instantiate_bt();
}
}
void BTPlayer::set_bt_instance(const Ref<BTInstance> &p_bt_instance) {
ERR_FAIL_COND_MSG(p_bt_instance.is_null(), "BTPlayer: Failed to set behavior tree instance - instance is null.");
ERR_FAIL_COND_MSG(!p_bt_instance->is_instance_valid(), "BTPlayer: Failed to set behavior tree instance - instance is not valid.");
bt_instance = p_bt_instance;
blackboard = p_bt_instance->get_blackboard();
agent_node = p_bt_instance->get_agent()->get_path();
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
bt_instance->set_monitor_performance(monitor_performance); if (IS_DEBUGGER_ACTIVE()) {
bt_instance->register_with_debugger(); LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
#endif // DEBUG_ENABLED
blackboard_plan.unref();
behavior_tree.unref();
}
void BTPlayer::set_scene_root_hint(Node *p_scene_root) {
ERR_FAIL_NULL_MSG(p_scene_root, "BTPlayer: Failed to set scene root hint - scene root is null.");
if (bt_instance.is_valid()) {
ERR_PRINT("BTPlayer: Scene root hint shouldn't be set after the behavior tree is instantiated. This change will not affect the current behavior tree instance.");
} }
#endif
scene_root_hint = p_scene_root;
} }
void BTPlayer::set_behavior_tree(const Ref<BehaviorTree> &p_tree) { void BTPlayer::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
if (Engine::get_singleton()->is_editor_hint()) {
if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) {
behavior_tree->disconnect(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan));
}
if (p_tree.is_valid()) {
p_tree->connect(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan));
}
behavior_tree = p_tree; behavior_tree = p_tree;
_update_blackboard_plan(); if (Engine::get_singleton()->is_editor_hint() == false && get_owner()) {
} else { _load_tree();
behavior_tree = p_tree;
if (get_owner() && is_inside_tree()) {
_update_blackboard_plan();
_initialize();
} }
}
}
void BTPlayer::set_agent_node(const NodePath &p_agent_node) {
agent_node = p_agent_node;
if (bt_instance.is_valid()) {
ERR_PRINT("BTPlayer: Agent node cannot be set after the behavior tree is instantiated. This change will not affect the behavior tree instance.");
}
}
void BTPlayer::set_blackboard_plan(const Ref<BlackboardPlan> &p_plan) {
blackboard_plan = p_plan;
_update_blackboard_plan();
} }
void BTPlayer::set_update_mode(UpdateMode p_mode) { void BTPlayer::set_update_mode(UpdateMode p_mode) {
@ -154,38 +84,70 @@ void BTPlayer::set_active(bool p_active) {
} }
void BTPlayer::update(double p_delta) { void BTPlayer::update(double p_delta) {
if (!bt_instance.is_valid()) { if (!tree_instance.is_valid()) {
ERR_PRINT_ONCE(vformat("BTPlayer doesn't have a behavior tree with a valid root task to execute (owner: %s)", get_owner())); ERR_PRINT_ONCE(vformat("BTPlayer doesn't have a behavior tree with a valid root task to execute (owner: %s)", get_owner()));
return; return;
} }
#ifdef DEBUG_ENABLED
double start = GET_TICKS_USEC();
#endif
if (active) { if (active) {
BT::Status status = bt_instance->update(p_delta); last_status = tree_instance->execute(p_delta);
emit_signal(LW_NAME(updated), status); emit_signal(LimboStringNames::get_singleton()->updated, last_status);
#ifndef DISABLE_DEPRECATED if (last_status == BTTask::SUCCESS || last_status == BTTask::FAILURE) {
if (status == BTTask::SUCCESS || status == BTTask::FAILURE) { emit_signal(LimboStringNames::get_singleton()->behavior_tree_finished, last_status);
emit_signal(LW_NAME(behavior_tree_finished), status);
} }
#endif // DISABLE_DEPRECATED
} }
#ifdef DEBUG_ENABLED
double end = GET_TICKS_USEC();
update_time_acc += (end - start);
update_time_n += 1.0;
#endif
} }
void BTPlayer::restart() { void BTPlayer::restart() {
ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTPlayer: Restart failed - no valid tree instance. Make sure the BTPlayer has a valid behavior tree with a valid root task."); tree_instance->abort();
bt_instance->get_root_task()->abort();
set_active(true); set_active(true);
} }
void BTPlayer::set_monitor_performance(bool p_monitor_performance) { #ifdef DEBUG_ENABLED
void BTPlayer::_set_monitor_performance(bool p_monitor_performance) {
monitor_performance = p_monitor_performance; monitor_performance = p_monitor_performance;
#ifdef DEBUG_ENABLED if (!get_owner()) {
if (bt_instance.is_valid()) { return;
bt_instance->set_monitor_performance(monitor_performance); }
Performance *perf = Performance::get_singleton();
if (monitor_performance) {
if (monitor_id == StringName()) {
monitor_id = vformat("limboai/update_ms|%s_%s_%s", get_owner()->get_name(), get_name(),
String(itos(get_instance_id())).md5_text().substr(0, 4));
}
if (!perf->has_custom_monitor(monitor_id)) {
PERFORMANCE_ADD_CUSTOM_MONITOR(monitor_id, callable_mp(this, &BTPlayer::_get_mean_update_time_msec));
}
} else if (monitor_id != StringName() && perf->has_custom_monitor(monitor_id)) {
perf->remove_custom_monitor(monitor_id);
} }
#endif
} }
double BTPlayer::_get_mean_update_time_msec() {
if (update_time_n) {
double mean_time_msec = (update_time_acc * 0.001) / update_time_n;
update_time_acc = 0.0;
update_time_n = 0.0;
return mean_time_msec;
}
return 0.0;
}
#endif // ! DEBUG_ENABLED
void BTPlayer::_notification(int p_notification) { void BTPlayer::_notification(int p_notification) {
switch (p_notification) { switch (p_notification) {
case NOTIFICATION_PROCESS: { case NOTIFICATION_PROCESS: {
@ -198,85 +160,72 @@ void BTPlayer::_notification(int p_notification) {
} break; } break;
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
if (!Engine::get_singleton()->is_editor_hint()) { if (!Engine::get_singleton()->is_editor_hint()) {
_initialize(); if (behavior_tree.is_valid()) {
} else { _load_tree();
_update_blackboard_plan();
} }
set_active(active); set_active(active);
} break;
case NOTIFICATION_ENTER_TREE: {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (bt_instance.is_valid()) { _set_monitor_performance(monitor_performance);
bt_instance->set_monitor_performance(monitor_performance); #endif
bt_instance->register_with_debugger(); }
} break;
#ifdef DEBUG_ENABLED
case NOTIFICATION_ENTER_TREE: {
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
} }
#endif // DEBUG_ENABLED
} break; } break;
case NOTIFICATION_EXIT_TREE: { case NOTIFICATION_EXIT_TREE: {
#ifdef DEBUG_ENABLED if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
if (bt_instance.is_valid()) { LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path());
bt_instance->set_monitor_performance(false);
bt_instance->unregister_with_debugger();
}
#endif // DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan))) {
behavior_tree->disconnect(LW_NAME(plan_changed), callable_mp(this, &BTPlayer::_update_blackboard_plan));
}
} }
} break; } break;
#endif // DEBUG_ENABLED
} }
} }
void BTPlayer::_bind_methods() { void BTPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_behavior_tree", "behavior_tree"), &BTPlayer::set_behavior_tree); ClassDB::bind_method(D_METHOD("set_behavior_tree", "p_path"), &BTPlayer::set_behavior_tree);
ClassDB::bind_method(D_METHOD("get_behavior_tree"), &BTPlayer::get_behavior_tree); ClassDB::bind_method(D_METHOD("get_behavior_tree"), &BTPlayer::get_behavior_tree);
ClassDB::bind_method(D_METHOD("set_agent_node", "agent_node"), &BTPlayer::set_agent_node); ClassDB::bind_method(D_METHOD("set_update_mode", "p_mode"), &BTPlayer::set_update_mode);
ClassDB::bind_method(D_METHOD("get_agent_node"), &BTPlayer::get_agent_node);
ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &BTPlayer::set_update_mode);
ClassDB::bind_method(D_METHOD("get_update_mode"), &BTPlayer::get_update_mode); ClassDB::bind_method(D_METHOD("get_update_mode"), &BTPlayer::get_update_mode);
ClassDB::bind_method(D_METHOD("set_active", "active"), &BTPlayer::set_active); ClassDB::bind_method(D_METHOD("set_active", "p_active"), &BTPlayer::set_active);
ClassDB::bind_method(D_METHOD("get_active"), &BTPlayer::get_active); ClassDB::bind_method(D_METHOD("get_active"), &BTPlayer::get_active);
ClassDB::bind_method(D_METHOD("set_blackboard", "blackboard"), &BTPlayer::set_blackboard); ClassDB::bind_method(D_METHOD("set_blackboard", "p_blackboard"), &BTPlayer::set_blackboard);
ClassDB::bind_method(D_METHOD("get_blackboard"), &BTPlayer::get_blackboard); ClassDB::bind_method(D_METHOD("get_blackboard"), &BTPlayer::get_blackboard);
ClassDB::bind_method(D_METHOD("set_prefetch_nodepath_vars", "p_value"), &BTPlayer::set_prefetch_nodepath_vars);
ClassDB::bind_method(D_METHOD("get_prefetch_nodepath_vars"), &BTPlayer::get_prefetch_nodepath_vars);
ClassDB::bind_method(D_METHOD("set_blackboard_plan", "plan"), &BTPlayer::set_blackboard_plan); ClassDB::bind_method(D_METHOD("_set_blackboard_data", "p_blackboard"), &BTPlayer::_set_blackboard_data);
ClassDB::bind_method(D_METHOD("get_blackboard_plan"), &BTPlayer::get_blackboard_plan); ClassDB::bind_method(D_METHOD("_get_blackboard_data"), &BTPlayer::_get_blackboard_data);
ClassDB::bind_method(D_METHOD("set_monitor_performance", "enable"), &BTPlayer::set_monitor_performance); ClassDB::bind_method(D_METHOD("update", "p_delta"), &BTPlayer::update);
ClassDB::bind_method(D_METHOD("get_monitor_performance"), &BTPlayer::get_monitor_performance);
ClassDB::bind_method(D_METHOD("update", "delta"), &BTPlayer::update);
ClassDB::bind_method(D_METHOD("restart"), &BTPlayer::restart); ClassDB::bind_method(D_METHOD("restart"), &BTPlayer::restart);
ClassDB::bind_method(D_METHOD("get_last_status"), &BTPlayer::get_last_status);
ClassDB::bind_method(D_METHOD("get_bt_instance"), &BTPlayer::get_bt_instance);
ClassDB::bind_method(D_METHOD("set_bt_instance", "bt_instance"), &BTPlayer::set_bt_instance);
ClassDB::bind_method(D_METHOD("set_scene_root_hint", "scene_root"), &BTPlayer::set_scene_root_hint);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "agent_node"), "set_agent_node", "get_agent_node");
ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Idle,Physics,Manual"), "set_update_mode", "get_update_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "get_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "get_active");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_NONE, "Blackboard", 0), "set_blackboard", "get_blackboard"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_NONE, "Blackboard", 0), "set_blackboard", "get_blackboard");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard_plan", PROPERTY_HINT_RESOURCE_TYPE, "BlackboardPlan", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_blackboard_plan", "get_blackboard_plan"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_blackboard_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_blackboard_data", "_get_blackboard_data");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitor_performance"), "set_monitor_performance", "get_monitor_performance"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "prefetch_nodepath_vars"), "set_prefetch_nodepath_vars", "get_prefetch_nodepath_vars");
BIND_ENUM_CONSTANT(IDLE); BIND_ENUM_CONSTANT(IDLE);
BIND_ENUM_CONSTANT(PHYSICS); BIND_ENUM_CONSTANT(PHYSICS);
BIND_ENUM_CONSTANT(MANUAL); BIND_ENUM_CONSTANT(MANUAL);
ADD_SIGNAL(MethodInfo("updated", PropertyInfo(Variant::INT, "status"))); ADD_SIGNAL(MethodInfo("behavior_tree_finished", PropertyInfo(Variant::INT, "p_status")));
ADD_SIGNAL(MethodInfo("updated", PropertyInfo(Variant::INT, "p_status")));
#ifndef DISABLE_DEPRECATED #ifdef DEBUG_ENABLED
ADD_SIGNAL(MethodInfo("behavior_tree_finished", PropertyInfo(Variant::INT, "status"))); ClassDB::bind_method(D_METHOD("_set_monitor_performance", "p_value"), &BTPlayer::_set_monitor_performance);
#endif ClassDB::bind_method(D_METHOD("_get_monitor_performance"), &BTPlayer::_get_monitor_performance);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitor_performance"), "_set_monitor_performance", "_get_monitor_performance");
#endif // DEBUG_ENABLED
} }
BTPlayer::BTPlayer() { BTPlayer::BTPlayer() {
blackboard = Ref<Blackboard>(memnew(Blackboard)); blackboard = Ref<Blackboard>(memnew(Blackboard));
agent_node = LW_NAME(node_pp);
} }
BTPlayer::~BTPlayer() { BTPlayer::~BTPlayer() {

View File

@ -1,7 +1,7 @@
/** /**
* bt_player.h * bt_player.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -13,9 +13,7 @@
#define BT_PLAYER_H #define BT_PLAYER_H
#include "../blackboard/blackboard.h" #include "../blackboard/blackboard.h"
#include "../blackboard/blackboard_plan.h"
#include "behavior_tree.h" #include "behavior_tree.h"
#include "bt_instance.h"
#include "tasks/bt_task.h" #include "tasks/bt_task.h"
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
@ -38,40 +36,28 @@ public:
private: private:
Ref<BehaviorTree> behavior_tree; Ref<BehaviorTree> behavior_tree;
NodePath agent_node;
Ref<BlackboardPlan> blackboard_plan;
UpdateMode update_mode = UpdateMode::PHYSICS; UpdateMode update_mode = UpdateMode::PHYSICS;
bool active = true; bool active = true;
Ref<Blackboard> blackboard; Ref<Blackboard> blackboard;
Node *scene_root_hint = nullptr; bool prefetch_nodepath_vars = true;
bool monitor_performance = false; int last_status = -1;
Ref<BTInstance> bt_instance; Ref<BTTask> tree_instance;
void _instantiate_bt(); void _load_tree();
void _update_blackboard_plan();
void _initialize();
_FORCE_INLINE_ Node *_get_scene_root() const { return scene_root_hint ? scene_root_hint : get_owner(); }
protected: protected:
static void _bind_methods(); static void _bind_methods();
void _notification(int p_notification); void _set_blackboard_data(Dictionary p_value) { blackboard->set_data(p_value.duplicate()); }
Dictionary _get_blackboard_data() const { return blackboard->get_data(); }
#ifdef LIMBOAI_GDEXTENSION void _notification(int p_notification);
String _to_string() const { return String(get_name()) + ":<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
public: public:
void set_behavior_tree(const Ref<BehaviorTree> &p_tree); void set_behavior_tree(const Ref<BehaviorTree> &p_tree);
Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; }; Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; };
void set_agent_node(const NodePath &p_agent_node);
NodePath get_agent_node() const { return agent_node; }
void set_blackboard_plan(const Ref<BlackboardPlan> &p_plan);
Ref<BlackboardPlan> get_blackboard_plan() const { return blackboard_plan; }
void set_update_mode(UpdateMode p_mode); void set_update_mode(UpdateMode p_mode);
UpdateMode get_update_mode() const { return update_mode; } UpdateMode get_update_mode() const { return update_mode; }
@ -81,19 +67,29 @@ public:
Ref<Blackboard> get_blackboard() const { return blackboard; } Ref<Blackboard> get_blackboard() const { return blackboard; }
void set_blackboard(const Ref<Blackboard> &p_blackboard) { blackboard = p_blackboard; } void set_blackboard(const Ref<Blackboard> &p_blackboard) { blackboard = p_blackboard; }
void set_monitor_performance(bool p_monitor_performance); void set_prefetch_nodepath_vars(bool p_value) { prefetch_nodepath_vars = p_value; }
bool get_monitor_performance() const { return monitor_performance; } bool get_prefetch_nodepath_vars() const { return prefetch_nodepath_vars; }
void update(double p_delta); void update(double p_delta);
void restart(); void restart();
int get_last_status() const { return last_status; }
Ref<BTInstance> get_bt_instance() { return bt_instance; }
void set_bt_instance(const Ref<BTInstance> &p_bt_instance);
void set_scene_root_hint(Node *p_scene_root);
BTPlayer(); BTPlayer();
~BTPlayer(); ~BTPlayer();
#ifdef DEBUG_ENABLED // Performance monitoring
private:
bool monitor_performance = false;
StringName monitor_id;
double update_time_acc = 0.0;
double update_time_n = 0.0;
void _set_monitor_performance(bool p_monitor_performance);
bool _get_monitor_performance() const { return monitor_performance; }
double _get_mean_update_time_msec();
#endif // DEBUG_ENABLED
}; };
#endif // BT_PLAYER_H #endif // BT_PLAYER_H

View File

@ -1,7 +1,7 @@
/** /**
* bt_state.cpp * bt_state.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -11,155 +11,81 @@
#include "bt_state.h" #include "bt_state.h"
#include "../editor/debugger/limbo_debugger.h"
#include "../util/limbo_compat.h" #include "../util/limbo_compat.h"
#include "../util/limbo_string_names.h" #include "../util/limbo_string_names.h"
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
#include "core/debugger/engine_debugger.h" #include "core/debugger/engine_debugger.h"
#include "core/error/error_macros.h"
#include "core/object/class_db.h"
#include "core/variant/variant.h"
#endif // LIMBOAI_MODULE #endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION #ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/engine_debugger.hpp> #include <godot_cpp/classes/engine_debugger.hpp>
#endif // LIMBOAI_GDEXTENSION #endif // LIMBOAI_GDEXTENSION
void BTState::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
if (Engine::get_singleton()->is_editor_hint()) {
if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan))) {
behavior_tree->disconnect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan));
}
if (p_tree.is_valid()) {
p_tree->connect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan));
}
behavior_tree = p_tree;
} else {
behavior_tree = p_tree;
}
_update_blackboard_plan();
}
void BTState::set_scene_root_hint(Node *p_scene_root) {
ERR_FAIL_NULL_MSG(p_scene_root, "BTState: Failed to set scene root hint - scene root is null.");
ERR_FAIL_COND_MSG(bt_instance.is_valid(), "BTState: Scene root hint shouldn't be set after initialization. This change will not affect the current behavior tree instance.");
scene_root_hint = p_scene_root;
}
void BTState::set_monitor_performance(bool p_monitor) {
monitor_performance = p_monitor;
#ifdef DEBUG_ENABLED
if (bt_instance.is_valid()) {
bt_instance->set_monitor_performance(monitor_performance);
}
#endif
}
void BTState::_update_blackboard_plan() {
if (get_blackboard_plan().is_null()) {
set_blackboard_plan(memnew(BlackboardPlan));
} else if (!RESOURCE_IS_BUILT_IN(get_blackboard_plan())) {
WARN_PRINT_ED("BTState: Using external resource for derived blackboard plan is not supported. Converted to built-in resource.");
set_blackboard_plan(get_blackboard_plan()->duplicate());
} else {
get_blackboard_plan()->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr);
}
}
Node *BTState::_get_prefetch_root_for_base_plan() {
return _get_scene_root();
}
void BTState::_setup() { void BTState::_setup() {
LimboState::_setup();
ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned."); ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned.");
Node *scene_root = _get_scene_root(); tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard());
ERR_FAIL_NULL_MSG(scene_root, "BTState: Initialization failed - unable to establish scene root. This is likely due to BTState not being owned by a scene node. Check BTState.set_scene_root_hint().");
bt_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), this, scene_root);
ERR_FAIL_COND_MSG(bt_instance.is_null(), "BTState: Initialization failed - failed to instantiate behavior tree.");
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
bt_instance->register_with_debugger(); if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
bt_instance->set_monitor_performance(monitor_performance); LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
}
#endif #endif
} }
void BTState::_exit() { void BTState::_exit() {
if (bt_instance.is_valid()) { ERR_FAIL_COND(tree_instance == nullptr);
bt_instance->get_root_task()->abort(); tree_instance->abort();
} else {
ERR_PRINT_ONCE("BTState: BehaviorTree is not assigned.");
}
LimboState::_exit();
} }
void BTState::_update(double p_delta) { void BTState::_update(double p_delta) {
GDVIRTUAL_CALL(_update, p_delta); ERR_FAIL_COND(tree_instance == nullptr);
if (!is_active()) { int status = tree_instance->execute(p_delta);
// Bail out if a transition happened in the meantime. emit_signal(LimboStringNames::get_singleton()->updated, p_delta);
return;
}
ERR_FAIL_COND(bt_instance.is_null());
BT::Status status = bt_instance->update(p_delta);
if (status == BTTask::SUCCESS) { if (status == BTTask::SUCCESS) {
get_root()->dispatch(success_event, Variant()); get_root()->dispatch(success_event, Variant());
} else if (status == BTTask::FAILURE) { } else if (status == BTTask::FAILURE) {
get_root()->dispatch(failure_event, Variant()); get_root()->dispatch(failure_event, Variant());
} }
emit_signal(LW_NAME(updated), p_delta);
} }
#ifdef DEBUG_ENABLED
void BTState::_notification(int p_notification) { void BTState::_notification(int p_notification) {
switch (p_notification) { switch (p_notification) {
#ifdef DEBUG_ENABLED
case NOTIFICATION_ENTER_TREE: { case NOTIFICATION_ENTER_TREE: {
if (bt_instance.is_valid()) { if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
bt_instance->register_with_debugger(); LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
bt_instance->set_monitor_performance(monitor_performance);
} }
} break; } break;
#endif // DEBUG_ENABLED
case NOTIFICATION_EXIT_TREE: { case NOTIFICATION_EXIT_TREE: {
#ifdef DEBUG_ENABLED if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
if (bt_instance.is_valid()) { LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path());
bt_instance->unregister_with_debugger();
bt_instance->set_monitor_performance(false);
}
#endif // DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
if (behavior_tree.is_valid() && behavior_tree->is_connected(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan))) {
behavior_tree->disconnect(LW_NAME(plan_changed), callable_mp(this, &BTState::_update_blackboard_plan));
}
} }
} break; } break;
} }
} }
#endif // DEBUG_ENABLED
void BTState::_bind_methods() { void BTState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_behavior_tree", "behavior_tree"), &BTState::set_behavior_tree); ClassDB::bind_method(D_METHOD("set_behavior_tree", "p_value"), &BTState::set_behavior_tree);
ClassDB::bind_method(D_METHOD("get_behavior_tree"), &BTState::get_behavior_tree); ClassDB::bind_method(D_METHOD("get_behavior_tree"), &BTState::get_behavior_tree);
ClassDB::bind_method(D_METHOD("get_bt_instance"), &BTState::get_bt_instance); ClassDB::bind_method(D_METHOD("set_success_event", "p_event_name"), &BTState::set_success_event);
ClassDB::bind_method(D_METHOD("set_success_event", "event"), &BTState::set_success_event);
ClassDB::bind_method(D_METHOD("get_success_event"), &BTState::get_success_event); ClassDB::bind_method(D_METHOD("get_success_event"), &BTState::get_success_event);
ClassDB::bind_method(D_METHOD("set_failure_event", "event"), &BTState::set_failure_event); ClassDB::bind_method(D_METHOD("set_failure_event", "p_event_name"), &BTState::set_failure_event);
ClassDB::bind_method(D_METHOD("get_failure_event"), &BTState::get_failure_event); ClassDB::bind_method(D_METHOD("get_failure_event"), &BTState::get_failure_event);
ClassDB::bind_method(D_METHOD("set_monitor_performance", "enable"), &BTState::set_monitor_performance);
ClassDB::bind_method(D_METHOD("get_monitor_performance"), &BTState::get_monitor_performance);
ClassDB::bind_method(D_METHOD("set_scene_root_hint", "scene_root"), &BTState::set_scene_root_hint);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "behavior_tree", PROPERTY_HINT_RESOURCE_TYPE, "BehaviorTree"), "set_behavior_tree", "get_behavior_tree");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "success_event"), "set_success_event", "get_success_event"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "success_event"), "set_success_event", "get_success_event");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "failure_event"), "set_failure_event", "get_failure_event"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "failure_event"), "set_failure_event", "get_failure_event");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitor_performance"), "set_monitor_performance", "get_monitor_performance");
} }
BTState::BTState() { BTState::BTState() {
success_event = LW_NAME(EVENT_SUCCESS); success_event = "success";
failure_event = LW_NAME(EVENT_FAILURE); failure_event = "failure";
} }

View File

@ -1,7 +1,7 @@
/** /**
* bt_state.h * bt_state.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -22,45 +22,34 @@ class BTState : public LimboState {
private: private:
Ref<BehaviorTree> behavior_tree; Ref<BehaviorTree> behavior_tree;
Ref<BTInstance> bt_instance; Ref<BTTask> tree_instance;
StringName success_event; String success_event;
StringName failure_event; String failure_event;
Node *scene_root_hint = nullptr;
bool monitor_performance = false;
_FORCE_INLINE_ Node *_get_scene_root() const { return scene_root_hint ? scene_root_hint : get_owner(); }
protected: protected:
static void _bind_methods(); static void _bind_methods();
void _notification(int p_notification);
virtual bool _should_use_new_scope() const override { return true; }
virtual void _update_blackboard_plan() override;
virtual Node *_get_prefetch_root_for_base_plan() override;
virtual void _setup() override; virtual void _setup() override;
virtual void _exit() override; virtual void _exit() override;
virtual void _update(double p_delta) override; virtual void _update(double p_delta) override;
public: public:
void set_behavior_tree(const Ref<BehaviorTree> &p_value); void set_behavior_tree(const Ref<BehaviorTree> &p_value) { behavior_tree = p_value; }
Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; } Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; }
Ref<BTInstance> get_bt_instance() const { return bt_instance; } void set_success_event(String p_success_event) { success_event = p_success_event; }
String get_success_event() const { return success_event; }
void set_success_event(const StringName &p_success_event) { success_event = p_success_event; } void set_failure_event(String p_failure_event) { failure_event = p_failure_event; }
StringName get_success_event() const { return success_event; } String get_failure_event() const { return failure_event; }
void set_failure_event(const StringName &p_failure_event) { failure_event = p_failure_event; }
StringName get_failure_event() const { return failure_event; }
void set_monitor_performance(bool p_monitor);
bool get_monitor_performance() const { return monitor_performance; }
void set_scene_root_hint(Node *p_node);
BTState(); BTState();
#ifdef DEBUG_ENABLED
protected:
void _notification(int p_notification);
#endif
}; };
#endif // BT_STATE_H #endif // BT_STATE_H

View File

@ -1,7 +1,7 @@
/** /**
* bt_check_trigger.cpp * bt_check_trigger.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -13,28 +13,28 @@
#include "../../../util/limbo_utility.h" #include "../../../util/limbo_utility.h"
void BTCheckTrigger::set_variable(const StringName &p_variable) { void BTCheckTrigger::set_variable(String p_variable) {
variable = p_variable; variable = p_variable;
emit_changed(); emit_changed();
} }
PackedStringArray BTCheckTrigger::get_configuration_warnings() { PackedStringArray BTCheckTrigger::get_configuration_warnings() {
PackedStringArray warnings = BTCondition::get_configuration_warnings(); PackedStringArray warnings = BTCondition::get_configuration_warnings();
if (variable == StringName()) { if (variable.is_empty()) {
warnings.append("Variable is not set."); warnings.append("Variable is not set.");
} }
return warnings; return warnings;
} }
String BTCheckTrigger::_generate_name() { String BTCheckTrigger::_generate_name() {
if (variable == StringName()) { if (variable.is_empty()) {
return "CheckTrigger ???"; return "CheckTrigger ???";
} }
return "CheckTrigger " + LimboUtility::get_singleton()->decorate_var(variable); return "CheckTrigger " + LimboUtility::get_singleton()->decorate_var(variable);
} }
BT::Status BTCheckTrigger::_tick(double p_delta) { BT::Status BTCheckTrigger::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(variable == StringName(), FAILURE, "BBCheckVar: `variable` is not set."); ERR_FAIL_COND_V_MSG(variable.is_empty(), FAILURE, "BBCheckVar: `variable` is not set.");
Variant trigger_value = get_blackboard()->get_var(variable, false); Variant trigger_value = get_blackboard()->get_var(variable, false);
if (trigger_value == Variant(true)) { if (trigger_value == Variant(true)) {
get_blackboard()->set_var(variable, false); get_blackboard()->set_var(variable, false);
@ -44,8 +44,8 @@ BT::Status BTCheckTrigger::_tick(double p_delta) {
} }
void BTCheckTrigger::_bind_methods() { void BTCheckTrigger::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_variable", "variable"), &BTCheckTrigger::set_variable); ClassDB::bind_method(D_METHOD("set_variable", "p_variable"), &BTCheckTrigger::set_variable);
ClassDB::bind_method(D_METHOD("get_variable"), &BTCheckTrigger::get_variable); ClassDB::bind_method(D_METHOD("get_variable"), &BTCheckTrigger::get_variable);
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "variable"), "set_variable", "get_variable"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "variable"), "set_variable", "get_variable");
} }

View File

@ -1,7 +1,7 @@
/** /**
* bt_check_trigger.h * bt_check_trigger.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -19,7 +19,7 @@ class BTCheckTrigger : public BTCondition {
TASK_CATEGORY(Blackboard); TASK_CATEGORY(Blackboard);
private: private:
StringName variable; String variable;
protected: protected:
static void _bind_methods(); static void _bind_methods();
@ -28,8 +28,8 @@ protected:
virtual Status _tick(double p_delta) override; virtual Status _tick(double p_delta) override;
public: public:
void set_variable(const StringName &p_variable); void set_variable(String p_variable);
StringName get_variable() const { return variable; } String get_variable() const { return variable; }
virtual PackedStringArray get_configuration_warnings() override; virtual PackedStringArray get_configuration_warnings() override;
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bt_check_var.cpp * bt_check_var.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -11,7 +11,7 @@
#include "bt_check_var.h" #include "bt_check_var.h"
void BTCheckVar::set_variable(const StringName &p_variable) { void BTCheckVar::set_variable(String p_variable) {
variable = p_variable; variable = p_variable;
emit_changed(); emit_changed();
} }
@ -21,18 +21,17 @@ void BTCheckVar::set_check_type(LimboUtility::CheckType p_check_type) {
emit_changed(); emit_changed();
} }
void BTCheckVar::set_value(const Ref<BBVariant> &p_value) { void BTCheckVar::set_value(Ref<BBVariant> p_value) {
value = p_value; value = p_value;
emit_changed(); emit_changed();
if (Engine::get_singleton()->is_editor_hint() && value.is_valid() && if (Engine::get_singleton()->is_editor_hint() && value.is_valid()) {
!value->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { value->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed)));
value->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed));
} }
} }
PackedStringArray BTCheckVar::get_configuration_warnings() { PackedStringArray BTCheckVar::get_configuration_warnings() {
PackedStringArray warnings = BTCondition::get_configuration_warnings(); PackedStringArray warnings = BTCondition::get_configuration_warnings();
if (variable == StringName()) { if (variable.is_empty()) {
warnings.append("`variable` should be assigned."); warnings.append("`variable` should be assigned.");
} }
if (!value.is_valid()) { if (!value.is_valid()) {
@ -42,7 +41,7 @@ PackedStringArray BTCheckVar::get_configuration_warnings() {
} }
String BTCheckVar::_generate_name() { String BTCheckVar::_generate_name() {
if (variable == StringName()) { if (variable.is_empty()) {
return "CheckVar ???"; return "CheckVar ???";
} }
@ -52,26 +51,26 @@ String BTCheckVar::_generate_name() {
} }
BT::Status BTCheckVar::_tick(double p_delta) { BT::Status BTCheckVar::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(variable == StringName(), FAILURE, "BTCheckVar: `variable` is not set."); ERR_FAIL_COND_V_MSG(variable.is_empty(), FAILURE, "BTCheckVar: `variable` is not set.");
ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BTCheckVar: `value` is not set."); ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BTCheckVar: `value` is not set.");
ERR_FAIL_COND_V_MSG(!get_blackboard()->has_var(variable), FAILURE, vformat("BTCheckVar: Blackboard variable doesn't exist: \"%s\". Returning FAILURE.", variable)); ERR_FAIL_COND_V_MSG(!get_blackboard()->has_var(variable), FAILURE, vformat("BTCheckVar: Blackboard variable doesn't exist: \"%s\". Returning FAILURE.", variable));
Variant left_value = get_blackboard()->get_var(variable, Variant()); Variant left_value = get_blackboard()->get_var(variable, Variant());
Variant right_value = value->get_value(get_scene_root(), get_blackboard()); Variant right_value = value->get_value(get_agent(), get_blackboard());
return LimboUtility::get_singleton()->perform_check(check_type, left_value, right_value) ? SUCCESS : FAILURE; return LimboUtility::get_singleton()->perform_check(check_type, left_value, right_value) ? SUCCESS : FAILURE;
} }
void BTCheckVar::_bind_methods() { void BTCheckVar::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_variable", "variable"), &BTCheckVar::set_variable); ClassDB::bind_method(D_METHOD("set_variable", "p_variable"), &BTCheckVar::set_variable);
ClassDB::bind_method(D_METHOD("get_variable"), &BTCheckVar::get_variable); ClassDB::bind_method(D_METHOD("get_variable"), &BTCheckVar::get_variable);
ClassDB::bind_method(D_METHOD("set_check_type", "check_type"), &BTCheckVar::set_check_type); ClassDB::bind_method(D_METHOD("set_check_type", "p_check_type"), &BTCheckVar::set_check_type);
ClassDB::bind_method(D_METHOD("get_check_type"), &BTCheckVar::get_check_type); ClassDB::bind_method(D_METHOD("get_check_type"), &BTCheckVar::get_check_type);
ClassDB::bind_method(D_METHOD("set_value", "value"), &BTCheckVar::set_value); ClassDB::bind_method(D_METHOD("set_value", "p_value"), &BTCheckVar::set_value);
ClassDB::bind_method(D_METHOD("get_value"), &BTCheckVar::get_value); ClassDB::bind_method(D_METHOD("get_value"), &BTCheckVar::get_value);
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "variable"), "set_variable", "get_variable"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "variable"), "set_variable", "get_variable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "check_type", PROPERTY_HINT_ENUM, "Equal,Less Than,Less Than Or Equal,Greater Than,Greater Than Or Equal,Not Equal"), "set_check_type", "get_check_type"); ADD_PROPERTY(PropertyInfo(Variant::INT, "check_type", PROPERTY_HINT_ENUM, "Equal,Less Than,Less Than Or Equal,Greater Than,Greater Than Or Equal,Not Equal"), "set_check_type", "get_check_type");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "value", PROPERTY_HINT_RESOURCE_TYPE, "BBVariant"), "set_value", "get_value"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "value", PROPERTY_HINT_RESOURCE_TYPE, "BBVariant"), "set_value", "get_value");
} }

View File

@ -1,7 +1,7 @@
/** /**
* bt_check_var.h * bt_check_var.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -22,7 +22,7 @@ class BTCheckVar : public BTCondition {
TASK_CATEGORY(Blackboard); TASK_CATEGORY(Blackboard);
private: private:
StringName variable; String variable;
LimboUtility::CheckType check_type = LimboUtility::CheckType::CHECK_EQUAL; LimboUtility::CheckType check_type = LimboUtility::CheckType::CHECK_EQUAL;
Ref<BBVariant> value; Ref<BBVariant> value;
@ -35,13 +35,13 @@ protected:
public: public:
virtual PackedStringArray get_configuration_warnings() override; virtual PackedStringArray get_configuration_warnings() override;
void set_variable(const StringName &p_variable); void set_variable(String p_variable);
StringName get_variable() const { return variable; } String get_variable() const { return variable; }
void set_check_type(LimboUtility::CheckType p_check_type); void set_check_type(LimboUtility::CheckType p_check_type);
LimboUtility::CheckType get_check_type() const { return check_type; } LimboUtility::CheckType get_check_type() const { return check_type; }
void set_value(const Ref<BBVariant> &p_value); void set_value(Ref<BBVariant> p_value);
Ref<BBVariant> get_value() const { return value; } Ref<BBVariant> get_value() const { return value; }
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bt_set_var.cpp * bt_set_var.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -12,7 +12,7 @@
#include "bt_set_var.h" #include "bt_set_var.h"
String BTSetVar::_generate_name() { String BTSetVar::_generate_name() {
if (variable == StringName()) { if (variable.is_empty()) {
return "SetVar ???"; return "SetVar ???";
} }
return vformat("Set %s %s= %s", return vformat("Set %s %s= %s",
@ -22,11 +22,11 @@ String BTSetVar::_generate_name() {
} }
BT::Status BTSetVar::_tick(double p_delta) { BT::Status BTSetVar::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(variable == StringName(), FAILURE, "BTSetVar: `variable` is not set."); ERR_FAIL_COND_V_MSG(variable.is_empty(), FAILURE, "BTSetVar: `variable` is not set.");
ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BTSetVar: `value` is not set."); ERR_FAIL_COND_V_MSG(!value.is_valid(), FAILURE, "BTSetVar: `value` is not set.");
Variant result; Variant result;
Variant error_result = LW_NAME(error_value); Variant error_result = LW_NAME(error_value);
Variant right_value = value->get_value(get_scene_root(), get_blackboard(), error_result); Variant right_value = value->get_value(get_agent(), get_blackboard(), error_result);
ERR_FAIL_COND_V_MSG(right_value == error_result, FAILURE, "BTSetVar: Failed to get parameter value. Returning FAILURE."); ERR_FAIL_COND_V_MSG(right_value == error_result, FAILURE, "BTSetVar: Failed to get parameter value. Returning FAILURE.");
if (operation == LimboUtility::OPERATION_NONE) { if (operation == LimboUtility::OPERATION_NONE) {
result = right_value; result = right_value;
@ -40,17 +40,16 @@ BT::Status BTSetVar::_tick(double p_delta) {
return SUCCESS; return SUCCESS;
}; };
void BTSetVar::set_variable(const StringName &p_variable) { void BTSetVar::set_variable(const String &p_variable) {
variable = p_variable; variable = p_variable;
emit_changed(); emit_changed();
} }
void BTSetVar::set_value(const Ref<BBVariant> &p_value) { void BTSetVar::set_value(Ref<BBVariant> p_value) {
value = p_value; value = p_value;
emit_changed(); emit_changed();
if (Engine::get_singleton()->is_editor_hint() && value.is_valid() && if (Engine::get_singleton()->is_editor_hint() && value.is_valid()) {
!value->is_connected(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed))) { value->connect(LW_NAME(changed), Callable(this, LW_NAME(emit_changed)));
value->connect(LW_NAME(changed), callable_mp((Resource *)this, &Resource::emit_changed));
} }
} }
@ -61,7 +60,7 @@ void BTSetVar::set_operation(LimboUtility::Operation p_operation) {
PackedStringArray BTSetVar::get_configuration_warnings() { PackedStringArray BTSetVar::get_configuration_warnings() {
PackedStringArray warnings = BTAction::get_configuration_warnings(); PackedStringArray warnings = BTAction::get_configuration_warnings();
if (variable == StringName()) { if (variable.is_empty()) {
warnings.append("`variable` should be assigned."); warnings.append("`variable` should be assigned.");
} }
if (!value.is_valid()) { if (!value.is_valid()) {
@ -71,14 +70,14 @@ PackedStringArray BTSetVar::get_configuration_warnings() {
} }
void BTSetVar::_bind_methods() { void BTSetVar::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_variable", "variable"), &BTSetVar::set_variable); ClassDB::bind_method(D_METHOD("set_variable", "p_variable"), &BTSetVar::set_variable);
ClassDB::bind_method(D_METHOD("get_variable"), &BTSetVar::get_variable); ClassDB::bind_method(D_METHOD("get_variable"), &BTSetVar::get_variable);
ClassDB::bind_method(D_METHOD("set_value", "value"), &BTSetVar::set_value); ClassDB::bind_method(D_METHOD("set_value", "p_value"), &BTSetVar::set_value);
ClassDB::bind_method(D_METHOD("get_value"), &BTSetVar::get_value); ClassDB::bind_method(D_METHOD("get_value"), &BTSetVar::get_value);
ClassDB::bind_method(D_METHOD("get_operation"), &BTSetVar::get_operation); ClassDB::bind_method(D_METHOD("get_operation"), &BTSetVar::get_operation);
ClassDB::bind_method(D_METHOD("set_operation", "operation"), &BTSetVar::set_operation); ClassDB::bind_method(D_METHOD("set_operation", "p_operation"), &BTSetVar::set_operation);
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "variable"), "set_variable", "get_variable"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "variable"), "set_variable", "get_variable");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "value", PROPERTY_HINT_RESOURCE_TYPE, "BBVariant"), "set_value", "get_value"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "value", PROPERTY_HINT_RESOURCE_TYPE, "BBVariant"), "set_value", "get_value");
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "None,Addition,Subtraction,Multiplication,Division,Modulo,Power,Bitwise Shift Left,Bitwise Shift Right,Bitwise AND,Bitwise OR,Bitwise XOR"), "set_operation", "get_operation"); ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "None,Addition,Subtraction,Multiplication,Division,Modulo,Power,Bitwise Shift Left,Bitwise Shift Right,Bitwise AND,Bitwise OR,Bitwise XOR"), "set_operation", "get_operation");
} }

View File

@ -1,7 +1,7 @@
/** /**
* bt_set_var.h * bt_set_var.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -22,7 +22,7 @@ class BTSetVar : public BTAction {
TASK_CATEGORY(Blackboard); TASK_CATEGORY(Blackboard);
private: private:
StringName variable; String variable;
Ref<BBVariant> value; Ref<BBVariant> value;
LimboUtility::Operation operation = LimboUtility::OPERATION_NONE; LimboUtility::Operation operation = LimboUtility::OPERATION_NONE;
@ -35,10 +35,10 @@ protected:
public: public:
virtual PackedStringArray get_configuration_warnings() override; virtual PackedStringArray get_configuration_warnings() override;
void set_variable(const StringName &p_variable); void set_variable(const String &p_variable);
StringName get_variable() const { return variable; } String get_variable() const { return variable; }
void set_value(const Ref<BBVariant> &p_value); void set_value(Ref<BBVariant> p_value);
Ref<BBVariant> get_value() const { return value; } Ref<BBVariant> get_value() const { return value; }
void set_operation(LimboUtility::Operation p_operation); void set_operation(LimboUtility::Operation p_operation);

View File

@ -1,7 +1,7 @@
/** /**
* bt_action.cpp * bt_action.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at

View File

@ -1,7 +1,7 @@
/** /**
* bt_action.h * bt_action.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -17,9 +17,6 @@
class BTAction : public BTTask { class BTAction : public BTTask {
GDCLASS(BTAction, BTTask); GDCLASS(BTAction, BTTask);
protected:
static void _bind_methods() {}
public: public:
virtual PackedStringArray get_configuration_warnings() override; virtual PackedStringArray get_configuration_warnings() override;
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bt_comment.cpp * bt_comment.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -30,7 +30,7 @@ PackedStringArray BTComment::get_configuration_warnings() {
if (get_child_count_excluding_comments() > 0) { if (get_child_count_excluding_comments() > 0) {
warnings.append("Can only have other comment tasks as children."); warnings.append("Can only have other comment tasks as children.");
} }
if (get_parent().is_null()) { if (get_parent() == nullptr) {
warnings.append("Can't be the root task."); warnings.append("Can't be the root task.");
} }
return warnings; return warnings;

View File

@ -1,7 +1,7 @@
/** /**
* bt_comment.h * bt_comment.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,9 +18,6 @@ class BTComment : public BTTask {
GDCLASS(BTComment, BTTask); GDCLASS(BTComment, BTTask);
TASK_CATEGORY(Utility); TASK_CATEGORY(Utility);
protected:
static void _bind_methods() {}
public: public:
virtual Ref<BTTask> clone() const override; virtual Ref<BTTask> clone() const override;
virtual PackedStringArray get_configuration_warnings() override; virtual PackedStringArray get_configuration_warnings() override;

View File

@ -1,7 +1,7 @@
/** /**
* bt_composite.cpp * bt_composite.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at

View File

@ -1,7 +1,7 @@
/** /**
* bt_composite.h * bt_composite.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -17,9 +17,6 @@
class BTComposite : public BTTask { class BTComposite : public BTTask {
GDCLASS(BTComposite, BTTask); GDCLASS(BTComposite, BTTask);
protected:
static void _bind_methods() {}
public: public:
virtual PackedStringArray get_configuration_warnings() override; virtual PackedStringArray get_configuration_warnings() override;
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bt_condition.cpp * bt_condition.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at

View File

@ -1,7 +1,7 @@
/** /**
* bt_condition.h * bt_condition.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -17,9 +17,6 @@
class BTCondition : public BTTask { class BTCondition : public BTTask {
GDCLASS(BTCondition, BTTask); GDCLASS(BTCondition, BTTask);
protected:
static void _bind_methods() {}
public: public:
virtual PackedStringArray get_configuration_warnings() override; virtual PackedStringArray get_configuration_warnings() override;
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bt_decorator.cpp * bt_decorator.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -18,8 +18,3 @@ PackedStringArray BTDecorator::get_configuration_warnings() {
} }
return warnings; return warnings;
} }
BT::Status BTDecorator::_tick(double p_delta) {
ERR_FAIL_COND_V_MSG(get_child_count() == 0, FAILURE, "BT decorator doesn't have a child.");
return get_child(0)->execute(p_delta);
}

View File

@ -1,7 +1,7 @@
/** /**
* bt_decorator.h * bt_decorator.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -17,11 +17,6 @@
class BTDecorator : public BTTask { class BTDecorator : public BTTask {
GDCLASS(BTDecorator, BTTask) GDCLASS(BTDecorator, BTTask)
protected:
static void _bind_methods() {}
virtual Status _tick(double p_delta) override;
public: public:
virtual PackedStringArray get_configuration_warnings() override; virtual PackedStringArray get_configuration_warnings() override;
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bt_task.cpp * bt_task.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -14,7 +14,6 @@
#include "../../blackboard/blackboard.h" #include "../../blackboard/blackboard.h"
#include "../../util/limbo_string_names.h" #include "../../util/limbo_string_names.h"
#include "../../util/limbo_utility.h" #include "../../util/limbo_utility.h"
#include "../behavior_tree.h"
#include "bt_comment.h" #include "bt_comment.h"
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
@ -77,73 +76,34 @@ Array BTTask::_get_children() const {
} }
void BTTask::_set_children(Array p_children) { void BTTask::_set_children(Array p_children) {
const int num_children = p_children.size();
int num_null = 0;
data.children.clear(); data.children.clear();
const int num_children = p_children.size();
data.children.resize(num_children); data.children.resize(num_children);
for (int i = 0; i < num_children; i++) { for (int i = 0; i < num_children; i++) {
Ref<BTTask> task = p_children[i]; Variant task_var = p_children[i];
if (task.is_null()) { Ref<BTTask> task_ref = task_var;
ERR_PRINT("Invalid BTTask reference."); task_ref->data.parent = this;
num_null += 1; task_ref->data.index = i;
continue; data.children.set(i, task_var);
} }
if (task->data.parent != nullptr && task->data.parent != this) {
task = task->clone();
if (task.is_null()) {
// * BTComment::clone() returns nullptr at runtime - we omit those.
num_null += 1;
continue;
}
}
int idx = i - num_null;
task->data.parent = this;
task->data.index = idx;
data.children.set(idx, task);
}
if (num_null > 0) {
data.children.resize(num_children - num_null);
}
}
void BTTask::set_display_collapsed(bool p_display_collapsed) {
data.display_collapsed = p_display_collapsed;
}
bool BTTask::is_displayed_collapsed() const {
return data.display_collapsed;
} }
String BTTask::get_task_name() { String BTTask::get_task_name() {
if (!data.custom_name.is_empty()) { if (data.custom_name.is_empty()) {
return data.custom_name; #ifdef LIMBOAI_MODULE
} if (get_script_instance() && get_script_instance()->has_method(LW_NAME(_generate_name))) {
if (unlikely(!get_script_instance()->get_script()->is_tool())) {
Ref<Script> task_script = get_script(); ERR_PRINT(vformat("BTTask: Task script should be a \"tool\" script!"));
} else {
if (task_script.is_valid()) { return get_script_instance()->call(LimboStringNames::get_singleton()->_generate_name);
// ! CURSED: Currently, has_method() doesn't return true for ClassDB-registered native virtual methods. This may break in the future.
bool has_generate_method = has_method(LW_NAME(_generate_name));
ERR_FAIL_COND_V_MSG(has_generate_method && !task_script->is_tool(), _generate_name(), vformat("BTTask: @tool annotation is required if _generate_name is defined: %s", task_script->get_path()));
if (task_script->is_tool() && has_generate_method) {
String call_result;
GDVIRTUAL_CALL(_generate_name, call_result);
if (call_result.is_empty() || call_result == "<null>") {
// Force reset script instance.
set_script(Variant());
set_script(task_script);
// Retry.
GDVIRTUAL_CALL(_generate_name, call_result);
}
ERR_FAIL_COND_V_MSG(call_result.is_empty() || call_result == "<null>", _generate_name(), vformat("BTTask: _generate_name() failed to return a proper name string (%s)", task_script->get_path()));
return call_result;
} }
} }
return _generate_name(); return _generate_name();
#elif LIMBOAI_GDEXTENSION
return call(LimboStringNames::get_singleton()->_generate_name);
#endif
}
return data.custom_name;
} }
Ref<BTTask> BTTask::get_root() const { Ref<BTTask> BTTask::get_root() const {
@ -161,68 +121,94 @@ void BTTask::set_custom_name(const String &p_name) {
} }
}; };
void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) { void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard) {
ERR_FAIL_NULL(p_agent); ERR_FAIL_COND(p_agent == nullptr);
ERR_FAIL_COND(p_blackboard.is_null()); ERR_FAIL_COND(p_blackboard == nullptr);
ERR_FAIL_NULL(p_scene_root);
data.agent = p_agent; data.agent = p_agent;
data.blackboard = p_blackboard; data.blackboard = p_blackboard;
data.scene_root = p_scene_root;
for (int i = 0; i < data.children.size(); i++) { for (int i = 0; i < data.children.size(); i++) {
get_child(i)->initialize(p_agent, p_blackboard, p_scene_root); get_child(i)->initialize(p_agent, p_blackboard);
} }
_setup(); VCALL_OR_NATIVE(_setup);
GDVIRTUAL_CALL(_setup);
} }
Ref<BTTask> BTTask::clone() const { Ref<BTTask> BTTask::clone() const {
Ref<BTTask> inst = duplicate(false); Ref<BTTask> inst = duplicate(false);
inst->data.parent = nullptr;
inst->data.agent = nullptr;
inst->data.blackboard.unref();
int num_null = 0;
for (int i = 0; i < data.children.size(); i++) {
Ref<BTTask> c = get_child(i)->clone();
if (c.is_valid()) {
c->data.parent = inst.ptr();
c->data.index = i;
inst->data.children.set(i - num_null, c);
} else {
num_null += 1;
}
}
if (num_null > 0) {
// * BTComment tasks return nullptr at runtime - we remove those.
inst->data.children.resize(data.children.size() - num_null);
}
// * Children are duplicated via children property. See _set_children().
// * Make BBParam properties unique.
HashMap<Ref<Resource>, Ref<Resource>> duplicates;
#ifdef LIMBOAI_MODULE #ifdef LIMBOAI_MODULE
// Make BBParam properties unique.
List<PropertyInfo> props; List<PropertyInfo> props;
inst->get_property_list(&props); inst->get_property_list(&props);
HashMap<Ref<Resource>, Ref<Resource>> duplicates;
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
PropertyInfo prop = E->get(); if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
#elif LIMBOAI_GDEXTENSION
TypedArray<Dictionary> props = inst->get_property_list();
for (int i = 0; i < props.size(); i++) {
PropertyInfo prop = PropertyInfo::from_dict(props[i]);
#endif
if (!(prop.usage & PROPERTY_USAGE_STORAGE)) {
continue; continue;
} }
Variant prop_value = inst->get(prop.name); Variant v = inst->get(E->get().name);
Ref<Resource> res = prop_value;
if (v.is_ref_counted()) {
Ref<RefCounted> ref = v;
if (ref.is_valid()) {
Ref<Resource> res = ref;
if (res.is_valid() && res->is_class("BBParam")) { if (res.is_valid() && res->is_class("BBParam")) {
// Duplicate BBParam
if (!duplicates.has(res)) { if (!duplicates.has(res)) {
duplicates[res] = res->duplicate(); duplicates[res] = res->duplicate();
} }
res = duplicates[res]; res = duplicates[res];
inst->set(prop.name, res); inst->set(E->get().name, res);
} else if (prop_value.get_type() == Variant::ARRAY) {
// Duplicate BBParams instances inside an array.
// - This code doesn't handle arrays of arrays.
// - A partial workaround for: https://github.com/godotengine/godot/issues/74918
// - We actually don't want to duplicate resources in clone() except for BBParam subtypes.
Array arr = prop_value;
if (arr.is_typed() && ClassDB::is_parent_class(arr.get_typed_class_name(), LW_NAME(BBParam))) {
for (int j = 0; j < arr.size(); j++) {
Ref<Resource> bb_param = arr[j];
if (bb_param.is_valid()) {
arr[j] = bb_param->duplicate();
} }
} }
} }
} }
#elif LIMBOAI_GDEXTENSION
// Make BBParam properties unique.
TypedArray<Dictionary> props = inst->get_property_list();
HashMap<Ref<Resource>, Ref<Resource>> duplicates;
for (int i = 0; i < props.size(); i++) {
Dictionary prop = props[i];
if (!(int(prop["usage"]) & PROPERTY_USAGE_STORAGE)) {
continue;
} }
StringName prop_name = prop["name"];
Variant v = inst->get(prop_name);
if (v.get_type() == Variant::OBJECT && int(prop["hint"]) == PROPERTY_HINT_RESOURCE_TYPE) {
Ref<RefCounted> ref = v;
if (ref.is_valid()) {
Ref<Resource> res = ref;
if (res.is_valid() && res->is_class("BBParam")) {
if (!duplicates.has(res)) {
duplicates[res] = res->duplicate();
}
res = duplicates[res];
inst->set(prop_name, res);
}
}
}
}
#endif // LIMBOAI_MODULE & LIMBOAI_GDEXTENSION
return inst; return inst;
} }
@ -234,21 +220,22 @@ BT::Status BTTask::execute(double p_delta) {
data.children.get(i)->abort(); data.children.get(i)->abort();
} }
} }
// First native, then script.
_enter(); VCALL_OR_NATIVE(_enter);
GDVIRTUAL_CALL(_enter);
} else { } else {
data.elapsed += p_delta; data.elapsed += p_delta;
} }
#ifdef LIMBOAI_MODULE
if (!GDVIRTUAL_CALL(_tick, p_delta, data.status)) { if (!GDVIRTUAL_CALL(_tick, p_delta, data.status)) {
data.status = _tick(p_delta); data.status = _tick(p_delta);
} }
#elif LIMBOAI_GDEXTENSION
data.status = (Status)(int)call(LimboStringNames::get_singleton()->_tick, p_delta);
#endif
if (data.status != RUNNING) { if (data.status != RUNNING) {
// First script, then native. VCALL_OR_NATIVE(_exit);
GDVIRTUAL_CALL(_exit);
_exit();
data.elapsed = 0.0; data.elapsed = 0.0;
} }
return data.status; return data.status;
@ -259,9 +246,7 @@ void BTTask::abort() {
get_child(i)->abort(); get_child(i)->abort();
} }
if (data.status == RUNNING) { if (data.status == RUNNING) {
// First script, then native. VCALL_OR_NATIVE(_exit);
GDVIRTUAL_CALL(_exit);
_exit();
} }
data.status = FRESH; data.status = FRESH;
data.elapsed = 0.0; data.elapsed = 0.0;
@ -290,9 +275,9 @@ void BTTask::add_child_at_index(Ref<BTTask> p_child, int p_idx) {
if (p_idx < 0 || p_idx > data.children.size()) { if (p_idx < 0 || p_idx > data.children.size()) {
p_idx = data.children.size(); p_idx = data.children.size();
} }
data.children.insert(p_idx, p_child);
p_child->data.parent = this; p_child->data.parent = this;
p_child->data.index = p_idx; p_child->data.index = p_idx;
data.children.insert(p_idx, p_child);
for (int i = p_idx + 1; i < data.children.size(); i++) { for (int i = p_idx + 1; i < data.children.size(); i++) {
get_child(i)->data.index = i; get_child(i)->data.index = i;
} }
@ -350,12 +335,8 @@ PackedStringArray BTTask::get_configuration_warnings() {
PackedStringArray ret; PackedStringArray ret;
PackedStringArray warnings; PackedStringArray warnings;
Ref<Script> task_script = get_script(); VCALL_V(_get_configuration_warnings, warnings); // Get script warnings.
if (task_script.is_valid() && task_script->is_tool()) {
GDVIRTUAL_CALL(_get_configuration_warnings, warnings); // Get script warnings.
}
ret.append_array(warnings); ret.append_array(warnings);
ret.append_array(_get_configuration_warnings());
return ret; return ret;
} }
@ -373,77 +354,63 @@ void BTTask::print_tree(int p_initial_tabs) {
} }
} }
Ref<BehaviorTree> BTTask::editor_get_behavior_tree() {
#ifdef TOOLS_ENABLED
BTTask *task = this;
while (task->data.behavior_tree_id.is_null() && task->get_parent().is_valid()) {
task = task->data.parent;
}
return Object::cast_to<BehaviorTree>(ObjectDB::get_instance(task->data.behavior_tree_id));
#else
ERR_PRINT("BTTask::editor_get_behavior_tree: Not available in release builds.");
return Ref<BehaviorTree>();
#endif
}
#ifdef TOOLS_ENABLED
void BTTask::editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt) {
data.behavior_tree_id = p_bt->get_instance_id();
}
#endif // TOOLS_ENABLED
void BTTask::_bind_methods() { void BTTask::_bind_methods() {
// Public Methods. // Public Methods.
ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root); ClassDB::bind_method(D_METHOD("is_root"), &BTTask::is_root);
ClassDB::bind_method(D_METHOD("get_root"), &BTTask::get_root); ClassDB::bind_method(D_METHOD("get_root"), &BTTask::get_root);
ClassDB::bind_method(D_METHOD("initialize", "agent", "blackboard", "scene_root"), &BTTask::initialize); ClassDB::bind_method(D_METHOD("initialize", "p_agent", "p_blackboard"), &BTTask::initialize);
ClassDB::bind_method(D_METHOD("clone"), &BTTask::clone); ClassDB::bind_method(D_METHOD("clone"), &BTTask::clone);
ClassDB::bind_method(D_METHOD("execute", "delta"), &BTTask::execute); ClassDB::bind_method(D_METHOD("execute", "p_delta"), &BTTask::execute);
ClassDB::bind_method(D_METHOD("get_child", "idx"), &BTTask::get_child); ClassDB::bind_method(D_METHOD("get_child", "p_idx"), &BTTask::get_child);
ClassDB::bind_method(D_METHOD("get_child_count"), &BTTask::get_child_count); ClassDB::bind_method(D_METHOD("get_child_count"), &BTTask::get_child_count);
ClassDB::bind_method(D_METHOD("get_child_count_excluding_comments"), &BTTask::get_child_count_excluding_comments); ClassDB::bind_method(D_METHOD("get_child_count_excluding_comments"), &BTTask::get_child_count_excluding_comments);
ClassDB::bind_method(D_METHOD("add_child", "task"), &BTTask::add_child); ClassDB::bind_method(D_METHOD("add_child", "p_child"), &BTTask::add_child);
ClassDB::bind_method(D_METHOD("add_child_at_index", "task", "idx"), &BTTask::add_child_at_index); ClassDB::bind_method(D_METHOD("add_child_at_index", "p_child", "p_idx"), &BTTask::add_child_at_index);
ClassDB::bind_method(D_METHOD("remove_child", "task"), &BTTask::remove_child); ClassDB::bind_method(D_METHOD("remove_child", "p_child"), &BTTask::remove_child);
ClassDB::bind_method(D_METHOD("remove_child_at_index", "idx"), &BTTask::remove_child_at_index); ClassDB::bind_method(D_METHOD("remove_child_at_index", "p_idx"), &BTTask::remove_child_at_index);
ClassDB::bind_method(D_METHOD("has_child", "task"), &BTTask::has_child); ClassDB::bind_method(D_METHOD("has_child", "p_child"), &BTTask::has_child);
ClassDB::bind_method(D_METHOD("is_descendant_of", "task"), &BTTask::is_descendant_of); ClassDB::bind_method(D_METHOD("is_descendant_of", "p_task"), &BTTask::is_descendant_of);
ClassDB::bind_method(D_METHOD("get_index"), &BTTask::get_index); ClassDB::bind_method(D_METHOD("get_index"), &BTTask::get_index);
ClassDB::bind_method(D_METHOD("next_sibling"), &BTTask::next_sibling); ClassDB::bind_method(D_METHOD("next_sibling"), &BTTask::next_sibling);
ClassDB::bind_method(D_METHOD("print_tree", "initial_tabs"), &BTTask::print_tree, Variant(0)); ClassDB::bind_method(D_METHOD("print_tree", "p_initial_tabs"), &BTTask::print_tree, Variant(0));
ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name); ClassDB::bind_method(D_METHOD("get_task_name"), &BTTask::get_task_name);
ClassDB::bind_method(D_METHOD("abort"), &BTTask::abort); ClassDB::bind_method(D_METHOD("abort"), &BTTask::abort);
ClassDB::bind_method(D_METHOD("editor_get_behavior_tree"), &BTTask::editor_get_behavior_tree);
// Properties, setters and getters. // Properties, setters and getters.
ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent); ClassDB::bind_method(D_METHOD("get_agent"), &BTTask::get_agent);
ClassDB::bind_method(D_METHOD("set_agent", "agent"), &BTTask::set_agent); ClassDB::bind_method(D_METHOD("set_agent", "p_agent"), &BTTask::set_agent);
ClassDB::bind_method(D_METHOD("get_scene_root"), &BTTask::get_scene_root);
ClassDB::bind_method(D_METHOD("_get_children"), &BTTask::_get_children); ClassDB::bind_method(D_METHOD("_get_children"), &BTTask::_get_children);
ClassDB::bind_method(D_METHOD("_set_children", "children"), &BTTask::_set_children); ClassDB::bind_method(D_METHOD("_set_children", "p_children"), &BTTask::_set_children);
ClassDB::bind_method(D_METHOD("get_blackboard"), &BTTask::get_blackboard); ClassDB::bind_method(D_METHOD("get_blackboard"), &BTTask::get_blackboard);
ClassDB::bind_method(D_METHOD("get_parent"), &BTTask::get_parent); ClassDB::bind_method(D_METHOD("get_parent"), &BTTask::get_parent);
ClassDB::bind_method(D_METHOD("get_status"), &BTTask::get_status); ClassDB::bind_method(D_METHOD("get_status"), &BTTask::get_status);
ClassDB::bind_method(D_METHOD("get_elapsed_time"), &BTTask::get_elapsed_time); ClassDB::bind_method(D_METHOD("get_elapsed_time"), &BTTask::get_elapsed_time);
ClassDB::bind_method(D_METHOD("get_custom_name"), &BTTask::get_custom_name); ClassDB::bind_method(D_METHOD("get_custom_name"), &BTTask::get_custom_name);
ClassDB::bind_method(D_METHOD("set_custom_name", "name"), &BTTask::set_custom_name); ClassDB::bind_method(D_METHOD("set_custom_name", "p_name"), &BTTask::set_custom_name);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_name"), "set_custom_name", "get_custom_name"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_name"), "set_custom_name", "get_custom_name");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "agent", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_agent", "get_agent"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "agent", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_agent", "get_agent");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scene_root", PROPERTY_HINT_NODE_TYPE, "Node", PROPERTY_USAGE_NONE), "", "get_scene_root");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_RESOURCE_TYPE, "Blackboard", PROPERTY_USAGE_NONE), "", "get_blackboard"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "blackboard", PROPERTY_HINT_RESOURCE_TYPE, "Blackboard", PROPERTY_USAGE_NONE), "", "get_blackboard");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "children", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_children", "_get_children"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "children", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_children", "_get_children");
ADD_PROPERTY(PropertyInfo(Variant::INT, "status", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_status"); ADD_PROPERTY(PropertyInfo(Variant::INT, "status", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_status");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "elapsed_time", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_elapsed_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "elapsed_time", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "", "get_elapsed_time");
#ifdef LIMBOAI_MODULE
GDVIRTUAL_BIND(_setup); GDVIRTUAL_BIND(_setup);
GDVIRTUAL_BIND(_enter); GDVIRTUAL_BIND(_enter);
GDVIRTUAL_BIND(_exit); GDVIRTUAL_BIND(_exit);
GDVIRTUAL_BIND(_tick, "delta"); GDVIRTUAL_BIND(_tick, "p_delta");
GDVIRTUAL_BIND(_generate_name); GDVIRTUAL_BIND(_generate_name);
GDVIRTUAL_BIND(_get_configuration_warnings); GDVIRTUAL_BIND(_get_configuration_warnings);
#elif LIMBOAI_GDEXTENSION
// TODO: Until virtual functions are implemented in godot-cpp, we do this. Replace this code when possible.
ClassDB::bind_method(D_METHOD("_setup"), &BTTask::_setup);
ClassDB::bind_method(D_METHOD("_enter"), &BTTask::_enter);
ClassDB::bind_method(D_METHOD("_exit"), &BTTask::_exit);
ClassDB::bind_method(D_METHOD("_tick", "p_delta"), &BTTask::_tick);
ClassDB::bind_method(D_METHOD("_generate_name"), &BTTask::_generate_name);
ClassDB::bind_method(D_METHOD("_get_configuration_warnings"), &BTTask::_get_configuration_warnings);
#endif
} }
BTTask::BTTask() { BTTask::BTTask() {

View File

@ -1,7 +1,7 @@
/** /**
* bt_task.h * bt_task.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -37,14 +37,11 @@
#ifdef LIMBOAI_GDEXTENSION #ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/engine.hpp> #include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/resource.hpp> #include <godot_cpp/classes/resource.hpp>
#include <godot_cpp/core/gdvirtual.gen.inc>
#include <godot_cpp/core/object.hpp> #include <godot_cpp/core/object.hpp>
#include <godot_cpp/templates/vector.hpp> #include <godot_cpp/templates/vector.hpp>
using namespace godot; using namespace godot;
#endif // LIMBOAI_GDEXTENSION #endif // LIMBOAI_GDEXTENSION
class BehaviorTree;
/** /**
* Base class for BTTask. * Base class for BTTask.
* Note: In order to properly return Status in the _tick virtual method (GDVIRTUAL1R...) * Note: In order to properly return Status in the _tick virtual method (GDVIRTUAL1R...)
@ -78,16 +75,11 @@ private:
int index = -1; int index = -1;
String custom_name; String custom_name;
Node *agent = nullptr; Node *agent = nullptr;
Node *scene_root = nullptr;
Ref<Blackboard> blackboard; Ref<Blackboard> blackboard;
BTTask *parent = nullptr; BTTask *parent = nullptr;
Vector<Ref<BTTask>> children; Vector<Ref<BTTask>> children;
Status status = FRESH; Status status = FRESH;
double elapsed = 0.0; double elapsed = 0.0;
bool display_collapsed = false;
#ifdef TOOLS_ENABLED
ObjectID behavior_tree_id;
#endif
} data; } data;
Array _get_children() const; Array _get_children() const;
@ -104,16 +96,14 @@ protected:
virtual void _exit() {} virtual void _exit() {}
virtual Status _tick(double p_delta) { return FAILURE; } virtual Status _tick(double p_delta) { return FAILURE; }
#ifdef LIMBOAI_MODULE
GDVIRTUAL0RC(String, _generate_name); GDVIRTUAL0RC(String, _generate_name);
GDVIRTUAL0(_setup); GDVIRTUAL0(_setup);
GDVIRTUAL0(_enter); GDVIRTUAL0(_enter);
GDVIRTUAL0(_exit); GDVIRTUAL0(_exit);
GDVIRTUAL1R(Status, _tick, double); GDVIRTUAL1R(Status, _tick, double);
GDVIRTUAL0RC(PackedStringArray, _get_configuration_warnings); GDVIRTUAL0RC(PackedStringArray, _get_configuration_warnings);
#endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION
String _to_string() const { return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
public: public:
// TODO: GDExtension doesn't have this method hmm... // TODO: GDExtension doesn't have this method hmm...
@ -125,11 +115,6 @@ public:
_FORCE_INLINE_ Node *get_agent() const { return data.agent; } _FORCE_INLINE_ Node *get_agent() const { return data.agent; }
void set_agent(Node *p_agent) { data.agent = p_agent; } void set_agent(Node *p_agent) { data.agent = p_agent; }
_FORCE_INLINE_ Node *get_scene_root() const { return data.scene_root; }
void set_display_collapsed(bool p_display_collapsed);
bool is_displayed_collapsed() const;
String get_custom_name() const { return data.custom_name; } String get_custom_name() const { return data.custom_name; }
void set_custom_name(const String &p_name); void set_custom_name(const String &p_name);
String get_task_name(); String get_task_name();
@ -137,7 +122,7 @@ public:
Ref<BTTask> get_root() const; Ref<BTTask> get_root() const;
virtual Ref<BTTask> clone() const; virtual Ref<BTTask> clone() const;
virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root); virtual void initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard);
virtual PackedStringArray get_configuration_warnings(); // ! Native version. virtual PackedStringArray get_configuration_warnings(); // ! Native version.
Status execute(double p_delta); Status execute(double p_delta);
@ -170,11 +155,6 @@ public:
void print_tree(int p_initial_tabs = 0); void print_tree(int p_initial_tabs = 0);
Ref<BehaviorTree> editor_get_behavior_tree();
#ifdef TOOLS_ENABLED
void editor_set_behavior_tree(const Ref<BehaviorTree> &p_bt);
#endif
BTTask(); BTTask();
~BTTask(); ~BTTask();
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bt_dynamic_selector.cpp * bt_dynamic_selector.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at

View File

@ -1,7 +1,7 @@
/** /**
* bt_dynamic_selector.h * bt_dynamic_selector.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -22,8 +22,6 @@ private:
int last_running_idx = 0; int last_running_idx = 0;
protected: protected:
static void _bind_methods() {}
virtual void _enter() override; virtual void _enter() override;
virtual Status _tick(double p_delta) override; virtual Status _tick(double p_delta) override;
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bt_dynamic_sequence.cpp * bt_dynamic_sequence.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at

View File

@ -1,7 +1,7 @@
/** /**
* bt_dynamic_sequence.h * bt_dynamic_sequence.h
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -22,8 +22,6 @@ private:
int last_running_idx = 0; int last_running_idx = 0;
protected: protected:
static void _bind_methods() {}
virtual void _enter() override; virtual void _enter() override;
virtual Status _tick(double p_delta) override; virtual Status _tick(double p_delta) override;
}; };

View File

@ -1,7 +1,7 @@
/** /**
* bt_parallel.cpp * bt_parallel.cpp
* ============================================================================= * =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors. * Copyright 2021-2023 Serhii Snitsaruk
* *
* Use of this source code is governed by an MIT-style * Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at * license that can be found in the LICENSE file or at
@ -49,11 +49,11 @@ BT::Status BTParallel::_tick(double p_delta) {
void BTParallel::_bind_methods() { void BTParallel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_num_successes_required"), &BTParallel::get_num_successes_required); ClassDB::bind_method(D_METHOD("get_num_successes_required"), &BTParallel::get_num_successes_required);
ClassDB::bind_method(D_METHOD("set_num_successes_required", "value"), &BTParallel::set_num_successes_required); ClassDB::bind_method(D_METHOD("set_num_successes_required", "p_value"), &BTParallel::set_num_successes_required);
ClassDB::bind_method(D_METHOD("get_num_failures_required"), &BTParallel::get_num_failures_required); ClassDB::bind_method(D_METHOD("get_num_failures_required"), &BTParallel::get_num_failures_required);
ClassDB::bind_method(D_METHOD("set_num_failures_required", "value"), &BTParallel::set_num_failures_required); ClassDB::bind_method(D_METHOD("set_num_failures_required", "p_value"), &BTParallel::set_num_failures_required);
ClassDB::bind_method(D_METHOD("get_repeat"), &BTParallel::get_repeat); ClassDB::bind_method(D_METHOD("get_repeat"), &BTParallel::get_repeat);
ClassDB::bind_method(D_METHOD("set_repeat", "enable"), &BTParallel::set_repeat); ClassDB::bind_method(D_METHOD("set_repeat", "p_value"), &BTParallel::set_repeat);
ADD_PROPERTY(PropertyInfo(Variant::INT, "num_successes_required"), "set_num_successes_required", "get_num_successes_required"); ADD_PROPERTY(PropertyInfo(Variant::INT, "num_successes_required"), "set_num_successes_required", "get_num_successes_required");
ADD_PROPERTY(PropertyInfo(Variant::INT, "num_failures_required"), "set_num_failures_required", "get_num_failures_required"); ADD_PROPERTY(PropertyInfo(Variant::INT, "num_failures_required"), "set_num_failures_required", "get_num_failures_required");

Some files were not shown because too many files have changed in this diff Show More