Compare commits

..

10 Commits

Author SHA1 Message Date
Serhii Snitsaruk 8a23496265
Bump version to 1.1.1 2024-08-06 21:41:09 +02:00
Serhii Snitsaruk 820663099d Merge branch “cherrypicks-4.2” 2024-08-06 21:36:10 +02:00
Serhii Snitsaruk ad821c4514 Fix C# exports print errors due to missing ClassDB binding 2024-08-06 19:58:05 +02:00
Serhii Snitsaruk 5a58eef4f1 Fix error if `changed` signal is already connected to a BBParam in a bunch of tasks 2024-08-06 19:20:25 +02:00
Serhii Snitsaruk 997f9572e2 Fix process_input is not enabled in active substate 2024-08-06 19:18:52 +02:00
Serhii Snitsaruk 01ae6a46de Fix BTPlayer.restart() crash 2024-08-06 19:17:41 +02:00
yds b5602169d4 Fix new task script path don't update after changing the setting 2024-08-06 19:16:48 +02:00
Serhii Snitsaruk d999e33efa Defer banner actions to not crash upon restart 2024-08-06 19:15:10 +02:00
Serhii Snitsaruk 9da78e52e7 Fix BlackboardPlan arrays shouldn't be shared between instanced agents
Variable values are deep copied now using Variant.duplicate(true). Note that currently it doesn't duplicate objects.
2024-08-06 19:14:42 +02:00
Serhii Snitsaruk e6d1e0d076 Fix connected signal error when loading BT for the second time 2024-08-06 19:12:54 +02:00
435 changed files with 3235 additions and 6666 deletions

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,7 +15,7 @@ runs:
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"
- name: Set NAME_PREFIX

View File

@ -15,11 +15,6 @@ runs:
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"
@ -27,12 +22,6 @@ runs:
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

View File

@ -5,7 +5,7 @@ on:
godot-ref:
description: A tag, branch or commit hash in the Godot repository.
type: string
default: master
default: 4.2
limboai-ref:
description: A tag, branch or commit hash in the LimboAI repository.
type: string
@ -13,7 +13,7 @@ on:
godot-cpp-ref:
description: A tag, branch or commit hash in the godot-cpp repository.
type: string
default: master
default: 4.2
jobs:
cache-sha:

View File

@ -37,104 +37,40 @@ jobs:
fail-fast: false
matrix:
include:
# * Standard arm64
- name: Template (arm64, debug)
arch: arm64
target: template_debug
dotnet: false
- name: Template (arm64, release)
arch: arm64
target: template_release
dotnet: false
# * Standard arm32
- name: Template (arm32, debug)
arch: arm32
target: template_debug
dotnet: false
- name: Template (arm32, release)
arch: arm32
target: template_release
dotnet: false
# * Standard x86_64
- name: Template (x86_64, debug)
arch: x86_64
target: template_debug
dotnet: false
- name: Template (x86_64, release)
arch: x86_64
target: template_release
dotnet: false
# * Standard x86_32
- name: Template (x86_32, debug)
arch: x86_32
target: template_debug
dotnet: false
- name: Template (x86_32, release)
arch: x86_32
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:
BIN: godot.linuxbsd.${{matrix.target}}.${{matrix.arch}}${{ matrix.dotnet == true && '.mono' || '' }}
BIN: godot.linuxbsd.${{matrix.target}}.${{matrix.arch}} #${{ matrix.build-mono == true && '.mono' || '' }}
steps:
- name: Clone Godot
@ -165,43 +101,33 @@ jobs:
python --version
scons --version
# ! Note: we stopped using the scons cache in release builds.
# - name: Set up scons cache
# uses: actions/cache@v4
# 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: Set up scons cache
uses: actions/cache@v4
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
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
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/*
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: tmp-android${{matrix.dotnet == true && '-dotnet' || ''}}-templates-${{strategy.job-index}}
name: tmp-android-templates-${{strategy.job-index}}
path: platform/android/java/lib/libs/*
make-android-package:
runs-on: "ubuntu-20.04"
name: Make Android package
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:
- name: Clone Godot
@ -222,7 +148,7 @@ jobs:
- name: Download Android template builds
uses: actions/download-artifact@v4
with:
pattern: tmp-android${{matrix.dotnet == true && '-dotnet' || ''}}-templates-*
pattern: tmp-android-templates-*
merge-multiple: true
path: platform/android/java/lib/libs/
@ -254,24 +180,24 @@ jobs:
mkdir -p 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/*
- name: Delete Android template builds
uses: geekyeggo/delete-artifact@v5
with:
name: tmp-android${{matrix.dotnet == true && '-dotnet' || ''}}-templates-*
name: tmp-android-templates-*
useGlob: true
failOnError: false
- name: Upload Android libs
uses: actions/upload-artifact@v4
with:
name: ${{env.NAME_PREFIX}}${{matrix.dotnet == true && '.dotnet' || ''}}.android-lib
name: ${{env.NAME_PREFIX}}.android-lib
path: bin/godot-lib.*
- name: Upload Android templates
uses: actions/upload-artifact@v4
with:
name: ${{env.NAME_PREFIX}}${{matrix.dotnet == true && '.dotnet' || ''}}.export-templates.android
name: ${{env.NAME_PREFIX}}.export-templates.android
path: out/*

View File

@ -5,17 +5,13 @@ on:
godot-cpp-ref:
description: A tag, branch or commit hash in the godot-cpp repository.
type: string
default: godot-4.3-stable
default: 4.2
limboai-ref:
description: A tag, branch or commit hash in the LimboAI repository.
type: string
default: master
test-build:
description: Limit to pre-defined test builds
type: boolean
default: false
debug-symbols:
description: Build with debug symbols
description: Should we perform only a limited number of test builds?
type: boolean
default: false
@ -24,28 +20,23 @@ on:
godot-cpp-ref:
description: A tag, branch or commit hash in the godot-cpp repository.
type: string
default: godot-4.3-stable
default: 4.2
limboai-ref:
description: A tag, branch or commit hash in the LimboAI repository.
type: string
default: master
test-build:
description: Limit to pre-defined test builds
type: boolean
default: false
debug-symbols:
description: Build with debug symbols
description: Should we perform only a limited number of test builds?
type: boolean
default: false
# Global Settings
env:
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
GODOT_VERSION: 4.2-stable
jobs:
gdextension:
@ -109,7 +100,7 @@ jobs:
- name: 🌐 Web (wasm32, debug)
runner: ubuntu-20.04
platform: web
target: editor
target: template_debug
arch: wasm32
should-build: ${{ !inputs.test-build }}
@ -123,7 +114,7 @@ jobs:
- name: 🤖 Android (arm64, debug)
runner: ubuntu-20.04
platform: android
target: editor
target: template_debug
arch: arm64
should-build: ${{ !inputs.test-build }}
@ -137,7 +128,7 @@ jobs:
- name: 🤖 Android (arm32, debug)
runner: ubuntu-20.04
platform: android
target: editor
target: template_debug
arch: arm32
should-build: ${{ !inputs.test-build }}
@ -151,7 +142,7 @@ jobs:
- name: 🤖 Android (x86_64, debug)
runner: ubuntu-20.04
platform: android
target: editor
target: template_debug
arch: x86_64
should-build: ${{ !inputs.test-build }}
@ -165,40 +156,10 @@ jobs:
- name: 🤖 Android (x86_32, debug)
runner: ubuntu-20.04
platform: android
target: editor
target: template_debug
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:
- { opts: { should-build: false } }
@ -206,12 +167,6 @@ jobs:
BIN: liblimboai.${{matrix.opts.platform}}.${{matrix.opts.target}}.${{matrix.opts.arch}}
steps:
- name: Clone LimboAI module
uses: actions/checkout@v4
with:
fetch-tags: true
ref: ${{ inputs.limboai-ref }}
- name: Clone godot-cpp
uses: actions/checkout@v4
with:
@ -220,8 +175,15 @@ jobs:
path: godot-cpp
ref: ${{ inputs.godot-cpp-ref }}
- name: Clone LimboAI module
uses: actions/checkout@v4
with:
path: limboai
fetch-tags: true
ref: ${{ inputs.limboai-ref }}
# 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
id: output-name-prefix
@ -229,7 +191,7 @@ jobs:
- name: Setup Linux toolchain
if: matrix.opts.platform == 'linux'
uses: ./.github/actions/setup-linux-toolchain
uses: ./limboai/.github/actions/setup-linux-toolchain
with:
arch: ${{matrix.opts.arch}}
@ -286,39 +248,58 @@ jobs:
uses: ammaraskar/msvc-problem-matcher@master
- name: Set up scons cache
if: inputs.test-build # ! Only cache test/PR builds
uses: actions/cache@v4
with:
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-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}}
restore-keys: |
${{env.BIN}}-${{inputs.debug-symbols}}-${{inputs.godot-cpp-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}}
${{env.BIN}}-${{inputs.debug-symbols}}-${{inputs.godot-cpp-ref}}-${{inputs.limboai-ref}}
${{env.BIN}}-${{inputs.debug-symbols}}-${{inputs.godot-cpp-ref}}
${{env.BIN}}-${{inputs.godot-cpp-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}}
${{env.BIN}}-${{inputs.godot-cpp-ref}}-${{inputs.limboai-ref}}
${{env.BIN}}-${{inputs.godot-cpp-ref}}
- name: Setup project structure for GDExtension
shell: bash
run: |
bash ./limboai/gdextension/setup_gdextension.sh --copy-all
echo "---"
ls -l
echo "---"
ls -l -R ./demo/
- name: Compilation
shell: bash
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
DEBUG_FLAGS: ${{ inputs.debug-symbols && 'debug_symbols=yes symbols_visibility=visible' || 'debug_symbols=no' }}
run: |
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
shell: bash
run: |
ls -R demo/addons/limboai/
mkdir 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/
cp limboai/{README,LICENSE,LOGO_LICENSE}.md out/addons/limboai/
cp -R limboai/demo/demo/ out/demo/
cp limboai/demo/LICENSE_ASSETS.md out/demo/
rm -f out/addons/limboai/bin/*.{exp,lib,pdb}
echo "${LIMBOAI_VERSION}" > out/addons/limboai/version.txt
echo "---"
ls -R out/
- name: Strip lib
if: matrix.opts.platform != 'windows' && matrix.opts.platform != 'web' && (matrix.opts.platform != 'android' || startsWith(matrix.opts.arch, 'x86'))
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
uses: actions/upload-artifact@v4
env:
@ -352,7 +333,7 @@ jobs:
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
wget "https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}/Godot_v${GODOT_VERSION}_linux.x86_64.zip" -O godot.zip
unzip godot.zip
rm godot.zip
mv Godot_* godot

View File

@ -25,7 +25,7 @@ on:
# Global Settings
env:
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_CLI_TELEMETRY_OPTOUT: true
@ -42,25 +42,21 @@ jobs:
target: template_release
arch: arm64
ios_simulator: false
dotnet: false
- name: Template (arm64, debug)
target: template_debug
arch: arm64
ios_simulator: false
dotnet: false
- name: Simulator (x86_64, release)
- name: Simulator Lib (x86_64, release)
target: template_release
arch: x86_64
ios_simulator: true
dotnet: false
- name: Simulator (x86_64, debug)
- name: Simulator Lib (x86_64, debug)
target: template_debug
arch: x86_64
ios_simulator: true
dotnet: false
# ! Disabled for now as it doesn't work with cctools-port and current LLVM.
# * See https://github.com/godotengine/build-containers/pull/85.
@ -74,32 +70,8 @@ jobs:
# arch: arm64
# 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:
BIN: godot.ios.${{matrix.target}}.${{matrix.arch}}${{ matrix.dotnet == true && '.mono' || '' }}
BIN: godot.ios.${{matrix.target}}.${{matrix.arch}}
steps:
- name: Clone Godot
@ -136,52 +108,35 @@ jobs:
- name: Set up Vulkan SDK
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
# ! Note: we stopped using the scons cache in release builds.
# - name: Set up scons cache
# uses: actions/cache@v4
# 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: Set up scons cache
uses: actions/cache@v4
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
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
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
uses: actions/upload-artifact@v4
with:
name: tmp-ios${{matrix.dotnet == true && '-dotnet' || ''}}-templates-${{strategy.job-index}}
name: tmp-ios-templates-${{strategy.job-index}}
path: bin/*
package-ios-templates:
runs-on: "macos-latest"
name: ${{ matrix.name }}
name: Package iOS templates
needs: ios-builds
strategy:
fail-fast: false
matrix:
include:
- name: Package iOS templates
dotnet: false
- name: Package iOS .NET templates
dotnet: true
steps:
- name: Clone Godot
uses: actions/checkout@v4
@ -200,19 +155,12 @@ jobs:
- name: Set up Vulkan SDK
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
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
sh misc/scripts/install_vulkan_sdk_macos.sh
- name: Download templates artifact
uses: actions/download-artifact@v4
with:
pattern: tmp-ios${{matrix.dotnet == true && '-dotnet' || ''}}-templates-*
pattern: tmp-ios-templates-*
merge-multiple: true
path: bin/
@ -222,6 +170,9 @@ jobs:
cp -r misc/dist/ios_xcode bin/
cd bin/
# --- Note: Doesn't look like libs need stripping ---
# strip *.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
mv libgodot.ios.template_debug.x86_64.simulator.a ios_xcode/libgodot.ios.debug.xcframework/ios-arm64_x86_64-simulator/libgodot.a
@ -237,19 +188,19 @@ jobs:
cd ios_xcode
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/*
- name: Upload template bundle
uses: actions/upload-artifact@v4
with:
name: ${{env.NAME_PREFIX}}${{matrix.dotnet == true && '.dotnet' || ''}}.export-templates.ios
name: ${{env.NAME_PREFIX}}.export-templates.ios
path: out/*
- name: Delete templates artifact
uses: geekyeggo/delete-artifact@v5
with:
name: tmp-ios${{matrix.dotnet == true && '-dotnet' || ''}}-templates-*
name: tmp-ios-templates-*
useGlob: true
failOnError: false

View File

@ -11,7 +11,7 @@ on:
type: string
default: master
test-build:
description: Limit to pre-defined test builds
description: Should we perform only a limited number of test builds?
type: boolean
default: false
@ -26,7 +26,7 @@ on:
type: string
default: master
test-build:
description: Limit to pre-defined test builds
description: Should we perform only a limited number of test builds?
type: boolean
default: false
@ -45,8 +45,6 @@ jobs:
fail-fast: false
matrix:
opts:
# * Standard x86_64
- name: Editor (x86_64, release)
target: editor
arch: x86_64
@ -65,8 +63,6 @@ jobs:
dotnet: false
should-build: ${{ !inputs.test-build }}
# * Standard x86_32
# - name: Editor (x86_32, release)
# target: editor
# arch: x86_32
@ -85,47 +81,11 @@ jobs:
dotnet: false
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)
target: editor
arch: x86_64
dotnet: true
should-build: true
should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_64, release)
target: template_release
@ -139,8 +99,6 @@ jobs:
dotnet: true
should-build: ${{ !inputs.test-build }}
# * .NET x86_32
# - name: Editor .NET (x86_32, release)
# target: editor
# arch: x86_32
@ -159,42 +117,6 @@ jobs:
dotnet: 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 }}
exclude:
- { opts: { should-build: false } }
@ -222,12 +144,7 @@ jobs:
with:
arch: ${{matrix.opts.arch}}
- name: Set up Wayland deps
run: |
sudo apt-get install libwayland-dev
- name: Set up scons cache
if: inputs.test-build # ! Only cache test/PR builds
uses: actions/cache@v4
with:
path: ${{github.workspace}}/.scons_cache/
@ -244,17 +161,25 @@ jobs:
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}}
- name: Build .NET assemblies
- name: Generate C# glue
if: matrix.opts.dotnet && matrix.opts.target == 'editor'
uses: ./modules/limboai/.github/actions/build-dotnet-assemblies
with:
platform: linuxbsd
bin: ${{env.BIN}}
env:
GODOT_VERSION_STATUS: limboai
run: |
./bin/$BIN --headless --generate-mono-glue ./modules/mono/glue || true
- name: Build .NET solutions
if: matrix.opts.dotnet && 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
env:
OUTDIR: ${{ startsWith(matrix.opts.target, 'template') && 'out/templates' || 'out/' }}
run: |
strip ./bin/godot.*
chmod +x ./bin/godot.*
mkdir -p ${{env.OUTDIR}}
mv bin/* ${{env.OUTDIR}}
@ -279,7 +204,7 @@ jobs:
BUILD_TYPE: ${{ endsWith(matrix.opts.target, 'release') && 'release' || 'debug' }}
run: |
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/
- name: Upload artifact

View File

@ -11,7 +11,7 @@ on:
type: string
default: master
test-build:
description: Limit to pre-defined test builds
description: Should we perform only a limited number of test builds?
type: boolean
default: false
@ -26,7 +26,7 @@ on:
type: string
default: master
test-build:
description: Limit to pre-defined test builds
description: Should we perform only a limited number of test builds?
type: boolean
default: false
@ -165,19 +165,9 @@ jobs:
- name: Set up Vulkan SDK
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
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
sh misc/scripts/install_vulkan_sdk_macos.sh
- name: Set up scons cache
if: inputs.test-build # ! Only cache test/PR builds
uses: actions/cache@v4
with:
path: ${{github.workspace}}/.scons_cache/
@ -193,15 +183,23 @@ jobs:
run: |
scons -j2 platform=macos target=${{matrix.opts.target}} arch=${{matrix.opts.arch}} module_mono_enabled=${{matrix.opts.dotnet}} ${{env.SCONSFLAGS}}
- name: Build .NET assemblies
if: matrix.opts.dotnet && matrix.opts.target == 'editor'
uses: ./modules/limboai/.github/actions/build-dotnet-assemblies
with:
platform: macos
bin: ${{env.BIN}}
- name: Generate C# glue
if: matrix.opts.dotnet && matrix.opts.target == 'editor' && matrix.opts.arch == 'x86_64'
env:
GODOT_VERSION_STATUS: limboai
run: |
./bin/$BIN --headless --generate-mono-glue ./modules/mono/glue || true
- name: Build .NET solutions
if: matrix.opts.dotnet && matrix.opts.target == 'editor' && matrix.opts.arch == 'x86_64'
env:
GODOT_VERSION_STATUS: limboai
run: |
./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=macos
- name: Prepare artifact
run: |
strip bin/godot.*
chmod +x bin/godot.*
- name: Upload artifact
@ -259,6 +257,9 @@ jobs:
run: |
ls bin/
lipo -create bin/godot.macos.editor.x86_64* bin/godot.macos.editor.arm64* -output bin/godot.macos.editor.universal
du -sh bin/
strip bin/godot.macos.editor.universal
du -sh bin/
mkdir -p out/editor/
cp -r misc/dist/macos_tools.app out/editor/${APP_NAME}
mkdir -p out/editor/${APP_NAME}/Contents/{MacOS,Resources}
@ -294,6 +295,7 @@ jobs:
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_debug.x86_64* bin/godot.macos.template_debug.arm64* -output bin/godot.macos.template_debug.universal
strip bin/godot.*.universal
cp -r misc/dist/macos_template.app macos_template.app
mkdir -p macos_template.app/Contents/MacOS
cp bin/godot.macos.template_debug.universal macos_template.app/Contents/MacOS/godot_macos_debug.universal
@ -302,7 +304,7 @@ jobs:
zip -q -9 -r macos.zip macos_template.app
mkdir -p out/templates/
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/

View File

@ -26,8 +26,8 @@ concurrency:
# Global Settings.
env:
GODOT_REF: "master"
GODOT_CPP_REF: "master"
GODOT_REF: "4.2"
GODOT_CPP_REF: "4.2"
jobs:
unit-tests:
@ -36,7 +36,7 @@ jobs:
# Settings
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
BIN: godot.linuxbsd.editor.dev.x86_64
@ -101,18 +101,6 @@ jobs:
run: |
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:
runs-on: ubuntu-latest
outputs:

View File

@ -40,42 +40,18 @@ jobs:
include:
- name: Template (release)
target: template_release
threads: true
dlink: false
- name: Template (release, dlink)
- name: Template (release, dlink_enabled=true)
target: template_release
threads: true
dlink: true
- name: Template (debug)
target: template_debug
threads: true
dlink: false
- name: Template (debug, dlink)
- name: Template (debug, dlink_enabled=true)
target: template_debug
threads: 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:
@ -120,39 +96,34 @@ jobs:
python --version
scons --version
# ! Note: we stopped using the scons cache in release builds.
# - name: Set up scons cache
# uses: actions/cache@v4
# with:
# path: ${{github.workspace}}/.scons_cache/
# key: ${{env.CACHE_NAME}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}}
# restore-keys: |
# ${{env.CACHE_NAME}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}}
# ${{env.CACHE_NAME}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}
# ${{env.CACHE_NAME}}-${{inputs.godot-ref}}
- name: Set up scons cache
uses: actions/cache@v4
with:
path: ${{github.workspace}}/.scons_cache/
key: ${{env.CACHE_NAME}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}}
restore-keys: |
${{env.CACHE_NAME}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}-${{env.LIMBOAI_VERSION}}
${{env.CACHE_NAME}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}
${{env.CACHE_NAME}}-${{inputs.godot-ref}}
- name: Compilation
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
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}} ${{env.SCONSFLAGS}}
- name: Prepare artifacts
run: |
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.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.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/
echo "${GODOT_VERSION}.limboai+${LIMBOAI_VERSION}" > out/templates/version.txt
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{env.NAME_PREFIX}}.${{matrix.target}}.web${{matrix.threads == false && '.nothreads' || ''}}${{matrix.dlink == true && '.dlink' || ''}}
name: ${{env.NAME_PREFIX}}.${{matrix.target}}.web${{matrix.dlink == true && '.dlink' || ''}}
path: out/*

View File

@ -11,7 +11,7 @@ on:
type: string
default: master
test-build:
description: Limit to pre-defined test builds
description: Should we perform only a limited number of test builds?
type: boolean
default: false
@ -26,174 +26,103 @@ on:
type: string
default: master
test-build:
description: Limit to pre-defined test builds
description: Should we perform only a limited number of test builds?
type: boolean
default: false
# Global Settings
env:
BUILD_IMAGE_VERSION: 4.3-f40
MESA_VERSION: 23.1.9-1
SCONS_CACHE_MSVC_CONFIG: true
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:
windows-builds:
runs-on: "ubuntu-24.04"
runs-on: "windows-latest"
name: ${{ matrix.opts.name }}
outputs:
built-dotnet-editor: ${{ steps.mark-dotnet-editor.outputs.built-dotnet-editor }}
strategy:
fail-fast: false
matrix:
opts:
# * Standard x86_64
- name: Editor (x86_64, release)
target: editor
arch: x86_64
llvm: false
dotnet: false
should-build: true
- name: Template (x86_64, release)
target: template_release
arch: x86_64
llvm: false
dotnet: false
should-build: ${{ !inputs.test-build }}
- name: Template (x86_64, debug)
target: template_debug
arch: x86_64
llvm: false
dotnet: false
should-build: ${{ !inputs.test-build }}
# * Standard x86_32
# - name: Editor (x86_32, release)
# target: editor
# arch: x86_32
# llvm: false
# dotnet: false
# should-build: ${{ !inputs.test-build }}
- name: Template (x86_32, release)
target: template_release
arch: x86_32
llvm: false
dotnet: false
should-build: ${{ !inputs.test-build }}
- name: Template (x86_32, debug)
target: template_debug
arch: x86_32
llvm: false
dotnet: false
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)
target: editor
arch: x86_64
llvm: false
dotnet: true
should-build: ${{ !inputs.test-build }}
should-build: true
- name: Template .NET (x86_64, release)
target: template_release
arch: x86_64
llvm: false
dotnet: true
should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_64, debug)
target: template_debug
arch: x86_64
llvm: false
dotnet: true
should-build: ${{ !inputs.test-build }}
# * .NET x86_32
# - name: Editor .NET (x86_32, release)
# target: editor
# arch: x86_32
# llvm: false
# dotnet: true
# should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_32, release)
target: template_release
arch: x86_32
llvm: false
dotnet: true
should-build: ${{ !inputs.test-build }}
- name: Template .NET (x86_32, debug)
target: template_debug
arch: x86_32
llvm: false
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 }}
exclude:
- { opts: { should-build: false } }
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.dotnet == true && '.mono' || '' }}
steps:
- name: Clone Godot
@ -208,11 +137,26 @@ jobs:
path: modules/limboai
ref: ${{ inputs.limboai-ref }}
# 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
- name: Set up Python 3.x
uses: actions/setup-python@v5
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
if: inputs.test-build # ! Only cache test/PR builds
uses: actions/cache@v4
with:
path: ${{github.workspace}}/.scons_cache/
@ -222,54 +166,25 @@ jobs:
${{env.BIN}}-${{inputs.godot-ref}}-${{inputs.limboai-ref}}
${{env.BIN}}-${{inputs.godot-ref}}
- name: Static ANGLE libs
- name: Compilation
env:
SCONS_CACHE: ${{github.workspace}}/.scons_cache/
run: |
mkdir -p deps/angle
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
scons -j2 platform=windows target=${{matrix.opts.target}} arch=${{matrix.opts.arch}} module_mono_enabled=${{matrix.opts.dotnet}} ${{env.SCONSFLAGS}}
- name: Mesa libs
- name: Generate C# glue
if: matrix.opts.dotnet && matrix.opts.target == 'editor'
env:
GODOT_VERSION_STATUS: limboai
run: |
mkdir -p deps/mesa
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
./bin/${{ env.BIN }} --headless --generate-mono-glue ./modules/mono/glue || true
- name: Pull build container
- name: Build .NET solutions
if: matrix.opts.dotnet && matrix.opts.target == 'editor'
env:
GODOT_VERSION_STATUS: limboai
run: |
podman pull ghcr.io/limbonaut/godot-windows:${{env.BUILD_IMAGE_VERSION}}
- 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}"
python ./modules/mono/build_scripts/build_assemblies.py --godot-output-dir=./bin --godot-platform=windows
- name: Prepare artifact
shell: bash
@ -289,7 +204,7 @@ jobs:
run: |
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
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/
- name: Upload artifact
@ -297,62 +212,3 @@ jobs:
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:
BIN: godot.windows.editor.${{matrix.opts.arch}}${{matrix.opts.llvm && '.llvm' || ''}}.mono
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: 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/*

4
.gitignore vendored
View File

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

View File

@ -1,4 +1,4 @@
Copyright (c) 2023-2025 Serhii Snitsaruk and the LimboAI contributors.
Copyright 2021-2024 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:

View File

@ -8,23 +8,22 @@
[![🔎 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)
[![GitHub License](https://img.shields.io/github/license/limbonaut/limboai)](https://github.com/limbonaut/limboai/blob/master/LICENSE.md)
[![Discord](https://img.shields.io/discord/1185664967379267774?logo=discord&link=https%3A%2F%2Fdiscord.gg%2FN5MGC95GpP)](https://discord.gg/N5MGC95GpP)
[![Support this project](https://img.shields.io/badge/Support%20this%20project-red?logo=kofi&logoColor=white&link=https%3A%2F%2Fko-fi.com%2Flimbonaut)](https://ko-fi.com/limbonaut)
[![Mastodon Follow](https://img.shields.io/mastodon/follow/109346796150895359?domain=https%3A%2F%2Fmastodon.gamedev.place)](https://mastodon.gamedev.place/@limbo)
>**🛈 Supported Godot Engine:** **4.3** (v1.2.0+) | **4.2** (v1.1.x releases)
**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.
It comes with a behavior tree editor, built-in documentation, visual debugger, extensive demo project with a tutorial, 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](https://limboai.readthedocs.io/en/latest/getting-started/hsm.html).
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.
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Y8Y2TCNH0)
>**🛈 Supported Godot Engine: 4.2**
![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.
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/latest/getting-started/introduction.html) and our demo project, which includes a tutorial.
## Demonstration
@ -36,12 +35,8 @@ Behavior Trees are powerful hierarchical structures used to model and control th
### 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
@ -50,13 +45,13 @@ Behavior Trees are powerful hierarchical structures used to model and control th
- Execute `BehaviorTree` resources using the `BTPlayer` node.
- Create complex behaviors by combining and nesting tasks in a hierarchy.
- 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.
- Blackboard system: Share data seamlessly between tasks using the `Blackboard`.
- Blackboard plans: Define variables in the BehaviorTree resource and override their values in the BTPlayer node.
- 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).
- Blackboard scopes: Prevent name conflicts and enable advanced techniques like [sharing data between several agents](https://limboai.readthedocs.io/en/latest/getting-started/using-blackboard.html#sharing-data-between-several-agents).
- Blackboard parameters: [Export a BB parameter](https://limboai.readthedocs.io/en/latest/getting-started/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.
- Visual Debugger: Inspect the execution of any BT in a running scene to identify and troubleshoot issues.
@ -67,24 +62,24 @@ Behavior Trees are powerful hierarchical structures used to model and control th
- Extend the `LimboState` class to implement state logic.
- `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.
- [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](https://limboai.readthedocs.io/en/latest/getting-started/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.
- 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](https://limboai.readthedocs.io/en/latest/getting-started/hsm.html#single-file-state-machine-setup) 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.
- **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.
Follow the [First steps](https://limboai.readthedocs.io/en/latest/index.html#first-steps) guide to learn how to get started with LimboAI and the demo project.
## 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
@ -93,36 +88,29 @@ LimboAI can be used as either a C++ module or as a GDExtension shared library. G
### Compiling from source
>**🛈 For GDExtension:** Refer to comments in [setup_gdextension.sh](./gdextension/setup_gdextension.sh) file.
- Download the Godot Engine source code and put this module source into the `modules/limboai` directory.
- Consult the Godot Engine documentation for instructions on [how to build from source code](https://docs.godotengine.org/en/stable/contributing/development/compiling/index.html).
- 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]*"`.
#### 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
- Online Documentation: [stable](https://limboai.readthedocs.io/en/stable/index.html), [latest](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/stable/behavior-trees/introduction.html)
- [Creating custom tasks in GDScript](https://limboai.readthedocs.io/en/stable/behavior-trees/custom-tasks.html)
- [Sharing data using Blackboard](https://limboai.readthedocs.io/en/stable/behavior-trees/using-blackboard.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)
- [Online Documentation](https://limboai.readthedocs.io/en/latest/index.html)
- [First steps](https://limboai.readthedocs.io/en/latest/index.html#first-steps)
- [Introduction to Behavior Trees](https://limboai.readthedocs.io/en/latest/getting-started/introduction.html)
- [Creating custom tasks in GDScript](https://limboai.readthedocs.io/en/latest/getting-started/custom-tasks.html)
- [Sharing data using Blackboard](https://limboai.readthedocs.io/en/latest/getting-started/using-blackboard.html)
- [Accessing nodes in the scene tree](https://limboai.readthedocs.io/en/latest/getting-started/accessing-nodes.html)
- [State machines](https://limboai.readthedocs.io/en/latest/getting-started/hsm.html)
- [Using GDExtension](https://limboai.readthedocs.io/en/latest/getting-started/gdextension.html)
- [Using LimboAI with C#](https://limboai.readthedocs.io/en/latest/getting-started/c-sharp.html)
- [Class reference](https://limboai.readthedocs.io/en/latest/getting-started/featured-classes.html)
## Contributing
Contributions are welcome! Please open issues for bug reports, feature requests, or code changes.
For detailed guidelines on contributing to code or documentation, check out our [Contributing](https://limboai.readthedocs.io/en/latest/getting-started/contributing.html) page.
Contributions are welcome! Please open issues for bug reports, feature requests, or code changes. Keep the minor versions backward-compatible when submitting pull requests.
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.
@ -134,6 +122,4 @@ I write about LimboAI development on Mastodon: https://mastodon.gamedev.place/@l
## 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
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/
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.

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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/**
* bb_node.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -11,9 +11,18 @@
#include "bb_node.h"
#ifdef LIMBOAI_MODULE
#include "core/error/error_macros.h"
#include "scene/main/node.h"
#endif // LIMBOAI_MODULE
#ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/node.hpp>
#endif // LIMBOAI_GDEXTENSION
Variant BBNode::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboard, const Variant &p_default) {
ERR_FAIL_NULL_V_MSG(p_scene_root, Variant(), "BBNode: get_value() failed - scene_root is null.");
ERR_FAIL_COND_V_MSG(p_blackboard.is_null(), Variant(), "BBNode: get_value() failed - blackboard is null.");
ERR_FAIL_NULL_V_MSG(p_blackboard, Variant(), "BBNode: get_value() failed - blackboard is null.");
Variant val;
if (get_value_source() == SAVED_VALUE) {
@ -24,10 +33,13 @@ Variant BBNode::get_value(Node *p_scene_root, const Ref<Blackboard> &p_blackboar
if (val.get_type() == Variant::NODE_PATH) {
return p_scene_root->get_node_or_null(val);
} else if (val.get_type() == Variant::OBJECT || val.get_type() == Variant::NIL) {
return val;
} 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;
} else {
return obj;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/**
* bb_variable.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -25,7 +25,7 @@ void BBVariable::set_value(const Variant &p_value) {
data->value_changed = true;
if (is_bound()) {
Object *obj = OBJECT_DB_GET_INSTANCE(data->bound_object);
Object *obj = ObjectDB::get_instance(ObjectID(data->bound_object));
ERR_FAIL_COND_MSG(!obj, "Blackboard: Failed to get bound object.");
#ifdef LIMBOAI_MODULE
bool r_valid;
@ -39,7 +39,7 @@ void BBVariable::set_value(const Variant &p_value) {
Variant BBVariable::get_value() const {
if (is_bound()) {
Object *obj = OBJECT_DB_GET_INSTANCE(data->bound_object);
Object *obj = ObjectDB::get_instance(ObjectID(data->bound_object));
ERR_FAIL_COND_V_MSG(!obj, data->value, "Blackboard: Failed to get bound object.");
#ifdef LIMBOAI_MODULE
bool r_valid;

View File

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

View File

@ -1,7 +1,7 @@
/**
* blackboard.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -10,7 +10,6 @@
*/
#include "blackboard.h"
#include "../util/limbo_compat.h"
#ifdef LIMBOAI_MODULE
#include "core/variant/variant.h"
@ -76,26 +75,6 @@ TypedArray<StringName> Blackboard::list_vars() const {
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) {
@ -157,7 +136,6 @@ void Blackboard::_bind_methods() {
ClassDB::bind_method(D_METHOD("erase_var", "var_name"), &Blackboard::erase_var);
ClassDB::bind_method(D_METHOD("clear"), &Blackboard::clear);
ClassDB::bind_method(D_METHOD("list_vars"), &Blackboard::list_vars);
ClassDB::bind_method(D_METHOD("print_state"), &Blackboard::print_state);
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);

View File

@ -1,7 +1,7 @@
/**
* blackboard.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -40,10 +40,6 @@ private:
protected:
static void _bind_methods();
#ifdef LIMBOAI_GDEXTENSION
String _to_string() const { return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
public:
void set_parent(const Ref<Blackboard> &p_blackboard) { parent = p_blackboard; }
Ref<Blackboard> get_parent() const { return parent; }
@ -53,11 +49,9 @@ public:
Variant get_var(const StringName &p_name, const Variant &p_default = Variant(), bool p_complain = true) const;
void set_var(const StringName &p_name, const Variant &p_value);
bool has_var(const StringName &p_name) const;
_FORCE_INLINE_ bool has_local_var(const StringName &p_name) const { return data.has(p_name); }
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 populate_from_dict(const Dictionary &p_dictionary);

View File

@ -1,7 +1,7 @@
/**
* blackboard_plan.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -13,20 +13,9 @@
#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];
@ -37,51 +26,29 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
}
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;
bool properties_changed = false;
if (value == StringName()) {
if (parent_scope_mapping.has(mapped_var_name)) {
prop_list_changed = true;
properties_changed = true;
parent_scope_mapping.erase(mapped_var_name);
}
} else {
if (!parent_scope_mapping.has(mapped_var_name)) {
prop_list_changed = true;
properties_changed = true;
}
parent_scope_mapping[mapped_var_name] = value;
}
if (prop_list_changed) {
if (properties_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);
@ -99,8 +66,6 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
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;
}
@ -113,64 +78,28 @@ bool BlackboardPlan::_set(const StringName &p_name, const Variant &p_value) {
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)) {
if (parent_scope_mapping.has(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;
@ -198,16 +127,14 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
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)) {
if (var.get_type() != Variant::NIL && (!is_derived() || !var_name.begins_with("_"))) {
if (has_mapping(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())) {
@ -226,12 +153,6 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
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));
@ -239,23 +160,6 @@ void BlackboardPlan::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
// * 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;
@ -295,11 +199,6 @@ 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();
@ -338,7 +237,7 @@ BBVariable BlackboardPlan::get_var(const StringName &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);
return var_list[p_index];
}
TypedArray<StringName> BlackboardPlan::list_vars() const {
@ -488,71 +387,48 @@ void BlackboardPlan::sync_with_base_plan() {
}
}
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());
inline void bb_add_var_dup_with_prefetch(const Ref<Blackboard> &p_blackboard, const StringName &p_name, const BBVariable &p_var, bool p_prefetch, Node *p_node) {
if (unlikely(p_prefetch && p_var.get_type() == Variant::NODE_PATH)) {
Node *n = p_node->get_node_or_null(p_var.get_value());
BBVariable var = p_var.duplicate(true);
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()));
if (p_blackboard->has_var(p_name)) {
// Not adding: Assuming variable was initialized by the user or in the parent scope.
return;
}
ERR_PRINT(vformat("BlackboardPlan: Prefetch failed for variable $%s with value: %s", p_name, p_var.get_value()));
var.set_value(Variant());
}
p_blackboard->assign_var(p_name, var);
} else {
p_blackboard->assign_var(p_name, p_var.duplicate(true));
}
}
p_blackboard->assign_var(p.first, var);
if (has_mapping) {
Ref<Blackboard> BlackboardPlan::create_blackboard(Node *p_node, const Ref<Blackboard> &p_parent_scope) {
ERR_FAIL_COND_V(p_node == nullptr && prefetch_nodepath_vars, memnew(Blackboard));
Ref<Blackboard> bb = memnew(Blackboard);
bb->set_parent(p_parent_scope);
populate_blackboard(bb, true, p_node);
return bb;
}
void BlackboardPlan::populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_node) {
ERR_FAIL_COND(p_node == nullptr && prefetch_nodepath_vars);
for (const Pair<StringName, BBVariable> &p : var_list) {
if (p_blackboard->has_var(p.first) && !overwrite) {
continue;
}
bb_add_var_dup_with_prefetch(p_blackboard, p.first, p.second, prefetch_nodepath_vars, p_node);
if (parent_scope_mapping.has(p.first)) {
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)));
ERR_CONTINUE_MSG(p_blackboard->get_parent() == nullptr, vformat("BlackboardPlan: Cannot link variable $%s to parent scope because the parent scope is not set.", 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);
}
}
}
@ -567,8 +443,8 @@ void BlackboardPlan::_bind_methods() {
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()));
ClassDB::bind_method(D_METHOD("create_blackboard", "node", "parent_scope"), &BlackboardPlan::create_blackboard, DEFVAL(Ref<Blackboard>()));
ClassDB::bind_method(D_METHOD("populate_blackboard", "blackboard", "overwrite", "node"), &BlackboardPlan::populate_blackboard);
// 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");

View File

@ -1,7 +1,7 @@
/**
* blackboard_plan.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -34,26 +34,18 @@ private:
// 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.
// Fetcher function for the parent scope plan. Funtion should return a Ref<BlackboardPlan>.
// Used in the inspector. When set, mapping feature becomes available.
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();
@ -63,10 +55,6 @@ protected:
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; }
@ -77,10 +65,6 @@ public:
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;
@ -100,10 +84,9 @@ public:
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);
Ref<Blackboard> create_blackboard(Node *p_agent, const Ref<Blackboard> &p_parent_scope = Ref<Blackboard>());
void populate_blackboard(const Ref<Blackboard> &p_blackboard, bool overwrite, Node *p_node);
BlackboardPlan();
};

View File

@ -1,7 +1,7 @@
/**
* behavior_tree.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -77,16 +77,13 @@ void BehaviorTree::copy_other(const Ref<BehaviorTree> &p_other) {
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 {
ERR_FAIL_COND_V_MSG(root_task.is_null(), nullptr, "BehaviorTree: Instantiation failed - BT has no valid root task.");
ERR_FAIL_NULL_V_MSG(p_agent, nullptr, "BehaviorTree: Instantiation failed - agent can't be null.");
ERR_FAIL_NULL_V_MSG(p_instance_owner, nullptr, "BehaviorTree: Instantiation failed -- instance owner can't be null.");
ERR_FAIL_COND_V_MSG(p_blackboard.is_null(), nullptr, "BehaviorTree: Instantiation failed - blackboard can't be null.");
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);
Ref<BTTask> BehaviorTree::instantiate(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) const {
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, memnew(BTTask), "Trying to instance a behavior tree with no valid agent.");
ERR_FAIL_NULL_V_MSG(p_scene_root, memnew(BTTask), "Trying to instance a behavior tree with no valid scene root.");
Ref<BTTask> inst = root_task->clone();
inst->initialize(p_agent, p_blackboard, p_scene_root);
return inst;
}
void BehaviorTree::_plan_changed() {
@ -119,7 +116,7 @@ void BehaviorTree::_bind_methods() {
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("copy_other", "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", "agent", "blackboard", "scene_root"), &BehaviorTree::instantiate);
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");

View File

@ -1,7 +1,7 @@
/**
* behavior_tree.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -13,7 +13,6 @@
#define BEHAVIOR_TREE_H
#include "../blackboard/blackboard_plan.h"
#include "bt_instance.h"
#include "tasks/bt_task.h"
#ifdef LIMBOAI_MODULE
@ -43,10 +42,6 @@ private:
protected:
static void _bind_methods();
#ifdef LIMBOAI_GDEXTENSION
String _to_string() const { return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
public:
#ifdef LIMBOAI_MODULE
virtual bool editor_can_reload_from_file() override { return false; }
@ -63,7 +58,7 @@ public:
Ref<BehaviorTree> clone() const;
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, Node *p_scene_root) const;
BehaviorTree();
~BehaviorTree();

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
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -11,6 +11,7 @@
#include "bt_player.h"
#include "../editor/debugger/limbo_debugger.h"
#include "../util/limbo_compat.h"
#include "../util/limbo_string_names.h"
@ -42,21 +43,25 @@
VARIANT_ENUM_CAST(BTPlayer::UpdateMode);
void BTPlayer::_instantiate_bt() {
bt_instance.unref();
void BTPlayer::_load_tree() {
#ifdef DEBUG_ENABLED
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path());
}
#endif
tree_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.");
Node *scene_root = get_owner();
ERR_FAIL_NULL_MSG(scene_root, "BTPlayer: Initialization failed - can't get scene root (make sure the BTPlayer's owner property is set).");
tree_instance = behavior_tree->instantiate(agent, blackboard, scene_root);
#ifdef DEBUG_ENABLED
bt_instance->set_monitor_performance(monitor_performance);
bt_instance->register_with_debugger();
#endif // DEBUG_ENABLED
if (IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
}
#endif
}
void BTPlayer::_update_blackboard_plan() {
@ -70,45 +75,6 @@ void BTPlayer::_update_blackboard_plan() {
blackboard_plan->set_base_plan(behavior_tree.is_valid() ? behavior_tree->get_blackboard_plan() : nullptr);
}
void BTPlayer::_initialize() {
if (blackboard.is_null()) {
blackboard = Ref<Blackboard>(memnew(Blackboard));
}
if (blackboard_plan.is_valid()) {
// 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
bt_instance->set_monitor_performance(monitor_performance);
bt_instance->register_with_debugger();
#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.");
}
scene_root_hint = p_scene_root;
}
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))) {
@ -121,16 +87,15 @@ void BTPlayer::set_behavior_tree(const Ref<BehaviorTree> &p_tree) {
_update_blackboard_plan();
} else {
behavior_tree = p_tree;
if (get_owner() && is_inside_tree()) {
_update_blackboard_plan();
_initialize();
if (get_owner()) {
_load_tree();
}
}
}
void BTPlayer::set_agent_node(const NodePath &p_agent_node) {
agent_node = p_agent_node;
if (bt_instance.is_valid()) {
if (tree_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.");
}
}
@ -154,37 +119,80 @@ void BTPlayer::set_active(bool p_active) {
}
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()));
return;
}
#ifdef DEBUG_ENABLED
double start = GET_TICKS_USEC();
#endif
if (active) {
BT::Status status = bt_instance->update(p_delta);
emit_signal(LW_NAME(updated), status);
#ifndef DISABLE_DEPRECATED
if (status == BTTask::SUCCESS || status == BTTask::FAILURE) {
emit_signal(LW_NAME(behavior_tree_finished), status);
last_status = tree_instance->execute(p_delta);
emit_signal(LimboStringNames::get_singleton()->updated, last_status);
if (last_status == BTTask::SUCCESS || last_status == BTTask::FAILURE) {
emit_signal(LimboStringNames::get_singleton()->behavior_tree_finished, last_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() {
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.");
bt_instance->get_root_task()->abort();
ERR_FAIL_COND_MSG(tree_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();
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;
#ifdef DEBUG_ENABLED
if (bt_instance.is_valid()) {
bt_instance->set_monitor_performance(monitor_performance);
if (!get_owner() && monitor_performance) {
// Don't add custom monitor if not in scene.
return;
}
#endif
if (monitor_performance) {
_add_custom_monitor();
} else {
_remove_custom_monitor();
}
}
void BTPlayer::_add_custom_monitor() {
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 (!Performance::get_singleton()->has_custom_monitor(monitor_id)) {
PERFORMANCE_ADD_CUSTOM_MONITOR(monitor_id, callable_mp(this, &BTPlayer::_get_mean_update_time_msec));
}
}
void BTPlayer::_remove_custom_monitor() {
if (monitor_id != StringName() && Performance::get_singleton()->has_custom_monitor(monitor_id)) {
Performance::get_singleton()->remove_custom_monitor(monitor_id);
}
}
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) {
switch (p_notification) {
@ -198,25 +206,37 @@ void BTPlayer::_notification(int p_notification) {
} break;
case NOTIFICATION_READY: {
if (!Engine::get_singleton()->is_editor_hint()) {
_initialize();
if (blackboard.is_null()) {
blackboard = Ref<Blackboard>(memnew(Blackboard));
}
if (blackboard_plan.is_valid()) {
blackboard_plan->populate_blackboard(blackboard, false, this);
}
if (behavior_tree.is_valid()) {
_load_tree();
}
set_active(active);
} else {
_update_blackboard_plan();
}
set_active(active);
} break;
case NOTIFICATION_ENTER_TREE: {
#ifdef DEBUG_ENABLED
if (bt_instance.is_valid()) {
bt_instance->set_monitor_performance(monitor_performance);
bt_instance->register_with_debugger();
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
}
if (monitor_performance) {
_add_custom_monitor();
}
#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_EXIT_TREE: {
#ifdef DEBUG_ENABLED
if (bt_instance.is_valid()) {
bt_instance->set_monitor_performance(false);
bt_instance->unregister_with_debugger();
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path());
}
if (monitor_performance) {
_remove_custom_monitor();
}
#endif // DEBUG_ENABLED
@ -244,16 +264,11 @@ void BTPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_blackboard_plan", "plan"), &BTPlayer::set_blackboard_plan);
ClassDB::bind_method(D_METHOD("get_blackboard_plan"), &BTPlayer::get_blackboard_plan);
ClassDB::bind_method(D_METHOD("set_monitor_performance", "enable"), &BTPlayer::set_monitor_performance);
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("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);
ClassDB::bind_method(D_METHOD("get_tree_instance"), &BTPlayer::get_tree_instance);
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");
@ -261,17 +276,19 @@ void BTPlayer::_bind_methods() {
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_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::BOOL, "monitor_performance"), "set_monitor_performance", "get_monitor_performance");
BIND_ENUM_CONSTANT(IDLE);
BIND_ENUM_CONSTANT(PHYSICS);
BIND_ENUM_CONSTANT(MANUAL);
ADD_SIGNAL(MethodInfo("behavior_tree_finished", PropertyInfo(Variant::INT, "status")));
ADD_SIGNAL(MethodInfo("updated", PropertyInfo(Variant::INT, "status")));
#ifndef DISABLE_DEPRECATED
ADD_SIGNAL(MethodInfo("behavior_tree_finished", PropertyInfo(Variant::INT, "status")));
#endif
#ifdef DEBUG_ENABLED
ClassDB::bind_method(D_METHOD("_set_monitor_performance", "enable"), &BTPlayer::_set_monitor_performance);
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() {

View File

@ -1,7 +1,7 @@
/**
* bt_player.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -15,7 +15,6 @@
#include "../blackboard/blackboard.h"
#include "../blackboard/blackboard_plan.h"
#include "behavior_tree.h"
#include "bt_instance.h"
#include "tasks/bt_task.h"
#ifdef LIMBOAI_MODULE
@ -43,25 +42,18 @@ private:
UpdateMode update_mode = UpdateMode::PHYSICS;
bool active = true;
Ref<Blackboard> blackboard;
Node *scene_root_hint = nullptr;
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:
static void _bind_methods();
void _notification(int p_notification);
#ifdef LIMBOAI_GDEXTENSION
String _to_string() const { return String(get_name()) + ":<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
public:
void set_behavior_tree(const Ref<BehaviorTree> &p_tree);
Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; };
@ -81,19 +73,32 @@ public:
Ref<Blackboard> get_blackboard() const { return blackboard; }
void set_blackboard(const Ref<Blackboard> &p_blackboard) { blackboard = p_blackboard; }
void set_monitor_performance(bool p_monitor_performance);
bool get_monitor_performance() const { return monitor_performance; }
void update(double p_delta);
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);
Ref<BTTask> get_tree_instance() { return tree_instance; }
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; }
void _add_custom_monitor();
void _remove_custom_monitor();
double _get_mean_update_time_msec();
#endif // DEBUG_ENABLED
};
#endif // BT_PLAYER_H

View File

@ -1,7 +1,7 @@
/**
* bt_state.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -11,6 +11,7 @@
#include "bt_state.h"
#include "../editor/debugger/limbo_debugger.h"
#include "../util/limbo_compat.h"
#include "../util/limbo_string_names.h"
@ -37,23 +38,6 @@ void BTState::set_behavior_tree(const Ref<BehaviorTree> &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));
@ -65,27 +49,23 @@ void BTState::_update_blackboard_plan() {
}
}
Node *BTState::_get_prefetch_root_for_base_plan() {
return _get_scene_root();
}
void BTState::_setup() {
LimboState::_setup();
ERR_FAIL_COND_MSG(behavior_tree.is_null(), "BTState: BehaviorTree is not assigned.");
Node *scene_root = _get_scene_root();
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.");
Node *scene_root = get_owner();
ERR_FAIL_NULL_MSG(scene_root, "BTState: Initialization failed - can't get scene root (make sure the BTState's owner property is set).");
tree_instance = behavior_tree->instantiate(get_agent(), get_blackboard(), scene_root);
#ifdef DEBUG_ENABLED
bt_instance->register_with_debugger();
bt_instance->set_monitor_performance(monitor_performance);
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
}
#endif
}
void BTState::_exit() {
if (bt_instance.is_valid()) {
bt_instance->get_root_task()->abort();
if (tree_instance.is_valid()) {
tree_instance->abort();
} else {
ERR_PRINT_ONCE("BTState: BehaviorTree is not assigned.");
}
@ -93,13 +73,13 @@ void BTState::_exit() {
}
void BTState::_update(double p_delta) {
GDVIRTUAL_CALL(_update, p_delta);
VCALL_ARGS(_update, p_delta);
if (!is_active()) {
// Bail out if a transition happened in the meantime.
return;
}
ERR_FAIL_COND(bt_instance.is_null());
BT::Status status = bt_instance->update(p_delta);
ERR_FAIL_NULL(tree_instance);
int status = tree_instance->execute(p_delta);
if (status == BTTask::SUCCESS) {
get_root()->dispatch(success_event, Variant());
} else if (status == BTTask::FAILURE) {
@ -112,19 +92,16 @@ void BTState::_notification(int p_notification) {
switch (p_notification) {
#ifdef DEBUG_ENABLED
case NOTIFICATION_ENTER_TREE: {
if (bt_instance.is_valid()) {
bt_instance->register_with_debugger();
bt_instance->set_monitor_performance(monitor_performance);
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->register_bt_instance(tree_instance, get_path());
}
} break;
#endif // DEBUG_ENABLED
case NOTIFICATION_EXIT_TREE: {
#ifdef DEBUG_ENABLED
if (bt_instance.is_valid()) {
bt_instance->unregister_with_debugger();
bt_instance->set_monitor_performance(false);
if (tree_instance.is_valid() && IS_DEBUGGER_ACTIVE()) {
LimboDebugger::get_singleton()->unregister_bt_instance(tree_instance, get_path());
}
#endif // DEBUG_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
@ -140,7 +117,7 @@ void BTState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_behavior_tree", "behavior_tree"), &BTState::set_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("get_tree_instance"), &BTState::get_tree_instance);
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);
@ -148,15 +125,9 @@ void BTState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_failure_event", "event"), &BTState::set_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::STRING_NAME, "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::BOOL, "monitor_performance"), "set_monitor_performance", "get_monitor_performance");
}
BTState::BTState() {

View File

@ -1,7 +1,7 @@
/**
* bt_state.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -22,13 +22,9 @@ class BTState : public LimboState {
private:
Ref<BehaviorTree> behavior_tree;
Ref<BTInstance> bt_instance;
Ref<BTTask> tree_instance;
StringName success_event;
StringName 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:
static void _bind_methods();
@ -37,7 +33,6 @@ protected:
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 _exit() override;
@ -47,7 +42,7 @@ public:
void set_behavior_tree(const Ref<BehaviorTree> &p_value);
Ref<BehaviorTree> get_behavior_tree() const { return behavior_tree; }
Ref<BTInstance> get_bt_instance() const { return bt_instance; }
Ref<BTTask> get_tree_instance() const { return tree_instance; }
void set_success_event(const StringName &p_success_event) { success_event = p_success_event; }
StringName get_success_event() const { return success_event; }
@ -55,11 +50,6 @@ public:
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();
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/**
* bt_comment.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* 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) {
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.");
}
return warnings;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
/**
* bt_task.cpp
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -125,18 +125,17 @@ String BTTask::get_task_name() {
Ref<Script> task_script = get_script();
if (task_script.is_valid()) {
// ! 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);
VCALL_V(_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);
VCALL_V(_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;
@ -163,7 +162,7 @@ void BTTask::set_custom_name(const String &p_name) {
void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node *p_scene_root) {
ERR_FAIL_NULL(p_agent);
ERR_FAIL_COND(p_blackboard.is_null());
ERR_FAIL_NULL(p_blackboard);
ERR_FAIL_NULL(p_scene_root);
data.agent = p_agent;
data.blackboard = p_blackboard;
@ -172,8 +171,7 @@ void BTTask::initialize(Node *p_agent, const Ref<Blackboard> &p_blackboard, Node
get_child(i)->initialize(p_agent, p_blackboard, p_scene_root);
}
_setup();
GDVIRTUAL_CALL(_setup);
VCALL_OR_NATIVE(_setup);
}
Ref<BTTask> BTTask::clone() const {
@ -181,48 +179,61 @@ Ref<BTTask> BTTask::clone() const {
// * Children are duplicated via children property. See _set_children().
// * Make BBParam properties unique.
HashMap<Ref<Resource>, Ref<Resource>> duplicates;
#ifdef LIMBOAI_MODULE
// Make BBParam properties unique.
List<PropertyInfo> props;
inst->get_property_list(&props);
HashMap<Ref<Resource>, Ref<Resource>> duplicates;
for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
PropertyInfo prop = E->get();
#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)) {
if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
Variant prop_value = inst->get(prop.name);
Ref<Resource> res = prop_value;
Variant v = inst->get(E->get().name);
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")) {
// Duplicate BBParam
if (!duplicates.has(res)) {
duplicates[res] = res->duplicate();
}
res = duplicates[res];
inst->set(prop.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();
inst->set(E->get().name, res);
}
}
}
}
#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;
}
@ -234,21 +245,16 @@ BT::Status BTTask::execute(double p_delta) {
data.children.get(i)->abort();
}
}
// First native, then script.
_enter();
GDVIRTUAL_CALL(_enter);
VCALL_OR_NATIVE(_enter);
} else {
data.elapsed += p_delta;
}
if (!GDVIRTUAL_CALL(_tick, p_delta, data.status)) {
data.status = _tick(p_delta);
}
VCALL_OR_NATIVE_ARGS_V(_tick, Status, data.status, p_delta);
if (data.status != RUNNING) {
// First script, then native.
GDVIRTUAL_CALL(_exit);
_exit();
VCALL_OR_NATIVE(_exit);
data.elapsed = 0.0;
}
return data.status;
@ -259,9 +265,7 @@ void BTTask::abort() {
get_child(i)->abort();
}
if (data.status == RUNNING) {
// First script, then native.
GDVIRTUAL_CALL(_exit);
_exit();
VCALL_OR_NATIVE(_exit);
}
data.status = FRESH;
data.elapsed = 0.0;
@ -352,7 +356,7 @@ PackedStringArray BTTask::get_configuration_warnings() {
PackedStringArray warnings;
Ref<Script> task_script = get_script();
if (task_script.is_valid() && task_script->is_tool()) {
GDVIRTUAL_CALL(_get_configuration_warnings, warnings); // Get script warnings.
VCALL_V(_get_configuration_warnings, warnings); // Get script warnings.
}
ret.append_array(warnings);
ret.append_array(_get_configuration_warnings());
@ -438,12 +442,16 @@ void BTTask::_bind_methods() {
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");
#ifdef LIMBOAI_MODULE
GDVIRTUAL_BIND(_setup);
GDVIRTUAL_BIND(_enter);
GDVIRTUAL_BIND(_exit);
GDVIRTUAL_BIND(_tick, "delta");
GDVIRTUAL_BIND(_generate_name);
GDVIRTUAL_BIND(_get_configuration_warnings);
#elif LIMBOAI_GDEXTENSION
// TODO: Registering virtual functions is not available in godot-cpp...
#endif
}
BTTask::BTTask() {

View File

@ -1,7 +1,7 @@
/**
* bt_task.h
* =============================================================================
* Copyright (c) 2023-present Serhii Snitsaruk and the LimboAI contributors.
* Copyright 2021-2024 Serhii Snitsaruk
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
@ -37,7 +37,6 @@
#ifdef LIMBOAI_GDEXTENSION
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/resource.hpp>
#include <godot_cpp/core/gdvirtual.gen.inc>
#include <godot_cpp/core/object.hpp>
#include <godot_cpp/templates/vector.hpp>
using namespace godot;
@ -104,16 +103,14 @@ protected:
virtual void _exit() {}
virtual Status _tick(double p_delta) { return FAILURE; }
#ifdef LIMBOAI_MODULE
GDVIRTUAL0RC(String, _generate_name);
GDVIRTUAL0(_setup);
GDVIRTUAL0(_enter);
GDVIRTUAL0(_exit);
GDVIRTUAL1R(Status, _tick, double);
GDVIRTUAL0RC(PackedStringArray, _get_configuration_warnings);
#ifdef LIMBOAI_GDEXTENSION
String _to_string() const { return "<" + get_class() + "#" + itos(get_instance_id()) + ">"; }
#endif
#endif // LIMBOAI_MODULE
public:
// TODO: GDExtension doesn't have this method hmm...

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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