diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 4f47eec..2342705 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -7,17 +7,16 @@ add_compile_definitions(DEBUG_MODE) set(BUILD_EXAMPLES false) add_subdirectory(libs/raylib-4.5.0) +add_subdirectory(libs/flecs) set(librarySources libs/cute_tiled/cute_tiled.c - libs/flecs/flecs.c libs/nuklear/nuklear.c libs/raygui/raygui.c ) set(libraryDirs libs/cute_tiled - libs/flecs libs/nuklear libs/raygui ) @@ -67,7 +66,7 @@ add_library(Breeze STATIC set_target_properties(Breeze PROPERTIES PUBLIC_HEADER "${BreezeHeaders}") -target_link_libraries(Breeze raylib) +target_link_libraries(Breeze raylib flecs::flecs_static) #target_include_directories(Breeze PUBLIC .) target_include_directories(Breeze diff --git a/engine/breeze/world/map.c b/engine/breeze/world/map.c index 46095cd..79bf46f 100644 --- a/engine/breeze/world/map.c +++ b/engine/breeze/world/map.c @@ -71,6 +71,7 @@ static void createColliders(BzTileMap *map) { // Top-most layer takes priority for (i32 i = map->layerCount - 1; i >= 0; i--) { BzTileLayer *layer = map->layers + i; + if (!layer->data) continue; if (layer->tilesetIdx == -1) continue; BzTileset *tileset = map->tilesets + layer->tilesetIdx; for (i32 y = 0; y < map->height; y++) { diff --git a/engine/libs/flecs/.bazelignore b/engine/libs/flecs/.bazelignore new file mode 100644 index 0000000..2c6ec0a --- /dev/null +++ b/engine/libs/flecs/.bazelignore @@ -0,0 +1 @@ +examples/build/bazel \ No newline at end of file diff --git a/engine/libs/flecs/.editorconfig b/engine/libs/flecs/.editorconfig new file mode 100644 index 0000000..bc4f2de --- /dev/null +++ b/engine/libs/flecs/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = false diff --git a/engine/libs/flecs/.github/FUNDING.yml b/engine/libs/flecs/.github/FUNDING.yml new file mode 100644 index 0000000..3078b89 --- /dev/null +++ b/engine/libs/flecs/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [SanderMertens] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/engine/libs/flecs/.github/ISSUE_TEMPLATE/bug_report.md b/engine/libs/flecs/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..d1e997f --- /dev/null +++ b/engine/libs/flecs/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Additional context** +Add any other context about the problem here (operating system, hardware, ...). diff --git a/engine/libs/flecs/.github/ISSUE_TEMPLATE/feature_request.md b/engine/libs/flecs/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..c701a3f --- /dev/null +++ b/engine/libs/flecs/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Describe the problem you are trying to solve.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]. Refrain from proposing a solution here. + +**Describe the solution you'd like** +A clear and concise description of what you would like to happen. diff --git a/engine/libs/flecs/.github/workflows/ci.yml b/engine/libs/flecs/.github/workflows/ci.yml new file mode 100644 index 0000000..ad6bb48 --- /dev/null +++ b/engine/libs/flecs/.github/workflows/ci.yml @@ -0,0 +1,833 @@ +name: CI + +on: [ push, pull_request ] + +jobs: + build-linux: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v3 + + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: | + bake --strict + + build-macos: + runs-on: macOS-latest + timeout-minutes: 30 + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v3 + + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: | + bake --strict + + build-windows: + runs-on: windows-latest + timeout-minutes: 30 + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v3 + - uses: ilammy/msvc-dev-cmd@v1 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + cd bake\build-Windows + nmake + cd .. + ./bake setup --local + + - name: build flecs (debug) + run: bake/bake --strict --cfg debug + + - name: build flecs (release) + run: bake/bake --strict --cfg release + + - name: build examples (debug) + run: bake/bake examples --strict --cfg debug + + - name: build examples (release) + run: bake/bake examples --strict --cfg debug + + build-msys: + runs-on: windows-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - { sys: mingw64 } + - { sys: mingw32 } + - { sys: ucrt64 } + - { sys: clang64 } + + defaults: + run: + shell: msys2 {0} + + steps: + - uses: actions/checkout@v3 + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.sys}} + update: true + install: >- + curl + git + pacboy: >- + toolchain:p + cmake:p + + - name: create cmake build folders + run: | + mkdir cmake_build + mkdir examples/c/cmake_build + mkdir examples/cpp/cmake_build + + - name: build flecs + working-directory: cmake_build + run: | + cmake -G "MinGW Makefiles" -DFLECS_STRICT=ON .. + cmake --build . -j 4 + + - name: build c examples + working-directory: examples/c/cmake_build + run: | + cmake -G "MinGW Makefiles" -DFLECS_STRICT=ON .. + cmake --build . -j 4 + + - name: build c++ examples + working-directory: examples/cpp/cmake_build + run: | + cmake -G "MinGW Makefiles" -DFLECS_STRICT=ON .. + cmake --build . -j 4 + + build: + needs: build-linux + runs-on: ${{ matrix.target.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + target: + - { os: ubuntu-20.04, cc: gcc-7, cxx: g++-7 } + - { os: ubuntu-20.04, cc: gcc-8, cxx: g++-8 } + - { os: ubuntu-20.04, cc: gcc-9, cxx: g++-9 } + - { os: ubuntu-20.04, cc: gcc-10, cxx: g++-10 } + - { os: ubuntu-20.04, cc: gcc-11, cxx: g++-11 } + - { os: ubuntu-latest, cc: gcc-12, cxx: g++-12 } + - { os: ubuntu-latest, cc: gcc-13, cxx: g++-13 } + - { os: ubuntu-20.04, cc: clang-8, cxx: clang++-8 } + - { os: ubuntu-20.04, cc: clang-9, cxx: clang++-9 } + - { os: ubuntu-20.04, cc: clang-10, cxx: clang++-10 } + - { os: ubuntu-20.04, cc: clang-11, cxx: clang++-11 } + - { os: ubuntu-20.04, cc: clang-12, cxx: clang++-12 } + - { os: ubuntu-latest, cc: clang-13, cxx: clang++-13 } + - { os: ubuntu-latest, cc: clang-14, cxx: clang++-14 } + - { os: ubuntu-latest, cc: clang-15, cxx: clang++-15 } + + env: + CC: ${{ matrix.target.cc }} + CXX: ${{ matrix.target.cxx }} + + steps: + - uses: actions/checkout@v3 + + - name: install compiler + run: | + sudo apt-get update + sudo apt-get install -y ${{ matrix.target.cc }} + sudo apt-get install -y ${{ matrix.target.cxx }} + + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs (debug) + run: | + bake --strict --cfg debug + + - name: build flecs (release) + run: | + bake --strict --cfg release + + - name: build examples (debug) + run: | + bake examples/c --strict --cfg debug + bake examples/cpp --strict --cfg debug + bake examples/os_api --strict --cfg debug + + - name: build examples (release) + run: | + bake examples/c --strict --cfg release + bake examples/cpp --strict --cfg release + bake examples/os_api --strict --cfg release + + build-cmake: + needs: build-linux + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, macOS-latest ] + + steps: + - uses: actions/checkout@v3 + + - name: create cmake build folders + run: | + mkdir cmake_build + mkdir examples/c/cmake_build + mkdir examples/cpp/cmake_build + + - name: build flecs + working-directory: cmake_build + run: | + cmake -DFLECS_STRICT=ON .. + cmake --build . -j 4 + + - name: build c examples + working-directory: examples/c/cmake_build + run: | + cmake -DFLECS_STRICT=ON .. + cmake --build . -j 4 + + - name: build c++ examples + working-directory: examples/cpp/cmake_build + run: | + cmake -DFLECS_STRICT=ON .. + cmake --build . -j 4 + + build-cmake-windows: + needs: build-windows + runs-on: windows-latest + timeout-minutes: 30 + + strategy: + matrix: + toolset: [default, v141, v142, clang-cl] + include: + - toolset: v141 + toolset_option: -T"v141" + - toolset: v142 + toolset_option: -T"v142" + - toolset: clang-cl + toolset_option: -T"ClangCl" + + steps: + - uses: actions/checkout@v3 + - name: create cmake build folders + run: | + mkdir cmake_build + mkdir examples/c/cmake_build + mkdir examples/cpp/cmake_build + + - name: build flecs + working-directory: cmake_build + run: | + cmake ${{ matrix.toolset_option }} -DFLECS_STRICT=ON .. + cmake --build . -j 4 + + - name: build c examples + working-directory: examples/c/cmake_build + run: | + cmake ${{ matrix.toolset_option }} -DFLECS_STRICT=ON .. + cmake --build . -j 4 + + - name: build c++ examples + working-directory: examples/cpp/cmake_build + run: | + cmake ${{ matrix.toolset_option }} -DFLECS_STRICT=ON .. + cmake --build . -j 4 + + build-meson: + needs: build-linux + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v3 + + - name: install meson + run: | + pip3 install meson + pip3 install ninja==1.10.2.4 + + - name: create build folder + run: meson meson_build + + - name: build flecs + working-directory: meson_build + run: | + meson compile + + build-custom: + needs: build-linux + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + compiler: [ gcc, clang ] + + steps: + - uses: actions/checkout@v3 + - name: Install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: FLECS_SOFT_ASSERT flag + run: | + bake rebuild --strict -D FLECS_SOFT_ASSERT + bake rebuild --strict --cfg release -D FLECS_SOFT_ASSERT + + - name: FLECS_KEEP_ASSERT flag + run: | + bake rebuild --strict -D FLECS_KEEP_ASSERT + bake rebuild --strict --cfg release -D FLECS_KEEP_ASSERT + + - name: no extensions + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD + + - name: FLECS_SYSTEM + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_SYSTEM + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_SYSTEM + + - name: FLECS_PIPELINE + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PIPELINE + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PIPELINE + + - name: FLECS_TIMER + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_TIMER + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_TIMER + + - name: FLECS_MODULE + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_MODULE + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_MODULE + + - name: FLECS_SNAPSHOT + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_SNAPSHOT + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_SNAPSHOT + + - name: FLECS_STATS + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_STATS + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_STATS + + - name: FLECS_PARSER + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PARSER + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PARSER + + - name: FLECS_PLECS + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PLECS + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PLECS + + - name: FLECS_META + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_META + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_META + + - name: FLECS_META_C + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_META_C + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_META_C + + - name: FLECS_EXPR + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_EXPR + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_EXPR + + - name: FLECS_JSON + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_JSON + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_JSON + + - name: FLECS_DOC + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_DOC + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_DOC + + - name: FLECS_COREDOC + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_COREDOC + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_COREDOC + + - name: FLECS_LOG + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_LOG + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_LOG + + - name: FLECS_APP + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_APP + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_APP + + - name: FLECS_OS_API_IMPL + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_OS_API_IMPL + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_OS_API_IMPL + + - name: FLECS_HTTP + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_HTTP + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_HTTP + + - name: FLECS_REST + run: | + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_REST + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_REST + + - name: FLECS_NO_LOG + run: | + bake rebuild --strict -D FLECS_NO_LOG + bake rebuild --strict --cfg release -D FLECS_NO_LOG + + - name: FLECS_DEFAULT + run: | + bake rebuild --strict + bake rebuild --strict --cfg release + + - name: custom_build tests + run: | + bake rebuild test/custom_builds --strict + bake runall test/custom_builds + + build-amalgamated: + needs: build-linux + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + compiler: [ gcc, clang ] + + steps: + - uses: actions/checkout@v3 + - name: build flecs + run: ${{ matrix.compiler }} flecs.c --shared -fPIC -pedantic -Wall -Wextra -Wno-unused-parameter -Werror -Wshadow -Wconversion -Wno-missing-field-initializers + + build-scan-build: + needs: build-linux + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v3 + + - name: install clang-build + run: | + sudo apt-get install -y clang-tools + + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: run scan-build + run: | + scan-build --status-bugs bake + + test-c-unix: + needs: [build-linux, build-macos] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, macOS-latest ] + + steps: + - uses: actions/checkout@v3 + + - name: compiler version + run: | + gcc --version + clang --version + + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: test api + run: bake run test/api -- -j 8 + + - name: test addons + run: bake run test/addons -- -j 8 + + - name: test meta + run: bake run test/meta -- -j 8 + + - name: test collections + run: bake run test/collections -- -j 8 + + test-cpp-unix: + needs: [build-linux] + runs-on: ${{ matrix.target.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + target: + - { os: ubuntu-20.04, cc: gcc-9, cxx: g++-9 } + - { os: ubuntu-20.04, cc: gcc-10, cxx: g++-10 } + - { os: ubuntu-20.04, cc: gcc-11, cxx: g++-11 } + - { os: ubuntu-latest, cc: gcc-12, cxx: g++-12 } + - { os: ubuntu-latest, cc: gcc-13, cxx: g++-13 } + - { os: ubuntu-20.04, cc: clang-8, cxx: clang++-8 } + - { os: ubuntu-20.04, cc: clang-9, cxx: clang++-9 } + - { os: ubuntu-20.04, cc: clang-10, cxx: clang++-10 } + - { os: ubuntu-20.04, cc: clang-11, cxx: clang++-11 } + - { os: ubuntu-20.04, cc: clang-12, cxx: clang++-12 } + - { os: ubuntu-latest, cc: clang-13, cxx: clang++-13 } + - { os: ubuntu-latest, cc: clang-14, cxx: clang++-14 } + - { os: ubuntu-latest, cc: clang-15, cxx: clang++-15 } + + env: + CC: ${{ matrix.target.cc }} + CXX: ${{ matrix.target.cxx }} + + steps: + - uses: actions/checkout@v3 + + - name: install compiler + run: | + sudo apt-get update + sudo apt-get install -y ${{ matrix.target.cc }} + sudo apt-get install -y ${{ matrix.target.cxx }} + + - name: compiler version + run: | + ${{ matrix.target.cc }} --version + ${{ matrix.target.cxx }} --version + + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: test c++ + run: bake run test/cpp_api -- -j 8 + + test-cpp-macos: + needs: [build-macos] + runs-on: ${{ matrix.os.version }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: + - { version: macOS-11, xcode: '11.7' } + - { version: macOS-11, xcode: '12.4' } + - { version: macOS-11, xcode: '13.0' } + - { version: macOS-12, xcode: '13.1' } + - { version: macOS-12, xcode: '14.0' } + + env: + CC: clang + CXX: clang++ + + steps: + - uses: actions/checkout@v3 + + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: ${{ matrix.os.xcode }} + + - name: compiler version + run: | + clang --version + + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: test c++ + run: bake run test/cpp_api -- -j 8 + + test-windows: + needs: build-windows + runs-on: windows-latest + timeout-minutes: 30 + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v3 + - uses: ilammy/msvc-dev-cmd@v1 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + cd bake\build-Windows + nmake + cd .. + ./bake setup --local + + - name: build flecs + run: bake/bake + + - name: test api + run: bake/bake run test\api -- -j 8 + + - name: test addons + run: bake/bake run test\addons -- -j 8 + + - name: test meta + run: bake/bake run test\meta -- -j 8 + + - name: test collections + run: bake/bake run test\collections -- -j 8 + + - name: test c++ + run: bake/bake run test\cpp_api -- -j 8 + + test-msys: + runs-on: windows-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - { sys: mingw64 } + - { sys: mingw32 } + - { sys: ucrt64 } + - { sys: clang64 } + + defaults: + run: + shell: msys2 {0} + + steps: + - uses: actions/checkout@v3 + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.sys}} + update: true + install: >- + curl + git + pacboy: >- + toolchain:p + cmake:p + + - name: install bake + run: | + cp `which mingw32-make` /usr/bin/make + git clone https://github.com/SanderMertens/bake + make -C bake/build-Mingw + bake/bake setup + + - name: build flecs + run: bake/bake --strict + + - name: test api + run: bake/bake run test/api -- -j 8 + + - name: test addons + run: bake/bake run test/addons -- -j 8 + + - name: test meta + run: bake/bake run test/meta -- -j 8 + + - name: test collections + run: bake/bake run test/collections -- -j 8 + + - name: test c++ + run: bake/bake run test/cpp_api -- -j 8 + + test-sanitized-api: + needs: [ build-linux ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest ] + + steps: + - uses: actions/checkout@v3 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: run tests + run: | + bake run test/api --cfg sanitize -- -j 8 + + test-sanitized-addons: + needs: [ build-linux ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest ] + + steps: + - uses: actions/checkout@v3 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: run tests + run: | + bake run test/addons --cfg sanitize -- -j 8 + + test-sanitized-meta: + needs: [ build-linux ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest ] + + steps: + - uses: actions/checkout@v3 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: run tests + run: | + bake run test/meta --cfg sanitize -- -j 8 + + test-sanitized-collections: + needs: [ build-linux ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest ] + + steps: + - uses: actions/checkout@v3 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: run tests + run: | + bake run test/collections --cfg sanitize -- -j 8 + + test-sanitized-cpp_api: + needs: [ build-linux ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest ] + + steps: + - uses: actions/checkout@v3 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: run tests + run: | + bake run test/cpp_api --cfg sanitize -- -j 8 + + test-cmake: + needs: [ build-linux, build-macos ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, macOS-latest ] + + steps: + - uses: actions/checkout@v3 + - name: clone bake + run: | + git clone https://github.com/SanderMertens/bake + + - name: build flecs & tests + run: | + cmake -DFLECS_TESTS=ON -DBAKE_DIRECTORY=bake + cmake --build . -j 4 + + - name: run tests + run: | + ctest -C Debug --verbose diff --git a/engine/libs/flecs/.github/workflows/pages.yml b/engine/libs/flecs/.github/workflows/pages.yml new file mode 100644 index 0000000..fd30202 --- /dev/null +++ b/engine/libs/flecs/.github/workflows/pages.yml @@ -0,0 +1,50 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Pages deployment + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Pages + uses: actions/configure-pages@v2 + + - name: Doxygen + uses: mattnotmitt/doxygen-action@v1.9.5 + with: + doxyfile-path: 'docs/cfg/Doxyfile' + + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + # Upload entire repository + path: 'docs/html' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/engine/libs/flecs/.gitignore b/engine/libs/flecs/.gitignore new file mode 100644 index 0000000..ceb92aa --- /dev/null +++ b/engine/libs/flecs/.gitignore @@ -0,0 +1,18 @@ +.DS_Store +bin +.bake_cache +.vscode +gcov +.idea +*.pdb +deps +cmake-build-debug +bazel-bin +bazel-flecs +bazel-bazel +bazel-genfiles +bazel-out +bazel-testlogs +**/CMakeFiles/* +.vs +out diff --git a/engine/libs/flecs/BUILD b/engine/libs/flecs/BUILD new file mode 100644 index 0000000..760c455 --- /dev/null +++ b/engine/libs/flecs/BUILD @@ -0,0 +1,9 @@ + +cc_library( + name = "flecs", + visibility = ["//visibility:public"], + + srcs = glob(["src/**/*.c", "src/**/*.h", "src/**/*.inl"]), + hdrs = glob(["include/**/*.h", "include/**/*.hpp", "include/**/*.inl"]), + includes = ["include"], +) diff --git a/engine/libs/flecs/CMakeLists.txt b/engine/libs/flecs/CMakeLists.txt new file mode 100644 index 0000000..3b0a274 --- /dev/null +++ b/engine/libs/flecs/CMakeLists.txt @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 3.5) +cmake_policy(SET CMP0063 NEW) + +project(flecs LANGUAGES C) + +option(FLECS_STATIC "Build static flecs lib" ON) +option(FLECS_SHARED "Build shared flecs lib" ON) +option(FLECS_PIC "Compile static flecs lib with position independent code (PIC)" ON) +option(FLECS_TESTS "Build flecs tests" OFF) + +include(cmake/target_default_compile_warnings.cmake) +include(cmake/target_default_compile_options.cmake) + +# Automatically generate the same folder structure in Visual Studio as we have on disk +macro(GroupSources curdir) + file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/${curdir} ${PROJECT_SOURCE_DIR}/${curdir}/*) + foreach(child ${children}) + if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child}) + GroupSources(${curdir}/${child}) + else() + string(REPLACE "/" "\\" groupname ${curdir}) + source_group(${groupname} FILES ${PROJECT_SOURCE_DIR}/${curdir}/${child}) + endif() + endforeach() +endmacro() + +file(GLOB children RELATIVE ${PROJECT_SOURCE_DIR}/. ${PROJECT_SOURCE_DIR}/./*) +foreach(child ${children}) + if(IS_DIRECTORY ${PROJECT_SOURCE_DIR}/${curdir}/${child}) + GroupSources(${child}) + endif() +endforeach() + +file(GLOB_RECURSE INC include/*.h include/*.hpp) +file(GLOB_RECURSE SRC src/*.c) + +set(FLECS_TARGETS "") + +macro(add_flecs_target TARGET CONFIG) + add_library(${TARGET} ${CONFIG} ${INC} ${SRC}) + add_library(flecs::${TARGET} ALIAS ${TARGET}) + + target_default_compile_options_c(${TARGET}) + target_default_compile_warnings_c(${TARGET}) + + if(WIN32) + target_link_libraries(${TARGET} wsock32 ws2_32) + endif() + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(${TARGET} pthread) + endif() + + if(FLECS_PIC) + set_property(TARGET ${TARGET} PROPERTY POSITION_INDEPENDENT_CODE ON) + endif() + + target_include_directories(${TARGET} PUBLIC + $ + $) + + list(APPEND FLECS_TARGETS ${TARGET}) +endmacro() + +if(FLECS_SHARED) + add_flecs_target(flecs SHARED) +endif() + +if(FLECS_STATIC) + add_flecs_target(flecs_static STATIC) + target_compile_definitions(flecs_static PUBLIC flecs_STATIC) +endif() + +if(FLECS_TESTS) + enable_testing() + add_subdirectory(test) +endif() + +message(STATUS "Targets: ${FLECS_TARGETS}") + +# define the install steps +include(GNUInstallDirs) +install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.h" + PATTERN "*.hpp" + PATTERN "*.inl") + +install(TARGETS ${FLECS_TARGETS} + EXPORT flecs-export + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +install(EXPORT flecs-export + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/flecs + NAMESPACE flecs:: + FILE flecs-config.cmake) diff --git a/engine/libs/flecs/LICENSE b/engine/libs/flecs/LICENSE new file mode 100644 index 0000000..8d7b5a0 --- /dev/null +++ b/engine/libs/flecs/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Sander Mertens + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/engine/libs/flecs/README.md b/engine/libs/flecs/README.md new file mode 100644 index 0000000..c3705c3 --- /dev/null +++ b/engine/libs/flecs/README.md @@ -0,0 +1,211 @@ +![flecs](docs/img/logo.png) + +[![Version](https://img.shields.io/github/v/release/sandermertens/flecs?include_prereleases&style=for-the-badge)](https://github.com/SanderMertens/flecs/releases) +[![MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](https://github.com/SanderMertens/flecs/blob/master/LICENSE) +[![Documentation](https://img.shields.io/badge/docs-flecs-blue?style=for-the-badge&color=blue)](https://www.flecs.dev/flecs/md_docs_Docs.html) +[![actions](https://img.shields.io/github/actions/workflow/status/SanderMertens/flecs/ci.yml?branch=master&style=for-the-badge)](https://github.com/SanderMertens/flecs/actions?query=workflow%3ACI) +[![Discord Chat](https://img.shields.io/discord/633826290415435777.svg?style=for-the-badge&color=%235a64f6)](https://discord.gg/BEzP5Rgrrp) + +Flecs is a fast and lightweight Entity Component System that lets you build games and simulations with millions of entities ([join the Discord!](https://discord.gg/BEzP5Rgrrp)). Here are some of the framework's highlights: + +- Fast and [portable](#language-bindings) zero dependency [C99 API](https://www.flecs.dev/flecs/group__c.html) +- Modern type-safe [C++11 API](https://www.flecs.dev/flecs/group__cpp.html) that doesn't use STL containers +- First open source ECS with full support for [Entity Relationships](https://www.flecs.dev/flecs/md_docs_Relationships.html)! +- Fast native support for [hierarchies](https://www.flecs.dev/flecs/md_docs_Relationships.html#autotoc_md277) and [prefabs](https://www.flecs.dev/flecs/md_docs_Relationships.html#autotoc_md305) +- Code base that builds in less than 5 seconds +- Runs [in the browser](https://flecs.dev/city) without modifications with emscripten +- Cache friendly [archetype/SoA storage](https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9) that can process millions of entities every frame +- Supports entities with hundreds of components and applications with tens of thousands of archetypes +- Automatic component registration that works out of the box across shared libraries/DLLs +- Write free functions with [queries](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/queries/basics) or run code automatically in [systems](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/systems/pipeline) +- Run games on multiple CPU cores with a fast lockless scheduler +- Verified on all major compilers and platforms with [CI](https://github.com/SanderMertens/flecs/actions) running more than 6000 tests +- Integrated [reflection framework](https://www.flecs.dev/flecs/group__c__addons__meta.html) with [JSON serializer](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection/basics_json) and support for [runtime components](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection/runtime_component) +- [Unit annotations](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection/units) for components +- Powerful [query language](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules) with support for [joins](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules/setting_variables) and [inheritance](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules/component_inheritance) +- [Statistics addon](https://www.flecs.dev/flecs/group__c__addons__stats.html) for profiling ECS performance +- A web-based UI for monitoring & controlling your apps ([demo](https://flecs.dev/explorer), [code](https://github.com/flecs-hub/explorer)): + +[![Flecs Explorer](docs/img/explorer.png)](https://flecs.dev/explorer) + +To support the project, give it a star 🌟 ! + +## What is an Entity Component System? +ECS is a way of organizing code and data that lets you build games that are larger, more complex and are easier to extend. Something is called an ECS when it: +- Has _entities_ that uniquely identify objects in a game +- Has _components_ which are datatypes that can be added to entities +- Has _systems_ which are functions that run for all entities matching a component _query_ + +For more information, check the [ECS FAQ](https://github.com/SanderMertens/ecs-faq)! + +## Try it out! +The [Flecs playground](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js) lets you try Flecs without writing any C/C++ code! + +[![Flecs playground](docs/img/playground.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js) + +To learn how to use the playground, check the [Flecs Script Tutorial](https://www.flecs.dev/flecs/md_docs_FlecsScriptTutorial.html). + +## Documentation +- [Quickstart](https://www.flecs.dev/flecs/md_docs_Quickstart.html) +- [FAQ](https://www.flecs.dev/flecs/md_docs_FAQ.html) +- [Examples](https://github.com/SanderMertens/flecs/tree/master/examples) +- [All Documentation](https://www.flecs.dev/flecs/md_docs_Docs.html) + +## Performance +For a list of regularly tracked benchmarks, see the [ECS Benchmark](https://github.com/SanderMertens/ecs_benchmark) project. + +## Show me the code! +C99 example: +```c +typedef struct { + float x, y; +} Position, Velocity; + +void Move(ecs_iter_t *it) { + Position *p = ecs_field(it, Position, 1); + Velocity *v = ecs_field(it, Velocity, 2); + + for (int i = 0; i < it->count; i++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} + +int main(int argc, char *argv[]) { + ecs_world_t *ecs = ecs_init(); + + ECS_COMPONENT(ecs, Position); + ECS_COMPONENT(ecs, Velocity); + + ECS_SYSTEM(ecs, Move, EcsOnUpdate, Position, Velocity); + + ecs_entity_t e = ecs_new_id(ecs); + ecs_set(ecs, e, Position, {10, 20}); + ecs_set(ecs, e, Velocity, {1, 2}); + + while (ecs_progress(ecs, 0)) { } +} +``` + +Same example in C++11: + +```cpp +struct Position { + float x, y; +}; + +struct Velocity { + float x, y; +}; + +int main(int argc, char *argv[]) { + flecs::world ecs; + + ecs.system() + .each([](Position& p, const Velocity& v) { + p.x += v.x; + p.y += v.y; + }); + + auto e = ecs.entity() + .set([](Position& p, Velocity& v) { + p = {10, 20}; + v = {1, 2}; + }); + + while (ecs.progress()) { } +} +``` + +## Projects using Flecs +If you have a project you'd like to share, let me know on [Discord](https://discord.gg/BEzP5Rgrrp)! + +### Tempest Rising +https://store.steampowered.com/app/1486920/Tempest_Rising/ +[![Tempest Rising](docs/img/projects/tempest_rising.png)](https://store.steampowered.com/app/1486920/Tempest_Rising/) + +### Territory Control +https://store.steampowered.com/app/690290/Territory_Control_2/ +[![image](docs/img/projects/territory_control.jpeg)](https://store.steampowered.com/app/690290/Territory_Control_2/) + +### The Forge +https://github.com/ConfettiFX/The-Forge +[![image](docs/img/projects/the_forge.jpg)](https://github.com/ConfettiFX/The-Forge) + +### Sol Survivor +https://nicok.itch.io/sol-survivor-demo +[![image](docs/img/projects/sol_survivor.png)](https://nicok.itch.io/sol-survivor-demo) + +### Equilibrium Engine +https://github.com/clibequilibrium/EquilibriumEngine +[![image](docs/img/projects/equilibrium_engine.png)](https://github.com/clibequilibrium/EquilibriumEngine) + +### Gravitas +https://thepunkcollective.itch.io/gravitas +[![image](docs/img/projects/gravitas.png)](https://thepunkcollective.itch.io/gravitas) + +### After Sun +https://github.com/foxnne/aftersun +[![image](docs/img/projects/after_sun.png)](https://github.com/foxnne/aftersun) + +### Tower defense (open source demo) +https://www.flecs.dev/tower_defense/etc ([repository](https://github.com/SanderMertens/tower_defense)) +![image](docs/img/projects/tower_defense.png) + +### Procedural City (open source demo) +https://www.flecs.dev/city ([repository](https://github.com/flecs-hub/city)) +![image](docs/img/projects/city.png) + +## Resources + +### Resources provided by the community :heart: +- [Unreal Minimum Viable Flecs Project](https://github.com/PreyK/Unreal-Minimum-Viable-Flecs) +- [Bgfx/Imgui module](https://github.com/flecs-hub/flecs-systems-bgfx/tree/bgfx_imgui) +- [Tower defense example](https://gist.github.com/oldmanauz/b4ced44737bf9d248233538fa06a989e) +- [Flecs + UE4 is magic](https://jtferson.github.io/blog/flecs_and_unreal/) +- [Quickstart with Flecs in UE4](https://jtferson.github.io/blog/quickstart_with_flecs_in_unreal_part_1/) +- [Automatic component registration in UE4](https://jtferson.github.io/blog/automatic_flecs_component_registration_in_unreal/) +- [Building a space battle with Flecs in UE4](https://twitter.com/ajmmertens/status/1361070033334456320) +- [Flecs + SDL + Web ASM example](https://github.com/HeatXD/flecs_web_demo) ([live demo](https://heatxd.github.io/flecs_web_demo/)) +- [Flecs + Raylib example](https://github.com/Lexxicon/FlecsRaylib) +- [Flecs + gunslinger example](https://github.com/MrFrenik/gs_examples/blob/main/ex_demos/flecs/source/main.c) +- [Flecs based 3D game engine with editor](https://bit.ly/3T9cc1o) + +### Flecs around the web +- [Discord](https://discord.gg/BEzP5Rgrrp) +- [Medium](https://ajmmertens.medium.com) +- [Twitter](https://twitter.com/ajmmertens) +- [Reddit](https://www.reddit.com/r/flecs) + +## Flecs Hub +[Flecs Hub](https://github.com/flecs-hub) is a collection of repositories that show how Flecs can be used to build game systems like input handling, hierarchical transforms and rendering. + +Module | Description +------------|------------------ +[flecs.components.cglm](https://github.com/flecs-hub/flecs-components-cglm) | Component registration for cglm (math) types +[flecs.components.input](https://github.com/flecs-hub/flecs-components-input) | Components that describe keyboard and mouse input +[flecs.components.transform](https://github.com/flecs-hub/flecs-components-transform) | Components that describe position, rotation and scale +[flecs.components.physics](https://github.com/flecs-hub/flecs-components-physics) | Components that describe physics and movement +[flecs.components.geometry](https://github.com/flecs-hub/flecs-components-geometry) | Components that describe geometry +[flecs.components.graphics](https://github.com/flecs-hub/flecs-components-graphics) | Components used for computer graphics +[flecs.components.gui](https://github.com/flecs-hub/flecs-components-gui) | Components used to describe GUI components +[flecs.systems.transform](https://github.com/flecs-hub/flecs-systems-transform) | Hierarchical transforms for scene graphs +[flecs.systems.physics](https://github.com/flecs-hub/flecs-systems-physics) | Systems for moving objects and collision detection +[flecs.systems.sokol](https://github.com/flecs-hub/flecs-systems-sokol) | Sokol-based renderer +[flecs.game](https://github.com/flecs-hub/flecs-game) | Generic game systems, like a camera controller + +## Language bindings +The following language bindings have been developed with Flecs! Note that these are projects built and maintained by helpful community members, and may not always be up to date with the latest commit from master! +- C#: + - [flecs-hub/flecs-cs](https://github.com/flecs-hub/flecs-cs) + - [BeanCheeseBurrito/Flecs.NET](https://github.com/BeanCheeseBurrito/Flecs.NET) +- Rust: + - [flecs-rs](https://github.com/jazzay/flecs-rs) + - [flecs-polyglot](https://github.com/flecs-hub/flecs-polyglot) +- Zig: + - [michal-z/zig-gamedev](https://github.com/michal-z/zig-gamedev/tree/main/libs/zflecs) + - [foxnne/zig-flecs](https://github.com/foxnne/zig-flecs) + - [prime31/zig-flecs](https://github.com/prime31/zig-flecs) +- Lua: + - [sro5h/flecs-luajit](https://github.com/sro5h/flecs-luajit) + - [flecs-hub/flecs-lua](https://github.com/flecs-hub/flecs-lua) diff --git a/engine/libs/flecs/WORKSPACE b/engine/libs/flecs/WORKSPACE new file mode 100644 index 0000000..34536eb --- /dev/null +++ b/engine/libs/flecs/WORKSPACE @@ -0,0 +1,52 @@ +load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") + +new_git_repository( + name = "bake", + remote = "git@github.com:SanderMertens/bake.git", + commit = "cfc90745f9daa7b7fba80f229af18cdd5029b066", + shallow_since = "1614835160 -0800", + + build_file_content = """ +cc_library( + name = "driver-test", + visibility = ["//visibility:public"], + deps = [":util", ":bake"], + + srcs = glob(["drivers/test/src/**/*.c", "drivers/test/src/**/*.h"]), + hdrs = glob(["drivers/test/include/**/*.h"]), + includes = ["drivers/test/include"], +) + +cc_library( + name = "bake", + visibility = ["//visibility:public"], + deps = [":util"], + + srcs = glob(["src/*.c", "src/*.h"]), + hdrs = glob(["include/*.h", "include/bake/*.h"]), + includes = ["include"], +) + +cc_library( + name = "util", + visibility = ["//visibility:public"], + defines = ["__BAKE__", "_XOPEN_SOURCE=600"], + + linkopts = select({ + "@bazel_tools//src/conditions:windows": [], + "//conditions:default": ["-lrt -lpthread -ldl"], + }), + + srcs = glob(["util/src/*.c"]) + select({ + "@bazel_tools//src/conditions:windows": glob(["util/src/win/*.c"]), + "//conditions:default": glob(["util/src/posix/*.c"]), + }), + hdrs = glob(["util/include/*.h", "util/include/bake-util/*.h"]) + select({ + "@bazel_tools//src/conditions:windows": glob(["util/include/bake-util/win/*.h"]), + "//conditions:default": glob(["util/include/bake-util/posix/*.h"]), + }), + includes = ["util/include"], +) +""" +) + diff --git a/engine/libs/flecs/cmake/target_default_compile_functions.cmake b/engine/libs/flecs/cmake/target_default_compile_functions.cmake new file mode 100644 index 0000000..23116be --- /dev/null +++ b/engine/libs/flecs/cmake/target_default_compile_functions.cmake @@ -0,0 +1,48 @@ + +macro(list_targets RESULT) + file(GLOB_RECURSE FILES LIST_DIRECTORIES true RELATIVE ${CUR_DIR} *) + set(TARGET_LIST "") + + foreach (FILE ${FILES}) + if (IS_DIRECTORY ${CUR_DIR}/${FILE}) + if (EXISTS ${CUR_DIR}/${FILE}/project.json) + message(STATUS "Found example ${FILE}") + list(APPEND TARGET_LIST ${FILE}) + endif() + endif () + endforeach () + + set(${RESULT} ${TARGET_LIST}) +endmacro() + +macro(create_target TARGET CONFIG) + if (CONFIG STREQUAL "") + set(TARGET_POSTFIX "") + else() + set(TARGET_POSTFIX "_${CONFIG}") + endif() + + set(TARGET_NAME "${TARGET}${TARGET_POSTFIX}") + string(REPLACE "/" "_" TARGET_NAME "${TARGET_NAME}") + string(REPLACE "\\" "_" TARGET_NAME "${TARGET_NAME}") + set(TARGET_DIR "${CUR_DIR}/${TARGET}") + + file(GLOB SRC ${TARGET_DIR}/src/*.c*) + add_executable(${TARGET_NAME} ${SRC}) + + target_include_directories(${TARGET_NAME} PUBLIC ${FLECS_DIR}/include) + target_include_directories(${TARGET_NAME} PUBLIC ${TARGET_DIR}/include) + target_link_libraries(${TARGET_NAME} flecs${TARGET_POSTFIX}) + endmacro() + +function(create_target_c TARGET CONFIG) + create_target("${TARGET}" "${CONFIG}") + target_default_compile_options_c(${TARGET_NAME}) + target_default_compile_warnings_c(${TARGET_NAME}) +endfunction() + +function(create_target_cxx TARGET CONFIG) + create_target("${TARGET}" "${CONFIG}") + target_default_compile_options_cxx(${TARGET_NAME}) + target_default_compile_warnings_cxx(${TARGET_NAME}) +endfunction() diff --git a/engine/libs/flecs/cmake/target_default_compile_options.cmake b/engine/libs/flecs/cmake/target_default_compile_options.cmake new file mode 100644 index 0000000..0610265 --- /dev/null +++ b/engine/libs/flecs/cmake/target_default_compile_options.cmake @@ -0,0 +1,26 @@ +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +option(FLECS_STRICT "Stricter warning options and Werror" OFF) + +function(target_default_compile_options_c THIS) + + set_target_properties(${THIS} PROPERTIES + LINKER_LANGUAGE C + C_STANDARD 99 + C_STANDARD_REQUIRED ON + C_VISIBILITY_PRESET hidden) + +endfunction() + +function(target_default_compile_options_cxx THIS) + + set_target_properties(${THIS} PROPERTIES + LINKER_LANGUAGE CXX + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON) + +endfunction() diff --git a/engine/libs/flecs/cmake/target_default_compile_warnings.cmake b/engine/libs/flecs/cmake/target_default_compile_warnings.cmake new file mode 100644 index 0000000..ddd64d7 --- /dev/null +++ b/engine/libs/flecs/cmake/target_default_compile_warnings.cmake @@ -0,0 +1,113 @@ +function(target_default_compile_warnings_c THIS) + + if (FLECS_STRICT) + + if (CMAKE_C_COMPILER_ID STREQUAL "Clang" + OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang") + + target_compile_options(${THIS} PRIVATE + $<$:-Wshadow> + $<$:-Wunused> + -Wall -Wextra -Werror + -Wcast-align + -Wpedantic + -Wconversion + -Wsign-conversion + -Wdouble-promotion + -Wno-missing-prototypes + -Wno-missing-variable-declarations) + + elseif (CMAKE_C_COMPILER_ID STREQUAL "GNU") + + target_compile_options(${THIS} PRIVATE + $<$:-Wshadow> + $<$:-Wunused> + -Wall -Wextra -Werror + -Wcast-align + -Wpedantic + -Wconversion + -Wsign-conversion + -Wdouble-promotion + -Wno-missing-prototypes + -Wno-missing-variable-declarations) + + elseif (CMAKE_C_COMPILER_ID STREQUAL "MSVC") + + target_compile_options(${THIS} PRIVATE + /W3 /WX + /w14242 /w14254 /w14263 + /w14265 /w14287 /we4289 + /w14296 /w14311 /w14545 + /w14546 /w14547 /w14549 + /w14555 /w14619 /w14640 + /w14826 /w14905 /w14906 + /w14928) + + else () + + message(WARNING + "No warning settings available for ${CMAKE_C_COMPILER_ID}. ") + + endif () + + endif () + +endfunction() + +function(target_default_compile_warnings_cxx THIS) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + + target_compile_options(${THIS} PRIVATE + #$<$:-Werror> + $<$:-Wshadow> + $<$:-Wunused> + -Wall -Wextra + -Wnon-virtual-dtor + -Wold-style-cast + -Wcast-align + -Woverloaded-virtual + -Wpedantic + -Wconversion + -Wsign-conversion + -Wdouble-promotion) + + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + + target_compile_options(${THIS} PRIVATE + #$<$:-Werror> + $<$:-Wshadow> + $<$:-Wunused> + -Wall -Wextra + -Wnon-virtual-dtor + -Wold-style-cast + -Wcast-align + -Woverloaded-virtual + -Wpedantic + -Wconversion + -Wsign-conversion + -Wdouble-promotion) + + elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + + target_compile_options(${THIS} PRIVATE + #$<$:/WX> + /W3 + /w14242 /w14254 /w14263 + /w14265 /w14287 /we4289 + /w14296 /w14311 /w14545 + /w14546 /w14547 /w14549 + /w14555 /w14619 /w14640 + /w14826 /w14905 /w14906 + /w14928) + + else () + + message(WARNING + "No Warnings specified for ${CMAKE_CXX_COMPILER_ID}. " + "Consider using one of the following compilers: Clang, GNU, MSVC, AppleClang.") + + endif () + +endfunction() diff --git a/engine/libs/flecs/codecov.yaml b/engine/libs/flecs/codecov.yaml new file mode 100644 index 0000000..fc77636 --- /dev/null +++ b/engine/libs/flecs/codecov.yaml @@ -0,0 +1,21 @@ +codecov: + notify: + require_ci_to_pass: no + +coverage: + precision: 2 + round: down + range: "70...100" + + status: + project: yes + patch: yes + changes: no + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no diff --git a/engine/libs/flecs/docs/DesignWithFlecs.md b/engine/libs/flecs/docs/DesignWithFlecs.md new file mode 100644 index 0000000..e8a08df --- /dev/null +++ b/engine/libs/flecs/docs/DesignWithFlecs.md @@ -0,0 +1,175 @@ +# Designing with Flecs +Designing games with ECS can be overwhelming and quite different from object oriented approaches, not to mention learning all the framework specific features! This guide provides a few quick tips for using the features provided by Flecs to build fast, readable and reusable code. + +Note that these are my own guidelines, and as a result this is an opinionated document. There are many other approaches to designing with ECS, and it would be silly to claim that this is "the one true way". Take this with a grain of salt, and feel free to deviate when you feel it is appropriate. + +One other note: this document is very light on feature documentation. The point of the document is to provide suggestions for how to design with Flecs features, not document their ins and outs. All of the features listed in this document are described in the manual and have example code. + +## Entities +Entities are probably the easiest thing to get used to. They typically map to in-game objects the same way you would create them in other game engines. + +### Entity Initialization +When creating entities, you typically want to initialize them with a set of default components, and maybe even default values. Flecs introduced prefabs for this use case. Prefabs let you create entity templates that contain components with default values. Creating entities from prefabs is not just an easy way to initialize entities, it is also faster, and helps with classifying the different kinds of entities you have. + +### Entity Lifecycle +Entities can be created and deleted dynamically. When entities are deleted, the existing handles to that entity are no longer valid. When you are working with entity handles, it is often good practice to make sure they are still alive. You can do this with the `is_alive()` function. Other than that, entity handles are stable, which means that you can safely store them in your components or elsewhere. + +### Entity Names +Flecs entities can be named. This makes it easy to identify entities in editors or while debugging, and also allows you to lookup entities by name. Names must be unique inside a scope, which is determined by the `ChildOf` relationship. For example, two entities with the same parent must have different names. + +Names can be looked up using a relative path or absolute path using the `ecs_lookup_fullpath` or `entity::lookup` functions. The default scope separator for C applications is a dot (`.`), whereas in C++ it is a double colon (`::`). Lookups use a hashmap internally, which provides O(1) performance. + +Entities can be assigned user friendly names with the doc addon, using the `ecs_doc_set_name` or `entity::set_doc_name` functions. Names assigned by the doc framework do not have to be unique. + +## Components +Designing your components is probably the most important thing you will do in your ECS application. The reason is that if you change a component you have to update all systems that use it. Fortunately components can be designed in a way that minimizes refactoring and does not negatively impact performance. + +### Component Size +The first tip is to try keep your components small and atomic. If you are choosing between a Transform component or separate Position, Rotation, Scale, Matrix components pick the latter. If you have a Turret component with a target and a rotation angle, split them up into two components. + +The first reason behind this is that querying for multiple components adds minimal overhead, because most queries are cached. + +The second reason is that it improves caching performance. If your system only needs Position, but also has to load all of the other data in Transform, you end up loading a lot of data in your cache that is not used. This means that useful data will get evicted from the cache more often, and that data needs to be loaded from RAM more often, which is much slower than when data is cached. + +The third reason is that it minimizes refactoring. There are only so many ways to split up components, and infinitely more ways to combine them. Once components are designed as atomic units of data there aren't many reasons to combine them which results in less refactoring. + +The fourth and last reason is that code using smaller components is more reusable. Reasons to combine two components may work well in one project, but not in another project. Atomic components are less opinionated since it's up to the project how they are combined in queries, which makes it more likely that they work well across projects. This trickles down to systems, as systems written for atomic components also end up being more reusable. + +A disadvantage of small components is that you get more of them in a project. This can make it harder to find the components a system needs, especially in large projects with hundreds of components. Tools like https://www.flecs.dev/explorer/ can help with finding and documenting components in a project. + +### Complex component data +There is a misconception that ECS components can only be plain data types, and should not have vectors, or more complex data structures. The reality is more nuanced. You may find yourself often needing specialized data structures, and it is perfectly fine to store these in components. + +## Queries +Queries are the primary method in Flecs for finding the entities for a set of components (or more specifically: a component expression). Queries are easy to use, but there a few things to keep in mind. + +### Use the right query +Flecs has cached queries and uncached queries. Cached queries (`ecs_query_t` and `flecs::query`) are expensive to create but very cheap to iterate. Uncached queries (`ecs_filter_t`, `flecs::filter`) are fast to create, but more expensive to iterate. If you need to do a quick ad-hoc query for which you couldn't know in advance what you had to query for, an uncached query is the best option. If you have a query that you know in advance and need to iterate many times, a cached query is preferred. + +Another difference is that uncached queries can be created from systems, while cached queries cannot. If you need a cached query in a system, it has to be created in advance and passed into the system, either by setting it as system context, adding a component to the system with the query, or passing it in the lambda capture list (C++ only). Systems themselves use cached queries. + +Make sure to not repeatedly create and destroy cached queries! For more information, see [the query manual](Queries.md#types) for more details. + +### Use in/inout/out annotations +Flecs analyzes how components are read and written by queries and uses this for things like change tracking and deciding when to merge command buffers. By default components are marked as `inout`. If a system only reads a component, make sure to mark it as `in`, as this can reduce the time spent by other parts of the application that rely on change detection and sync points. + +For more information, see [the query manual](Queries.md#access-modifiers). + +### Annotations +You can further annotate queries with components that are not matched with the query, but that are written using ECS operations (like add, remove, set etc.). Such operations are automatically deferred and merged at the end of the frame. With annotations you can enforce merging at an earlier point, by specifying that a component modification has been queued. When Flecs sees this, it will merge back the modifications before the next read. + +See the sync point examples for more detail: +- C: https://github.com/SanderMertens/flecs/blob/master/examples/c/systems/sync_point +- C++: https://github.com/SanderMertens/flecs/blob/master/examples/cpp/systems/sync_point + +## Systems +Designing systems is one of the hardest things to do when not coming from an ECS background. Object oriented code allows you to write logic that is local to a single object, whereas systems in ECS are ran for collections of similar objects. This requires a different approach towards design. + +### System Scope +Try to design your systems with a single responsibility. This can sometimes be difficult, especially if you are building new features and are not exactly sure yet what the end result will look like. That is fine. It is better to start with something that works, and refine it afterwards. If you find yourself with a system that does a lot of things, don't worry. Because systems in ECS are decoupled from everything else, it is generally pretty easy to split them up. + +While it is perfectly fine to have large systems in an ECS application, there are a few advantages to keeping them small: + +- Smaller systems make it easier to isolate behavior, as you can simply remove systems from your application that you don't want to test. +- Smaller systems usually have less complicated code, which can allow for more compiler optimizations (like auto vectorization). +- Smaller systems are easier to reuse across project. + +### System Scheduling +Flecs has the ability to run and schedule your systems for you. The advantage of this, versus manually listing and running systems, is that it is easier to import systems from multiple, reusable modules. + +A system is basically a combination of three things: a query, a function, and ordering information. The query finds the right entities, and the function is invoked with the matched entities. The ordering information makes sure that the system is inserted in the right point in your frame. If you get this right, you can import any number of systems into your project, and you can just run them without spending any time on manually sorting them out. + +The ordering information consists out of a phase (see phases and pipelines) and an implicit declaration order. Systems are ordered according to their phases first. Within a phase, they are ordered by declaration order. This may feel rigid, but is very deliberate. It prevents you from defining dependencies between systems, which can make it difficult to reuse systems across projects. + +On the other hand, if you are working with an existing framework or engine, you may not have the luxury of scheduling everything yourself. The engine may for example provide you with callbacks in which you need to do certain logic. Maybe you want to build your own threading system. In those situations it can make sense to take control of running systems yourself. + +Sometimes you may even not use systems at all, and just run queries. In this case you may want to disable the system addon (see the addsons section in the README). Note that some Flecs features depend on systems, like the REST API and timers. + +## Phases and Pipelines +Phases and pipelines are the primitives that Flecs uses to order systems. A pipeline is a set of ordered phases. Systems can be assigned to those phases. When using phases and pipelines correctly, it allows you to write plug & play systems that are easy to reuse in different projects. + +### Selecting a Phase +When you create a system, you can assign a phase to it. By default, that phase is `OnUpdate`. Flecs comes with a whole bunch of phases though, and just looking at the whole list can feel a bit overwhelming: + +- `OnLoad` +- `PostLoad` +- `PreUpdate` +- `OnUpdate` +- `OnValidate` +- `PostUpdate` +- `PreStore` +- `OnStore` + +So what do these all mean? Actually they mean nothing at all! They are just tags you can assign to systems, and those tags ensure that all systems in, say, the `PreUpdate` phase are executed _before_ the systems in the `OnUpdate` phase. What is also important to realize is that this list of phases is only the default provided by Flecs. Maybe your project needs only half of those, or maybe it needs entirely different ones! Flecs lets you specify your custom phases to match your project needs. + +There are some conventions around the builtin phases, and following them helps to ensure that your code works well with the Flecs module ecosystem. Here they are: + +### OnLoad +This phase contains all the systems that load data into your ECS. This would be a good place to load keyboard and mouse inputs. + +### PostLoad +Often the imported data needs to be processed. Maybe you want to associate your keypresses with high level actions rather than comparing explicitly in your game code if the user pressed the 'K' key. The PostLoad phase is a good place for this. + +### PreUpdate +Now that the input is loaded and processed, it's time to get ready to start processing our game logic. Anything that needs to happen after input processing but before processing the game logic can happen here. This can be a good place to prepare the frame, maybe clean up some things from the previous frame, etcetera. + +### OnUpdate +This is usually where the magic happens! This is where you put all of your gameplay systems. By default systems are added to this phase. + +### OnValidate +This phase was introduced to deal with validating the state of the game after processing the gameplay systems. Sometimes you moved entities too close to each other, or the speed of an entity is increased too much. This phase is for righting that wrong. A typical feature to implement in this phase would be collision detection. + +### PostUpdate +When your game logic has been updated, and your validation pass has ran, you may want to apply some corrections. For example, if your collision detection system detected collisions in the OnValidate phase, you may want to move the entities so that they no longer overlap. + +### PreStore +Now that all of the frame data is computed, validated and corrected for, it is time to prepare the frame for rendering. Any systems that need to run before rendering, but after processing the game logic should go here. A good example would be a system that calculates transform matrices from a scene graph. + +### OnStore +This is where it all comes together. Your frame is ready to be rendered, and that is exactly what you would do in this phase. + +That was a quick overview of all the builtin phases. Note that these are just guidelines! Feel free to deviate if your project calls for it. + +### Custom phases and pipelines +An application can add phases to the existing list, or define a pipeline from scratch. See the following examples on how to do this: + +- C: + - https://github.com/SanderMertens/flecs/tree/master/examples/c/systems/custom_phases + - https://github.com/SanderMertens/flecs/tree/master/examples/c/systems/custom_pipeline +- C++: + - https://github.com/SanderMertens/flecs/tree/master/examples/cpp/systems/custom_phases + - https://github.com/SanderMertens/flecs/tree/master/examples/cpp/systems/custom_pipeline + +## Modules +Large applications can often contain many components and systems. Some large commercial projects have reported up to 800 components! Managing all those components and systems becomes important on that scale, and that is what modules are for. Modules are one of those features that you usually don't think about when selecting an ECS, but they can make your life a lot easier. + +### Defining Modules +The purpose of modules is really to enable reusability. A well written module can be imported into any project, and will do its thing without any tweaking or tinkering. To achieve this, make sure to define your modules around features. Features seldomly consist out of a single system or component, but can have many. Examples are rendering, collision detection, input management, and so on. + +### Module Dependencies and Ordering +Modules can depend on each other. In fact, often do! Importing a module twice has no penalties in Flecs, it will not define your systems and components twice. This enables your application code to import the modules it needs, without having to worry about whether they were already loaded. + +The order in which you import dependencies in a module is important. If you define systems before the import, those systems will be ran _before_ the systems from the imported module _within the same phase_ (read this line a few times until you understand, it's important). This allows you some degree of flexibility around how systems from different modules should be scheduled. + +What is key to note here is that the granularity of control is at the module level, _never_ at the individual system level. The reason for this is that modules may reimplement their features with different systems. If you have inter-system dependencies, those could break easily every time you update a module. This also makes sure that you can replace one module for another without running into annoying compatibility issues. + +### Modules and Feature Swapping +A good practice to employ with modules is to split them up into components.* modules and systems.* modules. For example, you may have a module `components.physics` that contains all the components to store data for a physics system. You may then have a module called `systems.physics`, which imports the components module and "implements" the components. Because applications only have access to `components.physics` (they import it as well) but do not have direct access to the systems inside `systems.physics`, you can simply swap one physics implementation with another without changing application code. + +This is a powerful pattern enabled by ECS that will give your projects a lot of flexibility and freedom in refactoring code. + +### Module Overhead +You might wonder whether a module with lots of systems, of which only a few are used by your application makes your application slower. The answer is fortunately no. Flecs only inserts systems into the main loop that have matched with actual entities. Any imported systems that have never matched with anything remain dormant, and will not negatively affect performance. + +## Relationships +When you are working with Flecs, chances are that at some point you'll want to use relationships. The two most common uses for relationships are hierarchies like a scene graph (the `ChildOf` relationship) and prefabs (the `IsA` relationship). + +In many cases you might want to use your own relationships. Here are a few signs to look out for that can tell you to think about relationships: +- You have a component with an entity handle, and you need to find all entities that point to a specific entity. +- You have many components with the same or similar set of members, and systems that duplicate code for each component. +- You need to store multiple instances of the same component on an entity. +- You're designing some kind of container structure, like an inventory. +- You are looking to group entities by something like world cells or layers, and want to be able to lookup all entities for a cell. +- You're adding an enumeration type as component, but want to query for enumeration constants. + +See the [relationships blog](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c) and [relationships manual](Relationships.md) for more information. diff --git a/engine/libs/flecs/docs/Docs.md b/engine/libs/flecs/docs/Docs.md new file mode 100644 index 0000000..85a2f5e --- /dev/null +++ b/engine/libs/flecs/docs/Docs.md @@ -0,0 +1,37 @@ +# Documentation + +## Getting Started +- [FAQ](/flecs/md_docs_FAQ.html) +- [Quickstart](/flecs/md_docs_Quickstart.html) +- [Flecs Script Tutorial](/flecs/md_docs_FlecsScriptTutorial.html) +- [Designing with Flecs](/flecs/md_docs_DesignWithFlecs.html) +- [Getting Started with Unreal Engine](https://github.com/PreyK/Unreal-Minimum-Viable-Flecs) + +## Manuals +- [Manual](/flecs/md_docs_Manual.html) +- [Query Manual](/flecs/md_docs_Queries.html) +- [Systems Manual](/flecs/md_docs_Systems.html) +- [Relationships Manual](/flecs/md_docs_Relationships.html) +- [JSON Format Manual](/flecs/md_docs_JsonFormat.html) +- [REST API Manual](/flecs/md_docs_RestApi.html) + +## API reference + - [C API](/flecs/group__c.html) + - [C++ API](/flecs/group__cpp.html) + +## Examples +- [C examples](https://github.com/SanderMertens/flecs/tree/master/examples/c) +- [C++ examples](https://github.com/SanderMertens/flecs/tree/master/examples/cpp) + +## Demos +- [Tower Defense (C++)](https://github.com/SanderMertens/tower_defense) +- [City (C)](https://github.com/flecs-hub/city) + +## Articles +- [Where are my entities and components](https://ajmmertens.medium.com/building-an-ecs-1-where-are-my-entities-and-components-63d07c7da742) +- [Archetypes and vectorization](https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9) +- [Making the most of entity identifiers](https://ajmmertens.medium.com/doing-a-lot-with-a-little-ecs-identifiers-25a72bd2647) +- [Building games in ECS with entity relationships](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c) +- [Why storing state machines in ECS is a bad idea](https://ajmmertens.medium.com/why-storing-state-machines-in-ecs-is-a-bad-idea-742de7a18e59) +- [Why vanilla ECS is not enough](https://ajmmertens.medium.com/why-vanilla-ecs-is-not-enough-d7ed4e3bebe5) +- [ECS: From tool to paradigm](https://ajmmertens.medium.com/ecs-from-tool-to-paradigm-350587cdf216) diff --git a/engine/libs/flecs/docs/FAQ.md b/engine/libs/flecs/docs/FAQ.md new file mode 100644 index 0000000..5ebc63f --- /dev/null +++ b/engine/libs/flecs/docs/FAQ.md @@ -0,0 +1,133 @@ +# FAQ +Frequently asked questions. + +## What is an ECS? +See the [ECS FAQ](https://github.com/SanderMertens/ecs-faq) + +## Why is Flecs written in C? +There are a lot of reasons, but the main ones are: +- Faster compile times, especially when compared with header-only C++ libraries +- C can be called from almost any programming language +- C is super portable and has a tiny runtime so you can run it on a toaster if you want +- The rules and limitations of the ECS aren't dictated by any type system +- You can create a zero-overhead C++ API on top of C, but not the other way around + +## Can I use Flecs with C++14 or higher? +You can! Even though the C++ API is C++11, you can use it with any revision at or above 11. + +## Can I use std::vector or other types inside components? +You can! Components can contain almost any C++ type. + +## What is an archetype? +Archetype-based refers to the way the ECS stores components. It means that all entities with the same components are stored together in an archetype. This provides efficient CPU cache utilization and allows for things like vectorization and fast querying. + +Other examples of archetype implementations are Unity DOTS, Unreal Mass and Bevy ECS. + +For more information, see this blog: https://ajmmertens.medium.com/building-an-ecs-2-archetypes-and-vectorization-fe21690805f9 + +## How does Flecs compare with EnTT? +Flecs and EnTT both are ECS libraries, but other than that they are different in almost every way, which can make comparing the two frameworks tricky. When you are comparing Flecs and EnTT, you can _generally_ expect to see the following: + +- Add/remove operations are faster in EnTT +- Single component queries are faster in EnTT +- Multi-component queries are faster in Flecs +- Bulk-creating entities are faster in Flecs +- Entity destruct are faster in Flecs (especially for worlds/registries with lots of components) +- Iterating a single entity's components are faster in Flecs + +When doing a benchmark comparison don't rely on someone elses numbers, always test for your own use case! + +## Is Flecs used for commercial projects? +Yes, Flecs is used commercially. + +## Why are my queries so slow? +This is likely because you're creating a query in a loop. Queries should be created once, and iterated often. + +## Can Flecs be compiled to web assembly? +Yes it can! See the [quickstart manual](Quickstart.md) for more information. + +## Why am I getting an “use of undeclared identifier 'FLECS_ID …’” compiler error? +This happens in C if the variable that holds the component id can't be found. This example shows how to fix it: https://github.com/SanderMertens/flecs/tree/master/examples/c/entities/fwd_declare_component + +## Why are my entity ids so large? +When you inspect the integer value of an entity you may see that this value is very large, usually around 4 billion. This is not a bug, and instead means that the entity id has been recycled. This example shows when recycling happens: + +```cpp +flecs::entity e1 = world.entity(); // small id +e1.destruct(); // id is made available for recycling + +flecs::entity e2 = world.entity(); // recycles e1 +e1.is_alive(); // false +e2.is_alive(); // true + +std::cout << e2.id(); // large id, upper 32 bits contains liveliness count +``` + +## What is the difference between add & set? Why do both exist? +An `add` just adds a component without assigning a value to it. A `set` assigns a value to the component. Both operations ensure that the entity will have the component afterwards. + +An `add` is used mostly for adding things that don't have a value, like empty types/tags and most relationships. If `add` is used with a component that has a constructor, adding the component will invoke its constructor. + +Additionally you can use `emplace` to construct a component in place in the storage (similar to `std::vector::emplace`). + +## Can Flecs serialize components? +Yes it can! See the reflection examples: + +- https://github.com/SanderMertens/flecs/tree/master/examples/c/reflection +- https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection + +## Why is Flecs so large? +If you look at the size of the flecs.c and flecs.h files you might wonder why they are so large. There are a few reasons: + +- The files contain a _lot_ of comments and in-code documentation! +- Flecs has a small core with a lot of addons. The flecs.c and flecs.h files are the full source code, including addons. +- The flecs.h file contains the full C++ API. +- Flecs implements its own data structures like vectors and maps, vs. depending on something like the STL. +- C tends to be a bit more verbose than other languages. + +Not all addons are useful in any project. You can customize a Flecs build to only build the things you need, which reduces executable size and improves build speed. See the quickstart on how to customize a build. + +## Why does the explorer not work? +Make sure that: +- The REST API is enabled (see the [REST manual](https://www.flecs.dev/flecs/md_docs_RestApi.html)) +- You can reach the REST API by testing http://localhost:27750/entity/flecs +- You call `ecs_progress`/`world::progress` in your main loop + +If that doesn't work, see the [README of the explorer](https://github.com/flecs-hub/explorer) for potential issues with browser security settings and how to run a local instance of the explorer. + +## Does the explorer collect data from my application? +No! The https://flecs.dev/explorer page is a 100% client side application that does not talk to a backend. When you navigate to the page it will attempt to connect to http://localhost:27750 to find a running Flecs application. The data that the explorer fetches never leaves your machine. + +## Why can't I see component values in the explorer? +The explorer can only display component values if the reflection data has been registered for the component. See the [C reflection examples](https://github.com/SanderMertens/flecs/tree/master/examples/c/reflection) and [C++ reflection examples](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/reflection) for more information. + +## How do I detect which entities have changed? +Flecs has builtin change detection. Additionally, you can use an `OnSet` observer to get notified of changes to component values. See the query change detection and observer examples for more information. + +## Are relationships just a component with an entity handle? +No, relationships are a deeply integrated feature that is faster in many ways than a component with an entity handle. See the [relationship manual](Relationships.md) for more information. + +## Can I create systems outside of the main function? +You can! Systems can be created from any function, except other systems. + +## Can I use my own scheduler implementation? +You can! The flecs scheduler (implemented in the pipeline addon) is fully optional and is built on top of Flecs. You can create a custom pipeline, or a custom schedule implementation that does something else entirely than what Flecs provides. + +## Can I use Flecs without using systems? +You can! Systems are an optional addon that can be disabled. You can build applications that just use Flecs queries. + +## Why does the lookup function not find my entity? +This is likely because the entity has a parent. A lookup by name requires you to provide the full path to an entity, like: + +```c +ecs_lookup_fullpath(world, "parent.child"); +``` + +or in C++: + +```cpp +world.lookup("parent::child"); +``` + +## Can I add or remove components from within a system? +You can! By default ECS operations are deferred when called from inside a system, which means that they are added to a queue and processed later when it's safe to do so. Flecs does not have a dedicated command API, if you call an operation from a system it will be automatically added to the command queue! diff --git a/engine/libs/flecs/docs/FlecsScriptTutorial.md b/engine/libs/flecs/docs/FlecsScriptTutorial.md new file mode 100644 index 0000000..b734c32 --- /dev/null +++ b/engine/libs/flecs/docs/FlecsScriptTutorial.md @@ -0,0 +1,769 @@ +# Flecs Script Tutorial +This tutorial shows you how to use Flecs script, which is a declarative language for creating entities without having to write and compile code. Flecs script can be used for different things, such as building scenes, assets, or as a language for configuration files. + +In this tutorial we will be designing a simple fence asset from scratch, and make it parameterizable so it can be easily reused across scenes. By the end of the tutorial we'll have an asset that looks like this: + +[![preview of fence asset](img/script_tutorial/tut_playground_preview.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Enclosing%0A%0Afence_a%20%7B%0A%20%20-%20Enclosing%7B%7D%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B21%2C%2019%2C%20-37%7D%0A%20%20-%20Rotation3%7B-0.42%2C%20-0.52%7D%0A%7D%0A) + +Note: in order to use Flecs script an app needs to be built with the `FLECS_PLECS` addon. + +## Getting Started with the Explorer +The Flecs explorer is a web-based application that lets us write scripts and see the results directly. In the tutorial we will use the explorer in combination with the Flecs playground, which has a rendering canvas and comes preloaded with a number of modules and assets. + +Go to this URL to open the explorer with the playground: +https://www.flecs.dev/explorer/?wasm=https://www.flecs.dev/explorer/playground.js + +The page should look similar to this: +[![explorer with playground](img/script_tutorial/tut_playground.png)](https://www.flecs.dev/explorer/?wasm=https://www.flecs.dev/explorer/playground.js) + +The panel on the left is the entity treeview, which shows all of the entities in our scene. The center view is the canvas, which shows us the renderable entities of our scene (more on that later). Finally, the pane on the right is the editor, where we can write flecs scripts. + +At any point in time you can disable panels by clicing on the "x" in the top-right corner. Panels can be brought back by pressing on their button in the menu bar on the left. + +One other important thing is the link button in the top-right of the screen. You can use that button to obtain a link to your content, which is a good way to save progress, or to share what you've created with other people. + +## The Basics +Currently the explorer is showing the default scene. Let's clear it by removing all code from the editor. You should now see an empty canvas: +![explorer with empty canvas](img/script_tutorial/tut_playground_empty.png) + +Lets create an entity by typing its name into the editor: + +```js +my_entity +``` + +Notice that as we are typing the entity shows up in the treeview: +[![explorer with a single entity](img/script_tutorial/tut_playground_entity.png)](https://www.flecs.dev/explorer/?wasm=https://www.flecs.dev/explorer/playground.js&script=%0Amy_entity%0A) + +Entities are automatically created if they did not exist yet. Try entering the same entity twice: + +```js +my_entity +my_entity +``` + +Only one entity shows up in the treeview. The second time `my_entity` was parsed it already existed, so nothing needed to be done. + +## Adding Components +Now that we have an entity, let's add a few components and tags to it. Change the text in the editor to this, to create an entity with a tag called `SpaceShip`: + +```js +my_entity :- SpaceShip +``` + +Note that we didn't have to explicitly declare `SpaceShip` in advance, and that it also shows up as entity in the treeview. We can add multiple things to an entity this way: + +```js +my_entity :- SpaceShip +my_entity :- FasterThanLight +``` + +To avoid repeating the entity name many times, we can use the `{}` operators to open a scope for the entity. Inside the scope we can list components for the entity by prefixing them with a dash (`-`): + +```js +my_entity { + - SpaceShip + - FasterThanLight +} +``` + +We can inspect the entity and its contents by opening it in the entity inspector. To do this, click on the entity in the treeview. You should now see this: + +[![entity with two tags](img/script_tutorial/tut_playground_inspector.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=%0Amy_entity%20%7B%0A%20%20-%20SpaceShip%0A%20%20-%20FasterThanLight%0A%7D%0A&entity=my_entity) + +Note how the `SpaceShip` and `FasterThanLight` tags show up in the editor. There is also a `Script: main` tag, which exists for Flecs to keep track of which entities got created by our script. + +Adding a component is similar to adding tag with a value. Let's add the `Position3` component from the `flecs.components.transform` module which comes preloaded with the playground. Note how it also shows up in the inspector when we add this code: + +```js +my_entity { + - SpaceShip + - FasterThanLight + - flecs.components.transform.Position3{1, 2, 3} +} +``` + +Having to type the full module name each time we want to use the `Position3` component would be annoying. Add this line to the top of the script: + +```js +using flecs.components.* +``` + +We can now use the component without the module name, which looks much cleaner: + +```js +my_entity { + - SpaceShip + - FasterThanLight + - Position3{1, 2, 3} +} +``` + +If all went well, the playground should now look like this: + +[![entity with two tags and a component](img/script_tutorial/tut_playground_component.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Amy_entity%20%7B%0A%20%20-%20SpaceShip%0A%20%20-%20FasterThanLight%0A%20%20-%20Position3%7B1%2C%202%2C%203%7D%0A%7D%0A&entity=my_entity) + +Note how after we added the `Position3` component, the inspector also shows the `Transform` and `WorldCell` components. This happens because the playground imports modules that implement world partitioning and transforms, which we get for free just by using `flecs.components.transform.Position3` component. + +In addition to components and tags we can also add relationship pairs to entities. To add a pair, add this line to the scope of the entity, and note how it shows up in the inspector: + +```js + - (OwnedBy, Player) +``` + +Entities can be created in hierarchies. A child entity is created in the scope of an entity just like the components and tags, but without the preceding `-`. Add this to the scope of the entity: + +```js + cockpit { + pilot :- (Faction, Earth) + } +``` + +You can see the hierarchy this created in the treeview by expanding `my_entity`: + +[![entity with hierarchy](img/script_tutorial/tut_playground_hierarchy.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Amy_entity%20%7B%0A%20%20-%20SpaceShip%0A%20%20-%20FasterThanLight%0A%20%20-%20Position3%7B1%2C%202%2C%203%7D%0A%20%20%0A%20%20cockpit%20%7B%0A%20%20%20%20pilot%20%3A-%20(Faction%2C%20Earth)%0A%20%20%7D%0A%7D%0A&entity=my_entity) + +Congratulations! You now know how to create entities, hierarchies, and how to add components and tags. None of the entities we created so far are visible in the canvas however, so lets do something about that. + +## Drawing Shapes +We will be building a fence asset from just primitive shapes, where each entity is a single shape (note that in a real game you would likely want to use actual meshes). + +The renderer uses regular ECS queries to find the entities to render. For our entities to show up in these queries, they need to have at least three components: + +- `Position3` +- `Rgb` (a color) +- `Box` or `Rectangle` + +Let's start by drawing a plane. First remove all code from the editor except for this line: + +```js +using flecs.components.* +``` + +Now add these lines into the editor to create our ground `plane`: + +```js +plane { + - Position3{} + - Rectangle{100, 100} + - Rgb{0.9, 0.9, 0.9} +} +``` + +Something happened! But it doesn't look quite right: + +[![a buggy ground plane](img/script_tutorial/tut_playground_plane_wrong.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rectangle%7B100%2C%20100%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A) + +The rectangle is rotated the wrong way for our plane. To fix this we need to rotate it 90 degrees or `π/2` radians on the x axis. First lets define `π` as a constant value in our script: + +```js +const PI = 3.1415926 +``` + +Now add this line to the scope of `plane`: + +```js + - Rotation3{$PI / 2} +``` + +That looks better: + +[![a ground plane](img/script_tutorial/tut_playground_plane_rotated.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B100%2C%20100%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A) + +Let's increase the sides of the plane to `10000` so that the fog effect makes it blends in with the background, which gives the illusion of a horizon: + +[![a ground plane](img/script_tutorial/tut_playground_plane.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A) + +Note that the `PI` variable does not show up in the treeview. Variables do not create entities, and only exist within the context of a script. + +Let's now add a cube to the scene. The code for this looks similar to the plane: + +```js +box { + - Position3{} + - Box{10, 10, 10} + - Rgb{1, 0, 0} +} +``` + +The box is showing up, but it's intersecting with our plane: + +[![a cube](img/script_tutorial/tut_playground_half_box.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Abox%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Box%7B10%2C%2010%2C%2010%7D%0A%20%20-%20Rgb%7B1%2C%200%2C%200%7D%0A%7D%0A) + +To fix this, we can move it up by setting the `y` member of `Position3` to half its size: + +```js +box { + - Position3{y: 5} + - Box{10, 10, 10} + - Rgb{1, 0, 0} +} +``` + +Now the entire cube is visible, and should look like this: + +[![a cube](img/script_tutorial/tut_playground_box.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Abox%20%7B%0A%20%20-%20Position3%7By%3A%205%7D%0A%20%20-%20Box%7B10%2C%2010%2C%2010%7D%0A%20%20-%20Rgb%7B1%2C%200%2C%200%7D%0A%7D%0A) + +We now have all of the basic knowledge to start drawing a fence! Note that if you want to move the camera around, first click on the canvas to give it focus. You can now move the camera around. To release focus from the canvas, click on it again (the green border should disappear). + +## Drawing a Fence +To draw a fence we just need to combine differently sized boxes together. First remove the code for the cube from the editor. + +Before drawing the boxes, lets first make sure they're all created with the same color. We could add a color component with the same values to each entity, but that gets tedious if we want to change the color. Instead, we can use the `with` statement: + +```js +with Rgb{0.15, 0.1, 0.05} { + // Boxes go here +} +``` + +Inside the `with` statement we can now create two box entities for the left and right pillar of the fence: + +```js +with Rgb{0.15, 0.1, 0.05} { + left_pillar { + - Position3{x: -10, y: 5} + - Box{2, 10, 2} + } + right_pillar { + - Position3{x: 10, y: 5} + - Box{2, 10, 2} + } +} +``` + +The playground should now look like this: + +[![two pillars](img/script_tutorial/tut_playground_pillars.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Awith%20Rgb%7B0.15%2C%200.1%2C%200.05%7D%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-10%2C%20y%3A%205%7D%0A%20%20%20%20-%20Box%7B2%2C%2010%2C%202%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%2010%2C%20y%3A%205%7D%0A%20%20%20%20-%20Box%7B2%2C%2010%2C%202%7D%0A%20%20%7D%0A%7D%0A) + +That works, but the code is starting to look a bit unwieldy. There are lots of magic numbers which will become difficult to maintain as our asset gets more complex. Let's see if we can clean this up a bit. + +First lets define two variables for the color and box shape of the pillars. We already saw an example of a variable when we defined `PI`. These looks similar, except that because they are composite values, we also define their type: + +```js +const color : Rgb = {0.15, 0.1, 0.05} +const pillar_box : Box = {2, 10, 2} +``` + +This is better, but `pillar_box` still contains values that we have to update each time we want to change the shape of our fence. Instead we can do this, which is a bit more typing now but will be easier to maintain later: + +```js +const height = 10 +const color : Rgb = {0.15, 0.1, 0.05} + +const pillar_width = 2 +const pillar_box : Box = { + $pillar_width, + $height, + $pillar_width +} +``` + +Let's also add an additional `width` variable which stores the width of the fence (or the distance between two pillars): + +```js +const width = 20 +``` + +We can now update the code that creates the pillars to this: + +```js +with $color, $pillar_box { + left_pillar { + - Position3{x: -$width/2, y: $height/2} + } + right_pillar { + - Position3{x: $width/2, y: $height/2} + } +} +``` + +This will reproduce the exact same scene, but we can now tweak the variables to change the shape of our fence. Try playing around with the values of the different variables to see their effect! + +[![two modified pillars](img/script_tutorial/tut_playground_vars.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aconst%20width%20%3D%2010%0Aconst%20height%20%3D%2015%0Aconst%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0Aconst%20pillar_width%20%3D%204%0Aconst%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24pillar_width%2C%20%0A%20%20%24height%2C%20%0A%20%20%24pillar_width%0A%7D%0A%0Awith%20%24color%2C%20%24pillar_box%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%7D%0A) + +This is no fence yet. We're still missing the crossbars. Let's add another entity to see what it looks like: + +```js +bar { + - Position3{y: $height / 2} + - Box{$width, 2, 1} + - $color +} +``` + +[![two pillars and a bar](img/script_tutorial/tut_playground_bar.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aconst%20width%20%3D%2020%0Aconst%20height%20%3D%2010%0Aconst%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0Aconst%20pillar_width%20%3D%202%0Aconst%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24pillar_width%2C%20%0A%20%20%24height%2C%20%0A%20%20%24pillar_width%0A%7D%0A%0Awith%20%24color%2C%20%24pillar_box%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%7D%0A%0Abar%20%7B%0A%20%20-%20Position3%7By%3A%20%24height%20%2F%202%7D%0A%20%20-%20Box%7B%24width%2C%202%2C%201%7D%0A%20%20-%20%24color%20%20%0A%7D%0A) + +Let's add a second entity and space the two entities out a bit so they don't overlap. Also make sure to give both entities a different name, or else we'll end up overwriting the previous values: + +```js +top_bar { + - Position3{y: $height/2 + 2} + - Box{$width, 2, 1} + - $color +} +bottom_bar { + - Position3{y: $height/2 - 2} + - Box{$width, 2, 1} + - $color +} +``` + +[![a simple fence](img/script_tutorial/tut_playground_fence.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aconst%20width%20%3D%2020%0Aconst%20height%20%3D%2010%0Aconst%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0Aconst%20pillar_width%20%3D%202%0Aconst%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24pillar_width%2C%20%0A%20%20%24height%2C%20%0A%20%20%24pillar_width%0A%7D%0A%0Awith%20%24color%2C%20%24pillar_box%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%7D%0A%0Atop_bar%20%7B%0A%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%202%7D%0A%20%20-%20Box%7B%24width%2C%202%2C%201%7D%0A%20%20-%20%24color%20%20%0A%7D%0Abottom_bar%20%7B%0A%20%20-%20Position3%7By%3A%20%24height%2F2%20-%202%7D%0A%20%20-%20Box%7B%24width%2C%202%2C%201%7D%0A%20%20-%20%24color%20%20%0A%7D%0A) + +That starts to look more like a fence! Just like before however, we have a bunch of magic values and redundancies, but we now know how to fix that. First, let's define a `bar_sep` variable to specify how far apart the bars should be separated: + +```js +const bar_sep = 4 +``` + +Let's also create a variable for the bar shape and bar height, similar to what we did before. Let's make the bar thickness scale with the width of a pillar: + +```js +const bar_height = 2 +const bar_depth = $pillar_width/2 +const bar_box : Box = {$width, $bar_height, $bar_depth} +``` + +With that in place, we can cleanup the entity code for the bars: + +```js +with $color, $bar_box { + top_bar { + - Position3{y: $height/2 + $bar_sep/2} + } + bottom_bar { + - Position3{y: $height/2 - $bar_sep/2} + } +} +``` + +We now have our fence, which we can fully tweak with by changing the values of the variables! There are a few problems with the current code however: + +- The code is not packaged in a way that we can easily use +- We can make the fence very wide or very high, but it will end up looking weird. Ideally we increase the number of pillars and bars as we scale up the fence. + +The code is also growing. To make sure you don't lose track of what you're doing you can add comments: + +```js +// Create two bar entities +with $color, $bar_box { + top_bar { + - Position3{y: $height/2 + $bar_sep/2} + } + bottom_bar { + - Position3{y: $height/2 - $bar_sep/2} + } +} +``` + +## Prefabs +We have the basic structure of a fence, but what if we wanted two fences? We could of course duplicate all of the code, but that would be impractical. Instead we can package up what we have in a _prefab_. + +Turning the existing code into a prefab is easy. Simply add these lines around the fence code: + +```js +Prefab Fence { + // fence code goes here +} +``` + +This creates an entity `Fence` with the tag `Prefab`, and stores all entities we created so far as children of the `Fence` entity. This is the same as running the following code, but a bit more convenient: + +```js +Fence { + - Prefab + + // fence code goes here +} +``` + +Once you put the fence entities inside the prefab scope, you'll notice a few things: +- The fence disappears from the canvas +- The fence entities now show up under `Fence` in the treeview + +[![a fence prefab hierarchy](img/script_tutorial/tut_playground_prefab.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0APrefab%20Fence%20%7B%0A%0Aconst%20width%20%3D%2020%0Aconst%20height%20%3D%2010%0Aconst%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0Aconst%20pillar_width%20%3D%202%0Aconst%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24pillar_width%2C%20%0A%20%20%24height%2C%20%0A%20%20%24pillar_width%0A%7D%0A%0Awith%20%24color%2C%20%24pillar_box%20%7B%0A%20%20left_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%20%20right_pillar%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%7D%0A%7D%0A%0Aconst%20bar_sep%20%3D%204%0Aconst%20bar_height%20%3D%202%0Aconst%20bar_depth%20%3D%20%24pillar_width%2F2%0Aconst%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%24width%2C%20%0A%20%20%24bar_height%2C%20%0A%20%20%24bar_depth%0A%7D%0A%0Awith%20%24color%2C%20%24bar_box%20%7B%0A%20%20top_bar%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%7D%0A%20%20bottom_bar%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%7D%0A%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A) + +The reason that the fence disappears is that `Prefab` is a builtin tag that is by default ignored by queries, and as a result also by the renderer. Children of a prefab also become prefabs, so they also get ignored. + +That seems a bit underwhelming, but what we can now do is create an entity that _inherits_ from the `Fence` prefab, by adding this line to the bottom of the script: + +``` +my_fence : Fence +``` + +[![a fence prefab instance](img/script_tutorial/tut_playground_instance.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0APrefab%20Fence%20%7B%0A%20%20const%20width%20%3D%2020%0A%20%20const%20height%20%3D%2010%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Amy_fence%20%3A%20Fence%0A) + +Great, the fence is back! Even better, we can now create two fences simply by instantiating the prefab twice. Just make sure to give both instances different positions: + +```js +fence_a : Fence { + - Position3{-10} +} +fence_b : Fence { + - Position3{10} +} +``` + +[![two fence prefab instances](img/script_tutorial/tut_playground_two_instances.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0APrefab%20Fence%20%7B%0A%20%20const%20width%20%3D%2020%0A%20%20const%20height%20%3D%2010%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B10%7D%0A%7D%0A) + +We could save this script as is, load it into our game, and instantiate the prefab from regular C/C++ code. This is what that would look like: + +```c +// In C +ecs_plecs_from_file(world, "fence.flecs"); + +ecs_entity_t fence = ecs_lookup_fullpath(world, "Fence"); +ecs_entity_t fence_a = ecs_new_w_pair(world, EcsIsA, fence); +ecs_entity_t fence_b = ecs_new_w_pair(world, EcsIsA, fence); + +ecs_set(world, fence_a, EcsPosition3, {-10}); +ecs_set(world, fence_b, EcsPosition3, {10}); +``` +```cpp +// In C++ +using namespace flecs::components::transform; + +ecs_plecs_from_file(world, "fence.flecs"); + +auto fence = world.lookup("Fence"); +auto fence_a = world.entity().is_a(fence); +auto fence_b = world.entity().is_a(fence); + +fence_a.set({-10}); +fence_b.set({10}); +``` + +We still have some problems, which we'll address in the next section: +- Our prefab is static, we cannot change the parameters of the fence +- The pillars and bars don't increase when the fence size increases + +## Assemblies +Prefabs are static collections of entities and components that we can instantiate multiple times. Assemblies are _prefab compositions_ that can be parameterized. In other words, assemblies let us to create our Fence, while also specifying the width and height. + +Changing the above code into an assembly is easy. Just change this line: + +```js +Prefab Fence { + // fence code +} +``` + +into this: + +```js +assembly Fence { + // fence code +} +``` + +The editor will throw the following error: + +``` +assembly 'Fence' has no properties +``` + +[![a fence assembly](img/script_tutorial/tut_playground_assembly_error.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20const%20width%20%3D%2020%0A%20%20const%20height%20%3D%2010%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%3A%20Fence%20%7B%0A%20%20-%20Position3%7B10%7D%0A%7D%0A) + +What does this mean? Assemblies, unlike prefabs, need _properties_, which are like the inputs to the assembly. For our fence, these could be `width` and `heigth` values. + +To add properties to the assembly, we can take some of our existing `const` variables, and change `const` to `prop`. This will make the variables visible to the outside. Another thing we must do for properties is explicitly specify their type. + +Let's take the existing `width`, `height` and `color` variables, and change them to properties. It is also good practice to put the props at the top of the assembly code, so it's easy to see which inputs it has: + +```js +assembly Fence { + prop width : flecs.meta.f32 = 20 + prop height : flecs.meta.f32 = 10 + prop color : Rgb = {0.15, 0.1, 0.05} + + // fence code +} +``` + +We can get rid of the `flecs.meta.` prefix by adding this to the top of our script: + +```js +using flecs.meta +``` + +The code can now be changed to this: + +```js +assembly Fence { + prop width : f32 = 20 + prop height : f32 = 10 + prop color : Rgb = {0.15, 0.1, 0.05} + + // fence code +} +``` + +Note that while we were doing this, the fence disappeared again from the canvas. This happened because while we _inherit_ a prefab, we _assign_ an assembly. To make the fences visible again, change the code that creates the fences to this: + +```js +fence_a { + - Fence{} + - Position3{-10} +} +fence_b { + - Fence{} + - Position3{10} +} +``` + +The fences are back, with the default values we provided to our props. But we now have a new power! Try changing it to this: + +```js +fence_a { + - Fence{width: 10, height: 20} + - Position3{-10} +} +fence_b { + - Fence{width: 25, height: 10} + - Position3{10} +} +``` + +[![two instantiated fence assemblies](img/script_tutorial/tut_playground_assembly.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2020%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24pillar_box%20%7B%0A%20%20%20%20left_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20-%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20right_pillar%20%7B%0A%20%20%20%20%20%20-%20Position3%7Bx%3A%20%24width%2F2%2C%20y%3A%20%24height%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%7B%0A%20%20-%20Fence%7Bwidth%3A%2010%2C%20height%3A%2020%7D%0A%20%20-%20Position3%7B-10%7D%0A%7D%0Afence_b%20%7B%0A%20%20-%20Fence%7Bwidth%3A%2025%2C%20height%3A%2010%7D%0A%20%20-%20Position3%7B10%7D%0A%7D%0A) + +We are now well on our way to define a procedural fence. Note how similar assigning an assembly looks to assigning a component. This is no coincidence: an assembly is translated to a component, where each property becomes a member of that component. + +This is something we can exploit in C/C++ code to make assigning assemblies to entities as easy as assigning a component: + +```c +// In C +typedef struct { + float width; + float height; + EcsRgb color; +} Fence; + +// Register a regular component +ECS_COMPONENT(world, Fence); + +// Because the Fence assembly has the same name as the +// component it will "bind" to it. +ecs_plecs_from_file(world, "fence.flecs"); + +// Set the component as usual +ecs_entity_t fence_a = ecs_set(world, 0, Fence, {10, 20}); +ecs_entity_t fence_b = ecs_set(world, 0, Fence, {25, 10}); + +ecs_set(world, fence_a, EcsPosition3, {-10}); +ecs_set(world, fence_b, EcsPosition3, {10}); +``` +```cpp +// In C++ +using namespace flecs::components::transform; + +struct Fence { + float width; + float height; + flecs::components::graphics::Rgb color; +} + +// Because the Fence assembly has the same name as the +// component it will "bind" to it. +ecs_plecs_from_file(world, "fence.flecs"); + +auto fence_a = world.entity().set({10, 20}); +auto fence_b = world.entity().set({25, 10}); + +fence_a.set({-10}); +fence_b.set({10}); +``` + +For this to work, we have to make sure that the types and ordering of the properties in the script match up with the types and ordering of members in the component type. + +## Grids +Let's now see if we can change the number of pillars and bars depending on the fence width and height. + +At first glance this sounds like something that would require loops, and those are not supported in Flecs script. There is a way around this however, which is to use a `Grid` component that does the instantiating for us. + +The `Grid` component is provided by the `flecs.game` module, so before using it lets add this to the top of the script: + +```js +using flecs.game +``` + +The implementation for the `Grid` component can be found [here](https://github.com/flecs-hub/flecs-game/blob/main/src/main.c). We only use it in the tutorial for demonstration purposes. In practice we could create many different components that handle layout for us. + +The grid component lets us create multiple instances of an entity that are laid out in a 1, 2 or 3 dimensional grid. Let's apply this to the number of pillars, which we want to scale with the width of our fence. For this we need two things: + +- The width of the fence +- The minimum spacing between pillars + +We already have the fence width. Let's create another variable for the spacing: + +```js +const pillar_spacing = 10 +``` + +We can now calculate the number of pillars we want: + +```js +const pillar_count = $width / $pillar_spacing +``` + +The `Grid` component can't consume the pillar code directly. Instead it accepts a prefab, so lets turn the existing pillars code into a prefab: + +```js +Prefab Pillar { + - $color + - $pillar_box +} +``` + +Note how we left out `Position3`, as this will be set by the `Grid` component. Without further ado, let's create a row of pillars: + +```js + pillars { + - Position3{y: $height/2} + - Grid{ + x.count: $pillar_count + x.spacing: $pillar_spacing + prefab: Pillar + } + } +``` + +Let's disect what this code does: +- Create a `pillars` entity +- Create it at position `y: $height/2` so pillars don't sink into the ground +- With `$pillar_count` pillars on the x axis +- Spaced `$pillar_spacing` apart +- Using `Pillar` as prefab to instantiate the entities + +Let's also change the code that instantiates our two fences to just one: + +``` +fence :- Fence{} +``` + +And increase the `width` of the fence to `60`: + +```js + prop width : f32 = 60 +``` + +We now get a number of pilars that matches the fence length: + +[![a fence with multiple pillars](img/script_tutorial/tut_playground_grid.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2060%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20Prefab%20Pillar%20%7B%0A%20%20%20%20-%20%24color%0A%20%20%20%20-%20%24pillar_box%0A%20%20%7D%0A%20%20%20%20%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%0A%20%20%20%20%20%20x.spacing%3A%20%24pillar_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%20%20%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence%20%3A-%20Fence%7B%7D%0A) + +There's still something off though, which is that the pillars don't exactly line up with the ends up of the fence. We can fix this with a simple calculation that takes the number of pillars, and uses that to recompute the grid spacing. + +```js + const grid_spacing = $width / ($pillar_count - 1) +``` + +Note that this takes into account that there is one more pillar than there are spaces between pillars. When we replace `pillar_spacing` in the grid with `grid_spacing`, we get the correct pillar alignment: + +[![a fence with correctly aligned pillars](img/script_tutorial/tut_playground_pillar_grid.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20prop%20width%20%3A%20f32%20%3D%2060%0A%20%20prop%20height%20%3A%20f32%20%3D%2010%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%20const%20pillar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24pillar_width%2C%20%0A%20%20%20%20%24height%2C%20%0A%20%20%20%20%24pillar_width%0A%20%20%7D%0A%20%20%0A%20%20Prefab%20Pillar%20%7B%0A%20%20%20%20-%20%24color%0A%20%20%20%20-%20%24pillar_box%0A%20%20%7D%0A%20%20%20%20%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%0A%20%20%20%20%20%20x.spacing%3A%20%24grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%20%20%0A%20%20%0A%20%20const%20bar_sep%20%3D%204%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%2F2%0A%20%20const%20bar_box%20%3A%20Box%20%3D%20%7B%0A%20%20%20%20%24width%2C%20%0A%20%20%20%20%24bar_height%2C%20%0A%20%20%20%20%24bar_depth%0A%20%20%7D%0A%20%20%0A%20%20with%20%24color%2C%20%24bar_box%20%7B%0A%20%20%20%20top_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20%2B%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%20%20bottom_bar%20%7B%0A%20%20%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%20-%20%24bar_sep%2F2%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Afence_a%20%7B%0A%20%20-%20Fence%7B%7D%0A%20%20-%20Position3%7B-10%7D%0A%7D%0A) + +We can do the same thing for the bars. I'll skip right to the result since it involves very similar steps: + +[![a fence with multiple pillars and bars](img/script_tutorial/tut_playground_grids.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0A%2F%2F%20The%20ground%20plane%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%2F2%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0Afence%20%3A-%20Fence%7B%7D) + +The values got tweaked a bit to get a more easthetically pleasing result when scaling up. Feel free to tune the variables to values that produce something you like! + +We're almost done! There is only one thing left, which is to combine the fence assembly in a nested assembly so we can create an enclosing. + +## Nested Assemblies +Now that we have our Fence assembly ready to go, we can build a higher level assemblies which reuses existing the `Fence` assembly. One typical thing we could do is instantate a `Fence` four times, so that it creates an enclosed space. + +The code for this doesn't introduce anything beyond what we've already learned. Let's first setup a new assembly at the bottom of our script. +Inside the assembly, lets create `width` and `depth` properties, so we can define a rectangular area. Let's also add a `color` and `height` property that we can passthrough to our `Fence` assembly. + +When put together, this is what it looks like: + +```js +assembly Enclosing { + prop width: f32 = 40 + prop height: f32 = 10 + prop depth: f32 = 40 + prop color: Rgb = {0.15, 0.1, 0.05} + + // enclosing code goes here +} +``` + +Now we need to instantiate `Fence` four times, for each side of the rectangle. We'll also add a few convenience variables so we don't end +up writing the same divisions multiple times. + +```js + const width_half = $width / 2 + const depth_half = $depth / 2 + const PI = 3.1415926 + + left { + - Position3{x: -$width_half} + - Rotation3{y: $PI/2} + - Fence{width: $depth, height:$, color:$} + } + right { + - Position3{x: $width_half} + - Rotation3{y: $PI/2} + - Fence{width: $depth, height:$, color:$} + } + back { + - Position3{z: -$depth_half} + - Fence{width: $width, height:$, color:$} + } + front { + - Position3{z: $depth_half} + - Fence{width: $width, height:$, color:$} + } +``` + +Note how the passthrough parameters are specified as `height:$`. This is a shorthand notation for `height: $height`, and can save a lot of typing when working with nested assemblies. + +Let's now use our new `Enclosing` assembly by changing + +```js +fence :- Fence{} +``` + +to + +``` +enclosing :- Enclosing{} +``` + +Here is what that looks like. You might need to zoom out a bit with the camera to see the full fence. Remember that you can do this by first clicking on the canvas to give it focus, then using the WASD keys to move the camera around. Don't forget to click on the canvas again to give focus back to the explorer. + +[![an enclosing](img/script_tutorial/tut_playground_pasture.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Enclosing%0A%0Afence_a%20%7B%0A%20%20-%20Enclosing%7B%7D%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B21%2C%2019%2C%20-37%7D%0A%20%20-%20Rotation3%7B-0.42%2C%20-0.52%7D%0A%7D%0A) + +We can now easily modify our enclosing by passing in parameters for width and height: + +``` +enclosing :- Enclosing{width: 100, height: 30} +``` + +[![a tall enclosing](img/script_tutorial/tut_playground_pasture_2.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0A%2F%2F%20The%20ground%20plane%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%2F2%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%7D%0A%0Apasture%20%3A-%20Enclosing%7B%0A%20%20width%3A%20100%2C%20%0A%20%20depth%3A%20100%0A%20%20height%3A%2030%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B52%2C%2041%2C%20-82%7D%0A%20%20-%20Rotation3%7B-0.45%2C%20-0.54%7D%0A%7D%0A) + +Congratulations! You now know how to create assets in Flecs Script! If you'd like to see more examples of how assemblies can be used to create complex compositions of assets, take a look at the assemblies in the Flecs playground project: + +https://github.com/flecs-hub/playground/tree/main/etc/assets + +Additionally you can also try instantiating one of the assets that come preloaded in the playground. Try replacing the editor content with: + +```js +using flecs.components.* +using assemblies + +const PI = 3.1415926 + +plane { + - Position3{} + - Rotation3{$PI / 2} + - Rectangle{10000, 10000} + - Rgb{0.9, 0.9, 0.9} +} + +town :- Town{} +``` + +[![a town](img/script_tutorial/tut_playground_town.png)](https://www.flecs.dev/explorer/?show=plecs,explorer_canvas&local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20assemblies%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Atown%20%3A-%20Town%7B%7D) + +That's all for this tutorial. Have fun creating, and don't hesitate to share the results in the `#showcase` channel on Discord! (link: https://discord.gg/caR2WmY) diff --git a/engine/libs/flecs/docs/JsonFormat.md b/engine/libs/flecs/docs/JsonFormat.md new file mode 100644 index 0000000..01ff01b --- /dev/null +++ b/engine/libs/flecs/docs/JsonFormat.md @@ -0,0 +1,638 @@ +# JSON format +This document provides an overview of the JSON serializer format. For an overview of how to use the JSON serializer API, [see the JSON addon documentation](https://www.flecs.dev/flecs/group__c__addons__json.html). + +## Value kinds +This section describes value kinds that are used by the JSON serializers. + +### Name +A name field returns the name of an entity as returned by `ecs_get_name` or `flecs::entity::name`. + +**Example**: +```json +"Earth" +``` + +### Path +A path field returns the path identifier of an entity as returned by `ecs_get_fullpath` or `flecs::entity::path`. The path contains the entity's name and the names of its parent and grandparents separated by a dot (`.`). + +**Example**: +```json +"SolarSystem.Sun.Earth" +``` + +### Label +A label field returns the doc name of the entity as returned by `ecs_doc_get_name` or `flecs::entity::doc_name`. The doc name is a user-friendly name for the entity that does not need to be unique and may contain any character. + +Serializing labels requires the [doc](https://www.flecs.dev/flecs/group__c__addons__doc.html) addon. + +**Example**: +```json +"The Earth" +``` + +### Id +An id field returns a component id or pair. It is formatted as an array of at least one and at most three elements where the elements have the following meaning: + +- Component _or_ First element of pair +- Second element of pair +- Type flag + +Each element is formatted as a [Path](#path). + +**Example**: +```json +["transform.Position"] +["Likes", "Apples"] +["Movement", 0, "SWITCH"] +``` + +### Id** +Same as [Id](#id) but with [Label](#label)'s instead of paths. + +**Example**: +```json +["Entity position"] +["Located in", "San Francisco"] +``` + +### Value +A JSON object or scalar representing the value of a component. Component values can only be serialized if the component is described in the reflection framework (see the [meta addon](https://www.flecs.dev/flecs/group__c__addons__meta.html)). + +When a component has no value (e.g. a tag) or is not described by the reflection framework, `0` will be used as placeholder. + +**Example**: +```json +{"x": 10, "y": 20} +``` + +### Type** +A JSON object that represents the structure of a component type. + +**Example**: +```json +{ + "type_info": [{ + "x": ["float"], + "y": ["float"] + }, 0, 0] +} +``` + +### Entity +The entity kind is an object that contains metadata and data of a single serialized entity, and is returned by the [Entity serializer](#entity-serializer). + +The following sections describe the fields that an object of the entity kind may contain: + +#### "path +The entity path. + +Type: [Path](#path), optional + +#### "label +The entity doc name. + +Type: [Label](#label), optional + +#### "brief +Brief description (as returned by `ecs_doc_get_brief`). + +Type: string, optional + +#### "link +Link to URL (as returned by `ecs_doc_get_link`). + +Type: string, optional + +#### "is_a +Base entities. + +Type: Array([Entity](#entity)), optional + +#### "ids +Component ids. + +Type: Array([Id](#id)) + +#### "id_labels +Component labels. + +Type: Array([Id label](#id-label)), optional + +#### "hidden +Array indicating whether a component is hidden. Only applies to components of base entities (in the ["is_a"](is_a) array). + +A base component is hidden when it is overridden by an entity higher in the inheritance hierarchy. + +Type: Array(bool), optional + +#### "values +Component values. + +Type: Array([Value](#value)), optional + +#### "type_info +Array with objects that describe the component types of the terms. + +Type: Array([Type info](#type-info)), optional + +#### Example +Default serializer options: +```json +{ + "path":"Sun.Earth", + "ids":[ + ["Position"], + ["flecs.core.ChildOf", "Sun"], + ["flecs.core.Identifier", "flecs.core.Name"] + ] +} +``` + +This example shows an entity with a base (an `IsA` relationship) with default serializer options: +```json +{ + "path":"Sun.Earth", + "is_a": [{ + "path":"planets.RockyPlanet", + "ids": [ + ["planets.HasRocks"] + ] + }], + "ids":[ + ["Position"], + ["flecs.core.ChildOf", "Sun"], + ["flecs.core.Identifier", "flecs.core.Name"] + ] +} +``` + +This example shows an entity with a base with all serializer options enabled: +```json +{ + "path":"Sun.Earth", + "label": "The Earth", + "brief": "The planet you call home", + "link": "www.earth.com", + "is_a": [{ + "path":"planets.RockyPlanet", + "label":"A rocky planet", + "ids": [ + ["Position"], + ["planets.HasRocks"] + ], + "id_labels": [ + ["Position"], + ["HasRocks"] + ], + "values":[0, 0], + "hidden":[true, false] + }], + "ids":[ + ["Position"], + ["flecs.core.ChildOf", "Sun"], + ["flecs.core.Identifier", "flecs.core.Name"] + ], + "ids_labels":[ + ["Position"], + ["ChildOf", "Sun"], + ["Identifier", "Name"] + ], + "values":[ + {"x": 10, "y": 20}, + 0, 0 + ], + "type_info": [{ + "x": ["float"], + "y": ["float"] + }, 0, 0] +} +``` + +### Result +The result kind is an object that contains the metadata and data of a single result returned by an iterator (see [Iterator](#iterator)). + +The following sections describe the fields that an object of the entity kind may contain: + +#### parent +Parent of the entity in current result. + +Type: Array([Path](#path)), optional + +#### entities +Array with paths of the returned entities. + +Type: Array([Name](#name)), optional + +#### entity_labels +Same as [entities](#entities), but with [Label](#label)'s instead of paths. + +Type: Array([Label](#label)), optional + +#### entity_ids +Same as [entities](#entities), but with numerical ids instead of paths. + +Type: Array(Number), optional + +#### sources +Array with paths of sources for each term. A subject indicates the actual entity on which a component is stored. If this is the matched entity (default) the array will contain a `0` element. + +Type: Array([Path](#path)), optional + +#### variables +Array with variable values for current result (see [query variables](Queries.md#variables)). + +Type: Array([Path](#path)), optional + +#### variable_labels +Same as [variables](#variables), but with [Label](#label)'s instead of paths. + +Type: Array([Label](#label)), optional + +#### variable_ids +Same as [variables](#variables), but with numerical ids instead of paths. + +Type: Array(Number), optional + +#### ids +Array with component ids. This list is more specific than the ids array provided by the top-level iterator object. The arrays can be different in the case of terms with an `Or` operator, or terms with wildcard ids. + +Type: Array(string), optional + +#### is_set +Array with booleans that can be used to determine whether terms with the `Optional` operator are set. + +Type: Array(bool), optional + +#### values +Array with component values. The array contains an element for each term. If a component has no value, or no value could be serialized for the component a `[]` element is added. + +Each element in the array can be either an array with component values when the component is from the matched entity, or a single component value when the component is from another entity (like a parent, prefab, singleton). + +Type: Array(Array([Value](#value))), optional + +### Iterator +The iterator kind is an object that contains metadata and data of all the entities yielded by an iterator. + +#### "ids +Array with ids for each term. + +Type: Array(string), optional + +#### "variables +Array with variable names (see [query variables](https://www.flecs.dev/flecs/md_docs_Queries.html/#variables)). + +Type: Array(string), optional + +#### "results +Array with elements for each returned result. + +Type: Array([Result](#result)) + +#### "eval_duration +Time it took to serialize the iterator. + +Type: Number + +#### "type_info +Array with objects that describe the component types of the terms. + +Type: Array([Type info](#type-info)), optional + +#### Example + +```json +{ + "ids": ["Position", "Jedi"], + "results": [{ + "entities": ["Luke", "Yoda"], + "values": [ + [{ "x": 10, "y": 20 }, + { "x": 20, "y": 30 }], + 0 + ] + }] +} +``` + +## Entity serializer +The entity serializer returns a JSON string of the [Entity](#entity) type. This format is returned by `ecs_entity_to_json` and `flecs::entity::to_json`. + +### Serializer options +The following options (found in `ecs_entity_to_json_desc_t`) customize the output of the entity serializer: + +#### serialize_path +Serialize the ["path"](#path-1) member. + +**Default**: `true` + +**Example**: +```json +{"path": "SolarSystem.Sun.Earth"} +``` + +#### serialize_label +Serialize the ["label"](#label-1) member. + +**Default**: `false` + +**Example**: +```json +{"label": "Rocky planet"} +``` + +#### serialize_brief +Serialize the ["brief"](#brief) member. + +**Default**: `false` + +**Example**: +```json +{"brief": "A rocky planet"} +``` + +#### serialize_link +Serialize the ["link"](#link) member. + +**Default**: `false` + +**Example**: +```json +{"brief": "www.rocky-planet.com"} +``` + +#### serialize_id_labels +Serializes the ["id_labels"](#id_labels) member. + +**Default**: `false` + +**Example**: +```json +{"id_labels": [["A Rocky Planet"], ["Has surface water"]]} +``` + +#### serialize_base +Serializes the ["is_a"](#is_a) member. + +**Default**: `true` + +**Example**: +```json +{"is_a": [{ + "path":"planets.RockyPlanet", + "ids": [ + ["planets.HasRocks"] + ] +}]} +``` + +#### serialize_private +Serialize private components. Private components are regular components with the `EcsPrivate` (or `flecs::Private`) tag. When `serialize_private` is false, private components will be hidden from all arrays. + +**Example**: +```json +{"ids": ["Position", "PrivateComponent"]} +``` + +**Default**: `false` + +#### serialize_hidden +Serializes the ["hidden"](#hidden) member. + +**Example**: +```json +{"hidden":[true, false]} +``` + +Full example: +```json +{ + "path":"Sun.Earth", + "is_a": [{ + "path":"planets.RockyPlanet", + "ids": [ + ["Position"], + ["planets.HasRocks"] + ], + "hidden":[true, false] + }], + "ids":[ + ["Position"], + ["flecs.core.ChildOf", "Sun"], + ["flecs.core.Identifier", "flecs.core.Name"] + ] +} +``` +Note that `hidden[0]` is `true` in this example, because the `Position` component from `planets.RockyPlanet` is overridden by the entity. + +**Default**: `false` + +#### serialize_values +Serializes the ["values"](#values) member. + +**Example**: +```json +{"values":[{"x":10, "y": 20}, 0]} +``` + +**Default**: `false` + +#### serialize_type_info +Serialize the ["type_info"](#type_info) member. + +**Example**: +```json +{ + "type_info": [{ + "x": ["float"], + "y": ["float"] + }, 0, 0] +} +``` + +## Iterator serializer +The entity serializer returns a JSON string of the [Iterator](#iterator) type. This format is returned by `ecs_iter_to_json`. + +### Serializer options + +#### serialize_term_ids +Serialize the top level ["ids"](#term_ids) member. + +**Default**: `true` + +**Example**: + +Example result for query `Position, Jedi` +```json +{ + "ids": ["Position", "Jedi"], + "results": [ ] +} +``` + +#### serialize_ids +Serialize the result specific ["ids"](#ids-1) member. + +If the iterated query does not contain variable ids (either an `Or` term or a term with a wildcard id) the result specific `ids` member will exactly match the top-level `ids` member. + +**Default**: `true` + +**Example**: + +Example result for query `Position, (Likes, *)` +```json +{ + "ids": ["Position", "(Likes,*)"], + "results": [{ + "ids": ["Position", "(Likes,Apples)"] + }] +} +``` + +#### serialize_sources +Serialize the ["sources"](#sources) member. + +**Default**: `true` + +**Example**: + +Example result for query `Position, Position(parent)` +```json +{ + "ids": ["Position", "Position"], + "results": [{ + "entities": ["Parent.A", "Parent.B"], + "sources": [0, "Parent"] + }] +} +``` + +#### serialize_variables +Serialize the ["variables"](#variables) member. + +**Default**: `true` + +**Example**: + +Example result for query `Position, (Likes, _Food)` +```json +{ + "variables": ["Food"], + "results": [{ + "ids": ["Position", "(Likes,Apples)"], + "variables": ["Apples"], + }] +} +``` + +#### serialize_is_set +Serialize the ["is_set"](#is_set) member. + +**Default**: `true` + +**Example**: + +Example result for query `Position, ?Velocity` +```json +{ + "ids": ["Position", "Velocity"], + "results": [{ + "is_set": [true, false] + }] +} +``` + +#### serialize_values +Serialize the ["values"](#values) member. + +**Default**: `true` + +**Example**: + +Example result for query `Position` +```json +{ + "ids": ["Position"], + "results": [{ + "values": [ + [{ + "x": 10, + "y": 20 + }] + ] + }] +} +``` + +#### serialize_entities +Serialize the ["entities"](#entities) member. + +**Default**: `true` + +**Example**: +```json +{ + "results": [{ + "entities": ["MyEntity"] + }] +} +``` + +#### serialize_entity_labels +Serialize the ["entity_labels"](#entity_labels) member. + +**Default**: `false` + +**Example**: +```json +{ + "results": [{ + "entities": ["Parent.MyEntity"], + "entity_labels": ["My entity"] + }] +} +``` + +#### serialize_variable_labels +Serialize the ["variable_labels"](#variable_labels) member. + +**Default**: `false` + +**Example**: +```json +{ + "results": [{ + "variables": ["GrannySmith"], + "variable_labels": ["Granny smith"] + }] +} +``` + +#### measure_eval_duration +Serialize the ["eval_duration"](#eval_duration) member. + +**Default**: `false` + +**Example**: +```json +{ + "eval_duration": 0.001 +} +``` + +#### serialize_type_info +Serialize the ["type_info"](#type_info_1) member. + +**Default**: `false` + +**Example**: +```json +{ + "ids": ["Position", "Jedi"], + "type_info": [{ + "x": ["float"], + "y": ["float"] + }, 0], + "results": [{ + }] +} +``` diff --git a/engine/libs/flecs/docs/Manual.md b/engine/libs/flecs/docs/Manual.md new file mode 100644 index 0000000..250e7a0 --- /dev/null +++ b/engine/libs/flecs/docs/Manual.md @@ -0,0 +1,1227 @@ +# Manual + +## Introduction +Nobody likes to read manuals, and you should be able to get up and running with Flecs by using the quickstart, by looking at examples and by checking the documentation in the flecs header files. However, if you truly want to know how something works, or why it works that way, the manual is the right place to go. With that said, the manual is not exhaustive, and it complements the other sources of documentation. + +## Design Goals + +### 1. Performance +Flecs is designed from the ground up to provide blazing fast iteration speeds in systems that can be vectorized by default, while minimizing cache misses. In addition, Flecs has a unique graph-based storage engine that allows for extremely fast add, remove and bulk operations. These features, amongst others, ensure that applications can get the most out of the underlying hardware. + +### 2. Portability +Flecs has been implemented in C99 and features an external interface that is C89 compatible to ensure it is portable to a wide range of platforms. The framework contains a flexible operating system abstraction API that enables an application to easily port the library to new platforms. + +### 3. Reusability +ECS has the potential for being a platform for the development of reusable, loosely coupled, plug and play features like input, physics and rendering. Flecs modules enable such features to be packaged in a loosely coupled way so that applications can simply import them, while guaranteeing a correct execution order. In addition, Flecs has features like time management that ensure a consistent baseline across modules. + +### 4. Usability +Flecs is designed first and foremost to be a framework that simplifies the development of games and simulations. Rather than just providing a vanilla ECS implementation, Flecs provides many features that are commonly found in game development frameworks such as hierarchies, prefabs and time management, all integrated seamlessly with the core ECS system. + +### 5. Extensibility +Flecs is used with other frameworks and game engines, and as such not all of its features are useful in each application. For that reason Flecs has a modular design, so that applications can easily remove features from the core that they do not need. Additionally, since many features are built on top of the ECS core, applications can easily extend or reimplement them. + +### 6. Have fun! +There are few things as satisfying as building games. If nothing else, Flecs has been built to enable creative visions both big and small. I'm having a lot of fun building Flecs, I hope you will have fun using it, and that your users will have fun playing your games :) + +## Diagrams + +### High level architecture +This diagram provides an overview of how entities, components, tables, queries, filters and systems are wired together. +![Architecture diagram](img/flecs-architecture-overview.png) + +### Component add flow +This diagram provides an overview of the different steps that occur when adding a component to an entity. The diagram shows when component lifecycle callbacks, OnAdd triggers, OnSet systems, UnSet systems and monitors are invoked. Additionally the diagram shows how the defer mechanism is integrated with the listed Flecs operations. +![Component add flow](img/flecs-add-component-flow.png) + +### Component remove flow +This diagram provides an overview of the different steps that occur when removing a component from an entity. The diagram shows when component lifecycle callbacks, OnRemove triggers, OnSet systems, UnSet systems and monitors are invoked. Additionally the diagram shows how the defer mechanism is integrated with the listed Flecs operations. +![Component remove flow](img/flecs-remove-component-flow.png) + +### Staging flow +This diagram provides an overview of what happens when an application uses staging. Staging is a lockless mechanism that lets threads concurrently read & perform structural changes on the store. Changes are temporarily stored in a command queue per stage, which can be merged with the store when convenient. +![Staging flow](img/flecs-staging-flow.png) + +## API design + +### Naming conventions + +```c +// Component names ('Position') use PascalCase +typedef struct Position { + float x; + float y; // Component members ('y') use snake_case +} Position; + +typedef struct Velocity { + float x; + float y; +} Velocity; + +// System names ('Move') use PascalCase. API types use snake_case_t +void Move(ecs_iter_t *it) { + // Functions use snake_case + Position *p = ecs_field(it, Position, 1); + Velocity *v = ecs_field(it, Velocity, 2); + + for (int i = 0; i < it->count; i++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} + +int main(int argc, char *argv[]) { + ecs_world_t *world = ecs_init(); + + // Declarative function-style macros use SCREAMING_SNAKE_CASE + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + + // Module names are PascalCase + ECS_IMPORT(world, MyModule); + + // Enumeration constants ('EcsOnUpdate') use PascalCase + ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); + + // Function wrapper macros use snake_case + ecs_entity_t e = ecs_new(world, 0); + + // Builtin entities use PascalCase + ecs_add(world, EcsWorld, Position); + + return ecs_fini(world); +} +``` + +### Idempotence +Many operations in the Flecs API are idempotent, meaning that invoking an operation once has the same effect as invoking an operation multiple times with the same parameters. For example: + +```c +ecs_add(world, e, Position); +``` + +Has the same effect as: + +```c +ecs_add(world, e, Position); +ecs_add(world, e, Position); +``` + +This simplifies application code as it can be written in a declarative style, where the only thing that matters is that after the operation has been invoked, the post condition of the operation is satisfied. + +Some operations are idempotent but have side effects, like `ecs_set`: + +```c +ecs_set(world, e, Position, {10, 20}); +ecs_set(world, e, Position, {10, 20}); +``` + +The effect of invoking this operation once is the same as invoking the operation multiple times, but both invocations can invoke an OnSet observer which can introduce side effects. + +All declarative macros (`ECS_COMPONENT`, `ECS_SYSTEM`, ...) are idempotent: + +```c +{ + ECS_COMPONENT(world, Position); +} +{ + ECS_COMPONENT(world, Position); +} +``` + +The second time the `ECS_COMPONENT` macro is evaluated, the first instance will be found and returned. Note that because these macros may declare variables, they cannot be defined twice in the same C scope. + +### Error handling +As a result of the idempotent design of many operations, the API has a very small error surface. There are essentially two conditions under which an operation is unable to fulfill its postcondition: + +- The application provides invalid inputs to an operation +- The operating system is unable to fulfill a request, like a failure to allocate memory + +When either of those conditions occur, the library will throw an assertion in debug mode (the source is not compiled with `NDEBUG`). Except for errors caused by the OS, errors are almost always caused by the invocation of a single operation, which makes applications easy to debug. + +This approach has several advantages. Application code does not need to check for errors. If an error occurs, the assertion will cause application execution to halt. As a result of this, application code is cleaner and more robust, as it is impossible to forget to handle an error condition. + +### Memory ownership +Most of the API is handle based, as many API constructs are implemented using entities. There are a few instances where an application will interface with memory managed by the framework, or when an application needs to provide memory it manages to the API. In these scenarios there are four rules: + +- If an operation accepts a `const T*`, the application retains ownership of the memory +- If an operation accepts a `T*`, ownership is transferred from application to framework +- If an operation returns a `const T*`, the framework retains ownership of the memory +- If an operation returns a `T*`, ownership is transferred from framework to application + +The `ecs_get_name` operation is an example where the framework retains ownership: + +```c +const char *name = ecs_get_name(world, e); +``` + +The `ecs_get_fullpath` operation is an example where the ownership is transferred to the application: + +```c +char *path = ecs_get_fullpath(world, e); +``` + +Memory for which ownership has been transferred to the application will need to be freed by the application. This should be done by the `ecs_os_free` operation: + +```c +ecs_os_free(path); +``` + +### Entity names +An application can assign names to entities. Names can be assigned at entity creation, with the `ecs_entity_init` function: + +```c +ecs_entity_t e = ecs_entity(world, { .name = "MyEntity" }); +``` + +Alternatively, names can be assigned afterwards with the `ecs_set_name` function: + +```c +ecs_set_name(world, e, "MyEntity"); +``` + +The `ecs_set_name` function may be used as a shortcut to create a new named entity by providing 0 for the entity argument: + +```c +ecs_entity_t e = ecs_set_name(world, 0, "MyEntity"); +``` + +The name of an entity can be retrieved with the `ecs_get_name` function: + +```c +printf("Name = %s\n", ecs_get_name(world, e)); +``` + +The entity name is stored in `(EcsIdentifier, EcsName)`. Alternatively, the name can be retrieved with `ecs_get_pair`: +```c +const EcsIdentifier *ptr = ecs_get_pair(world, e, EcsIdentifier, EcsName); +printf("Name = %s\n", ptr->value); +``` + +Names can be used to lookup entities: + +```c +ecs_entity_t e = ecs_lookup(world, "MyEntity"); +``` + +When an entity is part of a hierarchy, names can be used to form a path: + +```c +ecs_entity_t parent = ecs_new_id(world); +ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); +ecs_entity_t grandchild = ecs_new_w_pair(world, EcsChildOf, child); + +ecs_set_name(world, parent, "Parent"); +ecs_set_name(world, child, "Child"); +ecs_set_name(world, grandchild, "GrandChild"); + +char *path = ecs_get_fullpath(world, grandchild); +printf("Path = %s\n", path); // prints Parent.Child.GrandChild +ecs_os_free(path); +``` + +A path can be created relative to a parent: + +```c +char *path = ecs_get_path(world, parent, grandchild); +printf("Path = %s\n", path); // prints Child.GrandChild +ecs_os_free(path); +``` + +Paths can be used to lookup an entity: + +```c +ecs_entity_t e = ecs_lookup_fullpath(world, "Parent.Child.GrandChild"); +``` + +Path lookups may be relative: + +```c +ecs_entity_t e = ecs_lookup_path(world, parent, "Child.GrandChild"); +``` + +### Macros +The C99 API heavily relies on function-style macros, probably more than you would see in other libraries. The number one reason for this is that an ECS framework needs to work with user-defined types, and C does not provide out of the box support for generics. A few strategies have been employed in the API to improve its overall ergonomics, type safety and readability. Let's start with a simple example: + +```c +typedef struct Position { + float x; + float y; +} Position; + +ECS_COMPONENT(world, Position); + +ecs_entity_t e = ecs_new(world, Position); +``` + +From a readability perspective this code looks fine as we can easily tell what is happening here. Though if we take a closer look, we can see that a typename is used where we expect an expression, and that is not possible in plain C. So what is going on? + +Let's first remove the `ECS_COMPONENT` macro and replace it with equivalent code (details are omitted for brevity): + +```c +ecs_entity_t ecs_id(Position) = ecs_component_init(world, &(ecs_component_desc_t){ + .entity.name = "Position", + .size = sizeof(Position), + .alignment = ECS_ALIGNOF(Position) +}); +``` + +The first line actually registers the component with Flecs, and captures its name and size. The result is stored in a variable with name `ecs_id(Position)`. Here, `ecs_id` is a macro that translates the typename of the component to a variable name. The actual name of the variable is: + +```c +FLECS__EPosition +``` + +ECS operations that accept a typename, such as `ecs_get` will look for the `FLECS__E` variable: +```c +Position *p = ecs_get(world, e, Position); +``` + +Translates into: + +```c +Position *p = (Position*)ecs_get_id(world, e, ecs_id(Position)); +``` + +As you can see, the `ecs_get` macro casts the result of the function to the correct type, so a compiler will throw a warning when an application tries to assign the result of the operation to a variable of the wrong type. + +Similarly, `ecs_set` is a macro that ensures that anything we pass into it is of the right type: + +```c +ecs_set(world, e, Position, {10, 20}); +``` + +Translates into: + +```c +ecs_set_id + (world, e, ecs_id(Position), sizeof(Position), + &(Position){10, 20}); +``` + +In addition to casting the value to the right type and passing in the component, this macro also captures the size of the type, which saves Flecs from having to do a component data lookup. + +Understanding how the macros work will go a long way in being able to write effective code in Flecs, and will lead to less surprises when debugging the code. + +## Entities +Entities are uniquely identifiable objects in a game or simulation. In a real time strategy game, there may be entities for the different units, buildings, UI elements and particle effects, but also for example the camera, world and player. An entity does not contain any state, and is not of a particular type. In a traditional OOP-based game, you may expect a tank in the game is of class "Tank". In ECS, an entity is simply a unique identifier, and any data and behavior associated with that entity is implemented with components and systems. + +In Flecs, an entity is represented by a 64 bit integer, which is also how it is exposed on the API: + +```c +typedef uint64_t ecs_entity_t; +``` + +Zero indicates an invalid entity. Applications can create new entities with the `ecs_new` operation: + +```c +ecs_entity_t e = ecs_new(world, 0); +``` + +This operation guarantees to return an unused entity identifier. The first entity returned is not 1, as Flecs creates a number of builtin entities during the initialization of the world. The identifier of the first returned entity is stored in the `EcsFirstUserEntityId` constant. + +### Id recycling +Entity identifiers are reused when deleted. The `ecs_new` operation will first attempt to recycle a deleted identifier before producing a new one. If no identifier can be recycled, it will return the last issued identifier + 1. + +Entity identifiers can only be recycled if they have been deleted with `ecs_delete`. When `ecs_delete` is invoked, the generation count of the entity is increased. The generation is encoded in the entity identifier, which means that any existing entity identifiers with the old generation encoded in it will be considered not alive. Calling a delete multiple times on an entity that is not alive has no effect. + +When using multiple threads, the `ecs_new` operation guarantees that the returned identifiers are unique, by using atomic increments instead of a simple increment operation. New ids generated from a thread will not be recycled ids, since this would require taking a lock on the administration. While this does not represent a memory leak, it could cause ids to rise over time. If this happens and is an issue, an application should pre-create the ids. + +### Generations +When an entity is deleted, the generation count for that entity id is increased. The entity generation count enables an application to test whether an entity is still alive or whether it has been deleted, even after the id has been recycled. Consider: + +```c +ecs_entity_t e = ecs_new(world, 0); +ecs_delete(world, e); // Increases generation + +e = ecs_new(world, 0); // Recycles id, but with new generation +``` + +The generation is encoded in the entity id, which means that even though the base id is the same in the above example, the value returned by the second `ecs_new` is different than the first. + +To test whether an entity is alive, an application can use the `ecs_is_alive` call: + +```c +ecs_entity_t e1 = ecs_new(world, 0); +ecs_delete(world, e1); + +ecs_entity_t e2 = ecs_new(world, 0); +ecs_is_alive(world, e1); // false +ecs_is_alive(world, e2); // true +``` + +It is not allowed to invoke operations on an entity that is not alive, and doing so may result in an assert. The only operation that is allowed on an entity that is not alive is `ecs_delete`. Calling delete multiple times on an entity that is not alive will not increase the generation. Additionally, it is also not allowed to add child entities to an entity that is not alive. This will also result in an assert. + +There are 16 bits reserved for generation in the entity id, which means that an application can delete the same id 65536 times before the generation resets to 0. To get the current generation of an entity, applications can use the `ECS_GENERATION` macro. To extract the entity id without the generation, an application can apply the `ECS_ENTITY_MASK` with a bitwise and: + +```c +ecs_entity_t generation = ECS_GENERATION(e); +ecs_entity_t id = e & ECS_ENTITY_MASK; +``` + +### Manual id generation +Applications do not have to rely on `ecs_new` and `ecs_delete` to create and delete entity identifiers. Entity ids may be used directly, like in this example: + +```c +ecs_add(world, 42, Position); +``` + +This is particularly useful when the lifecycle of an entity is managed by another data source (like a multiplayer server) and prevents networking code from having to check whether the entity exists. This also allows applications to reuse existing identifiers, as long as these fit inside a 64 bit integer. + +When not using manual ids, id recycling mechanisms are bypassed as these are only invoked by the `ecs_new` and `ecs_delete` operations. Combining manual ids with `ecs_new` and `ecs_delete` can result in unexpected behavior, as `ecs_new` may return an identifier that an application has already used. + +### Id ranges +An application can instruct Flecs to issue ids from a specific offset and up to a certain limit with the `ecs_set_entity_range` operation. This example ensures that id generation starts from id 5000: + +```c +ecs_set_entity_range(world, 5000, 0); +``` + +If the last issued id was higher than 5000, the operation will not cause the last id to be reset to 5000. An application can also specify the highest id that can be generated: + +```c +ecs_set_entity_range(world, 5000, 10000); +``` + +If invoking `ecs_new` would result in an id higher than `10000`, the application would assert. If `0` is provided for the maximum id, no upper bound will be enforced. + +It is possible for an application to enforce that entity operations (`ecs_add`, `ecs_remove`, `ecs_delete`) are only allowed for the configured range with the `ecs_enable_range_check` operation: + +```c +ecs_enable_range_check(world, true); +``` + +This can be useful for enforcing that an application is not modifying entities that are owned by another data source. + + +## Types + +### Basic usage +A type is typically used to describe the contents (components) of an entity. A simple example: + +```c +// Create entity with type Position +ecs_entity_t e = ecs_new(world, Position); + +// Add Velocity to the entity +ecs_add(world, e, Velocity); +``` + +After running this code, the type can be printed: + +```c +// Print the type of the entity +const ecs_type_t *type = ecs_get_type(world, e); +char *str = ecs_type_str(world, type); +``` + +Which will produce: + +``` +Position, Velocity +``` + +### Advanced usage +A type is stored as a vector of identifiers. Because components are stored as entities in Flecs, a type is defined as (pseudo, not actual definition): + +```cpp +typedef vector ecs_type_t; +``` + +As a result, an application is able to do this: + +```c +ecs_entity_t tag_1 = ecs_new(world, 0); +ecs_entity_t tag_2 = ecs_new(world, 0); + +ecs_entity_t e = ecs_new(world, 0); +ecs_add_id(world, e, tag_1); +ecs_add_id(world, e, tag_2); +``` + +Printing the contents of the type of `e` now would produce something similar to: + +``` +256, 257 +``` + +When the type contained components the names of the components were printed. This is because the component entities have a name. The following example sets the names for `tag_1` and `tag_2`: + +```c +ecs_set_name(world, tag_1, "tag_1"); +ecs_set_name(world, tag_2, "tag_2"); +``` + +Printing the type again will now produce: + +``` +tag_1, tag_2 +``` + +## Components +A component is a plain datatype that can be attached to an entity. An entity can contain any number of components, and each component can be added only once per entity. Components are registered with a world using the `ECS_COMPONENT` macro, after which they can be added and removed to and from entities. Components can be of any datatype. The following example shows how to register and use components: + +```c +// Components can be defined from regular types +typedef struct Position { + float x, y; +} Position; + +int main() { + ecs_world_t *world = ecs_init(); + + // Register the component with the world + ECS_COMPONENT(world, Position); + + // Create a new entity with the component + ecs_entity_t e = ecs_new(world, Position); + + // Remove the component from the entity + ecs_remove(world, e, Position); + + // Add the component again + ecs_add(world, e, Position); +} +``` + +Component values can be set with the `ecs_set` operation. If the entity did not yet have the component, it will be added: + +```c +ecs_set(world, e, Position, {10, 20}); +``` + +Applications can get the value of a component with the `ecs_get` function: + +The value of a component can be requested with `ecs_get`, which will return `NULL` if the entity does not have the component: + +```c +const Position *p = ecs_get(world, e, Position); +``` + +The `ecs_get` operation returns a const pointer which should not be modified by the application. An application can obtain a mutable pointer with `ecs_get_mut`. The `ecs_get_mut` operation ensures that, even when using multiple threads, an application obtains a pointer to a component that can be safely modified, whereas the `ecs_get` operation might return a pointer to memory that is shared between threads. When an application modified a component obtained with `ecs_get_mut`, it should invoke `ecs_modified` to let the framework know the component value was changed. An example: + +```c +Position *p = ecs_get_mut(world, e, Position); +p->x++; +ecs_modified(world, e, Position); +``` + +### Component handles +In order to be able to add, remove and set components on an entity, the API needs access to the component handle. A component handle uniquely identifies a component and is passed to API functions. There are two types of handles that are accepted by API functions, a type handle and an entity handle. These handles are automatically defined as variables by the `ECS_COMPONENT` macro. If an application wants to use the component in another scope, the handle will have to be either declared globally or passed to that scope explicitly. + +#### Global component handles +To globally declare a component, an application can use the `ECS_COMPONENT_DECLARE` and `ECS_COMPONENT_DEFINE` macros: + +```c +// Declare component variable in the global scope +ECS_COMPONENT_DECLARE(Position); + +// Function that uses the global component variable +ecs_entity_t create_entity(ecs_world_t *world) { + return ecs_new(world, Position); +} + +int main(int argc, char *argv[]) { + ecs_world_t *world = ecs_init(); + + // Register component, assign id to the global component variable + ECS_COMPONENT_DEFINE(world, Position); + + ecs_entity_t e = create_entity(world); + + return ecs_fini(world); +} +``` + +To make a component available for other source files, an application can use the regular `extern` keyword: + +```c +extern ECS_COMPONENT_DECLARE(Position); +``` + +Declaring components globally works with multiple worlds, as the second time a component is registered it will use the same id. There is one caveat: an application should not define a component in world 2 that is not defined in world 1 _before_ defining the shared components. The reason for this is that if world 2 does not know that the shared component exists, it may assign its id to another component, which can cause a conflict. + +If this is something you cannot guarantee in an application, a better (though more verbose) way is to use local component handles. + +#### Local component handles +When an application cannot declare component handles globally, it can pass component handles manually. Manually passing component handles takes the variables that are declared by the `ECS_COMPONENT` macro and passes them to other functions. This section describes how to pass those handles around. + +Some operations can process multiple components in a single operation, like `ecs_add` and `ecs_remove`. Such operations require a handle of `ecs_type_t`. The `ECS_COMPONENT` macro defines a variable of `ecs_type_t` that contains only the id of the component. The variable defined by `ECS_COMPONENT` can be accessed with `ecs_type(ComponentName)`. This escapes the component name, which is necessary as it would otherwise conflict with the C type name. The following example shows how to pass a type handle to another function: + +```c +typedef struct Position { + float x, y; +} Position; + +void new_w_position(ecs_world_t *t, ecs_id_t ecs_id(Position)) { + // ecs_new uses an ecs_id_t + ecs_new(world, Position); +} + +int main() { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + new_w_position(world, ecs_id(Position)); + + ecs_fini(world); +} +``` + +The `ecs_new`, `ecs_add` and `ecs_remove` (not exhaustive) functions are wrapper macros around functions that accept a component id. The following code is equivalent to the previous example: + +```c +typedef struct Position { + float x, y; +} Position; + +void new_w_position(ecs_world_t *t, ecs_id_t p_id) { + // Use plain variable name with the ecs_new_w_id operation + ecs_new_w_id(world, p_id); +} + +int main() { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + new_w_position(world, ecs_id(Position)); + + ecs_fini(world); +} +``` + +### Component disabling +Components can be disabled, which prevents them from being matched with queries. Contrary to removing a component, disabling a component does not remove it from an entity. When a component is enabled after disabling it, the original value of the component is restored. + +To enable or disable a component, use the `ecs_enable_component` function: + +```c +typedef struct Position { + float x, y; +} Position; + +int main() { + ecs_world_t *world = ecs_init(); + + ECS_COMPONENT(world, Position); + + ecs_entity_t e = ecs_new(world, Position); + + /* Component is enabled by default */ + + /* Disable the component */ + ecs_enable_component(world, e, Position, false); + + /* Will return false */ + printf("%d\n", ecs_is_enabled_component(world, e, Position)); + + /* Re-enable the component */ + ecs_enable_component(world, e, Position, true); + + ecs_fini(world); +} +``` + +Component disabling works by maintaining a bitset alongside the component array. When a component is enabled or disabled, the bit that corresponds with the entity is set to 1 or 0. Bitsets are not created by default. Only after invoking the `ecs_enable_component` operation for an entity will be entity be moved to a table that keeps track of a bitset for that component. + +When a query is matched with a table that has a bitset for a component, it will automatically use the bitset to skip disabled values. If an entity contains multiple components tracked by a bitset, the query will evaluate each bitset and only yield entities for which all components are enabled. To ensure optimal performance, the query will always return the largest range of enabled components. Nonetheless, iterating a table with a bitset is slower than a regular table. + +If a query is matched with a table that has one or more bitsets, but the query does not match with components tracked by a bitset, there is no performance penalty. + +Component disabling can be used to temporarily suspend and resume a component value. It can also be used as a faster alternative to `ecs_add`/`ecs_remove`. Since the operation only needs to set a bit, it is a significantly faster alternative to adding/removing components, at the cost of a slightly slower iteration speed. If a component needs to be added or removed frequently, enabling/disabling is recommended. + +#### Limitations +Component disabling does not work for components not matched with the entity. If a query matches with a component from a base (prefab) or parent entity and the component is disabled for that entity, the query will not take this into account. If entities with disabled components from a base or parent entity need to be skipped. a query should manually check this. + +Another limitation is that currently the query NOT (!) operator does not take into account disabled entities. The optional operator (?) technically works, but a query is unable to see whether a component has been set or not as both the enabled and disabled values are returned to the application in a single array. + +## Tagging +Tags are much like components, but they are not associated with a data type. Tags are typically used to add a flag to an entity, for example to indicate that an entity is an Enemy: + +```c +int main() { + ecs_world_t *world = ecs_init(); + + // Register the tag with the world. There is no Enemy type + ECS_TAG(world, Enemy); + + // Add the Enemy tag + ecs_add(world, e, Enemy); + + // Remove the Enemy tag + ecs_remove(world, e, Enemy); +} +``` + +### Tag handles +Just like components, the API needs a handle to a tag before it can use it, and just like `ECS_COMPONENT`, the `ECS_TAG` macro defines two variables, one of type `ecs_type_t` and one of `ecs_entity_t`. Passing a handle of an `ecs_type_t` into a function looks similar to a component: + +```c +void new_w_tag(ecs_world_t *t, ecs_type_t ecs_type(Tag)) { + // ecs_new uses an ecs_type_t + ecs_new(world, Tag); +} + +int main() { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + new_w_tag(world, ecs_type(Tag)); + + ecs_fini(world); +} +``` + +For functions that require an `ecs_entity_t` handle, the tag variable names are not escaped, since they do not clash with a C type name. An example: + +```c +void add_tag(ecs_world_t *t, ecs_entity_t e, ecs_entity_t Tag) { + ecs_add_id(world, e, Tag); +} + +int main() { + ecs_world_t *world = ecs_init(); + + ECS_TAG(world, Tag); + + ecs_entity_t e = ecs_new(world, 0); + add_tag(world, e, Tag); + + ecs_fini(world); +} +``` + +Anyone who paid careful attention to this example will notice that the `ecs_add_id` operation accepts two regular entities. + +## Observers +Observers are callbacks that are invoked when one or more events matches the query of an observer. Events can be either user defined or builtin. Examples of builtin events are `OnAdd`, `OnRemove` and `OnSet`. + +When an observer has a query with more than one component, the observer will not match until the entity for which the event is emitted satisfies the entire query. + +An example of an observer with a single component: + +```c +ECS_OBSERVER(world, AddPosition, EcsOnAdd, Position); +``` + +The implementation of the observer looks similar to a system: + +```c +void AddPosition(ecs_iter_t *it) { + Position *p = ecs_field(it, Position, 1); + + for (int i = 0; i < it->count; i++) { + p[i].x = 10; + p[i].y = 20; + printf("Position added\n"); + } +} +``` + +## Modules +Modules allow an application to split up systems and components into separate decoupled units. The purpose of modules is to make it easier to organize systems and components for large projects. Additionally, modules also make it easier to split off functionality into separate compilation units. + +A module consists out of a couple parts: + +- The declaration of the components, tags, and systems of the module contents +- An import function that loads the module contents for a world + +The declaration of the module contents module is typically located in a separate module header file, and look like this for a module named "Vehicles": + +```c +typedef struct Car { + float speed; +} Car; + +typedef struct Bus { + float speed; +} Bus; + +typedef struct MotorCycle { + float speed; +} MotorCycle; + +/* Components are declared with ECS_COMPONENT_DECLARE */ +extern ECS_COMPONENT_DECLARE(Car); +extern ECS_COMPONENT_DECLARE(Bus); +extern ECS_COMPONENT_DECLARE(MotorCycle); + +/* Tags are declared with ECS_DECLARE */ +extern ECS_DECLARE(Moving); + +/* Systems are declared with ECS_SYSTEM_DECLARE */ +extern ECS_SYSTEM_DECLARE(Move); + +/* Forward declaration to the import function */ +/* The function name has to follow the convention: Import */ +void VehiclesImport(ecs_world_t *world); +``` + +The import function for this module is typically located in a module source file, and would look like this: + +```c +void VehiclesImport(ecs_world_t *world) { + /* Define the module */ + ECS_MODULE(world, Vehicles); + + /* Declare components, tags, systems, and assign them to the module with their respective _DEFINE macros */ + ECS_COMPONENT_DEFINE(world, Car); + ECS_COMPONENT_DEFINE(world, Bus); + ECS_COMPONENT_DEFINE(world, MotorCycle); + ECS_TAG_DEFINE(world, Moving); + ECS_SYSTEM_DEFINE(world, Move, EcsOnUpdate, Car, Moving); +} +``` + +After the module has been defined, it can be imported in an application like this: + +```c +ecs_world_t *world = ecs_init(); + +/* Import module, which invokes the module import function */ +ECS_IMPORT(world, Vehicles); + +/* The module contents can now be used */ +ecs_entity_t e = ecs_new(world, Car); +``` + +Module contents are namespaced, which means that the identifiers of the content of the module (components, tags, systems) are stored in the scope of the module. For the above example module, everything would be stored in the `vehicles` scope. To resolve the `Car` component by name, an application would have to do: + +```c +ecs_entity_t car_entity = ecs_lookup_fullpath(world, "vehicles.Car"); +``` + +Note that even though the module name is specified with uppercase, the name is stored with lowercase. This is because the naming convention for modules in C is PascalCase, whereas the stored identifiers use snake_case. If a module name contains several uppercase letters, this will be translated to a nested module. For example, the C module name `MySimpleModule` will be translated to `my.simple.module`. + +### Modules in C++ +A module in C++ is defined as a class where the module contents are defined in the constructor. The above Vehicles module would look like this in C++: + +```cpp +/* In C++ it is more convenient to define tags as empty structs */ +struct Moving { }; + +/* Module implementation */ +class vehicles { +public: + vehicles(flecs::world& world) { + flecs::module(world, "vehicles"); + + m_car = flecs::component(world, "Car"); + m_bus = flecs::component(world, "Bus"); + m_motor_cycle = flecs::component(world, "MotorCycle"); + + m_moving = flecs::component(world, "Moving"); + m_move = flecs::system(world, "Move") + .each([](flecs::entity e, Car &car, Moving&) { + /* System implementation */ + }); + } + + flecs::entity m_car; + flecs::entity m_bus; + flecs::entity m_motor_cycle; + flecs::entity m_moving; + flecs::entity m_move; +} +``` + +An application can import the module in C++ like this: + +```cpp +flecs::world world; +flecs::import(world); +``` + +## Hierarchies +Entities in Flecs can be organized in hierarchies, which is useful when for example constructing a scene graph. To create hierarchies, applications can add `ChildOf` relationships to entities. This creates a relationship between a parent entity and a child entity that the application can later traverse. This is an example of a simple hierarchy: + +```c +ecs_entity_t parent = ecs_new(world, 0); +ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); +``` + +`ChildOf` relationships can be added and removed dynamically, similar to how components can be added and removed: + +```c +ecs_add_pair(world, child, EcsChildOf, parent); +ecs_remove_pair(world, child, EcsChildOf, parent); +``` + +`ChildOf` relationships can also be created through the `ECS_ENTITY` macro: + +```c +ECS_ENTITY(world, parent, 0); +ECS_ENTITY(world, child, (ChildOf, parent)); +``` + +### Iteration +Applications can iterate hierarchies depth first with the `ecs_children` API in C, and the `children()` iterator in C++. This example shows how to iterate all the children of an entity: + +In C: +```c +ecs_iter_t it = ecs_children(world, parent); + +while(ecs_children_next(&it)) { + for (int i = 0; i < it.count; i++) { + ecs_entity_t child = it.entities[i]; + char *path = ecs_get_fullpath(world, child); + printf("%s\n", path); + free(path); + } +} +``` + +In C++: +```cpp +e.children([&](flecs::entity child) { + std::cout << child.path() << std::endl; +}); +``` + +### Hierarchical queries +Queries and systems can request data from parents of the entity being iterated over with the `parent` modifier: + +```c +// Iterate all entities with Position that have a parent that also has Position +ecs_query_t *q = ecs_query_new(world, "Position(parent), Position"); +``` + +Additionally, a query can iterate the hierarchy in breadth-first order by providing the `cascade` modifier: + +```c +// Iterate all entities with Position that have a parent that also has Position +ecs_query_t *q = ecs_query_new(world, "Position(parent|cascade), Position"); +``` + +This does two things. First, it will iterate over all entities that have Position and that _optionally_ have a parent that has `Position`. By making the parent component optional, it is ensured that if an application is iterating a tree of entities, the root is also included. Secondly, the query iterates over the children in breadth-first order. This is particularly useful when writing transform systems, as they require parent entities to be transformed before child entities. + +The above query does not match root entities, as they do not have a parent with `Position`. To also match root entities, add `?` to make the term optional: + +```c +ecs_query_t *q = ecs_query_new(world, "?Position(parent|cascade), Position"); +``` + +See the [query manual](Queries.md) section for more details. + +### Path identifiers +When entities in a hierarchy have names assigned to them, they can be looked up with path expressions. A path expression is a list of entity names, separated by a scope separator character (by default a `.`, and `::` in the C++ API). This example shows how to request the path expression from an entity: + +```c +ECS_ENTITY(world, parent, 0); +ECS_ENTITY(world, child, (ChildOf, parent)); + +char *path = ecs_get_fullpath(world, child); +printf("%s\n", path); // Prints "parent.child" +free(path); +``` + +To lookup an entity using a path, use `ecs_lookup_fullpath`: + +```c +ecs_entity_t e = ecs_lookup_fullpath(world, "parent.child"); +``` + +Applications can also lookup entities using a relative path expression: + +```c +ecs_entity_t e = ecs_lookup_path(world, parent, "child.grand_child"); +``` + +Additionally, applications can specify a custom path separator when looking up or requesting paths: + +```c +// Lookup child::grand_child relative to parent +ecs_entity_t e = ecs_lookup_path_w_sep( + world, parent, "child::grand_child", "::", "::"); + +// Get path of child relative to parent +char *path = ecs_get_path_w_sep(world, parent, child, "::", "::"); +``` + +Note that the path separator is provided twice, once for the prefix and once for the separator. This lets the API correctly handle expressions like `::parent::child::grand_child`. + +### Scoping +Applications can set a default scope with the `ecs_set_scope` function, so that all operations are evaluated relative to a scope. The scope is set on a stage, which makes it thread safe when executed from within a flecs worker thread. This example shows how to set the scope: + +```c +ecs_entity_t parent = ecs_new(world, 0); + +// Set the current scope to the parent +ecs_entity_t prev_scope = ecs_set_scope(world, parent); + +// This entity is created as child of parent +ecs_entity_t child = ecs_new(world, 0); + +// Look for "child" relative to parent +ecs_entity_t e = ecs_lookup_fullpath(world, "child"); + +// It's good practice to restore the previous scope +ecs_set_scope(prev_scope); +``` + +Modules automatically set the scope to the module itself, so that the module acts as a namespace for its contents. + +### Paths and signatures +When referencing entities or components in a signature or type expression that are not stored in the root, an application will have to provide the path. Signatures and type expressions always use the dot (`.`) as separator. For example, if a component "Position" is defined in the module "transform", a system subscribing for the component would have to be defined like this: + +```c +ECS_SYSTEM(world, Move, EcsOnUpdate, transform.Position); +``` + +The same goes for other parts of the API that accept a type expression, like `ECS_ENTITY`: + +```c +ECS_ENTITY(world, Movable, transform.Position); +``` + +If the system would be defined in the same scope as the `Position` component, it would not need to specify the path: + +```c +ECS_ENTITY(world, transform, 0); + +ecs_entity_t prev_scope = ecs_set_scope(world, transform); + +ECS_COMPONENT(world, Position); + +// System is in the same scope, no need to add "transform" +ECS_SYSTEM(world, MoveInScope, EcsOnUpdate, Position); + +ecs_set_scope(world, prev_scope); + +// This system is not in the same scope, and needs to add transform +ECS_SYSTEM(world, MoveNotInScope, EcsOnUpdate, transform.Position); +``` + +## Inheritance +Inheritance is the ability to share components between entities by _inheriting_ from them, by using the `IsA` relationship. This is a simple example in the C API: + +```c +// Create a base entity +ecs_entity_t base = ecs_new(world, 0); +ecs_set(world, base, Position, {10, 20}); + +// Derive from base +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); + +// e now shares Position with base +ecs_get(world, base, Position) == ecs_get(world, e, Position); // 1 +``` + +`IsA` relationships can be added and removed dynamically, similar to how components can be added and removed: + +```c +ecs_add_id(world, e, (IsA, base)); +ecs_remove_id(world, e, (IsA, base)); +``` + +`IsA` relationships can also be created through the `ECS_ENTITY` macro: + +```c +ECS_ENTITY(world, base, Position); +ECS_ENTITY(world, e, (IsA, base)); +``` + +`IsA` relationships can be nested: + +```c +ecs_entity_t base = ecs_new(world, 0); +ecs_set(world, base, Position, {10, 20}); + +ecs_entity_t derived = ecs_new_w_pair(world, EcsIsA, base); + +// Derive from "derived" which is itself derived from base +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, derived); + +// All three entities now share Position +ecs_get(world, base, Position) == ecs_get(world, e, Position); // 1 +ecs_get(world, base, Position) == ecs_get(world, derived, Position); // 1 +``` + +### Overriding +Derived entities can override components from their base by adding the component as they would normally. When overriding a component, the value of the base component is copied to the entity. This example shows how a derived entity overrides the Position component: + +```c +// Shortcut for creating a base entity and setting Position +ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); + +// Derive from the base +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); + +// Override Position +ecs_add(world, e, Position); + +// Position component no longer matches with base +ecs_get(world, base, Position) != ecs_get(world, e, Position); // 1 + +// Prints {10, 20} +const Position *p = ecs_get(world, e, Position); +printf("{%f, %f}\n", p->x, p->y); +``` + +When an entity shared a component from a base entity, we say that the component is "shared". If the component is not shared, it is "owned". After an entity overrides a component, it will own the component. + +It is possible to remove an override, in which case the component will be shared with the base entity again: + +```c +// Removes override on Position +ecs_remove(world, e, Position); + +// Position is again shared with base +ecs_get(world, base, Position) == ecs_get(world, e, Position); // 1 +``` + +Overrides work with nested `IsA` relationships: + +```c +// Shortcut for creating a base entity and setting Position +ecs_entity_t base = ecs_new(world, 0); +ecs_set(world, base, Position, {10, 20}); +ecs_set(world, base, Velocity, {1, 1}); + +// Create derived entity, override Position +ecs_entity_t derived = ecs_new_w_pair(world, EcsIsA, base); +ecs_add(world, base, Position); + +// Derive from 'derived', which is derived from base +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, derived); + +// The entity now shares Position from derived, and Velocity from base +``` + +### Automatic overriding +In some scenarios it is desirable that an entity is initialized with a specific set of values, yet does not share the components from the base entity. In this case the derived entity can override each component individually, but this can become hard to maintain as components are added or removed to the base. This can be achieved by marking components as owned. Consider the following example: + +```c +// Create a base. Simply deriving the base will share the component, but not override it. +ecs_entity_t Base = ecs_set(world, 0, Position, {10, 20}); + +// Mark as OVERRIDE. This ensures that when base is derived from, Position is overridden +ecs_add_id(world, world, Base, ECS_OVERRIDE | ecs_id(Position)); + +// Create entity from BaseType. This adds the IsA relationship in addition +// to overriding Position, effectively initializing the Position component for the entity. +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Base); +``` + +The combination of instancing, overriding and OVERRIDE is one of the fastest and easiest ways to create an entity with a set of initialized components. The OVERRIDE relationship can also be specified inside type expressions. The following example is equivalent to the previous one: + +```c +ECS_ENTITY(world, Base, Position, OVERRIDE | Position); + +ecs_set(world, Base, Position, {10, 20}); + +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Base); +``` + +### Inheritance hierarchies +If a base entity has children, derived entities of that base entity will, when the `IsA` relationship is added, acquire the same set of children. Take this example: + +```c +ecs_entity_t parent = ecs_new(world, 0); +ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent); +ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent); + +// Derive from parent, two children are added to the entity +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, parent); +``` + +The children that are copied to the entity will have exactly the same set of components as the children of the base. For example, if the base child has components `Position, Velocity`, the derived child will also have `Position, Velocity`. Furthermore, the values of the base child components will be copied to the entity child: + +```c +ecs_entity_t parent = ecs_new(world, 0); +ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); +ecs_set_name(world, child, "Child"); // Give child a name, so we can look it up +ecs_set(world, child, Position, {10, 20}); + +// Derive from parent, two children are added to the derived entity +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, parent); +ecs_entity_t e_child = ecs_lookup_path(world, e, "Child"); +const Position *p = ecs_get(world, e_child, Position); +printf("{%f, %f}\n", p->x, p->y); // Prints {10, 20} + +// The components are not shared with the derived child! +ecs_get(world, child, Position) != ecs_get(world, e_child, Position); // 1 +``` + +Since the children of the derived entity have the exact same components as the base children, their components are not shared. Component sharing between children is possible however, as `IsA` relationships are also copied over to the child of the derived entity: + +```c +ecs_entity_t parent = ecs_new(world, 0); + +// Create child base from which we will share components +ecs_entity_t child_base = ecs_new(world, 0); +ecs_set(world, child_base, Position, {10, 20}); +ecs_set_name(world, child, "Child"); + +// Create actual child that inherits from the child base +ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); +ecs_add_pair(world, child, EcsIsA, child_base); + +// Inherit from parent, two children are added to the entity +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, parent); +ecs_entity_t e_child = ecs_lookup_path(world, e, "Child"); + +// The component is now shared with the child and child_base +ecs_get(world, child, Position) == ecs_get(world, e_child, Position); // 1 +``` + +### Prefabs +Prefabs are entities that can be used as templates for other entities. Prefabs are regular entities, except that they are not matched by default with systems. To create a prefab, add the `EcsPrefab` tag when creating an entity: + +```c +ecs_entity_t prefab = ecs_new_w_id(world, EcsPrefab); +``` + +The `EcsPrefab` tag can also be added or removed dynamically: + +```c +ecs_add_id(world, prefab, EcsPrefab); +ecs_remove_id(world, prefab, EcsPrefab); +``` + +Prefabs can also be created with the `ECS_PREFAB` macro: + +```c +ECS_PREFAB(world, prefab, Position, Velocity); +``` + +To instantiate a prefab, an application can use the `IsA` relationship: + +```c +ecs_entity_t e = ecs_new_w_pair(world, IsA, prefab); +``` + +To ensure that entities that inherit from a prefab don't also inherit the `Prefab` tag (which would cause them to not get matched with systems), the `Prefab` tag does not propagate to derived entities. This is illustrated in the following example: + +```c +ECS_PREFAB(world, prefab, Position); + +ecs_has(world, prefab, EcsPrefab); // true +ecs_has(world, prefab, Position); // true + +ecs_entity_t e = ecs_new_w_pair(world, IsA, prefab); +ecs_has(world, e, EcsPrefab); // false +ecs_has(world, e, Position); // true +``` + +## Deferred operations +Applications can defer entity with the `ecs_defer_begin` and `ecs_defer_end` functions. This records all operations that happen inside the begin - end block, and executes them when `ecs_defer_end` is called. Deferred operations are useful when an application wants to make modifications to an entity while iterating, as doing this without deferring an operation could modify the underlying data structure. An example: + +```c +ecs_defer_begin(world); + ecs_entity_t e = ecs_new(world, 0); + ecs_add(world, e, Position); + ecs_set(world, e, Velocity, {1, 1}); +ecs_defer_end(world); +``` + +The effects of these operations will not be visible until the `ecs_defer_end` operation. + +There are a few things to keep in mind when deferring: +- creating a new entity will always return a new id which increases the last used id counter of the world +- `ecs_get_mut` returns a pointer initialized with the current component value, and does not take into account deferred set or get_mut operations +- if an operation is called on an entity which was deleted while deferred, the operation will ignored by `ecs_defer_end` +- if a child entity is created for a deleted parent while deferred, the child entity will be deleted by `ecs_defer_end` + diff --git a/engine/libs/flecs/docs/Queries.md b/engine/libs/flecs/docs/Queries.md new file mode 100644 index 0000000..ab20f5e --- /dev/null +++ b/engine/libs/flecs/docs/Queries.md @@ -0,0 +1,3120 @@ +# Queries +At the core of an Entity Component System are queries, which make it possible to find entities matching a list of conditions in realtime, for example: + +``` +Position, Velocity +``` + +This query returns all entities that at least have the `Position` and `Velocity` components. Queries provide direct access to cache efficient storages of matched components, which gives applications the ability to process large numbers (think millions) of entities each frame. + +## Highlights +Here are some of the highlights of Flecs queries: + +- Queries can [cache results](#cached-queries), which removes search overhead from time critical game loops + +- Queries can efficiently traverse [entity relationship](#Relationships) graphs, reducing the need for building custom hard to maintain data structures ([blog](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c)). + +- Queries can be created at runtime and match components that are created at runtime. + +- Queries support `and`, `or`, `not` and `optional` [operators](#operator-overview). + +- Queries can combine components from multiple [sources](#source), like a transform system that uses `Position` component from an entity and its parent. + +- Queries can be observed, allowing applications to get notified when entities start or stop matching a query. + +- Query results are self describing, which means iterators can be passed to generic code which can then do things like [serializing it to JSON](https://github.com/SanderMertens/flecs/blob/master/include/flecs/addons/json.h). + +- Queries can be created with the regular API or by parsing a query string, making it possible to [create tools that create queries at runtime](https://www.flecs.dev/explorer/). + +- Queries support [component inheritance](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules/component_inheritance). + +## Definitions + +| Name | Description | +|--------------|-------------| +| Id | An id that can be matched, added and removed | +| Component | Id with a single element (same as an entity id) | +| Pair | Id with two elements | +| Tag | Component or pair not associated with data | +| Relationship | Used to refer to first element of pair | +| Target | Used to refer to second element of pair | +| Source | Entity on which a term is matched | +| Iterator | Object used to iterate a query | +| Field | A single value or array of values made available by an iterator. An iterator usually provides a field per query term. | + +## Examples +Make sure to check out the code examples in the repository: + +https://github.com/SanderMertens/flecs/tree/master/examples + + - [queries (C)](https://github.com/SanderMertens/flecs/tree/master/examples/c/queries) + - [queries (C++)](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/queries) + - [rules (C)](https://github.com/SanderMertens/flecs/tree/master/examples/c/rules) + - [rules (C++)](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/rules) + +## Types +Flecs has different query types, which are optimized for different kinds of use cases. This section provides a brief overview of each kind: + + ### Filters + Filters are cheap to create, low overhead, reasonably efficient to iterate. They are good for ad-hoc queries with runtime-defined conditions. An example: + + ```c + ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, { ecs_id(Velocity) } + } +}); + +ecs_iter_t it = ecs_filter_iter(world, f); +while (ecs_filter_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + + for (int i = 0; i < it.count; i ++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} +``` +```cpp +flecs::filter f = + world.filter(); + +f.each([](Position& p, Velocity& v) { + p.x += v.x; + p.y += v.y; +}); +``` + +### Cached Queries +Cached queries cache the output of a filter. They are more expensive to create and have higher overhead, but are the fastest to iterate. Cached queries are the default for systems. An example: + +```c +ecs_query_t *q = ecs_query(world, { + .filter.terms = { + { ecs_id(Position) }, { ecs_id(Velocity) } + } +}); + +ecs_iter_t it = ecs_query_iter(world, q); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + + for (int i = 0; i < it.count; i ++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} +``` +```cpp +flecs::query q = + world.query(); + +q.each([](Position& p, Velocity& v) { + p.x += v.x; + p.y += v.y; +}); +``` + +### Rules +Rules are a constraint-based query engine capable of traversing graphs. They are more expensive to create than filters, have low overhead, and their iteration performance depends on query complexity. An example: + +```c +ecs_rule_t *r = ecs_rule(world, { + .terms = { + { ecs_id(Position) }, { ecs_id(Velocity) } + } +}); + +ecs_iter_t it = ecs_rule_iter(world, r); +while (ecs_rule_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + + for (int i = 0; i < it.count; i ++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} +``` +```cpp +flecs::rule r = + world.rule(); + +r.each([](Position& p, Velocity& v) { + p.x += v.x; + p.y += v.y; +}); +``` + +For more information on how each implementation performs, see [Performance](#performance). + +## Creation +This section explains how to create queries in the different language bindings and the flecs query DSL. + +### Query Descriptors (C) +Query descriptors are the C API for creating queries. The API uses a type called `ecs_filter_desc_t`, to describe the structure of a query. This type is used to create all query kinds (`ecs_filter_t`, `ecs_query_t`, `ecs_rule_t`). An example: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, { ecs_id(Velocity) }, + } +}); +``` + +The example shows the short notation, which looks like this when expanded: + +```c +ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ + .terms = { + { ecs_id(Position) }, { ecs_id(Velocity) }, + } +}); +``` + +Query descriptors can also be used by the C++ API. However because C++ does not support taking the address of a temporary, and not all language revisions support designated initializers, query descriptors in C++ should be used like this: + +```c +ecs_filter_desc_t desc = {}; // Zero-initialize the struct +desc.terms[0].id = ecs_id(Position); +desc.terms[1].id = ecs_id(Velocity); +ecs_filter_t *f = ecs_filter_init(world, &desc); +``` + +The following table provides an overview of the query types with the init/fini functions: + +| Kind | Type | Init | Fini | Descriptor type | +|--------|----------------|-------------------|-------------------|---------------------| +| Filter | `ecs_filter_t` | `ecs_filter_init` | `ecs_filter_fini` | `ecs_filter_desc_t` | +| Query | `ecs_query_t` | `ecs_query_init` | `ecs_query_fini` | `ecs_query_desc_t` | +| Rule | `ecs_rule_t` | `ecs_rule_init` | `ecs_rule_fini` | `ecs_filter_desc_t` | + +Additionally the descriptor types for systems (`ecs_system_desc_t`) and observers (`ecs_observer_desc_t`) embed the `ecs_filter_desc_t` descriptor type. + +### Query Builder (C++) +Query builders are the C++ API for creating queries. The builder API is built on top of the descriptor API, and adds a layer of convenience and type safety that matches modern idiomatic C++. The builder API is implemented for all query kinds (filters, cached queries, rules). An example of a simple query: + +```cpp +flecs::filter f = + world.filter(); +``` + +Queries created with template arguments provide a type safe way to iterate components: + +```cpp +f.each([](Position& p, const Velocity& v) { + p.x += v.x; + p.y += v.y; +}); +``` + +The builder API allows for incrementally constructing queries: + +```cpp +flecs::filter q = world.filter_builder(); +f.term(); + +if (add_npc) { + f.term(); // Conditionally add +} + +f.build(); // Create query +``` + +The following table provides an overview of the query types with the factory functions: + +| Kind | Type | Factory | +|--------|-----------------|-------------------------| +| Filter | `flecs::filter` | `world::filter_builder` | +| Query | `flecs::query` | `world::query_builder` | +| Rule | `flecs::rule` | `world::rule_builder` | + +Additional helper methods have been added to the C++ API to replace combinations of the `term` method with other methods. They are the following: + +| Term | Equivalent | +|------------------------|------------------------------| +| `.with()` | `.term()` | +| `.without()`| `.term().not_()` | +| `.read()` | `.term().read()` | +| `.write()` | `.term().write()` | + +### Query DSL +The query DSL (domain specific language) is a string format that can represent a query. The query DSL is used by convenience macros in the C API like `ECS_SYSTEM` and `ECS_OBSERVER`, and makes it easier to create queries at runtime for tools like https://www.flecs.dev/explorer/. An example of a simple query in the DSL: + +``` +Position, [in] Velocity +``` + +An example of how the DSL is used with the `ECS_SYSTEM` convenience macro: + +```c +ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity); +``` + +Queries can be created from expressions with both the descriptor and builder APIs: + +```c +ecs_filter_t *f = ecs_filter(world, { + .expr = "Position, [in] Velocity" +}); +``` +```cpp +flecs::filter<> f = world.filter_builder() + .expr("Position, [in] Velocity") + .build(); +``` + +The query DSL requires the `FLECS_PARSER` addon to be included in a build. + +## Iteration +This section describes the different ways queries can be iterated. The code examples use filters, but also apply to cached queries and rules. + +### Iterators (C) +In the C API an iterator object of type `ecs_iter_t` can be created for each of the query kinds, using the `ecs_filter_iter`, `ecs_query_iter` and `ecs_rule_iter` functions. This iterator can then be iterated with the respective `next` functions: `ecs_filter_next`, `ecs_query_next` and `ecs_rule_next`. + +An iterator can also be iterated with the `ecs_iter_next` function which is slightly slower, but does not require knowledge about the source the iterator was created for. + +An example: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, { ecs_id(Velocity) }, + } +}); + +ecs_iter_t it = ecs_filter_iter(world, f); + +// Outer loop: matching tables +while (ecs_filter_next(&it)) { + Position *p = ecs_field(&it, Position, 1); // 1st term + Velocity *v = ecs_field(&it, Velocity, 2); // 2nd term + + // Inner loop: entities in table + for (int i = 0; i < it.count; i ++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} +``` + +Iteration is split up into two loops: the outer loop which iterates tables, and the inner loop which iterates the entities in that table. This approach provides direct access to component arrays, which allows compilers to do performance optimizations like auto-vectorization. + +The indices provided to the `ecs_field` function must correspond with the order in which terms have been specified in the query. This index starts counting from `1`, with index `0` reserved for the array containing entity ids. + +### Each (C++) +The `each` function is the default and often fastest approach for iterating a query in C++. `each` can be called directly on a `flecs::filter`, `flecs::query` and `flecs::rule`. An example: + +```cpp +auto f = world.filter(); + +f.each([](Position& p, const Velocity& v) { + p.x += v.x; + p.y += v.y; +}); +``` + +A `flecs::entity` can be added as first argument: + +```cpp +auto f = world.filter(); + +f.each([](flecs::entity e, Position& p) { + std::cout << e.name() << ": " + << p.x << ", " << p.y + << std::endl; +}); +``` + +A `flecs::iter` and `size_t` argument can be added as first arguments. This variant of `each` provides access to the `flecs::iter` object, which contains more information about the object being iterated. The `size_t` argument contains the index of the entity being iterated, which can be used to obtain entity-specific data from the `flecs::iter` object. An example: + +```cpp +auto f = world.filter_builder() + .term(Likes, flecs::Wildcard) + .build(); + +f.each([](flecs::iter& it, size_t index, Position& p) { + flecs::entity e = it.entity(index); + std::cout << e.name() << ": " + << it.id(2).str() // prints pair + << std::endl; +}); +``` + +When a query contains a template argument that is an empty type (a struct without any members), it should be passed by value instead of by reference: + +```cpp +struct Tag { }; + +auto f = world.filter(); + +f.each([](flecs::entity e, Tag) { + std::cout << e.name() << std::endl; +}); +``` + +Alternatively an empty type can be specified outside of the query type, which removes it from the signature of `each`: + +```cpp +struct Tag { }; + +auto f = world.filter_builder() + .term() + .build(); + +f.each([](flecs::entity e) { + std::cout << e.name() << std::endl; +}); +``` + +### Iter (C++) +The `iter` function has an outer and inner loop (similar to C iterators) which provides more control over how to iterate entities in a table than `each`. The `iter` function makes it possible, for example, to run code only once per table, or iterate entities multiple times. + +An example: + +```cpp +auto f = world.filter(); + +f.iter([](flecs::iter& it, Position *p, Velocity *v) { + // Inner loop + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + std::cout << it.entity(i).name() << ": " + << p.x << ", " << p.y + << std::endl; + } +}); +``` + +Instead of using `flecs::iter` as iterator directly, an application can also use the `flecs::iter::count` method which provides more flexibility: + +```cpp +f.iter([](flecs::iter& it, Position *p, Velocity *v) { + // Inner loop with manual iteration + for (size_t i = 0; i < it.count(); i ++) { + // ... + } +}); +``` + +The component arguments may be omitted, and can be obtained from the iterator object: + +```cpp +auto f = world.filter(); + +f.iter([](flecs::iter& it) { + auto p = it.field(1); + auto v = it.field(2); + + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +}); +``` + +This can be combined with an untyped variant of the `field` method to access component data without having to know component types at compile time. This can be useful for generic code, like serializers: + +```cpp +auto f = world.filter(); + +f.iter([](flecs::iter& it) { + void *ptr = it.field(1); + flecs::id type_id = it.id(1); + + // ... +}); +``` + +### Iteration safety +Entities can be moved between tables when components are added or removed. This can cause unwanted side effects while iterating a table, like iterating an entity twice, or missing an entity. To prevent this from happening, a table is locked by the C++ `each` and `iter` functions, meaning no entities can be moved from or to it. + +When an application attempts to add or remove components to an entity in a table being iterated over, this can throw a runtime assert. An example: + +```cpp +auto f = world.filter(); + +f.each([](flecs::entity e, Position&) { + e.add(); // throws locked table assert +}); +``` + +This can be addressed by deferring operations while the query is being iterated: + +```cpp +auto f = world.filter(); + +world.defer([&]{ + f.each([](flecs::entity e, Position&) { + e.add(); // OK + }); +}); // operations are executed here +``` + +An application can also use the `defer_begin` and `defer_end` functions which achieve the same goal: + +```cpp +auto f = world.filter(); + +world.defer_begin(); + +f.each([](flecs::entity e, Position&) { + e.add(); // OK +}); + +world.defer_end(); // operations are executed here +``` + +Code ran by a system is deferred by default. + +## Reference +This section goes over the different features of queries and how they can be expressed by the query descriptor API, query builder API and in the query DSL. + +### Components +> *Supported by: filters, cached queries, rules* + +A component is any single id that can be added to an entity. This includes tags and regular entities, which are ids that are not associated with a datatype. + +To match a query, an entity must have all the requested components. An example: + +```cpp +flecs::entity e1 = world.entity() + .add(); + +flecs::entity e2 = world.entity() + .add() + .add(); + +flecs::entity e3 = world.entity() + .add() + .add() + .add(); +``` + +Only entities `e2` and `e3` match the query `Position, Velocity`. + +The following sections describe how to create queries for components in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +To query for a component in C, the `id` field of a term can be set: + +```c +ECS_COMPONENT(world, Position); +ECS_COMPONENT(world, Velocity); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { .id = ecs_id(Position) }, + { .id = ecs_id(Velocity) } + } +}); +``` + +The `id` field is guaranteed to be the first member of a term, which allows the previous code to be rewritten in this shorter form: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Velocity) } + } +}); +``` + +The `ecs_id` macro converts the component typename into the variable name that holds the component identifier. This macro is required for components created with `ECS_COMPONENT`, but not when querying for regular tags/entities: + +```c +ECS_TAG(world, Npc); +ecs_entity_t Platoon_01 = ecs_new_id(world); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { Npc }, + { Platoon_01 } + } +}); +``` + +Components can also be queried for by name by setting the `.first.name` member in a term: + +```c +ECS_COMPONENT(world, Position); +ECS_COMPONENT(world, Velocity); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { .first.name = "Position" }, + { .first.name = "Velocity" } + } +}); +``` + +#### Query Builder (C++) +An easy way to query for components in C++ is to pass them as template arguments to the query factory function: + +```cpp +flecs::filter f = + world.filter(); +``` + +This changes the returned query type, which determines the type of the function used to iterate the query: + +```cpp +f.each([](Position& p, const Velocity& v) { }); +``` + +The builder API makes it possible to add components to a query without modifying the query type: + +```cpp +flecs::filter f = + world.filter_builder() + .term() + .build(); +``` + +When template arguments are mixed with the builder API, the components added by the `term` function will be placed after the components provided as template arguments. + +The builder API makes it possible to query for regular entity ids created at runtime: + +```cpp +flecs::entity Npc = world.entity(); +flecs::entity Platoon_01 = world.entity(); + +flecs::filter<> f = world.filter_builder() + .term(Npc) + .term(Platoon_01) + .build(); +``` + +Components can also be queried for by name. To query for component types by name, they have to be used or registered first. + +```cpp +// Register component type so we can look it up by name +world.component(); + +// Create entity with name so we can look it up +flecs::entity Npc = world.entity("Npc"); + +flecs::filter<> f = world.filter_builder() + .term("Position") + .term("Npc") + .build(); +``` + +#### Query DSL +To query for a components in the query DSL they can be specified in a comma separated list of identifiers. The rules for resolving identifiers are the same as the `ecs_lookup_fullpath` / `world.lookup` functions. An example: + +``` +Position, Velocity +``` + +Any named entity can be specified this way. Consider: + +```cpp +struct Npc { }; + +// Register component type so we can look it up by name +world.component(); + +// Create entity with name so we can look it up +flecs::entity Platoon_01 = world.entity("Platoon_01"); + +flecs::entity e = world.entity() + .add() + .add(Platoon_01); +``` + +The entity `e` from this example will be matched by this query: +```cpp +Npc, Platoon_01 +``` + +When an identifier in the query DSL consists purely out of numeric characters it is converted to an entity id. If in the previous example `Npc` has id `100` and `Platoon_01` has id `101`, the following query string would be equivalent: + +``` +100, 101 +``` + +The `,` symbol in the query DSL is referred to as the `and` operator, as an entity must have all comma-separated components in order to match the query. + +### Wildcards +> *Supported by: filters, cached queries, rules* + +Wildcards allow a single query term to match with more than one (component) ids. Flecs supports two kinds of wildcards: + +| Name | DSL Symbol | C identifier | C++ identifier | Description | +|----------|-----|---------------|----------------|-------------| +| Wildcard | `*` | `EcsWildcard` | `flecs::Wildcard` | Match all | +| Any | `_` | `EcsAny` | `flecs::Any` | Match at most one | + +The `Wildcard` wildcard returns an individual result for anything that it matches. The query in the following example will return twice for entity `e`, once for component `Position` and once for component `Velocity`: + +```c +ECS_COMPONENT(world, Position); +ECS_COMPONENT(world, Velocity); + +ecs_entity_t e = ecs_new_id(world); +ecs_add(world, e, Position); +ecs_add(world, e, Velocity); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { EcsWildcard } + } +}); +``` +```cpp +flecs::entity e = world.entity() + .add() + .add(); + +flecs::filter<> f = world.filter_builder() + .term(flecs::Wildcard) + .build(); +``` + +The `Any` wildcard returns a single result for the first component that it matches. The query in the following example will return once for entity `e`: + +```c +ECS_COMPONENT(world, Position); +ECS_COMPONENT(world, Velocity); + +ecs_entity_t e = ecs_new_id(world); +ecs_add(world, e, Position); +ecs_add(world, e, Velocity); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { EcsAny } + } +}); +``` +```cpp +flecs::entity e = world.entity() + .add() + .add(); + +flecs::filter<> f = world.filter_builder() + .term(flecs::Any) + .build(); +``` + +When using the `Any` wildcard it is undefined which component will be matched, as this can be influenced by other parts of the query. It is guaranteed that iterating the same query twice on the same dataset will produce the same result. + +Wildcards are particularly useful when used in combination with pairs (next section). + +### Pairs +> *Supported by: filters, cached queries, rules* + +A pair is an id that encodes two elements. Pairs, like components, can be added to entities and are the foundation for [Relationships](Relationships.md). + +The elements of a pair are allowed to be wildcards. When a query pair contains the `Wildcard` wildcard, a query returns a result for each matching pair on an entity. When a query pair returns an `Any` wildcard, the query returns at most a single matching pair on an entity. + +The following sections describe how to create queries for pairs in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +To query for a pair in C, the `id` field of a term can be set to a pair using the `ecs_pair` macro: + +```c +ecs_entity_t Likes = ecs_new_id(world); +ecs_entity_t Bob = ecs_new_id(world); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { .id = ecs_pair(Likes, Bob) } + } +}); +``` + +The `id` field is guaranteed to be the first member of a term, which allows the previous code to be rewritten in this shorter form: + +```c +ecs_entity_t Likes = ecs_new_id(world); +ecs_entity_t Bob = ecs_new_id(world); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_pair(Likes, Bob) } + } +}); +``` + +When an element of the pair is a component type, use the `ecs_id` macro to obtain the identifier to the id variable of the component type: + +```c +ECS_COMPONENT(world, Eats); +ecs_entity_t Apples = ecs_new_id(world); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_pair(ecs_id(Eats), Apples) } + } +}); +``` + +The `ecs_isa`, `ecs_childof` and `ecs_dependson` convenience macros can be used to create pairs for builtin relationships. The two queries in the next example are equivalent: + +```c +ecs_filter_t *f_1 = ecs_filter(world, { + .terms = { + { ecs_pair(EcsChildOf, parent) } + } +}); + +ecs_filter_t *f_2 = ecs_filter(world, { + .terms = { + { ecs_childof(parent) } + } +}); +``` + +Pair queries can be created by setting their individual elements in the `first.id` and `second.id` members of a term: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { .first.id = Eats, .second.id = Apples } + } +}); +``` + +Alternatively, one or both elements of a pair can be resolved by name. The two queries in the next example are equivalent: + +```c +// ECS_TAG creates named entities +ECS_TAG(world, Eats); +ECS_TAG(world, Apples); + +ecs_filter_t *f_1 = ecs_filter(world, { + .terms = { + { .first.name = "Eats", .second.id = Apples } + } +}); + +ecs_filter_t *f_2 = ecs_filter(world, { + .terms = { + { .first.name = "Eats", .second.name = "Apples" } + } +}); +``` + +When a query pair contains a wildcard, the `ecs_field_id` function can be used to determine the id of the pair element that matched the query: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_pair(Likes, EcsWildcard) } + } +}); + +ecs_iter_t it = ecs_filter_iter(world, f); +while (ecs_filter_next(&it)) { + ecs_id_t id = ecs_field_id(&it, 1); + ecs_entity_t second = ecs_pair_second(world, id); + + for (int i = 0; i < it.count; i ++) { + printf("entity %s likes %s\n", + ecs_get_name(world, it.entities[i]), + ecs_get_name(world, second)); + } +} +``` + +#### Query Builder (C++) +When both parts of a pair are types, a `flecs::pair` template can be used. Pair templates can be made part of the query type, which makes them part of the argument list of the iterator functions. An example: + +```cpp +struct Eats { float value; }; +struct Apples { }; +// Alias to save typing +using EatsApples = flecs::pair; + +flecs::filter f = + world.filter(); + +// Do not use reference argument for pair +f.each([](EatsApples v) { + // Use -> operator to access value of pair + v->value ++; +}); +``` + +When using the `iter` function to iterate a query with a pair template, the argument type assumes the type of the pair. This is required as the component array being passed directly to the `iter` function. An example: + +```cpp +flecs::filter f = + world.filter(); + +f.iter([](flecs::iter& it, Eats *v) { + for (auto i : it) { + v[i].value ++; + } +}) +``` + +Pairs can also be added to queries using the builder API. This allows for the pair to be composed out of both types and regular entities. The three queries in the following example are equivalent: + +```cpp +struct Eats { float value; }; +struct Apples { }; + +flecs::entity eats = world.component(); +flecs::entity apples = world.component(); + +flecs::filter<> f_1 = world.filter_builder() + .term() + .build(); + +flecs::filter<> f_2 = world.filter_builder() + .term(apples) + .build(); + +flecs::filter<> f_3 = world.filter_builder() + .term(eats, apples) + .build(); +``` + +Individual elements of a pair can be specified with the `first` and `second` methods. The methods apply to the last added term. An example: + +```cpp +flecs::filter<> f = world.filter_builder() + .term().first().second(apples) + .build(); +``` + +Individual elements of a pair can be resolved by name by using the `first` and `second` methods: + +```cpp +flecs::filter<> f = world.filter_builder() + .term().first("Eats").second("Apples") + .build(); +``` + +When a query pair contains a wildcard, the `flecs::iter::pair` method can be used to determine the id of the pair element that matched the query: + +```cpp +flecs::filter<> f = world.filter_builder() + .term(flecs::Wildcard) + .build(); + +f.each([](flecs::iter& it, size_t index) { + flecs::entity second = it.pair(1).second(); + flecs::entity e = it.entity(index); + + std::cout << "entity " << e.name() + << " likes " second.name() + << std::endl; +}); +``` + +#### Query DSL +To query for a pair in the query DSL, the elements of a pair are a comma separated list surrounded by parentheses. An example: + +``` +(Likes, Apples) +``` + +A query may contain multiple pairs: + +``` +(Likes, Apples), (Likes, Pairs) +``` + +Queries for pairs that contain wildcards should use the symbols for either the `Wildcard` or `Any` wildcards: + +``` +(Likes, *) +``` +``` +(Likes, _) +``` + +A pair may contain two wildcards: + +``` +(*, *) +``` + +### Access modifiers +> *Supported by: filters, cached queries, rules* + +Access modifiers specify which components of a query can be read and/or written. The different access modifiers are: + +| Name | DSL identifier | C identifier | C++ identifier | Description | +|-------|----------------|--------------|----------------|-------------| +| In | `in` | `EcsIn` | `flecs::In` | Component is readonly | +| Out | `out` | `EcsOut` | `flecs::Out` | Component is write only | +| InOut | `inout` | `EcsInOut` | `flecs::InOut` | Component can be read/written | +| None | `none` | `EcsInOutNone` | `flecs::InOutNone` | Component is neither read nor written | +| Default | n/a | `EcsInOutDefault` | `flecs::InOutDefault` | Default modifier is selected for term | + +Access modifiers can be used by API functions to ensure a component cannot be written, for example by requiring a component to be accessed with a `const` modifier. APIs may also infer access modifiers where possible, for example by using the `In` modifier for a query term with a type that has a `const` modifier. + +When using pipelines, the scheduler may use access modifiers to determine where sync points are inserted. This typically happens when a system access modifier indicates a system writing to a component not matched by the query (for example, by using `set`), and is followed by a system that reads that component. + +Access modifiers may also be used by serializers that serialize the output of an iterator (for example: `ecs_iter_to_json`). A serializer may for example decide to not serialize component values that have the `Out` or `None` modifiers. + +When no access modifier is specified, `Default` is assumed. This selects `InOut` for components owned by the matched entity, and `In` for components that are from entities other than the one matched by the query. + +When a query term can either match a component from the matched entity or another entity (for example: when a component is inherited from a prefab) the `Default` access modifier only provides write access for the results where the component is owned by the matched entity. This prevents accidentally writing to a shared component. This behavior can be overridden by explicitly specifying an access modifier. + +When a query term matches a tag (a component not associated with data) with a `Default` modifier, the `None` modifier is selected. + +The following sections show how to use access modifiers in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptors (C) +Access modifiers can be set using the `inout` member: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Velocity), .inout = EcsIn } + } +}); +``` + +#### Query Builder (C++) +Access modifiers can be set using the `inout` method: + +```cpp +flecs::filter<> f = world.filter_builder() + .term() + .term().inout(flecs::In) + .build(); +``` + +When the `const` modifier is added to a type, the `flecs::In` modifier is automatically set: + +```c +// Velocity term will be added with flecs::In modifier +flecs::filter f = + world.filter(); +``` + +This also applies to types added with `term`: + +```c +flecs::filter<> f = world.filter_builder() + .term() + .term() // uses flecs::In modifier + .build(); +``` + +When a component is added by the `term` method and retrieved from a `flecs::iter` object during iteration, it must meet the constraints of the access modifiers. If the constraints are not met, a runtime assert may be thrown: + +```cpp +flecs::filter<> f = world.filter_builder() + .term() + .term().inout(flecs::In) + .build(); + +f.iter([](flecs::iter& it) { + auto p = it.field(1); // OK + auto p = it.field(1); // OK + auto v = it.field(2); // OK + auto v = it.field(2); // Throws assert +}); +``` + +The builder API has `in()`, `inout()`, `out()` and `inout_none()` convenience methods: + +```cpp +flecs::filter<> f = world.filter_builder() + .term().inout() + .term().in() + .build(); +``` + +#### Query DSL +Access modifiers in the query DSL can be specified inside of angular brackets before the component identifier: + +``` +Position, [in] Velocity +``` + +### Operator Overview +> *Supported by: filters, cached queries, rules* + +The following operators are supported by queries: + +| Name | DSL operator | C identifier | C++ identifier | Description | +|-----------|----------------|---------------|-------------------|-------------| +| And | `,` | `EcsAnd` | `flecs::And` | Match at least once with term | +| Or | `\|\|` | `EcsOr` | `flecs::Or` | Match at least once with one of the OR terms | +| Not | `!` | `EcsNot` | `flecs::Not` | Must not match with term | +| Optional | `?` | `EcsOptional` | `flecs::Optional` | May match with term | +| Equal | `==` | `EcsPredEq` | `flecs::PredEq` | Equals entity/entity name | +| Not equal | `!=` | `EcsPredNeq` | `flecs::PredNeq` | Not equals entity/entity name | +| Match | `~=` | `EcsPredMatch`| `flecs::PredMatch`| Match entity name with substring | +| AndFrom | `AND \|` | `EcsAndFrom` | `flecs::AndFrom` | Match all components from id at least once | +| OrFrom | `OR \|` | `EcsOrFrom` | `flecs::OrFrom` | Match at least one component from id at least once | +| NotFrom | `NOT \|` | `EcsNotFrom` | `flecs::NotFrom` | Don't match any components from id | + +### And Operator +> *Supported by: filters, cached queries, rules* + +The `And` operator is used when no other operators are specified. The following sections show how to use the `And` operator in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +When no operator is specified, `And` is assumed. The following two queries are equivalent: + +```c +ecs_filter_t *f_1 = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Velocity) } + } +}); + +ecs_filter_t *f_2 = ecs_filter(world, { + .terms = { + { ecs_id(Position), .oper = EcsAnd }, + { ecs_id(Velocity), .oper = EcsAnd } + } +}); +``` + +#### Query Builder (C++) +When no operator is specified, `And` is assumed. The following two queries are equivalent: + +```cpp +flecs::filter f_1 = world.filter(); + +flecs::filter<> f_2 = world.filter_builder() + .term() + .term() + .build(); + +flecs::filter<> f_2 = world.filter_builder() + .term().oper(flecs::And) + .term().oper(flecs::And) + .build(); +``` + +The builder API has a `and_` convenience method: + +```cpp +flecs::filter<> f = world.filter_builder() + .term().and_(); // note escaping, 'and' is a C++ keyword + .term().and_(); + .build(); +``` + +#### Query DSL +Query expressions with comma separated lists use the `And` operator: + +``` +Position, Velocity +``` + +### Or Operator +> *Supported by: filters, cached queries, rules* + +The `Or` operator allows for matching a single component from a list. Using the `Or` operator means that a single term can return results of multiple types. When the value of a component is used while iterating the results of an `Or` operator, an application has to make sure that it is working with the expected type. + +When using the `Or` operator, the terms participating in the `Or` expression are made available as a single field. Field indices obtained from an iterator need to account for this. Consider the following query: + +``` +Position, Velocity || Speed, Mass +``` + +This query has 4 terms, while an iterator for the query returns results with 3 fields. This is important to consider when retrieving the field for a term, as its index has to be adjusted. In this example, `Position` has index 1, `Velocity || Speed` has index 2, and `Mass` has index 3. + +The following sections show how to use the `Or` operator in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +To create a query with `Or` terms, set `oper` to `EcsOr`: + +```c +// Position, Velocity || Speed, Mass +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Velocity), .oper = EcsOr }, + { ecs_id(Speed) }, + { ecs_id(Mass) } + } +}); + +ecs_iter_t it = ecs_filter_iter(world, f); +while (ecs_filter_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + Mass *m = ecs_field(&it, Mass, 3); // not 4, because of the Or expression + + ecs_id_t vs_id = ecs_field_id(&it, 2); + if (vs_id == ecs_id(Velocity)) { + // We can only use ecs_field if the field type is the same for all results, + // but we can get the table column directly. + Velocity *v = ecs_table_get_id(world, it.table, ecs_id(Velocity), it.offset); + // iterate as usual + } else if (vs_id == ecs_id(Speed)) { + Speed *s = ecs_table_get_id(world, it.table, ecs_id(Speed), it.offset); + // iterate as usual + } +} +``` + +#### Query Builder (C++) +To create a query with `Or` terms, use the `oper` method with `flecs::Or`: + +```cpp +// Position, Velocity || Speed, Mass +flecs::filter<> f = world.filter_builder() + .term() + .term().oper(flecs::Or) + .term() + .term() + .build(); + +f.iter([&](flecs::iter& it) { + auto p = it.field(1); + auto v = it.field(3); // not 4, because of the Or expression + + flecs::id vs_id = it.id(2); + if (vs_id == world.id()) { + // We can only use ecs_field if the field type is the same for all results, + // but we can use range() to get the table column directly. + auto v = it.range().get(); + // iterate as usual + } else if (vs_id == world.id()) { + auto s = it.range().get(); + // iterate as usual + } +}); +``` + +The builder API has a `or_` convenience method: + +```cpp +flecs::filter<> f = world.filter_builder() + .term() + .term().or_(); // note escaping, 'or' is a C++ keyword + .term() + .term() + .build(); +``` + +#### Query DSL +To create a query with `Or` terms, use the `||` symbol: + +``` +Position, Velocity || Speed, Mass +``` + +### Not Operator +> *Supported by: filters, cached queries, rules* + +The `Not` operator makes it possible to exclude entities with a specified component. Fields for terms that uses the `Not` operator will never provide data. + +A note on performance: `Not` terms are efficient to evaluate when combined with other terms, but queries that only have `Not` terms (or [`Optional`](#optional-operator)) can be expensive. This is because the storage only maintains indices for tables that _have_ a component, not for tables that do _not have_ a component. + +The following sections show how to use the `Not` operator in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +To create a query with `Not` terms, set `oper` to `EcsNot`: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Velocity), .oper = EcsNot } + } +}); +``` + +#### Query Builder (C++) +To create a query with `Not` terms, use the `oper` method with `flecs::Not`: + +```cpp +flecs::filter<> f = world.filter_builder() + .term() + .term().oper(flecs::Not) + .build(); +``` + +The builder API has a `not_` convenience method: + +```cpp +flecs::filter<> f = world.filter_builder() + .term() + .term().not_(); // note escaping, 'not' is a C++ keyword + .build(); +``` + +#### Query DSL +To create a query with `Not` terms, use the `!` symbol: + +``` +Position, !Velocity +``` + +### Optional Operator +> *Supported by: filters, cached queries, rules* + +The `Optional` operator optionally matches with a component. While this operator does not affect the entities that are matched by a query, it can provide more efficient access to a component when compared to conditionally getting the component in user code. Before accessing the value provided by an optional term, code must first check if the term was set. + +A note on performance: just like the `Not` operator `Optional` terms are efficient to evaluate when combined with other terms, but queries that only have `Optional` terms can be expensive. Because the `Optional` operator does not restrict query results, a query that only has `Optional` terms will match all entities. + +When an optional operator is used in a rule, and a variable written by the optional term is read by a subsequent term, the subsequent term becomes a _dependent term_. This means that if the optional term does not match, the dependent term will be ignored. For example: + +``` +SpaceShip, ?(DockedTo, $planet), Planet($planet) +``` + +Because the second term is optional, the variable `$planet` may or may not be set depending on whether the term was matched. As a result the third term becomes dependent: if `$planet` was not set, the term will be ignored. + +The following sections show how to use the `Optional` operator in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +To create a query with `Optional` terms, set `oper` to `EcsOptional`: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Velocity), .oper = EcsOptional } + } +}); + +ecs_iter_t it = ecs_filter_iter(world, f); +while (ecs_filter_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + if (ecs_field_is_set(&it, 2)) { + Velocity *v = ecs_field(&it, Velocity, 2); + // iterate as usual + } else { + // iterate as usual + } +} +``` + +#### Query Builder (C++) +To create a query with `Optional` terms, call the `oper` method with `flecs::Optional`: + +```cpp +flecs::filter<> f = world.filter_builder() + .term() + .term().oper(flecs::Optional) + .build(); + +f.iter([&](flecs::iter& it) { + auto p = it.field(1); + + if (it.is_set(2)) { + auto v = it.field(2); + // iterate as usual + } else if (vs_id == world.id()) { + // iterate as usual + } +}); +``` + +The builder API has an `optional` convenience method: + +```cpp +flecs::filter<> f = world.filter_builder() + .term() + .term().optional(); + .build(); +``` + +#### Query DSL +To create a query with `Optional` terms, use the `?` symbol: + +``` +Position, ?Velocity +``` + +### Equality operators +> *Supported by: rules* + +Equality operators (`==`, `!=`, `~=`) allow a query to ensure that a variable equals a specific entity, that the entity it stores has a specific name, or that the entity name partially matches a substring. + +The left hand side of an equality operator must be a variable. The right hand side of an operator can be an entity identifier or a string for the `==` and `!=` operators, and must be a string in case of the `~=` operator. For example: + +Test if variable `$this` equals `Foo` (`Foo` must exist at query creation time): + +```js +$this == Foo +``` + +Test if variable `$this` equals entity with name `Foo` (`Foo` does not need to exist at query creation time): + +```js +$this == "Foo" +``` + +Test if variable `$this` stores an entity with a name that has substring `Fo`: + +```js +$this ~= "Fo" +``` + +When the equals operator (`==`) is used with a variable that has not yet been initialized, the right-hand side of the operator will be assigned to the variable. + +Other than regular operators, equality operators are set as `first`, with the left hand being `src` and the right hand being `second`. Equality operators can be combined with `And`, `Not` and `Or` terms. + +Terms with equality operators return no data. + +#### Query Descriptor (C) +```c +ecs_rule_t *r = ecs_rule(world, { + .terms = { + // $this == Foo + { .first.id = EcsPredEq, .second.id = Foo }, + // $this != Bar + { .first.id = EcsPredEq, .second.id = Bar, .oper = EcsNot }, + // $this == "Foo" + { .first.id = EcsPredEq, .second = { .name = "Foo", .flags = EcsIsName }}, + // $this ~= "Fo" + { .first.id = EcsPredMatch, .second = { .name = "Fo", .flags = EcsIsName }}, + } +}); +``` + +#### Query Builder (C++) +```cpp +world.rule_builder() + // $this == Foo + .with(flecs::PredEq, Foo) + // $this != Foo + .without(flecs::PredEq, Bar) + // $this == "Foo" + .with(flecs::PredEq).second("Foo").flags(EcsIsName) + // $this ~= "Fo" + .with(flecs::PredMatch).second("Fo").flags(EcsIsName) + .build(); +``` + +#### Query DSL +```js +$this == Foo +$this != Foo +$this == "Foo" +$this != "Fo" +``` + +### AndFrom, OrFrom, NotFrom Operators +> *Supported by: filters, cached queries* + +The `AndFrom`, `OrFrom` and `NotFrom` operators make it possible to match a list of components that is defined outside of the query. Instead of matching the id provided in the term, the operators match the list of components _of_ the provided id as if they were provided as a list of terms with `And`, `Or` or `Not` operators. For example, if entity `e` has components `Position, Velocity` and is combined in a query with the `AndFrom` operator, entities matching the query must have both `Position` and `Velocity`. + +The `AndFrom`, `OrFrom` and `NotFrom` operators are especially useful when combined with prefab entities, which by default are not matched with queries themselves. Components that have the `DontInherit` property are ignored while matching the operators, which means that using a prefab in combination with `AndFrom`, `OrFrom` and `NotFrom` will not cause components like `Prefab` or `ChildOf` to be considered. + +Component lists can be organized recursively by adding an id to an entity with the `AND` and `OR` id flags. + +Fields for terms that use the `AndFrom`, `OrFrom` or `NotFrom` operators never provide data. Access modifiers for these operators default to `InOutNone`. When a the `AndFrom`, `OrFrom` or `NotFrom` operator is combined with an access modifier other than `InOutDefault` or `InOutNone` query creation will fail. + +The following sections show how to use the operators in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +To use the `AndFrom`, `OrFrom` and `NotFrom` operators, set `oper` to `EcsAndFrom`, `EcsOrFrom` or `EcsNotFrom` + +```c +ecs_entity_t type_list = ecs_new_w_id(world, EcsPrefab); +ecs_add(world, type_list, Position); +ecs_add(world, type_list, Velocity); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { type_list, .oper = EcsAndFrom }, // match Position, Velocity + { type_list, .oper = EcsOrFrom }, // match Position || Velocity + { type_list, .oper = EcsNotFrom } // match !Position, !Velocity + } +}); +``` + +#### Query Builder (C++) +To use the `AndFrom`, `OrFrom` and `NotFrom` operators, call the `oper` method with `flecs::AndFrom`, `flecs::OrFrom` or `flecs::NotFrom`. + +```cpp +flecs::entity type_list = world.prefab() + .add() + .add(); + +flecs::filter<> f = world.filter_builder() + .term(type_list).oper(flecs::AndFrom) // match Position, Velocity + .term(type_list).oper(flecs::OrFrom) // match Position || Velocity + .term(type_list).oper(flecs::NotFrom) // match !Position, !Velocity + .build(); +``` + +The builder API has the `and_from`, `or_from` and `not_from` convenience methods: + +```cpp +flecs::filter<> f = world.filter_builder() + .term(type_list).and_from() + .term(type_list).or_from() + .term(type_list).not_from() + .build(); +``` + +#### Query DSL +To create a query with the `AndFrom`, `OrFrom` and `NotFrom` operators in the C API, use `AND`, `OR` and `NOT` in combination with the bitwise OR operator (`|`): + +``` +AND | type_list, OR | type_list, NOT | type_list +``` + +### Query scopes +> *Supported by: rules* + +Query scopes are a mechanism that allows for treating the output of a number of terms as a single condition. For example, the following query has two terms with an `Or` operator that are negated by a `Not` operator: + +```c +// Match entities with Position that don't have Velocity or Speed +Position, !{ Velocity || Speed } +``` + +A query scope can contain any number of terms and operators. The following query has a scope with mixed operators: + +``` +Position, !{ Mass, Velocity || Speed, !Rotation } +``` + +Query scopes allow for the creation of complex queries when combined with variables and relationships. The following query finds all entities that have no children with `Position`: + +``` +Position($this), !{ ChildOf($child, $this), Position($child) } +``` + +Note how this is different from this query, which finds all children that don't have `Position`: + +``` +Position($this), ChildOf($child, $this), !Position($child) +``` + +Whereas the first query only returns parents without children with `Position`, the second query returns parents that have _at least one child_ that doesn't have `Position`. + +When a scope is evaluated, the entire result set of the scope is treated as a single term. This has as side effect that any variables first declared inside the scope are not available outside of the scope. For example, in the following query the value for variable `$child` is undefined, as it is first used inside a scope: + +``` +Position($this), !{ ChildOf($child, $this), Position($child) } +``` + +Scopes currently have the following limitations: + - scopes can only be combined with Not operators (e.g. `!{ ... }`). Future versions of flecs will add support for combining scopes with Or operators (e.g. `{ ... } || { ... }`). + - scopes cannot appear as first terms in a query. + +The following examples show how to use scopes in the different language bindings: + +#### Query Descriptor (C) +```c +ecs_rule_t *r = ecs_rule(world, { + .terms = { + // Position, !{ Velocity || Speed } + { .id = ecs_id(Position) }, + { .id = EcsScopeOpen, .src.flags = EcsIsEntity, .oper = EcsNot }, + { .id = ecs_id(Velocity), .oper = EcsOr }, + { .id = ecs_id(Speed) }, + { .id = EcsScopeClose, .src.flags = EcsIsEntity } + } +}); +``` + +#### Query Builder (C++) +```cpp +world.rule_builder() + // Position, !{ Velocity || Speed } + .with() + .scope_open().not_() + .with().or_() + .with() + .scope_close() + .build(); +``` + +#### Query DSL +```js +Position, !{ Velocity || Speed } +``` + +### Source +> *Supported by: filters, cached queries, rules* + +Source is a property of a term that specifies the entity on which the term should be matched. Queries support two kinds of sources: static and variable. A static source is known when the query is created (for example: match `SimTime` on entity `Game`), whereas a variable source is resolved while the query is evaluated. When no explicit source is specified, a default variable source called `$This` is used (see [Variables](#variables)). + +When a query only has terms with fixed sources, iterating the query will return a result at least once when it matches, and at most once if the query terms do not match wildcards. If a query has one or more terms with a fixed source that do not match the entity, the query will return no results. A source does not need to match the query when the query is created. + +When a term has a fixed source and the [access modifiers](#access-modifiers) are not explicitly set, the access modifier defaults to `In`, instead of `InOut`. The rationale behind this is that it encourages code to only makes local changes (changes to components owned by the matched entity) which is easier to maintain and multithread. This default can be overridden by explicitly setting access modifiers. + +The following sections show how to use variable and fixed sources with the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +To specify a fixed source, set the `src.id` member to the entity to match. The following example shows how to set a source, and how to access the value provided by a term with a fixed source: + +```c +ecs_entity_t Game = ecs_new_id(world); +ecs_add(world, Game, SimTime); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, // normal term, uses $This source + { ecs_id(Velocity) }, // normal term, also uses $This source + { ecs_id(SimTime), .src.id = Game } // fixed source, match SimTime on Game + } +}); + +ecs_iter_t it = ecs_filter_iter(world, f); +while (ecs_filter_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + SimTime *st = ecs_field(&it, SimTime, 3); + + for (int i = 0; i < it.count; i ++) { + p[i].x += v[i].x * st[i].value; + p[i].y += v[i].y * st[i].value; + } +} +``` + +Note how in this example all components can be accessed as arrays. When a query has mixed fields (fields with both arrays and single values), behavior defaults to entity-based iteration where entities are returned one at a time. As a result, `i` in the previous example will never be larger than `0`, which is why this code works even though there is only a single instance of the `SimTime` component. + +Returning entities one at a time can negatively affect performance, especially for large tables. To learn more about why this behavior exists and how to ensure that mixed results use table-based iteration, see [Instancing](#instancing). + +A source may also be specified by name by setting the `src.name` member: + +```c +ecs_entity_t Game = ecs_entity(world, { .name = "Game" }); +ecs_add(world, Game, SimTime); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(SimTime), .src.name = "Game" } + } +}); +``` + +This examples shows how to access the entities matched by the default `$This` source and a fixed source: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, // normal term, uses $This source + { ecs_id(SimTime), .src.id = Game } // fixed source, match SimTime on Game + } +}); + +while (ecs_filter_next(&it)) { + ecs_entity_t src_1 = ecs_field_src(&it, 1); // Returns 0, meaning entity is stored in it.entities + ecs_entity_t src_2 = ecs_field_src(&it, 2); // Returns Game + + for (int i = 0; i < it.count; i ++) { + printf("$This = %s, src_2 = %s\n", + ecs_get_name(world, it.entities[i]), + ecs_get_name(world, src_2)); + } +} +``` + +The `entities` and `count` member are solely populated by the number of entities matched by the default `$This` source. If a query only contains fixed sources, `count` will be set to 0. This is important to keep in mind, as the inner for loop from the last example would never be iterated for a query that only has fixed sources. + +#### Query Builder (C++) +To specify a fixed source, call the `src` method to the entity to match. The following example shows how to set a source, and how to access the value provided by a term with a fixed source: + +```cpp +flecs::entity Game = world.entity() + .add(); + +flecs::filter<> f = world.filter_builder() + .term() // normal term, uses $This source + .term() // normal term, also uses $This source + .term().src(Game) // fixed source, match SimTime on Game + .build(); + +f.iter([](flecs::iter& it) { + auto p = it.field(1); + auto v = it.field(2); + auto st = it.field(3); + + for (auto i : it) { + p[i].x += v[i].x * st[i].value; + p[i].y += v[i].y * st[i].value; + } +}); +``` + +Note how in this example all components can be accessed as arrays. When a query has mixed fields (fields with both arrays and single values), behavior defaults to entity-based iteration where entities are returned one at a time. As a result, `i` in the previous example will never be larger than `0`, which is why this code works even though there is only a single instance of the `SimTime` component. + +Returning entities one at a time can negatively affect performance, especially for large tables. To learn more about why this behavior exists and how to ensure that mixed results use table-based iteration, see [Instancing](#instancing). + +The next example shows how queries with mixed `$This` and fixed sources can be iterated with `each`. The `each` function does not have the performance drawback of the last `iter` example, as it uses [instancing](#instancing) by default. + +```cpp +flecs::filter f = + world.filter_builder() + .arg(3).src(Game) // set fixed source for 3rd template argument (SimTime) + .build(); + +// Because all components are now part of the filter type, we can use each +f.each([](flecs::entity e, Position& p, Velocity& v, SimTime& st) { + p.x += v.x * st.value; + p.y += v.y * st.value; +}); +``` + +When a query has no terms for the `$This` source, it must be iterated with the `iter` function or with a variant of `each` that does not have a signature with `flecs::entity` as first argument: + +```cpp +flecs::filter f = + world.filter_builder() + .arg(1).src(Cfg) + .arg(2).src(Game) + .build(); + +// Ok (note that it.count() will be 0) +f.iter([](flecs::iter& it, SimConfig *sc, SimTime *st) { + st->value += sc->sim_speed; +}); + +// Ok +f.each([](SimConfig& sc, SimTime& st) { + st.value += sc.sim_speed; +}); + +// Ok +f.each([](flecs::iter& it, size_t index, SimConfig& sc, SimTime& st) { + st.value += sc.sim_speed; +}); + +// Not ok: there is no entity to pass to first argument +f.each([](flecs::entity e, SimConfig& sc, SimTime& st) { + st.value += sc.sim_speed; +}); +``` + +A source may also be specified by name: + +```cpp +flecs::filter f = + world.filter_builder() + .arg(1).src("Cfg") + .arg(2).src("Game") + .build(); +``` + +#### Query DSL +To specify a source in the DSL, use parenthesis after the component identifier. The following example uses the default `$This` source for `Position` and `Velocity`, and `Game` as source for `SimTime`. + +``` +Position, Velocity, SimTime(Game) +``` + +In the previous example the source for `Position` and `Velocity` is implicit. The following example shows the same query with explicit sources for all terms: + +``` +Position($This), Velocity($This), SimTime(Game) +``` + +To specify a source for a pair, the second element of the pair is placed inside the parenthesis after the source. The following query uses the default `$This` source for the `(Color, Diffuse)` pair, and `Game` as source for the `(Color, Sky)` pair. + +``` +(Color, Diffuse), Color(Game, Sky) +``` + +In the previous example the source for `(Color, Diffuse)` is implicit. The following example shows the same query with explicit sources for all terms: + +``` +Color($This, Diffuse), Color(Game, Sky) +``` + +### Singletons +> *Supported by: filters, cached queries, rules* + +Singletons are components that are added to themselves, which can be matched by providing the component id as [source](#source). + +The following sections show how to use singletons in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +A singleton query is created by specifying the same id as component and source: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { Player }, + { ecs_id(Position) }, + { ecs_id(Input), .src = ecs_id(Input) } // match Input on itself + } +}); +``` + +The singleton component data is accessed in the same way a component from a static [source](#source) is accessed. + +#### Query Builder (C++) +A singleton query can be created by specifying the same id as component and source: + +```cpp +flecs::filter f = world.filter_builder() + .term().src() // match Input on itself + .build(); +``` + +The builder API provides a `singleton` convenience function: + +```cpp +flecs::filter f = world.filter_builder() + .term().singleton() // match Input on itself + .build(); +``` + +The singleton component data is accessed in the same way a component from a static [source](#source) is accessed. + +#### Query DSL +A singleton query can be created by specifying the same id as component and source: + +``` +Player, Position, Input(Input) +``` + +For convenience the `$` character may be used as source, which resolves to the component id: + +``` +Player, Position, Input($) +``` + +### Relationship Traversal +> *Supported by: filters, cached queries, rules(!)* + +Relationship traversal enables a query to search for a component by traversing a relationship. One of the most common examples of where this is useful is a Transform system, which matches `Position` on an entity and the entity's parent. To find the `Position` component on a parent entity, a query traverses the `ChildOf` relationship upwards: + +![filter diagram](img/relationship_traversal.png) + +The arrows in this diagram indicate the direction in which the query is traversing the `ChildOf` relationship to find the component. A query will continue traversing until it has found an entity with the component, or until a root (an entity without the relationship) has been found. The traversal is depth-first. If an entity has multiple instances of a relationship a query will first traverse the first instance until its root entity before continuing with the second instance. + +Using the relationship traversal feature will in most cases provide better performance than doing the traversal in user code. This is especially true for cached queries, where the results of traversal are cached. Relationship traversal can in some edge cases cause performance degradation, especially in applications with large numbers of cached queries and deep hierarchies. See the [Performance](#performance) section for more details. + +Any relationship used for traversal must have the [Traversable](Relationships.md#traversable-property) property. Attempting to create a query that traverses a relationship that does not have the `Traversable` property will cause query creation to fail. This safeguards against creating queries that could end up in an infinite traversal loop when a cyclic relationship is encountered. + +Components that have the [DontInherit](Relationships.md#dontinherit-property) property cannot be matched through traversal. Examples of builtin components that have the `DontInherit` property are `Prefab` (instances of prefabs should not be considered prefabs) and `ChildOf` (a child of a parent is not a child of the parent's parent). + +Relationship traversal works for both variable and fixed [sources](#source). + +#### Traversal Flags +Traversal behavior can be customized with the following bitflags, in addition to the relationship being traversed: + +| Name | DSL identifier | C identifier | C++ identifier | Description | +|----------|----------------|---------------|-------------------|-------------| +| Self | `self` | `EcsSelf` | `flecs::Self` | Match self | +| Up | `up` | `EcsUp` | `flecs::Up` | Match by traversing upwards | +| Down | `down` | `EcsDown` | `flecs::Down` | Match by traversing downwards (derived, cannot be set) | +| Parent | `parent` | `EcsParent` | `flecs::Parent` | Short for up(ChildOf) | +| Cascade | `cascade` | `EcsCascade` | `flecs::Cascade` | Same as Up, but iterate in breadth-first order | +| Desc | `desc` | `EcsDesc` | `flecs::Desc` | Combine with Cascade to iterate hierarchy bottom to top | + +If just `Self` is set a query will only match components on the matched entity (no traversal). If just `Up` is set, a query will only match components that can be reached by following the relationship and ignore components from the matched entity. If both `Self` and `Up` are set, the query will first look on the matched entity, and if it does not have the component the query will continue searching by traverse the relationship. + +Query terms default to `Self|Up` for the `IsA` relationship. This means that components inherited from prefabs will be matched automatically by queries, unless specified otherwise. When a relationship that is not `IsA` is traversed, the entities visited while traversing will still be tested for inherited components. This means that an entity with a parent that inherits the `Mass` component from a prefab will match a query that traverses the `ChildOf` relationship to match the `Mass` component. A code example: + +```cpp +flecs::entity base = world.entity() + .add(); + +flecs::entity parent = world.entity() + .add(flecs::IsA, base); // inherits Mass + +flecs::entity child = world.entity() + .add(flecs::ChildOf, parent); + +// This filter matches 'child', because it has a parent that inherits Mass +flecs::filter<> f = world.filter_builder() + .term().up(flecs::ChildOf) + .build(); +``` + +When a component is matched through traversal and its [access modifier](#access-modifiers) is not explicitly set, it defaults to `flecs::In`. This behavior is consistent with terms that have a fixed [source](#source). + +#### Iteration +When a component is matched through traversal, the behavior is the same as though the component was matched through a fixed [source](#source): iteration will switch from table-based to entity-based. This happens on a per-result basis: if all terms are matched on the matched entity the entire table will be returned by the iterator. If one of the terms was matched through traversal, entities are returned one by one. + +While returning entities one by one is measurably slower than iterating entire tables, this default behavior enables matching inherited components by default without requiring the user code to be explicitly aware of the difference between a regular component and an inherited component. An advantage of this approach is that applications that use inherited components can interoperate with third party systems that do not explicitly handle them. + +To ensure fast table-based iteration an application can enable [instancing](#instancing). Instanced iteration is as fast as, and often faster than regular iteration. Using inherited components that are shared across many entities can improve cache efficiency as less data needs to be loaded from main RAM, and values are more likely to already be stored in the cache. + +> Note: the C++ `each` API always uses [instancing](#instancing), which guarantees fast table-based iteration. + +#### Limitations +This list is an overview of current relationship traversal limitations: + +- The `Down` flag is currently reserved for internal purposes and should not be set when creating a query. +- The `Cascade` flag only works for cached queries. +- Rule queries currently only traverse when this is implicitly required (by transitive relationships, inheritance), but ignores flags set at query creation time. +- Rule queries do not yet support all forms of implicit traversal (most notably the one used by prefabs) +- Traversal flags can currently only be specified for the term source. +- Union relationships are not supported for traversal. + +The following sections show how to use traversal in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. In the following example, both `base` and `inst` match the query: + +```c +ecs_entity_t base = ecs_new(world, Position); +ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); // Inherits Position + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) } + } +}); +``` + +Implicit traversal can be disabled by setting the `flags` member to `EcsSelf`. The following example only matches `base`: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position), .src.flags = EcsSelf } + } +}); +``` + +To use a different relationship for traversal, use the `trav` member in combination with the `EcsUp` flag. The following example only matches `child`: + +```c +ecs_entity_t parent = ecs_new(world, Position); +ecs_entity_t child = ecs_new(world, Position); + +ecs_add_pair(world, child, EcsChildOf, parent); + +ecs_filter_t *f = ecs_filter(world, { + .terms = { + // term matches parent & child + { ecs_id(Position) }, + // term just matches child, parent does not have a parent with Position + { ecs_id(Position), .src.flags = EcsUp, src.trav = EcsChildOf } + } +}); +``` + +The `EcsParent` flag can be used which is shorthand for `EcsUp` with `EcsChildOf`. The query in the following example is equivalent to the one in the previous example: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Position), .src.flags = EcsParent } + } +}); +``` + +If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional. The following example matches both `parent` and `child`. The second term is not set for the result that contains `parent`. + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position) }, + { ecs_id(Position), .src.flags = EcsParent, .oper = EcsOptional } + } +}); +``` + +By adding the `EcsCascade` flag, a query will iterate the hierarchy top-down. This is only supported for cached queries: + +```c +ecs_query_t *q = ecs_query(world, { + .filter.terms = { + { ecs_id(Position) }, + { ecs_id(Position), .src.flags = EcsCascade|EcsParent, .oper = EcsOptional } + } +}); +``` + +Relationship traversal can be combined with fixed [source](#source) terms. The following query matches if the `my_widget` entity has a parent with the `Window` component: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Window), .src.flags = EcsParent, .src.id = my_widget } + } +}); +``` + +The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: + +```cpp +ecs_filter_t *f_1 = ecs_filter(world, { + .terms = { + { ecs_id(Position) } + } +}); + +ecs_filter_t *f_2 = ecs_filter(world, { + .terms = {{ + .id = ecs_id(Position), // match Position + .src.flags = EcsSelf | EcsUp // first match self, traverse upwards while not found + .src.trav = EcsIsA, // traverse using the IsA relationship + }} +}); +``` + +#### Query Builder (C++) +By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. In the following example, both `base` and `inst` match the query: + +```cpp +flecs::entity base = world.entity() + .add(); + +flecs::entity inst = world.entity() + .is_a(base); // short for .add(flecs::IsA, base) + +flecs::filter<> f = world.filter(); +``` + +Implicit traversal can be disabled by calling the `self` method for the term. The following example only matches `base`: + +```cpp +flecs::filter<> f = world.filter_builder() + .term().self() + .build(); +``` + +To use a different relationship for traversal, use the `up` method with the relationship as argument. The following example only matches `child`: + +```cpp +flecs::entity parent = world.entity() + .add(); + +flecs::entity child = world.entity() + .child_of(parent); // short for .add(flecs::ChildOf, parent) + +flecs::filter<> f = world.filter_builder() + // term matches parent & child + .term() + // term just matches child, parent does not have a parent with Position + .term().up(flecs::ChildOf) + .build(); +``` + +The `parent` method can be used which is shorthand for `up(flecs::ChildOf)`. The query in the following example is equivalent to the one in the previous example: + +```cpp +flecs::filter<> f = world.filter_builder() + .term() + .term().parent() + .build(); +``` + +If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional. The following example matches both `parent` and `child`. The second term is not set for the result that contains `parent`. + +```cpp +flecs::filter<> f = world.filter_builder() + .term() + .term().parent().optional() + .build(); +``` + +By calling the `cascade` method, a query will iterate the hierarchy top-down. Note that the example could also have called both `parent()` and `cascade()`. The `cascade` feature is only supported for cached queries: + +```cpp +flecs::query<> q = world.query_builder() + .term() + .term().cascade(flecs::ChildOf).optional() + .build(); +``` + +Relationship traversal can be combined with fixed [source](#source) terms. The following query matches if the `my_widget` entity has a parent with the `Window` component: + +```cpp +flecs::filter<> f = world.filter_builder() + .term().src(my_widget).parent() + .build(); +``` + +The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: + +```cpp +flecs::filter<> f = world.filter(); + +flecs::filter<> f = world.filter_builder() + .term() // match Position + .self() // first match self + .up(flecs::IsA) // traverse IsA upwards while not found + .build(); +``` + +#### Query DSL +By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. The following query matches `Position` on entities that either have the component or inherit it: + +``` +Position +``` + +Implicit traversal can be disabled by adding `self` enclosed by parentheses after the component identifier: + +``` +Position(self) +``` + +To use a different relationship for traversal, specify `up` with the relationship as argument: + +``` +Position(up(ChildOf)) +``` + +The `parent` keyword can be used which is shorthand for `up(ChildOf)`. The query in the following example is equivalent to the one in the previous example: + +``` +Position(parent) +``` + +If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional: + +``` +Position, ?Position(parent) +``` + +By adding the `cascade` keyword, a query will iterate the `ChildOf` hierarchy top-down. The `cascade` feature is only supported by cached queries: + +``` +Position, ?Position(parent|cascade) +``` + +Relationship traversal can be combined with fixed [source](#source) terms, by using a colon (`:`) to separate the traversal flags and the source identifier. The following query matches if the `my_widget` entity has a parent with the `Window` component: + +``` +Window(parent:my_widget) +``` + +The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: + +``` +Position +Position(self|up(IsA)) +``` + +### Instancing +> *Supported by: filters, cached queries, rules* + +> **Note**: this section is useful when optimizing code, but is not required knowledge for using queries. + +Instancing is the ability to return results with fields that have different numbers of elements. An example of where this is relevant is a query with terms that have both variable and fixed [sources](#source): + +``` +Position, Velocity, SimTime(Game) +``` + +This query may return a result with a table that has many entities, therefore resulting in many component instances being available for `Position` and `Velocity`, while only having a single instance for the `SimTime` component. + +Another much more common cause of mixed results is if a matched component is inherited from a prefab. Consider this example: + +```cpp +flecs::entity base = world.prefab("base") + .set({10}); + +// Entities that share Mass and own Position +flecs::entity inst_1 = world.entity("inst_1") + .is_a(base) // short for .add(flecs::IsA, base) + .set({10, 20}); + +flecs::entity inst_2 = world.entity("inst_2") + .is_a(base) + .set({30, 40}); + +// Entities that own Mass and Position +flecs::entity ent_1 = world.entity("ent_1") + .set({20}) + .set({40, 50}); + +flecs::entity ent_2 = world.entity("ent_2") + .set({30}) + .set({50, 60}); + +flecs::filter f = world.filter(); +``` + +The filter in this example will match both `inst_1`, `inst_2` because they inherit `Mass`, and `ent_1` and `ent_2` because they own `Mass`. The following example shows an example of code that iterates the filter: + +```cpp +f.iter([](flecs::iter& it, Position *p, Mass *m) { + std::cout << "Result" << std::endl; + for (auto i : it) { + std::cout << " - " << it.entity(i).name() << ": " + << m[i].value + << std::endl; + } +}); +``` + +With iteration being table-based the expectation is that the result looks something like this, where all entities in the same table are iterated in the same result: + +``` +Result + - inst_1: 10 + - inst_2: 10 +Result + - ent_1: 20 + - ent_2: 30 +``` + +Instead, when ran this code produces the following output: + +``` +Result + - inst_1: 10 +Result + - inst_2: 10 +Result + - ent_1: 20 + - ent_2: 30 +``` + +The prefab instances are returned one at a time, even though they are stored in the same table. The rationale behind this behavior is that it prevents a common error that was present in the previous code example, specifically where the `Mass` component was read: + +```cpp + << m[i].value +``` + +If `inst_1` and `inst_2` would have been returned in the same result it would have caused the inner for loop to loop twice. This means `i` would have become `1` and `m[1].value` would have caused an out of bounds violation, likely crashing the code. Why does this happen? + +The reason is that both entities inherit the same instance of the `Mass` component. The `Mass` pointer provided by the iterator is not an array to multiple values, in this case it is _a pointer to a single value_. To fix this error, the component would have to be accessed as: + +```cpp + << m->value +``` + +To prevent subtle errors like this from happening, iterators will switch to entity-based iteration when a result has fields of mixed lengths. This returns entities one by one, and guarantees that the loop variable `i` always has value `0`. The expression `m[0].value` is equivalent to `m->value`, which is the right way to access the component. + +> **Note**: As long as all fields in a result are of equal length, the iterator switches to table-based iteration. This is shown in the previous example: both `ent_1` and `ent_2` are returned in the same result. + +While this provides a safe default to iterate results with shared components, it does come at a performance cost. Iterating entities one at a time can be much slower than iterating an entire table, especially if tables are large. When this cost is too great, iteration can be *instanced*, which prevents switching from table-based to entity-based iteration. + +> **Note**: The implementation of the C++ `each` function always uses instancing. + +The following diagram shows the difference in how results are returned between instanced iteration and non-instanced iteration. Each result block corresponds with a call to the `iter` method in the above example: + +![filter diagram](img/query_instancing.png) + +Note that when iteration is not instanced, `inst_1` and `inst_2` are returned in separate results, whereas with instanced iteration they are both combined in the same result. Iterating the right result is faster for a few reasons: + +- Fewer results means less overhead from iteration code +- Direct access to component arrays allows for optimizations like auto-vectorization + +However, what the diagram also shows is that code for instanced iterators must handle results where `Mass` is either an array or a single value. This is the tradeoff of instancing: faster iteration at the cost of more complex iteration code. + +The following sections show how to use instancing in the different language bindings. The code examples use filter queries, but also apply to queries and rules. + +#### Query Descriptor (C) +Queries can be instanced by setting the `instanced` member to true: + +```c +ecs_filter_t *f = ecs_filter(world, { + .terms = { + { ecs_id(Position), src.flags = EcsSelf }, // Never inherit Position + { ecs_id(Mass) } + }, + + // Instancing is a property of the iterator, but by setting it on the query + // all iterators created for the query will be instanced. + .instanced = true +}); + +ecs_iter_t it = ecs_filter_iter(world, &it); +while (ecs_filter_next(&it)) { + // Fetch components as usual + Position *p = ecs_field(&it, Position, 1); + Mass *m = ecs_field(&it, Mass, 2); + + if (ecs_field_is_self(&it, 2)) { + // Mass is matched on self, access as array + for (int i = 0; i < it.count; i ++) { + p[i].x += 1.0 / m[i].value; + p[i].y += 1.0 / m[i].value; + } + } else { + // Mass is matched on other entity, access as single value + for (int i = 0; i < it.count; i ++) { + p[i].x += 1.0 / m->value; + p[i].y += 1.0 / m->value; + } + } +} +``` + +Note how the `ecs_field_is_self` test is moved outside of the for loops. This keeps conditional statements outside of the core loop, which enables optimizations like auto-vectorization. + +#### Query Builder (C++) +Queries can be instanced by calling the `instanced` method: + +```cpp +flecs::filter f = world.filter_builder() + // Never inherit Position + .arg(1).self() + // Instancing is a property of the iterator, but by setting it on the query + // all iterators created for the query will be instanced. + .instanced() + .build(); + +f.iter([](flecs::iter& it, Position *p, Mass *v) { + if (it.is_self(2)) { + // Mass is matched on self, access as array + for (auto i : it) { + p[i].x += 1.0 / m[i].value; + p[i].y += 1.0 / m[i].value; + } + } else { + // Mass is matched on other entity, access as single value + for (auto i : it) { + p[i].x += 1.0 / m->value; + p[i].y += 1.0 / m->value; + } + } +}); +``` + +Note how the `it.is_self()` test is moved outside of the for loops. This keeps conditional statements outside of the core loop, which enables optimizations like auto-vectorization. + +### Variables +> *Partial support: filters, cached queries. Full support: rules* + +Query variables represent the state of a query while it is being evaluated. The most common form of state is "the entity (or table) against which the query is evaluated". While a query is evaluating an entity or table, it has to store it somewhere. In flecs, that "somewhere" is a query variable. + +Consider this query example, written down with explicit term [sources](#source): + +``` +Position($This), Velocity($This) +``` + +The first term to encounter a variable is usually the one to populate it with all candidates that could match that term. Subsequent terms then use the already populated variable to test if it matches. If the condition matches, the query moves on to the next term. If the condition fails, the query moves back to the previous term and, if necessary, populates the variable with the next candidate. These kinds of conditions are usually referred to as [predicates](https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)), and this evaluation process is called [backtracking](https://en.wikipedia.org/wiki/Backtracking). + +This process effectively _constrains_ the possible results that a term could yield. By itself, the `Velocity` term would return all entities with the `Velocity` component, but because `$This` has been assigned already with entities that have `Position`, the term only feeds forward entities that have both `Position` and `Velocity`. + +While using variables as [source](#source) is the most common application for variables, variables can be used in any part of the term. Consider constructing a query for all spaceships that are docked to a planet. A first attempt could look like this: + +``` +SpaceShip, (DockedTo, *) +``` + +When rewritten with explicit sources, the query looks like this: + +``` +SpaceShip($This), DockedTo($This, *) +``` + +This returns all spaceships that are docked to _anything_, instead of docked to planets. To constrain the result of this query, the wildcard used as target for the `DockedTo` relationship can be replaced with a variable. An example: + +``` +SpaceShip($This), DockedTo($This, $Location) +``` + +When the second term is evaluated for the first time, `$Location` will not yet be populated. This causes the term to do two things: + +1. Test if the entity/table populated in `$This` has `(DockedTo, *)` +2. If so, populate `$Location` with the id matched by `*`. + +After evaluating the second term, the `$Location` variable is populated with the location the spaceship is docked to. We can now use this variable in a new term, that constrains the location to only entities that have `Planet`: + +``` +SpaceShip($This), DockedTo($This, $Location), Planet($Location) +``` + +This query returns the desired result ("return all spaceships docked to a planet"). + +Variables can also be used to constrain matched components. Consider the following example query: + +``` +Serializable($Component), $Component($This) +``` + +This query returns serializable components for all entities that have at least one. + +#### Setting Variables +By default variables are assigned while the query is being iterated, but variables can be set before query iteration to constrain the results of a query. Consider the previous example: + +``` +SpaceShip($This), DockedTo($This, $Location) +``` + +An application can set the `$This` variable or `$Location` variables, or both, before starting iteration to constrain the results returned by the query. This makes it possible to reuse a single query for multiple purposes, which provides better performance when compared to creating multiple queries. + +The following sections show how to use queries in the different language bindings. The code examples use rules queries, which currently are the only queries that support using variables other than `$This`. + +#### Query Descriptor (C) +Query variables can be specified by setting the `name` member in combination with setting the `EcsIsVariable` bit in the `flags` member: + +```c +// SpaceShip, (DockedTo, $Location), Planet($Location) +ecs_rule_t *r = ecs_rule(world, { + .terms = { + { .id = SpaceShip }, + { + .first.id = DockedTo, + .second = { + .name = "Location", + .flags = EcsIsVariable + } + }, + { + .id = Planet, + .src = { + .name = "Location", + .flags = EcsIsVariable + } + } + } +}); +``` + +An application can constrain the results of the query by setting the variable before starting iteration: + +```c +ecs_entity_t earth = ecs_new(world, Planet); + +// Find index for Location variable +int32_t location_var = ecs_rule_find_var(r, "Location"); + +// Constrain results of iterator to return spaceships docked to Earth +ecs_iter_t it = ecs_rule_iter(world, r); +ecs_iter_set_var(&it, location_var, earth); + +// Iterate as usual +``` + +#### Query Builder (C++) +Query variables can be specified by specifying a name with a `$` prefix: + +```cpp +auto r = world.rule_builder() + .term() + .term().second("$Location") + .term().src("$Location") + .build(); +``` + +Alternatively, variables can also be specified using the `var` method: + +```cpp +auto r = world.rule_builder() + .term() + .term().second().var("Location") + .term().src().var("Location") + .build(); +``` + +An application can constrain the results of the query by setting the variable before starting iteration: + +```cpp +flecs::entity earth = world.entity(); + +int32_t location_var = r.find_var("Location"); +r.iter().set_var(location_var, earth).each([]{ + // iterate as usual +}); +``` + +Alternatively the variable name can be provided to `set_var` directly: + +```cpp +flecs::entity earth = world.entity(); + +r.iter().set_var("Location", earth).each([]{ + // iterate as usual +}); +``` + +### Change Detection +> *Supported by: cached queries* + +Change detection makes it possible for applications to know whether data matching a query has changed. Changes are tracked at the table level, for each component in the table. While this is less granular than per entity tracking, the mechanism has minimal overhead, and can be used to skip entities in bulk. + +Change detection works by storing a list of counters on tracked tables, where each counter tracks changes for a component in the table. When a component in the table changes, the corresponding counter is increased. An additional counter is stored for changes that add or remove entities to the table. Queries with change detection store a copy of the list of counters for each table in the cache, and compare counters to detect changes. To reduce overhead, counters are only tracked for tables matched with queries that use change detection. + +The change detection feature cannot detect all changes. The following scenarios are detected by change detection: +- Anything that causes adding or removing a component, tag or pair +- Deleting an entity +- Setting a component value with `set` +- Calling `modified` for an entity/component +- Iterating the table _without skipping it_ with a query that has `inout` or `out` terms +- A change in tables matched by the query + +The following scenarios are not detected by change detection: +- Modifying a component obtained by `get_mut` without calling `modified` +- Modifying the value of a ref (`ecs_ref_t` or `flecs::ref`) without calling `modified` + +A query with change detection enabled will only report a change for the components it matched with, or when an entity got added/removed to a matched table. A change to a component in a matched table that is not matched by the query will not be reported by the query. + +By default queries do not use change detection. Change detection is automatically enabled when a function that requires change detection is called on the query, for example if an application calls `changed()` on the query. Once change detection is enabled it will stay enabled for both the query and the tables the query is matched with. + +When a change occurred in a table matching a query, the query state for that table will remain changed until the table is iterated by the query. + +When a query iterates a table for which changes are tracked and the query has `inout` or `out` terms that are matched with components of that table, the counter for those components will be increased by default. An application can indicate that no components were modified by skipping the table (see code examples). + +> When a query uses change detection and has `out` or `inout` terms, its state will always be changed as iterating the query increases the table counters. It is recommended to only use terms with the `in` access modifier in combination with change detection. + +The following sections show how to use change detection in the different language bindings. The code examples use cached queries, which is the only kind of query for which change detection is supported. + +#### Query Descriptor (C) +The following example shows how the change detection API is used in C: + +```c +// Query used for change detection. Note that change detection is not enabled on +// the query itself, but by calling change detection functions for the query. +ecs_query_t *q_read = ecs_query(world, { + .filter.terms = {{ .id = ecs_id(Position), .inout = EcsIn }} +}); + +// Query used to create changes +ecs_query_t *q_write = ecs_query(world, { + .filter.terms = {{ .id = ecs_id(Position) }} // defaults to inout +}); + +// Test if changes have occurred for anything matching the query. If this is the +// first call to the function, it will enable change detection for the query. +bool changed = ecs_query_changed(q_read, NULL); + +// Setting a component will update the changed state +ecs_entity_t e = ecs_new_id(world); +ecs_set(world, e, Position, {10, 20}); + +// Iterating a query with inout/out terms will update the change state +ecs_iter_t it = ecs_query_iter(world, q_write); +while (ecs_query_next(&it)) { + if (dont_change) { + // If no changes are made to the iterated table, the skip function can be + // called to prevent marking the matched components as dirty. + ecs_query_skip(&it); + } else { + // Iterate as usual. It does not matter whether the code actually writes the + // components or not: when a table is not skipped, components matched with + // inout or out terms will be marked dirty by the iterator. + } +} + +// Iterating tables with q_read will reset the changed state +it = ecs_query_iter(world, q_read); +while (ecs_query_next(&it)) { + if (ecs_query_changed(q_read, &it)) { + // Check if the current table has changed. The change state will be reset + // after the table is iterated, so code can respond to changes in individual + // tables. + } +} +``` + +#### Query Builder (C++) +The following example shows how the change detection API is used in C++: + +```cpp +// Query used for change detection. Note that change detection is not enabled on +// the query itself, but by calling change detection functions for the query. +flecs::query q_read = world.query(); + +// Query used to create changes +flecs::query q_write = world.query(); // defaults to inout + +// Test if changes have occurred for anything matching the query. If this is the +// first call to the function, it will enable change detection for the query. +bool changed = q_read.changed(); + +// Setting a component will update the changed state +flecs::entity e = world.entity() + .set({10, 20}); + +q_write.iter([](flecs::iter& it, Position *p) { + if (dont_change) { + // If no changes are made to the iterated table, the skip function can be + // called to prevent marking the matched components as dirty. + it.skip(); + } else { + // Iterate as usual. It does not matter whether the code actually writes the + // components or not: when a table is not skipped, components matched with + // inout or out terms will be marked dirty by the iterator. + } +}); + +q_read.iter([](flecs::iter& it, Position *p) { + if (it.changed()) { + // Check if the current table has changed. The change state will be reset + // after the table is iterated, so code can respond to changes in individual + // tables. + } +}); +``` + +### Sorting +> *Supported by: cached queries* + +Sorted queries allow an application to specify a component that entities should be sorted on. Sorting is enabled by setting the `order_by` function in combination with the component to order on. Sorted queries sort the tables they match with when necessary. To determine whether a table needs to be sorted, sorted queries use [change detection](#change-detection). A query determines whether a sort operation is needed when an iterator is created for it. + +> Because sorting relies on change detection, it has the same limitations with respect to detecting changes. When using sorted queries, make sure a query is able to detect the changes necessary for knowing when to (re)sort. + +Query sorting works best for data that does not change often, as the sorting process can be expensive. This is especially true for queries that match with many tables, as one step of the [sorting algorithm](#sorting-algorithm) scans all matched tables repeatedly to find an ordered set of slices. + +An application should also prevent having sorted queries with conflicting sorting requirements. This can cause scenarios in which both queries are invalidating each others ordering, which can result in a resort each time an iterator is created for one of the conflicting queries. + +Sorted queries are encouraged to mark the component used for sorting as `In`. If a sorted query has write access to the sorted component, iterating the query will invalidate its own order which can lead to continuous resorting. + +Components matched through [traversal](#relationship-traversal) can be used to sort entities. This often results in more efficient sorting as component values can be used to sort entire tables, and as a result tables themselves do not have to be sorted. + +#### Sorting Algorithm +Sorted queries use a two-step process to return entities in a sorted order. The sort algorithm used in both steps is quicksort. The first step sorts contents of all tables matched by the query. The second step is to find a list of ordered slices across the tables matched by the query. This second step is necessary to support datasets where ordered results have entities interleaved from multiple tables. An example data set: + +Entity | Components (table) | Value used for sorting +--------|--------------------|----------------------- +E1 | Position | 1 +E2 | Position | 3 +E3 | Position | 4 +E4 | Position, Velocity | 5 +E5 | Position, Velocity | 7 +E6 | Position, Mass | 8 +E7 | Position | 10 +E8 | Position | 11 + +To make sure a query iterates the entities in the right order, it will iterate entities in the ordered tables to determine the largest slice of ordered entities in each table, which the query will iterate in order. Slices are computed during the sorting step. For the above set of entities slices would look like this: + +Table | Slice +-------------------|------- +Position | 0..2 +Position, Velocity | 3..4 +Position, Mass | 5 +Position | 6..7 + +To minimize time spent on sorting, the results of a sort are cached. The performance overhead of iterating an already sorted query is comparable to iterating a regular query, though for degenerate scenarios where a sort produces many slices for comparatively few tables the performance overhead can be significant. + +The following sections show how to use sorting in the different language bindings. The code examples use cached queries, which is the only kind of query for which change detection is supported. + +#### Query Descriptor (C) +The following example shows how to use sorted queries in C: + +```c +ecs_query_t *q = ecs_query(world, { + .filter.terms = { + // Use readonly term for component used for sorting + { ecs_id(Depth), .inout = EcsIn } + { ecs_id(Position) }, + }, + .order_by_component = ecs_id(Position), // The component to use for sorting + .order_by = compare_position, +}); +``` + +The function signature of the `order_by` function should look like the following example: + +```c +int compare_position(ecs_entity_t e1, const void *v1, ecs_entity_t e2, const void *v2) { + const Depth *d1 = v1; + const Depth *d2 = v2; + return (d1->value > d2->value) - (d1->value < d2->value); +} +``` + +A query may only use entity identifiers to sort by not setting the `order_by_component` member: + +```c +ecs_query_t *q = ecs_query(world, { + .filter.terms = { + { ecs_id(Position) }, + }, + .order_by = compare_entity, +}); +``` + +The following example shows a function that sorts by entity id: + +```c +int compare_entity(ecs_entity_t e1, const void *v1, ecs_entity_t e2, const void *v2) { + return (e1 > e2) - (e1 < e2); +} +``` + +#### Query Builder (C++) +The following example shows how to use sorted queries in C++: + +```cpp +// Use readonly term for component used for sorting +auto q = world.query_builder() + .order_by([](flecs::entity_t e1, const Depth *d1, flecs::entity_t e2, const Depth *d2) { + return (d1->value > d2->value) - (d1->value < d2->value); + }) + .build(); +``` + +Queries may specify a component id if the component is not known at compile time: + +```cpp +flecs::entity depth_id = world.component(); + +auto q = world.query_builder() + .term(depth_id).in() + .order_by(depth_id, [](flecs::entity_t e1, const void *d1, flecs::entity_t e2, const void *d2) { + // Generic sort code ... + }) + .build(); +``` + +Queries may specify zero for component id to sort on entity ids: + +```cpp +auto q = world.query_builder() + .order_by(0, [](flecs::entity_t e1, const void *d1, flecs::entity_t e2, const void *d2) { + return (e1 > e2) - (e1 < e2); + }) + .build(); +``` + +### Grouping +> *Supported by: cached queries* + +Grouping is the ability of queries to assign an id ("group id") to a set of tables. Grouped tables are iterated together, as they are stored together in the query cache. Additionally, groups in the query cache are sorted by group id, which guarantees that tables with a lower group id are iterated after tables with a higher group id. Grouping is only supported for cached queries. + +Group ids are local to a query, and as a result queries with grouping do not modify the tables they match with. + +Grouping is the least granular, most efficient mechanism to order results of a query. Grouping does not rely on sorting individual tables or entities. Instead, sorting only happens when a new group id is introduced to a query, which is rare in many scenarios. Queries maintain an index that maps group ids to the first and last elements in the cache belonging to that group. This allows for tables to be inserted into the cache in a fast constant time operation, while also providing ordered access to tables. + +The grouping mechanism is used internally by the `cascade` feature. Queries that use `cascade` use a computed hierarchy depth as group id. Because lower group ids are iterated before higher group ids, this provides in a bread-first topological sort of tables and entities that is almost free to maintain. + +Queries may be grouped and [sorted](#sorting) at the same time. When combined, grouping takes precedence over sorting. Tables are first assigned to their groups in the query cache, after which each group is sorted individually. + +An example of an internal query that uses `cascade` grouping in combination with [sorting](#sorting) is the builtin pipeline query. The pipeline query first groups systems by their depth in the `DependsOn` relationship tree. Within the depth-based groups systems are ordered based on their entity id, which ensures systems are iterated in order of declaration. + +#### Group Iterators +A group iterator iterates over a single group of a grouped query. This can be useful when an application may need to match different entities based on the context of the game, such as editor mode, day/night, inside/outside or location in the world. + +One example is that of an open game which is divided up into world cells. Even though a world may contain many entities, only the entities in cells close to the player need to be processed. Instead of creating a cached query per world cell, apps can create a single query grouped by world cell, and use group iterators to only iterate the necessary cells. + +Grouped iterators, when used in combination with a good group_by function are one of the fastest available mechanisms for finding entities in Flecs. The feature provides the iteration performance of having a cached query per group, but without the overhead of having to maintain multiple caches. Whether a group has ten or ten thousand tables does not matter, which makes the feature an enabler for games with large numbers of entities. + +The following sections show how to use sorting in the different language bindings. The code examples use cached queries, which is the only kind of query for which change detection is supported. + +#### Query Descriptor (C) +The following example shows how grouping can be used to group entities that are in the same game region. + +```c +ecs_entity_t Region = ecs_new_id(world); +ecs_entity_t Unit = ecs_new_id(world); + +ecs_entity_t Region_01 = ecs_new_id(world); +ecs_entity_t Region_02 = ecs_new_id(world); + +// Example of entities created in different regions +ecs_entity_t unit_01 = ecs_new_w_id(world, Unit); +ecs_entity_t unit_02 = ecs_new_w_id(world, Unit); + +ecs_add_pair(world, unit_01, Region, Region_01); +ecs_add_pair(world, unit_02, Region, Region_02); + +// Create query that groups entities that are in the same region +ecs_query(world, { + .filter.terms = {{ Unit }}, + .group_by = group_by_target, // function that groups by relationship target + .group_by_id = Region // optional, passed to group_by function +}); +``` + +The following example shows what the implementation of `group_by_target` could look like: + +```c +uint64_t group_by_target( + ecs_world_t *world, ecs_table_t *table, ecs_id_t id, void *ctx) +{ + // Use id (Region) to find target for relationship + ecs_id_t pair = 0; + if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &pair) != -1) { + return ecs_pair_second(world, pair); // Return second element (target) + } else { + return 0; // Table does not have relationship + } +} +``` + +#### Query Builder (C++) +The following example shows how grouping can be used to group entities that are in the same game region. + +```cpp +struct Region { }; +struct Unit { }; + +flecs::entity Region_01 = world.entity(); +flecs::entity Region_02 = world.entity(); + +// Example of entities created in different regions +flecs::entity unit_01 = world.entity() + .add() + .add(Region_01); + +flecs::entity unit_02 = world.entity() + .add() + .add(Region_02); + +// Create query that groups entities that are in the same region +flecs::query<> q = world.query_builder() + .term() + .group_by([]( + flecs::world_t *world, + flecs::table_t *table, + flecs::id_t id, + void *ctx) + { + flecs::entity_t result = 0; + ecs_id_t pair = 0; + + // Use id (Region) to find target for relationship + if (ecs_search(world, table, ecs_pair(id, EcsWildcard), &pair) != -1) { + result = ecs_pair_second(world, pair); // Return second element (target) + } + + return result; + }) + .build(); +``` + +### Component Inheritance +> *Supported by: rules* + +Component inheritance allows for a query to match entities with a component and all subsets of that component, as defined by the `IsA` relationship. Component inheritance is enabled for all queries by default, for components where it applies. + +It is possible to prevent inheriting from a component from by adding the [Final](Relationships.md#final-property) property. Queries for components with the `Final` property will not attempt to resolve component inheritance. + +Inheritance relationships can, but are not required to mirror inheritance of the types used as long as it does not impact the layout of the type. Component inheritance is most often used with tags. + +The following sections show how to use component inheritance in the different language bindings. The code examples use rules, which is the only kind of query for which component inheritance is currently supported. + +#### Query Descriptor (C) +The following example shows a rule that uses component inheritance to match entities: + +```c +ecs_entity_t Unit = ecs_new_id(world); +ecs_entity_t MeleeUnit = ecs_new_w_pair(world, EcsIsA, Unit); +ecs_entity_t RangedUnit = ecs_new_w_pair(world, EcsIsA, Unit); + +ecs_entity_t unit_01 = ecs_new_w_id(world, MeleeUnit); +ecs_entity_t unit_02 = ecs_new_w_id(world, RangedUnit); + +// Matches entities with Unit, MeleeUnit and RangedUnit +ecs_rule_t *r = ecs_rule(world, { + .terms = {{ Unit }} +}); + +// Iterate as usual +``` + +#### Query Builder (C++) +The following example shows a rule that uses component inheritance to match entities: + +```cpp +flecs::entity Unit = ecs_new_id(world); +flecs::entity MeleeUnit = world.entity().is_a(Unit); +flecs::entity RangedUnit = world.entity().is_a(Unit); + +flecs::entity unit_01 = world.entity().add(MeleeUnit); +flecs::entity unit_02 = world.entity().add(RangedUnit); + +// Matches entities with Unit, MeleeUnit and RangedUnit +flecs::rule r = world.rule(); + +// Iterate as usual +``` + +### Transitive Relationships +> *Supported by: rules* + +When a [transitive relationship](Relationships.md#transitive-relationships) is used by a query, a query will automatically look up or test against pairs that satisfy the transitive property. Transitivity is usually defined as: + +> If R(X, Y) and R(Y, Z) then R(X, Z) + +In this example, `R` is the transitive relationship and `X`, `Y` and `Z` are entities that are used both as source and second element. A typical example of a transitive relationship is `LocatedIn`. If Bob (X) is located in Manhattan (Y) and Manhattan (Y) is located in New York (Z), then Bob (X) is also located in New York (Z). Therefore "located in" is transitive. + +A relationship can be made transitive by adding the [transitive](Relationships.md#transitive-property) property. This would ensure that both `(LocatedIn, ManHattan)` and `(LocatedIn, NewYork)` queries would match `Bob`. When the location is replaced with a variable, the variable will assume all values encountered while traversing the transitive relationship. For example, `(LocatedIn, $Place)` would return results with `Place = [ManHattan, NewYork]` for `Bob`, and a result with `Place = [NewYork]` for `ManHattan`. + +An example of a builtin relationship that has the `Transitive` property is the `IsA` relationship. + +The following sections show how to use transitive relationships in the different language bindings. The code examples use rules, which is the only kind of query for which transitive relationships are currently supported. + +#### Query Descriptor (C) +The following example shows a rule that uses transitivity to match entities that are located in New York: + +```c +// Create LocatedIn relationship with transitive property +ecs_entity_t LocatedIn = ecs_new_w_id(world, EcsTransitive); + +ecs_entity_t NewYork = ecs_new_id(world); +ecs_entity_t Manhattan = ecs_new_w_pair(world, LocatedIn, NewYork); +ecs_entity_t CentralPark = ecs_new_w_pair(world, LocatedIn, Manhattan); +ecs_entity_t Bob = ecs_new_w_pair(world, LocatedIn, CentralPark); + +// Matches ManHattan, CentralPark, Bob +ecs_rule_t *r = ecs_rule(world, { + .terms = {{ ecs_pair(LocatedIn, NewYork) }} +}); + +// Iterate as usual +``` + +Queries for transitive relationships can be compared with variables. This query returns all locations an entity is in: + +```c +// Matches: +// - ManHattan (Place = NewYork) +// - CentralPark (Place = ManHattan, NewYork) +// - Bob (Place = CentralPark, ManHattan, NewYork) +ecs_rule_t *r = ecs_rule(world, { + .terms = {{ .first.id = LocatedIn, .second.name = "Place", .second.flags = EcsIsVariable }} +}); +``` + +Variables can be used to constrain the results of a transitive query. The following query returns locations an entity is in that are a city: + +```c +// Add City property to NewYork +ecs_entity_t City = ecs_new_id(world); +ecs_add_id(world, NewYork, City); + +// Matches: +// - ManHattan (Place = NewYork) +// - CentralPark (Place = NewYork) +// - Bob (Place = NewYork) +ecs_rule_t *r = ecs_rule(world, { + .terms = { + { .first.id = LocatedIn, .second.name = "Place", .second.flags = EcsIsVariable }, + { .id = City, .src.name = "Place", .src.flags = EcsIsVariable } + } +}); +``` + +#### Query Builder (C++) +The following example shows a rule that uses transitivity to match entities that are located in New York: + +```cpp +// Create LocatedIn relationship with transitive property +struct LocatedIn { }; + +world.component().add(flecs::Transitive); + +flecs::entity NewYork = world.entity(); +flecs::entity Manhattan = world.entity().add(NewYork); +flecs::entity CentralPark = world.entity().add(Manhattan); +flecs::entity Bob = world.entity().add(CentralPark); + +// Matches ManHattan, CentralPark, Bob +flecs::rule<> r = world.rule_builder() + .term(NewYork) + .build(); + +// Iterate as usual +``` + +Queries for transitive relationships can be compared with variables. This query returns all locations an entity is in: + +```cpp +// Matches: +// - ManHattan (Place = NewYork) +// - CentralPark (Place = ManHattan, NewYork) +// - Bob (Place = CentralPark, ManHattan, NewYork) +flecs::rule<> r = world.rule_builder() + .term().second("$Place") + .build(); +``` + +Variables can be used to constrain the results of a transitive query. The following query returns locations an entity is in that are a city: + +```cpp +struct City { }; + +// Add City property to NewYork +NewYork.add(City); + +// Matches: +// - ManHattan (Place = NewYork) +// - CentralPark (Place = NewYork) +// - Bob (Place = NewYork) +flecs::rule<> r = world.rule_builder() + .term().second("$Place") + .term().src("$Place") + .build(); +``` + +#### Query DSL +Transitivity in a query is enabled by adding the `Transitive` property to a relationship. As a result, a query for a transitive relationship looks the same as a query for a non-transitive relationship. The following examples show the queries used in the C/C++ examples: + +Match all entities located in New York: + +``` +(LocatedIn, NewYork) +``` + +Return all locations an entity is in: + +``` +(LocatedIn, $Place) +``` + +Return the city entities are in: + +``` +(LocatedIn, $Place), City($Place) +``` + +### Reflexive Relationships +> *Supported by: rules* + +When a query matches a [reflexive](Relationships.md#reflexive-property) relationship, a query term will evaluate to true if the source and target are equal. Reflexivity can be defined as: + +> R(X, X) + +For example, if relationship IsA (R) is reflexive, then a Tree (X) is a Tree (X). An example of a builtin relationship that has the `Reflexive` property is the `IsA` relationship. + +The following sections show how to use transitive relationships in the different language bindings. The code examples use rules, which is the only kind of query for which transitive relationships are currently supported. + +#### Query Descriptor (C) +The following example shows a rule that uses the `IsA` reflexive relationship: + +```c +ecs_entity_t Tree = ecs_new_id(world); +ecs_entity_t Oak = ecs_new_w_pair(world, EcsIsA, Tree); + +// Matches Tree, Oak +ecs_rule_t *r = ecs_rule(world, { + .terms = {{ ecs_pair(EcsIsA, Tree )}} +}); +``` + +#### Query Builder (C++) +The following example shows a rule that uses the `IsA` reflexive relationship: + +```cpp +flecs::entity Tree = world.entity(); +flecs::entity Oak = world.entity().is_a(Tree); + +// Matches Tree, Oak +flecs::rule<> r = world.rule_builder() + .term(flecs::IsA, Tree) + .build(); +``` + +#### Query DSL +Reflexivity in a query is enabled by adding the `Reflexive` property to a relationship. As a result, a query for a reflexive relationship looks the same as a query for a non-reflexive relationship. The following example shows the query used in the C/C++ examples: + +``` +(IsA, Tree) +``` + +## Performance +This section describes performance characteristics for each query type. + +### Filter +A filter is a data type that describes the structure of a query in Flecs. Filters are the cheapest to create, as creating one just requires initializing the value of the filter object. + +Filters can serve different purposes, like iterating all matching entities, or testing if a specific entity matches with a filter. Filters can also be used to filter the output of other queries, including other filters. + +While the exact way a filter is evaluated depends on what kind of filter it is, almost all filter evaluation follows steps similar to the ones in this diagram: + +![filter diagram](img/filter_diagram.png) + +Each node in the diagram represents a function that can return either true or false. When a node returns true, evaluation moves on to the next node. When it returns false, evaluation goes back one node. These kinds of functions are called [predicates](https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)), and this evaluation process is called [backtracking](https://en.wikipedia.org/wiki/Backtracking). + +Whether a node returns true or false depends on what its function does: + +- The first node invokes the function `select(Position)`. This function finds all *tables* with `Position`, and will forward these tables to the next node. As long as `select` is able to find more tables with `Position`, the node will keep returning true. When no more tables can be found the node returns false. + +- The second node invokes the function `with(Velocity)`. This function takes the table found by the previous node and returns true if the table has `Velocity`, and false if it does not. + +Both functions are O(1) operations. + +When a table reaches the `yield` node it means that it matched all parts of the filter, and it will be returned by the iterator doing the evaluation. + +> A table groups all entities that have _exactly_ the same components. Thus if one entity in a table matches a node, all entities in the table match the node. This is one of the main reasons queries are fast: instead of checking each individual entity for components, we can eliminate a table with thousands of entities in a single operation. + +Because filters are fast to create, have low overhead, and are reasonably efficient to iterate, they are the goto solution for when an application cannot know in advance what it needs to query for, like finding all children for a specific entity: + +```c +ecs_filter_f fs = ECS_FILTER_INIT; +ecs_filter_t *f = ecs_filter(world, { + .storage = &fs, // optional, but prevents allocation + .terms = {{ ecs_childof(e) }} +}); + +ecs_iter_t child_it = ecs_filter_iter(f); +while (ecs_filter_next(&child_it)) { + for (int c = 0; c < child_it.count; c ++) { + printf("child %s\n", ecs_get_name(world, + child_it.entities[c])); + } +} + +ecs_filter_fini(f); +``` +```cpp +auto f = world.filter_builder() + .term(flecs::ChildOf, e) + .build(); + +f.each([](flecs::entity child) { + std::cout << child.path().str() << "\n"; +}); +``` + +While filter evaluation for a single component is very fast, each additional component adds some overhead. This is not just because of the time it takes to do an additional check, but because more tables will get discarded during evaluation. The cost of discarded tables adds up as they pass through one or more nodes before getting rejected, consuming resources while not contributing to the query result. + +This cost can be particularly significant for filters that match a large (5-10) number of components in applications with many (>1000s) tables. While Flecs implements several strategies to limit this overhead, like storing empty tables separately from non-empty tables, this is something to keep in mind when using filters. + +### Cached Queries +A cached query is an object that caches the output of a filter. Cached queries are significantly faster to iterate than a filter, as an iterator just needs to walk the list of cache entries and return each one to the application. + +This also makes the performance of cached queries more predictable. The overhead per returned table for a filter depends on how many components it matches with, and how many tables it had to discard. For a cached query the cost per table is much lower, and is constant time. + +> [A benchmark showing the difference between cached queries and filters](https://twitter.com/ajmmertens/status/1509473999205507080). + +This predictable behavior is why cached queries are the default for Flecs systems. Once tables are created and caches are initialized, the (often time critical) main loop of an application does not have to spend time matching entities with systems, which frees up resources for application logic. + +Another benefit of cached queries is that they are compatible with good code practices. Cached queries add minimal overhead to systems, which means applications do not have to compromise on design where the cost of evaluating a query has to be weighed against the benefit of having many small decoupled systems. + +The following example shows how cached queries are used: + +```c +// Creates query, populates the cache with existing tables +ecs_query_t *q = ecs_query(world, { + .filter.terms = { + { ecs_id(Position) }, { ecs_id(Velocity) } + } +}); + +// When a new table is created that matches the query, it is +// added to the cache +ecs_entity_t e = ecs_new_id(world); +ecs_add(world, e, Position); +ecs_add(world, e, Velocity); + +// Iterate the tables in the cache +ecs_iter_t it = ecs_query_iter(world, q); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + Velocity *v = ecs_field(&it, Velocity, 2); + + for (int i = 0; i < it.count; i ++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} +``` +```cpp +// Creates query, populates the cache with existing tables +auto q = world.query(); + +// When a new table is created that matches the query, it is +// added to the cache +auto e = world.entity() + .add() + .add(); + +// Iterate the tables in the cache +q.each([](Position& p, Velocity& v) { + p.x += v.x; + p.y += v.y; +}); +``` + +The scenarios described so far are best case scenarios for cached queries, where their performance is amongst the fastest of any ECS implementation. To build games that perform well however, it also helps to know when cached queries perform badly: + +- Cached queries do not perform well when they are repeatedly created and destroyed in loops, or when they are only used a handful of times. The overhead of initializing the query cache and the cost of keeping it up to date could in those cases be higher than using a filter. + +- The cost of table creation/deletion increases with the number of queries in the world. Queries use observers to get notified of new tables. That means that for each table event, the number of queries notified is the number of queries with at least one component in common with the table. + +- ECS operations can cause matched tables to no longer match. A simple example is that of a query matching `Position` on a parent entity, where the component is removed from the parent. This triggers cache revalidation, where a query reevaluates its filter to correct invalid entries. When this happens for a large number of queries and tables, this can be time consuming. + +- Performance can degrade when a query needs to traverse a deep (>50 levels) hierarchy in order to determine if a table matches. If a hierarchy has deeply nested children and a parent component can only be found on the root, a query will have to traverse this tree likely for multiple tables, which is expensive. + + + diff --git a/engine/libs/flecs/docs/Quickstart.md b/engine/libs/flecs/docs/Quickstart.md new file mode 100644 index 0000000..3a54209 --- /dev/null +++ b/engine/libs/flecs/docs/Quickstart.md @@ -0,0 +1,918 @@ +# Flecs Quickstart +This document provides a quick overview of the different features and concepts in Flecs with short examples. This is a good resource if you're just getting started or just want to get a better idea of what kind of features are available in Flecs! + +## Building Flecs +To use Flecs, copy the [flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c) and [flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.h) files from the repository root to your project's source folder. When building, make sure your build system is setup to do the following: + +- If it is a C++ project, make sure to compile [flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c) as C code, for example by using `gcc`/`clang` instead of `g++`/`clang++`. + +- If you are building on Windows and you're not using the Microsoft Visual Studio compiler, make sure to add `-lWs2_32` to **the end(!)** of the linker command. The socket API is used for connecting to Flecs explorer. + +- When compiling Flecs with `gcc`/`clang`, add `-std=gnu99` to the compiler command. This ensures that addons that rely on time & socket functions are compiled correctly. + +- C++ files that use Flecs must be compiled with `-std=c++0x` (C++11) or higher. + +### Dynamic linking +To build Flecs as a dynamic library, remove this line from the top of the [flecs.h](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.h) file: + +```c +#define flecs_STATIC +``` + +When compiling [flecs.c](https://raw.githubusercontent.com/SanderMertens/flecs/master/flecs.c), make sure to define `flecs_EXPORTS`, for example by adding `-Dflecs_EXPORTS` to the compiler command. + +Alternatively Flecs can also be built as a dynamic library with the cmake, meson, bazel or [bake](https://github.com/SanderMertens/bake) build files provided in the repository. These use the files from `src` to build as opposed to the amalgamated files, which is better suited for development. + +### Building with CMake + +Locate `flecs` on your system (either by cloning or as a submodule) and use `add_subdirectory` or use `FetchContent` to download the source code from the master branch of the [flecs repository](https://github.com/SanderMertens/flecs). After that, add the following to your `CMakeLists.txt` file: + +```cmake +target_link_libraries(${PROJECT_NAME} flecs::flecs_static) +``` + +### Building with Bake + +Download or `git clone` the [flecs repository](https://github.com/SanderMertens/flecs) and run `bake` from inside the directory. After that, add the following to your `project.json` file's value property: + +```json +"use": ["flecs"] +``` + +### Running tests (bake) +First make sure you have [bake](https://github.com/SanderMertens/bake) installed (see the bake repository for instructions). + +Run the following commands to run all tests (use `-j` to specify the number of threads): + +```bash +# Core test suite +bake run test/api -- -j 4 + +# Addon tests +bake run test/addons -- -j 4 + +# Reflection tests +bake run test/meta -- -j 4 + +# C++ tests +bake run test/cpp_api -- -j 4 +``` + +To run tests with asan enabled, add `--cfg sanitize` to the command: + +```bash +bake run --cfg sanitize test/api -- -j 4 +``` + +#### Running tests (cmake, experimental) +First make sure to clone [bake](https://github.com/SanderMertens/bake). + +Run the following commands to run all the tests: + +```bash +# Generate make files for Flecs and tests +cmake -DFLECS_TESTS=ON -DBAKE_DIRECTORY="path to cloned bake repository" + +# Build flecs and test suites +cmake --build . -j 4 + +# Run the tests +ctest -C Debug --verbose +``` + +### Emscripten +When building for emscripten, add the following command line options to the `emcc` link command: +```bash +-s ALLOW_MEMORY_GROWTH=1 +-s STACK_SIZE=1mb +-s EXPORTED_RUNTIME_METHODS=cwrap +-s MODULARIZE=1 +-s EXPORT_NAME="my_app" +``` + +### Addons +Flecs has a modular architecture that makes it easy to only build the features you really need. By default all addons are built. To customize a build, first define `FLECS_CUSTOM_BUILD`, then add defines for the addons you need. For example: + +```c +#define FLECS_CUSTOM_BUILD // Don't build all addons +#define FLECS_SYSTEM // Build FLECS_SYSTEM +``` + +Additionally, you can also specify addons to exclude from a build by adding `NO` to the define: + +```c +#define FLECS_NO_LOG +``` + +The following addons can be configured: + +Addon | Description | Define | +--------------|--------------------------------------------------|---------------------| +[Cpp](/flecs/group__cpp.html) | C++11 API | FLECS_CPP | +[Module](/flecs/group__c__addons__module.html) | Organize game logic into reusable modules | FLECS_MODULE | +[System](flecs/group__c__addons__system.html) | Create & run systems | FLECS_SYSTEM | +[Pipeline](/flecs/group__c__addons__pipeline.html) | Automatically schedule & multithread systems | FLECS_PIPELINE | +[Timer](/flecs/group__c__addons__timer.html) | Run systems at time intervals or at a rate | FLECS_TIMER | +[Meta](/flecs/group__c__addons__meta.html) | Flecs reflection system | FLECS_META | +[Meta_C](/flecs/group__c__addons__meta__c.html) | (C) Utilities for auto-inserting reflection data | FLECS_META_C | +[Units](/flecs/group__c__addons__units.html) | Builtin unit types | FLECS_UNITS | +[Expr](/flecs/group__c__addons__expr.html) | String format optimized for ECS data | FLECS_EXPR | +[JSON](/flecs/group__c__addons__json.html) | JSON format | FLECS_JSON | +[Doc](/flecs/group__c__addons__doc.html) | Add documentation to components, systems & more | FLECS_DOC | +[Coredoc](/flecs/group__c__addons__coredoc.html) | Documentation for builtin components & modules | FLECS_COREDOC | +[Http](/flecs/group__c__addons__http.html) | Tiny HTTP server for processing simple requests | FLECS_HTTP | +[Rest](/flecs/group__c__addons__rest.html) | REST API for showing entities in the browser | FLECS_REST | +[Parser](/flecs/group__c__addons__parser.html) | Create entities & queries from strings | FLECS_PARSER | +[Plecs](/flecs/group__c__addons__plecs.html) | Small utility language for asset/scene loading | FLECS_PLECS | +[Rules](/flecs/group__c__addons__rules.html) | Powerful prolog-like query language | FLECS_RULES | +[Snapshot](/flecs/group__c__addons__snapshot.html) | Take snapshots of the world & restore them | FLECS_SNAPSHOT | +[Stats](/flecs/group__c__addons__stats.html) | Functions for collecting statistics | FLECS_STATS | +[Monitor](/flecs/group__c__addons__monitor.html) | Periodically collect & store flecs statistics | FLECS_MONITOR | +[Metrics](/flecs/group__c__addons__metrics.html) | Create metrics from user-defined components | FLECS_METRICS | +[Alerts](/flecs/group__c__addons__alerts.html) | Create alerts from user-defined queries | FLECS_ALERTS | +[Log](/flecs/group__c__addons__log.html) | Extended tracing and error logging | FLECS_LOG | +[Journal](/flecs/group__c__addons__journal.html) | Journaling of API functions | FLECS_JOURNAL | +[App](/flecs/group__c__addons__app.html) | Flecs application framework | FLECS_APP | +[OS API Impl](/flecs/group__c__addons__os__api__impl.html) | Default OS API implementation for Posix/Win32 | FLECS_OS_API_IMPL | + +## Concepts +This section contains an overview of all the different concepts in Flecs and how they wire together. The sections in the quickstart go over them in more detail and with code examples. + +![Flecs Overview](img/flecs-quickstart-overview.png) + +### World +The world is the container for all ECS data. It stores the entities and their components, does queries and runs systems. Typically there is only a single world, but there is no limit on the number of worlds an application can create. + +```c +ecs_world_t *world = ecs_init(); + +// Do the ECS stuff + +ecs_fini(world); +``` +```cpp +flecs::world world; + +// Do the ECS stuff +``` + +### Entity +An entity is a unique thing in the world, and is represented by a 64 bit id. Entities can be created and deleted. If an entity is deleted it is no longer considered "alive". A world can contain up to 4 billion(!) alive entities. Entity identifiers contain a few bits that make it possible to check whether an entity is alive or not. + +```c +ecs_entity_t e = ecs_new_id(world); +ecs_is_alive(world, e); // true! + +ecs_delete(world, e); +ecs_is_alive(world, e); // false! +``` +```cpp +auto e = world.entity(); +e.is_alive(); // true! + +e.destruct(); +e.is_alive(); // false! +``` + +Entities can have names which makes it easier to identify them in an application. In C++ the name can be passed to the constructor. In C a name can be assigned with the `ecs_entity_init` function/`ecs_entity` macro. If a name is provided during entity creation time and an entity with that name already exists, the existing entity will be returned. + +```c +ecs_entity_t e = ecs_entity(world, { .name = "Bob" }); + +printf("Entity name: %s\n", ecs_get_name(world, e)); +``` +```cpp +auto e = world.entity("Bob"); + +std::cout << "Entity name: " << e.name() << std::endl; +``` + +Entities can be looked up by name with the `lookup` function: + +```c +ecs_entity_t e = ecs_lookup(world, "Bob"); +``` +```cpp +auto e = world.lookup("Bob"); +``` + +### Id +An id is a 64 bit number that can encode anything that can be added to an entity. In flecs this can be either a component, tag or a pair. A component is data that can be added to an entity. A tag is an "empty" component. A pair is a combination of two component/tag ids which is used to encode entity relationships. All entity/component/tag identifiers are valid ids, but not all ids are valid entity identifier. + +The following sections describe components, tags and pairs in more detail. + +### Component +A component is a type of which instances can be added and removed to entities. Each component can be added only once to an entity (though not really, see [Pair](#pair)). In C applications components must be registered before use. In C++ this happens automatically. + +```c +ECS_COMPONENT(world, Position); +ECS_COMPONENT(world, Velocity); + +ecs_entity_t e = ecs_new_id(world); + +// Add a component. This creates the component in the ECS storage, but does not +// assign it with a value. +ecs_add(world, e, Velocity); + +// Set the value for the Position & Velocity components. A component will be +// added if the entity doesn't have it yet. +ecs_set(world, e, Position, {10, 20}); +ecs_set(world, e, Velocity, {1, 2}); + +// Get a component +const Position *p = ecs_get(world, e, Position); + +// Remove component +ecs_remove(world, e, Position); +``` +```cpp +auto e = world.entity(); + +// Add a component. This creates the component in the ECS storage, but does not +// assign it with a value. +e.add(); + +// Set the value for the Position & Velocity components. A component will be +// added if the entity doesn't have it yet. +e.set({10, 20}) + .set({1, 2}); + +// Get a component +const Position *p = e.get(); + +// Remove component +e.remove(); +``` + +Each component is associated by a unique entity identifier by Flecs. This makes it possible to inspect component data, or attach your own data to components. C applications can use the `ecs_id` macro to get the entity id for a component. C++ applications can use the `world::id` function: + +```c +ECS_COMPONENT(world, Position); + +ecs_entity_t pos_e = ecs_id(Position); +printf("Name: %s\n", ecs_get_name(world, pos_e)); // outputs 'Name: Position' + +// It's possible to add components like you would for any entity +ecs_add(world, pos_e, Serializable); +``` +```cpp +flecs::entity pos_e = world.entity(); +std::cout << "Name: " << pos_e.name() << std::endl; // outputs 'Name: Position' + +// It's possible to add components like you would for any entity +pos_e.add(); +``` + +The thing that makes an ordinary entity a component is the `EcsComponent` (or `flecs::Component`, in C++) component. This is a builtin component that tells Flecs how much space is needed to store a component, and can be inspected by applications: + +```c +ECS_COMPONENT(world, Position); + +ecs_entity_t pos_e = ecs_id(Position); + +const EcsComponent *c = ecs_get(world, pos_e, EcsComponent); +printf("Component size: %u\n", c->size); +``` +```cpp +flecs::entity pos_e = world.entity(); + +const EcsComponent *c = pos_e.get(); +std::cout << "Component size: " << c->size << std::endl; +``` + +Because components are stored as regular entities, they can in theory also be deleted. To prevent unexpected accidents however, by default components are registered with a tag that prevents them from being deleted. If this tag were to be removed, deleting a component would cause it to be removed from all entities. For more information on these policies, see [Relationship cleanup properties](Relationships.md#cleanup-properties). + +### Tag +A tag is a component that does not have any data. In Flecs tags can be either empty types (in C++) or regular entities (C & C++) that do not have the `EcsComponent` component (or have an `EcsComponent` component with size 0). Tags can be added & removed using the same APIs as adding & removing components, but because tags have no data, they cannot be assigned a value. Because tags (like components) are regular entities, they can be created & deleted at runtime. + +```c +// Create Enemy tag +ecs_entity_t Enemy = ecs_new_id(world); + +// Create entity, add Enemy tag +ecs_entity_t e = ecs_new_id(world); + +ecs_add_id(world, e, Enemy); +ecs_has_id(world, e, Enemy); // true! + +ecs_remove_id(world, e, Enemy); +ecs_has_id(world, e, Enemy); // false! +``` +```cpp +// Option 1: create Tag as empty struct +struct Enemy { }; + +// Create entity, add Enemy tag +auto e = world.entity().add(); +e.has(); // true! + +e.remove(); +e.has(); // false! + + +// Option 2: create Tag as entity +auto Enemy = world.entity(); + +// Create entity, add Enemy tag +auto e = world.entity().add(Enemy); +e.has(Enemy); // true! + +e.remove(Enemy); +e.has(Enemy); // false! +``` + +Note that both options in the C++ example achieve the same effect. The only difference is that in option 1 the tag is fixed at compile time, whereas in option 2 the tag can be created dynamically at runtime. + +When a tag is deleted, the same rules apply as for components (see [Relationship cleanup properties](Relationships.md#cleanup-properties)). + +### Pair +A pair is a combination of two entity ids. Pairs can be used to store entity relationships, where the first id represents the relationship kind and the second id represents the relationship target (called "object"). This is best explained by an example: + +```c +// Create Likes relationship +ecs_entity_t Likes = ecs_new_id(world); + +// Create a small graph with two entities that like each other +ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Alice = ecs_new_id(world); + +ecs_add_pair(world, Bob, Likes, Alice); // Bob likes Alice +ecs_add_pair(world, Alice, Likes, Bob); // Alice likes Bob +ecs_has_pair(world, Bob, Likes, Alice); // true! + +ecs_remove_pair(world, Bob, Likes, Alice); +ecs_has_pair(world, Bob, Likes, Alice); // false! +``` +```cpp +// Create Likes relationship as empty type (tag) +struct Likes { }; + +// Create a small graph with two entities that like each other +auto Bob = world.entity(); +auto Alice = world.entity(); + +Bob.add(Alice); // Bob likes Alice +Alice.add(Bob); // Alice likes Bob +Bob.has(Alice); // true! + +Bob.remove(Alice); +Bob.has(Alice); // false! +``` + +A pair can be encoded in a single 64 bit identifier by using the `ecs_pair` macro in C, or the `world.pair` function in C++: + +```c +ecs_id_t id = ecs_pair(Likes, Bob); +``` +```cpp +flecs::id id = world.pair(Bob); +``` + +The following examples show how to get back the elements from a pair: + +```c +if (ecs_id_is_pair(id)) { + ecs_entity_t relationship = ecs_pair_first(world, id); + ecs_entity_t target = ecs_pair_second(world, id); +} +``` +```cpp +if (id.is_pair()) { + auto relationship = id.first(); + auto target = id.second(); +} +``` + +A component or tag can be added multiple times to the same entity as long as it is part of a pair, and the pair itself is unique: + +```c +ecs_add_pair(world, Bob, Eats, Apples); +ecs_add_pair(world, Bob, Eats, Pears); +ecs_add_pair(world, Bob, Grows, Pears); + +ecs_has_pair(world, Bob, Eats, Apples); // true! +ecs_has_pair(world, Bob, Eats, Pears); // true! +ecs_has_pair(world, Bob, Grows, Pears); // true! +``` +```cpp +Bob.add(Eats, Apples); +Bob.add(Eats, Pears); +Bob.add(Grows, Pears); + +Bob.has(Eats, Apples); // true! +Bob.has(Eats, Pears); // true! +Bob.has(Grows, Pears); // true! +``` + +The `target` function can be used in C and C++ to get the object for a relationship: + +```c +ecs_entity_t o = ecs_get_target(world, Alice, Likes, 0); // Returns Bob +``` +```cpp +auto o = Alice.target(); // Returns Bob +``` + +Entity relationships enable lots of interesting patterns and possibilities. Make sure to check out the [Relationships manual](Relationships.md). + +### Hierarchies +Flecs has builtin support for hierarchies with the builtin `EcsChildOf` (or `flecs::ChildOf`, in C++) relationship. A hierarchy can be created with the regular relationship API, or with the `child_of` shortcut in C++: + +```c +ecs_entity_t parent = ecs_new_id(world); + +// ecs_new_w_pair is the same as ecs_new_id + ecs_add_pair +ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); + +// Deleting the parent also deletes its children +ecs_delete(world, parent); +``` +```cpp +auto parent = world.entity(); +auto child = world.entity().child_of(parent); + +// Deleting the parent also deletes its children +parent.destruct(); +``` + +When entities have names, they can be used together with hierarchies to generate path names or do relative lookups: + +```c +ecs_entity_t parent = ecs_entity(world, { + .name = "parent" +}); + +ecs_entity_t child = ecs_entity(world, { + .name = "child" +}); + +ecs_add_pair(world, child, EcsChildOf, parent); + +char *path = ecs_get_fullpath(world, child); +printf("%s\n", path); // output: 'parent.child' +ecs_os_free(path); + +ecs_lookup_path(world, 0, "parent.child"); // returns child +ecs_lookup_path(world, parent, "child"); // returns child +``` +```cpp +auto parent = world.entity("parent"); +auto child = world.entity("child").child_of(parent); +std::cout << child.path() << std::endl; // output: 'parent::child' + +world.lookup("parent::child"); // returns child +parent.lookup("child"); // returns child +``` + +Queries (see below) can use hierarchies to order data breadth-first, which can come in handy when you're implementing a transform system: + +```c +ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .filter.terms = { + { ecs_id(Position) }, + { ecs_id(Position), .src = { + .flags = EcsCascade, // Breadth-first order + .trav = EcsChildOf // Use ChildOf relationship for traversal + }} + } +}); + +ecs_iter_t it = ecs_query_iter(world, q); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 1); + Position *p_parent = ecs_field(&it, Position, 2); + for (int i = 0; i < it.count; i++) { + // Do the thing + } +} +``` +```cpp +auto q = world.query_builder() + .term_at(2).parent().cascade() + .build(); + +q.each([](Position& p, Position& p_parent) { + // Do the thing +}); +``` + +### Instancing +Flecs has builtin support for instancing (sharing a single component with multiple entities) through the builtin `EcsIsA` relationship (`flecs::IsA` in C++). An entity with an `IsA` relationship to a base entity "inherits" all entities from that base: + +```c +// Shortcut to create entity & set a component +ecs_entity_t base = ecs_set(world, 0, Triangle, {{0, 0}, {1, 1}, {-1, -1}}); + +// Create entity that shares components with base +ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); +const Triangle *t = ecs_get(world, e, Triangle); // gets Triangle from base +``` +```cpp +auto base = world.entity().set({{0, 0}, {1, 1}, {-1, -1}}); + +// Create entity that shares components with base +auto e = world.entity().is_a(base); +const Triangle *t = e.get(); // gets Triangle from base +``` + +Entities can override components from their base: + +```c +// Add private instance of Triangle to e, copy value from base +ecs_add(world, e, Triangle); +``` +```cpp +// Add private instance of Triangle to e, copy value from base +e.add(); +``` + +Instancing can be used to build modular prefab hierarchies, as the foundation of a batched renderer with instancing support, or just to reduce memory footprint by sharing common data across entities. + +### Type +The type (often referred to as "archetype") is the list of ids an entity has. Types can be used for introspection which is useful when debugging, or when for example building an entity editor. The most common thing to do with a type is to convert it to text and print it: + +```c +ECS_COMPONENT(world, Position); +ECS_COMPONENT(world, Velocity); + +ecs_entity_t e = ecs_new_id(world); +ecs_add(world, e, Position); +ecs_add(world, e, Velocity); + +const ecs_type_t *type = ecs_get_type(world, e); +char *type_str = ecs_type_str(world, type); +printf("Type: %s\n", type_str); // output: 'Position,Velocity' +ecs_os_free(type_str); +``` +```cpp +auto e = ecs.entity() + .add() + .add(); + +std::cout << e.type().str() << std::endl; // output: 'Position,Velocity' +``` + +A type can also be iterated by an application: +```c +const ecs_type_t *type = ecs_get_type(world, e); +for (int i = 0; i < type->count; i++) { + if (type->array[i] == ecs_id(Position)) { + // Found Position component! + } +} +``` +```cpp +e.each([&](flecs::id id) { + if (id == world.id()) { + // Found Position component! + } +}); +``` + +### Singleton +A singleton is a single instance of a component that can be retrieved without an entity. The functions for singletons are very similar to the regular API: + +```c +// Set singleton component +ecs_singleton_set(world, Gravity, { 9.81 }); + +// Get singleton component +const Gravity *g = ecs_singleton_get(world, Gravity); +``` +```cpp +// Set singleton component +world.set({ 9.81 }); + +// Get singleton component +const Gravity *g = world.get(); +``` + +Singleton components are created by adding the component to its own entity id. The above code examples are shortcuts for these regular API calls: + +```c +ecs_set(world, ecs_id(Gravity), Gravity, {10, 20}); + +const Gravity *g = ecs_get(world, ecs_id(Gravity), Gravity); +``` +```cpp +flecs::entity grav_e = world.entity(); + +grav_e.set({10, 20}); + +const Gravity *g = grav_e.get(); +``` + +The following examples show how to query for a singleton component: + +```c +// Create query that matches Gravity as singleton +ecs_query_t *q = ecs_query(ecs, { + .filter.terms = { + // Regular component + { .id = ecs_id(Velocity) }, + // A singleton is a component matched on itself + { .id = ecs_id(Gravity), .src.id = ecs_id(Gravity) } + } +}); + +// Create a system using the query DSL with a singleton: +ECS_SYSTEM(world, ApplyGravity, EcsOnUpdate, Velocity, Gravity($)); +``` + +```cpp +world.query_builder() + .term_at(2).singleton() + .build(); +``` + +### Filter +Filters are a kind of uncached query that are cheap to create. This makes them a good fit for scenarios where an application doesn't know in advance what it has to query for, like when finding the children for a parent. The following example shows a simple filter: + +```c +// Initialize a filter with 2 terms on the stack +ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ + .terms = { + { ecs_id(Position) }, + { ecs_pair(EcsChildOf, parent) } + } +}); + +// Iterate the filter results. Because entities are grouped by their type there +// are two loops: an outer loop for the type, and an inner loop for the entities +// for that type. +ecs_iter_t it = ecs_filter_iter(world, f); +while (ecs_filter_next(&it)) { + // Each type has its own set of component arrays + Position *p = ecs_field(&it, Position, 1); + + // Iterate all entities for the type + for (int i = 0; i < it.count; i++) { + printf("%s: {%f, %f}\n", ecs_get_name(world, it.entities[i]), + p[i].x, p[i].y); + } +} + +ecs_filter_fini(f); +``` +```cpp +// For simple queries the each function can be used +world.each([](Position& p, Velocity& v) { // flecs::entity argument is optional + p.x += v.x; + p.y += v.y; +}); + +// More complex filters can first be created, then iterated +auto f = world.filter_builder() + .term(flecs::ChildOf, parent) + .build(); + +// Option 1: each() function that iterates each entity +f.each([](flecs::entity e, Position& p) { + std::cout << e.name() << ": {" << p.x << ", " << p.y << "}" << std::endl; +}); + +// Option 2: iter() function that iterates each archetype +f.iter([](flecs::iter& it, Position *p) { + for (int i : it) { + std::cout << it.entity(i).name() + << ": {" << p[i].x << ", " << p[i].y << "}" << std::endl; + } +}); +``` + +Filters can use operators to exclude components, optionally match components or match one out of a list of components. Additionally filters may contain wildcards for terms which is especially useful when combined with pairs. + +The following example shows a filter that matches all entities with a parent that do not have `Position`: + +```c +ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ + .terms = { + { ecs_pair(EcsChildOf, EcsWildcard) } + { ecs_id(Position), .oper = EcsNot }, + } +}); + +// Iteration code is the same +``` +```cpp +auto f = world.filter_builder<>() + .term(flecs::ChildOf, flecs::Wildcard) + .term().oper(flecs::Not) + .build(); + +// Iteration code is the same +``` + +### Query +Queries are cached versions of filters. They are slower to create than filters, but much faster to iterate since this just means iterating their cache. + +The API for queries is similar to filters: + +```c +// Create a query with 2 terms +ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .filter.terms = { + { ecs_id(Position) }, + { ecs_pair(EcsChildOf, EcsWildcard) } + } +}); + +ecs_iter_t it = ecs_query_iter(world, q); +while (ecs_query_next(&it)) { + // Same as for filters +} +``` +```cpp +// Create a query with two terms +auto q = world.query_builder() + .term(flecs::ChildOf, flecs::Wildcard) + .build(); + +// Iteration is the same as filters +``` + +When using queries, make sure to reuse a query object instead of creating a new one each time you need it. Query creation is expensive, and many of the performance benefits of queries are lost when they are created in loops. + +See the [query manual](Queries.md) for more details. + +### System +A system is a query combined with a callback. Systems can be either ran manually or ran as part of an ECS-managed main loop (see [Pipeline](#pipeline)). The system API looks similar to queries: + +```c +// Option 1, use the ECS_SYSTEM convenience macro +ECS_SYSTEM(world, Move, 0, Position, Velocity); +ecs_run(world, Move, delta_time, NULL); // Run system + +// Option 2, use the ecs_system_init function/ecs_system macro +ecs_entity_t move_sys = ecs_system(world, { + .query.filter.terms = { + {ecs_id(Position)}, + {ecs_id(Velocity)}, + }, + .callback = Move +}); + +ecs_run(world, move_sys, delta_time, NULL); // Run system + +// The callback code (same for both options) +void Move(ecs_iter_t *it) { + Position *p = ecs_field(it, Position, 1); + Velocity *v = ecs_field(it, Velocity, 2); + + for (int i = 0; i < it->count; i++) { + p[i].x += v[i].x * it->delta_time; + p[i].y += v[i].y * it->delta_time; + } +} +``` +```cpp +// Use each() function that iterates each individual entity +auto move_sys = world.system() + .iter([](flecs::iter it, Position *p, Velocity *v) { + for (int i : it) { + p[i].x += v[i].x * it.delta_time(); + p[i].y += v[i].y * it.delta_time(); + } + }); + + // Just like with filters & queries, systems have both the iter() and + // each() methods to iterate entities. + +move_sys.run(); +``` + +Systems are stored as entities with an `EcsSystem` component (`flecs::System` in C++), similar to components. That means that an application can use a system as a regular entity: + +```c +printf("System: %s\n", ecs_get_name(world, move_sys)); +ecs_add(world, move_sys, EcsOnUpdate); +ecs_delete(world, move_sys); +``` +```cpp +std::cout << "System: " << move_sys.name() << std::endl; +move_sys.add(flecs::OnUpdate); +move_sys.destruct(); +``` + +### Pipeline +A pipeline is a list of tags that when matched, produces a list of systems to run. These tags are also referred to as a system "phase". Flecs comes with a default pipeline that has the following phases: + +```c +EcsOnLoad +EcsPostLoad +EcsPreUpdate +EcsOnUpdate +EcsOnValidate +EcsPostUpdate +EcsPreStore +EcsOnStore +``` +```cpp +flecs::OnLoad +flecs::PostLoad +flecs::PreUpdate +flecs::OnUpdate +flecs::OnValidate +flecs::PostUpdate +flecs::PreStore +flecs::OnStore +``` + +When a pipeline is executed, systems are ran in the order of the phases. This makes pipelines and phases the primary mechanism for defining ordering between systems. The following code shows how to assign systems to a pipeline, and how to run the pipeline with the `progress()` function: + +```c +ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); +ECS_SYSTEM(world, Transform, EcsPostUpdate, Position, Transform); +ECS_SYSTEM(world, Render, EcsOnStore, Transform, Mesh); + +ecs_progress(world, 0); // run systems in default pipeline +``` +```cpp +world.system("Move").kind(flecs::OnUpdate).each( ... ); +world.system("Transform").kind(flecs::PostUpdate).each( ... ); +world.system("Render").kind(flecs::OnStore).each( ... ); + +world.progress(); +``` + +Because phases are just tags that are added to systems, applications can use the regular API to add/remove systems to a phase: +```c +ecs_remove_id(world, Move, EcsOnUpdate); +ecs_add_id(world, Move, EcsPostUpdate); +``` +```cpp +move_sys.add(flecs::OnUpdate); +move_sys.remove(flecs::PostUpdate); +``` + +Inside a phase, systems are guaranteed to be ran in their declaration order. + +### Observer +Observers are callbacks that are invoked when one or more events matches the query of an observer. Events can be either user defined or builtin. Examples of builtin events are `OnAdd`, `OnRemove` and `OnSet`. + +When an observer has a query with more than one component, the observer will not be invoked until the entity for which the event is emitted satisfies the entire query. + +An example of an observer with two components: + +```c +ecs_observer(world, { + .filter.terms = { { ecs_id(Position) }, { ecs_id(Velocity) }}, + .event = EcsOnSet, + .callback = OnSetPosition +}); + +// Callback code is same as system + +ecs_entity_t e = ecs_new_id(world); // Doesn't invoke the observer +ecs_set(world, e, Position, {10, 20}); // Doesn't invoke the observer +ecs_set(world, e, Velocity, {1, 2}); // Invokes the observer +ecs_set(world, e, Position, {20, 40}); // Invokes the observer +``` +```cpp +world.observer("OnSetPosition").event(flecs::OnSet).each( ... ); + +auto e = ecs.entity(); // Doesn't invoke the observer +e.set({10, 20}); // Doesn't invoke the observer +e.set({1, 2}); // Invokes the observer +e.set({20, 30}); // Invokes the observer +``` + +### Module +A module is a function that imports and organizes components, systems, triggers, observers, prefabs into the world as reusable units of code. A well designed module has no code that directly relies on code of another module, except for components definitions. All module contents are stored as child entities inside the module scope with the `ChildOf` relationship. The following examples show how to define a module in C and C++: + +```c +// Module header (e.g. MyModule.h) +typedef struct { + float x; + float y; +} Position; + +extern ECS_COMPONENT_DECLARE(Position); + +// The import function name has to follow the convention: Import +void MyModuleImport(ecs_world_t *world); + +// Module source (e.g. MyModule.c) +ECS_COMPONENT_DECLARE(Position); + +void MyModuleImport(ecs_world_t *world) { + ECS_MODULE(world, MyModule); + ECS_COMPONENT_DEFINE(world, Position); +} + +// Import code +ECS_IMPORT(world, MyModule); +``` +```cpp +struct my_module { + my_module(flecs::world& world) { + world.module(); + + // Define components, systems, triggers, ... as usual. They will be + // automatically created inside the scope of the module. + } +}; + +// Import code +world.import(); +``` diff --git a/engine/libs/flecs/docs/Relationships.md b/engine/libs/flecs/docs/Relationships.md new file mode 100644 index 0000000..828be81 --- /dev/null +++ b/engine/libs/flecs/docs/Relationships.md @@ -0,0 +1,1414 @@ +# Relationships +The relationships feature makes it possible to describe entity graphs natively in ECS. Graphs are created by adding and removing relationships from one entity to another entity. See this [blog](https://ajmmertens.medium.com/building-games-in-ecs-with-entity-relationships-657275ba2c6c) for an introduction to entity relationships. + +Adding/removing relationships is similar to adding/removing regular components, with as difference that instead of a single component id, a relationship adds a pair of two things to an entity. In this pair, the first element represents the relationship (e.g. "Eats"), and the second element represents the relationship target (e.g. "Apples"). + +Relationships can be used to describe many things, from hierarchies to inventory systems to trade relationships between players in a game. The following sections go over how to use relationships, and what features they support. + +## Definitions + +| Name | Description | +|--------------|-------------| +| Id | An id that can be added and removed | +| Component | Id with a single element (same as an entity id) | +| Pair | Id with two elements | +| Tag | Component or pair not associated with data | +| Relationship | Used to refer to first element of pair | +| Target | Used to refer to second element of pair | +| Source | Entity to which an id is added | + +## Examples +Make sure to check out the code examples in the repository: + + - [relationships (C)](https://github.com/SanderMertens/flecs/tree/master/examples/c/relationships) + - [relationships (C++)](https://github.com/SanderMertens/flecs/tree/master/examples/cpp/relationships) + +## Introduction +The following code is a simple example that uses relationships: + +```c +ecs_entity_t Likes = ecs_new_id(world); +ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Alice = ecs_new_id(world); + +// Bob Likes Alice +ecs_add_pair(world, Bob, Likes, Alice); + +// Bob Likes Alice no more +ecs_remove_pair(world, Bob, Likes, Alice); +``` +```cpp +auto Likes = world.entity(); +auto Bob = world.entity(); +auto Alice = world.entity(); + +// Bob Likes Alice +Bob.add(Likes, Alice); + +// Bob Likes Alice no more +Bob.remove(Likes, Alice); +``` + +In this example, we refer to `Bob` as the "source", `Likes` as the "relationship" and `Alice` as the "target". A relationship when combined with an target is called a "relationship pair". + +The same relationship can be added multiple times to an entity, as long as its target is different: + +```c +ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Eats = ecs_new_id(world); +ecs_entity_t Apples = ecs_new_id(world); +ecs_entity_t Pears = ecs_new_id(world); + +ecs_add_pair(world, Bob, Eats, Apples); +ecs_add_pair(world, Bob, Eats, Pears); + +ecs_has_pair(world, Bob, Eats, Apples); // true +ecs_has_pair(world, Bob, Eats, Pears); // true +``` +```cpp +auto Bob = world.entity(); +auto Eats = world.entity(); +auto Apples = world.entity(); +auto Pears = world.entity(); + +Bob.add(Eats, Apples); +Bob.add(Eats, Pears); + +Bob.has(Eats, Apples); // true +Bob.has(Eats, Pears); // true +``` + +An application can query for relationships with the `(Relationship, Target)` notation: + +```c +// Find all entities that eat apples +ecs_query_t *q = ecs_query_new(world, "(Eats, Apples)"); + +// Find all entities that eat anything +ecs_query_t *q = ecs_query_new(world, "(Eats, *)"); + +// Or with the ecs_query_init function: +ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .filter.terms = {{ecs_pair(Eats, Apples)}} +}); +``` +```cpp +// Find all entities that eat apples +auto q = world.query("(Eats, Apples)"); + +// Find all entities that eat anything +auto q = world.query("(Eats, *)"); + +// With the query builder API: +auto q = world.query_builder<>() + .term(Eats, Apples) + .build(); + +// Or when using pair types, when both relationship & target are compile time types: +auto q = world.query>(); +``` + +This example just shows a simple relationship query. Relationship queries are much more powerful than this as they provide the ability to match against entity graphs of arbitrary size. For more information on relationship queries see the [query manual](Queries.md). + +## Relationship queries +There are a number of ways an application can query for relationships. The following kinds of queries are available for all (unidirectional) relationships, and are all constant time: + +### Test if entity has a relationship pair +```c +ecs_has_pair(world, Bob, Eats, Apples); +``` +```cpp +Bob.has(Eats, Apples); +``` + +### Test if entity has a relationship wildcard +```c +ecs_has_pair(world, Bob, Eats, EcsWildcard); +``` +```cpp +Bob.has(Eats, flecs::Wildcard); +``` + +### Get parent for entity +```c +ecs_entity_t parent = ecs_get_parent(world, Bob); +``` +```cpp +flecs::entity parent = Bob.parent(); +``` + +### Find first target of a relationship for entity +```c +ecs_entity_t food = ecs_get_target(world, Bob, Eats, 0); +``` +```cpp +flecs::entity food = Bob.target(Eats); +``` + +### Find all targets of a relationship for entity +```c +int32_t index = 0; +while ((food = ecs_get_target(world, Bob, Eats, index ++))) { + // ... +} +``` +```cpp +int32_t index = 0; +while ((food = Bob.target(Eats, index ++))) { + // ... +} +``` + +### Find target of a relationship with component +```c +ecs_entity_t parent = ecs_get_target_for(world, Bob, EcsChildOf, Position); +``` +```cpp +flecs::entity parent = Bob.target_for(flecs::ChildOf); +``` + +### Iterate all pairs for entity +```c +const ecs_type_t *type = ecs_get_type(world, Bob); +for (int i = 0; i < type->count; i ++) { + ecs_id_t id = type->array[i]; + if (ECS_IS_PAIR(id)) { + ecs_entity_t first = ecs_pair_first(world, id); + ecs_entity_t second = ecs_pair_second(world, id); + } +} +``` +```cpp +Bob.each([](flecs::id id) { + if (id.is_pair()) { + flecs::entity first = id.pair().first(); + flecs::entity second = id.pair().second(); + } +}); +``` + +### Find all entities with a pair +```c +ecs_filter_t *f = ecs_filter(world, { + .terms[0] = ecs_pair(Eats, Apples) +}); + +ecs_iter_t it = ecs_filter_iter(world, f); +while (ecs_filter_next(&it)) { + for (int i = 0; i < it.count; i ++) { + // Iterate as usual + } +} + +ecs_filter_fini(f); +``` +```cpp +world.filter_builder() + .term(Eats, Apples) + .build() + .each([](flecs::entity e) { + // Iterate as usual + }); +``` + +### Find all entities with a pair wildcard +```c +ecs_filter_t *f = ecs_filter(world, { + .terms[0] = ecs_pair(Eats, EcsWildcard) +}); + +ecs_iter_t it = ecs_filter_iter(world, f); +while (ecs_filter_next(&it)) { + ecs_id_t pair_id = ecs_field_id(&it, 1); + ecs_entity_t food = ecs_pair_second(world, pair_id); // Apples, ... + for (int i = 0; i < it.count; i ++) { + // Iterate as usual + } +} +ecs_filter_fini(f); +``` +```cpp +world.filter_builder() + .term(Eats, flecs::Wildcard) + .build() + .each([](flecs::iter& it, size_t i) { + flecs::entity food = it.pair(1).second(); // Apples, ... + flecs::entity e = it.entity(i); + // Iterate as usual + }); +``` + +### Iterate all children for a parent +```c +ecs_iter_t it = ecs_children(world, parent); +while (ecs_children_next(&it)) { + for (int i = 0; i < it.count; i ++) { + ecs_entity_t child = it.entities[i]; + // ... + } +} +``` +```cpp +parent.children([](flecs::entity child) { + // ... +}); +``` + +More advanced queries are possible with filters, queries and rules. See the [Queries manual](Queries.md) for more details. + +## Relationship components +Relationship pairs, just like regular component, can be associated with data. To associate data with a relationship pair, at least one of its elements needs to be a component. A pair can be associated with at most one type. To determine which type is associated with a relationship pair, the following rules are followed in order: + +- If neither the first nor second elements are a type, the pair is a tag +- If the first element has the [tag](Relationships.md#tag-property) property, the pair is a tag +- If the first element is a type, the pair type is the first element +- If the second element is a type, the pair type is the second element + +The following examples show how these rules can be used: + +```c +typedef struct { + float x, y; +} Position; + +typedef struct { + float amount; +} Eats; + +// Components +ECS_COMPONENT(world, Position); +ECS_COMPONENT(world, Eats); + +// Tags +ecs_entity_t Likes = ecs_new_id(world); +ecs_entity_t Begin = ecs_new_id(world); +ecs_entity_t End = ecs_new_id(world); +ecs_entity_t Apples = ecs_new_id(world); + +ecs_entity_t e = ecs_new_id(world); + +// Both Likes and Apples are tags, so (Likes, Apples) is a tag +ecs_add_pair(world, e, Likes, Apples); + +// Eats is a type and Apples is a tag, so (Eats, Apples) has type Eats +ecs_set_pair(world, e, Eats, Apples, { .amount = 1 }); + +// Begin is a tags and Position is a type, so (Begin, Position) has type Position +ecs_set_pair_second(world, e, Begin, Position, {0, 0}); +ecs_set_pair_second(world, e, End, Position, {10, 20}); // same for End + +// ChildOf has the Tag property, so even though Position is a type, the pair +// does not assume the Position type +ecs_add_pair(world, e, EcsChildOf, Position); +``` +```cpp +struct Position { + float x, y; +}; + +struct Eats { + float amount; +}; + +// Empty types (types without members) are automatically interpreted as tags +struct Begin { }; +struct End { }; + +// Tags +flecs::entity Likes = world.entity(); +flecs::entity Apples = world.entity(); + +flecs::entity e = world.entity(); + +// Both Likes and Apples are tags, so (Likes, Apples) is a tag +e.add(Likes, Apples); + +// Eats is a type and Apples is a tag, so (Eats, Apples) has type Eats +e.set(Apples, { 1 }); + +// Begin is a tags and Position is a type, so (Begin, Position) has type Position +e.set({0, 0}); +e.set({10, 20}); // Same for End + +// ChildOf has the Tag property, so even though Position is a type, the pair +// does not assume the Position type +e.add(flecs::ChildOf, world.id()); +``` + +### Using relationships to add components multiple times +A limitation of components is that they can only be added once to an entity. Relationships make it possible to get around this limitation, as a component _can_ be added multiple times, as long as the pair is unique. Pairs can be constructed on the fly from new entity identifiers, which means this is possible: + +```c +typedef struct { + float x; + float y; +} Position; + +ecs_entity_t e = ecs_new_id(world); + +ecs_entity_t first = ecs_new_id(world); +ecs_entity_t second = ecs_new_id(world); +ecs_entity_t third = ecs_new_id(world); + +// Add component position 3 times, for 3 different objects +ecs_add_pair(world, e, Position, first, {1, 2}); +ecs_add_pair(world, e, Position, second, {3, 4}); +ecs_add_pair(world, e, Position, third, {5, 6}); +``` +```cpp +struct Position { + float x; + float y; +} + +auto e = world.entity(); + +auto first = world.entity(); +auto second = world.entity(); +auto third = world.entity(); + +// Add component position 3 times, for 3 different objects +e.set(first, {1, 2}); +e.set(second, {3, 4}); +e.set(third, {5, 6}); +``` + +## Relationship wildcards +When querying for relationship pairs, it is often useful to be able to find all instances for a given relationship or target. To accomplish this, an application can use wildcard expressions. Consider the following example, that queries for all entities with a `Likes` relationship: + +```c +ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .filter.terms = { + {ecs_pair(Likes, EcsWildcard)} + } +}); + +ecs_iter_t it = ecs_query_iter(world, q); + +while (ecs_query_next(&it)) { + ecs_id_t id = ecs_field_id(&it, 1); // Obtain pair id + + // Get relationship & target + ecs_entity_t rel = ecs_pair_first(world, id); + ecs_entity_t obj = ecs_pair_second(world, id); + + for (int i = 0; i < it.count; it++) { + printf("entity %d has relationship %s, %s\n", + it.entities[i], + ecs_get_name(world, rel), + ecs_get_name(world, obj)); + } +} +``` +```cpp +auto q = world.query_builder() + .term(Likes, flecs::Wildcard) + .build(); + +q.iter([](flecs::iter& it) { + auto id = it.pair(1); + + for (auto i : it) { + cout << "entity " << it.entity(i) << " has relationship " + << id.first().name() << ", " + << id.second().name() << endl; + } +}); +``` + +Wildcards may appear in query expressions, using the `*` character: + +```c +ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ + .filter.expr = "(Likes, *)" +}); +``` +```cpp +auto q = world.query("(Likes, *)"); +``` + +Wildcards may used for the relationship or target part of a pair, or both: + +```cpp +"(Likes, *)" // Matches all Likes relationships +"(*, Alice)" // Matches all relationships with Alice as target +"(*, *)" // Matches all relationships +``` + +## Inspecting relationships +An application can use pair wildcard expressions to find all instances of a relationship for an entity. The following example shows how to find all `Eats` relationships for an entity: + +```c +// Bob eats apples and pears +ecs_entity_t Eats = ecs_new_entity(world, "Eats"); +ecs_entity_t Apples = ecs_new_entity(world, "Apples"); +ecs_entity_t Pears = ecs_new_entity(world, "Pears"); + +ecs_entity_t Bob = ecs_new_id(world); +ecs_add_pair(world, Bob, Eats, Apples); +ecs_add_pair(world, Bob, Eats, Pears); + +// Find all (Eats, *) relationships in Bob's type +ecs_table_t *bob_table = ecs_get_table(world, Bob); +ecs_type_t bob_type = ecs_get_type(world, Bob); +ecs_id_t wildcard = ecs_pair(Eats, EcsWildcard); +ecs_id_t *ids = ecs_vector_first(bob_type, ecs_id_t); +int32_t cur = -1; + +while (-1 != (cur = ecs_search_offset(world, bob_table, cur + 1, wildcard, 0))){ + ecs_entity_t obj = ecs_pair_second(world, ids[cur]); + printf("Bob eats %s\n", ecs_get_name(world, obj)); +} +``` +```cpp +// Bob eats apples and pears +auto Bob = world.entity(); +auto Eats = world.entity(); +auto Apples = world.entity(); +auto Pears = world.entity(); + +Bob.add(Eats, Apples); +Bob.add(Eats, Pears); + +// Find all (Eats, *) relationships in Bob's type +bob.match(world.pair(Eats, flecs::Wildcard), [](flecs::id id) { + cout << "Bob eats " << id.second().name() << endl; +}); + +// For target wildcard pairs, each() can be used: +bob.each(Eats, [](flecs::entity obj) { + cout << "Bob eats " << obj.name() << endl; +}) +``` + +## Builtin relationships +Flecs comes with a few builtin relationships that have special meaning within the framework. While they are implemented as regular relationships and therefore obey the same rules as any custom relationship, they are used to enhance the features of different parts of the framework. The following two sections describe the builtin relationships of Flecs. + +### The IsA relationship +The `IsA` relationship is a builtin relationship that allows applications to express that one entity is equivalent to another. This relationship is at the core of component sharing and plays a large role in queries. The `IsA` relationship can be used like any other relationship, as is shown here: + +```c +ecs_entity_t Apple = ecs_new_id(world); +ecs_entity_t Fruit = ecs_new_id(world); +ecs_add_pair(world, Apple, EcsIsA, Fruit); +``` +```cpp +auto Apple = world.entity(); +auto Fruit = world.entity(); +Apple.add(flecs::IsA, Fruit); +``` + +In C++, adding an `IsA` relationship has a shortcut: + +```cpp +Apple.is_a(Fruit); +``` + +This indicates to Flecs that an `Apple` is equivalent to a `Fruit` and should be treated as such. This equivalence is one-way, as a `Fruit` is not equivalent to an `Apple`. Another way to think about this is that `IsA` allows an application to express subsets and supersets. An `Apple` is a subset of `Fruit`. `Fruit` is a superset of `Apple`. + +We can also add `IsA` relationships to `Apple`: + +```c +ecs_entity_t GrannySmith = ecs_new_id(world); +ecs_add_pair(world, GrannySmith, EcsIsA, Apple); +``` +```cpp +auto GrannySmith = world.entity(); +GrannySmith.add(flecs::IsA, Apple); +``` + +This specifies that `GrannySmith` is a subset of `Apple`. A key thing to note here is that because `Apple` is a subset of `Fruit`, `GrannySmith` is a subset of `Fruit` as well. This means that if an application were to query for `(IsA, Fruit)` it would both match `Apple` and `GrannySmith`. This property of the `IsA` relationship is called "transitivity" and it is a feature that can be applied to any relationship. See the [section on Transitivity](#transitive-property) for more details. + +#### Component sharing +An entity with an `IsA` relationship to another entity is equivalent to the other entity. So far the examples showed how querying for an `IsA` relationship will find the subsets of the thing that was queried for. In order for entities to be treated as true equivalents though, everything the superset contains (its components, tags, relationships) must also be found on the subsets. Consider: + +```c +ecs_entity_t Spaceship = ecs_new_id(world); +ecs_set(world, Spaceship, MaxSpeed, {100}); +ecs_set(world, SpaceShip, Defense, {50}); + +ecs_entity_t Frigate = ecs_new_id(world); +ecs_add(world, Frigate, EcsIsA, Spaceship); +ecs_set(world, Frigate, Defense, {100}); +``` +```cpp +auto Spaceship = world.entity() + .set({100}) + .set({50}); + +auto Frigate = world.entity() + .is_a(SpaceShip) // shorthand for .add(flecs::IsA, Spaceship) + .set({75}); +``` + +Here, the `Frigate` "inherits" the contents of `SpaceShip`. Even though `MaxSpeed` was never added directly to `Frigate`, an application can do this: + +```c +// Obtain the inherited component from Spaceship +const MaxSpeed *v = ecs_get(world, Frigate, MaxSpeed); +v->value == 100; // true +``` +```cpp +// Obtain the inherited component from Spaceship +const MaxSpeed *v = Frigate.get(); +v->value == 100; // true +``` + +While the `Frigate` entity also inherited the `Defense` component, it overrode this with its own value, so that the following example works: + +```c +// Obtain the overridden component from Frigate +const Defense *v = ecs_get(world, Frigate, Defense); +v->value == 75; // true +``` +```cpp +// Obtain the overridden component from Frigate +const Defense *v = Frigate.get(); +v->value == 75; // true +``` + +The ability to share components is also applied transitively, so `Frigate` could be specialized further into a `FastFrigate`: + +```c +ecs_entity_t FastFrigate = ecs_new_id(world); +ecs_add(world, FastFrigate, EcsIsA, Frigate); +ecs_set(world, FastFrigate, MaxSpeed, {200}); + +// Obtain the overridden component from FastFrigate +const MaxSpeed *s = ecs_get(world, Frigate, MaxSpeed); +s->value == 200; // true + +// Obtain the inherited component from Frigate +const Defense *d = Frigate.get(); +d->value == 75; // true +``` +```cpp +auto FastFrigate = world.entity() + .is_a(Frigate) + .set({200}); + +// Obtain the overridden component from FastFrigate +const MaxSpeed *s = Frigate.get(); +s->value == 200; // true + +// Obtain the inherited component from Frigate +const Defense *d = Frigate.get(); +d->value == 75; // true +``` + +This ability to inherit and override components is one of the key enabling features of Flecs prefabs, and is further explained in the [Inheritance section](Manual.md#Inheritance) of the manual. + +### The ChildOf relationship +The `ChildOf` relationship is the builtin relationship that allows for the creation of entity hierarchies. The following example shows how hierarchies can be created with `ChildOf`: + +```c +ecs_entity_t Spaceship = ecs_new_id(world); +ecs_entity_t Cockpit = ecs_new_id(world); + +ecs_add_pair(world, Cockpit, EcsChildOf, Spaceship); +``` +```cpp +auto Spaceship = world.entity(); +auto Cockpit = world.entity(); + +Cockpit.add(flecs::ChildOf, Spaceship); +``` + +In C++, adding a `ChildOf` relationship has a shortcut: + +```cpp +Cockpit.child_of(Spaceship); +``` + +The `ChildOf` relationship is defined so that when a parent is deleted, its children are also deleted. For more information on specifying cleanup behavior for relationships, see the [Relationship cleanup properties](#cleanup-properties) section. + +The `ChildOf` relationship is defined as a regular relationship in Flecs. There are however a number of features that interact with `ChildOf`. The following sections describe these features. + +#### Namespacing +Entities in flecs can have names, and name lookups can be relative to a parent. Relative name lookups can be used as a namespacing mechanism to prevent clashes between entity names. This example shows a few examples of name lookups in combination with hierarchies: + +```c +// Create two entities with a parent/child name +ecs_entity_t parent = ecs_entity(world, { + .name = "Parent" +}); + +ecs_entity_t child = ecs_entity(world, { + .name = "Child" +}); + +// Create the hierarchy +ecs_add_pair(world, child, EcsChildOf, parent); + +child = ecs_lookup_fullpath(world, "Parent::Child"); // true +child = ecs_lookup_path(world, parent, "Child"); // true +``` +```cpp +auto parent = world.entity("Parent"); +auto child = world.entity("Child") + .child_of(parent); + +child == world.lookup("Parent::Child"); // true +child == parent.lookup("Child"); // true +``` + +#### Scoping +In some scenarios a number of entities all need to be created with the same parent. Rather than adding the relationship to each entity, it is possible to configure the parent as a scope, which ensures that all entities created afterwards are created in the scope. The following example shows how: + +```c +ecs_entity_t parent = ecs_new_id(world); +ecs_entity_t prev = ecs_set_scope(world, parent); + +// Note that we're not using the ecs_new_id function for the children. This +// function only generates a new id, and does not add the scope to the entity. +ecs_entity_t child_a = ecs_new(world, 0); +ecs_entity_t child_b = ecs_new(world, 0); + +// Restore the previous scope +ecs_set_scope(world, prev); + +ecs_has_pair(world, child_a, EcsChildOf, parent); // true +ecs_has_pair(world, child_b, EcsChildOf, parent); // true +``` +```cpp +auto parent = world.entity(); +auto prev = world.set_scope(parent); + +auto child_a = world.entity(); +auto child_b = world.entity(); + +// Restore the previous scope +world.set_scope(prev); + +child_a.has(flecs::ChildOf, parent); // true +child_b.has(flecs::ChildOf, parent); // true +``` + +Scopes in C++ can also be used with the `scope` function on an entity, which accepts a (typically lambda) function: + +```cpp +auto parent = world.entity().scope([&]{ + auto child_a = world.entity(); + auto child_b = world.entity(); + + child_a.has(flecs::ChildOf, parent); // true + child_b.has(flecs::ChildOf, parent); // true +}); +``` + +Scopes are the mechanism that ensure contents of a module are created as children of the module, without having to explicitly add the module as a parent. + +## Cleanup properties +When entities that are used as tags, components, relationships or relationship targets are deleted, cleanup policies ensure that the store does not contain any dangling references. Any cleanup policy provides this guarantee, so while they are configurable, applications cannot configure policies that allows for dangling references. + +**Note**: this only applies to entities (like tags, components, relationships) that are added _to_ other entities. It does not apply to components that store an entity value, so: + +```c +struct MyComponent { + entity e; // not covered by cleanup policies +} +``` +```c +e.add(ChildOf, parent); // covered by cleanup policies +``` + +The default policy is that any references to the entity will be **removed**. For example, when the tag `Archer` is deleted, it will be removed from all entities that have it, which is similar to invoking the `remove_all` operation: + +```c +ecs_remove_all(world, Archer); +``` +```cpp +world.remove_all(Archer); +``` + +Since entities can be used in relationship pairs, just calling `remove_all` on just the entity itself does not guarantee that no dangling references are left. A more comprehensive description of what happens is: + +```c +ecs_remove_all(world, Archer); +ecs_remove_all(world, ecs_pair(Archer, EcsWildcard)); +ecs_remove_all(world, ecs_pair(EcsWildcard, Archer)); +``` +```cpp +world.remove_all(Archer); +world.remove_all(Archer, flecs::Wildcard); +world.remove_all(flecs::Wildcard, Archer); +``` + +This succeeds in removing all possible references to `Archer`. Sometimes this behavior is not what we want however. Consider a parent-child hierarchy, where we want to delete the child entities when the parent is deleted. Instead of removing `(ChildOf, parent)` from all children, we need to _delete_ the children. + +We also want to specify this per relationship. If an entity has `(Likes, parent)` we may not want to delete that entity, meaning the cleanup we want to perform for `Likes` and `ChildOf` may not be the same. + +This is what cleanup policies are for: to specify which action needs to be executed under which condition. They are applied _to_ entities that have a reference to the entity being deleted: if I delete the `Archer` tag I remove the tag _from_ all entities that have it. + +To configure a cleanup policy for an entity, a `(Condition, Action)` pair can be added to it. If no policy is specified, the default cleanup action (`Remove`) is performed. + +There are three cleanup actions: + +- `Remove`: as if doing `remove_all(entity)` (default) +- `Delete`: as if doing `delete_with(entity)` +- `Panic`: throw a fatal error (default for components) + +There are two cleanup conditions: + +- `OnDelete`: the component, tag or relationship is deleted +- `OnDeleteTarget`: a target used with the relationship is deleted + +Policies apply to both regular and pair instances, so to all entities with `T` as well as `(T, *)`. + +### Examples +The following examples show how to use cleanup policies + +**(OnDelete, Remove)** +```c +// Remove Archer from entities when Archer is deleted +ECS_TAG(world, Archer); +ecs_add_pair(world, Archer, EcsOnDelete, EcsRemove); + +ecs_entity_t e = ecs_new_w_id(world, Archer); + +// This will remove Archer from e +ecs_delete(world, Archer); +``` +```cpp +// Remove Archer from entities when Archer is deleted +world.component() + .add(flecs::OnDelete, flecs::Remove); + +auto e = world.entity().add(); + +// This will remove Archer from e +world.component().destruct(); +``` + +**(OnDelete, Delete)** +```c +// Delete entities with Archer when Archer is deleted +ECS_TAG(world, Archer); +ecs_add_pair(world, Archer, EcsOnDelete, EcsDelete); + +ecs_entity_t e = ecs_new_w_id(world, Archer); + +// This will delete e +ecs_delete(world, Archer); +``` +```cpp +// Delete entities with Archer when Archer is deleted +world.component() + .add(flecs::OnDelete, flecs::Delete); + +auto e = world.entity().add(); + +// This will delete e +world.component().destruct(); +``` + +**(OnDeleteTarget, Delete)** +```c +// Delete children when deleting parent +ECS_TAG(world, ChildOf); +ecs_add_pair(world, ChildOf, EcsOnDeleteTarget, EcsDelete); + +ecs_entity_t p = ecs_new_id(world); +ecs_entity_t e = ecs_new_w_pair(world, ChildOf, p); + +// This will delete both p and e +ecs_delete(world, p); +``` +```cpp +// Delete children when deleting parent +world.component() + .add(flecs::OnDeleteTarget, flecs::Delete); + +auto p = world.entity(); +auto e = world.entity().add(p); + +// This will delete both p and e +p.destruct(); +``` + +### Cleanup order +While cleanup actions allow for specifying what needs to happen when a particular entity is deleted, or when an entity used with a particular relationship is deleted, they do not enforce a strict cleanup _order_. The reason for this is that there can be many orderings that satisfy the cleanup policies. + +This is important to consider especially when writing `OnRemove` triggers or hooks, as the order in which they are invoked highly depends on the order in which entities are cleaned up. + +Take an example with a parent and a child that both have the `Node` tag: + +```cpp +world.observer() + .event(flecs::OnRemove) + .each([](flecs::entity e) { }); + +flecs::entity p = world.entity().add(); +flecs::entity c = world.entity().add().child_of(p); +``` + +In this example, when calling `p.destruct()` the observer is first invoked for the child, and then for the parent, which is to be expected as the child is deleted before the parent. Cleanup policies do not however guarantee that this is always the case. + +An application could also call `world.component().destruct()` which would delete the `Node` component and all of its instances. In this scenario the cleanup policies for the `ChildOf` relationship are not considered, and therefore the ordering is undefined. Another typical scenario in which ordering is undefined is when an application has cyclical relationships with a `Delete` cleanup action. + +#### Cleanup order during world teardown +Cleanup issues often show up during world teardown as the ordering in which entities are deleted is controlled by the application. While world teardown respects cleanup policies, there can be many entity delete orderings that are valid according to the cleanup policies, but not all of them are equally useful. There are ways to organize entities that helps world cleanup to do the right thing. These are: + +**Organize components, triggers, observers and systems in modules.** +Storing these entities in modules ensures that they stay alive for as long as possible. This leads to more predictable cleanup ordering as components will be deleted as their entities are, vs. when the component is deleted. It also ensures that triggers and observers are not deleted while matching events are still being generated. + +**Avoid organizing components, triggers, observers and systems under entities that are not modules**. If a non-module entity with children is stored in the root, it will get cleaned up along with other regular entities. If you have entities such as these organized in a non-module scope, consider adding the `EcsModule`/`flecs::Module` tag to the root of that scope. + +The next section goes into more detail on why this improves cleanup behavior and what happens during world teardown. + +#### World teardown sequence +To understand why some ways to organize entities work better than others, having an overview of what happens during world teardown is useful. Here is a list of the steps that happen when a world is deleted: + +1. **Find all root entities** +World teardown starts by finding all root entities, which are entities that do not have the builtin `ChildOf` relationship. Note that empty entities (entities without any components) are not found during this step. + +2. **Filter out modules, components, observers and systems** +This ensures that components are not cleaned up before the entities that use them, and triggers, observers and systems are not cleaned up while there are still conditions under which they could be invoked. + +3. **Filter out entities that have no children** +If entities have no children they cannot cause complex cleanup logic. This also decreases the likelihood of initiating cleanup actions that could impact other entities. + +4. **Delete root entities** +The root entities that were not filtered out will be deleted. + +5. **Delete everything else** +The last step will delete all remaining entities. At this point cleanup policies are no longer considered and cleanup order is undefined. + +## Relationship properties +Relationship properties are tags that can be added to relationships to modify their behavior. + +### Tag property +A relationship can be marked as a tag in which case it will never contain data. By default the data associated with a pair is determined by whether either the relationship or target are components. For some relationships however, even if the target is a component, no data should be added to the relationship. Consider the following example: + +```c +typedef struct { + float x; + float y; +} Position; + +ECS_TAG(world, Serializable); +ECS_COMPONENT(world, Position); + +ecs_entity_t e = ecs_new_id(world); +ecs_set(world, e, Position, {10, 20}); +ecs_add_pair(world, e, Serializable, ecs_id(Position)); + +// Gets value from Position component +const Position *p = ecs_get(world, e, Position); + +// Gets (unintended) value from (Serializable, Position) pair +const Position *p = ecs_get_pair_object(world, e, Serializable, Position); +``` +```cpp +struct Serializable { }; // Tag, contains no data + +struct Position { + float x, y; +}; + +auto e = ecs.entity() + .set({10, 20}) + .add(); // Because Serializable is a tag, the pair + // has a value of type Position + +// Gets value from Position component +const Position *p = e.get(); + +// Gets (unintended) value from (Serializable, Position) pair +const Position *p = e.get(); +``` + +To prevent data from being associated with pairs that can apply to components, the `Tag` property can be added to relationships: + +```c +// Ensure that Serializable never contains data +ecs_add_id(world, Serializable, EcsTag); + +// Because Serializable is marked as a Tag, no data is added for the pair +// even though Position is a component +ecs_add_pair(world, e, Serializable, ecs_id(Position)); + +// This is still OK +const Position *p = ecs_get(world, e, Position); + +// This no longer works, the pair has no data +const Position *p = ecs_get_pair_object(world, e, Serializable, Position); +``` +```cpp +// Ensure that Serializable never contains data +ecs.component() + .add(); + +auto e = ecs.entity() + .set({10, 20}) + .add(); // Because Serializable marked as a Tag, no + // data is added for the pair even though + // Position is a component + +// Gets value from Position component +const Position *p = e.get(); + +// This no longer works, the pair has no data +const Position *p = e.get(); +``` + +The `Tag` property is only interpreted when it is added to the relationship part of a pair. + +### Final property +Entities can be annotated with the `Final` property, which prevents using them with `IsA` relationship. This is similar to the concept of a final class as something that cannot be extended. The following example shows how use `Final`: + +```c +ecs_entity_t e = ecs_new_id(world); +ecs_add_id(world, e, EcsFinal); + +ecs_entity_t i = ecs_new_id(world); +ecs_add_pair(world, e, i, EcsIsA, e); // not allowed +``` +```cpp +auto e = ecs.entity() + .add(flecs::Final); + +auto i = ecs.entity() + .is_a(e); // not allowed +``` + +Queries may use the final property to optimize, as they do not have to explore subsets of a final entity. For more information on how queries interpret final, see the [Query manual](Queries.md). By default, all components are created as final. + +### DontInherit property +The `DontInherit` property prevents inheriting a component from a base entity (`IsA` target). Consider the following example: + +```c +ecs_entity_t TagA = ecs_new_id(world); +ecs_entity_t TagB = ecs_new_id(world); +ecs_add_id(world, TagB, EcsDontInherit); + +ecs_entity_t base = ecs_new_id(world); +ecs_add_id(world, base, TagA); +ecs_add_id(world, base, TagB); + +ecs_entity_t inst = ecs_new_w_pair(world, base); +ecs_has_id(world, inst, TagA); // true +ecs_has_id(world, inst, TagB); // false +``` +```cpp +struct TagA = { }; +struct TagB = { }; + +world.component().add(flecs::DontInherit); + +auto base = world.entity() + .add() + .add(); + +auto inst = world.entity().is_a(base); +inst.has(); // true +inst.has(); // false +``` + +The builtin `Prefab`, `Disabled`, `Identifier` and `ChildOf` tags/relationships are marked as `DontInherit`. + +### AlwaysOverride property +The `AlwaysOverride` property ensures that a component is always automatically overridden when an inheritance (`IsA`) relationship is added. The behavior of this property is as if `OVERRIDE | Component` is always added together with `Component`. + +```c +ECS_COMPONENT(world, Position); +ECS_COMPONENT(world, Velocity); + +ecs_add_id(world, ecs_id(Position), EcsAlwaysOverride); + +ecs_entity_t base = ecs_new_id(world); +ecs_set(world, base, Position, {10, 20}); +ecs_set(world, base, Velocity, {1, 2}); + +ecs_entity_t inst = ecs_new_w_pair(world, base); +ecs_has(world, inst, Position); // true +ecs_has(world, inst, Velocity); // true +ecs_owns(world, inst, Position); // true +ecs_owns(world, inst, Velocity); // false +``` +```cpp +world.component().add(flecs::AlwaysOverride); + +auto base = world.entity() + .set({10, 20}) + .add({1, 2}) + +auto inst = world.entity().is_a(base); +inst.has(); // true +inst.has(); // true +inst.owns(); // true +inst.owns(); // false +``` + +### Transitive property +Relationships can be marked as transitive. A formal-ish definition if transitivity in the context of relationships is: + +``` +If Relationship(EntityA, EntityB) And Relationship(EntityB, EntityC) Then Relationship(EntityA, EntityC) +``` + +What this means becomes more obvious when translated to a real-life example: + +``` +If Manhattan is located in New York, and New York is located in the USA, then Manhattan is located in the USA. +``` + +In this example, `LocatedIn` is the relationship and `Manhattan`, `New York` and `USA` are entities `A`, `B` and `C`. Another common example of transitivity is found in OOP inheritance: + +``` +If a Square is a Rectangle and a Rectangle is a Shape, then a Square is a Shape. +``` + +In this example `IsA` is the relationship and `Square`, `Rectangle` and `Shape` are the entities. + +When relationships in Flecs are marked as transitive, queries can follow the transitive relationship to see if an entity matches. Consider this example dataset: + +```c +ecs_entity_t LocatedIn = ecs_new_id(world); +ecs_entity_t Manhattan = ecs_new_id(world); +ecs_entity_t NewYork = ecs_new_id(world); +ecs_entity_t USA = ecs_new_id(world); + +ecs_add_pair(world, Manhattan, LocatedIn, NewYork); +ecs_add_pair(world, NewYork, LocatedIn, USA); +``` +```cpp +auto LocatedIn = world.entity(); +auto Manhattan = world.entity(); +auto NewYork = world.entity(); +auto USA = world.entity(); + +Manhattan.add(LocatedIn, NewYork); +NewYork.add(LocatedIn, USA); +``` + +If we were now to query for `(LocatedIn, USA)` we would only match `NewYork`, because we never added `(LocatedIn, USA)` to `Manhattan`. To make sure queries `Manhattan` as well we have to make the `LocatedIn` relationship transitive. We can simply do this by adding the transitive property to the relationship entity: + +```c +ecs_add_id(world, LocatedIn, Transitive); +``` +```cpp +LocatedIn.add(flecs::Transitive); +``` + +When now querying for `(LocatedIn, USA)`, the query will follow the `LocatedIn` relationship and return both `NewYork` and `Manhattan`. For more details on how queries use transitivity, see the [Transitive Relationships section in the query manual](Queries.md#transitive-relationships). + +### Reflexive property +A relationship can be marked reflexive which means that a query like `Relationship(Entity, Entity)` should evaluate to true. The utility of `Reflexive` becomes more obvious with an example: + +Given this dataset: +``` +IsA(Oak, Tree) +``` + +we can ask whether an oak is a tree: +``` +IsA(Oak, Tree) +- Yes, an Oak is a tree (Oak has (IsA, Tree)) +``` + +We can also ask whether a tree is a tree, which it obviously is: +``` +IsA(Tree, Tree) +- Yes, even though Tree does not have (IsA, Tree) +``` + +However, this does not apply to all relationships. Consider a dataset with a `LocatedIn` relationship: + +``` +LocatedIn(SanFrancisco, UnitedStates) +``` + +we can now ask whether SanFrancisco is located in SanFrancisco, which it is not: +``` +LocatedIn(SanFrancisco, SanFrancisco) +- No +``` + +In these examples, `IsA` is a reflexive relationship, whereas `LocatedIn` is not. + +### Acyclic property +A relationship can be marked with the `Acyclic` property to indicate that it cannot contain cycles. Both the builtin `ChildOf` and `IsA` relationships are marked acyclic. Knowing whether a relationship is acyclic allows the storage to detect and throw errors when a cyclic relationship is introduced by accident. + +Note that because cycle detection requires expensive algorithms, adding `Acyclic` to a relationship does not guarantee that an error will be thrown when a cycle is accidentally introduced. While detection may improve over time, an application that runs without errors is no guarantee that it does not contain acyclic relationships with cycles. + +### Traversable property +Traversable relationships are allowed to be traversed automatically by queries, for example using the `up` bitflag (upwards traversal, see [query traversal flags](Queries.md#traversal-flags)). Traversable relationships are also marked as `Acyclic`, which ensures a query won't accidentally attempt to traverse a relationship that contains cycles. + +Events are propagated along the edges of traversable relationships. A typical example of this is when a component value is changed on a prefab. The event of this change will be propagated by traversing the `IsA` relationship downwards, for all instances of the prefab. Event propagation does not happen for relationships that are not marked with `Traversable`. + +### Exclusive property +The `Exclusive` property enforces that an entity can have only a single instance of a relationship. When a second instance is added, it replaces the first instance. An example of a relationship with the `Exclusive` property is the builtin `ChildOf` relationship: + +```c +ecs_add_pair(world, child, EcsChildOf, parent_a); +ecs_add_pair(world, child, EcsChildOf, parent_b); // replaces (ChildOf, parent_a) +``` +```cpp +e.child_of(parent_a); +e.child_of(parent_b); // replaces (ChildOf, parent_a) +``` + +To create a custom exclusive relationship, add the `Exclusive` property: +```c +ecs_entity_t MarriedTo = ecs_new_id(world); +ecs_add_id(world, MarriedTo, EcsExclusive); +``` +```cpp +flecs::entity MarriedTo = world.entity() + .add(flecs::Exclusive); +``` + +### Union property +The `Union` is similar to `Exclusive` in that it enforces that an entity can have only a single instance of a relationship. The difference between `Exclusive` and `Union` is that `Union` combines different relationship targets in a single table. This reduces table fragmentation, and as a result speeds up add/remove operations. This increase in add/remove speed does come at a cost: iterating a query with union terms is more expensive than iterating a regular relationship. + +The API for using the `Union` property is similar to regular relationships, as this example shows: + +```c +ecs_entity_t Movement = ecs_new_id(world); +ecs_add_id(world, Movement, EcsUnion); + +ecs_entity_t Walking = ecs_new_id(world); +ecs_entity_t Running = ecs_new_id(world); + +ecs_entity_t e = ecs_new_id(world); +ecs_add_pair(world, e, Movement, Running); +ecs_add_pair(world, e, Movement, Walking); // replaces (Movement, Running) +``` +```cpp +flecs::entity Movement = world.entity().add(flecs::Union); +flecs::entity Walking = world.entity(); +flecs::entity Running = world.entity(); + +flecs::entity e = world.entity().add(Movement, Running); +e.add(Movement, Walking); // replaces (Movement, Running) +``` + +When compared to regular relationships, union relationships have some differences and limitations: +- Relationship cleanup does not work yet for union relationships +- Removing a union relationship removes any target, even if the specified target is different +- Filters and rules do not support union relationships +- Union relationships cannot have data +- Union relationship query terms can use only the `And` operator +- Queries with a `(R, *)` term will return `(R, *)` as term id for each entity + +### Symmetric property +The `Symmetric` property enforces that when a relationship `(R, Y)` is added to entity `X`, the relationship `(R, X)` will be added to entity `Y`. The reverse is also true, if relationship `(R, Y)` is removed from `X`, relationship `(R, X)` will be removed from `Y`. + +The symmetric property is useful for relationships that do not make sense unless they are bidirectional. Examples of such relationships are `AlliesWith`, `MarriedTo`, `TradingWith` and so on. An example: + +```c +ecs_entity_t MarriedTo = ecs_new_w_id(world, EcsSymmetric); +ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Alice = ecs_new_id(world); +ecs_add_pair(world, Bob, MarriedTo, Alice); // Also adds (MarriedTo, Bob) to Alice +``` +```cpp +auto MarriedTo = world.entity().add(flecs::Symmetric); +auto Bob = ecs.entity(); +auto Alice = ecs.entity(); +Bob.add(MarriedTo, Alice); // Also adds (MarriedTo, Bob) to Alice +``` + +### With property +The `With` relationship can be added to components to indicate that it must always come together with another component. The following example shows how `With` can be used with regular components/tags: + +```c +ecs_entity_t Responsibility = ecs_new_id(world); +ecs_entity_t Power = ecs_new_w_pair(world, EcsWith, Responsibility); + +// Create new entity that has both Power and Responsibility +ecs_entity_t e = ecs_new_w_id(world, Power); +``` +```cpp +auto Responsibility = world.entity(); +auto Power = world.entity().add(flecs::With, Responsibility); + +// Create new entity that has both Power and Responsibility +auto e = world.entity().add(Power); +``` + +When the `With` relationship is added to a relationship, the additional id added to the entity will be a relationship pair as well, with the same target as the original relationship: + +```c +ecs_entity_t Likes = ecs_new_id(world); +ecs_entity_t Loves = ecs_new_w_pair(world, EcsWith, Likes); +ecs_entity_t Pears = ecs_new_id(world); + +// Create new entity with both (Loves, Pears) and (Likes, Pears) +ecs_entity_t e = ecs_new_w_pair(world, Loves, Pears); +``` +```cpp +auto Likes = world.entity(); +auto Loves = world.entity().add(flecs::With, Likes); +auto Pears = world.entity(); + +// Create new entity with both (Loves, Pears) and (Likes, Pears) +auto e = world.entity().add(Loves, Pears); +``` + +### OneOf property +The `OneOf` property enforces that the target of the relationship is a child of a specified entity. `OneOf` can be used to indicate that the target needs to be either a child of the relationship (common for enum relationships), or of another entity. + +The following example shows how to constrain the relationship target to a child of the relationship: + +```c +ecs_entity_t Food = ecs_new_id(world); + +// Enforce that target of relationship is child of Food +ecs_add_id(world, Food, EcsOneOf); + +ecs_entity_t Apples = ecs_new_w_pair(world, EcsChildOf, Food); +ecs_entity_t Fork = ecs_new_id(world); + +// This is ok, Apples is a child of Food +ecs_entity_t a = ecs_new_w_pair(world, Food, Apples); + +// This is not ok, Fork is not a child of Food +ecs_entity_t b = ecs_new_w_pair(world, Food, Fork); +``` +```cpp +// Enforce that target of relationship is child of Food +auto Food = world.entity().add(flecs::OneOf); +auto Apples = world.entity().child_of(Food); +auto Fork = world.entity(); + +// This is ok, Apples is a child of Food +auto a = world.entity().add(Food, Apples); + +// This is not ok, Fork is not a child of Food +auto b = world.entity().add(Food, Fork); +``` + +The following example shows how `OneOf` can be used to enforce that the relationship target is the child of an entity other than the relationship: + +```c +ecs_entity_t Food = ecs_new_id(world); +ecs_entity_t Eats = ecs_new_id(world); + +// Enforce that target of relationship is child of Food +ecs_add_pair(world, Eats, EcsOneOf, Food); + +ecs_entity_t Apples = ecs_new_w_pair(world, EcsChildOf, Food); +ecs_entity_t Fork = ecs_new_id(world); + +// This is ok, Apples is a child of Food +ecs_entity_t a = ecs_new_w_pair(world, Eats, Apples); + +// This is not ok, Fork is not a child of Food +ecs_entity_t b = ecs_new_w_pair(world, Eats, Fork); +``` +```cpp +// Enforce that target of relationship is child of Food +auto Food = world.entity(); +auto Eats = world.entity().add(flecs::OneOf, Food); +auto Apples = world.entity().child_of(Food); +auto Fork = world.entity(); + +// This is ok, Apples is a child of Food +auto a = world.entity().add(Eats, Apples); + +// This is not ok, Fork is not a child of Food +auto b = world.entity().add(Eats, Fork); +``` + +## Relationship performance +This section goes over the performance implications of using relationships. + +### Introduction +The ECS storage needs to know two things in order to store components for entities: + +- Which ids are associated with an entity +- Which types are associated with those ids + +Ids represent anything that can be added to an entity. An id that is not associated with a type is called a tag. An id associated with a type is a component. For regular components, the id is a regular entity that has the builtin `Component` component. This component contains the information needed by the storage to associate the entity with a type. If an entity does not have the `Component` component, it is a tag. + +### Storing relationships +Relationships do not fundamentally change or extend the capabilities of the storage. Relationship pairs are two elements encoded into a single 64-bit id, which means that on the storage level they are treated the same way as regular component ids. What changes is the function that determines which type is associated with an id. For regular components this is simply a check on whether an entity has `Component`. To support relationships, [new rules](#relationship-components) are added to determine the type of an id. + +Because of this, adding/removing relationships to entities has the same performance as adding/removing regular components. This becomes more obvious when looking more closely at a function that adds a relationship pair. The following example shows how the function that adds a regular component and the function that adds a pair actually map to the same functions: + +```c +// Component +ECS_COMPONENT(world, Position); + +// Tags +ecs_entity_t Likes = ecs_new_id(world); +ecs_entity_t Apples = ecs_new_id(world); +ecs_entity_t Npc = ecs_new_id(world); + +ecs_entity_t e = ecs_new_id(world); + +// The ecs_add_id function can be used to add one id to another +ecs_add_id(world, e, Npc); + +// The ecs_add function is translated into an ecs_add_id function: +ecs_add(world, e, Position); +ecs_add_id(world, e, ecs_id(Position)); + +// The ecs_add_pair function is translated into an ecs_add_id function: +ecs_add_pair(world, e, Likes, Apples); +ecs_add_id(world, e, ecs_pair(Likes, Apples)); + +// ecs_pair() translates two ids into a single 64bit pair id. +``` + +This example also applies to C++, as the C++ API maps to the same C API functions. + +While most of the storage uses the same code paths for regular components and relationships, there are a few properties of the storage that can impact performance when using relationships. These properties are not unique to relationships, but are more likely to be significant when using relationships. + +### Id ranges +Flecs reserves entity ids under a threshold (`FLECS_HI_COMPONENT_ID`, default is 256) for components. This low id range is used by the storage to more efficiently encode graph edges between tables. Graph edges for components with low ids use direct array indexing, whereas graph edges for high ids use a hashmap. Graph edges are used to find the next archetype when adding/removing component ids, and are a contributing factor to the performance overhead of add/remove operations. + +Because of the way pair ids are encoded, a pair will never be in the low id range. This means that adding/removing a pair id always uses a hashmap to find the next archetype. This introduces a small overhead, which is usually 5-10% of the total cost of an operation. + +### Fragmentation +Fragmentation is a property of archetype-based ECS implementations where entities are spread out over more tables as the number of different component combinations increases. The overhead of fragmentation is visible in two areas: + +- Table creation +- Queries (queries have to match & iterate more tables) + +Applications that make extensive use of relationships might observe high levels of fragmentation, as relationships can introduce many different combinations of components. While the Flecs storage is optimized for supporting large amounts (hundreds of thousands) of tables, fragmentation is a factor to consider when using relationships. + +Fragmentation can be reduced by using [union relationships](#union-property). There are additional storage improvements on the roadmap that will decrease the overhead of fragmentation introduced by relationships. + +### Relationship flattening +To reduce fragmentation, a relationship can be flattened by calling the `ecs_flatten` operation. This combines entities from different relationship targets in the same table, provided that they otherwise have the same components. The operation is recursive, meaning that for a specified relationship, the entire subtree is flattened. + +Flattening a subtree can be done with or without preserving depth information of the original tree. Preserving depth information ensures that features like `cascade` still work as usual, whereas not preserving depth information further decreases fragmentation. + +The flattening operation by default removes names of the flattened entities. This behavior can be overridden with an option. When flattening for the builtin `ChildOf` relationship, flattening with preservation of names can cause the operation to fail, if entities from different subtrees have the same name. + +After the flatten operation, entities for the same target are stored in contiguous slices in the new table. The new table will have a `(Target, Relationship)` pair, which contains data about the original target, and the start and number of entities in the contiguous block. This information is used by queries when iterating to ensure entities for the same target can still be iterated in bulk. + +Relationship flattening is an experimental feature, and some limitations apply for the current implementation. After a subtree has been flattened, the entities in that subtree can no longer be individually deleted from a target, and cannot be moved to another parent. Additionally, no components can be added/removed to entities in a flattened subtree. Relationship flattening is currently supported only for exclusive, acyclic relationships. + +For additional information, see the API documentation for the `ecs_flatten` operation. + +### Table Creation +When an id added to an entity is deleted, all references to that id are deleted from the storage (see [cleanup properties](#cleanup-properties)). For example, when the component `Position` is deleted it is removed from all entities, and all tables with the `Position` component are deleted. While not unique to relationships, it is more common for relationships to trigger cleanup actions, as relationship pairs contain regular entities. + +The opposite is also true. Because relationship pairs can contain regular entities which can be created on the fly, table creation is more common than in applications that do not use relationships. While Flecs is optimized for fast table creation, creating and cleaning up tables is inherently more expensive than creating/deleting an entity. Therefore table creation is a factor to consider, especially for applications that make extensive use of relationships. + +### Indexing +To improve the speed of evaluating queries, Flecs has indices that store all tables for a given component id. Whenever a new table is created, it is registered with the indices for the ids the table has, including ids for relationship pairs. + +While registering a table for a relationship index is not more expensive than registering a table for a regular index, a table with relationships has to also register itself with the appropriate wildcard indices for its relationships. For example, a table with relationship `(Likes, Apples)` registers itself with the `(Likes, Apples)`, `(Likes, *)`, `(*, Apples)` and `(*, *)` indices. For this reason, creating new tables with relationships has a higher overhead than a table without relationships. + +### Wildcard Queries +A wildcard query for a relationship pair, like `(Likes, *)` may return multiple results for each instance of the relationship. To find all instances of a relationship, the table index (see previous section) stores two additional pieces of information: + +- The `column`: At which offset in the table type does the id first occur +- The `count`: How many occurrences of the id does the table have + +If the id is not a wildcard id, the number of occurrences will always be one. When the id is a wildcard, a table type may have multiple occurrences of a relationship. For wildcard queries in the form of `(Likes, *)`, finding all occurrences is cheap, as a query can start at the `column` and iterate the next `count` members. + +For wildcard queries in the form of `(*, Apples)`, however, the pair ids are not stored contiguously in a table type. This means that if a table has multiple instances that match `(*, Apples)`, a query will have to perform a linear search starting from `column`. Once the query has found `count` occurrences, it can stop searching. + +The following example of a table type shows how relationships are ordered, and demonstrates why `(Likes, *)` wildcards are easier to resolve than `(*, Apples)` wildcards: + +``` +Npc, (Likes, Apples), (Likes, Pears), (Likes, Bananas), (Eats, Apples), (Eats, Pears) +``` + +The index for `(Likes, *)` will have `column=1, count=3`, whereas the index for `(*, Pears)` will have `column=2, count=2`. To find all occurrences of `(Likes, *)` a query can start iteration at index 1 and iterate 3 elements. To find all instances of `(*, Pears)` a query has to start at index 2 and scan until the second instance is found. diff --git a/engine/libs/flecs/docs/RestApi.md b/engine/libs/flecs/docs/RestApi.md new file mode 100644 index 0000000..3ffb706 --- /dev/null +++ b/engine/libs/flecs/docs/RestApi.md @@ -0,0 +1,355 @@ +# REST API +This document provides an overview of the REST API. The Flecs REST API enables (web) clients to inspect the contents of the ECS store, by remotely running queries and requesting entities. + +## Enable the REST API +The REST API can be enabled in an application by instantiating the `EcsRest`/`flecs::Rest` component: + +```c +// Start REST API with default parameters +ecs_singleton_set(world, EcsRest, {0}); +``` +```cpp +// Start REST API with default parameters +world.set({}); +``` + +When an application uses the app addon [FLECS_APP](https://www.flecs.dev/flecs/group__c__addons__app.html) the REST interface can be enabled like this: + +```c +// Start application main loop, enable REST interface +ecs_app_run(world, &(ecs_app_desc_t){ + .enable_rest = true +}); +``` +```cpp +// Start application main loop, enable REST interface +world.app() + .enable_rest() + .run(); +``` + +To test if the REST API is working, navigate to http://localhost:27750/entity/flecs/core/World. Upon success this request should return a reply that looks like: +```json +{"path":"World", "ids":[["flecs.rest.Rest"], ["flecs.core.Identifier", "flecs.core.Name"], ["flecs.core.Identifier", "flecs.core.Symbol"], ["flecs.core.ChildOf", "flecs.core"], ["flecs.doc.Description", "flecs.core.Name"], ["flecs.doc.Description", "flecs.doc.Brief"]]} +``` + +When the monitor module is imported, the REST API provides a `stats` endpoint with statistics for different time intervals: + +```c +// Import monitor addon +ECS_IMPORT(world, FlecsMonitor); +``` +```cpp +world.import(); +``` + +When an application uses the app addon [FLECS_APP](https://www.flecs.dev/flecs/group__c__addons__app.html) the monitoring can be enabled like this: +```c +// Start application main loop, enable REST interface and monitoring +ecs_app_run(world, &(ecs_app_desc_t){ + .enable_rest = true, + .enable_monitor = true +}); +``` +```cpp +// Start application main loop, enable REST interface and monitoring +world.app() + .enable_rest() + .enable_monitor() + .run(); +``` + +For the full C/C++ API reference [see the REST addon documentation](https://www.flecs.dev/flecs/group__c__addons__rest.html). + +## Explorer +An application with REST enabled can be remotely monitored with the [Flecs Explorer](https://github.com/flecs-hub/explorer). When the application is running with REST enabled, navigate to https://flecs.dev/explorer. This should connect the Explorer to the application. + +When the connection is successful, the Explorer should look similar to this: + +![Remote Explorer](img/explorer-remote.png) + +The remote icon next to the title should be visible. If the connection is not successful it could be that the explorer did not receive a response fast enough. To force the explorer to connect remotely, add `?remote=true` to the request: https://flecs.dev/explorer?remote=true. + +If connection issues persist, the browser could be preventing connections to a local application. See the [Explorer repository README](https://github.com/flecs-hub/explorer) for more information on how to workaround this and other issues. + +**Tip**: To show the application title in the explorer, pass the command line arguments to `ecs_init`/`flecs::world::world`: + +```c +ecs_world_t *world = ecs_init_w_args(argc, argv); +``` +```c++ +flecs::world world(argc, argc); +``` + +### Queries +The search bar in the explorer makes it possible to directly query the ECS storage. A query can be entered in the "Search" box, and needs to be specified in the query DSL (see [query manual](Queries.md#query-dsl)). An example: + +![Remote Explorer](img/explorer-query.png) + +The default query engine used by the search bar is the rules engine, which means it is possible to use features like query variables: + +![Remote Explorer](img/explorer-rules.png) + +It is also possible to inspect the results of existing queries. This can be done by entering a `?-`, followed by the name of the query. Queries can be given a name by setting the `.entity` field to a named entity in C: + +```c +ecs_query_t *q = ecs_query(world, { + .entity = ecs_entity(world, { .name = "Move" }), + .filter.terms = { + { ecs_id(Position) }, + { ecs_id(Velocity) }, + } +}); +``` + +In C++ a name can be provided to the query factory method: + +```cpp +auto q = world.query("Move"); +``` + +This also works for filters and rules. Because systems are queries, the name of a system can be entered to inspect the entities matched by the system: + +![Remote Explorer](img/explorer-system.png) + +When a named rule query has variables, variables can be optionally provided as arguments to the query. The following example provides the value `Apples` to the query variable `food` for query `eats_query`, which constrains the results to only show results with `Apples`: + +![Remote Explorer](img/explorer-arguments.png) + +The underlying query that was used for this screenshot was created like this: + +```c +ecs_rule(world, { + .entity = ecs_entity(world, { .name = "eats_query" }), + .expr = "(Eats, $food)" +}); +``` + +## Endpoints +This section describes the endpoints of the REST API. + +### entity +``` +GET /entity/ +``` +The entity endpoint requests data from an entity. The path is the entity path or name of the entity to query for. The reply is formatted according to the [JSON serializer Entity](JsonFormat.md#entity) type. + +The following parameters can be provided to the endpoint: + +#### path +Add path (name) for entity. + +**Default**: true + +#### label +Add label (from doc framework) for entity. + +**Default**: false + +#### brief +Add brief description (from doc framework) for entity. + +**Default**: false + +#### link +Add link (from doc framework) for entity. + +**Default**: false + +#### id_labels +Add labels (from doc framework) for (component) ids. + +**Default**: false + +#### base +Add base components. + +**Default**: true + +#### values +Add component values. + +**Default**: false + +#### private +Add private components. + +**Default**: false + +#### type_info +Add reflection data for component types. + +**Default**: false + +#### alerts +Add active alerts for entity. + +**Default**: false + +#### Example +``` +/entity/my_entity +/entity/my_entity?values=true +/entity/parent/child +``` + +### enable +``` +PUT /enable/ +``` +The enable endpoint enables the entity specified by the path by removing the `flecs.core.Disabled` component. If the entity does not have the `flecs.core.Disabled` component, the endpoint has no side effects. + +### disable +``` +PUT /disable/ +``` +The disable endpoint disables the entity specified by the path by adding the `flecs.core.Disabled` component. If the entity already has the `flecs.core.Disabled` component, the endpoint has no side effects. + +### delete +``` +PUT /delete/ +``` +The delete endpoint deletes the entity specified by the path. + +### set +``` +PUT /set/&data={...} +``` +The set endpoint sets components for the entity specified by the path. The data argument uses the same layout as the `ids` and `values` fields produced by the `ecs_entity_to_json` function. An example: + +```json +{ + "ids": [["Position"], ["Velocity"], ["Eats", "Apples"]], + "values": [ + {"x": 10, "y": 20}, + {"x": 1, "y": 1}, + {"amount": 3} + ] +} +``` + +### query +``` +GET /query?q= +``` +The query endpoint requests data for a query. The implementation uses the +rules query engine. The reply is formatted as an [JSON serializer Iterator](JsonFormat.md#iterator) type. + +The following parameters can be provided to the endpoint: + +#### name +Specify the name of an existing query or system. + +#### offset +Skip the first _offset_ number of entities. + +**Default**: 0 + +#### limit +Return at most _limit_ number of entities. + +**Default**: 100 + +#### term_ids +Add top-level "ids" array with components as specified by query. + +**Default**: false + +#### term_labels +Add top-level "id_labels" array with doc names of components as specified by query. + +**Default**: false + +#### ids +Add result-specific "ids" array with components as matched. Can be different from top-level "ids" array for queries with wildcards. + +**Default**: false + +#### id_labels +Add result-specific "id_labels" array with doc names of components as matched. Can be different from top-level "term_labels" array for queries with wildcards. + +**Default**: false + +#### sources +Add result-specific "sources" array with component source. A 0 element indicates the component is matched on the current (This) entity. + +**Default**: false + +#### variables +Add result-specific "variables" array with values for variables, if any. + +**Default**: true + +#### is_set +Add result-specific "is_set" array with boolean elements indicating whether component was matched (used for optional terms). + +**Default**: false + +#### values +Add result-specific "values" array with component values. A 0 element indicates a component that could not be serialized, which can be either +because no reflection data was registered, because the component has no +data, or because the query didn't request it. + +**Default**: false + +#### entities +Add result-specific "entities" array with matched entities. + +**Default**: true + +#### entity_labels +Include doc name for entities. + +**Default**: false + +#### entity_ids +Include numerical ids for entities. + +**Default**: false + +#### variable_labels +Include doc name for variables. + +**Default**: false + +#### variable_ids +Include numerical ids for variables. + +**Default**: false + +#### duration +Include measurement on how long it took to serialize result. + +**Default**: false + +#### type_info +Add top-level "type_info" array with reflection data on the type in +the query. If a query element has a component that has no reflection +data, a 0 element is added to the array. + +**Default**: false + +#### Example: +``` +/query?q=Position +/query?q=Position&values=true +/query?q=Position%2CVelocity +/query?name=systems.Move +``` + +### stats +``` +GET /stats// +``` +The stats endpoint returns statistics for a specified category or period. This endpoint requires the monitor module to be imported (see above). The supported categories are: + +- `world` +- `pipeline` + +The supported periods are: + +- 1s +- 1m +- 1h +- 1d +- 1w diff --git a/engine/libs/flecs/docs/Systems.md b/engine/libs/flecs/docs/Systems.md new file mode 100644 index 0000000..1780f2e --- /dev/null +++ b/engine/libs/flecs/docs/Systems.md @@ -0,0 +1,875 @@ +# Systems +Systems are queries + a function that can be ran manually or get scheduled as part of a pipeline. To use systems, applications must build Flecs with the `FLECS_SYSTEM` addon (enabled by default). + +An example of a simple system: + +```c +// System implementation +void Move(ecs_iter_t *it) { + // Get fields from system query + Position *p = ecs_field(it, Position, 1); + Velocity *v = ecs_field(it, Velocity, 2); + + // Iterate matched entities + for (int i = 0; i < it->count, i++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} + +// System declaration +ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity); +``` +```cpp +// System declaration +flecs::system sys = world.system("Move") + .each([](Position& p, const Velocity &v) { + // Each is invoked for each entity + p.x += v.x; + p.y += v.y; + }); +``` + +In C, a system can also be created with the `ecs_system_init` function / `ecs_system` shorthand which provides more flexibility. The same system can be created like this: + +```c +ecs_entity_t ecs_id(Move) = ecs_system(world, { + .entity = ecs_entity(world, { /* ecs_entity_desc_t */ + .name = "Move", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .query.filter.terms = { /* ecs_filter_desc_t::terms */ + { ecs_id(Position) }, + { ecs_id(Velocity), .inout = EcsIn } + } + .callback = Move +}) +``` + +To manually run a system, do: + +```c +ecs_run(world, ecs_id(Move), 0.0 /* delta_time */, NULL /* param */) +``` +```cpp +sys.run(); +``` + +By default systems are registered for a pipeline which orders systems by their "phase" (`EcsOnUpdate`). To run all systems in a pipeline, do: + +```c +ecs_progress(world, 0 /* delta_time */); +``` +```cpp +world.progress(); +``` + +To run systems as part of a pipeline, applications must build Flecs with the `FLECS_PIPELINE` addon (enabled by default). To prevent a system from being registered as part of a pipeline, specify 0 as phase: + +```c +ECS_SYSTEM(world, Move, 0, Position, [in] Velocity); +``` +```cpp +flecs::system sys = world.system("Move") + .kind(0) + .each([](Position& p, const Velocity &v) { /* ... */ }); +``` + +## System Iteration +Because systems use queries, the iterating code looks similar: + +```c +// Query iteration +ecs_iter_t it = ecs_query_iter(world, q); + +// Iterate tables matched by query +while (ecs_query_next(&it)) { + // Get fields from query + Position *p = ecs_field(it, Position, 1); + Velocity *v = ecs_field(it, Velocity, 2); + + // Iterate matched entities + for (int i = 0; i < it->count, i++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} + +// System code +void Move(ecs_iter_t *it) { + // Get fields from system query + Position *p = ecs_field(it, Position, 1); + Velocity *v = ecs_field(it, Velocity, 2); + + // Iterate matched entities + for (int i = 0; i < it->count, i++) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +} +``` + +Note how query iteration has an outer and an inner loop, whereas system iteration only has the inner loop. The outer loop for systems is iterated by the `ecs_run` function, which invokes the system function. When running a pipeline, this means that a system callback can be invoked multiple times per frame, once for each matched table. + +In C++ system and query iteration uses the same `each`/`iter` functions: + +```cpp +// Query iteration (each) +q.each([](Position& p, const Velocity &v) { /* ... */ }); + +// System iteration (each) +world.system("Move") + .each([](Position& p, const Velocity &v) { /* ... */ }); +``` +```cpp +// Query iteration (iter) +q.iter([](flecs::iter& it, Position *p, const Velocity *v) { + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } +}); + +// System iteration (iter) +world.system("Move") + .iter([](flecs::iter& it, Position *p, const Velocity *v) { + for (auto i : it) { + p[i].x += v[i].x; + p[i].y += v[i].y; + } + }); +``` + +Just like in the C API, the `iter` function can be invoked multiple times per frame, once for each matched table. The `each` function is called once per matched entity. + +Note that there is no significant performance difference between `iter` and `each`, which can both be vectorized by the compiler. By default `each` can actually end up being faster, as it is instanced (see [query manual](Queries.md#each-c)). + +## Using delta_time +A system provides a `delta_time` which contains the time passed since the last frame: + +```c +Position *p = ecs_field(it, Position, 1); +Velocity *v = ecs_field(it, Velocity, 2); + +for (int i = 0; i < it->count, i++) { + p[i].x += v[i].x * it->delta_time; + p[i].y += v[i].y * it->delta_time; +} +``` +```cpp +world.system("Move") + .iter([](flecs::iter& it, size_t, Position& p, const Velocity &v) { + p.x += v.x * it.delta_time(); + p.y += v.y * it.delta_time(); + }); + +world.system("Move") + .iter([](flecs::iter& it, Position *p, const Velocity *v) { + for (auto i : it) { + p[i].x += v[i].x * it.delta_time(); + p[i].y += v[i].y * it.delta_time(); + } + }); +``` + +This is the value passed into `ecs_progress`: + +```c +ecs_progress(world, delta_time); +``` +```cpp +world.progress(delta_time); +``` + +Passing a value for `delta_time` is useful if you're running `progress()` from within an existing game loop that already has time management. Providing 0 for the argument, or omitting it in the C++ API will cause `progress()` to measure the time since the last call and use that as value for `delta_time`: + +```c +ecs_progress(world, 0); +``` +```cpp +world.progress(); +``` + +A system may also use `delta_system_time`, which is the time elapsed since the last time the system was invoked. This can be useful when a system is not invoked each frame, for example when using a timer. + +## Tasks +A task is a system that matches no entities. Tasks are ran once per frame, and are useful for running code that is not related to entities. An example of a task system: + +```c +// System function +void PrintTime(ecs_iter_t *it) { + printf("Time: %f\n", it->delta_time); +} + +// System declaration using the ECS_SYSTEM macro +ECS_SYSTEM(world, PrintTime, EcsOnUpdate, 0); + +// System declaration using the descriptor API +ecs_system(world, { + .entity = ecs_entity(world, { + .name = "PrintTime", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .callback = PrintTime +}); + +// Runs PrintTime +ecs_progress(world, 0); +``` +```cpp +world.system("PrintTime") + .kind(flecs::OnUpdate) + .iter([](flecs::iter& it) { + printf("Time: %f\n", it.delta_time()); + }); + +// Runs PrintTime +world.progress(); +``` + +Tasks may query for components from a fixed source or singleton: + +```c +// System function +void PrintTime(ecs_iter_t *it) { + // Get singleton component + Game *g = ecs_field(it, Game, 1); + + printf("Time: %f\n", g->time); +} + +// System declaration using the ECS_SYSTEM macro +ECS_SYSTEM(world, PrintTime, EcsOnUpdate, Game($)); + +// System declaration using the descriptor API +ecs_system(world, { + .entity = ecs_entity(world, { + .name = "PrintTime", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .query.filter.terms = { + { .id = ecs_id(Game), .src.id = ecs_id(Game) } // Singleton source + }, + .callback = PrintTime +}); +``` +```cpp +world.system("PrintTime") + .term_at(1).singleton() + .kind(flecs::OnUpdate) + .iter([](flecs::iter& it, Game *g) { + printf("Time: %f\n", g->time); + }); +``` + +Note that `it->count` is always 0 for tasks, as they don't match any entities. In C++ this means that the function passed to `each` is never invoked for tasks. Applications will have to use `iter` instead when using tasks in C++. + +## Pipelines +A pipeline is a list of systems that is executed when the `ecs_progress`/`world::progress` function is invoked. Which systems are part of the pipeline is determined by a pipeline query. A pipeline query is a regular ECS query, which matches system entities. Flecs has a builtin pipeline with a predefined query, in addition to offering the ability to specify a custom pipeline query. + +A pipeline by default orders systems by their entity id, to ensure deterministic order. This generally means that systems will be ran in the order they are declared, as entity ids are monotonically increasing. Note that this is not guaranteed: when an application deletes entities before creating a system, the system can receive a recycled id, which means it could be lower than the last issued id. For this reason it is recommended to prevent entity deletion while registering systems. When this can't be avoided, an application can create a custom pipeline with a user-defined `order_by` function (see custom pipeline). + +Pipelines may utilize additional query mechanisms for ordering, such as `cascade` or `group_by`. + +In addition to a system query, pipelines also analyze the components that systems are reading and writing to determine where to insert sync points. During a sync point enqueued commands are ran, which ensures that systems after a sync point can see all mutations from before a sync point. + +### Builtin Pipeline +The builtin pipeline matches systems that depend on a _phase_. A phase is any entity with the `EcsPhase`/`flecs::Phase` tag. To add a dependency on a phase, the `DependsOn` relationship is used. This happens automatically when using the `ECS_SYSTEM` macro/`flecs::system::kind` method: + +```c +// System is created with (DependsOn, OnUpdate) +ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); +``` +```cpp +// System is created with (DependsOn, OnUpdate) +world.system("Move") + .kind(flecs::OnUpdate) + .each([](Position& p, Velocity& v) { + // ... + }); +``` + +Systems are ordered using a topology sort on the `DependsOn` relationship. Systems higher up in the topology are ran first. In the following example the order of systems is `InputSystem, MoveSystem, CollisionSystem`: + +``` + PreUpdate + / \ + InputSystem OnUpdate + / \ + MoveSystem PostUpdate + / + CollisionSystem +``` + +Flecs has the following builtin phases, listed in topology order: + +- `EcsOnStart` +- `EcsOnLoad` +- `EcsPostLoad` +- `EcsPreUpdate` +- `EcsOnUpdate` +- `EcsOnValidate` +- `EcsPostUpdate` +- `EcsPreStore` +- `EcsOnStore` + +In C++ the phase identifiers are namespaced: + +- `flecs::OnStart` +- `flecs::OnLoad` +- `flecs::PostLoad` +- `flecs::PreUpdate` +- `flecs::OnUpdate` +- `flecs::OnValidate` +- `flecs::PostUpdate` +- `flecs::PreStore` +- `flecs::OnStore` + +The `EcsOnStart`/`flecs::OnStart` phase is a special phase that is only ran the first time `progress()` is called. + +An application can create custom phases, which can be (but don't need to be) branched off of existing ones: + +```c +// Phases must have the EcsPhase tag +ecs_entity_t Physics = ecs_new_w_id(ecs, EcsPhase); +ecs_entity_t Collisions = ecs_new_w_id(ecs, EcsPhase); + +// Phases can (but don't have to) depend on other phases which forces ordering +ecs_add_pair(ecs, Physics, EcsDependsOn, EcsOnUpdate); +ecs_add_pair(ecs, Collisions, EcsDependsOn, Physics); + +// Custom phases can be used just like regular phases +ECS_SYSTEM(world, Collide, Collisions, Position, Velocity); +``` + +#### Builtin Pipeline Query +The builtin pipeline query looks like this: + +Query DSL: +``` +flecs.system.System, Phase(cascade(DependsOn)), !Disabled(up(DependsOn)), !Disabled(up(ChildOf)) +``` + +C descriptor API: +```c +ecs_pipeline_init(world, &(ecs_pipeline_desc_t){ + .query.filter.terms = { + { .id = EcsSystem }, + { .id = EcsPhase, .src.flags = EcsCascade, .src.trav = EcsDependsOn }, + { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsDependsOn, .oper = EcsNot }, + { .id = EcsDisabled, .src.flags = EcsUp, .src.trav = EcsChildOf, .oper = EcsNot } + } +}); +``` + +C++ builder API: +```c +world.pipeline() + .with(flecs::System) + .with(flecs::Phase).cascade(flecs::DependsOn) + .without(flecs::Disabled).up(flecs::DependsOn) + .without(flecs::Disabled).up(flecs::ChildOf) + .build(); +``` + +### Custom pipeline +Applications can create their own pipelines which fully customize which systems are matched, and in which order they are executed. Custom pipelines can use phases and `DependsOn`, or they may use a completely different approach. This example shows how to create a pipeline that matches all systems with the `Foo` tag: + +```c +ECS_TAG(world, Foo); + +// Create custom pipeline +ecs_entity_t pipeline = ecs_pipeline_init(world, &(ecs_pipeline_desc_t){ + .query.filter.terms = { + { .id = EcsSystem }, // mandatory + { .id = Foo } + } +}); + +// Configure the world to use the custom pipeline +ecs_set_pipeline(world, pipeline); + +// Create system +ECS_SYSTEM(world, Move, Foo, Position, Velocity); + +// Runs the pipeline & system +ecs_progress(world, 0); +``` +```cpp +// Create custom pipeline +flecs::entity pipeline = world.pipeline() + .with(flecs::System) + .with(Foo) // or .with() if a type + .build(); + +// Configure the world to use the custom pipeline +world.set_pipeline(pipeline); + +// Create system +auto move = world.system("Move") + .kind(Foo) // or .kind() if a type + .each(...); + +// Runs the pipeline & system +world.progress(); +``` + +Note that `ECS_SYSTEM` kind parameter/`flecs::system::kind` add the provided entity both by itself as well as with a `DependsOn` relationship. As a result, the above `Move` system ends up with both: + +- `Foo` +- `(DependsOn, Foo)` + +This allows applications to still use the macro/builder API with custom pipelines, even if the custom pipeline does not use the `DependsOn` relationship. To avoid adding the `DependsOn` relationship, `0` can be passed to `ECS_SYSTEM` or `flecs::system::kind` followed by adding the tag manually: + +```c +ecs_add(world, Move, Foo); +``` +```cpp +move.add(Foo); +``` + +#### Pipeline switching performance +When running a multithreaded application, switching pipelines can be an expensive operation. The reason for this is that it requires tearing down and recreating the worker threads with the new pipeline context. For this reason it can be more efficient to use queries that allow for enabling/disabling groups of systems vs. switching pipelines. + +For example, the builtin pipeline excludes groups of systems from the schedule that: +- have the `Disabled` tag +- have a parent (module) with the `Disabled` tag +- depend on a phase with the `Disabled` tag + +### Disabling systems +Because pipelines use regular ECS queries, adding the `EcsDisabled`/`flecs::Disabled` tag to a system entity will exclude the system from the pipeline. An application can use the `ecs_enable` function or `entity::enable`/`entity::disable` methods to enable/disable a system: + +```c +// Disable system in C +ecs_enable(world, Move, false); +// Enable system in C +ecs_enable(world, Move, true); +``` +```cpp +// Disable system in C++ +s.disable(); +// Enable system in C++ +s.enable(); +``` + +Additionally the `EcsDisabled`/`flecs::Disabled` tag can be added/removed directly: + +```c +ecs_add_id(world, Move, EcsDisabled); +``` +```c +s.add(flecs::Disabled); +``` + +Note that this applies both to builtin pipelines and custom pipelines, as entiites with the `Disabled` tag are ignored by default by queries. + +Phases can also be disabled when using the builtin pipeline, which excludes all systems that depend on the phase. Note that is transitive, if `PhaseB` depends on `PhaseA` and `PhaseA` is disabled, systems that depend on both `PhaseA` and `PhaseB` will be excluded from the pipeline. For this reason, the builtin phases don't directly depend on each other, so that disabling `EcsOnUpdate` does not exclude systems that depend on `EcsPostUpdate`. + +When the parent of a system is disabled, it will also be excluded from the builin pipeline. This makes it possible to disable all systems in a module with a single operation. + +## Staging +When calling `progress()` the world enters a readonly state in which all ECS operations like `add`, `remove`, `set` etc. are enqueued as commands (called "staging"). This makes sure that it is safe for systems to iterate component arrays while enqueueing operations. Without staging, component storage arrays could be reallocated to a different memory location, which could cause system code to crash. Additionally, enqueueing operations makes it safe for multiple threads to iterate the same world without taking locks as thread gets its own command queue. + +In general the framework tries its best to make sure that running code inside a system doesn't have different results than running it outside of a system, but because operations are enqueued as commands, this is not always the case. For example, the following code would return true outside of a system, but false inside of a system: + +```cpp +if (!e.has()) { + e.add(); + return e.has(); +} +``` + +Note that commands are only enqueued for ECS operations like `add`, `remove`, `set` etc. Reading or writing a queried for component directly does not enqueue commands. As a rule of thumb, anything that does not require calling an ECS function/method does not enqueue a command. + +There are a number of things applications can do to force merging of operations, or to prevent operations from being enqueued as commands. To decide which mechanism to use, an application has to decide whether it needs: + +1. Commands to be merged before another system +2. Operations not to be enqueued as commands. + +The mechanisms to accomplish this are sync points for 1), and `no_readonly` systems for 2). + +### Sync points +Sync points are moments during the frame where all commands are flushed to the storage. Systems that run after a sync point will be able to see all operations that happened up until the sync point. Sync points are inserted automatically by analyzing which commands could have been inserted and which components are being read by systems. + +Because Flecs can't see inside the implementation of a system, pipelines can't know for which components a system could insert commands. This means that by default a pipeline assumes that systems insert no commands / that it is OK for commands to be merged at the end of the frame. To get commands to merge sooner, systems must be annotated with the components they write. + +A pipeline tracks on a per-component basis whether commands could have been inserted for it, and when a component is being read. When a pipeline sees a read for a component for which commmands could have been inserted, a sync point is inserted before the system that reads. This ensures that sync points are only inserted when necessary: + +- Multiple systems that enqueue commands can run before a sync point, possibly combining commands for multiple reads +- When a system is inactive (e.g. it doesn't match any entities) or is disabled, it will be ignored for sync point insertion +- Different combinations of modules have different sync requirements, automatic sync point insertion ensures that sync points are only inserted for the set of systems that are imported and are active. + +To make the scheduler aware that a system can enqueue commands for a component, use the `out` modifier in combination with matching a component on an empty entity (`0`). This tells the scheduler that even though a system is not matching with the component, it is still "writing" it: + +```c +// The '()' means, don't match this component on an entity, while `[out]` indicates +// that the component is being written. This is interpreted by pipelines as a +// system that can potentially enqueue commands for the Transform component. +ECS_SYSTEM(world, SetTransform, EcsOnUpdate, Position, [out] Transform()); +``` +```c +// When using the descriptor API for creating the system, set the EcsIsEntity +// flag while leaving the id field to 0. This is equivalent to doing `()` in the DSL. +ecs_system(world, { + .query.filter.terms = { + { ecs_id(Position) }, + { + .inout = EcsOut, + .id = ecs_id(Transform), + .src.flags = EcsIsEntity, + .src.id = 0 /* Default value */ + } + }, + /* ... */ +}); +``` +```cpp +// In the C++ API, use the write method to indicate commands could be inserted. +world.system() + .write() + .each( /* ... */); +``` + +This will cause insertion of a sync point before the next system that reads `Transform`. Similarly, a system can also be annotated with reading a component that it doesn't match with, which is useful when a system calls `get`: + +```c +ECS_SYSTEM(world, SetTransform, EcsOnUpdate, Position, [in] Transform()); +``` +```c +ecs_system(world, { + .query.filter.terms = { + { ecs_id(Position) }, + { + .inout = EcsIn, + .id = ecs_id(Transform), + .src.flags = EcsIsEntity, + .src.id = 0 /* Default value */ + } + }, + /* ... */ +}); +``` +```cpp +// In the C++ API, use the read method to indicate a component is read using .get +world.system() + .read() + .each( /* ... */); +``` + +### No readonly systems +By default systems are ran while the world is in "readonly" mode, where all ECS operations are enqueued as commands. Note that readonly mode only applies to "structural" changes, such as changing the components of an entity or other operations that mutate ECS data structures. Systems can still write component values while in readonly mode. + +In some cases however, operations need to be immediately visible to a system. A typical example is a system that assigns tasks to resources, like assigning plates to a waiter. A system should only assign plates to a waiter that hasn't been assigned any plates yet, but to know which waiters are free, the operation that assigns a plate to a waiter must be immediately visible. + +To accomplish this, systems can be marked with the `no_readonly` flag, which signals that a system should be ran while the world is not in readonly mode. This causes ECS operations to not get enqueued, and allows the system to directly see the results of operations. There are a few limitations to `no_readonly` systems: + +- `no_readonly` systems are always single threaded +- operations on the iterated over entity must still be deferred + +The reason for the latter limitation is that allowing for operations on the iterated over entity would cause the system to modify the storage it is iterating, which could cause undefined behavior similar to what you'd see when changing a vector while iterating it. + +The following example shows how to create a `no_readonly` system: + +```c +ecs_system(ecs, { + .entity = ecs_entity(ecs, { + .name = "AssignPlate", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .query.filter.terms = { + { .id = Plate }, + }, + .callback = AssignPlate, + .no_readonly = true // disable readonly mode for this system +}); +``` +```cpp + ecs.system("AssignPlate") + .with() + .no_readonly() // disable readonly mode for this system + .iter([&](flecs::iter& it) { /* ... */ }) +``` + +This ensures the world is not in readonly mode when the system is ran. Operations are however still enqueued as commands, which ensures that the system can enqueue commands for the entity that is being iterated over. To prevent commands from being enqueued, a system needs to suspend and resume command enqueueing. This is an extra step, but makes it possible for a system to both enqueue commands for the iterated over entity, as well as do operations that are immediately visible. An example: + +```c +void AssignPlate(ecs_iter_t *it) { + for (int i = 0; i < it->count; i ++) { + // ECS operations ran here are visible after running the system + ecs_defer_suspend(it->world); + // ECS operations ran here are immediately visible + ecs_defer_resume(it->world); + // ECS operations ran here are visible after running the system + } +} +``` +```cpp +.iter([](flecs::iter& it) { + for (auto i : it) { + // ECS operations ran here are visible after running the system + it.world().defer_suspend(); + // ECS operations ran here are immediately visible + it.world().defer_resume(); + // ECS operations ran here are visible after running the system + } +}); +``` + +Note that `defer_suspend` and `defer_resume` may only be called from within a `no_readonly` system. + +### Threading +Systems in Flecs can be multithreaded. This requires both the system to be created as a multithreaded system, as well as configuring the world to have a number of worker threads. To create worker threads, use the `set_threads` function: + +```c +ecs_set_threads(world, 4); // Create 4 worker threads +``` +```cpp +world.set_threads(4); +``` + +To create a multithreaded system, use the `multi_threaded` flag: + +```c +ecs_system(ecs, { + .entity = ecs_entity(ecs, { + .add = { ecs_dependson(EcsOnUpdate) } + }), + .query.filter.terms = { + { .id = ecs_id(Position) } + }, + .callback = Dummy, + .multi_threaded = true // run system on multiple threads +}); +``` +```cpp +world.system() + .multi_threaded() + .each( /* ... */ ); +``` + +By default systems are created as single threaded. Single threaded systems are always ran on the main thread. Multithreaded systems are ran on all worker threads. The scheduler runs each multithreaded system on all threads, and divides the number of matched entities across the threads. The way entities are allocated to threads ensures that the same entity is always processed by the same thread, until the next sync point. This means that in an ideal case, all systems in a frame can run until completion without having to synchronize. + +The way the scheduler ensures that the same entities are processed by the same threads is by slicing up the entities in a table into N slices, where N is the number of threads. For a table that has 1000 entities, the first thread will process entities 0..249, thread 2 250..499, thread 3 500..749 and thread 4 entities 750..999. For more details on this behavior, see `ecs_worker_iter`/`flecs::iterable::worker_iter`. + +### Threading with Async Tasks +Systems in Flecs can also be multithreaded using an external asynchronous task system. Instead of creating regular worker threads using `set_threads`, use the `set_task_threads` function and provide the OS API callbacks to create and wait for task completion using your job system. +This can be helpful when using Flecs within an application which already has a job queue system to handle multithreaded tasks. + +```c +ecs_set_tasks_threads(world, 4); // Create 4 worker task threads for the duration of each ecs_progress update +``` +```cpp +world.set_task_threads(4); +``` + +For simplicity, these task callbacks use the same format as Flecs `ecs_os_api_t` thread APIs. In fact, you could provide your regular os thread api functions to create short-duration threads for multithreaded system processing. +Create multithreaded systems using the `multi_threaded` flag as with `ecs_set_threads` above. + +When `ecs_progress` is called, your `ecs_os_api.task_new_` callback will be called once for every task thread needed to create task threads on demand. When `ecs_progress` is complete, your `ecs_os_api.task_join_` function will be called to clean up each task thread. +By providing callback functions which create and remove tasks for your specific asynchronous task system, you can use Flecs with any kind of async task management scheme. +The only limitation is that your async task manager must be able to create and execute the number of simultaneous tasks specified in `ecs_set_task_threads` and must exist for the duration of `ecs_progress`. + +## Timers +When running a pipeline, systems are ran each time `progress()` is called. The `FLECS_TIMER` addon makes it possible to run systems at a specific time interval or rate. + +### Interval +The following example shows how to run systems at a time interval: + +```c +// Using the ECS_SYSTEM macro +ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity); +ecs_set_interval(world, ecs_id(Move), 1.0); // Run at 1Hz +``` +```c +// Using the ecs_system_init function/ecs_system macro: +ecs_system(world, { + .entity = ecs_entity(world, { + .name = "Move", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .query.filter.terms = { + { ecs_id(Position) }, + { ecs_id(Velocity), .inout = EcsIn } + }, + .callback = Move, + .interval = 1.0 // Run at 1Hz +}); +``` +```cpp +world.system() + .interval(1.0) // Run at 1Hz + .each(...); +``` + +### Rate +The following example shows how to run systems every Nth tick with a rate filter: + +```c +// Using the ECS_SYSTEM macro +ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity); +ecs_set_rate(world, ecs_id(Move), 2); // Run every other frame +``` +```c +// Using the ecs_system_init function/ecs_system macro: +ecs_system(world, { + .entity = ecs_entity(world, { + .name = "Move", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .query.filter.terms = { + { ecs_id(Position) }, + { ecs_id(Velocity), .inout = EcsIn } + }, + .callback = Move, + .rate = 2.0 // Run every other frame +}); +``` +```cpp +world.system() + .rate(2) // Run every other frame + .each(...); +``` + +### Tick source +Instead of setting the interval or rate directly on the system, an application may also create a separate entity that holds the time or rate filter, and use that as a "tick source" for a system. This makes it easier to use the same settings across groups of systems: + +```c +// Using the ECS_SYSTEM macro +ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity); + +// Passing 0 to entity argument will create a new entity. Similarly a rate +// filter entity could be created with ecs_set_rate(world, 0, 2) +ecs_entity_t tick_source = ecs_set_interval(world, 0, 1.0); + +// Set tick source for system +ecs_set_tick_source(world, ecs_id(Move), tick_source); +``` +```c +// Passing 0 to entity argument will create a new entity. Similarly a rate +// filter entity could be created with ecs_set_rate(world, 0, 2) +ecs_entity_t tick_source = ecs_set_interval(world, 0, 1.0); + +// Using the ecs_system_init function/ecs_system macro: +ecs_system(world, { + .entity = ecs_entity(world, { + .name = "Move", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .query.filter.terms = { + { ecs_id(Position) }, + { ecs_id(Velocity), .inout = EcsIn } + }, + .callback = Move, + .tick_source = tick_source // Set tick source for system +}); +``` +```cpp +// A rate filter can be created with .rate(2) +flecs::entity tick_source = world.timer() + .interval(1.0); + +world.system() + .tick_source(tick_source) // Set tick source for system + .each(...); +``` + +Interval filters can be paused and resumed: +```c +// Pause timer +ecs_stop_timer(world, tick_source); + +// Resume timer +ecs_start_timer(world, tick_source); +``` +```cpp +// Pause timer +tick_source.stop(); + +// Resume timer +tick_source.start(); +``` + +An additional advantage of using shared interval/rate filter between systems is that it guarantees that systems are ran at the same tick. When a system is disabled, its interval/rate filters aren't updated, which means that when the system is reenabled again it would be out of sync with other systems that had the same interval/rate. specified. When using a shared tick source however the system is guaranteed to run at the same tick as other systems with the same tick source, even after the system is reenabled. + +### Nested tick sources +One tick source can be used as the input of another (rate) tick source. The rate tick source will run at each Nth tick of the input tick source. This can be used to create nested tick sources, like in the following example: + +```c +// Tick at 1Hz +ecs_entity_t each_second = ecs_set_interval(world, 0, 1.0); + +// Tick each minute +ecs_entity_t each_minute = ecs_set_rate(world, 0, 60); +ecs_set_tick_source(world, each_minute, each_second); + +// Tick each hour +ecs_entity_t each_hour = ecs_set_rate(world, 0, 60); +ecs_set_tick_source(world, each_hour, each_minute); +``` +```cpp +// Tick at 1Hz +flecs::entity each_second = world.timer() + .interval(1.0); + +// Tick each minute +flecs::entity each_minute = world.timer() + .rate(60, each_second); + +// Tick each hour +flecs::entity each_hour = world.timer() + .rate(60, each_minute); +``` + +Systems can also act as each others tick source: + +```c +// Tick at 1Hz +ecs_entity_t each_second = ecs_system(world, { + .entity = ecs_entity(world, { + .name = "EachSecond", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .callback = Dummy, + .interval = 1.0 +}); + +// Tick each minute +ecs_entity_t each_minute = ecs_system(world, { + .entity = ecs_entity(world, { + .name = "EachMinute", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .callback = Dummy, + .tick_source = each_second, + .rate = 60 +}); + +// Tick each hour +ecs_entity_t each_hour = ecs_system(world, { + .entity = ecs_entity(world, { + .name = "EachHour", + .add = { ecs_dependson(EcsOnUpdate) } + }), + .callback = Dummy, + .tick_source = each_minute, + .rate = 60 +}); +``` +```cpp +// Tick at 1Hz +flecs::entity each_second = world.system("EachSecond") + .interval(1.0) + .iter([](flecs::iter& it) { /* ... */ }); + +// Tick each minute +flecs::entity each_minute = world.system("EachMinute") + .tick_source(each_second) + .rate(60) + .iter([](flecs::iter& it) { /* ... */ }); + +// Tick each hour +flecs::entity each_hour = world.system("EachHour") + .tick_source(each_minute) + .rate(60) + .iter([](flecs::iter& it) { /* ... */ }); +``` diff --git a/engine/libs/flecs/docs/cfg/Doxyfile b/engine/libs/flecs/docs/cfg/Doxyfile new file mode 100644 index 0000000..85bc17a --- /dev/null +++ b/engine/libs/flecs/docs/cfg/Doxyfile @@ -0,0 +1,2700 @@ +# Doxyfile 1.9.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = Flecs + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = v3.2 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = A fast entity component system (ECS) for C & C++ + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = docs/img/logo_small.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# numer of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = YES + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = include/flecs.h \ + include/flecs/os_api.h \ + include/flecs/addons \ + README.md \ + docs/Docs.md \ + docs/Quickstart.md \ + docs/FAQ.md \ + docs/DesignWithFlecs.md \ + docs/FlecsScriptTutorial.md \ + docs/Manual.md \ + docs/Queries.md \ + docs/Systems.md \ + docs/Relationships.md \ + docs/RestApi.md \ + docs/JsonFormat.md + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.h \ + *.hpp \ + *.inl \ + *.md \ + *.dox + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = docs/img + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = README.md + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = docs/cfg/header.html + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = docs/cfg/footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = docs/cfg/doxygen-awesome.css \ + docs/cfg/doxygen-awesome-sidebar-only.css \ + docs/cfg/doxygen-awesome-sidebar-only-darkmode-toggle.css \ + docs/cfg/custom.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = docs/cfg/doxygen-awesome-darkmode-toggle.js \ + docs/cfg/doxygen-awesome-fragment-copy-button.js \ + docs/cfg/doxygen-awesome-paragraph-link.js \ + docs/cfg/doxygen-awesome-interactive-toc.js \ + docs/img/logo_small_dark.png + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. Default setting AUTO_LIGHT +# enables light output unless the user preference is dark output. Other options +# are DARK to always use dark mode, LIGHT to always use light mode, AUTO_DARK to +# default to dark mode unless the user prefers light mode, and TOGGLE to let the +# user toggle between dark and light mode via a button. +# Possible values are: LIGHT Always generate light output., DARK Always generate +# dark output., AUTO_LIGHT Automatically set the mode according to the user +# preference, use light mode if no preference is set (the default)., AUTO_DARK +# Automatically set the mode according to the user preference, use dark mode if +# no preference is set. and TOGGLE Allow to user to switch between light and +# dark mode via a button.. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /